From e68b9d00a6e05b3a941f63ffb696f91e554ac5ec Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 18 Oct 2024 20:33:49 +0200 Subject: Adding upstream version 9.0.3. Signed-off-by: Daniel Baumann --- modules/actions/github.go | 133 + modules/actions/github_test.go | 119 + modules/actions/log.go | 224 ++ modules/actions/task_state.go | 123 + modules/actions/task_state_test.go | 165 + modules/actions/workflows.go | 702 ++++ modules/actions/workflows_test.go | 163 + modules/activitypub/client.go | 273 ++ modules/activitypub/client_test.go | 138 + modules/activitypub/main_test.go | 18 + modules/activitypub/user_settings.go | 48 + modules/activitypub/user_settings_test.go | 30 + modules/analyze/code_language.go | 27 + modules/analyze/generated.go | 27 + modules/analyze/vendor.go | 13 + modules/analyze/vendor_test.go | 41 + modules/assetfs/layered.go | 256 ++ modules/assetfs/layered_test.go | 110 + modules/auth/common.go | 22 + modules/auth/openid/discovery_cache.go | 57 + modules/auth/openid/discovery_cache_test.go | 49 + modules/auth/openid/openid.go | 37 + modules/auth/pam/pam.go | 43 + modules/auth/pam/pam_stub.go | 22 + modules/auth/pam/pam_test.go | 20 + modules/auth/password/hash/argon2.go | 80 + modules/auth/password/hash/bcrypt.go | 54 + modules/auth/password/hash/common.go | 28 + modules/auth/password/hash/dummy.go | 33 + modules/auth/password/hash/dummy_test.go | 26 + modules/auth/password/hash/hash.go | 189 ++ modules/auth/password/hash/hash_test.go | 191 ++ modules/auth/password/hash/pbkdf2.go | 67 + modules/auth/password/hash/scrypt.go | 67 + modules/auth/password/hash/setting.go | 76 + modules/auth/password/hash/setting_test.go | 38 + modules/auth/password/password.go | 136 + modules/auth/password/password_test.go | 77 + modules/auth/password/pwn.go | 52 + modules/auth/password/pwn/pwn.go | 118 + modules/auth/password/pwn/pwn_test.go | 51 + modules/auth/webauthn/webauthn.go | 77 + modules/auth/webauthn/webauthn_test.go | 25 + modules/avatar/avatar.go | 139 + modules/avatar/avatar_test.go | 137 + modules/avatar/hash.go | 28 + modules/avatar/hash_test.go | 26 + modules/avatar/identicon/block.go | 717 +++++ modules/avatar/identicon/colors.go | 134 + modules/avatar/identicon/identicon.go | 140 + modules/avatar/identicon/identicon_test.go | 39 + modules/avatar/identicon/polygon.go | 68 + modules/avatar/identicon/testdata/.gitignore | 1 + modules/avatar/testdata/animated.webp | Bin 0 -> 4934 bytes modules/avatar/testdata/avatar.jpeg | Bin 0 -> 521 bytes modules/avatar/testdata/avatar.png | Bin 0 -> 159 bytes modules/base/base.go | 9 + modules/base/natural_sort.go | 90 + modules/base/natural_sort_test.go | 29 + modules/base/tool.go | 168 + modules/base/tool_test.go | 129 + modules/cache/cache.go | 184 ++ modules/cache/cache_redis.go | 161 + modules/cache/cache_test.go | 150 + modules/cache/cache_twoqueue.go | 208 ++ modules/cache/context.go | 181 ++ modules/cache/context_test.go | 79 + modules/charset/ambiguous.go | 59 + modules/charset/ambiguous/ambiguous.json | 1 + modules/charset/ambiguous/generate.go | 188 ++ modules/charset/ambiguous_gen.go | 836 +++++ modules/charset/ambiguous_gen_test.go | 31 + modules/charset/breakwriter.go | 43 + modules/charset/breakwriter_test.go | 68 + modules/charset/charset.go | 211 ++ modules/charset/charset_test.go | 385 +++ modules/charset/escape.go | 58 + modules/charset/escape_status.go | 27 + modules/charset/escape_stream.go | 289 ++ modules/charset/escape_test.go | 194 ++ modules/charset/htmlstream.go | 200 ++ modules/charset/invisible/generate.go | 121 + modules/charset/invisible_gen.go | 36 + modules/container/filter.go | 21 + modules/container/filter_test.go | 28 + modules/container/set.go | 56 + modules/container/set_test.go | 36 + modules/csv/csv.go | 149 + modules/csv/csv_test.go | 590 ++++ modules/emoji/emoji.go | 186 ++ modules/emoji/emoji_data.go | 3404 ++++++++++++++++++++ modules/emoji/emoji_test.go | 99 + modules/eventsource/event.go | 118 + modules/eventsource/event_test.go | 53 + modules/eventsource/manager.go | 79 + modules/eventsource/manager_run.go | 115 + modules/eventsource/messenger.go | 68 + modules/forgefed/activity.go | 65 + modules/forgefed/activity_test.go | 171 + modules/forgefed/actor.go | 218 ++ modules/forgefed/actor_test.go | 225 ++ modules/forgefed/forgefed.go | 52 + modules/forgefed/nodeinfo.go | 19 + modules/forgefed/repository.go | 111 + modules/forgefed/repository_test.go | 145 + modules/generate/generate.go | 75 + modules/generate/generate_test.go | 35 + modules/git/README.md | 3 + modules/git/batch.go | 46 + modules/git/batch_reader.go | 347 ++ modules/git/blame.go | 211 ++ modules/git/blame_sha256_test.go | 148 + modules/git/blame_test.go | 147 + modules/git/blob.go | 228 ++ modules/git/blob_test.go | 59 + modules/git/command.go | 473 +++ modules/git/command_race_test.go | 38 + modules/git/command_test.go | 70 + modules/git/commit.go | 587 ++++ modules/git/commit_info.go | 176 + modules/git/commit_info_test.go | 175 + modules/git/commit_reader.go | 110 + modules/git/commit_sha256_test.go | 211 ++ modules/git/commit_test.go | 401 +++ modules/git/diff.go | 328 ++ modules/git/diff_test.go | 169 + modules/git/error.go | 187 ++ modules/git/foreachref/format.go | 83 + modules/git/foreachref/format_test.go | 66 + modules/git/foreachref/parser.go | 128 + modules/git/foreachref/parser_test.go | 227 ++ modules/git/git.go | 422 +++ modules/git/git_test.go | 96 + modules/git/grep.go | 195 ++ modules/git/grep_test.go | 203 ++ modules/git/hook.go | 143 + modules/git/internal/cmdarg.go | 9 + modules/git/last_commit_cache.go | 159 + modules/git/log_name_status.go | 437 +++ modules/git/notes.go | 99 + modules/git/notes_test.go | 53 + modules/git/object_format.go | 143 + modules/git/object_id.go | 114 + modules/git/object_id_test.go | 49 + modules/git/object_signature.go | 11 + modules/git/parse.go | 137 + modules/git/parse_test.go | 103 + modules/git/pipeline/catfile.go | 108 + modules/git/pipeline/lfs.go | 254 ++ modules/git/pipeline/namerev.go | 33 + modules/git/pipeline/revlist.go | 86 + modules/git/pushoptions/pushoptions.go | 113 + modules/git/pushoptions/pushoptions_test.go | 125 + modules/git/ref.go | 214 ++ modules/git/ref_test.go | 38 + modules/git/remote.go | 39 + modules/git/repo.go | 342 ++ modules/git/repo_archive.go | 80 + modules/git/repo_attribute.go | 286 ++ modules/git/repo_attribute_test.go | 351 ++ modules/git/repo_base.go | 124 + modules/git/repo_base_test.go | 163 + modules/git/repo_blame.go | 23 + modules/git/repo_blob_test.go | 70 + modules/git/repo_branch.go | 349 ++ modules/git/repo_branch_test.go | 197 ++ modules/git/repo_commit.go | 677 ++++ modules/git/repo_commit_test.go | 103 + modules/git/repo_commitgraph.go | 20 + modules/git/repo_compare.go | 345 ++ modules/git/repo_compare_test.go | 164 + modules/git/repo_gpg.go | 58 + modules/git/repo_hook.go | 14 + modules/git/repo_index.go | 159 + modules/git/repo_language_stats.go | 251 ++ modules/git/repo_language_stats_test.go | 42 + modules/git/repo_object.go | 101 + modules/git/repo_ref.go | 157 + modules/git/repo_ref_test.go | 56 + modules/git/repo_stats.go | 151 + modules/git/repo_stats_test.go | 37 + modules/git/repo_tag.go | 366 +++ modules/git/repo_tag_test.go | 364 +++ modules/git/repo_test.go | 57 + modules/git/repo_tree.go | 156 + modules/git/signature.go | 67 + modules/git/signature_test.go | 47 + modules/git/submodule.go | 119 + modules/git/submodule_test.go | 42 + modules/git/tag.go | 129 + modules/git/tag_test.go | 109 + .../tests/repos/language_stats_repo/COMMIT_EDITMSG | 3 + modules/git/tests/repos/language_stats_repo/HEAD | 1 + modules/git/tests/repos/language_stats_repo/config | 5 + .../tests/repos/language_stats_repo/description | 1 + modules/git/tests/repos/language_stats_repo/index | Bin 0 -> 553 bytes .../tests/repos/language_stats_repo/info/exclude | 6 + .../git/tests/repos/language_stats_repo/logs/HEAD | 2 + .../language_stats_repo/logs/refs/heads/master | 2 + .../1e/ea60592b55dcb45c36029cc1202132e9fb756c | Bin 0 -> 63 bytes .../22/b6aa0588563508d8879f062470c8cbc7b2f2bb | Bin 0 -> 200 bytes .../34/1fca5b5ea3de596dc483e54c2db28633cd2f97 | Bin 0 -> 172 bytes .../42/25ecfaf6bafbcfa31ea5cbd8121c36d9457085 | Bin 0 -> 116 bytes .../4a/c803638e4b8995146e329a05e096fa2c77a03d | Bin 0 -> 73 bytes .../64/4c37ad7fe64ac012df7e59d27a92e3137c640e | Bin 0 -> 53 bytes .../6c/633a0067b463e459ae952716b17ae36aa30adc | Bin 0 -> 371 bytes .../8e/b563dc106e3dfd3ad0fa81f7a0c5e2604f80cd | Bin 0 -> 201 bytes .../8f/ee858da5796dfb37704761701bb8e800ad9ef3 | Bin 0 -> 828 bytes .../aa/a21bf84c8b2304608d3fc83b747840f2456299 | Bin 0 -> 54 bytes .../da/a5abe3c5f42cae598e362e8a8db6284565d6bb | Bin 0 -> 64 bytes .../repos/language_stats_repo/refs/heads/master | 1 + modules/git/tests/repos/repo1_bare/HEAD | 1 + modules/git/tests/repos/repo1_bare/config | 4 + modules/git/tests/repos/repo1_bare/description | 1 + modules/git/tests/repos/repo1_bare/index | Bin 0 -> 65 bytes modules/git/tests/repos/repo1_bare/info/exclude | 6 + modules/git/tests/repos/repo1_bare/logs/HEAD | 2 + .../tests/repos/repo1_bare/logs/refs/heads/master | 2 + .../0b/9f291245f6c596fd30bee925fe94fe0cbadd60 | Bin 0 -> 54 bytes .../11/93ff46343f4f6a0522e2b28b871e905178c1f0 | Bin 0 -> 23 bytes .../15/3f451b9ee7fa1da317ab17a127e9fd9d384310 | Bin 0 -> 31 bytes .../18/4d49c75a0b202b1d2ea2fcb5861c329321fcd6 | Bin 0 -> 56 bytes .../1c/91d130dc5fb75fd2d9f586a058650889cfe7fb | Bin 0 -> 813 bytes .../21/6bf54c2f2e2916b830ebe09e8c58a6ed52d86b | Bin 0 -> 28 bytes .../28/345b214c5967bd9cdd98cc7f88f2f1ac574e02 | Bin 0 -> 85 bytes .../28/39944139e0de9737a044f78b0e4b40d989a9e3 | 1 + .../28/b55526e7100924d864dd89e35c1ea62e7a5a32 | Bin 0 -> 818 bytes .../2e/65efe2a145dda7ee51d1741299f848e5bf752e | Bin 0 -> 16 bytes .../30/4c56b3bef33d0afeb8515ee803c839daf30ab8 | Bin 0 -> 85 bytes .../34/d1da713bf7de1c535e1d7d3ca985afd84bc7e5 | Bin 0 -> 23 bytes .../36/f97d9a96457e2bab511db30fe2db03893ebc64 | Bin 0 -> 770 bytes .../37/991dec2c8e592043f47155ce4808d4580f9123 | 1 + .../38/441bf2c4d4c27efff94728c9eb33266f44a702 | Bin 0 -> 134 bytes .../3a/d28a9149a2864384548f3d17ed7f38014c9e8a | Bin 0 -> 638 bytes .../4b/825dc642cb6eb9a060e54bf8d69288fbee4904 | Bin 0 -> 15 bytes .../50/13716a9da8e66ea21059a84f1b4311424d2b7f | Bin 0 -> 31 bytes .../59/dfb0bb505a601006e31fed53d2e24e44fca9ca | Bin 0 -> 144 bytes .../5c/80b0245c1c6f8343fa418ec374b13b5d4ee658 | 3 + .../62/d735f9efa9cf5b7df6bac9917b80e4779f4315 | Bin 0 -> 111 bytes .../64/3a35374408002fcf2f0e8d42d262a1e0e2f80e | Bin 0 -> 32 bytes .../6c/493ff740f9380390d5c9ddef4af18697ac9375 | Bin 0 -> 21 bytes .../6f/bd69e9823458e6c4a2fc5c0f6bc022b2f2acd1 | 1 + .../7e/3b688f3369ca28ebafbda9f8ef39713dd12fc8 | Bin 0 -> 85 bytes .../80/06ff9adbf0cb94da7dad9e537e53817f9fa5c0 | Bin 0 -> 176 bytes .../82/26f571dcc2d2f33a7179d929b10b9c39faa631 | Bin 0 -> 71 bytes .../83/b9c4da46ed59098a009f8640c77eac97b71dfe | Bin 0 -> 115 bytes .../8d/92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2 | Bin 0 -> 153 bytes .../93/3305878a3c9ad485c29b87fb662a73a9675c4b | Bin 0 -> 770 bytes .../95/bb4d39648ee7e325106df01a621c530863a653 | Bin 0 -> 121 bytes .../98/1ff127cc331753bba28e1377c35934f1ca9b56 | Bin 0 -> 56 bytes .../9c/9aef8dd84e02bc7ec12641deb4c930a7c30185 | 2 + .../a4/79ead1abb694ffca26f67b09c8313b12fa2a13 | Bin 0 -> 30 bytes .../b1/4df6442ea5a1b382985a6549b85d435376c351 | Bin 0 -> 18 bytes .../b1/fc9917b618c924cf4aa421dae74e8bf9b556d3 | Bin 0 -> 176 bytes .../b7/5f44edbd9252c32bf9faa0c1257ffb3b126c24 | Bin 0 -> 80 bytes .../c8/c90111bdc18b3afd2b2906007059e95ac8fdc3 | Bin 0 -> 111 bytes .../ca/6b5ddf303169a72d2a2971acde4f6eea194e5c | 4 + .../ce/064814f4a0d337b333e646ece456cd39fab612 | Bin 0 -> 815 bytes .../cf/8b0b492a950b358a7ce7f9d01b18aef48a6b2d | Bin 0 -> 827 bytes .../d0/845fe2f85710b50d673dafe98236bf9f2023da | Bin 0 -> 50 bytes .../e2/129701f1a4d54dc44f03c93bca0a2aec7c5449 | Bin 0 -> 21 bytes .../f1/a6cb52b2d16773290cefe49ad0684b50a4f930 | Bin 0 -> 111 bytes .../fe/af4ba6bc635fec442f46ddd4512416ec43c2c2 | Bin 0 -> 817 bytes modules/git/tests/repos/repo1_bare/pulls/1.patch | 0 modules/git/tests/repos/repo1_bare/pulls/2.patch | 39 + .../git/tests/repos/repo1_bare/refs/heads/branch1 | 1 + .../git/tests/repos/repo1_bare/refs/heads/branch2 | 1 + .../git/tests/repos/repo1_bare/refs/heads/master | 1 + .../git/tests/repos/repo1_bare/refs/notes/commits | 1 + .../tests/repos/repo1_bare/refs/tags/signed-tag | 1 + modules/git/tests/repos/repo1_bare/refs/tags/test | 1 + modules/git/tests/repos/repo1_bare_sha256/HEAD | 1 + modules/git/tests/repos/repo1_bare_sha256/config | 6 + .../git/tests/repos/repo1_bare_sha256/description | 1 + .../git/tests/repos/repo1_bare_sha256/info/exclude | 6 + .../git/tests/repos/repo1_bare_sha256/info/refs | 7 + .../repo1_bare_sha256/objects/info/commit-graph | Bin 0 -> 2048 bytes .../repos/repo1_bare_sha256/objects/info/packs | 2 + ...be78665970b0c38c6b495d5fc034bc7a7b95334b.bitmap | Bin 0 -> 710 bytes ...2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.idx | Bin 0 -> 2576 bytes ...f9be78665970b0c38c6b495d5fc034bc7a7b95334b.pack | Bin 0 -> 5656 bytes ...2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.rev | Bin 0 -> 224 bytes .../git/tests/repos/repo1_bare_sha256/packed-refs | 8 + .../tests/repos/repo1_bare_sha256/refs/heads/main | 1 + modules/git/tests/repos/repo2_empty/HEAD | 1 + modules/git/tests/repos/repo2_empty/config | 6 + modules/git/tests/repos/repo2_empty/description | 1 + modules/git/tests/repos/repo2_empty/info/exclude | 6 + .../tests/repos/repo2_empty/objects/info/.gitkeep | 0 .../tests/repos/repo2_empty/objects/pack/.gitkeep | 0 .../tests/repos/repo2_empty/refs/heads/.gitkeep | 0 .../git/tests/repos/repo2_empty/refs/tags/.gitkeep | 0 modules/git/tests/repos/repo3_notes/COMMIT_EDITMSG | 1 + modules/git/tests/repos/repo3_notes/HEAD | 1 + modules/git/tests/repos/repo3_notes/config | 7 + modules/git/tests/repos/repo3_notes/description | 1 + modules/git/tests/repos/repo3_notes/index | Bin 0 -> 145 bytes modules/git/tests/repos/repo3_notes/logs/HEAD | 2 + .../tests/repos/repo3_notes/logs/refs/heads/master | 2 + .../29/7128d6553180486c780e2f747cb6d0014bf1f6 | Bin 0 -> 55 bytes .../2f/7e2ea1e905c14c8a98e7ce47b395592834b9ef | Bin 0 -> 81 bytes .../3e/668dbfac39cbc80a9ff9c61eb565d944453ba4 | 3 + .../42/716fdb6f261867472899d785123e6ecaa5ca02 | Bin 0 -> 44 bytes .../56/a6051ca2b02b04ef92d5150c9ef600403cb1de | Bin 0 -> 16 bytes .../61/6c62e75fce60d806f4afe993211705a00a2544 | Bin 0 -> 21 bytes .../65/4c8b6b63c08bf37f638d3f521626b7fbbd4d37 | 1 + .../ba/0a96fa63532d6c5087ecef070b0250ed72fa47 | Bin 0 -> 125 bytes .../c9/34d51cee361fdee21a3f3bb1a285f5ea9bc225 | Bin 0 -> 55 bytes .../d8/263ee9860594d2806b0dfd1bfd17528b0ba2a4 | Bin 0 -> 16 bytes .../f3/6ad903e408cb8f4ed90bda02e3a1fd2fab7907 | Bin 0 -> 21 bytes .../fe/c9fe57e9864fe537f02f825e377c4a8a65ad2e | Bin 0 -> 113 bytes .../git/tests/repos/repo3_notes/refs/heads/master | 1 + .../git/tests/repos/repo3_notes/refs/notes/commits | 1 + modules/git/tests/repos/repo4_commitsbetween/HEAD | 1 + .../git/tests/repos/repo4_commitsbetween/config | 7 + .../git/tests/repos/repo4_commitsbetween/logs/HEAD | 4 + .../repo4_commitsbetween/logs/refs/heads/main | 4 + .../27/734c860ab19650d48e71f9f12d9bd194ed82ea | Bin 0 -> 53 bytes .../56/a6051ca2b02b04ef92d5150c9ef600403cb1de | Bin 0 -> 16 bytes .../78/a445db1eac62fe15e624e1137965969addf344 | 3 + .../a7/8e5638b66ccfe7e1b4689d3d5684e42c97d7ca | Bin 0 -> 160 bytes .../ad/74ceca1b8fde10c7d933bd2e56d347dddb4ab5 | Bin 0 -> 53 bytes .../b5/d8dd0ddd9d8d752bb47b5f781f09f478316098 | Bin 0 -> 18 bytes .../d8/263ee9860594d2806b0dfd1bfd17528b0ba2a4 | Bin 0 -> 16 bytes .../e2/3cc6a008501f1491b0480cedaef160e41cf684 | Bin 0 -> 53 bytes .../fd/c1b615bdcff0f0658b216df0c9209e5ecb7c78 | Bin 0 -> 126 bytes .../repos/repo4_commitsbetween/refs/heads/main | 1 + modules/git/tests/repos/repo5_pulls/HEAD | 1 + modules/git/tests/repos/repo5_pulls/config | 6 + modules/git/tests/repos/repo5_pulls/description | 1 + modules/git/tests/repos/repo5_pulls/info/exclude | 6 + .../1a/2959532d2d18daa87bbd9f9d16051bef7b51df | Bin 0 -> 119 bytes .../56/51a1c4a48c47484a7a00a967ba4b6dde070bbf | Bin 0 -> 120 bytes .../58/a4bcc53ac13e7ff76127e0fb518b5262bf09af | 1 + .../6d/0b4cca434953833618fcd3dd7acff42c800df1 | Bin 0 -> 120 bytes .../a5/2ca5af1b0277638ce20797f80bb1a2997470ab | Bin 0 -> 120 bytes .../bf/4dc0709be60f043821351ff4bb2b17e5cabbb2 | 2 + .../d8/e0bbb45f200e67d9a784ce55bd90821af45ebd | 2 + .../ed/5119b3c1f45547b6785bc03eac7f87570fa17f | Bin 0 -> 660 bytes .../ed/8f4d2fa5b2420706580d191f5dd50c4e491f3f | 3 + .../ee/469963e76ae1bb7ee83d7510df2864e6c8c640 | Bin 0 -> 650 bytes .../git/tests/repos/repo5_pulls/objects/info/packs | 2 + ...ck-81423f591973f5d9dab89cc45afa1c544448133e.idx | Bin 0 -> 1408 bytes ...k-81423f591973f5d9dab89cc45afa1c544448133e.pack | Bin 0 -> 2363 bytes modules/git/tests/repos/repo5_pulls/packed-refs | 5 + .../git/tests/repos/repo5_pulls/refs/heads/master | 1 + .../repos/repo5_pulls/refs/heads/master-clone | 1 + .../repos/repo5_pulls/refs/heads/test-patch-1 | 1 + .../git/tests/repos/repo5_pulls/refs/pull/4/head | 1 + modules/git/tests/repos/repo5_pulls_sha256/HEAD | 1 + modules/git/tests/repos/repo5_pulls_sha256/config | 6 + .../git/tests/repos/repo5_pulls_sha256/description | 1 + .../git/tests/repos/repo5_pulls_sha256/info/refs | 4 + .../repo5_pulls_sha256/objects/info/commit-graph | Bin 0 -> 1544 bytes .../repos/repo5_pulls_sha256/objects/info/packs | 2 + ...41fe145d4a02b788eb26c31022a362312660a29d.bitmap | Bin 0 -> 414 bytes ...42641fe145d4a02b788eb26c31022a362312660a29d.idx | Bin 0 -> 1736 bytes ...2641fe145d4a02b788eb26c31022a362312660a29d.pack | Bin 0 -> 3140 bytes ...42641fe145d4a02b788eb26c31022a362312660a29d.rev | Bin 0 -> 140 bytes .../git/tests/repos/repo5_pulls_sha256/packed-refs | 5 + .../tests/repos/repo5_pulls_sha256/refs/heads/main | 1 + modules/git/tests/repos/repo6_blame/HEAD | 1 + modules/git/tests/repos/repo6_blame/config | 4 + .../31/bb4b42cecf0a98fc9a32fc5aaeaf53ec52643c | Bin 0 -> 98 bytes .../3b/0f66d8b065f8adbf2fef7d986528c655b98cb1 | Bin 0 -> 35 bytes .../45/fb6cbc12f970b04eacd5cd4165edd11c8d7376 | Bin 0 -> 167 bytes .../49/7701e5bb8676e419b93875d8f0808c7b31aed9 | Bin 0 -> 24 bytes .../54/4d8f7a3b15927cddf2299b4b562d6ebd71b6a7 | Bin 0 -> 175 bytes .../a8/9199e8dea077e4a8ba0bc01bc155275cfdd044 | Bin 0 -> 57 bytes .../af/7486bd54cfc39eea97207ca666aa69c9d6df93 | Bin 0 -> 134 bytes .../b8/d1ba1ccb58ee3744b3d1434aae7d26ce2d9421 | Bin 0 -> 54 bytes .../ca/411a3b842c3caec045772da42de16b3ffdafe8 | Bin 0 -> 54 bytes .../git/tests/repos/repo6_blame/refs/heads/master | 1 + modules/git/tests/repos/repo6_blame_sha256/HEAD | 1 + modules/git/tests/repos/repo6_blame_sha256/config | 6 + .../git/tests/repos/repo6_blame_sha256/description | 1 + .../tests/repos/repo6_blame_sha256/info/exclude | 6 + .../git/tests/repos/repo6_blame_sha256/info/refs | 1 + .../repo6_blame_sha256/objects/info/commit-graph | Bin 0 -> 1376 bytes .../repos/repo6_blame_sha256/objects/info/packs | 2 + ...62b611ac24312a5ffc3d3703d7c5cc906bdaee8e.bitmap | Bin 0 -> 318 bytes ...3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.idx | Bin 0 -> 1456 bytes ...c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.pack | Bin 0 -> 904 bytes ...3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.rev | Bin 0 -> 112 bytes .../git/tests/repos/repo6_blame_sha256/packed-refs | 2 + .../tests/repos/repo6_blame_sha256/refs/refs/main | 1 + modules/git/tests/repos/repo6_merge/HEAD | 1 + .../02/2f4ce6214973e018f02bf363bf8a2e3691f699 | Bin 0 -> 280 bytes .../05/45879290cc368a8becebc4aa34002c52d5fecc | Bin 0 -> 152 bytes .../1e/5d0a65fe099ef12d24b28f783896e4b8172576 | Bin 0 -> 88 bytes .../37/d35c7ed39e4e16d0b579a5b995b7e30b0e9411 | 2 + .../38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 | 2 + .../66/7e0fbc6bc02c2285d17f542e89b23c0fa5482b | Bin 0 -> 122 bytes .../9f/d90b1d524c0fea776ed5e6476da02ea1740597 | Bin 0 -> 26 bytes .../ae/4b035e7c4afbc000576cee3f713ea0c2f1e1e2 | 5 + .../ba/2906d0666cf726c7eaadd2cd3db615dedfdf3a | Bin 0 -> 20 bytes .../c1/a95c2eff8151c6d1437a0d5d3322a73ff38fb8 | Bin 0 -> 120 bytes .../cc/d1d4d594029e68c388ecef5aa3063fa1055831 | Bin 0 -> 26 bytes .../cd/fc1aaf7a149151cb7bff639fafe05668d4bbd2 | Bin 0 -> 120 bytes .../d1/792641396ff7630d35fbb0b74b86b0c71bca77 | Bin 0 -> 145 bytes .../ec/d11d8da0f25eaa99f64a37a82da98685f381e2 | Bin 0 -> 33 bytes .../fa/49b077972391ad58037050f2a75f74e3671e92 | Bin 0 -> 24 bytes .../git/tests/repos/repo6_merge/refs/heads/main | 1 + .../repos/repo6_merge/refs/heads/merge/add_file | 1 + .../repos/repo6_merge/refs/heads/merge/modify_file | 1 + .../repos/repo6_merge/refs/heads/merge/remove_file | 1 + modules/git/tests/repos/repo6_merge_sha256/HEAD | 1 + modules/git/tests/repos/repo6_merge_sha256/config | 6 + .../git/tests/repos/repo6_merge_sha256/description | 1 + .../tests/repos/repo6_merge_sha256/info/exclude | 6 + .../git/tests/repos/repo6_merge_sha256/info/refs | 4 + .../repo6_merge_sha256/objects/info/commit-graph | Bin 0 -> 1564 bytes .../repos/repo6_merge_sha256/objects/info/packs | 3 + ...1ab6a096c7530f577d5c0a79c63d9ac2b44f7510.bitmap | Bin 0 -> 410 bytes ...ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.idx | Bin 0 -> 1696 bytes ...c91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.pack | Bin 0 -> 1556 bytes ...ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.rev | Bin 0 -> 136 bytes ...6d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.idx | Bin 0 -> 1176 bytes ...87e67bb2a4a5501ca1fa3528fad8a9474fba7e50.mtimes | Bin 0 -> 84 bytes ...d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.pack | Bin 0 -> 447 bytes ...6d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.rev | Bin 0 -> 84 bytes .../git/tests/repos/repo6_merge_sha256/packed-refs | 5 + .../tests/repos/repo6_merge_sha256/refs/heads/main | 1 + modules/git/tree.go | 178 + modules/git/tree_blob.go | 81 + modules/git/tree_entry.go | 277 ++ modules/git/tree_entry_mode.go | 35 + modules/git/tree_test.go | 28 + modules/git/url/url.go | 89 + modules/git/url/url_test.go | 167 + modules/git/utils.go | 138 + modules/git/utils_test.go | 26 + modules/gitgraph/graph.go | 116 + modules/gitgraph/graph_models.go | 256 ++ modules/gitgraph/graph_test.go | 714 ++++ modules/gitgraph/parser.go | 336 ++ modules/gitrepo/branch.go | 49 + modules/gitrepo/gitrepo.go | 103 + modules/gitrepo/walk.go | 15 + modules/graceful/context.go | 36 + modules/graceful/manager.go | 260 ++ modules/graceful/manager_common.go | 108 + modules/graceful/manager_unix.go | 201 ++ modules/graceful/manager_windows.go | 190 ++ modules/graceful/net_unix.go | 321 ++ modules/graceful/net_windows.go | 19 + modules/graceful/releasereopen/releasereopen.go | 61 + .../graceful/releasereopen/releasereopen_test.go | 44 + modules/graceful/restart_unix.go | 115 + modules/graceful/server.go | 284 ++ modules/graceful/server_hooks.go | 73 + modules/graceful/server_http.go | 37 + modules/hcaptcha/error.go | 47 + modules/hcaptcha/hcaptcha.go | 140 + modules/hcaptcha/hcaptcha_test.go | 106 + modules/highlight/highlight.go | 233 ++ modules/highlight/highlight_test.go | 196 ++ modules/hostmatcher/hostmatcher.go | 161 + modules/hostmatcher/hostmatcher_test.go | 161 + modules/hostmatcher/http.go | 65 + modules/html/html.go | 25 + modules/httpcache/httpcache.go | 101 + modules/httpcache/httpcache_test.go | 100 + modules/httplib/request.go | 206 ++ modules/httplib/serve.go | 237 ++ modules/httplib/serve_test.go | 109 + modules/httplib/url.go | 27 + modules/httplib/url_test.go | 123 + modules/indexer/code/bleve/bleve.go | 354 ++ .../indexer/code/elasticsearch/elasticsearch.go | 388 +++ .../code/elasticsearch/elasticsearch_test.go | 16 + modules/indexer/code/git.go | 199 ++ modules/indexer/code/indexer.go | 310 ++ modules/indexer/code/indexer_test.go | 145 + modules/indexer/code/internal/indexer.go | 54 + modules/indexer/code/internal/model.go | 44 + modules/indexer/code/internal/util.go | 32 + modules/indexer/code/search.go | 228 ++ modules/indexer/internal/base32.go | 21 + modules/indexer/internal/bleve/batch.go | 58 + modules/indexer/internal/bleve/indexer.go | 102 + modules/indexer/internal/bleve/metadata.go | 55 + modules/indexer/internal/bleve/metadata_test.go | 28 + modules/indexer/internal/bleve/query.go | 56 + modules/indexer/internal/bleve/util.go | 48 + modules/indexer/internal/db/indexer.go | 34 + modules/indexer/internal/elasticsearch/indexer.go | 93 + modules/indexer/internal/elasticsearch/util.go | 68 + modules/indexer/internal/indexer.go | 37 + modules/indexer/internal/meilisearch/filter.go | 119 + modules/indexer/internal/meilisearch/indexer.go | 88 + modules/indexer/internal/meilisearch/util.go | 38 + modules/indexer/internal/paginator.go | 34 + modules/indexer/issues/bleve/bleve.go | 300 ++ modules/indexer/issues/bleve/bleve_test.go | 18 + modules/indexer/issues/db/db.go | 107 + modules/indexer/issues/db/options.go | 112 + modules/indexer/issues/dboptions.go | 105 + .../indexer/issues/elasticsearch/elasticsearch.go | 290 ++ .../issues/elasticsearch/elasticsearch_test.go | 48 + modules/indexer/issues/indexer.go | 315 ++ modules/indexer/issues/indexer_test.go | 410 +++ modules/indexer/issues/internal/indexer.go | 42 + modules/indexer/issues/internal/model.go | 150 + modules/indexer/issues/internal/tests/tests.go | 771 +++++ modules/indexer/issues/meilisearch/meilisearch.go | 301 ++ .../indexer/issues/meilisearch/meilisearch_test.go | 97 + modules/indexer/issues/util.go | 193 ++ modules/indexer/stats/db.go | 84 + modules/indexer/stats/indexer.go | 88 + modules/indexer/stats/indexer_test.go | 52 + modules/indexer/stats/queue.go | 49 + modules/issue/template/template.go | 489 +++ modules/issue/template/template_test.go | 963 ++++++ modules/issue/template/unmarshal.go | 147 + modules/json/json.go | 172 + modules/keying/keying.go | 125 + modules/keying/keying_test.go | 111 + modules/label/label.go | 46 + modules/label/parser.go | 118 + modules/label/parser_test.go | 72 + modules/lfs/LICENSE | 20 + modules/lfs/client.go | 32 + modules/lfs/client_test.go | 21 + modules/lfs/content_store.go | 163 + modules/lfs/endpoint.go | 107 + modules/lfs/endpoint_test.go | 74 + modules/lfs/filesystem_client.go | 88 + modules/lfs/http_client.go | 259 ++ modules/lfs/http_client_test.go | 377 +++ modules/lfs/pointer.go | 129 + modules/lfs/pointer_scanner.go | 109 + modules/lfs/pointer_test.go | 103 + modules/lfs/shared.go | 115 + modules/lfs/transferadapter.go | 89 + modules/lfs/transferadapter_test.go | 172 + modules/log/color.go | 115 + modules/log/color_console.go | 17 + modules/log/color_console_other.go | 69 + modules/log/color_console_windows.go | 42 + modules/log/color_router.go | 87 + modules/log/event_format.go | 253 ++ modules/log/event_format_test.go | 114 + modules/log/event_writer.go | 54 + modules/log/event_writer_base.go | 169 + modules/log/event_writer_conn.go | 111 + modules/log/event_writer_conn_test.go | 76 + modules/log/event_writer_console.go | 40 + modules/log/event_writer_file.go | 53 + modules/log/flags.go | 138 + modules/log/flags_test.go | 31 + modules/log/groutinelabel.go | 19 + modules/log/groutinelabel_test.go | 33 + modules/log/init.go | 44 + modules/log/level.go | 136 + modules/log/level_test.go | 56 + modules/log/logger.go | 50 + modules/log/logger_global.go | 83 + modules/log/logger_impl.go | 240 ++ modules/log/logger_test.go | 146 + modules/log/manager.go | 142 + modules/log/manager_test.go | 43 + modules/log/misc.go | 78 + modules/log/stack.go | 80 + modules/markup/asciicast/asciicast.go | 64 + modules/markup/camo.go | 46 + modules/markup/camo_test.go | 44 + modules/markup/common/footnote.go | 498 +++ modules/markup/common/footnote_test.go | 62 + modules/markup/common/html.go | 16 + modules/markup/common/linkify.go | 153 + modules/markup/console/console.go | 89 + modules/markup/console/console_test.go | 33 + modules/markup/csv/csv.go | 157 + modules/markup/csv/csv_test.go | 33 + modules/markup/external/external.go | 146 + modules/markup/file_preview.go | 364 +++ modules/markup/html.go | 1325 ++++++++ modules/markup/html_internal_test.go | 486 +++ modules/markup/html_test.go | 1029 ++++++ modules/markup/markdown/ast.go | 176 + modules/markup/markdown/callout/ast.go | 37 + modules/markup/markdown/callout/github.go | 141 + modules/markup/markdown/callout/github_legacy.go | 78 + modules/markup/markdown/color_util.go | 19 + modules/markup/markdown/color_util_test.go | 50 + modules/markup/markdown/convertyaml.go | 83 + modules/markup/markdown/goldmark.go | 213 ++ modules/markup/markdown/markdown.go | 303 ++ modules/markup/markdown/markdown_test.go | 1365 ++++++++ modules/markup/markdown/math/block_node.go | 41 + modules/markup/markdown/math/block_parser.go | 125 + modules/markup/markdown/math/block_renderer.go | 42 + modules/markup/markdown/math/inline_block_node.go | 31 + modules/markup/markdown/math/inline_node.go | 48 + modules/markup/markdown/math/inline_parser.go | 153 + modules/markup/markdown/math/inline_renderer.go | 51 + modules/markup/markdown/math/math.go | 108 + modules/markup/markdown/meta.go | 103 + modules/markup/markdown/meta_test.go | 110 + modules/markup/markdown/prefixed_id.go | 59 + modules/markup/markdown/renderconfig.go | 126 + modules/markup/markdown/renderconfig_test.go | 162 + modules/markup/markdown/toc.go | 54 + modules/markup/markdown/transform_codespan.go | 56 + modules/markup/markdown/transform_heading.go | 32 + modules/markup/markdown/transform_image.go | 65 + modules/markup/markdown/transform_link.go | 46 + modules/markup/markdown/transform_list.go | 85 + modules/markup/mdstripper/mdstripper.go | 199 ++ modules/markup/mdstripper/mdstripper_test.go | 85 + modules/markup/orgmode/orgmode.go | 196 ++ modules/markup/orgmode/orgmode_test.go | 160 + modules/markup/renderer.go | 393 +++ modules/markup/renderer_test.go | 4 + modules/markup/sanitizer.go | 235 ++ modules/markup/sanitizer_test.go | 110 + modules/markup/tests/repo/repo1_filepreview/HEAD | 1 + modules/markup/tests/repo/repo1_filepreview/config | 6 + .../tests/repo/repo1_filepreview/description | 1 + .../tests/repo/repo1_filepreview/info/exclude | 6 + .../19/0d9492934af498c3f669d6a2431dc5459e5b20 | Bin 0 -> 120 bytes .../3f/ed9bce8610a52048747f627b3863374642c85c | Bin 0 -> 91 bytes .../4b/825dc642cb6eb9a060e54bf8d69288fbee4904 | Bin 0 -> 15 bytes .../4c/1aaf56bcb9f39dcf65f3f250726850aed13cd6 | Bin 0 -> 187 bytes .../83/57a737d04385bb7f2ab59ff184be94756e7972 | Bin 0 -> 44 bytes .../84/22d40f12717e1ebd5cef2449f6c09d1f775969 | Bin 0 -> 23 bytes .../8c/7e5a667f1b771847fe88c01c3de34413a1b220 | Bin 0 -> 16 bytes .../d4/490327def9658be036d6a52c4417d84e74dd4c | Bin 0 -> 46 bytes .../ee/2b1253d9cf407796e2e724926cbe3a974b214d | 1 + .../tests/repo/repo1_filepreview/refs/heads/master | 1 + modules/mcaptcha/mcaptcha.go | 26 + modules/metrics/collector.go | 388 +++ modules/migration/comment.go | 34 + modules/migration/downloader.go | 37 + modules/migration/error.go | 25 + modules/migration/file_format.go | 110 + modules/migration/file_format_test.go | 39 + .../migration/file_format_testdata/issue_a.json | 14 + modules/migration/file_format_testdata/issue_a.yml | 10 + .../migration/file_format_testdata/issue_b.json | 5 + .../migration/file_format_testdata/milestones.json | 20 + modules/migration/issue.go | 48 + modules/migration/label.go | 13 + modules/migration/messenger.go | 10 + modules/migration/milestone.go | 18 + modules/migration/null_downloader.go | 88 + modules/migration/options.go | 41 + modules/migration/pullrequest.go | 74 + modules/migration/reaction.go | 17 + modules/migration/release.go | 46 + modules/migration/repo.go | 17 + modules/migration/retry_downloader.go | 194 ++ modules/migration/review.go | 67 + modules/migration/schemas/issue.json | 114 + modules/migration/schemas/label.json | 28 + modules/migration/schemas/milestone.json | 67 + modules/migration/schemas/reaction.json | 29 + modules/migration/schemas_bindata.go | 8 + modules/migration/schemas_dynamic.go | 47 + modules/migration/schemas_static.go | 23 + modules/migration/uploader.go | 23 + modules/nosql/leveldb.go | 24 + modules/nosql/manager.go | 116 + modules/nosql/manager_leveldb.go | 214 ++ modules/nosql/manager_redis.go | 258 ++ modules/nosql/manager_redis_test.go | 81 + modules/nosql/redis.go | 100 + modules/nosql/redis_test.go | 34 + modules/optional/option.go | 45 + modules/optional/option_test.go | 59 + modules/optional/serialization.go | 46 + modules/optional/serialization_test.go | 191 ++ modules/options/base.go | 42 + modules/options/dynamic.go | 15 + modules/options/options_bindata.go | 8 + modules/options/static.go | 14 + modules/packages/alpine/metadata.go | 242 ++ modules/packages/alpine/metadata_test.go | 144 + modules/packages/arch/metadata.go | 341 ++ modules/packages/arch/metadata_test.go | 447 +++ modules/packages/cargo/parser.go | 178 + modules/packages/cargo/parser_test.go | 108 + modules/packages/chef/metadata.go | 134 + modules/packages/chef/metadata_test.go | 93 + modules/packages/composer/metadata.go | 187 ++ modules/packages/composer/metadata_test.go | 154 + modules/packages/conan/conanfile_parser.go | 67 + modules/packages/conan/conanfile_parser_test.go | 51 + modules/packages/conan/conaninfo_parser.go | 123 + modules/packages/conan/conaninfo_parser_test.go | 85 + modules/packages/conan/metadata.go | 23 + modules/packages/conan/reference.go | 155 + modules/packages/conan/reference_test.go | 148 + modules/packages/conda/metadata.go | 242 ++ modules/packages/conda/metadata_test.go | 152 + modules/packages/container/helm/helm.go | 55 + modules/packages/container/metadata.go | 166 + modules/packages/container/metadata_test.go | 62 + modules/packages/content_store.go | 75 + modules/packages/cran/metadata.go | 242 ++ modules/packages/cran/metadata_test.go | 153 + modules/packages/debian/metadata.go | 221 ++ modules/packages/debian/metadata_test.go | 187 ++ modules/packages/goproxy/metadata.go | 94 + modules/packages/goproxy/metadata_test.go | 76 + modules/packages/hashed_buffer.go | 81 + modules/packages/hashed_buffer_test.go | 47 + modules/packages/helm/metadata.go | 130 + modules/packages/maven/metadata.go | 93 + modules/packages/maven/metadata_test.go | 90 + modules/packages/multi_hasher.go | 122 + modules/packages/multi_hasher_test.go | 54 + modules/packages/npm/creator.go | 289 ++ modules/packages/npm/creator_test.go | 302 ++ modules/packages/npm/metadata.go | 26 + modules/packages/nuget/metadata.go | 239 ++ modules/packages/nuget/metadata_test.go | 188 ++ modules/packages/nuget/symbol_extractor.go | 186 ++ modules/packages/nuget/symbol_extractor_test.go | 82 + modules/packages/pub/metadata.go | 153 + modules/packages/pub/metadata_test.go | 136 + modules/packages/pypi/metadata.go | 15 + modules/packages/rpm/metadata.go | 298 ++ modules/packages/rpm/metadata_test.go | 164 + modules/packages/rubygems/marshal.go | 311 ++ modules/packages/rubygems/marshal_test.go | 99 + modules/packages/rubygems/metadata.go | 220 ++ modules/packages/rubygems/metadata_test.go | 89 + modules/packages/swift/metadata.go | 214 ++ modules/packages/swift/metadata_test.go | 145 + modules/packages/vagrant/metadata.go | 96 + modules/packages/vagrant/metadata_test.go | 111 + modules/paginator/paginator.go | 204 ++ modules/paginator/paginator_test.go | 312 ++ modules/pprof/pprof.go | 45 + modules/private/actions.go | 25 + modules/private/forgejo_actions.go | 32 + modules/private/hook.go | 129 + modules/private/internal.go | 96 + modules/private/key.go | 30 + modules/private/mail.go | 33 + modules/private/manager.go | 120 + modules/private/request.go | 128 + modules/private/restore_repo.go | 36 + modules/private/serv.go | 63 + modules/process/context.go | 68 + modules/process/error.go | 25 + modules/process/manager.go | 243 ++ modules/process/manager_exec.go | 79 + modules/process/manager_stacktraces.go | 353 ++ modules/process/manager_test.go | 111 + modules/process/manager_unix.go | 17 + modules/process/manager_windows.go | 15 + modules/process/process.go | 38 + modules/proxy/proxy.go | 98 + modules/proxyprotocol/conn.go | 505 +++ modules/proxyprotocol/errors.go | 44 + modules/proxyprotocol/listener.go | 46 + modules/proxyprotocol/util.go | 14 + modules/public/mime_types.go | 40 + modules/public/public.go | 118 + modules/public/public_bindata.go | 8 + modules/public/public_test.go | 34 + modules/public/serve_dynamic.go | 15 + modules/public/serve_static.go | 24 + modules/queue/backoff.go | 63 + modules/queue/base.go | 42 + modules/queue/base_channel.go | 131 + modules/queue/base_channel_test.go | 11 + modules/queue/base_dummy.go | 38 + modules/queue/base_levelqueue.go | 83 + modules/queue/base_levelqueue_common.go | 93 + modules/queue/base_levelqueue_test.go | 78 + modules/queue/base_levelqueue_unique.go | 88 + modules/queue/base_redis.go | 162 + modules/queue/base_redis_test.go | 138 + modules/queue/base_redis_with_server_test.go | 133 + modules/queue/base_test.go | 141 + modules/queue/config.go | 36 + modules/queue/lqinternal/lqinternal.go | 48 + modules/queue/manager.go | 113 + modules/queue/manager_test.go | 125 + modules/queue/mock/inmemorymockredis.go | 133 + modules/queue/mock/redisuniversalclient.go | 343 ++ modules/queue/queue.go | 68 + modules/queue/testhelper.go | 40 + modules/queue/workergroup.go | 350 ++ modules/queue/workerqueue.go | 260 ++ modules/queue/workerqueue_test.go | 291 ++ modules/recaptcha/recaptcha.go | 90 + modules/references/references.go | 594 ++++ modules/references/references_test.go | 563 ++++ modules/regexplru/regexplru.go | 44 + modules/regexplru/regexplru_test.go | 27 + modules/repository/branch.go | 145 + modules/repository/branch_test.go | 32 + modules/repository/collaborator.go | 44 + modules/repository/collaborator_test.go | 308 ++ modules/repository/commits.go | 173 + modules/repository/commits_test.go | 210 ++ modules/repository/create.go | 297 ++ modules/repository/create_test.go | 46 + modules/repository/delete.go | 33 + modules/repository/env.go | 87 + modules/repository/fork.go | 32 + modules/repository/hooks.go | 233 ++ modules/repository/init.go | 182 ++ modules/repository/init_test.go | 30 + modules/repository/license.go | 112 + modules/repository/license_test.go | 181 ++ modules/repository/main_test.go | 16 + modules/repository/push.go | 70 + modules/repository/repo.go | 388 +++ modules/repository/repo_test.go | 76 + modules/repository/temp.go | 45 + modules/secret/secret.go | 78 + modules/secret/secret_test.go | 32 + modules/session/db.go | 171 + modules/session/redis.go | 225 ++ modules/session/store.go | 29 + modules/session/virtual.go | 198 ++ modules/setting/actions.go | 106 + modules/setting/actions_test.go | 157 + modules/setting/admin.go | 32 + modules/setting/admin_test.go | 33 + modules/setting/api.go | 40 + modules/setting/asset_dynamic.go | 8 + modules/setting/asset_static.go | 8 + modules/setting/attachment.go | 35 + modules/setting/attachment_test.go | 134 + modules/setting/badges.go | 24 + modules/setting/cache.go | 85 + modules/setting/camo.go | 32 + modules/setting/config.go | 98 + modules/setting/config/getter.go | 49 + modules/setting/config/value.go | 94 + modules/setting/config_env.go | 170 + modules/setting/config_env_test.go | 151 + modules/setting/config_provider.go | 360 +++ modules/setting/config_provider_test.go | 157 + modules/setting/cors.go | 34 + modules/setting/cron.go | 32 + modules/setting/cron_test.go | 44 + modules/setting/database.go | 204 ++ modules/setting/database_sqlite.go | 15 + modules/setting/database_test.go | 109 + modules/setting/f3.go | 26 + modules/setting/federation.go | 51 + modules/setting/forgejo_storage_test.go | 264 ++ modules/setting/git.go | 123 + modules/setting/git_test.go | 66 + modules/setting/highlight.go | 17 + modules/setting/i18n.go | 68 + modules/setting/incoming_email.go | 89 + modules/setting/incoming_email_test.go | 74 + modules/setting/indexer.go | 119 + modules/setting/indexer_test.go | 71 + modules/setting/lfs.go | 82 + modules/setting/lfs_test.go | 102 + modules/setting/log.go | 270 ++ modules/setting/log_test.go | 386 +++ modules/setting/mailer.go | 309 ++ modules/setting/mailer_test.go | 54 + modules/setting/markup.go | 192 ++ modules/setting/metrics.go | 21 + modules/setting/migrations.go | 28 + modules/setting/mime_type_map.go | 28 + modules/setting/mirror.go | 58 + modules/setting/oauth2.go | 174 + modules/setting/oauth2_test.go | 61 + modules/setting/other.go | 29 + modules/setting/packages.go | 124 + modules/setting/packages_test.go | 199 ++ modules/setting/path.go | 214 ++ modules/setting/path_test.go | 243 ++ modules/setting/picture.go | 109 + modules/setting/project.go | 19 + modules/setting/proxy.go | 37 + modules/setting/queue.go | 120 + modules/setting/quota.go | 26 + modules/setting/repository.go | 376 +++ modules/setting/repository_archive.go | 25 + modules/setting/repository_archive_test.go | 112 + modules/setting/security.go | 173 + modules/setting/server.go | 368 +++ modules/setting/server_test.go | 36 + modules/setting/service.go | 262 ++ modules/setting/service_test.go | 133 + modules/setting/session.go | 78 + modules/setting/setting.go | 238 ++ modules/setting/setting_test.go | 32 + modules/setting/ssh.go | 197 ++ modules/setting/storage.go | 275 ++ modules/setting/storage_test.go | 468 +++ modules/setting/task.go | 26 + modules/setting/time.go | 28 + modules/setting/ui.go | 170 + modules/setting/webhook.go | 48 + modules/sitemap/sitemap.go | 82 + modules/sitemap/sitemap_test.go | 167 + modules/ssh/init.go | 55 + modules/ssh/ssh.go | 388 +++ modules/ssh/ssh_graceful.go | 34 + modules/storage/helper.go | 39 + modules/storage/helper_test.go | 51 + modules/storage/local.go | 154 + modules/storage/local_test.go | 61 + modules/storage/minio.go | 310 ++ modules/storage/minio_test.go | 216 ++ modules/storage/storage.go | 226 ++ modules/storage/storage_test.go | 52 + modules/storage/testdata/aws_credentials | 3 + modules/storage/testdata/minio.json | 12 + modules/structs/activity.go | 25 + modules/structs/activitypub.go | 9 + modules/structs/admin_user.go | 53 + modules/structs/attachment.go | 31 + modules/structs/commit_status.go | 73 + modules/structs/commit_status_test.go | 174 + modules/structs/cron.go | 15 + modules/structs/doc.go | 4 + modules/structs/fork.go | 12 + modules/structs/git_blob.go | 13 + modules/structs/git_hook.go | 19 + modules/structs/hook.go | 520 +++ modules/structs/issue.go | 269 ++ modules/structs/issue_comment.go | 86 + modules/structs/issue_label.go | 75 + modules/structs/issue_milestone.go | 44 + modules/structs/issue_reaction.go | 21 + modules/structs/issue_stopwatch.go | 23 + modules/structs/issue_test.go | 106 + modules/structs/issue_tracked_time.go | 37 + modules/structs/lfs_lock.go | 64 + modules/structs/mirror.go | 32 + modules/structs/miscellaneous.go | 101 + modules/structs/moderation.go | 13 + modules/structs/nodeinfo.go | 43 + modules/structs/notifications.go | 49 + modules/structs/org.go | 59 + modules/structs/org_member.go | 9 + modules/structs/org_team.go | 54 + modules/structs/package.go | 33 + modules/structs/pull.go | 119 + modules/structs/pull_review.go | 111 + modules/structs/quota.go | 163 + modules/structs/release.go | 55 + modules/structs/repo.go | 421 +++ modules/structs/repo_actions.go | 34 + modules/structs/repo_branch.go | 112 + modules/structs/repo_collaborator.go | 17 + modules/structs/repo_commit.go | 73 + modules/structs/repo_compare.go | 10 + modules/structs/repo_file.go | 172 + modules/structs/repo_flags.go | 9 + modules/structs/repo_key.go | 40 + modules/structs/repo_note.go | 10 + modules/structs/repo_refs.go | 18 + modules/structs/repo_tag.go | 76 + modules/structs/repo_topic.go | 28 + modules/structs/repo_tree.go | 24 + modules/structs/repo_watch.go | 18 + modules/structs/repo_wiki.go | 46 + modules/structs/secret.go | 24 + modules/structs/settings.go | 38 + modules/structs/status.go | 42 + modules/structs/task.go | 31 + modules/structs/user.go | 120 + modules/structs/user_app.go | 53 + modules/structs/user_email.go | 27 + modules/structs/user_gpgkey.go | 53 + modules/structs/user_key.go | 22 + modules/structs/variable.go | 37 + modules/structs/visible_type.go | 58 + modules/structs/workflow.go | 15 + modules/svg/processor.go | 59 + modules/svg/processor_test.go | 29 + modules/svg/svg.go | 59 + modules/sync/exclusive_pool.go | 69 + modules/sync/status_pool.go | 57 + modules/sync/status_pool_test.go | 31 + modules/system/appstate.go | 26 + modules/system/appstate_test.go | 66 + modules/system/db.go | 36 + modules/system/item_runtime.go | 15 + modules/templates/base.go | 40 + modules/templates/dynamic.go | 15 + modules/templates/eval/eval.go | 344 ++ modules/templates/eval/eval_test.go | 94 + modules/templates/helper.go | 269 ++ modules/templates/helper_test.go | 67 + modules/templates/htmlrenderer.go | 287 ++ modules/templates/htmlrenderer_test.go | 107 + modules/templates/mailer.go | 110 + modules/templates/main_test.go | 24 + modules/templates/scopedtmpl/scopedtmpl.go | 239 ++ modules/templates/scopedtmpl/scopedtmpl_test.go | 99 + modules/templates/static.go | 22 + modules/templates/templates_bindata.go | 8 + modules/templates/util_avatar.go | 81 + modules/templates/util_dict.go | 121 + modules/templates/util_json.go | 35 + modules/templates/util_misc.go | 193 ++ modules/templates/util_render.go | 264 ++ modules/templates/util_render_test.go | 223 ++ modules/templates/util_slice.go | 35 + modules/templates/util_string.go | 68 + modules/templates/util_string_test.go | 20 + modules/templates/util_test.go | 79 + modules/templates/vars/vars.go | 92 + modules/templates/vars/vars_test.go | 72 + modules/test/logchecker.go | 107 + modules/test/logchecker_test.go | 58 + modules/test/utils.go | 48 + modules/test/utils_test.go | 18 + modules/testlogger/testlogger.go | 580 ++++ modules/timeutil/datetime.go | 68 + modules/timeutil/datetime_test.go | 47 + modules/timeutil/executable.go | 50 + modules/timeutil/since.go | 145 + modules/timeutil/since_test.go | 87 + modules/timeutil/timestamp.go | 100 + modules/timeutil/timestampnano.go | 28 + modules/translation/i18n/errors.go | 13 + modules/translation/i18n/format.go | 41 + modules/translation/i18n/i18n.go | 50 + modules/translation/i18n/i18n_test.go | 204 ++ modules/translation/i18n/localestore.go | 166 + modules/translation/mock.go | 40 + modules/translation/translation.go | 303 ++ modules/translation/translation_test.go | 50 + modules/turnstile/turnstile.go | 92 + modules/typesniffer/typesniffer.go | 143 + modules/typesniffer/typesniffer_test.go | 137 + modules/updatechecker/update_checker.go | 143 + modules/updatechecker/update_checker_test.go | 17 + modules/uri/uri.go | 42 + modules/uri/uri_test.go | 19 + modules/user/user.go | 35 + modules/user/user_test.go | 43 + modules/util/color.go | 57 + modules/util/color_test.go | 63 + modules/util/error.go | 65 + modules/util/file_unix.go | 27 + modules/util/file_unix_test.go | 36 + modules/util/file_windows.go | 15 + modules/util/filebuffer/file_backed_buffer.go | 156 + modules/util/filebuffer/file_backed_buffer_test.go | 36 + modules/util/io.go | 78 + modules/util/io_test.go | 67 + modules/util/keypair.go | 57 + modules/util/keypair_test.go | 62 + modules/util/legacy.go | 38 + modules/util/legacy_test.go | 38 + modules/util/pack.go | 33 + modules/util/pack_test.go | 28 + modules/util/paginate.go | 33 + modules/util/paginate_test.go | 46 + modules/util/path.go | 322 ++ modules/util/path_test.go | 213 ++ modules/util/remove.go | 104 + modules/util/rotatingfilewriter/writer.go | 246 ++ modules/util/rotatingfilewriter/writer_test.go | 49 + modules/util/sanitize.go | 72 + modules/util/sanitize_test.go | 74 + modules/util/sec_to_time.go | 81 + modules/util/sec_to_time_test.go | 30 + modules/util/shellquote.go | 101 + modules/util/shellquote_test.go | 91 + modules/util/slice.go | 73 + modules/util/slice_test.go | 55 + modules/util/string.go | 97 + modules/util/string_test.go | 47 + modules/util/timer.go | 36 + modules/util/timer_test.go | 30 + modules/util/truncate.go | 54 + modules/util/truncate_test.go | 46 + modules/util/url.go | 50 + modules/util/util.go | 264 ++ modules/util/util_test.go | 277 ++ modules/validation/binding.go | 209 ++ modules/validation/binding_test.go | 62 + modules/validation/glob_pattern_test.go | 61 + modules/validation/helpers.go | 136 + modules/validation/helpers_test.go | 216 ++ modules/validation/refname_test.go | 265 ++ modules/validation/regex_pattern_test.go | 59 + modules/validation/validatable.go | 84 + modules/validation/validatable_test.go | 69 + modules/validation/validurl_test.go | 110 + modules/web/handler.go | 193 ++ modules/web/middleware/binding.go | 162 + modules/web/middleware/cookie.go | 85 + modules/web/middleware/data.go | 63 + modules/web/middleware/flash.go | 65 + modules/web/middleware/locale.go | 59 + modules/web/middleware/request.go | 14 + modules/web/route.go | 211 ++ modules/web/route_test.go | 179 + modules/web/routemock.go | 61 + modules/web/routemock_test.go | 71 + modules/web/routing/context.go | 49 + modules/web/routing/funcinfo.go | 172 + modules/web/routing/funcinfo_test.go | 80 + modules/web/routing/logger.go | 109 + modules/web/routing/logger_manager.go | 124 + modules/web/routing/requestrecord.go | 28 + modules/web/types/response.go | 10 + modules/webhook/structs.go | 39 + modules/webhook/type.go | 100 + modules/zstd/option.go | 46 + modules/zstd/zstd.go | 163 + modules/zstd/zstd_test.go | 304 ++ 1114 files changed, 109798 insertions(+) create mode 100644 modules/actions/github.go create mode 100644 modules/actions/github_test.go create mode 100644 modules/actions/log.go create mode 100644 modules/actions/task_state.go create mode 100644 modules/actions/task_state_test.go create mode 100644 modules/actions/workflows.go create mode 100644 modules/actions/workflows_test.go create mode 100644 modules/activitypub/client.go create mode 100644 modules/activitypub/client_test.go create mode 100644 modules/activitypub/main_test.go create mode 100644 modules/activitypub/user_settings.go create mode 100644 modules/activitypub/user_settings_test.go create mode 100644 modules/analyze/code_language.go create mode 100644 modules/analyze/generated.go create mode 100644 modules/analyze/vendor.go create mode 100644 modules/analyze/vendor_test.go create mode 100644 modules/assetfs/layered.go create mode 100644 modules/assetfs/layered_test.go create mode 100644 modules/auth/common.go create mode 100644 modules/auth/openid/discovery_cache.go create mode 100644 modules/auth/openid/discovery_cache_test.go create mode 100644 modules/auth/openid/openid.go create mode 100644 modules/auth/pam/pam.go create mode 100644 modules/auth/pam/pam_stub.go create mode 100644 modules/auth/pam/pam_test.go create mode 100644 modules/auth/password/hash/argon2.go create mode 100644 modules/auth/password/hash/bcrypt.go create mode 100644 modules/auth/password/hash/common.go create mode 100644 modules/auth/password/hash/dummy.go create mode 100644 modules/auth/password/hash/dummy_test.go create mode 100644 modules/auth/password/hash/hash.go create mode 100644 modules/auth/password/hash/hash_test.go create mode 100644 modules/auth/password/hash/pbkdf2.go create mode 100644 modules/auth/password/hash/scrypt.go create mode 100644 modules/auth/password/hash/setting.go create mode 100644 modules/auth/password/hash/setting_test.go create mode 100644 modules/auth/password/password.go create mode 100644 modules/auth/password/password_test.go create mode 100644 modules/auth/password/pwn.go create mode 100644 modules/auth/password/pwn/pwn.go create mode 100644 modules/auth/password/pwn/pwn_test.go create mode 100644 modules/auth/webauthn/webauthn.go create mode 100644 modules/auth/webauthn/webauthn_test.go create mode 100644 modules/avatar/avatar.go create mode 100644 modules/avatar/avatar_test.go create mode 100644 modules/avatar/hash.go create mode 100644 modules/avatar/hash_test.go create mode 100644 modules/avatar/identicon/block.go create mode 100644 modules/avatar/identicon/colors.go create mode 100644 modules/avatar/identicon/identicon.go create mode 100644 modules/avatar/identicon/identicon_test.go create mode 100644 modules/avatar/identicon/polygon.go create mode 100644 modules/avatar/identicon/testdata/.gitignore create mode 100644 modules/avatar/testdata/animated.webp create mode 100644 modules/avatar/testdata/avatar.jpeg create mode 100644 modules/avatar/testdata/avatar.png create mode 100644 modules/base/base.go create mode 100644 modules/base/natural_sort.go create mode 100644 modules/base/natural_sort_test.go create mode 100644 modules/base/tool.go create mode 100644 modules/base/tool_test.go create mode 100644 modules/cache/cache.go create mode 100644 modules/cache/cache_redis.go create mode 100644 modules/cache/cache_test.go create mode 100644 modules/cache/cache_twoqueue.go create mode 100644 modules/cache/context.go create mode 100644 modules/cache/context_test.go create mode 100644 modules/charset/ambiguous.go create mode 100644 modules/charset/ambiguous/ambiguous.json create mode 100644 modules/charset/ambiguous/generate.go create mode 100644 modules/charset/ambiguous_gen.go create mode 100644 modules/charset/ambiguous_gen_test.go create mode 100644 modules/charset/breakwriter.go create mode 100644 modules/charset/breakwriter_test.go create mode 100644 modules/charset/charset.go create mode 100644 modules/charset/charset_test.go create mode 100644 modules/charset/escape.go create mode 100644 modules/charset/escape_status.go create mode 100644 modules/charset/escape_stream.go create mode 100644 modules/charset/escape_test.go create mode 100644 modules/charset/htmlstream.go create mode 100644 modules/charset/invisible/generate.go create mode 100644 modules/charset/invisible_gen.go create mode 100644 modules/container/filter.go create mode 100644 modules/container/filter_test.go create mode 100644 modules/container/set.go create mode 100644 modules/container/set_test.go create mode 100644 modules/csv/csv.go create mode 100644 modules/csv/csv_test.go create mode 100644 modules/emoji/emoji.go create mode 100644 modules/emoji/emoji_data.go create mode 100644 modules/emoji/emoji_test.go create mode 100644 modules/eventsource/event.go create mode 100644 modules/eventsource/event_test.go create mode 100644 modules/eventsource/manager.go create mode 100644 modules/eventsource/manager_run.go create mode 100644 modules/eventsource/messenger.go create mode 100644 modules/forgefed/activity.go create mode 100644 modules/forgefed/activity_test.go create mode 100644 modules/forgefed/actor.go create mode 100644 modules/forgefed/actor_test.go create mode 100644 modules/forgefed/forgefed.go create mode 100644 modules/forgefed/nodeinfo.go create mode 100644 modules/forgefed/repository.go create mode 100644 modules/forgefed/repository_test.go create mode 100644 modules/generate/generate.go create mode 100644 modules/generate/generate_test.go create mode 100644 modules/git/README.md create mode 100644 modules/git/batch.go create mode 100644 modules/git/batch_reader.go create mode 100644 modules/git/blame.go create mode 100644 modules/git/blame_sha256_test.go create mode 100644 modules/git/blame_test.go create mode 100644 modules/git/blob.go create mode 100644 modules/git/blob_test.go create mode 100644 modules/git/command.go create mode 100644 modules/git/command_race_test.go create mode 100644 modules/git/command_test.go create mode 100644 modules/git/commit.go create mode 100644 modules/git/commit_info.go create mode 100644 modules/git/commit_info_test.go create mode 100644 modules/git/commit_reader.go create mode 100644 modules/git/commit_sha256_test.go create mode 100644 modules/git/commit_test.go create mode 100644 modules/git/diff.go create mode 100644 modules/git/diff_test.go create mode 100644 modules/git/error.go create mode 100644 modules/git/foreachref/format.go create mode 100644 modules/git/foreachref/format_test.go create mode 100644 modules/git/foreachref/parser.go create mode 100644 modules/git/foreachref/parser_test.go create mode 100644 modules/git/git.go create mode 100644 modules/git/git_test.go create mode 100644 modules/git/grep.go create mode 100644 modules/git/grep_test.go create mode 100644 modules/git/hook.go create mode 100644 modules/git/internal/cmdarg.go create mode 100644 modules/git/last_commit_cache.go create mode 100644 modules/git/log_name_status.go create mode 100644 modules/git/notes.go create mode 100644 modules/git/notes_test.go create mode 100644 modules/git/object_format.go create mode 100644 modules/git/object_id.go create mode 100644 modules/git/object_id_test.go create mode 100644 modules/git/object_signature.go create mode 100644 modules/git/parse.go create mode 100644 modules/git/parse_test.go create mode 100644 modules/git/pipeline/catfile.go create mode 100644 modules/git/pipeline/lfs.go create mode 100644 modules/git/pipeline/namerev.go create mode 100644 modules/git/pipeline/revlist.go create mode 100644 modules/git/pushoptions/pushoptions.go create mode 100644 modules/git/pushoptions/pushoptions_test.go create mode 100644 modules/git/ref.go create mode 100644 modules/git/ref_test.go create mode 100644 modules/git/remote.go create mode 100644 modules/git/repo.go create mode 100644 modules/git/repo_archive.go create mode 100644 modules/git/repo_attribute.go create mode 100644 modules/git/repo_attribute_test.go create mode 100644 modules/git/repo_base.go create mode 100644 modules/git/repo_base_test.go create mode 100644 modules/git/repo_blame.go create mode 100644 modules/git/repo_blob_test.go create mode 100644 modules/git/repo_branch.go create mode 100644 modules/git/repo_branch_test.go create mode 100644 modules/git/repo_commit.go create mode 100644 modules/git/repo_commit_test.go create mode 100644 modules/git/repo_commitgraph.go create mode 100644 modules/git/repo_compare.go create mode 100644 modules/git/repo_compare_test.go create mode 100644 modules/git/repo_gpg.go create mode 100644 modules/git/repo_hook.go create mode 100644 modules/git/repo_index.go create mode 100644 modules/git/repo_language_stats.go create mode 100644 modules/git/repo_language_stats_test.go create mode 100644 modules/git/repo_object.go create mode 100644 modules/git/repo_ref.go create mode 100644 modules/git/repo_ref_test.go create mode 100644 modules/git/repo_stats.go create mode 100644 modules/git/repo_stats_test.go create mode 100644 modules/git/repo_tag.go create mode 100644 modules/git/repo_tag_test.go create mode 100644 modules/git/repo_test.go create mode 100644 modules/git/repo_tree.go create mode 100644 modules/git/signature.go create mode 100644 modules/git/signature_test.go create mode 100644 modules/git/submodule.go create mode 100644 modules/git/submodule_test.go create mode 100644 modules/git/tag.go create mode 100644 modules/git/tag_test.go create mode 100644 modules/git/tests/repos/language_stats_repo/COMMIT_EDITMSG create mode 100644 modules/git/tests/repos/language_stats_repo/HEAD create mode 100644 modules/git/tests/repos/language_stats_repo/config create mode 100644 modules/git/tests/repos/language_stats_repo/description create mode 100644 modules/git/tests/repos/language_stats_repo/index create mode 100644 modules/git/tests/repos/language_stats_repo/info/exclude create mode 100644 modules/git/tests/repos/language_stats_repo/logs/HEAD create mode 100644 modules/git/tests/repos/language_stats_repo/logs/refs/heads/master create mode 100644 modules/git/tests/repos/language_stats_repo/objects/1e/ea60592b55dcb45c36029cc1202132e9fb756c create mode 100644 modules/git/tests/repos/language_stats_repo/objects/22/b6aa0588563508d8879f062470c8cbc7b2f2bb create mode 100644 modules/git/tests/repos/language_stats_repo/objects/34/1fca5b5ea3de596dc483e54c2db28633cd2f97 create mode 100644 modules/git/tests/repos/language_stats_repo/objects/42/25ecfaf6bafbcfa31ea5cbd8121c36d9457085 create mode 100644 modules/git/tests/repos/language_stats_repo/objects/4a/c803638e4b8995146e329a05e096fa2c77a03d create mode 100644 modules/git/tests/repos/language_stats_repo/objects/64/4c37ad7fe64ac012df7e59d27a92e3137c640e create mode 100644 modules/git/tests/repos/language_stats_repo/objects/6c/633a0067b463e459ae952716b17ae36aa30adc create mode 100644 modules/git/tests/repos/language_stats_repo/objects/8e/b563dc106e3dfd3ad0fa81f7a0c5e2604f80cd create mode 100644 modules/git/tests/repos/language_stats_repo/objects/8f/ee858da5796dfb37704761701bb8e800ad9ef3 create mode 100644 modules/git/tests/repos/language_stats_repo/objects/aa/a21bf84c8b2304608d3fc83b747840f2456299 create mode 100644 modules/git/tests/repos/language_stats_repo/objects/da/a5abe3c5f42cae598e362e8a8db6284565d6bb create mode 100644 modules/git/tests/repos/language_stats_repo/refs/heads/master create mode 100644 modules/git/tests/repos/repo1_bare/HEAD create mode 100644 modules/git/tests/repos/repo1_bare/config create mode 100644 modules/git/tests/repos/repo1_bare/description create mode 100644 modules/git/tests/repos/repo1_bare/index create mode 100644 modules/git/tests/repos/repo1_bare/info/exclude create mode 100644 modules/git/tests/repos/repo1_bare/logs/HEAD create mode 100644 modules/git/tests/repos/repo1_bare/logs/refs/heads/master create mode 100644 modules/git/tests/repos/repo1_bare/objects/0b/9f291245f6c596fd30bee925fe94fe0cbadd60 create mode 100644 modules/git/tests/repos/repo1_bare/objects/11/93ff46343f4f6a0522e2b28b871e905178c1f0 create mode 100644 modules/git/tests/repos/repo1_bare/objects/15/3f451b9ee7fa1da317ab17a127e9fd9d384310 create mode 100644 modules/git/tests/repos/repo1_bare/objects/18/4d49c75a0b202b1d2ea2fcb5861c329321fcd6 create mode 100644 modules/git/tests/repos/repo1_bare/objects/1c/91d130dc5fb75fd2d9f586a058650889cfe7fb create mode 100644 modules/git/tests/repos/repo1_bare/objects/21/6bf54c2f2e2916b830ebe09e8c58a6ed52d86b create mode 100644 modules/git/tests/repos/repo1_bare/objects/28/345b214c5967bd9cdd98cc7f88f2f1ac574e02 create mode 100644 modules/git/tests/repos/repo1_bare/objects/28/39944139e0de9737a044f78b0e4b40d989a9e3 create mode 100644 modules/git/tests/repos/repo1_bare/objects/28/b55526e7100924d864dd89e35c1ea62e7a5a32 create mode 100644 modules/git/tests/repos/repo1_bare/objects/2e/65efe2a145dda7ee51d1741299f848e5bf752e create mode 100644 modules/git/tests/repos/repo1_bare/objects/30/4c56b3bef33d0afeb8515ee803c839daf30ab8 create mode 100644 modules/git/tests/repos/repo1_bare/objects/34/d1da713bf7de1c535e1d7d3ca985afd84bc7e5 create mode 100644 modules/git/tests/repos/repo1_bare/objects/36/f97d9a96457e2bab511db30fe2db03893ebc64 create mode 100644 modules/git/tests/repos/repo1_bare/objects/37/991dec2c8e592043f47155ce4808d4580f9123 create mode 100644 modules/git/tests/repos/repo1_bare/objects/38/441bf2c4d4c27efff94728c9eb33266f44a702 create mode 100644 modules/git/tests/repos/repo1_bare/objects/3a/d28a9149a2864384548f3d17ed7f38014c9e8a create mode 100644 modules/git/tests/repos/repo1_bare/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 create mode 100644 modules/git/tests/repos/repo1_bare/objects/50/13716a9da8e66ea21059a84f1b4311424d2b7f create mode 100644 modules/git/tests/repos/repo1_bare/objects/59/dfb0bb505a601006e31fed53d2e24e44fca9ca create mode 100644 modules/git/tests/repos/repo1_bare/objects/5c/80b0245c1c6f8343fa418ec374b13b5d4ee658 create mode 100644 modules/git/tests/repos/repo1_bare/objects/62/d735f9efa9cf5b7df6bac9917b80e4779f4315 create mode 100644 modules/git/tests/repos/repo1_bare/objects/64/3a35374408002fcf2f0e8d42d262a1e0e2f80e create mode 100644 modules/git/tests/repos/repo1_bare/objects/6c/493ff740f9380390d5c9ddef4af18697ac9375 create mode 100644 modules/git/tests/repos/repo1_bare/objects/6f/bd69e9823458e6c4a2fc5c0f6bc022b2f2acd1 create mode 100644 modules/git/tests/repos/repo1_bare/objects/7e/3b688f3369ca28ebafbda9f8ef39713dd12fc8 create mode 100644 modules/git/tests/repos/repo1_bare/objects/80/06ff9adbf0cb94da7dad9e537e53817f9fa5c0 create mode 100644 modules/git/tests/repos/repo1_bare/objects/82/26f571dcc2d2f33a7179d929b10b9c39faa631 create mode 100644 modules/git/tests/repos/repo1_bare/objects/83/b9c4da46ed59098a009f8640c77eac97b71dfe create mode 100644 modules/git/tests/repos/repo1_bare/objects/8d/92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2 create mode 100644 modules/git/tests/repos/repo1_bare/objects/93/3305878a3c9ad485c29b87fb662a73a9675c4b create mode 100644 modules/git/tests/repos/repo1_bare/objects/95/bb4d39648ee7e325106df01a621c530863a653 create mode 100644 modules/git/tests/repos/repo1_bare/objects/98/1ff127cc331753bba28e1377c35934f1ca9b56 create mode 100644 modules/git/tests/repos/repo1_bare/objects/9c/9aef8dd84e02bc7ec12641deb4c930a7c30185 create mode 100644 modules/git/tests/repos/repo1_bare/objects/a4/79ead1abb694ffca26f67b09c8313b12fa2a13 create mode 100644 modules/git/tests/repos/repo1_bare/objects/b1/4df6442ea5a1b382985a6549b85d435376c351 create mode 100644 modules/git/tests/repos/repo1_bare/objects/b1/fc9917b618c924cf4aa421dae74e8bf9b556d3 create mode 100644 modules/git/tests/repos/repo1_bare/objects/b7/5f44edbd9252c32bf9faa0c1257ffb3b126c24 create mode 100644 modules/git/tests/repos/repo1_bare/objects/c8/c90111bdc18b3afd2b2906007059e95ac8fdc3 create mode 100644 modules/git/tests/repos/repo1_bare/objects/ca/6b5ddf303169a72d2a2971acde4f6eea194e5c create mode 100644 modules/git/tests/repos/repo1_bare/objects/ce/064814f4a0d337b333e646ece456cd39fab612 create mode 100644 modules/git/tests/repos/repo1_bare/objects/cf/8b0b492a950b358a7ce7f9d01b18aef48a6b2d create mode 100644 modules/git/tests/repos/repo1_bare/objects/d0/845fe2f85710b50d673dafe98236bf9f2023da create mode 100644 modules/git/tests/repos/repo1_bare/objects/e2/129701f1a4d54dc44f03c93bca0a2aec7c5449 create mode 100644 modules/git/tests/repos/repo1_bare/objects/f1/a6cb52b2d16773290cefe49ad0684b50a4f930 create mode 100644 modules/git/tests/repos/repo1_bare/objects/fe/af4ba6bc635fec442f46ddd4512416ec43c2c2 create mode 100644 modules/git/tests/repos/repo1_bare/pulls/1.patch create mode 100644 modules/git/tests/repos/repo1_bare/pulls/2.patch create mode 100644 modules/git/tests/repos/repo1_bare/refs/heads/branch1 create mode 100644 modules/git/tests/repos/repo1_bare/refs/heads/branch2 create mode 100644 modules/git/tests/repos/repo1_bare/refs/heads/master create mode 100644 modules/git/tests/repos/repo1_bare/refs/notes/commits create mode 100644 modules/git/tests/repos/repo1_bare/refs/tags/signed-tag create mode 100644 modules/git/tests/repos/repo1_bare/refs/tags/test create mode 100644 modules/git/tests/repos/repo1_bare_sha256/HEAD create mode 100644 modules/git/tests/repos/repo1_bare_sha256/config create mode 100644 modules/git/tests/repos/repo1_bare_sha256/description create mode 100644 modules/git/tests/repos/repo1_bare_sha256/info/exclude create mode 100644 modules/git/tests/repos/repo1_bare_sha256/info/refs create mode 100644 modules/git/tests/repos/repo1_bare_sha256/objects/info/commit-graph create mode 100644 modules/git/tests/repos/repo1_bare_sha256/objects/info/packs create mode 100644 modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.bitmap create mode 100644 modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.idx create mode 100644 modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.pack create mode 100644 modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.rev create mode 100644 modules/git/tests/repos/repo1_bare_sha256/packed-refs create mode 100644 modules/git/tests/repos/repo1_bare_sha256/refs/heads/main create mode 100644 modules/git/tests/repos/repo2_empty/HEAD create mode 100644 modules/git/tests/repos/repo2_empty/config create mode 100644 modules/git/tests/repos/repo2_empty/description create mode 100644 modules/git/tests/repos/repo2_empty/info/exclude create mode 100644 modules/git/tests/repos/repo2_empty/objects/info/.gitkeep create mode 100644 modules/git/tests/repos/repo2_empty/objects/pack/.gitkeep create mode 100644 modules/git/tests/repos/repo2_empty/refs/heads/.gitkeep create mode 100644 modules/git/tests/repos/repo2_empty/refs/tags/.gitkeep create mode 100644 modules/git/tests/repos/repo3_notes/COMMIT_EDITMSG create mode 100644 modules/git/tests/repos/repo3_notes/HEAD create mode 100644 modules/git/tests/repos/repo3_notes/config create mode 100644 modules/git/tests/repos/repo3_notes/description create mode 100644 modules/git/tests/repos/repo3_notes/index create mode 100644 modules/git/tests/repos/repo3_notes/logs/HEAD create mode 100644 modules/git/tests/repos/repo3_notes/logs/refs/heads/master create mode 100644 modules/git/tests/repos/repo3_notes/objects/29/7128d6553180486c780e2f747cb6d0014bf1f6 create mode 100644 modules/git/tests/repos/repo3_notes/objects/2f/7e2ea1e905c14c8a98e7ce47b395592834b9ef create mode 100644 modules/git/tests/repos/repo3_notes/objects/3e/668dbfac39cbc80a9ff9c61eb565d944453ba4 create mode 100644 modules/git/tests/repos/repo3_notes/objects/42/716fdb6f261867472899d785123e6ecaa5ca02 create mode 100644 modules/git/tests/repos/repo3_notes/objects/56/a6051ca2b02b04ef92d5150c9ef600403cb1de create mode 100644 modules/git/tests/repos/repo3_notes/objects/61/6c62e75fce60d806f4afe993211705a00a2544 create mode 100644 modules/git/tests/repos/repo3_notes/objects/65/4c8b6b63c08bf37f638d3f521626b7fbbd4d37 create mode 100644 modules/git/tests/repos/repo3_notes/objects/ba/0a96fa63532d6c5087ecef070b0250ed72fa47 create mode 100644 modules/git/tests/repos/repo3_notes/objects/c9/34d51cee361fdee21a3f3bb1a285f5ea9bc225 create mode 100644 modules/git/tests/repos/repo3_notes/objects/d8/263ee9860594d2806b0dfd1bfd17528b0ba2a4 create mode 100644 modules/git/tests/repos/repo3_notes/objects/f3/6ad903e408cb8f4ed90bda02e3a1fd2fab7907 create mode 100644 modules/git/tests/repos/repo3_notes/objects/fe/c9fe57e9864fe537f02f825e377c4a8a65ad2e create mode 100644 modules/git/tests/repos/repo3_notes/refs/heads/master create mode 100644 modules/git/tests/repos/repo3_notes/refs/notes/commits create mode 100644 modules/git/tests/repos/repo4_commitsbetween/HEAD create mode 100644 modules/git/tests/repos/repo4_commitsbetween/config create mode 100644 modules/git/tests/repos/repo4_commitsbetween/logs/HEAD create mode 100644 modules/git/tests/repos/repo4_commitsbetween/logs/refs/heads/main create mode 100644 modules/git/tests/repos/repo4_commitsbetween/objects/27/734c860ab19650d48e71f9f12d9bd194ed82ea create mode 100644 modules/git/tests/repos/repo4_commitsbetween/objects/56/a6051ca2b02b04ef92d5150c9ef600403cb1de create mode 100644 modules/git/tests/repos/repo4_commitsbetween/objects/78/a445db1eac62fe15e624e1137965969addf344 create mode 100644 modules/git/tests/repos/repo4_commitsbetween/objects/a7/8e5638b66ccfe7e1b4689d3d5684e42c97d7ca create mode 100644 modules/git/tests/repos/repo4_commitsbetween/objects/ad/74ceca1b8fde10c7d933bd2e56d347dddb4ab5 create mode 100644 modules/git/tests/repos/repo4_commitsbetween/objects/b5/d8dd0ddd9d8d752bb47b5f781f09f478316098 create mode 100644 modules/git/tests/repos/repo4_commitsbetween/objects/d8/263ee9860594d2806b0dfd1bfd17528b0ba2a4 create mode 100644 modules/git/tests/repos/repo4_commitsbetween/objects/e2/3cc6a008501f1491b0480cedaef160e41cf684 create mode 100644 modules/git/tests/repos/repo4_commitsbetween/objects/fd/c1b615bdcff0f0658b216df0c9209e5ecb7c78 create mode 100644 modules/git/tests/repos/repo4_commitsbetween/refs/heads/main create mode 100644 modules/git/tests/repos/repo5_pulls/HEAD create mode 100644 modules/git/tests/repos/repo5_pulls/config create mode 100644 modules/git/tests/repos/repo5_pulls/description create mode 100644 modules/git/tests/repos/repo5_pulls/info/exclude create mode 100644 modules/git/tests/repos/repo5_pulls/objects/1a/2959532d2d18daa87bbd9f9d16051bef7b51df create mode 100644 modules/git/tests/repos/repo5_pulls/objects/56/51a1c4a48c47484a7a00a967ba4b6dde070bbf create mode 100644 modules/git/tests/repos/repo5_pulls/objects/58/a4bcc53ac13e7ff76127e0fb518b5262bf09af create mode 100644 modules/git/tests/repos/repo5_pulls/objects/6d/0b4cca434953833618fcd3dd7acff42c800df1 create mode 100644 modules/git/tests/repos/repo5_pulls/objects/a5/2ca5af1b0277638ce20797f80bb1a2997470ab create mode 100644 modules/git/tests/repos/repo5_pulls/objects/bf/4dc0709be60f043821351ff4bb2b17e5cabbb2 create mode 100644 modules/git/tests/repos/repo5_pulls/objects/d8/e0bbb45f200e67d9a784ce55bd90821af45ebd create mode 100644 modules/git/tests/repos/repo5_pulls/objects/ed/5119b3c1f45547b6785bc03eac7f87570fa17f create mode 100644 modules/git/tests/repos/repo5_pulls/objects/ed/8f4d2fa5b2420706580d191f5dd50c4e491f3f create mode 100644 modules/git/tests/repos/repo5_pulls/objects/ee/469963e76ae1bb7ee83d7510df2864e6c8c640 create mode 100644 modules/git/tests/repos/repo5_pulls/objects/info/packs create mode 100644 modules/git/tests/repos/repo5_pulls/objects/pack/pack-81423f591973f5d9dab89cc45afa1c544448133e.idx create mode 100644 modules/git/tests/repos/repo5_pulls/objects/pack/pack-81423f591973f5d9dab89cc45afa1c544448133e.pack create mode 100644 modules/git/tests/repos/repo5_pulls/packed-refs create mode 100644 modules/git/tests/repos/repo5_pulls/refs/heads/master create mode 100644 modules/git/tests/repos/repo5_pulls/refs/heads/master-clone create mode 100644 modules/git/tests/repos/repo5_pulls/refs/heads/test-patch-1 create mode 100644 modules/git/tests/repos/repo5_pulls/refs/pull/4/head create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/HEAD create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/config create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/description create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/info/refs create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/objects/info/commit-graph create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/objects/info/packs create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.bitmap create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.idx create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.pack create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.rev create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/packed-refs create mode 100644 modules/git/tests/repos/repo5_pulls_sha256/refs/heads/main create mode 100644 modules/git/tests/repos/repo6_blame/HEAD create mode 100644 modules/git/tests/repos/repo6_blame/config create mode 100644 modules/git/tests/repos/repo6_blame/objects/31/bb4b42cecf0a98fc9a32fc5aaeaf53ec52643c create mode 100644 modules/git/tests/repos/repo6_blame/objects/3b/0f66d8b065f8adbf2fef7d986528c655b98cb1 create mode 100644 modules/git/tests/repos/repo6_blame/objects/45/fb6cbc12f970b04eacd5cd4165edd11c8d7376 create mode 100644 modules/git/tests/repos/repo6_blame/objects/49/7701e5bb8676e419b93875d8f0808c7b31aed9 create mode 100644 modules/git/tests/repos/repo6_blame/objects/54/4d8f7a3b15927cddf2299b4b562d6ebd71b6a7 create mode 100644 modules/git/tests/repos/repo6_blame/objects/a8/9199e8dea077e4a8ba0bc01bc155275cfdd044 create mode 100644 modules/git/tests/repos/repo6_blame/objects/af/7486bd54cfc39eea97207ca666aa69c9d6df93 create mode 100644 modules/git/tests/repos/repo6_blame/objects/b8/d1ba1ccb58ee3744b3d1434aae7d26ce2d9421 create mode 100644 modules/git/tests/repos/repo6_blame/objects/ca/411a3b842c3caec045772da42de16b3ffdafe8 create mode 100644 modules/git/tests/repos/repo6_blame/refs/heads/master create mode 100644 modules/git/tests/repos/repo6_blame_sha256/HEAD create mode 100644 modules/git/tests/repos/repo6_blame_sha256/config create mode 100644 modules/git/tests/repos/repo6_blame_sha256/description create mode 100644 modules/git/tests/repos/repo6_blame_sha256/info/exclude create mode 100644 modules/git/tests/repos/repo6_blame_sha256/info/refs create mode 100644 modules/git/tests/repos/repo6_blame_sha256/objects/info/commit-graph create mode 100644 modules/git/tests/repos/repo6_blame_sha256/objects/info/packs create mode 100644 modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.bitmap create mode 100644 modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.idx create mode 100644 modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.pack create mode 100644 modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.rev create mode 100644 modules/git/tests/repos/repo6_blame_sha256/packed-refs create mode 100644 modules/git/tests/repos/repo6_blame_sha256/refs/refs/main create mode 100644 modules/git/tests/repos/repo6_merge/HEAD create mode 100644 modules/git/tests/repos/repo6_merge/objects/02/2f4ce6214973e018f02bf363bf8a2e3691f699 create mode 100644 modules/git/tests/repos/repo6_merge/objects/05/45879290cc368a8becebc4aa34002c52d5fecc create mode 100644 modules/git/tests/repos/repo6_merge/objects/1e/5d0a65fe099ef12d24b28f783896e4b8172576 create mode 100644 modules/git/tests/repos/repo6_merge/objects/37/d35c7ed39e4e16d0b579a5b995b7e30b0e9411 create mode 100644 modules/git/tests/repos/repo6_merge/objects/38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 create mode 100644 modules/git/tests/repos/repo6_merge/objects/66/7e0fbc6bc02c2285d17f542e89b23c0fa5482b create mode 100644 modules/git/tests/repos/repo6_merge/objects/9f/d90b1d524c0fea776ed5e6476da02ea1740597 create mode 100644 modules/git/tests/repos/repo6_merge/objects/ae/4b035e7c4afbc000576cee3f713ea0c2f1e1e2 create mode 100644 modules/git/tests/repos/repo6_merge/objects/ba/2906d0666cf726c7eaadd2cd3db615dedfdf3a create mode 100644 modules/git/tests/repos/repo6_merge/objects/c1/a95c2eff8151c6d1437a0d5d3322a73ff38fb8 create mode 100644 modules/git/tests/repos/repo6_merge/objects/cc/d1d4d594029e68c388ecef5aa3063fa1055831 create mode 100644 modules/git/tests/repos/repo6_merge/objects/cd/fc1aaf7a149151cb7bff639fafe05668d4bbd2 create mode 100644 modules/git/tests/repos/repo6_merge/objects/d1/792641396ff7630d35fbb0b74b86b0c71bca77 create mode 100644 modules/git/tests/repos/repo6_merge/objects/ec/d11d8da0f25eaa99f64a37a82da98685f381e2 create mode 100644 modules/git/tests/repos/repo6_merge/objects/fa/49b077972391ad58037050f2a75f74e3671e92 create mode 100644 modules/git/tests/repos/repo6_merge/refs/heads/main create mode 100644 modules/git/tests/repos/repo6_merge/refs/heads/merge/add_file create mode 100644 modules/git/tests/repos/repo6_merge/refs/heads/merge/modify_file create mode 100644 modules/git/tests/repos/repo6_merge/refs/heads/merge/remove_file create mode 100644 modules/git/tests/repos/repo6_merge_sha256/HEAD create mode 100644 modules/git/tests/repos/repo6_merge_sha256/config create mode 100644 modules/git/tests/repos/repo6_merge_sha256/description create mode 100644 modules/git/tests/repos/repo6_merge_sha256/info/exclude create mode 100644 modules/git/tests/repos/repo6_merge_sha256/info/refs create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/info/commit-graph create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/info/packs create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.bitmap create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.idx create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.pack create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.rev create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.idx create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.mtimes create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.pack create mode 100644 modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.rev create mode 100644 modules/git/tests/repos/repo6_merge_sha256/packed-refs create mode 100644 modules/git/tests/repos/repo6_merge_sha256/refs/heads/main create mode 100644 modules/git/tree.go create mode 100644 modules/git/tree_blob.go create mode 100644 modules/git/tree_entry.go create mode 100644 modules/git/tree_entry_mode.go create mode 100644 modules/git/tree_test.go create mode 100644 modules/git/url/url.go create mode 100644 modules/git/url/url_test.go create mode 100644 modules/git/utils.go create mode 100644 modules/git/utils_test.go create mode 100644 modules/gitgraph/graph.go create mode 100644 modules/gitgraph/graph_models.go create mode 100644 modules/gitgraph/graph_test.go create mode 100644 modules/gitgraph/parser.go create mode 100644 modules/gitrepo/branch.go create mode 100644 modules/gitrepo/gitrepo.go create mode 100644 modules/gitrepo/walk.go create mode 100644 modules/graceful/context.go create mode 100644 modules/graceful/manager.go create mode 100644 modules/graceful/manager_common.go create mode 100644 modules/graceful/manager_unix.go create mode 100644 modules/graceful/manager_windows.go create mode 100644 modules/graceful/net_unix.go create mode 100644 modules/graceful/net_windows.go create mode 100644 modules/graceful/releasereopen/releasereopen.go create mode 100644 modules/graceful/releasereopen/releasereopen_test.go create mode 100644 modules/graceful/restart_unix.go create mode 100644 modules/graceful/server.go create mode 100644 modules/graceful/server_hooks.go create mode 100644 modules/graceful/server_http.go create mode 100644 modules/hcaptcha/error.go create mode 100644 modules/hcaptcha/hcaptcha.go create mode 100644 modules/hcaptcha/hcaptcha_test.go create mode 100644 modules/highlight/highlight.go create mode 100644 modules/highlight/highlight_test.go create mode 100644 modules/hostmatcher/hostmatcher.go create mode 100644 modules/hostmatcher/hostmatcher_test.go create mode 100644 modules/hostmatcher/http.go create mode 100644 modules/html/html.go create mode 100644 modules/httpcache/httpcache.go create mode 100644 modules/httpcache/httpcache_test.go create mode 100644 modules/httplib/request.go create mode 100644 modules/httplib/serve.go create mode 100644 modules/httplib/serve_test.go create mode 100644 modules/httplib/url.go create mode 100644 modules/httplib/url_test.go create mode 100644 modules/indexer/code/bleve/bleve.go create mode 100644 modules/indexer/code/elasticsearch/elasticsearch.go create mode 100644 modules/indexer/code/elasticsearch/elasticsearch_test.go create mode 100644 modules/indexer/code/git.go create mode 100644 modules/indexer/code/indexer.go create mode 100644 modules/indexer/code/indexer_test.go create mode 100644 modules/indexer/code/internal/indexer.go create mode 100644 modules/indexer/code/internal/model.go create mode 100644 modules/indexer/code/internal/util.go create mode 100644 modules/indexer/code/search.go create mode 100644 modules/indexer/internal/base32.go create mode 100644 modules/indexer/internal/bleve/batch.go create mode 100644 modules/indexer/internal/bleve/indexer.go create mode 100644 modules/indexer/internal/bleve/metadata.go create mode 100644 modules/indexer/internal/bleve/metadata_test.go create mode 100644 modules/indexer/internal/bleve/query.go create mode 100644 modules/indexer/internal/bleve/util.go create mode 100644 modules/indexer/internal/db/indexer.go create mode 100644 modules/indexer/internal/elasticsearch/indexer.go create mode 100644 modules/indexer/internal/elasticsearch/util.go create mode 100644 modules/indexer/internal/indexer.go create mode 100644 modules/indexer/internal/meilisearch/filter.go create mode 100644 modules/indexer/internal/meilisearch/indexer.go create mode 100644 modules/indexer/internal/meilisearch/util.go create mode 100644 modules/indexer/internal/paginator.go create mode 100644 modules/indexer/issues/bleve/bleve.go create mode 100644 modules/indexer/issues/bleve/bleve_test.go create mode 100644 modules/indexer/issues/db/db.go create mode 100644 modules/indexer/issues/db/options.go create mode 100644 modules/indexer/issues/dboptions.go create mode 100644 modules/indexer/issues/elasticsearch/elasticsearch.go create mode 100644 modules/indexer/issues/elasticsearch/elasticsearch_test.go create mode 100644 modules/indexer/issues/indexer.go create mode 100644 modules/indexer/issues/indexer_test.go create mode 100644 modules/indexer/issues/internal/indexer.go create mode 100644 modules/indexer/issues/internal/model.go create mode 100644 modules/indexer/issues/internal/tests/tests.go create mode 100644 modules/indexer/issues/meilisearch/meilisearch.go create mode 100644 modules/indexer/issues/meilisearch/meilisearch_test.go create mode 100644 modules/indexer/issues/util.go create mode 100644 modules/indexer/stats/db.go create mode 100644 modules/indexer/stats/indexer.go create mode 100644 modules/indexer/stats/indexer_test.go create mode 100644 modules/indexer/stats/queue.go create mode 100644 modules/issue/template/template.go create mode 100644 modules/issue/template/template_test.go create mode 100644 modules/issue/template/unmarshal.go create mode 100644 modules/json/json.go create mode 100644 modules/keying/keying.go create mode 100644 modules/keying/keying_test.go create mode 100644 modules/label/label.go create mode 100644 modules/label/parser.go create mode 100644 modules/label/parser_test.go create mode 100644 modules/lfs/LICENSE create mode 100644 modules/lfs/client.go create mode 100644 modules/lfs/client_test.go create mode 100644 modules/lfs/content_store.go create mode 100644 modules/lfs/endpoint.go create mode 100644 modules/lfs/endpoint_test.go create mode 100644 modules/lfs/filesystem_client.go create mode 100644 modules/lfs/http_client.go create mode 100644 modules/lfs/http_client_test.go create mode 100644 modules/lfs/pointer.go create mode 100644 modules/lfs/pointer_scanner.go create mode 100644 modules/lfs/pointer_test.go create mode 100644 modules/lfs/shared.go create mode 100644 modules/lfs/transferadapter.go create mode 100644 modules/lfs/transferadapter_test.go create mode 100644 modules/log/color.go create mode 100644 modules/log/color_console.go create mode 100644 modules/log/color_console_other.go create mode 100644 modules/log/color_console_windows.go create mode 100644 modules/log/color_router.go create mode 100644 modules/log/event_format.go create mode 100644 modules/log/event_format_test.go create mode 100644 modules/log/event_writer.go create mode 100644 modules/log/event_writer_base.go create mode 100644 modules/log/event_writer_conn.go create mode 100644 modules/log/event_writer_conn_test.go create mode 100644 modules/log/event_writer_console.go create mode 100644 modules/log/event_writer_file.go create mode 100644 modules/log/flags.go create mode 100644 modules/log/flags_test.go create mode 100644 modules/log/groutinelabel.go create mode 100644 modules/log/groutinelabel_test.go create mode 100644 modules/log/init.go create mode 100644 modules/log/level.go create mode 100644 modules/log/level_test.go create mode 100644 modules/log/logger.go create mode 100644 modules/log/logger_global.go create mode 100644 modules/log/logger_impl.go create mode 100644 modules/log/logger_test.go create mode 100644 modules/log/manager.go create mode 100644 modules/log/manager_test.go create mode 100644 modules/log/misc.go create mode 100644 modules/log/stack.go create mode 100644 modules/markup/asciicast/asciicast.go create mode 100644 modules/markup/camo.go create mode 100644 modules/markup/camo_test.go create mode 100644 modules/markup/common/footnote.go create mode 100644 modules/markup/common/footnote_test.go create mode 100644 modules/markup/common/html.go create mode 100644 modules/markup/common/linkify.go create mode 100644 modules/markup/console/console.go create mode 100644 modules/markup/console/console_test.go create mode 100644 modules/markup/csv/csv.go create mode 100644 modules/markup/csv/csv_test.go create mode 100644 modules/markup/external/external.go create mode 100644 modules/markup/file_preview.go create mode 100644 modules/markup/html.go create mode 100644 modules/markup/html_internal_test.go create mode 100644 modules/markup/html_test.go create mode 100644 modules/markup/markdown/ast.go create mode 100644 modules/markup/markdown/callout/ast.go create mode 100644 modules/markup/markdown/callout/github.go create mode 100644 modules/markup/markdown/callout/github_legacy.go create mode 100644 modules/markup/markdown/color_util.go create mode 100644 modules/markup/markdown/color_util_test.go create mode 100644 modules/markup/markdown/convertyaml.go create mode 100644 modules/markup/markdown/goldmark.go create mode 100644 modules/markup/markdown/markdown.go create mode 100644 modules/markup/markdown/markdown_test.go create mode 100644 modules/markup/markdown/math/block_node.go create mode 100644 modules/markup/markdown/math/block_parser.go create mode 100644 modules/markup/markdown/math/block_renderer.go create mode 100644 modules/markup/markdown/math/inline_block_node.go create mode 100644 modules/markup/markdown/math/inline_node.go create mode 100644 modules/markup/markdown/math/inline_parser.go create mode 100644 modules/markup/markdown/math/inline_renderer.go create mode 100644 modules/markup/markdown/math/math.go create mode 100644 modules/markup/markdown/meta.go create mode 100644 modules/markup/markdown/meta_test.go create mode 100644 modules/markup/markdown/prefixed_id.go create mode 100644 modules/markup/markdown/renderconfig.go create mode 100644 modules/markup/markdown/renderconfig_test.go create mode 100644 modules/markup/markdown/toc.go create mode 100644 modules/markup/markdown/transform_codespan.go create mode 100644 modules/markup/markdown/transform_heading.go create mode 100644 modules/markup/markdown/transform_image.go create mode 100644 modules/markup/markdown/transform_link.go create mode 100644 modules/markup/markdown/transform_list.go create mode 100644 modules/markup/mdstripper/mdstripper.go create mode 100644 modules/markup/mdstripper/mdstripper_test.go create mode 100644 modules/markup/orgmode/orgmode.go create mode 100644 modules/markup/orgmode/orgmode_test.go create mode 100644 modules/markup/renderer.go create mode 100644 modules/markup/renderer_test.go create mode 100644 modules/markup/sanitizer.go create mode 100644 modules/markup/sanitizer_test.go create mode 100644 modules/markup/tests/repo/repo1_filepreview/HEAD create mode 100644 modules/markup/tests/repo/repo1_filepreview/config create mode 100644 modules/markup/tests/repo/repo1_filepreview/description create mode 100644 modules/markup/tests/repo/repo1_filepreview/info/exclude create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/19/0d9492934af498c3f669d6a2431dc5459e5b20 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/3f/ed9bce8610a52048747f627b3863374642c85c create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/4c/1aaf56bcb9f39dcf65f3f250726850aed13cd6 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/83/57a737d04385bb7f2ab59ff184be94756e7972 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/84/22d40f12717e1ebd5cef2449f6c09d1f775969 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/8c/7e5a667f1b771847fe88c01c3de34413a1b220 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/d4/490327def9658be036d6a52c4417d84e74dd4c create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/ee/2b1253d9cf407796e2e724926cbe3a974b214d create mode 100644 modules/markup/tests/repo/repo1_filepreview/refs/heads/master create mode 100644 modules/mcaptcha/mcaptcha.go create mode 100755 modules/metrics/collector.go create mode 100644 modules/migration/comment.go create mode 100644 modules/migration/downloader.go create mode 100644 modules/migration/error.go create mode 100644 modules/migration/file_format.go create mode 100644 modules/migration/file_format_test.go create mode 100644 modules/migration/file_format_testdata/issue_a.json create mode 100644 modules/migration/file_format_testdata/issue_a.yml create mode 100644 modules/migration/file_format_testdata/issue_b.json create mode 100644 modules/migration/file_format_testdata/milestones.json create mode 100644 modules/migration/issue.go create mode 100644 modules/migration/label.go create mode 100644 modules/migration/messenger.go create mode 100644 modules/migration/milestone.go create mode 100644 modules/migration/null_downloader.go create mode 100644 modules/migration/options.go create mode 100644 modules/migration/pullrequest.go create mode 100644 modules/migration/reaction.go create mode 100644 modules/migration/release.go create mode 100644 modules/migration/repo.go create mode 100644 modules/migration/retry_downloader.go create mode 100644 modules/migration/review.go create mode 100644 modules/migration/schemas/issue.json create mode 100644 modules/migration/schemas/label.json create mode 100644 modules/migration/schemas/milestone.json create mode 100644 modules/migration/schemas/reaction.json create mode 100644 modules/migration/schemas_bindata.go create mode 100644 modules/migration/schemas_dynamic.go create mode 100644 modules/migration/schemas_static.go create mode 100644 modules/migration/uploader.go create mode 100644 modules/nosql/leveldb.go create mode 100644 modules/nosql/manager.go create mode 100644 modules/nosql/manager_leveldb.go create mode 100644 modules/nosql/manager_redis.go create mode 100644 modules/nosql/manager_redis_test.go create mode 100644 modules/nosql/redis.go create mode 100644 modules/nosql/redis_test.go create mode 100644 modules/optional/option.go create mode 100644 modules/optional/option_test.go create mode 100644 modules/optional/serialization.go create mode 100644 modules/optional/serialization_test.go create mode 100644 modules/options/base.go create mode 100644 modules/options/dynamic.go create mode 100644 modules/options/options_bindata.go create mode 100644 modules/options/static.go create mode 100644 modules/packages/alpine/metadata.go create mode 100644 modules/packages/alpine/metadata_test.go create mode 100644 modules/packages/arch/metadata.go create mode 100644 modules/packages/arch/metadata_test.go create mode 100644 modules/packages/cargo/parser.go create mode 100644 modules/packages/cargo/parser_test.go create mode 100644 modules/packages/chef/metadata.go create mode 100644 modules/packages/chef/metadata_test.go create mode 100644 modules/packages/composer/metadata.go create mode 100644 modules/packages/composer/metadata_test.go create mode 100644 modules/packages/conan/conanfile_parser.go create mode 100644 modules/packages/conan/conanfile_parser_test.go create mode 100644 modules/packages/conan/conaninfo_parser.go create mode 100644 modules/packages/conan/conaninfo_parser_test.go create mode 100644 modules/packages/conan/metadata.go create mode 100644 modules/packages/conan/reference.go create mode 100644 modules/packages/conan/reference_test.go create mode 100644 modules/packages/conda/metadata.go create mode 100644 modules/packages/conda/metadata_test.go create mode 100644 modules/packages/container/helm/helm.go create mode 100644 modules/packages/container/metadata.go create mode 100644 modules/packages/container/metadata_test.go create mode 100644 modules/packages/content_store.go create mode 100644 modules/packages/cran/metadata.go create mode 100644 modules/packages/cran/metadata_test.go create mode 100644 modules/packages/debian/metadata.go create mode 100644 modules/packages/debian/metadata_test.go create mode 100644 modules/packages/goproxy/metadata.go create mode 100644 modules/packages/goproxy/metadata_test.go create mode 100644 modules/packages/hashed_buffer.go create mode 100644 modules/packages/hashed_buffer_test.go create mode 100644 modules/packages/helm/metadata.go create mode 100644 modules/packages/maven/metadata.go create mode 100644 modules/packages/maven/metadata_test.go create mode 100644 modules/packages/multi_hasher.go create mode 100644 modules/packages/multi_hasher_test.go create mode 100644 modules/packages/npm/creator.go create mode 100644 modules/packages/npm/creator_test.go create mode 100644 modules/packages/npm/metadata.go create mode 100644 modules/packages/nuget/metadata.go create mode 100644 modules/packages/nuget/metadata_test.go create mode 100644 modules/packages/nuget/symbol_extractor.go create mode 100644 modules/packages/nuget/symbol_extractor_test.go create mode 100644 modules/packages/pub/metadata.go create mode 100644 modules/packages/pub/metadata_test.go create mode 100644 modules/packages/pypi/metadata.go create mode 100644 modules/packages/rpm/metadata.go create mode 100644 modules/packages/rpm/metadata_test.go create mode 100644 modules/packages/rubygems/marshal.go create mode 100644 modules/packages/rubygems/marshal_test.go create mode 100644 modules/packages/rubygems/metadata.go create mode 100644 modules/packages/rubygems/metadata_test.go create mode 100644 modules/packages/swift/metadata.go create mode 100644 modules/packages/swift/metadata_test.go create mode 100644 modules/packages/vagrant/metadata.go create mode 100644 modules/packages/vagrant/metadata_test.go create mode 100644 modules/paginator/paginator.go create mode 100644 modules/paginator/paginator_test.go create mode 100644 modules/pprof/pprof.go create mode 100644 modules/private/actions.go create mode 100644 modules/private/forgejo_actions.go create mode 100644 modules/private/hook.go create mode 100644 modules/private/internal.go create mode 100644 modules/private/key.go create mode 100644 modules/private/mail.go create mode 100644 modules/private/manager.go create mode 100644 modules/private/request.go create mode 100644 modules/private/restore_repo.go create mode 100644 modules/private/serv.go create mode 100644 modules/process/context.go create mode 100644 modules/process/error.go create mode 100644 modules/process/manager.go create mode 100644 modules/process/manager_exec.go create mode 100644 modules/process/manager_stacktraces.go create mode 100644 modules/process/manager_test.go create mode 100644 modules/process/manager_unix.go create mode 100644 modules/process/manager_windows.go create mode 100644 modules/process/process.go create mode 100644 modules/proxy/proxy.go create mode 100644 modules/proxyprotocol/conn.go create mode 100644 modules/proxyprotocol/errors.go create mode 100644 modules/proxyprotocol/listener.go create mode 100644 modules/proxyprotocol/util.go create mode 100644 modules/public/mime_types.go create mode 100644 modules/public/public.go create mode 100644 modules/public/public_bindata.go create mode 100644 modules/public/public_test.go create mode 100644 modules/public/serve_dynamic.go create mode 100644 modules/public/serve_static.go create mode 100644 modules/queue/backoff.go create mode 100644 modules/queue/base.go create mode 100644 modules/queue/base_channel.go create mode 100644 modules/queue/base_channel_test.go create mode 100644 modules/queue/base_dummy.go create mode 100644 modules/queue/base_levelqueue.go create mode 100644 modules/queue/base_levelqueue_common.go create mode 100644 modules/queue/base_levelqueue_test.go create mode 100644 modules/queue/base_levelqueue_unique.go create mode 100644 modules/queue/base_redis.go create mode 100644 modules/queue/base_redis_test.go create mode 100644 modules/queue/base_redis_with_server_test.go create mode 100644 modules/queue/base_test.go create mode 100644 modules/queue/config.go create mode 100644 modules/queue/lqinternal/lqinternal.go create mode 100644 modules/queue/manager.go create mode 100644 modules/queue/manager_test.go create mode 100644 modules/queue/mock/inmemorymockredis.go create mode 100644 modules/queue/mock/redisuniversalclient.go create mode 100644 modules/queue/queue.go create mode 100644 modules/queue/testhelper.go create mode 100644 modules/queue/workergroup.go create mode 100644 modules/queue/workerqueue.go create mode 100644 modules/queue/workerqueue_test.go create mode 100644 modules/recaptcha/recaptcha.go create mode 100644 modules/references/references.go create mode 100644 modules/references/references_test.go create mode 100644 modules/regexplru/regexplru.go create mode 100644 modules/regexplru/regexplru_test.go create mode 100644 modules/repository/branch.go create mode 100644 modules/repository/branch_test.go create mode 100644 modules/repository/collaborator.go create mode 100644 modules/repository/collaborator_test.go create mode 100644 modules/repository/commits.go create mode 100644 modules/repository/commits_test.go create mode 100644 modules/repository/create.go create mode 100644 modules/repository/create_test.go create mode 100644 modules/repository/delete.go create mode 100644 modules/repository/env.go create mode 100644 modules/repository/fork.go create mode 100644 modules/repository/hooks.go create mode 100644 modules/repository/init.go create mode 100644 modules/repository/init_test.go create mode 100644 modules/repository/license.go create mode 100644 modules/repository/license_test.go create mode 100644 modules/repository/main_test.go create mode 100644 modules/repository/push.go create mode 100644 modules/repository/repo.go create mode 100644 modules/repository/repo_test.go create mode 100644 modules/repository/temp.go create mode 100644 modules/secret/secret.go create mode 100644 modules/secret/secret_test.go create mode 100644 modules/session/db.go create mode 100644 modules/session/redis.go create mode 100644 modules/session/store.go create mode 100644 modules/session/virtual.go create mode 100644 modules/setting/actions.go create mode 100644 modules/setting/actions_test.go create mode 100644 modules/setting/admin.go create mode 100644 modules/setting/admin_test.go create mode 100644 modules/setting/api.go create mode 100644 modules/setting/asset_dynamic.go create mode 100644 modules/setting/asset_static.go create mode 100644 modules/setting/attachment.go create mode 100644 modules/setting/attachment_test.go create mode 100644 modules/setting/badges.go create mode 100644 modules/setting/cache.go create mode 100644 modules/setting/camo.go create mode 100644 modules/setting/config.go create mode 100644 modules/setting/config/getter.go create mode 100644 modules/setting/config/value.go create mode 100644 modules/setting/config_env.go create mode 100644 modules/setting/config_env_test.go create mode 100644 modules/setting/config_provider.go create mode 100644 modules/setting/config_provider_test.go create mode 100644 modules/setting/cors.go create mode 100644 modules/setting/cron.go create mode 100644 modules/setting/cron_test.go create mode 100644 modules/setting/database.go create mode 100644 modules/setting/database_sqlite.go create mode 100644 modules/setting/database_test.go create mode 100644 modules/setting/f3.go create mode 100644 modules/setting/federation.go create mode 100644 modules/setting/forgejo_storage_test.go create mode 100644 modules/setting/git.go create mode 100644 modules/setting/git_test.go create mode 100644 modules/setting/highlight.go create mode 100644 modules/setting/i18n.go create mode 100644 modules/setting/incoming_email.go create mode 100644 modules/setting/incoming_email_test.go create mode 100644 modules/setting/indexer.go create mode 100644 modules/setting/indexer_test.go create mode 100644 modules/setting/lfs.go create mode 100644 modules/setting/lfs_test.go create mode 100644 modules/setting/log.go create mode 100644 modules/setting/log_test.go create mode 100644 modules/setting/mailer.go create mode 100644 modules/setting/mailer_test.go create mode 100644 modules/setting/markup.go create mode 100644 modules/setting/metrics.go create mode 100644 modules/setting/migrations.go create mode 100644 modules/setting/mime_type_map.go create mode 100644 modules/setting/mirror.go create mode 100644 modules/setting/oauth2.go create mode 100644 modules/setting/oauth2_test.go create mode 100644 modules/setting/other.go create mode 100644 modules/setting/packages.go create mode 100644 modules/setting/packages_test.go create mode 100644 modules/setting/path.go create mode 100644 modules/setting/path_test.go create mode 100644 modules/setting/picture.go create mode 100644 modules/setting/project.go create mode 100644 modules/setting/proxy.go create mode 100644 modules/setting/queue.go create mode 100644 modules/setting/quota.go create mode 100644 modules/setting/repository.go create mode 100644 modules/setting/repository_archive.go create mode 100644 modules/setting/repository_archive_test.go create mode 100644 modules/setting/security.go create mode 100644 modules/setting/server.go create mode 100644 modules/setting/server_test.go create mode 100644 modules/setting/service.go create mode 100644 modules/setting/service_test.go create mode 100644 modules/setting/session.go create mode 100644 modules/setting/setting.go create mode 100644 modules/setting/setting_test.go create mode 100644 modules/setting/ssh.go create mode 100644 modules/setting/storage.go create mode 100644 modules/setting/storage_test.go create mode 100644 modules/setting/task.go create mode 100644 modules/setting/time.go create mode 100644 modules/setting/ui.go create mode 100644 modules/setting/webhook.go create mode 100644 modules/sitemap/sitemap.go create mode 100644 modules/sitemap/sitemap_test.go create mode 100644 modules/ssh/init.go create mode 100644 modules/ssh/ssh.go create mode 100644 modules/ssh/ssh_graceful.go create mode 100644 modules/storage/helper.go create mode 100644 modules/storage/helper_test.go create mode 100644 modules/storage/local.go create mode 100644 modules/storage/local_test.go create mode 100644 modules/storage/minio.go create mode 100644 modules/storage/minio_test.go create mode 100644 modules/storage/storage.go create mode 100644 modules/storage/storage_test.go create mode 100644 modules/storage/testdata/aws_credentials create mode 100644 modules/storage/testdata/minio.json create mode 100644 modules/structs/activity.go create mode 100644 modules/structs/activitypub.go create mode 100644 modules/structs/admin_user.go create mode 100644 modules/structs/attachment.go create mode 100644 modules/structs/commit_status.go create mode 100644 modules/structs/commit_status_test.go create mode 100644 modules/structs/cron.go create mode 100644 modules/structs/doc.go create mode 100644 modules/structs/fork.go create mode 100644 modules/structs/git_blob.go create mode 100644 modules/structs/git_hook.go create mode 100644 modules/structs/hook.go create mode 100644 modules/structs/issue.go create mode 100644 modules/structs/issue_comment.go create mode 100644 modules/structs/issue_label.go create mode 100644 modules/structs/issue_milestone.go create mode 100644 modules/structs/issue_reaction.go create mode 100644 modules/structs/issue_stopwatch.go create mode 100644 modules/structs/issue_test.go create mode 100644 modules/structs/issue_tracked_time.go create mode 100644 modules/structs/lfs_lock.go create mode 100644 modules/structs/mirror.go create mode 100644 modules/structs/miscellaneous.go create mode 100644 modules/structs/moderation.go create mode 100644 modules/structs/nodeinfo.go create mode 100644 modules/structs/notifications.go create mode 100644 modules/structs/org.go create mode 100644 modules/structs/org_member.go create mode 100644 modules/structs/org_team.go create mode 100644 modules/structs/package.go create mode 100644 modules/structs/pull.go create mode 100644 modules/structs/pull_review.go create mode 100644 modules/structs/quota.go create mode 100644 modules/structs/release.go create mode 100644 modules/structs/repo.go create mode 100644 modules/structs/repo_actions.go create mode 100644 modules/structs/repo_branch.go create mode 100644 modules/structs/repo_collaborator.go create mode 100644 modules/structs/repo_commit.go create mode 100644 modules/structs/repo_compare.go create mode 100644 modules/structs/repo_file.go create mode 100644 modules/structs/repo_flags.go create mode 100644 modules/structs/repo_key.go create mode 100644 modules/structs/repo_note.go create mode 100644 modules/structs/repo_refs.go create mode 100644 modules/structs/repo_tag.go create mode 100644 modules/structs/repo_topic.go create mode 100644 modules/structs/repo_tree.go create mode 100644 modules/structs/repo_watch.go create mode 100644 modules/structs/repo_wiki.go create mode 100644 modules/structs/secret.go create mode 100644 modules/structs/settings.go create mode 100644 modules/structs/status.go create mode 100644 modules/structs/task.go create mode 100644 modules/structs/user.go create mode 100644 modules/structs/user_app.go create mode 100644 modules/structs/user_email.go create mode 100644 modules/structs/user_gpgkey.go create mode 100644 modules/structs/user_key.go create mode 100644 modules/structs/variable.go create mode 100644 modules/structs/visible_type.go create mode 100644 modules/structs/workflow.go create mode 100644 modules/svg/processor.go create mode 100644 modules/svg/processor_test.go create mode 100644 modules/svg/svg.go create mode 100644 modules/sync/exclusive_pool.go create mode 100644 modules/sync/status_pool.go create mode 100644 modules/sync/status_pool_test.go create mode 100644 modules/system/appstate.go create mode 100644 modules/system/appstate_test.go create mode 100644 modules/system/db.go create mode 100644 modules/system/item_runtime.go create mode 100644 modules/templates/base.go create mode 100644 modules/templates/dynamic.go create mode 100644 modules/templates/eval/eval.go create mode 100644 modules/templates/eval/eval_test.go create mode 100644 modules/templates/helper.go create mode 100644 modules/templates/helper_test.go create mode 100644 modules/templates/htmlrenderer.go create mode 100644 modules/templates/htmlrenderer_test.go create mode 100644 modules/templates/mailer.go create mode 100644 modules/templates/main_test.go create mode 100644 modules/templates/scopedtmpl/scopedtmpl.go create mode 100644 modules/templates/scopedtmpl/scopedtmpl_test.go create mode 100644 modules/templates/static.go create mode 100644 modules/templates/templates_bindata.go create mode 100644 modules/templates/util_avatar.go create mode 100644 modules/templates/util_dict.go create mode 100644 modules/templates/util_json.go create mode 100644 modules/templates/util_misc.go create mode 100644 modules/templates/util_render.go create mode 100644 modules/templates/util_render_test.go create mode 100644 modules/templates/util_slice.go create mode 100644 modules/templates/util_string.go create mode 100644 modules/templates/util_string_test.go create mode 100644 modules/templates/util_test.go create mode 100644 modules/templates/vars/vars.go create mode 100644 modules/templates/vars/vars_test.go create mode 100644 modules/test/logchecker.go create mode 100644 modules/test/logchecker_test.go create mode 100644 modules/test/utils.go create mode 100644 modules/test/utils_test.go create mode 100644 modules/testlogger/testlogger.go create mode 100644 modules/timeutil/datetime.go create mode 100644 modules/timeutil/datetime_test.go create mode 100644 modules/timeutil/executable.go create mode 100644 modules/timeutil/since.go create mode 100644 modules/timeutil/since_test.go create mode 100644 modules/timeutil/timestamp.go create mode 100644 modules/timeutil/timestampnano.go create mode 100644 modules/translation/i18n/errors.go create mode 100644 modules/translation/i18n/format.go create mode 100644 modules/translation/i18n/i18n.go create mode 100644 modules/translation/i18n/i18n_test.go create mode 100644 modules/translation/i18n/localestore.go create mode 100644 modules/translation/mock.go create mode 100644 modules/translation/translation.go create mode 100644 modules/translation/translation_test.go create mode 100644 modules/turnstile/turnstile.go create mode 100644 modules/typesniffer/typesniffer.go create mode 100644 modules/typesniffer/typesniffer_test.go create mode 100644 modules/updatechecker/update_checker.go create mode 100644 modules/updatechecker/update_checker_test.go create mode 100644 modules/uri/uri.go create mode 100644 modules/uri/uri_test.go create mode 100644 modules/user/user.go create mode 100644 modules/user/user_test.go create mode 100644 modules/util/color.go create mode 100644 modules/util/color_test.go create mode 100644 modules/util/error.go create mode 100644 modules/util/file_unix.go create mode 100644 modules/util/file_unix_test.go create mode 100644 modules/util/file_windows.go create mode 100644 modules/util/filebuffer/file_backed_buffer.go create mode 100644 modules/util/filebuffer/file_backed_buffer_test.go create mode 100644 modules/util/io.go create mode 100644 modules/util/io_test.go create mode 100644 modules/util/keypair.go create mode 100644 modules/util/keypair_test.go create mode 100644 modules/util/legacy.go create mode 100644 modules/util/legacy_test.go create mode 100644 modules/util/pack.go create mode 100644 modules/util/pack_test.go create mode 100644 modules/util/paginate.go create mode 100644 modules/util/paginate_test.go create mode 100644 modules/util/path.go create mode 100644 modules/util/path_test.go create mode 100644 modules/util/remove.go create mode 100644 modules/util/rotatingfilewriter/writer.go create mode 100644 modules/util/rotatingfilewriter/writer_test.go create mode 100644 modules/util/sanitize.go create mode 100644 modules/util/sanitize_test.go create mode 100644 modules/util/sec_to_time.go create mode 100644 modules/util/sec_to_time_test.go create mode 100644 modules/util/shellquote.go create mode 100644 modules/util/shellquote_test.go create mode 100644 modules/util/slice.go create mode 100644 modules/util/slice_test.go create mode 100644 modules/util/string.go create mode 100644 modules/util/string_test.go create mode 100644 modules/util/timer.go create mode 100644 modules/util/timer_test.go create mode 100644 modules/util/truncate.go create mode 100644 modules/util/truncate_test.go create mode 100644 modules/util/url.go create mode 100644 modules/util/util.go create mode 100644 modules/util/util_test.go create mode 100644 modules/validation/binding.go create mode 100644 modules/validation/binding_test.go create mode 100644 modules/validation/glob_pattern_test.go create mode 100644 modules/validation/helpers.go create mode 100644 modules/validation/helpers_test.go create mode 100644 modules/validation/refname_test.go create mode 100644 modules/validation/regex_pattern_test.go create mode 100644 modules/validation/validatable.go create mode 100644 modules/validation/validatable_test.go create mode 100644 modules/validation/validurl_test.go create mode 100644 modules/web/handler.go create mode 100644 modules/web/middleware/binding.go create mode 100644 modules/web/middleware/cookie.go create mode 100644 modules/web/middleware/data.go create mode 100644 modules/web/middleware/flash.go create mode 100644 modules/web/middleware/locale.go create mode 100644 modules/web/middleware/request.go create mode 100644 modules/web/route.go create mode 100644 modules/web/route_test.go create mode 100644 modules/web/routemock.go create mode 100644 modules/web/routemock_test.go create mode 100644 modules/web/routing/context.go create mode 100644 modules/web/routing/funcinfo.go create mode 100644 modules/web/routing/funcinfo_test.go create mode 100644 modules/web/routing/logger.go create mode 100644 modules/web/routing/logger_manager.go create mode 100644 modules/web/routing/requestrecord.go create mode 100644 modules/web/types/response.go create mode 100644 modules/webhook/structs.go create mode 100644 modules/webhook/type.go create mode 100644 modules/zstd/option.go create mode 100644 modules/zstd/zstd.go create mode 100644 modules/zstd/zstd_test.go (limited to 'modules') diff --git a/modules/actions/github.go b/modules/actions/github.go new file mode 100644 index 0000000..c27d4ed --- /dev/null +++ b/modules/actions/github.go @@ -0,0 +1,133 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + webhook_module "code.gitea.io/gitea/modules/webhook" +) + +const ( + GithubEventPullRequest = "pull_request" + GithubEventPullRequestTarget = "pull_request_target" + GithubEventPullRequestReviewComment = "pull_request_review_comment" + GithubEventPullRequestReview = "pull_request_review" + GithubEventRegistryPackage = "registry_package" + GithubEventCreate = "create" + GithubEventDelete = "delete" + GithubEventFork = "fork" + GithubEventPush = "push" + GithubEventIssues = "issues" + GithubEventIssueComment = "issue_comment" + GithubEventRelease = "release" + GithubEventPullRequestComment = "pull_request_comment" + GithubEventGollum = "gollum" + GithubEventSchedule = "schedule" + GithubEventWorkflowDispatch = "workflow_dispatch" +) + +// IsDefaultBranchWorkflow returns true if the event only triggers workflows on the default branch +func IsDefaultBranchWorkflow(triggedEvent webhook_module.HookEventType) bool { + switch triggedEvent { + case webhook_module.HookEventDelete: + // GitHub "delete" event + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#delete + return true + case webhook_module.HookEventFork: + // GitHub "fork" event + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#fork + return true + case webhook_module.HookEventIssueComment: + // GitHub "issue_comment" event + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment + return true + case webhook_module.HookEventPullRequestComment: + // GitHub "pull_request_comment" event + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_comment-use-issue_comment + return true + case webhook_module.HookEventWiki: + // GitHub "gollum" event + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#gollum + return true + case webhook_module.HookEventSchedule: + // GitHub "schedule" event + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule + return true + case webhook_module.HookEventWorkflowDispatch: + // GitHub "workflow_dispatch" event + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch + return true + case webhook_module.HookEventIssues, + webhook_module.HookEventIssueAssign, + webhook_module.HookEventIssueLabel, + webhook_module.HookEventIssueMilestone: + // Github "issues" event + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues + return true + } + + return false +} + +// canGithubEventMatch check if the input Github event can match any Gitea event. +func canGithubEventMatch(eventName string, triggedEvent webhook_module.HookEventType) bool { + switch eventName { + case GithubEventRegistryPackage: + return triggedEvent == webhook_module.HookEventPackage + + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#gollum + case GithubEventGollum: + return triggedEvent == webhook_module.HookEventWiki + + case GithubEventWorkflowDispatch: + return triggedEvent == webhook_module.HookEventWorkflowDispatch + + case GithubEventIssues: + switch triggedEvent { + case webhook_module.HookEventIssues, + webhook_module.HookEventIssueAssign, + webhook_module.HookEventIssueLabel, + webhook_module.HookEventIssueMilestone: + return true + + default: + return false + } + + case GithubEventPullRequest, GithubEventPullRequestTarget: + switch triggedEvent { + case webhook_module.HookEventPullRequest, + webhook_module.HookEventPullRequestSync, + webhook_module.HookEventPullRequestAssign, + webhook_module.HookEventPullRequestLabel, + webhook_module.HookEventPullRequestReviewRequest, + webhook_module.HookEventPullRequestMilestone: + return true + + default: + return false + } + + case GithubEventPullRequestReview: + switch triggedEvent { + case webhook_module.HookEventPullRequestReviewApproved, + webhook_module.HookEventPullRequestReviewComment, + webhook_module.HookEventPullRequestReviewRejected: + return true + + default: + return false + } + + case GithubEventIssueComment: + // https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_comment-use-issue_comment + return triggedEvent == webhook_module.HookEventIssueComment || + triggedEvent == webhook_module.HookEventPullRequestComment + + case GithubEventSchedule: + return triggedEvent == webhook_module.HookEventSchedule + + default: + return eventName == string(triggedEvent) + } +} diff --git a/modules/actions/github_test.go b/modules/actions/github_test.go new file mode 100644 index 0000000..6652ff6 --- /dev/null +++ b/modules/actions/github_test.go @@ -0,0 +1,119 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/stretchr/testify/assert" +) + +func TestCanGithubEventMatch(t *testing.T) { + testCases := []struct { + desc string + eventName string + triggeredEvent webhook_module.HookEventType + expected bool + }{ + // registry_package event + { + "registry_package matches", + GithubEventRegistryPackage, + webhook_module.HookEventPackage, + true, + }, + { + "registry_package cannot match", + GithubEventRegistryPackage, + webhook_module.HookEventPush, + false, + }, + // issues event + { + "issue matches", + GithubEventIssues, + webhook_module.HookEventIssueLabel, + true, + }, + { + "issue cannot match", + GithubEventIssues, + webhook_module.HookEventIssueComment, + false, + }, + // issue_comment event + { + "issue_comment matches", + GithubEventIssueComment, + webhook_module.HookEventIssueComment, + true, + }, + { + "issue_comment cannot match", + GithubEventIssueComment, + webhook_module.HookEventIssues, + false, + }, + // pull_request event + { + "pull_request matches", + GithubEventPullRequest, + webhook_module.HookEventPullRequestSync, + true, + }, + { + "pull_request cannot match", + GithubEventPullRequest, + webhook_module.HookEventPullRequestComment, + false, + }, + // pull_request_target event + { + "pull_request_target matches", + GithubEventPullRequest, + webhook_module.HookEventPullRequest, + true, + }, + { + "pull_request_target cannot match", + GithubEventPullRequest, + webhook_module.HookEventPullRequestComment, + false, + }, + // pull_request_review event + { + "pull_request_review matches", + GithubEventPullRequestReview, + webhook_module.HookEventPullRequestReviewComment, + true, + }, + { + "pull_request_review cannot match", + GithubEventPullRequestReview, + webhook_module.HookEventPullRequestComment, + false, + }, + // other events + { + "create event", + GithubEventCreate, + webhook_module.HookEventCreate, + true, + }, + { + "create pull request comment", + GithubEventIssueComment, + webhook_module.HookEventPullRequestComment, + true, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + assert.Equalf(t, tc.expected, canGithubEventMatch(tc.eventName, tc.triggeredEvent), "canGithubEventMatch(%v, %v)", tc.eventName, tc.triggeredEvent) + }) + } +} diff --git a/modules/actions/log.go b/modules/actions/log.go new file mode 100644 index 0000000..5a1425e --- /dev/null +++ b/modules/actions/log.go @@ -0,0 +1,224 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "bufio" + "context" + "fmt" + "io" + "os" + "strings" + "time" + + "code.gitea.io/gitea/models/dbfs" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/modules/zstd" + + runnerv1 "code.gitea.io/actions-proto-go/runner/v1" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const ( + MaxLineSize = 64 * 1024 + DBFSPrefix = "actions_log/" + + timeFormat = "2006-01-02T15:04:05.0000000Z07:00" + defaultBufSize = MaxLineSize +) + +// WriteLogs appends logs to DBFS file for temporary storage. +// It doesn't respect the file format in the filename like ".zst", since it's difficult to reopen a closed compressed file and append new content. +// Why doesn't it store logs in object storage directly? Because it's not efficient to append content to object storage. +func WriteLogs(ctx context.Context, filename string, offset int64, rows []*runnerv1.LogRow) ([]int, error) { + flag := os.O_WRONLY + if offset == 0 { + // Create file only if offset is 0, or it could result in content holes if the file doesn't exist. + flag |= os.O_CREATE + } + name := DBFSPrefix + filename + f, err := dbfs.OpenFile(ctx, name, flag) + if err != nil { + return nil, fmt.Errorf("dbfs OpenFile %q: %w", name, err) + } + defer f.Close() + + stat, err := f.Stat() + if err != nil { + return nil, fmt.Errorf("dbfs Stat %q: %w", name, err) + } + if stat.Size() < offset { + // If the size is less than offset, refuse to write, or it could result in content holes. + // However, if the size is greater than offset, we can still write to overwrite the content. + return nil, fmt.Errorf("size of %q is less than offset", name) + } + + if _, err := f.Seek(offset, io.SeekStart); err != nil { + return nil, fmt.Errorf("dbfs Seek %q: %w", name, err) + } + + writer := bufio.NewWriterSize(f, defaultBufSize) + + ns := make([]int, 0, len(rows)) + for _, row := range rows { + n, err := writer.WriteString(FormatLog(row.Time.AsTime(), row.Content) + "\n") + if err != nil { + return nil, err + } + ns = append(ns, n) + } + + if err := writer.Flush(); err != nil { + return nil, err + } + return ns, nil +} + +func ReadLogs(ctx context.Context, inStorage bool, filename string, offset, limit int64) ([]*runnerv1.LogRow, error) { + f, err := OpenLogs(ctx, inStorage, filename) + if err != nil { + return nil, err + } + defer f.Close() + + if _, err := f.Seek(offset, io.SeekStart); err != nil { + return nil, fmt.Errorf("file seek: %w", err) + } + + scanner := bufio.NewScanner(f) + maxLineSize := len(timeFormat) + MaxLineSize + 1 + scanner.Buffer(make([]byte, maxLineSize), maxLineSize) + + var rows []*runnerv1.LogRow + for scanner.Scan() && (int64(len(rows)) < limit || limit < 0) { + t, c, err := ParseLog(scanner.Text()) + if err != nil { + return nil, fmt.Errorf("parse log %q: %w", scanner.Text(), err) + } + rows = append(rows, &runnerv1.LogRow{ + Time: timestamppb.New(t), + Content: c, + }) + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("ReadLogs scan: %w", err) + } + + return rows, nil +} + +const ( + // logZstdBlockSize is the block size for zstd compression. + // 128KB leads the compression ratio to be close to the regular zstd compression. + // And it means each read from the underlying object storage will be at least 128KB*(compression ratio). + // The compression ratio is about 30% for text files, so the actual read size is about 38KB, which should be acceptable. + logZstdBlockSize = 128 * 1024 // 128KB +) + +// TransferLogs transfers logs from DBFS to object storage. +// It happens when the file is complete and no more logs will be appended. +// It respects the file format in the filename like ".zst", and compresses the content if needed. +func TransferLogs(ctx context.Context, filename string) (func(), error) { + name := DBFSPrefix + filename + remove := func() { + if err := dbfs.Remove(ctx, name); err != nil { + log.Warn("dbfs remove %q: %v", name, err) + } + } + f, err := dbfs.Open(ctx, name) + if err != nil { + return nil, fmt.Errorf("dbfs open %q: %w", name, err) + } + defer f.Close() + + var reader io.Reader = f + if strings.HasSuffix(filename, ".zst") { + r, w := io.Pipe() + reader = r + zstdWriter, err := zstd.NewSeekableWriter(w, logZstdBlockSize) + if err != nil { + return nil, fmt.Errorf("zstd NewSeekableWriter: %w", err) + } + go func() { + defer func() { + _ = w.CloseWithError(zstdWriter.Close()) + }() + if _, err := io.Copy(zstdWriter, f); err != nil { + _ = w.CloseWithError(err) + return + } + }() + } + + if _, err := storage.Actions.Save(filename, reader, -1); err != nil { + return nil, fmt.Errorf("storage save %q: %w", filename, err) + } + return remove, nil +} + +func RemoveLogs(ctx context.Context, inStorage bool, filename string) error { + if !inStorage { + name := DBFSPrefix + filename + err := dbfs.Remove(ctx, name) + if err != nil { + return fmt.Errorf("dbfs remove %q: %w", name, err) + } + return nil + } + err := storage.Actions.Delete(filename) + if err != nil { + return fmt.Errorf("storage delete %q: %w", filename, err) + } + return nil +} + +func OpenLogs(ctx context.Context, inStorage bool, filename string) (io.ReadSeekCloser, error) { + if !inStorage { + name := DBFSPrefix + filename + f, err := dbfs.Open(ctx, name) + if err != nil { + return nil, fmt.Errorf("dbfs open %q: %w", name, err) + } + return f, nil + } + + f, err := storage.Actions.Open(filename) + if err != nil { + return nil, fmt.Errorf("storage open %q: %w", filename, err) + } + + var reader io.ReadSeekCloser = f + if strings.HasSuffix(filename, ".zst") { + r, err := zstd.NewSeekableReader(f) + if err != nil { + return nil, fmt.Errorf("zstd NewSeekableReader: %w", err) + } + reader = r + } + + return reader, nil +} + +func FormatLog(timestamp time.Time, content string) string { + // Content shouldn't contain new line, it will break log indexes, other control chars are safe. + content = strings.ReplaceAll(content, "\n", `\n`) + if len(content) > MaxLineSize { + content = content[:MaxLineSize] + } + return fmt.Sprintf("%s %s", timestamp.UTC().Format(timeFormat), content) +} + +func ParseLog(in string) (time.Time, string, error) { + index := strings.IndexRune(in, ' ') + if index < 0 { + return time.Time{}, "", fmt.Errorf("invalid log: %q", in) + } + timestamp, err := time.Parse(timeFormat, in[:index]) + if err != nil { + return time.Time{}, "", err + } + return timestamp, in[index+1:], nil +} diff --git a/modules/actions/task_state.go b/modules/actions/task_state.go new file mode 100644 index 0000000..1f36e02 --- /dev/null +++ b/modules/actions/task_state.go @@ -0,0 +1,123 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + actions_model "code.gitea.io/gitea/models/actions" +) + +const ( + preStepName = "Set up job" + postStepName = "Complete job" +) + +// FullSteps returns steps with "Set up job" and "Complete job" +func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep { + if len(task.Steps) == 0 { + return fullStepsOfEmptySteps(task) + } + + // firstStep is the first step that has run or running, not include preStep. + // For example, + // 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): firstStep is step1. + // 2. preStep(Success) -> step1(Skipped) -> step2(Success) -> postStep(Success): firstStep is step2. + // 3. preStep(Success) -> step1(Running) -> step2(Waiting) -> postStep(Waiting): firstStep is step1. + // 4. preStep(Success) -> step1(Skipped) -> step2(Skipped) -> postStep(Skipped): firstStep is nil. + // 5. preStep(Success) -> step1(Cancelled) -> step2(Cancelled) -> postStep(Cancelled): firstStep is nil. + var firstStep *actions_model.ActionTaskStep + // lastHasRunStep is the last step that has run. + // For example, + // 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1. + // 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3. + // 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2. + // So its Stopped is the Started of postStep when there are no more steps to run. + var lastHasRunStep *actions_model.ActionTaskStep + + var logIndex int64 + for _, step := range task.Steps { + if firstStep == nil && (step.Status.HasRun() || step.Status.IsRunning()) { + firstStep = step + } + if step.Status.HasRun() { + lastHasRunStep = step + } + logIndex += step.LogLength + } + + preStep := &actions_model.ActionTaskStep{ + Name: preStepName, + LogLength: task.LogLength, + Started: task.Started, + Status: actions_model.StatusRunning, + } + + // No step has run or is running, so preStep is equal to the task + if firstStep == nil { + preStep.Stopped = task.Stopped + preStep.Status = task.Status + } else { + preStep.LogLength = firstStep.LogIndex + preStep.Stopped = firstStep.Started + preStep.Status = actions_model.StatusSuccess + } + logIndex += preStep.LogLength + + if lastHasRunStep == nil { + lastHasRunStep = preStep + } + + postStep := &actions_model.ActionTaskStep{ + Name: postStepName, + Status: actions_model.StatusWaiting, + } + // If the lastHasRunStep is the last step, or it has failed, postStep has started. + if lastHasRunStep.Status.IsFailure() || lastHasRunStep == task.Steps[len(task.Steps)-1] { + postStep.LogIndex = logIndex + postStep.LogLength = task.LogLength - postStep.LogIndex + postStep.Started = lastHasRunStep.Stopped + postStep.Status = actions_model.StatusRunning + } + if task.Status.IsDone() { + postStep.Status = task.Status + postStep.Stopped = task.Stopped + } + ret := make([]*actions_model.ActionTaskStep, 0, len(task.Steps)+2) + ret = append(ret, preStep) + ret = append(ret, task.Steps...) + ret = append(ret, postStep) + + return ret +} + +func fullStepsOfEmptySteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep { + preStep := &actions_model.ActionTaskStep{ + Name: preStepName, + LogLength: task.LogLength, + Started: task.Started, + Stopped: task.Stopped, + Status: actions_model.StatusRunning, + } + + postStep := &actions_model.ActionTaskStep{ + Name: postStepName, + LogIndex: task.LogLength, + Started: task.Stopped, + Stopped: task.Stopped, + Status: actions_model.StatusWaiting, + } + + if task.Status.IsDone() { + preStep.Status = task.Status + if preStep.Status.IsSuccess() { + postStep.Status = actions_model.StatusSuccess + } else { + postStep.Status = actions_model.StatusCancelled + } + } + + return []*actions_model.ActionTaskStep{ + preStep, + postStep, + } +} diff --git a/modules/actions/task_state_test.go b/modules/actions/task_state_test.go new file mode 100644 index 0000000..ff0fd57 --- /dev/null +++ b/modules/actions/task_state_test.go @@ -0,0 +1,165 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + actions_model "code.gitea.io/gitea/models/actions" + + "github.com/stretchr/testify/assert" +) + +func TestFullSteps(t *testing.T) { + tests := []struct { + name string + task *actions_model.ActionTask + want []*actions_model.ActionTaskStep + }{ + { + name: "regular", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{ + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090}, + }, + Status: actions_model.StatusSuccess, + Started: 10000, + Stopped: 10100, + LogLength: 100, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010}, + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090}, + {Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 10100}, + }, + }, + { + name: "failed step", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{ + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 20, Started: 10010, Stopped: 10020}, + {Status: actions_model.StatusFailure, LogIndex: 30, LogLength: 60, Started: 10020, Stopped: 10090}, + {Status: actions_model.StatusCancelled, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + }, + Status: actions_model.StatusFailure, + Started: 10000, + Stopped: 10100, + LogLength: 100, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010}, + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 20, Started: 10010, Stopped: 10020}, + {Status: actions_model.StatusFailure, LogIndex: 30, LogLength: 60, Started: 10020, Stopped: 10090}, + {Status: actions_model.StatusCancelled, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + {Name: postStepName, Status: actions_model.StatusFailure, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 10100}, + }, + }, + { + name: "first step is running", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{ + {Status: actions_model.StatusRunning, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 0}, + }, + Status: actions_model.StatusRunning, + Started: 10000, + Stopped: 10100, + LogLength: 100, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010}, + {Status: actions_model.StatusRunning, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 0}, + {Name: postStepName, Status: actions_model.StatusWaiting, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + }, + }, + { + name: "first step has canceled", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{ + {Status: actions_model.StatusCancelled, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + }, + Status: actions_model.StatusFailure, + Started: 10000, + Stopped: 10100, + LogLength: 100, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusFailure, LogIndex: 0, LogLength: 100, Started: 10000, Stopped: 10100}, + {Status: actions_model.StatusCancelled, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + {Name: postStepName, Status: actions_model.StatusFailure, LogIndex: 100, LogLength: 0, Started: 10100, Stopped: 10100}, + }, + }, + { + name: "empty steps", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{}, + Status: actions_model.StatusSuccess, + Started: 10000, + Stopped: 10100, + LogLength: 100, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 100, Started: 10000, Stopped: 10100}, + {Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 100, LogLength: 0, Started: 10100, Stopped: 10100}, + }, + }, + { + name: "all steps finished but task is running", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{ + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090}, + }, + Status: actions_model.StatusRunning, + Started: 10000, + Stopped: 0, + LogLength: 100, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010}, + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090}, + {Name: postStepName, Status: actions_model.StatusRunning, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 0}, + }, + }, + { + name: "skipped task", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{ + {Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + }, + Status: actions_model.StatusSkipped, + Started: 0, + Stopped: 0, + LogLength: 0, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + {Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + {Name: postStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + }, + }, + { + name: "first step is skipped", + task: &actions_model.ActionTask{ + Steps: []*actions_model.ActionTaskStep{ + {Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090}, + }, + Status: actions_model.StatusSuccess, + Started: 10000, + Stopped: 10100, + LogLength: 100, + }, + want: []*actions_model.ActionTaskStep{ + {Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010}, + {Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, + {Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090}, + {Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 10100}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, FullSteps(tt.task), "FullSteps(%v)", tt.task) + }) + } +} diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go new file mode 100644 index 0000000..94c221e --- /dev/null +++ b/modules/actions/workflows.go @@ -0,0 +1,702 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "bytes" + "io" + "strings" + + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + api "code.gitea.io/gitea/modules/structs" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/gobwas/glob" + "github.com/nektos/act/pkg/jobparser" + "github.com/nektos/act/pkg/model" + "github.com/nektos/act/pkg/workflowpattern" + "gopkg.in/yaml.v3" +) + +type DetectedWorkflow struct { + EntryName string + TriggerEvent *jobparser.Event + Content []byte +} + +func init() { + model.OnDecodeNodeError = func(node yaml.Node, out any, err error) { + // Log the error instead of panic or fatal. + // It will be a big job to refactor act/pkg/model to return decode error, + // so we just log the error and return empty value, and improve it later. + log.Error("Failed to decode node %v into %T: %v", node, out, err) + } +} + +func IsWorkflow(path string) bool { + if (!strings.HasSuffix(path, ".yaml")) && (!strings.HasSuffix(path, ".yml")) { + return false + } + + return strings.HasPrefix(path, ".forgejo/workflows") || strings.HasPrefix(path, ".gitea/workflows") || strings.HasPrefix(path, ".github/workflows") +} + +func ListWorkflows(commit *git.Commit) (git.Entries, error) { + tree, err := commit.SubTree(".forgejo/workflows") + if _, ok := err.(git.ErrNotExist); ok { + tree, err = commit.SubTree(".gitea/workflows") + } + if _, ok := err.(git.ErrNotExist); ok { + tree, err = commit.SubTree(".github/workflows") + } + if _, ok := err.(git.ErrNotExist); ok { + return nil, nil + } + if err != nil { + return nil, err + } + + entries, err := tree.ListEntriesRecursiveFast() + if err != nil { + return nil, err + } + + ret := make(git.Entries, 0, len(entries)) + for _, entry := range entries { + if strings.HasSuffix(entry.Name(), ".yml") || strings.HasSuffix(entry.Name(), ".yaml") { + ret = append(ret, entry) + } + } + return ret, nil +} + +func GetContentFromEntry(entry *git.TreeEntry) ([]byte, error) { + f, err := entry.Blob().DataAsync() + if err != nil { + return nil, err + } + content, err := io.ReadAll(f) + _ = f.Close() + if err != nil { + return nil, err + } + return content, nil +} + +func GetEventsFromContent(content []byte) ([]*jobparser.Event, error) { + workflow, err := model.ReadWorkflow(bytes.NewReader(content)) + if err != nil { + return nil, err + } + events, err := jobparser.ParseRawOn(&workflow.RawOn) + if err != nil { + return nil, err + } + + return events, nil +} + +func DetectWorkflows( + gitRepo *git.Repository, + commit *git.Commit, + triggedEvent webhook_module.HookEventType, + payload api.Payloader, + detectSchedule bool, +) ([]*DetectedWorkflow, []*DetectedWorkflow, error) { + entries, err := ListWorkflows(commit) + if err != nil { + return nil, nil, err + } + + workflows := make([]*DetectedWorkflow, 0, len(entries)) + schedules := make([]*DetectedWorkflow, 0, len(entries)) + for _, entry := range entries { + content, err := GetContentFromEntry(entry) + if err != nil { + return nil, nil, err + } + + // one workflow may have multiple events + events, err := GetEventsFromContent(content) + if err != nil { + log.Warn("ignore invalid workflow %q: %v", entry.Name(), err) + continue + } + for _, evt := range events { + log.Trace("detect workflow %q for event %#v matching %q", entry.Name(), evt, triggedEvent) + if evt.IsSchedule() { + if detectSchedule { + dwf := &DetectedWorkflow{ + EntryName: entry.Name(), + TriggerEvent: evt, + Content: content, + } + schedules = append(schedules, dwf) + } + } else if detectMatched(gitRepo, commit, triggedEvent, payload, evt) { + dwf := &DetectedWorkflow{ + EntryName: entry.Name(), + TriggerEvent: evt, + Content: content, + } + workflows = append(workflows, dwf) + } + } + } + + return workflows, schedules, nil +} + +func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*DetectedWorkflow, error) { + entries, err := ListWorkflows(commit) + if err != nil { + return nil, err + } + + wfs := make([]*DetectedWorkflow, 0, len(entries)) + for _, entry := range entries { + content, err := GetContentFromEntry(entry) + if err != nil { + return nil, err + } + + // one workflow may have multiple events + events, err := GetEventsFromContent(content) + if err != nil { + log.Warn("ignore invalid workflow %q: %v", entry.Name(), err) + continue + } + for _, evt := range events { + if evt.IsSchedule() { + log.Trace("detect scheduled workflow: %q", entry.Name()) + dwf := &DetectedWorkflow{ + EntryName: entry.Name(), + TriggerEvent: evt, + Content: content, + } + wfs = append(wfs, dwf) + } + } + } + + return wfs, nil +} + +func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool { + if !canGithubEventMatch(evt.Name, triggedEvent) { + return false + } + + switch triggedEvent { + case // events with no activity types + webhook_module.HookEventWorkflowDispatch, + webhook_module.HookEventCreate, + webhook_module.HookEventDelete, + webhook_module.HookEventFork, + webhook_module.HookEventWiki, + webhook_module.HookEventSchedule: + if len(evt.Acts()) != 0 { + log.Warn("Ignore unsupported %s event arguments %v", triggedEvent, evt.Acts()) + } + // no special filter parameters for these events, just return true if name matched + return true + + case // push + webhook_module.HookEventPush: + return matchPushEvent(commit, payload.(*api.PushPayload), evt) + + case // issues + webhook_module.HookEventIssues, + webhook_module.HookEventIssueAssign, + webhook_module.HookEventIssueLabel, + webhook_module.HookEventIssueMilestone: + return matchIssuesEvent(payload.(*api.IssuePayload), evt) + + case // issue_comment + webhook_module.HookEventIssueComment, + // `pull_request_comment` is same as `issue_comment` + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_comment-use-issue_comment + webhook_module.HookEventPullRequestComment: + return matchIssueCommentEvent(payload.(*api.IssueCommentPayload), evt) + + case // pull_request + webhook_module.HookEventPullRequest, + webhook_module.HookEventPullRequestSync, + webhook_module.HookEventPullRequestAssign, + webhook_module.HookEventPullRequestLabel, + webhook_module.HookEventPullRequestReviewRequest, + webhook_module.HookEventPullRequestMilestone: + return matchPullRequestEvent(gitRepo, commit, payload.(*api.PullRequestPayload), evt) + + case // pull_request_review + webhook_module.HookEventPullRequestReviewApproved, + webhook_module.HookEventPullRequestReviewRejected: + return matchPullRequestReviewEvent(payload.(*api.PullRequestPayload), evt) + + case // pull_request_review_comment + webhook_module.HookEventPullRequestReviewComment: + return matchPullRequestReviewCommentEvent(payload.(*api.PullRequestPayload), evt) + + case // release + webhook_module.HookEventRelease: + return matchReleaseEvent(payload.(*api.ReleasePayload), evt) + + case // registry_package + webhook_module.HookEventPackage: + return matchPackageEvent(payload.(*api.PackagePayload), evt) + + default: + log.Warn("unsupported event %q", triggedEvent) + return false + } +} + +func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobparser.Event) bool { + // with no special filter parameters + if len(evt.Acts()) == 0 { + return true + } + + matchTimes := 0 + hasBranchFilter := false + hasTagFilter := false + refName := git.RefName(pushPayload.Ref) + // all acts conditions should be satisfied + for cond, vals := range evt.Acts() { + switch cond { + case "branches": + hasBranchFilter = true + if !refName.IsBranch() { + break + } + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Skip(patterns, []string{refName.BranchName()}, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + case "branches-ignore": + hasBranchFilter = true + if !refName.IsBranch() { + break + } + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Filter(patterns, []string{refName.BranchName()}, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + case "tags": + hasTagFilter = true + if !refName.IsTag() { + break + } + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Skip(patterns, []string{refName.TagName()}, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + case "tags-ignore": + hasTagFilter = true + if !refName.IsTag() { + break + } + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Filter(patterns, []string{refName.TagName()}, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + case "paths": + filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before) + if err != nil { + log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err) + } else { + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Skip(patterns, filesChanged, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + } + case "paths-ignore": + filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before) + if err != nil { + log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err) + } else { + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Filter(patterns, filesChanged, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + } + default: + log.Warn("push event unsupported condition %q", cond) + } + } + // if both branch and tag filter are defined in the workflow only one needs to match + if hasBranchFilter && hasTagFilter { + matchTimes++ + } + return matchTimes == len(evt.Acts()) +} + +func matchIssuesEvent(issuePayload *api.IssuePayload, evt *jobparser.Event) bool { + // with no special filter parameters + if len(evt.Acts()) == 0 { + return true + } + + matchTimes := 0 + // all acts conditions should be satisfied + for cond, vals := range evt.Acts() { + switch cond { + case "types": + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues + // Actions with the same name: + // opened, edited, closed, reopened, assigned, unassigned, milestoned, demilestoned + // Actions need to be converted: + // label_updated -> labeled + // label_cleared -> unlabeled + // Unsupported activity types: + // deleted, transferred, pinned, unpinned, locked, unlocked + + action := issuePayload.Action + switch action { + case api.HookIssueLabelUpdated: + action = "labeled" + case api.HookIssueLabelCleared: + action = "unlabeled" + } + for _, val := range vals { + if glob.MustCompile(val, '/').Match(string(action)) { + matchTimes++ + break + } + } + default: + log.Warn("issue event unsupported condition %q", cond) + } + } + return matchTimes == len(evt.Acts()) +} + +func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) bool { + acts := evt.Acts() + activityTypeMatched := false + matchTimes := 0 + + if vals, ok := acts["types"]; !ok { + // defaultly, only pull request `opened`, `reopened` and `synchronized` will trigger workflow + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request + activityTypeMatched = prPayload.Action == api.HookIssueSynchronized || prPayload.Action == api.HookIssueOpened || prPayload.Action == api.HookIssueReOpened + } else { + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request + // Actions with the same name: + // opened, edited, closed, reopened, assigned, unassigned, review_requested, review_request_removed, milestoned, demilestoned + // Actions need to be converted: + // synchronized -> synchronize + // label_updated -> labeled + // label_cleared -> unlabeled + // Unsupported activity types: + // converted_to_draft, ready_for_review, locked, unlocked, auto_merge_enabled, auto_merge_disabled, enqueued, dequeued + + action := prPayload.Action + switch action { + case api.HookIssueSynchronized: + action = "synchronize" + case api.HookIssueLabelUpdated: + action = "labeled" + case api.HookIssueLabelCleared: + action = "unlabeled" + } + log.Trace("matching pull_request %s with %v", action, vals) + for _, val := range vals { + if glob.MustCompile(val, '/').Match(string(action)) { + activityTypeMatched = true + matchTimes++ + break + } + } + } + + var ( + headCommit = commit + err error + ) + if evt.Name == GithubEventPullRequestTarget && (len(acts["paths"]) > 0 || len(acts["paths-ignore"]) > 0) { + headCommit, err = gitRepo.GetCommit(prPayload.PullRequest.Head.Sha) + if err != nil { + log.Error("GetCommit [ref: %s]: %v", prPayload.PullRequest.Head.Sha, err) + return false + } + } + + // all acts conditions should be satisfied + for cond, vals := range acts { + switch cond { + case "types": + // types have been checked + continue + case "branches": + refName := git.RefName(prPayload.PullRequest.Base.Ref) + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Skip(patterns, []string{refName.ShortName()}, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + case "branches-ignore": + refName := git.RefName(prPayload.PullRequest.Base.Ref) + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Filter(patterns, []string{refName.ShortName()}, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + case "paths": + filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.Base.Ref) + if err != nil { + log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err) + } else { + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Skip(patterns, filesChanged, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + } + case "paths-ignore": + filesChanged, err := headCommit.GetFilesChangedSinceCommit(prPayload.PullRequest.Base.Ref) + if err != nil { + log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", headCommit.ID.String(), err) + } else { + patterns, err := workflowpattern.CompilePatterns(vals...) + if err != nil { + break + } + if !workflowpattern.Filter(patterns, filesChanged, &workflowpattern.EmptyTraceWriter{}) { + matchTimes++ + } + } + default: + log.Warn("pull request event unsupported condition %q", cond) + } + } + return activityTypeMatched && matchTimes == len(evt.Acts()) +} + +func matchIssueCommentEvent(issueCommentPayload *api.IssueCommentPayload, evt *jobparser.Event) bool { + // with no special filter parameters + if len(evt.Acts()) == 0 { + return true + } + + matchTimes := 0 + // all acts conditions should be satisfied + for cond, vals := range evt.Acts() { + switch cond { + case "types": + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment + // Actions with the same name: + // created, edited, deleted + // Actions need to be converted: + // NONE + // Unsupported activity types: + // NONE + + for _, val := range vals { + if glob.MustCompile(val, '/').Match(string(issueCommentPayload.Action)) { + matchTimes++ + break + } + } + default: + log.Warn("issue comment event unsupported condition %q", cond) + } + } + return matchTimes == len(evt.Acts()) +} + +func matchPullRequestReviewEvent(prPayload *api.PullRequestPayload, evt *jobparser.Event) bool { + // with no special filter parameters + if len(evt.Acts()) == 0 { + return true + } + + matchTimes := 0 + // all acts conditions should be satisfied + for cond, vals := range evt.Acts() { + switch cond { + case "types": + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_review + // Activity types with the same name: + // NONE + // Activity types need to be converted: + // reviewed -> submitted + // reviewed -> edited + // Unsupported activity types: + // dismissed + + actions := make([]string, 0) + if prPayload.Action == api.HookIssueReviewed { + // the `reviewed` HookIssueAction can match the two activity types: `submitted` and `edited` + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_review + actions = append(actions, "submitted", "edited") + } + + matched := false + for _, val := range vals { + for _, action := range actions { + if glob.MustCompile(val, '/').Match(action) { + matched = true + break + } + } + if matched { + break + } + } + if matched { + matchTimes++ + } + default: + log.Warn("pull request review event unsupported condition %q", cond) + } + } + return matchTimes == len(evt.Acts()) +} + +func matchPullRequestReviewCommentEvent(prPayload *api.PullRequestPayload, evt *jobparser.Event) bool { + // with no special filter parameters + if len(evt.Acts()) == 0 { + return true + } + + matchTimes := 0 + // all acts conditions should be satisfied + for cond, vals := range evt.Acts() { + switch cond { + case "types": + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_review_comment + // Activity types with the same name: + // NONE + // Activity types need to be converted: + // reviewed -> created + // reviewed -> edited + // Unsupported activity types: + // deleted + + actions := make([]string, 0) + if prPayload.Action == api.HookIssueReviewed { + // the `reviewed` HookIssueAction can match the two activity types: `created` and `edited` + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_review_comment + actions = append(actions, "created", "edited") + } + + matched := false + for _, val := range vals { + for _, action := range actions { + if glob.MustCompile(val, '/').Match(action) { + matched = true + break + } + } + if matched { + break + } + } + if matched { + matchTimes++ + } + default: + log.Warn("pull request review comment event unsupported condition %q", cond) + } + } + return matchTimes == len(evt.Acts()) +} + +func matchReleaseEvent(payload *api.ReleasePayload, evt *jobparser.Event) bool { + // with no special filter parameters + if len(evt.Acts()) == 0 { + return true + } + + matchTimes := 0 + // all acts conditions should be satisfied + for cond, vals := range evt.Acts() { + switch cond { + case "types": + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release + // Activity types with the same name: + // published + // Activity types need to be converted: + // updated -> edited + // Unsupported activity types: + // unpublished, created, deleted, prereleased, released + + action := payload.Action + if action == api.HookReleaseUpdated { + action = "edited" + } + for _, val := range vals { + if glob.MustCompile(val, '/').Match(string(action)) { + matchTimes++ + break + } + } + default: + log.Warn("release event unsupported condition %q", cond) + } + } + return matchTimes == len(evt.Acts()) +} + +func matchPackageEvent(payload *api.PackagePayload, evt *jobparser.Event) bool { + // with no special filter parameters + if len(evt.Acts()) == 0 { + return true + } + + matchTimes := 0 + // all acts conditions should be satisfied + for cond, vals := range evt.Acts() { + switch cond { + case "types": + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#registry_package + // Activity types with the same name: + // NONE + // Activity types need to be converted: + // created -> published + // Unsupported activity types: + // updated + + action := payload.Action + if action == api.HookPackageCreated { + action = "published" + } + for _, val := range vals { + if glob.MustCompile(val, '/').Match(string(action)) { + matchTimes++ + break + } + } + default: + log.Warn("package event unsupported condition %q", cond) + } + } + return matchTimes == len(evt.Acts()) +} diff --git a/modules/actions/workflows_test.go b/modules/actions/workflows_test.go new file mode 100644 index 0000000..965d01f --- /dev/null +++ b/modules/actions/workflows_test.go @@ -0,0 +1,163 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + "code.gitea.io/gitea/modules/git" + api "code.gitea.io/gitea/modules/structs" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDetectMatched(t *testing.T) { + testCases := []struct { + desc string + commit *git.Commit + triggeredEvent webhook_module.HookEventType + payload api.Payloader + yamlOn string + expected bool + }{ + { + desc: "HookEventCreate(create) matches GithubEventCreate(create)", + triggeredEvent: webhook_module.HookEventCreate, + payload: nil, + yamlOn: "on: create", + expected: true, + }, + { + desc: "HookEventIssues(issues) `opened` action matches GithubEventIssues(issues)", + triggeredEvent: webhook_module.HookEventIssues, + payload: &api.IssuePayload{Action: api.HookIssueOpened}, + yamlOn: "on: issues", + expected: true, + }, + { + desc: "HookEventIssueComment(issue_comment) `created` action matches GithubEventIssueComment(issue_comment)", + triggeredEvent: webhook_module.HookEventIssueComment, + payload: &api.IssueCommentPayload{Action: api.HookIssueCommentCreated}, + yamlOn: "on:\n issue_comment:\n types: [created]", + expected: true, + }, + + { + desc: "HookEventIssues(issues) `milestoned` action matches GithubEventIssues(issues)", + triggeredEvent: webhook_module.HookEventIssues, + payload: &api.IssuePayload{Action: api.HookIssueMilestoned}, + yamlOn: "on: issues", + expected: true, + }, + + { + desc: "HookEventPullRequestSync(pull_request_sync) matches GithubEventPullRequest(pull_request)", + triggeredEvent: webhook_module.HookEventPullRequestSync, + payload: &api.PullRequestPayload{Action: api.HookIssueSynchronized}, + yamlOn: "on: pull_request", + expected: true, + }, + { + desc: "HookEventPullRequest(pull_request) `label_updated` action doesn't match GithubEventPullRequest(pull_request) with no activity type", + triggeredEvent: webhook_module.HookEventPullRequest, + payload: &api.PullRequestPayload{Action: api.HookIssueLabelUpdated}, + yamlOn: "on: pull_request", + expected: false, + }, + { + desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with no activity type", + triggeredEvent: webhook_module.HookEventPullRequest, + payload: &api.PullRequestPayload{Action: api.HookIssueClosed}, + yamlOn: "on: pull_request", + expected: false, + }, + { + desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with branches", + triggeredEvent: webhook_module.HookEventPullRequest, + payload: &api.PullRequestPayload{ + Action: api.HookIssueClosed, + PullRequest: &api.PullRequest{ + Base: &api.PRBranchInfo{}, + }, + }, + yamlOn: "on:\n pull_request:\n branches: [main]", + expected: false, + }, + { + desc: "HookEventPullRequest(pull_request) `label_updated` action matches GithubEventPullRequest(pull_request) with `label` activity type", + triggeredEvent: webhook_module.HookEventPullRequest, + payload: &api.PullRequestPayload{Action: api.HookIssueLabelUpdated}, + yamlOn: "on:\n pull_request:\n types: [labeled]", + expected: true, + }, + { + desc: "HookEventPullRequestReviewComment(pull_request_review_comment) matches GithubEventPullRequestReviewComment(pull_request_review_comment)", + triggeredEvent: webhook_module.HookEventPullRequestReviewComment, + payload: &api.PullRequestPayload{Action: api.HookIssueReviewed}, + yamlOn: "on:\n pull_request_review_comment:\n types: [created]", + expected: true, + }, + { + desc: "HookEventPullRequestReviewRejected(pull_request_review_rejected) doesn't match GithubEventPullRequestReview(pull_request_review) with `dismissed` activity type (we don't support `dismissed` at present)", + triggeredEvent: webhook_module.HookEventPullRequestReviewRejected, + payload: &api.PullRequestPayload{Action: api.HookIssueReviewed}, + yamlOn: "on:\n pull_request_review:\n types: [dismissed]", + expected: false, + }, + { + desc: "HookEventRelease(release) `published` action matches GithubEventRelease(release) with `published` activity type", + triggeredEvent: webhook_module.HookEventRelease, + payload: &api.ReleasePayload{Action: api.HookReleasePublished}, + yamlOn: "on:\n release:\n types: [published]", + expected: true, + }, + { + desc: "HookEventRelease(updated) `updated` action matches GithubEventRelease(edited) with `edited` activity type", + triggeredEvent: webhook_module.HookEventRelease, + payload: &api.ReleasePayload{Action: api.HookReleaseUpdated}, + yamlOn: "on:\n release:\n types: [edited]", + expected: true, + }, + + { + desc: "HookEventPackage(package) `created` action doesn't match GithubEventRegistryPackage(registry_package) with `updated` activity type", + triggeredEvent: webhook_module.HookEventPackage, + payload: &api.PackagePayload{Action: api.HookPackageCreated}, + yamlOn: "on:\n registry_package:\n types: [updated]", + expected: false, + }, + { + desc: "HookEventWiki(wiki) matches GithubEventGollum(gollum)", + triggeredEvent: webhook_module.HookEventWiki, + payload: nil, + yamlOn: "on: gollum", + expected: true, + }, + { + desc: "HookEventSchedule(schedule) matches GithubEventSchedule(schedule)", + triggeredEvent: webhook_module.HookEventSchedule, + payload: nil, + yamlOn: "on: schedule", + expected: true, + }, + { + desc: "HookEventWorkflowDispatch(workflow_dispatch) matches GithubEventWorkflowDispatch(workflow_dispatch)", + triggeredEvent: webhook_module.HookEventWorkflowDispatch, + payload: nil, + yamlOn: "on: workflow_dispatch", + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + evts, err := GetEventsFromContent([]byte(tc.yamlOn)) + require.NoError(t, err) + assert.Len(t, evts, 1) + assert.Equal(t, tc.expected, detectMatched(nil, tc.commit, tc.triggeredEvent, tc.payload, evts[0])) + }) + } +} diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go new file mode 100644 index 0000000..064d898 --- /dev/null +++ b/modules/activitypub/client.go @@ -0,0 +1,273 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// TODO: Think about whether this should be moved to services/activitypub (compare to exosy/services/activitypub/client.go) +package activitypub + +import ( + "bytes" + "context" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + "io" + "net/http" + "strings" + "time" + + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/proxy" + "code.gitea.io/gitea/modules/setting" + + "github.com/go-fed/httpsig" +) + +const ( + // ActivityStreamsContentType const + ActivityStreamsContentType = `application/ld+json; profile="https://www.w3.org/ns/activitystreams"` + httpsigExpirationTime = 60 +) + +func CurrentTime() string { + return time.Now().UTC().Format(http.TimeFormat) +} + +func containsRequiredHTTPHeaders(method string, headers []string) error { + var hasRequestTarget, hasDate, hasDigest, hasHost bool + for _, header := range headers { + hasRequestTarget = hasRequestTarget || header == httpsig.RequestTarget + hasDate = hasDate || header == "Date" + hasDigest = hasDigest || header == "Digest" + hasHost = hasHost || header == "Host" + } + if !hasRequestTarget { + return fmt.Errorf("missing http header for %s: %s", method, httpsig.RequestTarget) + } else if !hasDate { + return fmt.Errorf("missing http header for %s: Date", method) + } else if !hasHost { + return fmt.Errorf("missing http header for %s: Host", method) + } else if !hasDigest && method != http.MethodGet { + return fmt.Errorf("missing http header for %s: Digest", method) + } + return nil +} + +// Client struct +type ClientFactory struct { + client *http.Client + algs []httpsig.Algorithm + digestAlg httpsig.DigestAlgorithm + getHeaders []string + postHeaders []string +} + +// NewClient function +func NewClientFactory() (c *ClientFactory, err error) { + if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil { + return nil, err + } else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil { + return nil, err + } + + c = &ClientFactory{ + client: &http.Client{ + Transport: &http.Transport{ + Proxy: proxy.Proxy(), + }, + Timeout: 5 * time.Second, + }, + algs: setting.HttpsigAlgs, + digestAlg: httpsig.DigestAlgorithm(setting.Federation.DigestAlgorithm), + getHeaders: setting.Federation.GetHeaders, + postHeaders: setting.Federation.PostHeaders, + } + return c, err +} + +type APClientFactory interface { + WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) +} + +// Client struct +type Client struct { + client *http.Client + algs []httpsig.Algorithm + digestAlg httpsig.DigestAlgorithm + getHeaders []string + postHeaders []string + priv *rsa.PrivateKey + pubID string +} + +// NewRequest function +func (cf *ClientFactory) WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) { + priv, err := GetPrivateKey(ctx, user) + if err != nil { + return nil, err + } + privPem, _ := pem.Decode([]byte(priv)) + privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes) + if err != nil { + return nil, err + } + + c := Client{ + client: cf.client, + algs: cf.algs, + digestAlg: cf.digestAlg, + getHeaders: cf.getHeaders, + postHeaders: cf.postHeaders, + priv: privParsed, + pubID: pubID, + } + return &c, nil +} + +// NewRequest function +func (c *Client) newRequest(method string, b []byte, to string) (req *http.Request, err error) { + buf := bytes.NewBuffer(b) + req, err = http.NewRequest(method, to, buf) + if err != nil { + return nil, err + } + req.Header.Add("Accept", "application/json, "+ActivityStreamsContentType) + req.Header.Add("Date", CurrentTime()) + req.Header.Add("Host", req.URL.Host) + req.Header.Add("User-Agent", "Gitea/"+setting.AppVer) + req.Header.Add("Content-Type", ActivityStreamsContentType) + + return req, err +} + +// Post function +func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) { + var req *http.Request + if req, err = c.newRequest(http.MethodPost, b, to); err != nil { + return nil, err + } + + signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime) + if err != nil { + return nil, err + } + if err := signer.SignRequest(c.priv, c.pubID, req, b); err != nil { + return nil, err + } + + resp, err = c.client.Do(req) + return resp, err +} + +// Create an http GET request with forgejo/gitea specific headers +func (c *Client) Get(to string) (resp *http.Response, err error) { + var req *http.Request + if req, err = c.newRequest(http.MethodGet, nil, to); err != nil { + return nil, err + } + signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.getHeaders, httpsig.Signature, httpsigExpirationTime) + if err != nil { + return nil, err + } + if err := signer.SignRequest(c.priv, c.pubID, req, nil); err != nil { + return nil, err + } + + resp, err = c.client.Do(req) + return resp, err +} + +// Create an http GET request with forgejo/gitea specific headers +func (c *Client) GetBody(uri string) ([]byte, error) { + response, err := c.Get(uri) + if err != nil { + return nil, err + } + log.Debug("Client: got status: %v", response.Status) + if response.StatusCode != 200 { + err = fmt.Errorf("got non 200 status code for id: %v", uri) + return nil, err + } + defer response.Body.Close() + body, err := io.ReadAll(response.Body) + if err != nil { + return nil, err + } + log.Debug("Client: got body: %v", charLimiter(string(body), 120)) + return body, nil +} + +// Limit number of characters in a string (useful to prevent log injection attacks and overly long log outputs) +// Thanks to https://www.socketloop.com/tutorials/golang-characters-limiter-example +func charLimiter(s string, limit int) string { + reader := strings.NewReader(s) + buff := make([]byte, limit) + n, _ := io.ReadAtLeast(reader, buff, limit) + if n != 0 { + return fmt.Sprint(string(buff), "...") + } + return s +} + +type APClient interface { + newRequest(method string, b []byte, to string) (req *http.Request, err error) + Post(b []byte, to string) (resp *http.Response, err error) + Get(to string) (resp *http.Response, err error) + GetBody(uri string) ([]byte, error) +} + +// contextKey is a value for use with context.WithValue. +type contextKey struct { + name string +} + +// clientFactoryContextKey is a context key. It is used with context.Value() to get the current Food for the context +var ( + clientFactoryContextKey = &contextKey{"clientFactory"} + _ APClientFactory = &ClientFactory{} +) + +// Context represents an activitypub client factory context +type Context struct { + context.Context + e APClientFactory +} + +func NewContext(ctx context.Context, e APClientFactory) *Context { + return &Context{ + Context: ctx, + e: e, + } +} + +// APClientFactory represents an activitypub client factory +func (ctx *Context) APClientFactory() APClientFactory { + return ctx.e +} + +// provides APClientFactory +type GetAPClient interface { + GetClientFactory() APClientFactory +} + +// GetClientFactory will get an APClientFactory from this context or returns the default implementation +func GetClientFactory(ctx context.Context) (APClientFactory, error) { + if e := getClientFactory(ctx); e != nil { + return e, nil + } + return NewClientFactory() +} + +// getClientFactory will get an APClientFactory from this context or return nil +func getClientFactory(ctx context.Context) APClientFactory { + if clientFactory, ok := ctx.(APClientFactory); ok { + return clientFactory + } + clientFactoryInterface := ctx.Value(clientFactoryContextKey) + if clientFactoryInterface != nil { + return clientFactoryInterface.(GetAPClient).GetClientFactory() + } + return nil +} diff --git a/modules/activitypub/client_test.go b/modules/activitypub/client_test.go new file mode 100644 index 0000000..647a0a5 --- /dev/null +++ b/modules/activitypub/client_test.go @@ -0,0 +1,138 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2023 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package activitypub + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "regexp" + "testing" + "time" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCurrentTime(t *testing.T) { + date := CurrentTime() + _, err := time.Parse(http.TimeFormat, date) + require.NoError(t, err) + assert.Equal(t, "GMT", date[len(date)-3:]) +} + +/* ToDo: Set Up tests for http get requests + +Set up an expected response for GET on api with user-id = 1: +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1" + ], + "id": "http://localhost:3000/api/v1/activitypub/user-id/1", + "type": "Person", + "icon": { + "type": "Image", + "mediaType": "image/png", + "url": "http://localhost:3000/avatar/3120fd0edc57d5d41230013ad88232e2" + }, + "url": "http://localhost:3000/me", + "inbox": "http://localhost:3000/api/v1/activitypub/user-id/1/inbox", + "outbox": "http://localhost:3000/api/v1/activitypub/user-id/1/outbox", + "preferredUsername": "me", + "publicKey": { + "id": "http://localhost:3000/api/v1/activitypub/user-id/1#main-key", + "owner": "http://localhost:3000/api/v1/activitypub/user-id/1", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAo1VDZGWQBDTWKhpWiPQp\n7nD94UsKkcoFwDQVuxE3bMquKEHBomB4cwUnVou922YkL3AmSOr1sX2yJQGqnCLm\nOeKS74/mCIAoYlu0d75bqY4A7kE2VrQmQLZBbmpCTfrPqDaE6Mfm/kXaX7+hsrZS\n4bVvzZCYq8sjtRxdPk+9ku2QhvznwTRlWLvwHmFSGtlQYPRu+f/XqoVM/DVRA/Is\nwDk9yiNIecV+Isus0CBq1jGQkfuVNu1GK2IvcSg9MoDm3VH/tCayAP+xWm0g7sC8\nKay6Y/khvTvE7bWEKGQsJGvi3+4wITLVLVt+GoVOuCzdbhTV2CHBzn7h30AoZD0N\nY6eyb+Q142JykoHadcRwh1a36wgoG7E496wPvV3ST8xdiClca8cDNhOzCj8woY+t\nTFCMl32U3AJ4e/cAsxKRocYLZqc95dDqdNQiIyiRMMkf5NaA/QvelY4PmFuHC0WR\nVuJ4A3mcti2QLS9j0fSwSJdlfolgW6xaPgjdvuSQsgX1AgMBAAE=\n-----END PUBLIC KEY-----\n" + } +} + +Set up a user called "me" for all tests + + + +*/ + +func TestClientCtx(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + pubID := "myGpgId" + cf, err := NewClientFactory() + log.Debug("ClientFactory: %v\nError: %v", cf, err) + require.NoError(t, err) + + c, err := cf.WithKeys(db.DefaultContext, user, pubID) + + log.Debug("Client: %v\nError: %v", c, err) + require.NoError(t, err) + _ = NewContext(db.DefaultContext, cf) +} + +/* TODO: bring this test to work or delete +func TestActivityPubSignedGet(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1, Name: "me"}) + pubID := "myGpgId" + c, err := NewClient(db.DefaultContext, user, pubID) + require.NoError(t, err) + + expected := "TestActivityPubSignedGet" + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Regexp(t, regexp.MustCompile("^"+setting.Federation.DigestAlgorithm), r.Header.Get("Digest")) + assert.Contains(t, r.Header.Get("Signature"), pubID) + assert.Equal(t, r.Header.Get("Content-Type"), ActivityStreamsContentType) + body, err := io.ReadAll(r.Body) + require.NoError(t, err) + assert.Equal(t, expected, string(body)) + fmt.Fprint(w, expected) + })) + defer srv.Close() + + r, err := c.Get(srv.URL) + require.NoError(t, err) + defer r.Body.Close() + body, err := io.ReadAll(r.Body) + require.NoError(t, err) + assert.Equal(t, expected, string(body)) + +} +*/ + +func TestActivityPubSignedPost(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + pubID := "https://example.com/pubID" + cf, err := NewClientFactory() + require.NoError(t, err) + c, err := cf.WithKeys(db.DefaultContext, user, pubID) + require.NoError(t, err) + + expected := "BODY" + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Regexp(t, regexp.MustCompile("^"+setting.Federation.DigestAlgorithm), r.Header.Get("Digest")) + assert.Contains(t, r.Header.Get("Signature"), pubID) + assert.Equal(t, ActivityStreamsContentType, r.Header.Get("Content-Type")) + body, err := io.ReadAll(r.Body) + require.NoError(t, err) + assert.Equal(t, expected, string(body)) + fmt.Fprint(w, expected) + })) + defer srv.Close() + + r, err := c.Post([]byte(expected), srv.URL) + require.NoError(t, err) + defer r.Body.Close() + body, err := io.ReadAll(r.Body) + require.NoError(t, err) + assert.Equal(t, expected, string(body)) +} diff --git a/modules/activitypub/main_test.go b/modules/activitypub/main_test.go new file mode 100644 index 0000000..4591f1f --- /dev/null +++ b/modules/activitypub/main_test.go @@ -0,0 +1,18 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package activitypub + +import ( + "testing" + + "code.gitea.io/gitea/models/unittest" + + _ "code.gitea.io/gitea/models" + _ "code.gitea.io/gitea/models/actions" + _ "code.gitea.io/gitea/models/activities" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} diff --git a/modules/activitypub/user_settings.go b/modules/activitypub/user_settings.go new file mode 100644 index 0000000..7f939af --- /dev/null +++ b/modules/activitypub/user_settings.go @@ -0,0 +1,48 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package activitypub + +import ( + "context" + + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/util" +) + +const rsaBits = 3072 + +// GetKeyPair function returns a user's private and public keys +func GetKeyPair(ctx context.Context, user *user_model.User) (pub, priv string, err error) { + var settings map[string]*user_model.Setting + settings, err = user_model.GetSettings(ctx, user.ID, []string{user_model.UserActivityPubPrivPem, user_model.UserActivityPubPubPem}) + if err != nil { + return pub, priv, err + } else if len(settings) == 0 { + if priv, pub, err = util.GenerateKeyPair(rsaBits); err != nil { + return pub, priv, err + } + if err = user_model.SetUserSetting(ctx, user.ID, user_model.UserActivityPubPrivPem, priv); err != nil { + return pub, priv, err + } + if err = user_model.SetUserSetting(ctx, user.ID, user_model.UserActivityPubPubPem, pub); err != nil { + return pub, priv, err + } + return pub, priv, err + } + priv = settings[user_model.UserActivityPubPrivPem].SettingValue + pub = settings[user_model.UserActivityPubPubPem].SettingValue + return pub, priv, err +} + +// GetPublicKey function returns a user's public key +func GetPublicKey(ctx context.Context, user *user_model.User) (pub string, err error) { + pub, _, err = GetKeyPair(ctx, user) + return pub, err +} + +// GetPrivateKey function returns a user's private key +func GetPrivateKey(ctx context.Context, user *user_model.User) (priv string, err error) { + _, priv, err = GetKeyPair(ctx, user) + return priv, err +} diff --git a/modules/activitypub/user_settings_test.go b/modules/activitypub/user_settings_test.go new file mode 100644 index 0000000..f510e7a --- /dev/null +++ b/modules/activitypub/user_settings_test.go @@ -0,0 +1,30 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package activitypub + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + + _ "code.gitea.io/gitea/models" // https://forum.gitea.com/t/testfixtures-could-not-clean-table-access-no-such-table-access/4137/4 + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUserSettings(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + pub, priv, err := GetKeyPair(db.DefaultContext, user1) + require.NoError(t, err) + pub1, err := GetPublicKey(db.DefaultContext, user1) + require.NoError(t, err) + assert.Equal(t, pub, pub1) + priv1, err := GetPrivateKey(db.DefaultContext, user1) + require.NoError(t, err) + assert.Equal(t, priv, priv1) +} diff --git a/modules/analyze/code_language.go b/modules/analyze/code_language.go new file mode 100644 index 0000000..74e7a06 --- /dev/null +++ b/modules/analyze/code_language.go @@ -0,0 +1,27 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package analyze + +import ( + "path/filepath" + + "github.com/go-enry/go-enry/v2" +) + +// GetCodeLanguage detects code language based on file name and content +func GetCodeLanguage(filename string, content []byte) string { + if language, ok := enry.GetLanguageByExtension(filename); ok { + return language + } + + if language, ok := enry.GetLanguageByFilename(filename); ok { + return language + } + + if len(content) == 0 { + return enry.OtherLanguage + } + + return enry.GetLanguage(filepath.Base(filename), content) +} diff --git a/modules/analyze/generated.go b/modules/analyze/generated.go new file mode 100644 index 0000000..f608387 --- /dev/null +++ b/modules/analyze/generated.go @@ -0,0 +1,27 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package analyze + +import ( + "path/filepath" + "strings" + + "github.com/go-enry/go-enry/v2/data" +) + +// IsGenerated returns whether or not path is a generated path. +func IsGenerated(path string) bool { + ext := strings.ToLower(filepath.Ext(path)) + if _, ok := data.GeneratedCodeExtensions[ext]; ok { + return true + } + + for _, m := range data.GeneratedCodeNameMatchers { + if m(path) { + return true + } + } + + return false +} diff --git a/modules/analyze/vendor.go b/modules/analyze/vendor.go new file mode 100644 index 0000000..adcca92 --- /dev/null +++ b/modules/analyze/vendor.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package analyze + +import ( + "github.com/go-enry/go-enry/v2" +) + +// IsVendor returns whether or not path is a vendor path. +func IsVendor(path string) bool { + return enry.IsVendor(path) +} diff --git a/modules/analyze/vendor_test.go b/modules/analyze/vendor_test.go new file mode 100644 index 0000000..aafd3c4 --- /dev/null +++ b/modules/analyze/vendor_test.go @@ -0,0 +1,41 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package analyze + +import "testing" + +func TestIsVendor(t *testing.T) { + tests := []struct { + path string + want bool + }{ + {"cache/", true}, + {"random/cache/", true}, + {"cache", false}, + {"dependencies/", true}, + {"Dependencies/", true}, + {"dependency/", false}, + {"dist/", true}, + {"dist", false}, + {"random/dist/", true}, + {"random/dist", false}, + {"deps/", true}, + {"configure", true}, + {"a/configure", true}, + {"config.guess", true}, + {"config.guess/", false}, + {".vscode/", true}, + {"doc/_build/", true}, + {"a/docs/_build/", true}, + {"a/dasdocs/_build-vsdoc.js", true}, + {"a/dasdocs/_build-vsdoc.j", false}, + } + for _, tt := range tests { + t.Run(tt.path, func(t *testing.T) { + if got := IsVendor(tt.path); got != tt.want { + t.Errorf("IsVendor() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go new file mode 100644 index 0000000..9678d23 --- /dev/null +++ b/modules/assetfs/layered.go @@ -0,0 +1,256 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package assetfs + +import ( + "context" + "fmt" + "io" + "io/fs" + "net/http" + "os" + "path/filepath" + "sort" + "time" + + "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/process" + "code.gitea.io/gitea/modules/util" + + "github.com/fsnotify/fsnotify" +) + +// Layer represents a layer in a layered asset file-system. It has a name and works like http.FileSystem +type Layer struct { + name string + fs http.FileSystem + localPath string +} + +func (l *Layer) Name() string { + return l.name +} + +// Open opens the named file. The caller is responsible for closing the file. +func (l *Layer) Open(name string) (http.File, error) { + return l.fs.Open(name) +} + +// Local returns a new Layer with the given name, it serves files from the given local path. +func Local(name, base string, sub ...string) *Layer { + // TODO: the old behavior (StaticRootPath might not be absolute), not ideal, just keep the same as before + // Ideally, the caller should guarantee the base is absolute, guessing a relative path based on the current working directory is unreliable. + base, err := filepath.Abs(base) + if err != nil { + // This should never happen in a real system. If it happens, the user must have already been in trouble: the system is not able to resolve its own paths. + panic(fmt.Sprintf("Unable to get absolute path for %q: %v", base, err)) + } + root := util.FilePathJoinAbs(base, sub...) + return &Layer{name: name, fs: http.Dir(root), localPath: root} +} + +// Bindata returns a new Layer with the given name, it serves files from the given bindata asset. +func Bindata(name string, fs http.FileSystem) *Layer { + return &Layer{name: name, fs: fs} +} + +// LayeredFS is a layered asset file-system. It works like http.FileSystem, but it can have multiple layers. +// The first layer is the top layer, and it will be used first. +// If the file is not found in the top layer, it will be searched in the next layer. +type LayeredFS struct { + layers []*Layer +} + +// Layered returns a new LayeredFS with the given layers. The first layer is the top layer. +func Layered(layers ...*Layer) *LayeredFS { + return &LayeredFS{layers: layers} +} + +// Open opens the named file. The caller is responsible for closing the file. +func (l *LayeredFS) Open(name string) (http.File, error) { + for _, layer := range l.layers { + f, err := layer.Open(name) + if err == nil || !os.IsNotExist(err) { + return f, err + } + } + return nil, fs.ErrNotExist +} + +// ReadFile reads the named file. +func (l *LayeredFS) ReadFile(elems ...string) ([]byte, error) { + bs, _, err := l.ReadLayeredFile(elems...) + return bs, err +} + +// ReadLayeredFile reads the named file, and returns the layer name. +func (l *LayeredFS) ReadLayeredFile(elems ...string) ([]byte, string, error) { + name := util.PathJoinRel(elems...) + for _, layer := range l.layers { + f, err := layer.Open(name) + if os.IsNotExist(err) { + continue + } else if err != nil { + return nil, layer.name, err + } + bs, err := io.ReadAll(f) + _ = f.Close() + return bs, layer.name, err + } + return nil, "", fs.ErrNotExist +} + +func shouldInclude(info fs.FileInfo, fileMode ...bool) bool { + if util.CommonSkip(info.Name()) { + return false + } + if len(fileMode) == 0 { + return true + } else if len(fileMode) == 1 { + return fileMode[0] == !info.Mode().IsDir() + } + panic("too many arguments for fileMode in shouldInclude") +} + +func readDir(layer *Layer, name string) ([]fs.FileInfo, error) { + f, err := layer.Open(name) + if os.IsNotExist(err) { + return nil, nil + } else if err != nil { + return nil, err + } + defer f.Close() + return f.Readdir(-1) +} + +// ListFiles lists files/directories in the given directory. The fileMode controls the returned files. +// * omitted: all files and directories will be returned. +// * true: only files will be returned. +// * false: only directories will be returned. +// The returned files are sorted by name. +func (l *LayeredFS) ListFiles(name string, fileMode ...bool) ([]string, error) { + fileSet := make(container.Set[string]) + for _, layer := range l.layers { + infos, err := readDir(layer, name) + if err != nil { + return nil, err + } + for _, info := range infos { + if shouldInclude(info, fileMode...) { + fileSet.Add(info.Name()) + } + } + } + files := fileSet.Values() + sort.Strings(files) + return files, nil +} + +// ListAllFiles returns files/directories in the given directory, including subdirectories, recursively. +// The fileMode controls the returned files: +// * omitted: all files and directories will be returned. +// * true: only files will be returned. +// * false: only directories will be returned. +// The returned files are sorted by name. +func (l *LayeredFS) ListAllFiles(name string, fileMode ...bool) ([]string, error) { + return listAllFiles(l.layers, name, fileMode...) +} + +func listAllFiles(layers []*Layer, name string, fileMode ...bool) ([]string, error) { + fileSet := make(container.Set[string]) + var list func(dir string) error + list = func(dir string) error { + for _, layer := range layers { + infos, err := readDir(layer, dir) + if err != nil { + return err + } + for _, info := range infos { + path := util.PathJoinRelX(dir, info.Name()) + if shouldInclude(info, fileMode...) { + fileSet.Add(path) + } + if info.IsDir() { + if err = list(path); err != nil { + return err + } + } + } + } + return nil + } + if err := list(name); err != nil { + return nil, err + } + files := fileSet.Values() + sort.Strings(files) + return files, nil +} + +// WatchLocalChanges watches local changes in the file-system. It's used to help to reload assets when the local file-system changes. +func (l *LayeredFS) WatchLocalChanges(ctx context.Context, callback func()) { + ctx, _, finished := process.GetManager().AddTypedContext(ctx, "Asset Local FileSystem Watcher", process.SystemProcessType, true) + defer finished() + + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Error("Unable to create watcher for asset local file-system: %v", err) + return + } + defer watcher.Close() + + for _, layer := range l.layers { + if layer.localPath == "" { + continue + } + layerDirs, err := listAllFiles([]*Layer{layer}, ".", false) + if err != nil { + log.Error("Unable to list directories for asset local file-system %q: %v", layer.localPath, err) + continue + } + layerDirs = append(layerDirs, ".") + for _, dir := range layerDirs { + if err = watcher.Add(util.FilePathJoinAbs(layer.localPath, dir)); err != nil && !os.IsNotExist(err) { + log.Error("Unable to watch directory %s: %v", dir, err) + } + } + } + + debounce := util.Debounce(100 * time.Millisecond) + + for { + select { + case <-ctx.Done(): + return + case event, ok := <-watcher.Events: + if !ok { + return + } + log.Trace("Watched asset local file-system had event: %v", event) + debounce(callback) + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Error("Watched asset local file-system had error: %v", err) + } + } +} + +// GetFileLayerName returns the name of the first-seen layer that contains the given file. +func (l *LayeredFS) GetFileLayerName(elems ...string) string { + name := util.PathJoinRel(elems...) + for _, layer := range l.layers { + f, err := layer.Open(name) + if os.IsNotExist(err) { + continue + } else if err != nil { + return "" + } + _ = f.Close() + return layer.name + } + return "" +} diff --git a/modules/assetfs/layered_test.go b/modules/assetfs/layered_test.go new file mode 100644 index 0000000..58876d9 --- /dev/null +++ b/modules/assetfs/layered_test.go @@ -0,0 +1,110 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package assetfs + +import ( + "io" + "io/fs" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLayered(t *testing.T) { + dir := filepath.Join(t.TempDir(), "assetfs-layers") + dir1 := filepath.Join(dir, "l1") + dir2 := filepath.Join(dir, "l2") + + mkdir := func(elems ...string) { + require.NoError(t, os.MkdirAll(filepath.Join(elems...), 0o755)) + } + write := func(content string, elems ...string) { + require.NoError(t, os.WriteFile(filepath.Join(elems...), []byte(content), 0o644)) + } + + // d1 & f1: only in "l1"; d2 & f2: only in "l2" + // da & fa: in both "l1" and "l2" + mkdir(dir1, "d1") + mkdir(dir1, "da") + mkdir(dir1, "da/sub1") + + mkdir(dir2, "d2") + mkdir(dir2, "da") + mkdir(dir2, "da/sub2") + + write("dummy", dir1, ".DS_Store") + write("f1", dir1, "f1") + write("fa-1", dir1, "fa") + write("d1-f", dir1, "d1/f") + write("da-f-1", dir1, "da/f") + + write("f2", dir2, "f2") + write("fa-2", dir2, "fa") + write("d2-f", dir2, "d2/f") + write("da-f-2", dir2, "da/f") + + assets := Layered(Local("l1", dir1), Local("l2", dir2)) + + f, err := assets.Open("f1") + require.NoError(t, err) + bs, err := io.ReadAll(f) + require.NoError(t, err) + assert.EqualValues(t, "f1", string(bs)) + _ = f.Close() + + assertRead := func(expected string, expectedErr error, elems ...string) { + bs, err := assets.ReadFile(elems...) + if err != nil { + require.ErrorIs(t, err, expectedErr) + } else { + require.NoError(t, err) + assert.Equal(t, expected, string(bs)) + } + } + assertRead("f1", nil, "f1") + assertRead("f2", nil, "f2") + assertRead("fa-1", nil, "fa") + + assertRead("d1-f", nil, "d1/f") + assertRead("d2-f", nil, "d2/f") + assertRead("da-f-1", nil, "da/f") + + assertRead("", fs.ErrNotExist, "no-such") + + files, err := assets.ListFiles(".", true) + require.NoError(t, err) + assert.EqualValues(t, []string{"f1", "f2", "fa"}, files) + + files, err = assets.ListFiles(".", false) + require.NoError(t, err) + assert.EqualValues(t, []string{"d1", "d2", "da"}, files) + + files, err = assets.ListFiles(".") + require.NoError(t, err) + assert.EqualValues(t, []string{"d1", "d2", "da", "f1", "f2", "fa"}, files) + + files, err = assets.ListAllFiles(".", true) + require.NoError(t, err) + assert.EqualValues(t, []string{"d1/f", "d2/f", "da/f", "f1", "f2", "fa"}, files) + + files, err = assets.ListAllFiles(".", false) + require.NoError(t, err) + assert.EqualValues(t, []string{"d1", "d2", "da", "da/sub1", "da/sub2"}, files) + + files, err = assets.ListAllFiles(".") + require.NoError(t, err) + assert.EqualValues(t, []string{ + "d1", "d1/f", + "d2", "d2/f", + "da", "da/f", "da/sub1", "da/sub2", + "f1", "f2", "fa", + }, files) + + assert.Empty(t, assets.GetFileLayerName("no-such")) + assert.EqualValues(t, "l1", assets.GetFileLayerName("f1")) + assert.EqualValues(t, "l2", assets.GetFileLayerName("f2")) +} diff --git a/modules/auth/common.go b/modules/auth/common.go new file mode 100644 index 0000000..77361f6 --- /dev/null +++ b/modules/auth/common.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/log" +) + +func UnmarshalGroupTeamMapping(raw string) (map[string]map[string][]string, error) { + groupTeamMapping := make(map[string]map[string][]string) + if raw == "" { + return groupTeamMapping, nil + } + err := json.Unmarshal([]byte(raw), &groupTeamMapping) + if err != nil { + log.Error("Failed to unmarshal group team mapping: %v", err) + return nil, err + } + return groupTeamMapping, nil +} diff --git a/modules/auth/openid/discovery_cache.go b/modules/auth/openid/discovery_cache.go new file mode 100644 index 0000000..3a8d119 --- /dev/null +++ b/modules/auth/openid/discovery_cache.go @@ -0,0 +1,57 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openid + +import ( + "sync" + "time" + + "github.com/yohcop/openid-go" +) + +type timedDiscoveredInfo struct { + info openid.DiscoveredInfo + time time.Time +} + +type timedDiscoveryCache struct { + cache map[string]timedDiscoveredInfo + ttl time.Duration + mutex *sync.Mutex +} + +func newTimedDiscoveryCache(ttl time.Duration) *timedDiscoveryCache { + return &timedDiscoveryCache{cache: map[string]timedDiscoveredInfo{}, ttl: ttl, mutex: &sync.Mutex{}} +} + +func (s *timedDiscoveryCache) Put(id string, info openid.DiscoveredInfo) { + s.mutex.Lock() + defer s.mutex.Unlock() + + s.cache[id] = timedDiscoveredInfo{info: info, time: time.Now()} +} + +// Delete timed-out cache entries +func (s *timedDiscoveryCache) cleanTimedOut() { + now := time.Now() + for k, e := range s.cache { + diff := now.Sub(e.time) + if diff > s.ttl { + delete(s.cache, k) + } + } +} + +func (s *timedDiscoveryCache) Get(id string) openid.DiscoveredInfo { + s.mutex.Lock() + defer s.mutex.Unlock() + + // Delete old cached while we are at it. + s.cleanTimedOut() + + if info, has := s.cache[id]; has { + return info.info + } + return nil +} diff --git a/modules/auth/openid/discovery_cache_test.go b/modules/auth/openid/discovery_cache_test.go new file mode 100644 index 0000000..5a7f450 --- /dev/null +++ b/modules/auth/openid/discovery_cache_test.go @@ -0,0 +1,49 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openid + +import ( + "testing" + "time" +) + +type testDiscoveredInfo struct{} + +func (s *testDiscoveredInfo) ClaimedID() string { + return "claimedID" +} + +func (s *testDiscoveredInfo) OpEndpoint() string { + return "opEndpoint" +} + +func (s *testDiscoveredInfo) OpLocalID() string { + return "opLocalID" +} + +func TestTimedDiscoveryCache(t *testing.T) { + dc := newTimedDiscoveryCache(1 * time.Second) + + // Put some initial values + dc.Put("foo", &testDiscoveredInfo{}) // openid.opEndpoint: "a", openid.opLocalID: "b", openid.claimedID: "c"}) + + // Make sure we can retrieve them + if di := dc.Get("foo"); di == nil { + t.Errorf("Expected a result, got nil") + } else if di.OpEndpoint() != "opEndpoint" || di.OpLocalID() != "opLocalID" || di.ClaimedID() != "claimedID" { + t.Errorf("Expected opEndpoint opLocalID claimedID, got %v %v %v", di.OpEndpoint(), di.OpLocalID(), di.ClaimedID()) + } + + // Attempt to get a non-existent value + if di := dc.Get("bar"); di != nil { + t.Errorf("Expected nil, got %v", di) + } + + // Sleep one second and try retrieve again + time.Sleep(1 * time.Second) + + if di := dc.Get("foo"); di != nil { + t.Errorf("Expected a nil, got a result") + } +} diff --git a/modules/auth/openid/openid.go b/modules/auth/openid/openid.go new file mode 100644 index 0000000..249ce02 --- /dev/null +++ b/modules/auth/openid/openid.go @@ -0,0 +1,37 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openid + +import ( + "time" + + "github.com/yohcop/openid-go" +) + +// For the demo, we use in-memory infinite storage nonce and discovery +// cache. In your app, do not use this as it will eat up memory and +// never +// free it. Use your own implementation, on a better database system. +// If you have multiple servers for example, you may need to share at +// least +// the nonceStore between them. +var ( + nonceStore = openid.NewSimpleNonceStore() + discoveryCache = newTimedDiscoveryCache(24 * time.Hour) +) + +// Verify handles response from OpenID provider +func Verify(fullURL string) (id string, err error) { + return openid.Verify(fullURL, discoveryCache, nonceStore) +} + +// Normalize normalizes an OpenID URI +func Normalize(url string) (id string, err error) { + return openid.Normalize(url) +} + +// RedirectURL redirects browser +func RedirectURL(id, callbackURL, realm string) (string, error) { + return openid.RedirectURL(id, callbackURL, realm) +} diff --git a/modules/auth/pam/pam.go b/modules/auth/pam/pam.go new file mode 100644 index 0000000..cca1482 --- /dev/null +++ b/modules/auth/pam/pam.go @@ -0,0 +1,43 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build pam + +package pam + +import ( + "errors" + + "github.com/msteinert/pam" +) + +// Supported is true when built with PAM +var Supported = true + +// Auth pam auth service +func Auth(serviceName, userName, passwd string) (string, error) { + t, err := pam.StartFunc(serviceName, userName, func(s pam.Style, msg string) (string, error) { + switch s { + case pam.PromptEchoOff: + return passwd, nil + case pam.PromptEchoOn, pam.ErrorMsg, pam.TextInfo: + return "", nil + } + return "", errors.New("Unrecognized PAM message style") + }) + if err != nil { + return "", err + } + + if err = t.Authenticate(0); err != nil { + return "", err + } + + if err = t.AcctMgmt(0); err != nil { + return "", err + } + + // PAM login names might suffer transformations in the PAM stack. + // We should take whatever the PAM stack returns for it. + return t.GetItem(pam.User) +} diff --git a/modules/auth/pam/pam_stub.go b/modules/auth/pam/pam_stub.go new file mode 100644 index 0000000..3631eee --- /dev/null +++ b/modules/auth/pam/pam_stub.go @@ -0,0 +1,22 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build !pam + +package pam + +import ( + "errors" +) + +// Supported is false when built without PAM +var Supported = false + +// Auth not supported lack of pam tag +func Auth(serviceName, userName, passwd string) (string, error) { + // bypass the lint on callers: SA4023: this comparison is always true (staticcheck) + if !Supported { + return "", errors.New("PAM not supported") + } + return "", nil +} diff --git a/modules/auth/pam/pam_test.go b/modules/auth/pam/pam_test.go new file mode 100644 index 0000000..e9b844e --- /dev/null +++ b/modules/auth/pam/pam_test.go @@ -0,0 +1,20 @@ +//go:build pam + +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package pam + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPamAuth(t *testing.T) { + result, err := Auth("gitea", "user1", "false-pwd") + require.Error(t, err) + assert.EqualError(t, err, "Authentication failure") + assert.Len(t, result, 0) +} diff --git a/modules/auth/password/hash/argon2.go b/modules/auth/password/hash/argon2.go new file mode 100644 index 0000000..0cd6472 --- /dev/null +++ b/modules/auth/password/hash/argon2.go @@ -0,0 +1,80 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package hash + +import ( + "encoding/hex" + "strings" + + "code.gitea.io/gitea/modules/log" + + "golang.org/x/crypto/argon2" +) + +func init() { + MustRegister("argon2", NewArgon2Hasher) +} + +// Argon2Hasher implements PasswordHasher +// and uses the Argon2 key derivation function, hybrant variant +type Argon2Hasher struct { + time uint32 + memory uint32 + threads uint8 + keyLen uint32 +} + +// HashWithSaltBytes a provided password and salt +func (hasher *Argon2Hasher) HashWithSaltBytes(password string, salt []byte) string { + if hasher == nil { + return "" + } + return hex.EncodeToString(argon2.IDKey([]byte(password), salt, hasher.time, hasher.memory, hasher.threads, hasher.keyLen)) +} + +// NewArgon2Hasher is a factory method to create an Argon2Hasher +// The provided config should be either empty or of the form: +// "