From 52ae0c58025e6f179e6bf8b9642565d747b39ff0 Mon Sep 17 00:00:00 2001 From: upstream-mirror Date: Tue, 15 Sep 2026 21:41:59 +0500 Subject: [PATCH] snapshot of https://github.com/go-gitea/gitea.git (main, 2026-09-15; blobs > 131072B omitted) --- .air.toml | 26 + .changelog.yml | 59 + .devcontainer/devcontainer.json | 45 + .dockerignore | 90 + .editorconfig | 33 + .envrc | 1 + .gitattributes | 10 + .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/bug-report.yaml | 28 + .github/ISSUE_TEMPLATE/config.yml | 17 + .github/ISSUE_TEMPLATE/feature-request.yaml | 20 + .github/actionlint.yaml | 7 + .github/actions/docker-dryrun/action.yml | 31 + .github/actions/free-disk-space/action.yml | 22 + .github/actions/go-cache/action.yml | 56 + .github/actions/go-setup/action.yml | 26 + .github/actions/node-setup/action.yml | 30 + .github/actions/pgsql-shard/action.yml | 40 + .github/actions/python-setup/action.yml | 10 + .github/labeler.yml | 14 + .github/pull_request_template.md | 9 + .github/workflows/cache-prune.yml | 48 + .github/workflows/cache-seeder.yml | 97 + .github/workflows/cron-licenses.yml | 34 + .github/workflows/cron-renovate.yml | 32 + .github/workflows/cron-translations.yml | 42 + .github/workflows/files-changed.yml | 143 ++ .github/workflows/giteabot-backport.yml | 26 + .github/workflows/giteabot.yml | 65 + .github/workflows/pull-compliance.yml | 109 + .github/workflows/pull-db-tests.yml | 254 ++ .github/workflows/pull-docker-dryrun.yml | 47 + .github/workflows/pull-e2e-tests.yml | 37 + .github/workflows/pull-labeler.yml | 50 + .../workflows/release-nightly-snapcraft.yml | 48 + .github/workflows/release-nightly.yml | 144 ++ .github/workflows/release-tag-rc.yml | 149 ++ .github/workflows/release-tag-version.yml | 161 ++ .github/zizmor.yml | 9 + .gitignore | 122 + .golangci.yml | 201 ++ .ignore | 8 + .mailmap | 2 + .markdownlint.yaml | 15 + .shellcheckrc | 2 + .spectral.yaml | 7 + .yamllint.yaml | 42 + AGENTS.md | 20 + BSDmakefile | 58 + CLAUDE.md | 1 + CODE_OF_CONDUCT.md | 96 + CONTRIBUTING.md | 293 +++ DCO | 34 + Dockerfile | 86 + Dockerfile.rootless | 88 + LICENSE | 20 + MAINTAINERS | 67 + Makefile | 659 +++++ README.md | 198 ++ README.zh-cn.md | 200 ++ README.zh-tw.md | 200 ++ SECURITY.md | 94 + assets/codemirror-languages.json | 1296 ++++++++++ assets/favicon.svg | 31 + assets/logo.svg | 31 + assets/misspellings.csv | 21 + build/generate-bindata.go | 27 + build/generate-emoji.go | 219 ++ build/generate-gitignores.go | 129 + build/generate-go-licenses.go | 239 ++ build/generate-openapi.go | 72 + build/openapi3gen/convert.go | 387 +++ build/openapi3gen/convert_test.go | 218 ++ build/openapi3gen/enumscan.go | 192 ++ build/openapi3gen/enumscan_test.go | 249 ++ build/update-locales.sh | 22 + cmd/actions.go | 53 + cmd/admin.go | 176 ++ cmd/admin_auth.go | 109 + cmd/admin_auth_ldap.go | 473 ++++ cmd/admin_auth_ldap_test.go | 1348 ++++++++++ cmd/admin_auth_oauth.go | 321 +++ cmd/admin_auth_oauth_test.go | 343 +++ cmd/admin_auth_smtp.go | 199 ++ cmd/admin_auth_smtp_test.go | 271 ++ cmd/admin_regenerate.go | 44 + cmd/admin_user.go | 29 + cmd/admin_user_change_password.go | 78 + cmd/admin_user_change_password_test.go | 94 + cmd/admin_user_create.go | 242 ++ cmd/admin_user_create_test.go | 135 + cmd/admin_user_delete.go | 84 + cmd/admin_user_delete_test.go | 111 + cmd/admin_user_disable_2fa.go | 72 + cmd/admin_user_disable_2fa_test.go | 119 + cmd/admin_user_generate_access_token.go | 97 + cmd/admin_user_list.go | 60 + cmd/admin_user_must_change_password.go | 63 + cmd/admin_user_must_change_password_test.go | 78 + cmd/audit.go | 17 + cmd/cert.go | 206 ++ cmd/cert_test.go | 125 + cmd/cmdtest/cmd_test.go | 248 ++ cmd/config.go | 156 ++ cmd/config_test.go | 85 + cmd/docs.go | 68 + cmd/doctor.go | 218 ++ cmd/doctor_convert.go | 54 + cmd/doctor_test.go | 34 + cmd/dump.go | 328 +++ cmd/dump_repo.go | 195 ++ cmd/embedded.go | 313 +++ cmd/generate.go | 181 ++ cmd/helper.go | 149 ++ cmd/hook.go | 782 ++++++ cmd/hook_test.go | 55 + cmd/keys.go | 84 + cmd/mailer.go | 39 + cmd/main.go | 193 ++ cmd/main_test.go | 45 + cmd/manager.go | 154 ++ cmd/manager_logging.go | 92 + cmd/migrate.go | 43 + cmd/migrate_storage.go | 310 +++ cmd/migrate_storage_test.go | 73 + cmd/restore_repo.go | 68 + cmd/serv.go | 352 +++ cmd/serv_test.go | 56 + cmd/web.go | 392 +++ cmd/web_acme.go | 144 ++ cmd/web_graceful.go | 49 + cmd/web_https.go | 125 + cmd/web_https_test.go | 109 + contrib/README | 1 + contrib/development/README.md | 14 + contrib/development/vscode/launch.json | 18 + contrib/development/vscode/settings.json | 4 + contrib/development/vscode/tasks.json | 27 + contrib/fhs-compliant-script/gitea | 40 + contrib/grafana-monitoring-mixin/.gitignore | 2 + contrib/grafana-monitoring-mixin/Makefile | 31 + contrib/grafana-monitoring-mixin/README.md | 33 + .../grafana-monitoring-mixin/config.libsonnet | 99 + .../dashboards/dashboards.libsonnet | 1 + .../dashboards/overview.libsonnet | 467 ++++ .../grafana-monitoring-mixin/jsonnetfile.json | 15 + .../jsonnetfile.lock.json | 16 + .../lib/alerts.jsonnet | 1 + .../lib/dashboards.jsonnet | 6 + .../lib/rules.jsonnet | 1 + .../grafana-monitoring-mixin/mixin.libsonnet | 2 + contrib/options/label/Advanced | 26 + contrib/sample-page/privacy.html.sample | 196 ++ contrib/sample-page/tos.html.sample | 245 ++ contrib/service/freebsd/gitea | 48 + contrib/service/gentoo/gitea | 44 + contrib/service/launchd/io.gitea.web.plist | 39 + contrib/service/openbsd/gitea | 19 + contrib/service/openwrt/gitea | 35 + contrib/service/sunos/gitea.xml | 46 + contrib/service/supervisor/gitea | 16 + contrib/service/systemd/gitea.service | 85 + contrib/service/sysvinit/gitea | 89 + contrib/upgrade.sh | 140 ++ crowdin.yml | 12 + docker/README.md | 7 + docker/manifest.rootless.tmpl | 29 + docker/manifest.tmpl | 29 + docker/root/etc/nsswitch.conf | 15 + docker/root/etc/s6/.s6-svscan/finish | 2 + docker/root/etc/s6/gitea/finish | 2 + docker/root/etc/s6/gitea/run | 6 + docker/root/etc/s6/gitea/setup | 66 + docker/root/etc/s6/openssh/finish | 8 + docker/root/etc/s6/openssh/run | 6 + docker/root/etc/s6/openssh/setup | 66 + docker/root/etc/templates/app.ini | 60 + docker/root/etc/templates/sshd_config | 43 + docker/root/usr/bin/entrypoint | 41 + docker/root/usr/local/bin/environment-to-ini | 2 + docker/root/usr/local/bin/gitea | 15 + docker/rootless/etc/templates/app.ini | 57 + .../usr/local/bin/docker-entrypoint.sh | 17 + docker/rootless/usr/local/bin/docker-setup.sh | 51 + .../rootless/usr/local/bin/environment-to-ini | 2 + docker/rootless/usr/local/bin/gitea | 40 + docs/build-setup.md | 67 + docs/build-source.md | 92 + docs/community-governance.md | 263 ++ docs/development.md | 141 ++ docs/guidelines-backend.md | 150 ++ docs/guidelines-frontend.md | 101 + docs/guidelines-refactoring.md | 38 + docs/release-management.md | 115 + docs/testing.md | 151 ++ eslint.config.ts | 1206 +++++++++ eslint.json.config.ts | 37 + flake.lock | 27 + flake.nix | 94 + go.mod | 288 +++ go.sum | 882 +++++++ main.go | 57 + main_timezones.go | 16 + modelmigration/base/db.go | 520 ++++ modelmigration/base/db_test.go | 103 + modelmigration/base/hash.go | 16 + modelmigration/base/shared.go | 25 + .../issue_user.yml | 20 + .../oauth2_application.yml | 2 + .../expected_webhook.yml | 9 + .../hook_task.yml | 8 + .../webhook.yml | 10 + .../Test_AddIssueResourceIndexTable/issue.yml | 4 + .../attachment.yml | 11 + .../Test_AddRepoIDForAttachment/issue.yml | 3 + .../Test_AddRepoIDForAttachment/release.yml | 3 + .../project_issue.yml | 9 + .../project.yml | 23 + .../project_board.yml | 26 + .../issue_label.yml | 28 + .../Test_DeleteOrphanedIssueLabels/label.yml | 43 + .../action_run.yml | 29 + .../action_run_job.yml | 16 + .../commit_status.yml | 51 + .../commit_status_summary.yml | 19 + .../repository.yml | 9 + .../expected_webauthn_credential.yml | 12 + .../u2f_registration.yml | 21 + .../webauthn_credential.yml | 30 + .../Test_RemoveInvalidLabels/comment.yml | 52 + .../Test_RemoveInvalidLabels/issue.yml | 21 + .../Test_RemoveInvalidLabels/issue_label.yml | 35 + .../Test_RemoveInvalidLabels/label.yml | 25 + .../Test_RemoveInvalidLabels/repository.yml | 17 + .../Test_RepositoryFormat/comment.yml | 3 + .../Test_RepositoryFormat/commit_status.yml | 3 + .../Test_RepositoryFormat/pull_request.yml | 5 + .../Test_RepositoryFormat/release.yml | 3 + .../Test_RepositoryFormat/repo_archiver.yml | 3 + .../repo_indexer_status.yml | 3 + .../Test_RepositoryFormat/repository.yml | 11 + .../Test_RepositoryFormat/review_state.yml | 5 + .../expected_webauthn_credential.yml | 9 + .../webauthn_credential.yml | 30 + .../Test_UnwrapLDAPSourceCfg/login_source.yml | 48 + .../Test_UpdateBadgeColName/badge.yml | 4 + .../expected_milestone.yml | 19 + .../Test_UpdateOpenMilestoneCounts/issue.yml | 25 + .../milestone.yml | 19 + modelmigration/migrations.go | 562 +++++ modelmigration/migrations_test.go | 28 + modelmigration/migrationtest/tests.go | 125 + modelmigration/v1_10/v100.go | 83 + modelmigration/v1_10/v101.go | 20 + modelmigration/v1_10/v88.go | 66 + modelmigration/v1_10/v89.go | 39 + modelmigration/v1_10/v90.go | 21 + modelmigration/v1_10/v91.go | 29 + modelmigration/v1_10/v92.go | 17 + modelmigration/v1_10/v93.go | 19 + modelmigration/v1_10/v94.go | 27 + modelmigration/v1_10/v95.go | 23 + modelmigration/v1_10/v96.go | 64 + modelmigration/v1_10/v97.go | 18 + modelmigration/v1_10/v98.go | 20 + modelmigration/v1_10/v99.go | 39 + modelmigration/v1_11/v102.go | 22 + modelmigration/v1_11/v103.go | 19 + modelmigration/v1_11/v104.go | 34 + modelmigration/v1_11/v105.go | 25 + modelmigration/v1_11/v106.go | 27 + modelmigration/v1_11/v107.go | 19 + modelmigration/v1_11/v108.go | 19 + modelmigration/v1_11/v109.go | 18 + modelmigration/v1_11/v110.go | 32 + modelmigration/v1_11/v111.go | 436 ++++ modelmigration/v1_11/v112.go | 48 + modelmigration/v1_11/v113.go | 23 + modelmigration/v1_11/v114.go | 51 + modelmigration/v1_11/v115.go | 158 ++ modelmigration/v1_11/v116.go | 34 + modelmigration/v1_12/v117.go | 18 + modelmigration/v1_12/v118.go | 27 + modelmigration/v1_12/v119.go | 17 + modelmigration/v1_12/v120.go | 21 + modelmigration/v1_12/v121.go | 20 + modelmigration/v1_12/v122.go | 18 + modelmigration/v1_12/v123.go | 19 + modelmigration/v1_12/v124.go | 25 + modelmigration/v1_12/v125.go | 23 + modelmigration/v1_12/v126.go | 27 + modelmigration/v1_12/v127.go | 44 + modelmigration/v1_12/v128.go | 126 + modelmigration/v1_12/v129.go | 18 + modelmigration/v1_12/v130.go | 112 + modelmigration/v1_12/v131.go | 22 + modelmigration/v1_12/v132.go | 22 + modelmigration/v1_12/v133.go | 19 + modelmigration/v1_12/v134.go | 113 + modelmigration/v1_12/v135.go | 22 + modelmigration/v1_12/v136.go | 120 + modelmigration/v1_12/v137.go | 17 + modelmigration/v1_12/v138.go | 22 + modelmigration/v1_12/v139.go | 27 + modelmigration/v1_13/v140.go | 50 + modelmigration/v1_13/v141.go | 22 + modelmigration/v1_13/v142.go | 26 + modelmigration/v1_13/v143.go | 52 + modelmigration/v1_13/v144.go | 27 + modelmigration/v1_13/v145.go | 81 + modelmigration/v1_13/v146.go | 84 + modelmigration/v1_13/v147.go | 154 ++ modelmigration/v1_13/v148.go | 15 + modelmigration/v1_13/v149.go | 24 + modelmigration/v1_13/v150.go | 39 + modelmigration/v1_13/v151.go | 197 ++ modelmigration/v1_13/v152.go | 17 + modelmigration/v1_13/v153.go | 26 + modelmigration/v1_13/v154.go | 56 + modelmigration/v1_14/main_test.go | 14 + modelmigration/v1_14/v155.go | 22 + modelmigration/v1_14/v156.go | 167 ++ modelmigration/v1_14/v157.go | 57 + modelmigration/v1_14/v158.go | 111 + modelmigration/v1_14/v159.go | 38 + modelmigration/v1_14/v160.go | 18 + modelmigration/v1_14/v161.go | 71 + modelmigration/v1_14/v162.go | 62 + modelmigration/v1_14/v163.go | 35 + modelmigration/v1_14/v164.go | 38 + modelmigration/v1_14/v165.go | 64 + modelmigration/v1_14/v166.go | 114 + modelmigration/v1_14/v167.go | 24 + modelmigration/v1_14/v168.go | 14 + modelmigration/v1_14/v169.go | 15 + modelmigration/v1_14/v170.go | 22 + modelmigration/v1_14/v171.go | 22 + modelmigration/v1_14/v172.go | 20 + modelmigration/v1_14/v173.go | 22 + modelmigration/v1_14/v174.go | 35 + modelmigration/v1_14/v175.go | 53 + modelmigration/v1_14/v176.go | 78 + modelmigration/v1_14/v176_test.go | 128 + modelmigration/v1_14/v177.go | 43 + modelmigration/v1_14/v177_test.go | 88 + modelmigration/v1_15/main_test.go | 14 + modelmigration/v1_15/v178.go | 19 + modelmigration/v1_15/v179.go | 29 + modelmigration/v1_15/v180.go | 123 + modelmigration/v1_15/v181.go | 92 + modelmigration/v1_15/v181_test.go | 55 + modelmigration/v1_15/v182.go | 43 + modelmigration/v1_15/v182_test.go | 60 + modelmigration/v1_15/v183.go | 38 + modelmigration/v1_15/v184.go | 69 + modelmigration/v1_15/v185.go | 23 + modelmigration/v1_15/v186.go | 26 + modelmigration/v1_15/v187.go | 47 + modelmigration/v1_15/v188.go | 18 + modelmigration/v1_16/main_test.go | 14 + modelmigration/v1_16/v189.go | 110 + modelmigration/v1_16/v189_test.go | 82 + modelmigration/v1_16/v190.go | 24 + modelmigration/v1_16/v191.go | 29 + modelmigration/v1_16/v192.go | 19 + modelmigration/v1_16/v193.go | 34 + modelmigration/v1_16/v193_test.go | 80 + modelmigration/v1_16/v194.go | 22 + modelmigration/v1_16/v195.go | 47 + modelmigration/v1_16/v195_test.go | 63 + modelmigration/v1_16/v196.go | 22 + modelmigration/v1_16/v197.go | 21 + modelmigration/v1_16/v198.go | 32 + modelmigration/v1_16/v199.go | 6 + modelmigration/v1_16/v200.go | 23 + modelmigration/v1_16/v201.go | 16 + modelmigration/v1_16/v202.go | 24 + modelmigration/v1_16/v203.go | 19 + modelmigration/v1_16/v204.go | 18 + modelmigration/v1_16/v205.go | 43 + modelmigration/v1_16/v206.go | 29 + modelmigration/v1_16/v207.go | 16 + modelmigration/v1_16/v208.go | 15 + modelmigration/v1_16/v209.go | 18 + modelmigration/v1_16/v210.go | 193 ++ modelmigration/v1_16/v210_test.go | 106 + modelmigration/v1_17/main_test.go | 14 + modelmigration/v1_17/v211.go | 14 + modelmigration/v1_17/v212.go | 94 + modelmigration/v1_17/v213.go | 19 + modelmigration/v1_17/v214.go | 24 + modelmigration/v1_17/v215.go | 24 + modelmigration/v1_17/v216.go | 7 + modelmigration/v1_17/v217.go | 26 + modelmigration/v1_17/v218.go | 54 + modelmigration/v1_17/v219.go | 28 + modelmigration/v1_17/v220.go | 29 + modelmigration/v1_17/v221.go | 74 + modelmigration/v1_17/v221_test.go | 66 + modelmigration/v1_17/v222.go | 63 + modelmigration/v1_17/v223.go | 101 + modelmigration/v1_18/main_test.go | 14 + modelmigration/v1_18/v224.go | 29 + modelmigration/v1_18/v225.go | 29 + modelmigration/v1_18/v226.go | 17 + modelmigration/v1_18/v227.go | 24 + modelmigration/v1_18/v228.go | 26 + modelmigration/v1_18/v229.go | 57 + modelmigration/v1_18/v229_test.go | 64 + modelmigration/v1_18/v230.go | 19 + modelmigration/v1_18/v230_test.go | 47 + modelmigration/v1_19/main_test.go | 14 + modelmigration/v1_19/v231.go | 20 + modelmigration/v1_19/v232.go | 26 + modelmigration/v1_19/v233.go | 181 ++ modelmigration/v1_19/v233_test.go | 87 + modelmigration/v1_19/v234.go | 29 + modelmigration/v1_19/v235.go | 18 + modelmigration/v1_19/v236.go | 24 + modelmigration/v1_19/v237.go | 17 + modelmigration/v1_19/v238.go | 28 + modelmigration/v1_19/v239.go | 24 + modelmigration/v1_19/v240.go | 176 ++ modelmigration/v1_19/v241.go | 19 + modelmigration/v1_19/v242.go | 27 + modelmigration/v1_19/v243.go | 18 + modelmigration/v1_20/main_test.go | 14 + modelmigration/v1_20/v244.go | 24 + modelmigration/v1_20/v245.go | 71 + modelmigration/v1_20/v246.go | 18 + modelmigration/v1_20/v247.go | 51 + modelmigration/v1_20/v248.go | 18 + modelmigration/v1_20/v249.go | 47 + modelmigration/v1_20/v250.go | 135 + modelmigration/v1_20/v251.go | 48 + modelmigration/v1_20/v252.go | 48 + modelmigration/v1_20/v253.go | 50 + modelmigration/v1_20/v254.go | 20 + modelmigration/v1_20/v255.go | 24 + modelmigration/v1_20/v256.go | 25 + modelmigration/v1_20/v257.go | 34 + modelmigration/v1_20/v258.go | 18 + modelmigration/v1_20/v259.go | 360 +++ modelmigration/v1_20/v259_test.go | 110 + modelmigration/v1_21/main_test.go | 14 + modelmigration/v1_21/v260.go | 26 + modelmigration/v1_21/v261.go | 25 + modelmigration/v1_21/v262.go | 18 + modelmigration/v1_21/v263.go | 47 + modelmigration/v1_21/v264.go | 91 + modelmigration/v1_21/v265.go | 21 + modelmigration/v1_21/v266.go | 25 + modelmigration/v1_21/v267.go | 24 + modelmigration/v1_21/v268.go | 18 + modelmigration/v1_21/v269.go | 14 + modelmigration/v1_21/v270.go | 28 + modelmigration/v1_21/v271.go | 18 + modelmigration/v1_21/v272.go | 17 + modelmigration/v1_21/v273.go | 47 + modelmigration/v1_21/v274.go | 37 + modelmigration/v1_21/v275.go | 17 + modelmigration/v1_21/v276.go | 172 ++ modelmigration/v1_21/v277.go | 18 + modelmigration/v1_21/v278.go | 18 + modelmigration/v1_21/v279.go | 24 + modelmigration/v1_22/main_test.go | 14 + modelmigration/v1_22/v280.go | 31 + modelmigration/v1_22/v281.go | 22 + modelmigration/v1_22/v282.go | 18 + modelmigration/v1_22/v283.go | 49 + modelmigration/v1_22/v283_test.go | 28 + modelmigration/v1_22/v284.go | 23 + modelmigration/v1_22/v285.go | 25 + modelmigration/v1_22/v286.go | 109 + modelmigration/v1_22/v286_test.go | 118 + modelmigration/v1_22/v287.go | 48 + modelmigration/v1_22/v287_test.go | 57 + modelmigration/v1_22/v288.go | 27 + modelmigration/v1_22/v289.go | 27 + modelmigration/v1_22/v290.go | 28 + modelmigration/v1_22/v291.go | 24 + modelmigration/v1_22/v292.go | 9 + modelmigration/v1_22/v293.go | 113 + modelmigration/v1_22/v293_test.go | 63 + modelmigration/v1_22/v294.go | 55 + modelmigration/v1_22/v294_test.go | 51 + modelmigration/v1_22/v295.go | 22 + modelmigration/v1_22/v296.go | 20 + modelmigration/v1_22/v297.go | 17 + modelmigration/v1_22/v298.go | 14 + modelmigration/v1_23/main_test.go | 14 + modelmigration/v1_23/v299.go | 28 + modelmigration/v1_23/v300.go | 27 + modelmigration/v1_23/v301.go | 24 + modelmigration/v1_23/v302.go | 24 + modelmigration/v1_23/v302_test.go | 51 + modelmigration/v1_23/v303.go | 31 + modelmigration/v1_23/v304.go | 22 + modelmigration/v1_23/v304_test.go | 40 + modelmigration/v1_23/v305.go | 24 + modelmigration/v1_23/v306.go | 23 + modelmigration/v1_23/v307.go | 22 + modelmigration/v1_23/v308.go | 54 + modelmigration/v1_23/v309.go | 79 + modelmigration/v1_23/v310.go | 24 + modelmigration/v1_23/v311.go | 23 + modelmigration/v1_24/v312.go | 29 + modelmigration/v1_24/v313.go | 31 + modelmigration/v1_24/v314.go | 21 + modelmigration/v1_24/v315.go | 23 + modelmigration/v1_24/v316.go | 28 + modelmigration/v1_24/v317.go | 58 + modelmigration/v1_24/v318.go | 23 + modelmigration/v1_24/v319.go | 23 + modelmigration/v1_24/v320.go | 60 + modelmigration/v1_25/main_test.go | 14 + modelmigration/v1_25/v321.go | 53 + modelmigration/v1_25/v321_test.go | 64 + modelmigration/v1_25/v322.go | 29 + modelmigration/v1_25/v322_test.go | 34 + modelmigration/v1_26/main_test.go | 14 + modelmigration/v1_26/v323.go | 47 + modelmigration/v1_26/v324.go | 25 + modelmigration/v1_26/v325.go | 20 + modelmigration/v1_26/v325_test.go | 45 + modelmigration/v1_26/v326.go | 337 +++ modelmigration/v1_26/v326_test.go | 109 + modelmigration/v1_26/v327.go | 23 + modelmigration/v1_26/v327_test.go | 33 + modelmigration/v1_26/v328.go | 20 + modelmigration/v1_26/v329.go | 64 + modelmigration/v1_26/v329_test.go | 85 + modelmigration/v1_26/v330.go | 20 + modelmigration/v1_27/main_test.go | 14 + modelmigration/v1_27/v331.go | 158 ++ modelmigration/v1_27/v331_test.go | 155 ++ modelmigration/v1_27/v332.go | 27 + modelmigration/v1_27/v333.go | 26 + modelmigration/v1_27/v333_test.go | 60 + modelmigration/v1_27/v334.go | 24 + modelmigration/v1_27/v334_test.go | 36 + modelmigration/v1_27/v335.go | 35 + modelmigration/v1_27/v336.go | 32 + modelmigration/v1_27/v337.go | 38 + modelmigration/v1_27/v338.go | 73 + modelmigration/v1_27/v338_test.go | 52 + modelmigration/v1_27/v339.go | 40 + modelmigration/v1_27/v339_test.go | 71 + modelmigration/v1_27/v340.go | 26 + modelmigration/v1_27/v341.go | 80 + modelmigration/v1_27/v341_test.go | 80 + modelmigration/v1_27/v342.go | 44 + modelmigration/v1_6/v70.go | 110 + modelmigration/v1_6/v71.go | 75 + modelmigration/v1_6/v72.go | 30 + modelmigration/v1_7/v73.go | 20 + modelmigration/v1_7/v74.go | 19 + modelmigration/v1_7/v75.go | 35 + modelmigration/v1_8/v76.go | 74 + modelmigration/v1_8/v77.go | 18 + modelmigration/v1_8/v78.go | 43 + modelmigration/v1_8/v79.go | 26 + modelmigration/v1_8/v80.go | 20 + modelmigration/v1_8/v81.go | 32 + modelmigration/v1_9/v82.go | 120 + modelmigration/v1_9/v83.go | 28 + modelmigration/v1_9/v84.go | 19 + modelmigration/v1_9/v85.go | 114 + modelmigration/v1_9/v86.go | 18 + modelmigration/v1_9/v87.go | 19 + modelmigration/v28/main_test.go | 14 + modelmigration/v28/v343.go | 24 + modelmigration/v28/v343_test.go | 37 + modelmigration/v28/v344.go | 27 + modelmigration/v28/v345.go | 25 + modelmigration/v28/v346.go | 52 + modelmigration/v28/v346_test.go | 80 + modelmigration/v28/v347.go | 25 + modelmigration/v28/v348.go | 24 + modelmigration/v28/v348_test.go | 38 + modelmigration/v28/v349.go | 29 + modelmigration/v28/v349_test.go | 47 + modelmigration/v28/v350.go | 28 + modelmigration/v28/v350_test.go | 41 + modelmigration/v28/v351.go | 23 + modelmigration/v28/v352.go | 38 + modelmigration/v28/v353.go | 37 + modelmigration/v28/v353_test.go | 48 + models/actions/artifact.go | 279 +++ models/actions/artifact_test.go | 27 + models/actions/config.go | 74 + models/actions/main_test.go | 21 + models/actions/run.go | 410 +++ models/actions/run_attempt.go | 193 ++ models/actions/run_attempt_list.go | 39 + models/actions/run_job.go | 854 +++++++ models/actions/run_job_list.go | 171 ++ models/actions/run_job_list_test.go | 61 + models/actions/run_job_status_test.go | 96 + models/actions/run_job_summary.go | 207 ++ models/actions/run_job_test.go | 403 +++ models/actions/run_list.go | 217 ++ models/actions/run_list_test.go | 165 ++ models/actions/run_test.go | 101 + models/actions/runner.go | 457 ++++ models/actions/runner_list.go | 65 + models/actions/runner_test.go | 83 + models/actions/runner_token.go | 121 + models/actions/runner_token_test.go | 39 + models/actions/schedule.go | 121 + models/actions/schedule_list.go | 34 + models/actions/schedule_spec.go | 73 + models/actions/schedule_spec_test.go | 69 + models/actions/scoped_workflow.go | 183 ++ models/actions/scoped_workflow_test.go | 139 ++ models/actions/status.go | 129 + models/actions/status_test.go | 104 + models/actions/task.go | 656 +++++ models/actions/task_list.go | 91 + models/actions/task_output.go | 55 + models/actions/task_step.go | 41 + models/actions/task_test.go | 450 ++++ models/actions/tasks_version.go | 101 + models/actions/token_permissions.go | 60 + models/actions/utils.go | 110 + models/actions/utils_test.go | 129 + models/actions/variable.go | 188 ++ models/activities/action.go | 614 +++++ models/activities/action_list.go | 284 +++ models/activities/action_test.go | 159 ++ models/activities/main_test.go | 17 + models/activities/notification.go | 422 ++++ models/activities/notification_list.go | 506 ++++ models/activities/notification_test.go | 177 ++ models/activities/repo_activity.go | 390 +++ models/activities/statistic.go | 135 + models/activities/user_heatmap.go | 74 + models/activities/user_heatmap_test.go | 97 + models/admin/task.go | 217 ++ models/asymkey/error.go | 236 ++ models/asymkey/gpg_key.go | 246 ++ models/asymkey/gpg_key_add.go | 161 ++ models/asymkey/gpg_key_commit_verification.go | 191 ++ models/asymkey/gpg_key_common.go | 147 ++ models/asymkey/gpg_key_import.go | 47 + models/asymkey/gpg_key_list.go | 38 + models/asymkey/gpg_key_test.go | 427 ++++ models/asymkey/gpg_key_verify.go | 86 + models/asymkey/key_display.go | 37 + models/asymkey/main_test.go | 23 + models/asymkey/ssh_key.go | 448 ++++ models/asymkey/ssh_key_authorized_keys.go | 202 ++ .../asymkey/ssh_key_authorized_keys_test.go | 90 + models/asymkey/ssh_key_fingerprint.go | 49 + models/asymkey/ssh_key_parse.go | 264 ++ models/asymkey/ssh_key_principals.go | 56 + models/asymkey/ssh_key_test.go | 492 ++++ models/asymkey/ssh_key_verify.go | 47 + models/audit/action.go | 208 ++ models/audit/audit_event.go | 209 ++ models/audit/audit_event_benchmark_test.go | 41 + models/audit/audit_event_test.go | 109 + models/audit/entity.go | 49 + models/audit/main_test.go | 14 + models/audit/origin.go | 14 + models/audit/scopes.go | 15 + models/auth/access_token.go | 181 ++ models/auth/access_token_scope.go | 407 +++ models/auth/access_token_scope_test.go | 114 + models/auth/access_token_test.go | 173 ++ models/auth/auth_token.go | 65 + models/auth/main_test.go | 20 + models/auth/oauth2.go | 685 +++++ models/auth/oauth2_list.go | 32 + models/auth/oauth2_test.go | 343 +++ models/auth/session.go | 112 + models/auth/source.go | 402 +++ models/auth/source_test.go | 55 + models/auth/token_cache.go | 23 + models/auth/twofactor.go | 212 ++ models/auth/twofactor_test.go | 82 + models/auth/webauthn.go | 202 ++ models/auth/webauthn_test.go | 66 + models/avatars/avatar.go | 156 ++ models/avatars/avatar_test.go | 51 + models/avatars/main_test.go | 18 + models/db/collation.go | 190 ++ models/db/common.go | 55 + models/db/conn.go | 187 ++ models/db/conn_test.go | 109 + models/db/consistency.go | 31 + models/db/context.go | 320 +++ models/db/context_committer_test.go | 102 + models/db/context_test.go | 137 + models/db/convert.go | 86 + models/db/driver_postgresschema.go | 49 + models/db/driver_sqlite_mattn.go | 31 + models/db/driver_sqlite_modernc.go | 41 + models/db/engine.go | 193 ++ models/db/engine_dump.go | 37 + models/db/engine_hook.go | 53 + models/db/engine_init.go | 127 + models/db/engine_test.go | 83 + models/db/error.go | 68 + models/db/index.go | 172 ++ models/db/index_test.go | 126 + models/db/install/db.go | 59 + models/db/iterate.go | 76 + models/db/iterate_test.go | 44 + models/db/list.go | 200 ++ models/db/list_test.go | 56 + models/db/log.go | 107 + models/db/main_test.go | 17 + models/db/name.go | 96 + models/db/paginator/main_test.go | 14 + models/db/paginator/paginator.go | 7 + models/db/paginator/paginator_test.go | 59 + models/db/search.go | 31 + models/db/sequence.go | 70 + models/dbfs/dbfile.go | 351 +++ models/dbfs/dbfs.go | 134 + models/dbfs/dbfs_test.go | 230 ++ models/dbfs/main_test.go | 14 + models/deploykey/deploykey.go | 99 + models/deploykey/deploykey_ssh.go | 75 + models/deploykey/deploykey_token.go | 76 + models/deploykey/deploykey_token_test.go | 52 + models/deploykey/error.go | 71 + models/deploykey/main_test.go | 14 + models/fixtures/access.yml | 181 ++ models/fixtures/access_token.yml | 35 + models/fixtures/action.yml | 77 + models/fixtures/action_artifact.yml | 181 ++ models/fixtures/action_run.yml | 143 ++ models/fixtures/action_run_job.yml | 133 + models/fixtures/action_runner.yml | 53 + models/fixtures/action_runner_token.yml | 37 + models/fixtures/action_task.yml | 181 ++ models/fixtures/action_task_output.yml | 22 + models/fixtures/attachment.yml | 170 ++ models/fixtures/badge.yml | 7 + models/fixtures/branch.yml | 253 ++ models/fixtures/collaboration.yml | 67 + models/fixtures/comment.yml | 106 + models/fixtures/commit_status.yml | 61 + models/fixtures/commit_status_index.yml | 7 + models/fixtures/deploy_key.yml | 3 + models/fixtures/email_address.yml | 321 +++ models/fixtures/external_login_user.yml | 3 + models/fixtures/follow.yml | 21 + models/fixtures/gpg_key.yml | 25 + models/fixtures/gpg_key_import.yml | 3 + models/fixtures/hook_task.yml | 2 + models/fixtures/issue.yml | 376 +++ models/fixtures/issue_assignees.yml | 22 + models/fixtures/issue_index.yml | 37 + models/fixtures/issue_label.yml | 21 + models/fixtures/issue_pin.yml | 8 + models/fixtures/issue_user.yml | 22 + models/fixtures/issue_watch.yml | 33 + models/fixtures/label.yml | 111 + models/fixtures/lfs_meta_object.yml | 34 + models/fixtures/login_source.yml | 3 + models/fixtures/milestone.yml | 56 + models/fixtures/mirror.yml | 51 + models/fixtures/notice.yml | 16 + models/fixtures/notification.yml | 56 + models/fixtures/oauth2_application.yml | 22 + models/fixtures/oauth2_authorization_code.yml | 2 + models/fixtures/oauth2_grant.yml | 33 + models/fixtures/org_user.yml | 139 ++ models/fixtures/project.yml | 73 + models/fixtures/project_board.yml | 79 + models/fixtures/project_issue.yml | 25 + models/fixtures/protected_branch.yml | 3 + models/fixtures/protected_tag.yml | 26 + models/fixtures/public_key.yml | 14 + models/fixtures/pull_request.yml | 121 + models/fixtures/reaction.yml | 41 + models/fixtures/release.yml | 164 ++ models/fixtures/renamed_branch.yml | 7 + models/fixtures/repo_archiver.yml | 3 + models/fixtures/repo_indexer_status.yml | 3 + models/fixtures/repo_license.yml | 3 + models/fixtures/repo_redirect.yml | 7 + models/fixtures/repo_topic.yml | 29 + models/fixtures/repo_transfer.yml | 33 + models/fixtures/repo_unit.yml | 765 ++++++ models/fixtures/repository.yml | 1792 ++++++++++++++ models/fixtures/review.yml | 218 ++ models/fixtures/star.yml | 21 + models/fixtures/stopwatch.yml | 13 + models/fixtures/system_setting.yml | 17 + models/fixtures/team.yml | 265 ++ models/fixtures/team_repo.yml | 79 + models/fixtures/team_unit.yml | 344 +++ models/fixtures/team_user.yml | 163 ++ models/fixtures/topic.yml | 31 + models/fixtures/tracked_time.yml | 73 + models/fixtures/two_factor.yml | 11 + models/fixtures/user.yml | 1560 ++++++++++++ models/fixtures/user_blocking.yml | 21 + models/fixtures/user_open_id.yml | 19 + models/fixtures/user_redirect.yml | 10 + models/fixtures/watch.yml | 43 + models/fixtures/webauthn_credential.yml | 11 + models/fixtures/webhook.yml | 2 + models/git/branch.go | 561 +++++ models/git/branch_list.go | 129 + models/git/branch_test.go | 287 +++ models/git/commit_status.go | 585 +++++ models/git/commit_status_summary.go | 92 + models/git/commit_status_test.go | 285 +++ models/git/lfs.go | 373 +++ models/git/lfs_lock.go | 171 ++ models/git/lfs_lock_list.go | 54 + models/git/lfs_lock_test.go | 82 + models/git/lfs_test.go | 61 + models/git/main_test.go | 18 + models/git/protected_branch.go | 653 +++++ models/git/protected_branch_list.go | 101 + models/git/protected_branch_list_test.go | 139 ++ models/git/protected_branch_test.go | 204 ++ models/git/protected_tag.go | 150 ++ models/git/protected_tag_test.go | 164 ++ models/git/ref.go | 36 + models/gituser/avatar_stack.go | 52 + models/gituser/gituser.go | 63 + models/init.go | 15 + models/issues/assignees.go | 192 ++ models/issues/assignees_test.go | 93 + models/issues/comment.go | 1354 ++++++++++ models/issues/comment_code.go | 138 ++ models/issues/comment_list.go | 551 +++++ models/issues/comment_test.go | 141 ++ models/issues/content_history.go | 246 ++ models/issues/content_history_test.go | 99 + models/issues/dependency.go | 201 ++ models/issues/dependency_test.go | 72 + models/issues/issue.go | 809 ++++++ models/issues/issue_index.go | 23 + models/issues/issue_label.go | 470 ++++ models/issues/issue_label_test.go | 28 + models/issues/issue_list.go | 613 +++++ models/issues/issue_list_test.go | 91 + models/issues/issue_lock.go | 65 + models/issues/issue_pin.go | 225 ++ models/issues/issue_project.go | 152 ++ models/issues/issue_project_multi_test.go | 149 ++ models/issues/issue_search.go | 525 ++++ models/issues/issue_stats.go | 179 ++ models/issues/issue_test.go | 467 ++++ models/issues/issue_update.go | 733 ++++++ models/issues/issue_update_test.go | 33 + models/issues/issue_user.go | 96 + models/issues/issue_user_test.go | 61 + models/issues/issue_watch.go | 138 ++ models/issues/issue_watch_test.go | 67 + models/issues/issue_xref.go | 362 +++ models/issues/issue_xref_test.go | 184 ++ models/issues/label.go | 558 +++++ models/issues/label_test.go | 461 ++++ models/issues/main_test.go | 33 + models/issues/milestone.go | 349 +++ models/issues/milestone_list.go | 207 ++ models/issues/milestone_test.go | 370 +++ models/issues/pull.go | 1022 ++++++++ models/issues/pull_list.go | 390 +++ models/issues/pull_list_test.go | 142 ++ models/issues/pull_test.go | 444 ++++ models/issues/reaction.go | 367 +++ models/issues/review.go | 1115 +++++++++ models/issues/review_list.go | 214 ++ models/issues/review_test.go | 442 ++++ models/issues/stopwatch.go | 247 ++ models/issues/stopwatch_test.go | 86 + models/issues/tracked_time.go | 364 +++ models/issues/tracked_time_test.go | 133 + models/organization/main_test.go | 21 + models/organization/org.go | 619 +++++ models/organization/org_list.go | 177 ++ models/organization/org_list_test.go | 90 + models/organization/org_test.go | 637 +++++ models/organization/org_user.go | 146 ++ models/organization/org_user_test.go | 153 ++ models/organization/org_worktime.go | 104 + models/organization/team.go | 290 +++ models/organization/team_invite.go | 158 ++ models/organization/team_invite_test.go | 47 + models/organization/team_list.go | 191 ++ models/organization/team_list_test.go | 44 + models/organization/team_repo.go | 96 + models/organization/team_repo_test.go | 30 + models/organization/team_test.go | 320 +++ models/organization/team_unit.go | 30 + models/organization/team_user.go | 73 + models/packages/alpine/search.go | 53 + models/packages/arch/search.go | 38 + models/packages/conan/references.go | 170 ++ models/packages/conan/search.go | 149 ++ models/packages/conda/search.go | 63 + models/packages/container/search.go | 287 +++ models/packages/cran/search.go | 90 + models/packages/debian/search.go | 141 ++ models/packages/descriptor.go | 309 +++ models/packages/nuget/search.go | 70 + models/packages/package.go | 357 +++ models/packages/package_blob.go | 165 ++ models/packages/package_blob_test.go | 74 + models/packages/package_blob_upload.go | 76 + models/packages/package_cleanup_rule.go | 109 + models/packages/package_file.go | 256 ++ models/packages/package_property.go | 175 ++ models/packages/package_test.go | 66 + models/packages/package_version.go | 353 +++ models/packages/rpm/search.go | 23 + models/perm/access/access.go | 286 +++ models/perm/access/access_test.go | 203 ++ .../access/actions_repo_permission_test.go | 201 ++ models/perm/access/main_test.go | 20 + models/perm/access/repo_permission.go | 752 ++++++ models/perm/access/repo_permission_test.go | 275 +++ models/perm/access_mode.go | 65 + models/perm/access_mode_test.go | 22 + models/project/column.go | 364 +++ models/project/column_list.go | 42 + models/project/column_list_test.go | 40 + models/project/column_test.go | 155 ++ models/project/issue.go | 117 + models/project/main_test.go | 23 + models/project/project.go | 481 ++++ models/project/project_test.go | 123 + models/project/template.go | 45 + models/pull/automerge.go | 100 + models/pull/review_state.go | 158 ++ models/renderhelper/commit_checker.go | 56 + models/renderhelper/main_test.go | 34 + models/renderhelper/repo_comment.go | 74 + models/renderhelper/repo_comment_test.go | 81 + models/renderhelper/repo_file.go | 78 + models/renderhelper/repo_file_test.go | 120 + models/renderhelper/repo_wiki.go | 77 + models/renderhelper/repo_wiki_test.go | 65 + models/renderhelper/simple_document.go | 35 + models/renderhelper/simple_document_test.go | 38 + models/repo/archive_test.go | 32 + models/repo/archiver.go | 167 ++ models/repo/attachment.go | 271 ++ models/repo/attachment_test.go | 119 + models/repo/avatar.go | 91 + models/repo/avatar_test.go | 27 + models/repo/collaboration.go | 141 ++ models/repo/collaboration_test.go | 121 + models/repo/fork.go | 103 + models/repo/fork_test.go | 32 + models/repo/git.go | 46 + models/repo/issue.go | 60 + models/repo/language_stats.go | 230 ++ models/repo/license.go | 143 ++ models/repo/main_test.go | 21 + models/repo/mirror.go | 125 + models/repo/org_repo.go | 202 ++ models/repo/pull_request_default.go | 16 + models/repo/pull_request_default_test.go | 112 + models/repo/pushmirror.go | 160 ++ models/repo/pushmirror_test.go | 49 + models/repo/redirect.go | 88 + models/repo/redirect_test.go | 76 + models/repo/release.go | 543 ++++ models/repo/release_test.go | 92 + models/repo/repo.go | 919 +++++++ models/repo/repo_indexer.go | 114 + models/repo/repo_list.go | 840 +++++++ models/repo/repo_list_test.go | 529 ++++ models/repo/repo_location.go | 37 + models/repo/repo_location_test.go | 27 + models/repo/repo_test.go | 237 ++ models/repo/repo_unit.go | 376 +++ models/repo/repo_unit_actions.go | 178 ++ models/repo/repo_unit_actions_test.go | 51 + models/repo/repo_unit_test.go | 104 + models/repo/search.go | 53 + models/repo/star.go | 104 + models/repo/star_test.go | 75 + models/repo/topic.go | 364 +++ models/repo/topic_test.go | 82 + models/repo/transfer.go | 285 +++ models/repo/update.go | 115 + models/repo/upload.go | 153 ++ models/repo/user_repo.go | 196 ++ models/repo/user_repo_test.go | 123 + models/repo/watch.go | 309 +++ models/repo/watch_test.go | 180 ++ models/repo/wiki.go | 75 + models/repo/wiki_test.go | 22 + models/repostats/main_test.go | 33 + models/repostats/repo.go | 328 +++ models/repostats/repo_test.go | 37 + models/secret/main_test.go | 14 + models/secret/secret.go | 258 ++ models/secret/secret_test.go | 174 ++ models/shared/types/ownertype.go | 29 + models/system/appstate.go | 56 + models/system/main_test.go | 19 + models/system/notice.go | 113 + models/system/notice_test.go | 107 + models/system/setting.go | 152 ++ models/system/setting_test.go | 51 + models/unit/unit.go | 374 +++ models/unit/unit_test.go | 95 + models/unittest/consistency.go | 190 ++ models/unittest/fixtures.go | 175 ++ models/unittest/fixtures_loader.go | 240 ++ models/unittest/fixtures_test.go | 121 + models/unittest/fscopy.go | 107 + models/unittest/mock_http.go | 142 ++ models/unittest/reflection.go | 40 + models/unittest/testdb.go | 243 ++ models/unittest/unit_tests.go | 197 ++ models/user/avatar.go | 102 + models/user/avatar_test.go | 66 + models/user/badge.go | 254 ++ models/user/badge_test.go | 185 ++ models/user/block.go | 127 + models/user/email_address.go | 540 ++++ models/user/email_address_test.go | 207 ++ models/user/error.go | 88 + models/user/external_login_user.go | 212 ++ models/user/follow.go | 79 + models/user/follow_test.go | 22 + models/user/list.go | 83 + models/user/main_test.go | 19 + models/user/must_change_password.go | 49 + models/user/openid.go | 127 + models/user/openid_test.go | 70 + models/user/redirect.go | 89 + models/user/redirect_test.go | 24 + models/user/search.go | 213 ++ models/user/setting.go | 249 ++ models/user/setting_options.go | 27 + models/user/setting_test.go | 59 + models/user/user.go | 1490 +++++++++++ models/user/user_extdata.go | 46 + models/user/user_list.go | 45 + models/user/user_system.go | 130 + models/user/user_system_test.go | 39 + models/user/user_test.go | 746 ++++++ models/user/user_update.go | 15 + models/webhook/hooktask.go | 263 ++ models/webhook/main_test.go | 20 + models/webhook/webhook.go | 353 +++ models/webhook/webhook_system.go | 111 + models/webhook/webhook_system_test.go | 37 + models/webhook/webhook_test.go | 391 +++ modules/actions/artifacts.go | 29 + modules/actions/artifacts_test.go | 36 + modules/actions/commit_status_info.go | 70 + modules/actions/github.go | 129 + modules/actions/github_test.go | 119 + modules/actions/jobparser/aliases.go | 121 + modules/actions/jobparser/aliases_test.go | 85 + modules/actions/jobparser/interpeter.go | 93 + modules/actions/jobparser/jobparser.go | 372 +++ modules/actions/jobparser/jobparser_test.go | 446 ++++ modules/actions/jobparser/model.go | 604 +++++ modules/actions/jobparser/model_test.go | 575 +++++ modules/actions/jobparser/roundtrip_test.go | 106 + .../testdata/continue_on_error_expr.in.yaml | 10 + .../testdata/continue_on_error_expr.out.yaml | 25 + .../jobparser/testdata/empty_step.in.yaml | 8 + .../jobparser/testdata/empty_step.out.yaml | 7 + .../jobparser/testdata/erase_needs.in.yaml | 16 + .../jobparser/testdata/erase_needs.out.yaml | 23 + .../jobparser/testdata/has_needs.in.yaml | 16 + .../jobparser/testdata/has_needs.out.yaml | 25 + .../jobparser/testdata/has_secrets.in.yaml | 14 + .../jobparser/testdata/has_secrets.out.yaml | 16 + .../jobparser/testdata/has_with.in.yaml | 15 + .../jobparser/testdata/has_with.out.yaml | 17 + .../testdata/job_name_with_matrix.in.yaml | 14 + .../testdata/job_name_with_matrix.out.yaml | 101 + .../jobparser/testdata/multiple_jobs.in.yaml | 22 + .../jobparser/testdata/multiple_jobs.out.yaml | 39 + .../testdata/multiple_matrix.in.yaml | 13 + .../testdata/multiple_matrix.out.yaml | 101 + .../testdata/null_job_explicit.in.yaml | 9 + .../testdata/null_job_implicit.in.yaml | 9 + .../testdata/prefixed_newline.in.yaml | 14 + .../testdata/prefixed_newline.out.yaml | 11 + modules/actions/jobparser/testdata_test.go | 21 + modules/actions/jobparser/uses.go | 79 + modules/actions/jobparser/uses_test.go | 195 ++ modules/actions/jobparser/workflow_call.go | 376 +++ .../actions/jobparser/workflow_call_test.go | 472 ++++ modules/actions/log.go | 232 ++ modules/actions/scoped_workflows.go | 93 + modules/actions/scoped_workflows_test.go | 62 + modules/actions/task_state.go | 124 + modules/actions/task_state_test.go | 186 ++ .../workflowpattern/workflow_pattern.go | 65 + .../workflowpattern/workflow_pattern_test.go | 416 ++++ modules/actions/workflows.go | 890 +++++++ modules/actions/workflows_test.go | 430 ++++ modules/analyze/code_language.go | 28 + modules/analyze/generated.go | 27 + modules/analyze/vendor.go | 31 + modules/analyze/vendor_test.go | 53 + modules/assetfs/embed.go | 375 +++ modules/assetfs/embed_test.go | 102 + modules/assetfs/layered.go | 273 ++ modules/assetfs/layered_test.go | 109 + modules/auth/common.go | 22 + modules/auth/httpauth/httpauth.go | 47 + modules/auth/httpauth/httpauth_test.go | 43 + modules/auth/openid/discovery_cache.go | 57 + modules/auth/openid/discovery_cache_test.go | 49 + modules/auth/openid/openid.go | 55 + modules/auth/openid/openid_test.go | 29 + modules/auth/pam/pam.go | 44 + modules/auth/pam/pam_stub.go | 22 + modules/auth/pam/pam_test.go | 19 + modules/auth/password/hash/argon2.go | 74 + modules/auth/password/hash/bcrypt.go | 54 + modules/auth/password/hash/common.go | 30 + modules/auth/password/hash/dummy.go | 33 + modules/auth/password/hash/dummy_test.go | 25 + modules/auth/password/hash/hash.go | 189 ++ modules/auth/password/hash/hash_test.go | 192 ++ 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 | 76 + modules/auth/password/pwn.go | 52 + modules/auth/password/pwn/pwn.go | 118 + modules/auth/password/pwn/pwn_test.go | 61 + modules/auth/webauthn/webauthn.go | 77 + modules/auth/webauthn/webauthn_test.go | 25 + modules/avatar/avatar.go | 135 + modules/avatar/avatar_test.go | 145 ++ modules/avatar/federated.go | 55 + modules/avatar/hash.go | 20 + modules/avatar/hash_test.go | 20 + modules/avatar/identicon/block.go | 717 ++++++ modules/avatar/identicon/colors.go | 134 + modules/avatar/identicon/identicon.go | 134 + modules/avatar/identicon/identicon_test.go | 40 + modules/avatar/identicon/polygon.go | 68 + 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/badge/badge.go | 129 + modules/badge/badge_glyph_width.go | 206 ++ modules/base/natural_sort.go | 84 + modules/base/natural_sort_test.go | 48 + modules/base/tool.go | 119 + modules/base/tool_test.go | 102 + modules/cache/cache.go | 148 ++ modules/cache/cache_redis.go | 162 ++ modules/cache/cache_test.go | 134 + modules/cache/cache_twoqueue.go | 208 ++ modules/cache/context.go | 43 + modules/cache/context_test.go | 50 + modules/cache/ephemeral.go | 90 + modules/cache/string_cache.go | 120 + modules/cachegroup/cachegroup.go | 12 + modules/charset/ambiguous.go | 59 + modules/charset/ambiguous_gen.go | 852 +++++++ modules/charset/ambiguous_gen_test.go | 37 + modules/charset/charset.go | 226 ++ modules/charset/charset_test.go | 254 ++ modules/charset/escape.go | 48 + modules/charset/escape_status.go | 17 + modules/charset/escape_stream.go | 418 ++++ modules/charset/escape_test.go | 213 ++ modules/charset/generate/ambiguous.json | 1 + modules/charset/generate/generate.go | 201 ++ modules/charset/invisible_gen.go | 38 + modules/commitstatus/commit_status.go | 82 + modules/commitstatus/commit_status_test.go | 201 ++ modules/consts/asymkey.go | 12 + modules/container/filter.go | 21 + modules/container/filter_test.go | 28 + modules/container/set.go | 93 + modules/container/set_test.go | 48 + modules/csv/csv.go | 151 ++ modules/csv/csv_test.go | 588 +++++ modules/dump/dumper.go | 263 ++ modules/dump/dumper_test.go | 151 ++ modules/emoji/emoji.go | 146 ++ modules/emoji/emoji_test.go | 125 + modules/fileicon/basic.go | 31 + modules/fileicon/entry.go | 35 + modules/fileicon/material.go | 162 ++ modules/fileicon/material_test.go | 27 + modules/fileicon/render.go | 47 + modules/fileicon/render_test.go | 75 + modules/generate/generate.go | 149 ++ modules/generate/generate_test.go | 36 + modules/git/archive.go | 74 + modules/git/archive_test.go | 29 + modules/git/attribute/attribute.go | 115 + modules/git/attribute/attribute_test.go | 37 + modules/git/attribute/batch.go | 208 ++ modules/git/attribute/batch_test.go | 173 ++ modules/git/attribute/checker.go | 97 + modules/git/attribute/checker_test.go | 84 + modules/git/attribute/main_test.go | 39 + modules/git/blame.go | 207 ++ modules/git/blame_sha256_test.go | 155 ++ modules/git/blame_test.go | 150 ++ modules/git/blob.go | 116 + modules/git/blob_gogit.go | 47 + modules/git/blob_nogogit.go | 108 + modules/git/blob_test.go | 76 + modules/git/branch.go | 95 + modules/git/catfile_batch.go | 61 + modules/git/catfile_batch_command.go | 75 + modules/git/catfile_batch_legacy.go | 87 + modules/git/catfile_batch_reader.go | 241 ++ modules/git/catfile_batch_test.go | 108 + modules/git/clone.go | 24 + modules/git/cmdverb.go | 36 + modules/git/commit.go | 272 ++ modules/git/commit2.go | 97 + modules/git/commit2_test.go | 76 + modules/git/commit_convert_gogit.go | 75 + modules/git/commit_count_cache.go | 27 + modules/git/commit_file.go | 88 + modules/git/commit_file_test.go | 175 ++ modules/git/commit_info.go | 115 + modules/git/commit_info_gogit.go | 207 ++ modules/git/commit_info_nogogit.go | 43 + modules/git/commit_info_nogogit_test.go | 57 + modules/git/commit_info_test.go | 166 ++ modules/git/commit_message.go | 194 ++ modules/git/commit_message_test.go | 151 ++ modules/git/commit_reader.go | 100 + modules/git/commit_sha256_test.go | 128 + modules/git/commit_submodule.go | 54 + modules/git/commit_submodule_file.go | 69 + modules/git/commit_submodule_file_test.go | 40 + modules/git/commit_test.go | 204 ++ modules/git/compare.go | 70 + modules/git/compare_test.go | 138 ++ modules/git/config.go | 205 ++ modules/git/config_managed.go | 30 + modules/git/config_submodule.go | 75 + modules/git/config_submodule_test.go | 49 + modules/git/config_test.go | 73 + modules/git/diff.go | 348 +++ modules/git/diff_test.go | 184 ++ modules/git/error.go | 126 + modules/git/fastimport.go | 67 + modules/git/fetch.go | 29 + modules/git/foreachref/format.go | 83 + modules/git/foreachref/format_test.go | 66 + modules/git/foreachref/parser.go | 135 + modules/git/foreachref/parser_test.go | 227 ++ modules/git/fsck.go | 16 + modules/git/git.go | 215 ++ modules/git/git_test.go | 37 + modules/git/gitcmd/command.go | 582 +++++ modules/git/gitcmd/command_test.go | 142 ++ modules/git/gitcmd/context.go | 32 + modules/git/gitcmd/env.go | 40 + modules/git/gitcmd/error.go | 166 ++ modules/git/gitcmd/error_test.go | 26 + modules/git/gitcmd/pipe.go | 88 + modules/git/gitrepo.go | 51 + modules/git/gitrepo/dbrepo.go | 28 + modules/git/gitrepo/gitrepo.go | 116 + modules/git/gpg.go | 78 + modules/git/gpg_test.go | 33 + modules/git/grep.go | 140 ++ modules/git/grep_test.go | 86 + modules/git/hook.go | 113 + modules/git/hooks.go | 245 ++ modules/git/hooks_test.go | 34 + modules/git/internal/cmdarg.go | 9 + modules/git/key.go | 55 + modules/git/languagestats/language_stats.go | 65 + .../git/languagestats/language_stats_get.go | 209 ++ .../git/languagestats/language_stats_test.go | 44 + modules/git/languagestats/main_test.go | 14 + modules/git/last_commit_cache.go | 74 + modules/git/last_commit_cache_gogit.go | 63 + modules/git/last_commit_cache_nogogit.go | 50 + modules/git/localfs.go | 66 + modules/git/log_name_status_nogogit.go | 390 +++ modules/git/main_test.go | 25 + modules/git/merge.go | 26 + modules/git/merge_tree.go | 59 + modules/git/merge_tree_test.go | 82 + modules/git/notes.go | 89 + modules/git/notes_test.go | 46 + modules/git/object_format.go | 165 ++ modules/git/object_id.go | 106 + modules/git/object_id_gogit.go | 30 + modules/git/object_id_test.go | 32 + modules/git/parse.go | 68 + modules/git/parse_treeentry.go | 73 + modules/git/parse_treeentry_test.go | 133 + modules/git/pipeline/catfile.go | 60 + modules/git/pipeline/lfs_common.go | 27 + modules/git/pipeline/lfs_find.go | 149 ++ modules/git/pipeline/lfs_test.go | 38 + modules/git/pipeline/main_test.go | 14 + modules/git/pipeline/namerev.go | 58 + modules/git/pipeline/revlist.go | 48 + modules/git/push.go | 27 + modules/git/redirection.go | 15 + modules/git/ref.go | 266 ++ modules/git/ref_test.go | 60 + modules/git/remote.go | 91 + modules/git/remote_managed.go | 57 + modules/git/repo.go | 320 +++ modules/git/repo_base_gogit.go | 72 + modules/git/repo_base_nogogit.go | 21 + modules/git/repo_base_nogogit_test.go | 31 + modules/git/repo_blob.go | 19 + modules/git/repo_blob_test.go | 69 + modules/git/repo_branch.go | 26 + modules/git/repo_branch_gogit.go | 153 ++ modules/git/repo_branch_nogogit.go | 184 ++ modules/git/repo_branch_test.go | 203 ++ modules/git/repo_commit.go | 459 ++++ modules/git/repo_commit_gogit.go | 106 + modules/git/repo_commit_nogogit.go | 140 ++ modules/git/repo_commit_test.go | 192 ++ modules/git/repo_commitgraph.go | 19 + modules/git/repo_commitgraph_gogit.go | 38 + modules/git/repo_compare.go | 134 + modules/git/repo_compare_test.go | 171 ++ modules/git/repo_index.go | 153 ++ modules/git/repo_object.go | 78 + modules/git/repo_ref.go | 98 + modules/git/repo_ref_gogit.go | 48 + modules/git/repo_ref_nogogit.go | 76 + modules/git/repo_ref_test.go | 55 + modules/git/repo_stats.go | 141 ++ modules/git/repo_stats_test.go | 36 + modules/git/repo_tag.go | 234 ++ modules/git/repo_tag_gogit.go | 101 + modules/git/repo_tag_nogogit.go | 131 + modules/git/repo_tag_test.go | 363 +++ modules/git/repo_test.go | 45 + modules/git/repo_tree.go | 70 + modules/git/repo_tree_gogit.go | 66 + modules/git/repo_tree_nogogit.go | 95 + modules/git/signature.go | 49 + modules/git/signature_gogit.go | 14 + modules/git/signature_nogogit.go | 30 + modules/git/signature_test.go | 47 + modules/git/size.go | 39 + modules/git/submodule.go | 58 + modules/git/submodule_test.go | 49 + modules/git/tag.go | 125 + modules/git/tag_test.go | 96 + .../git/tests/repos/language_stats_repo/HEAD | 1 + .../tests/repos/language_stats_repo/config | 5 + .../git/tests/repos/language_stats_repo/index | Bin 0 -> 553 bytes .../tests/repos/language_stats_repo/logs/HEAD | 1 + .../logs/refs/heads/master | 1 + .../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 .../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/index | Bin 0 -> 65 bytes modules/git/tests/repos/repo1_bare/logs/HEAD | 2 + .../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 .../git/tests/repos/repo1_bare/pulls/1.patch | 0 .../git/tests/repos/repo1_bare/pulls/2.patch | 39 + .../tests/repos/repo1_bare/refs/heads/branch1 | 1 + .../tests/repos/repo1_bare/refs/heads/branch2 | 1 + .../tests/repos/repo1_bare/refs/heads/master | 1 + .../tests/repos/repo1_bare/refs/notes/commits | 1 + .../repos/repo1_bare/refs/tags/signed-tag | 1 + .../git/tests/repos/repo1_bare/refs/tags/test | 1 + .../git/tests/repos/repo1_bare_sha256/HEAD | 1 + .../git/tests/repos/repo1_bare_sha256/config | 6 + .../tests/repos/repo1_bare_sha256/info/refs | 7 + .../objects/info/commit-graph | Bin 0 -> 2048 bytes .../repo1_bare_sha256/objects/info/packs | 2 + ...65970b0c38c6b495d5fc034bc7a7b95334b.bitmap | Bin 0 -> 710 bytes ...78665970b0c38c6b495d5fc034bc7a7b95334b.idx | Bin 0 -> 2576 bytes ...8665970b0c38c6b495d5fc034bc7a7b95334b.pack | Bin 0 -> 5656 bytes ...78665970b0c38c6b495d5fc034bc7a7b95334b.rev | Bin 0 -> 224 bytes .../tests/repos/repo1_bare_sha256/packed-refs | 8 + .../repos/repo1_bare_sha256/refs/heads/main | 1 + modules/git/tests/repos/repo2_empty/HEAD | 1 + modules/git/tests/repos/repo2_empty/config | 6 + .../repos/repo2_empty/objects/info/.gitkeep | 0 .../repos/repo2_empty/objects/pack/.gitkeep | 0 .../repos/repo2_empty/refs/heads/.gitkeep | 0 .../repos/repo2_empty/refs/tags/.gitkeep | 0 modules/git/tests/repos/repo3_notes/HEAD | 1 + modules/git/tests/repos/repo3_notes/config | 7 + modules/git/tests/repos/repo3_notes/index | Bin 0 -> 145 bytes modules/git/tests/repos/repo3_notes/logs/HEAD | 2 + .../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 .../tests/repos/repo3_notes/refs/heads/master | 1 + .../repos/repo3_notes/refs/notes/commits | 1 + .../git/tests/repos/repo4_commitsbetween/HEAD | 1 + .../tests/repos/repo4_commitsbetween/config | 7 + .../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 .../repo4_commitsbetween/refs/heads/main | 1 + modules/git/tests/repos/repo4_submodules/HEAD | 1 + .../git/tests/repos/repo4_submodules/config | 4 + .../97/c3d30df0e6492348292600920a6482feaebb74 | Bin 0 -> 110 bytes .../c7/e064ed49b44523cba8a5dfbc37d2ce1bb41d34 | Bin 0 -> 112 bytes .../e1/e59caba97193d48862d6809912043871f37437 | 2 + .../repos/repo4_submodules/refs/heads/master | 1 + modules/git/tests/repos/repo5_pulls/HEAD | 1 + modules/git/tests/repos/repo5_pulls/config | 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 .../repos/repo5_pulls/objects/info/packs | 2 + ...423f591973f5d9dab89cc45afa1c544448133e.idx | Bin 0 -> 1408 bytes ...23f591973f5d9dab89cc45afa1c544448133e.pack | Bin 0 -> 2363 bytes .../git/tests/repos/repo5_pulls/packed-refs | 5 + .../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 + .../tests/repos/repo5_pulls/refs/pull/4/head | 1 + .../git/tests/repos/repo5_pulls_sha256/HEAD | 1 + .../git/tests/repos/repo5_pulls_sha256/config | 6 + .../tests/repos/repo5_pulls_sha256/info/refs | 4 + .../objects/info/commit-graph | Bin 0 -> 1544 bytes .../repo5_pulls_sha256/objects/info/packs | 2 + ...45d4a02b788eb26c31022a362312660a29d.bitmap | Bin 0 -> 414 bytes ...fe145d4a02b788eb26c31022a362312660a29d.idx | Bin 0 -> 1736 bytes ...e145d4a02b788eb26c31022a362312660a29d.pack | Bin 0 -> 3140 bytes ...fe145d4a02b788eb26c31022a362312660a29d.rev | Bin 0 -> 140 bytes .../repos/repo5_pulls_sha256/packed-refs | 5 + .../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 .../tests/repos/repo6_blame/refs/heads/master | 1 + .../git/tests/repos/repo6_blame_sha256/HEAD | 1 + .../git/tests/repos/repo6_blame_sha256/config | 6 + .../tests/repos/repo6_blame_sha256/info/refs | 1 + .../objects/info/commit-graph | Bin 0 -> 1376 bytes .../repo6_blame_sha256/objects/info/packs | 2 + ...1ac24312a5ffc3d3703d7c5cc906bdaee8e.bitmap | Bin 0 -> 318 bytes ...b611ac24312a5ffc3d3703d7c5cc906bdaee8e.idx | Bin 0 -> 1456 bytes ...611ac24312a5ffc3d3703d7c5cc906bdaee8e.pack | Bin 0 -> 904 bytes ...b611ac24312a5ffc3d3703d7c5cc906bdaee8e.rev | Bin 0 -> 112 bytes .../repos/repo6_blame_sha256/packed-refs | 2 + .../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 .../tests/repos/repo6_merge/refs/heads/main | 1 + .../repo6_merge/refs/heads/merge/add_file | 1 + .../repo6_merge/refs/heads/merge/modify_file | 1 + .../repo6_merge/refs/heads/merge/remove_file | 1 + .../git/tests/repos/repo6_merge_sha256/HEAD | 1 + .../git/tests/repos/repo6_merge_sha256/config | 6 + .../tests/repos/repo6_merge_sha256/info/refs | 4 + .../objects/info/commit-graph | Bin 0 -> 1564 bytes .../repo6_merge_sha256/objects/info/packs | 3 + ...096c7530f577d5c0a79c63d9ac2b44f7510.bitmap | Bin 0 -> 410 bytes ...b6a096c7530f577d5c0a79c63d9ac2b44f7510.idx | Bin 0 -> 1696 bytes ...6a096c7530f577d5c0a79c63d9ac2b44f7510.pack | Bin 0 -> 1556 bytes ...b6a096c7530f577d5c0a79c63d9ac2b44f7510.rev | Bin 0 -> 136 bytes ...e67bb2a4a5501ca1fa3528fad8a9474fba7e50.idx | Bin 0 -> 1176 bytes ...bb2a4a5501ca1fa3528fad8a9474fba7e50.mtimes | Bin 0 -> 84 bytes ...67bb2a4a5501ca1fa3528fad8a9474fba7e50.pack | Bin 0 -> 447 bytes ...e67bb2a4a5501ca1fa3528fad8a9474fba7e50.rev | Bin 0 -> 84 bytes .../repos/repo6_merge_sha256/packed-refs | 5 + .../repos/repo6_merge_sha256/refs/heads/main | 1 + modules/git/tree.go | 78 + modules/git/tree_blob.go | 21 + modules/git/tree_blob_gogit.go | 61 + modules/git/tree_blob_nogogit.go | 47 + modules/git/tree_entry.go | 171 ++ modules/git/tree_entry_common_test.go | 77 + modules/git/tree_entry_gogit.go | 63 + modules/git/tree_entry_gogit_test.go | 27 + modules/git/tree_entry_mode.go | 92 + modules/git/tree_entry_nogogit.go | 47 + modules/git/tree_entry_test.go | 59 + modules/git/tree_gogit.go | 82 + modules/git/tree_nogogit.go | 107 + modules/git/tree_test.go | 42 + modules/git/url/url.go | 187 ++ modules/git/url/url_test.go | 277 +++ modules/git/utils.go | 59 + modules/git/utils_test.go | 31 + modules/glob/glob.go | 243 ++ modules/glob/glob_test.go | 210 ++ modules/globallock/globallock.go | 82 + modules/globallock/globallock_test.go | 59 + modules/globallock/locker.go | 38 + modules/globallock/locker_test.go | 176 ++ modules/globallock/memory_locker.go | 67 + modules/globallock/redis_locker.go | 133 + modules/graceful/context.go | 36 + modules/graceful/manager.go | 252 ++ modules/graceful/manager_common.go | 110 + modules/graceful/manager_unix.go | 201 ++ modules/graceful/manager_windows.go | 190 ++ modules/graceful/net_unix.go | 322 +++ modules/graceful/net_windows.go | 19 + .../graceful/releasereopen/releasereopen.go | 61 + .../releasereopen/releasereopen_test.go | 43 + modules/graceful/restart_unix.go | 119 + modules/graceful/server.go | 294 +++ modules/graceful/server_hooks.go | 54 + modules/graceful/server_http.go | 44 + modules/gtprof/event.go | 32 + modules/gtprof/gtprof.go | 25 + modules/gtprof/trace.go | 175 ++ modules/gtprof/trace_builtin.go | 96 + modules/gtprof/trace_const.go | 19 + modules/gtprof/trace_test.go | 96 + modules/hcaptcha/error.go | 47 + modules/hcaptcha/hcaptcha.go | 140 ++ modules/hcaptcha/hcaptcha_test.go | 132 + modules/highlight/benchmark_test.go | 48 + modules/highlight/highlight.go | 213 ++ modules/highlight/highlight_test.go | 234 ++ modules/highlight/lexerdetect.go | 312 +++ modules/highlight/lexerdetect_test.go | 74 + modules/hostmatcher/hostmatcher.go | 211 ++ modules/hostmatcher/hostmatcher_test.go | 218 ++ modules/hostmatcher/http.go | 81 + modules/htmlutil/html.go | 185 ++ modules/htmlutil/html_test.go | 40 + modules/httpcache/httpcache.go | 108 + modules/httpcache/httpcache_test.go | 78 + modules/httplib/content_disposition.go | 65 + modules/httplib/content_disposition_test.go | 64 + modules/httplib/request.go | 165 ++ modules/httplib/serve.go | 240 ++ modules/httplib/serve_test.go | 143 ++ modules/httplib/url.go | 242 ++ modules/httplib/url_test.go | 223 ++ modules/indexer/code/bleve/bleve.go | 400 +++ modules/indexer/code/bleve/bleve_test.go | 59 + modules/indexer/code/bleve/code_token.go | 65 + modules/indexer/code/bleve/token/path/path.go | 102 + .../code/bleve/token/path/path_test.go | 76 + .../code/elasticsearch/elasticsearch.go | 411 +++ .../code/elasticsearch/elasticsearch_test.go | 16 + modules/indexer/code/git.go | 200 ++ modules/indexer/code/gitgrep/gitgrep.go | 66 + modules/indexer/code/gitgrep/gitgrep_test.go | 19 + modules/indexer/code/indexer.go | 321 +++ modules/indexer/code/indexer_test.go | 352 +++ modules/indexer/code/internal/indexer.go | 60 + modules/indexer/code/internal/model.go | 44 + modules/indexer/code/internal/util.go | 48 + modules/indexer/code/search.go | 153 ++ modules/indexer/indexer.go | 54 + modules/indexer/internal/base32.go | 21 + modules/indexer/internal/bleve/batch.go | 58 + modules/indexer/internal/bleve/indexer.go | 100 + modules/indexer/internal/bleve/query.go | 66 + modules/indexer/internal/bleve/util.go | 115 + modules/indexer/internal/bleve/util_test.go | 77 + modules/indexer/internal/db/indexer.go | 34 + .../indexer/internal/elasticsearch/indexer.go | 409 +++ .../internal/elasticsearch/indexer_test.go | 39 + .../indexer/internal/elasticsearch/query.go | 132 + .../indexer/internal/elasticsearch/types.go | 76 + .../indexer/internal/elasticsearch/util.go | 34 + modules/indexer/internal/indexer.go | 37 + .../indexer/internal/meilisearch/filter.go | 119 + .../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 | 326 +++ modules/indexer/issues/bleve/bleve_test.go | 127 + .../issues/bleve/camelcase_keep_whole.go | 64 + modules/indexer/issues/db/db.go | 143 ++ modules/indexer/issues/db/options.go | 116 + modules/indexer/issues/dboptions.go | 112 + .../issues/elasticsearch/elasticsearch.go | 257 ++ .../elasticsearch/elasticsearch_test.go | 53 + modules/indexer/issues/indexer.go | 327 +++ modules/indexer/issues/indexer_test.go | 501 ++++ modules/indexer/issues/internal/indexer.go | 48 + modules/indexer/issues/internal/model.go | 162 ++ .../indexer/issues/internal/tests/tests.go | 752 ++++++ .../indexer/issues/meilisearch/meilisearch.go | 316 +++ .../issues/meilisearch/meilisearch_test.go | 94 + modules/indexer/issues/util.go | 204 ++ modules/indexer/stats/db.go | 84 + modules/indexer/stats/indexer.go | 88 + modules/indexer/stats/indexer_test.go | 49 + modules/indexer/stats/queue.go | 49 + modules/issue/template/template.go | 492 ++++ modules/issue/template/template_test.go | 964 ++++++++ modules/issue/template/unmarshal.go | 147 ++ modules/json/json.go | 117 + modules/json/json_test.go | 28 + modules/json/jsonv2.go | 95 + modules/label/label.go | 51 + modules/label/parser.go | 118 + modules/label/parser_test.go | 72 + modules/lfs/LICENSE | 20 + modules/lfs/client.go | 48 + modules/lfs/client_test.go | 32 + modules/lfs/content_store.go | 165 ++ modules/lfs/endpoint.go | 115 + modules/lfs/endpoint_test.go | 92 + modules/lfs/filesystem_client.go | 88 + modules/lfs/http_client.go | 288 +++ modules/lfs/http_client_test.go | 369 +++ modules/lfs/pointer.go | 128 + modules/lfs/pointer_scanner_gogit.go | 55 + modules/lfs/pointer_scanner_nogogit.go | 98 + modules/lfs/pointer_test.go | 102 + modules/lfs/shared.go | 134 + modules/lfs/transferadapter.go | 89 + modules/lfs/transferadapter_test.go | 179 ++ modules/lfstransfer/backend/backend.go | 301 +++ modules/lfstransfer/backend/lock.go | 294 +++ modules/lfstransfer/backend/util.go | 149 ++ modules/lfstransfer/backend/util_test.go | 53 + modules/lfstransfer/logger.go | 21 + modules/lfstransfer/main.go | 44 + modules/log/color.go | 121 + modules/log/color_console.go | 14 + modules/log/color_console_other.go | 20 + modules/log/color_console_windows.go | 42 + modules/log/color_router.go | 87 + modules/log/event_format.go | 255 ++ modules/log/event_format_test.go | 55 + 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 | 72 + modules/log/event_writer_console.go | 35 + modules/log/event_writer_file.go | 54 + modules/log/flags.go | 134 + modules/log/flags_test.go | 30 + modules/log/init.go | 44 + modules/log/level.go | 116 + modules/log/level_test.go | 55 + modules/log/logger.go | 50 + modules/log/logger_global.go | 93 + modules/log/logger_impl.go | 270 ++ modules/log/logger_test.go | 186 ++ modules/log/manager.go | 142 ++ modules/log/manager_test.go | 45 + modules/log/misc.go | 79 + modules/log/stack.go | 80 + modules/markup/camo.go | 45 + modules/markup/camo_test.go | 44 + modules/markup/common/common.go | 84 + modules/markup/common/common_test.go | 39 + modules/markup/common/footnote.go | 484 ++++ modules/markup/common/footnote_test.go | 60 + modules/markup/common/linkify.go | 152 ++ modules/markup/console/console.go | 83 + modules/markup/console/console_test.go | 46 + modules/markup/csv/csv.go | 152 ++ modules/markup/csv/csv_test.go | 30 + modules/markup/external/external.go | 166 ++ modules/markup/external/external_test.go | 24 + modules/markup/external/frontend.go | 88 + modules/markup/html.go | 445 ++++ modules/markup/html_codepreview.go | 90 + modules/markup/html_codepreview_test.go | 31 + modules/markup/html_commit.go | 245 ++ modules/markup/html_email.go | 33 + modules/markup/html_emoji.go | 124 + modules/markup/html_internal_test.go | 438 ++++ modules/markup/html_issue.go | 201 ++ modules/markup/html_issue_test.go | 91 + modules/markup/html_link.go | 218 ++ modules/markup/html_link_test.go | 24 + modules/markup/html_mention.go | 54 + modules/markup/html_node.go | 187 ++ modules/markup/html_node_test.go | 104 + modules/markup/html_test.go | 599 +++++ modules/markup/html_toc_test.go | 60 + modules/markup/internal/finalprocessor.go | 58 + modules/markup/internal/internal_test.go | 90 + modules/markup/internal/renderinternal.go | 77 + modules/markup/jupyter/jupyter-test.ipynb | 74 + modules/markup/jupyter/jupyter.go | 395 +++ modules/markup/jupyter/jupyter_test.go | 314 +++ modules/markup/main_test.go | 19 + modules/markup/markdown/ast.go | 183 ++ modules/markup/markdown/convertyaml.go | 98 + modules/markup/markdown/goldmark.go | 219 ++ modules/markup/markdown/main_test.go | 18 + modules/markup/markdown/markdown.go | 295 +++ .../markdown/markdown_attention_test.go | 58 + .../markdown/markdown_benchmark_test.go | 25 + modules/markup/markdown/markdown_math_test.go | 318 +++ modules/markup/markdown/markdown_test.go | 622 +++++ modules/markup/markdown/math/block_node.go | 42 + modules/markup/markdown/math/block_parser.go | 136 + .../markup/markdown/math/block_renderer.go | 61 + modules/markup/markdown/math/inline_node.go | 48 + modules/markup/markdown/math/inline_parser.go | 175 ++ .../markup/markdown/math/inline_renderer.go | 53 + modules/markup/markdown/math/math.go | 60 + modules/markup/markdown/meta.go | 103 + modules/markup/markdown/meta_test.go | 107 + modules/markup/markdown/renderconfig.go | 121 + modules/markup/markdown/renderconfig_test.go | 139 ++ .../markup/markdown/transform_blockquote.go | 160 ++ .../markup/markdown/transform_codeblock.go | 32 + modules/markup/markdown/transform_codespan.go | 78 + modules/markup/markdown/transform_list.go | 96 + modules/markup/mdstripper/mdstripper.go | 198 ++ modules/markup/mdstripper/mdstripper_test.go | 85 + modules/markup/orgmode/orgmode.go | 140 ++ modules/markup/orgmode/orgmode_test.go | 94 + modules/markup/render.go | 368 +++ modules/markup/render_helper.go | 57 + modules/markup/render_link.go | 75 + modules/markup/render_link_test.go | 34 + modules/markup/render_test.go | 28 + modules/markup/renderer.go | 108 + modules/markup/sanitizer.go | 51 + modules/markup/sanitizer_custom.go | 31 + modules/markup/sanitizer_default.go | 149 ++ modules/markup/sanitizer_default_test.go | 80 + modules/markup/sanitizer_description.go | 37 + modules/markup/sanitizer_description_test.go | 31 + modules/mcaptcha/mcaptcha.go | 71 + modules/mcaptcha/mcaptcha_test.go | 53 + modules/metrics/collector.go | 395 +++ modules/migration/comment.go | 34 + modules/migration/downloader.go | 36 + modules/migration/error.go | 25 + modules/migration/file_format.go | 117 + modules/migration/file_format_test.go | 38 + .../file_format_testdata/issue_a.json | 14 + .../file_format_testdata/issue_a.yml | 10 + .../file_format_testdata/issue_b.json | 5 + .../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 | 85 + modules/migration/options.go | 46 + modules/migration/pullrequest.go | 75 + modules/migration/reaction.go | 17 + modules/migration/release.go | 49 + modules/migration/repo.go | 18 + modules/migration/retry_downloader.go | 185 ++ 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 | 36 + modules/migration/schemas_dynamic.go | 44 + modules/migration/uploader.go | 26 + modules/nosql/leveldb.go | 24 + modules/nosql/manager.go | 78 + 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 | 37 + modules/optional/option.go | 83 + modules/optional/option_test.go | 78 + modules/optional/serialization.go | 46 + modules/optional/serialization_test.go | 195 ++ modules/options/base.go | 42 + modules/options/options_bindata.go | 23 + modules/options/options_dynamic.go | 15 + modules/packages/alpine/metadata.go | 253 ++ modules/packages/alpine/metadata_test.go | 153 ++ modules/packages/arch/metadata.go | 267 ++ modules/packages/arch/metadata_test.go | 195 ++ modules/packages/cargo/parser.go | 178 ++ modules/packages/cargo/parser_test.go | 112 + modules/packages/chef/metadata.go | 134 + modules/packages/chef/metadata_test.go | 92 + modules/packages/composer/metadata.go | 285 +++ modules/packages/composer/metadata_test.go | 198 ++ modules/packages/conan/conanfile_parser.go | 67 + .../packages/conan/conanfile_parser_test.go | 50 + modules/packages/conan/conaninfo_parser.go | 123 + .../packages/conan/conaninfo_parser_test.go | 84 + modules/packages/conan/metadata.go | 23 + modules/packages/conan/reference.go | 155 ++ modules/packages/conan/reference_test.go | 147 ++ modules/packages/conda/metadata.go | 242 ++ modules/packages/conda/metadata_test.go | 151 ++ modules/packages/container/const.go | 11 + modules/packages/container/helm/helm.go | 55 + modules/packages/container/metadata.go | 188 ++ modules/packages/container/metadata_test.go | 68 + modules/packages/content_store.go | 81 + modules/packages/cran/metadata.go | 242 ++ modules/packages/cran/metadata_test.go | 161 ++ modules/packages/debian/metadata.go | 265 ++ modules/packages/debian/metadata_test.go | 246 ++ modules/packages/filelist.go | 37 + modules/packages/goproxy/metadata.go | 103 + modules/packages/goproxy/metadata_test.go | 85 + modules/packages/hashed_buffer.go | 82 + modules/packages/hashed_buffer_test.go | 49 + modules/packages/helm/metadata.go | 151 ++ modules/packages/maven/metadata.go | 111 + modules/packages/maven/metadata_test.go | 123 + modules/packages/multi_hasher.go | 131 + modules/packages/multi_hasher_test.go | 53 + modules/packages/npm/creator.go | 511 ++++ modules/packages/npm/creator_test.go | 503 ++++ modules/packages/npm/metadata.go | 36 + modules/packages/nuget/metadata.go | 276 +++ modules/packages/nuget/metadata_test.go | 217 ++ modules/packages/nuget/symbol_extractor.go | 186 ++ .../packages/nuget/symbol_extractor_test.go | 84 + modules/packages/pub/metadata.go | 153 ++ modules/packages/pub/metadata_test.go | 135 + modules/packages/pypi/metadata.go | 15 + modules/packages/rpm/metadata.go | 339 +++ modules/packages/rpm/metadata_test.go | 163 ++ modules/packages/rubygems/marshal.go | 311 +++ modules/packages/rubygems/marshal_test.go | 98 + modules/packages/rubygems/metadata.go | 228 ++ modules/packages/rubygems/metadata_test.go | 156 ++ modules/packages/swift/metadata.go | 256 ++ modules/packages/swift/metadata_test.go | 324 +++ modules/packages/terraform/lock.go | 99 + modules/packages/terraform/state.go | 38 + modules/packages/vagrant/metadata.go | 96 + modules/packages/vagrant/metadata_test.go | 110 + modules/paginator/paginator.go | 220 ++ modules/paginator/paginator_test.go | 310 +++ modules/pprof/pprof.go | 52 + modules/private/actions.go | 25 + modules/private/hook.go | 121 + modules/private/internal.go | 121 + modules/private/internal_test.go | 37 + modules/private/key.go | 30 + modules/private/mail.go | 33 + modules/private/manager.go | 93 + modules/private/pushoptions.go | 45 + modules/private/pushoptions_test.go | 30 + modules/private/request.go | 128 + modules/private/restore_repo.go | 35 + modules/private/serv.go | 65 + modules/process/command.go | 54 + modules/process/command_unix.go | 32 + modules/process/command_unix_test.go | 67 + modules/process/command_windows.go | 19 + modules/process/context.go | 68 + modules/process/manager.go | 242 ++ modules/process/manager_stacktraces.go | 355 +++ modules/process/manager_test.go | 88 + modules/process/process.go | 37 + modules/proxy/proxy.go | 97 + modules/proxyprotocol/conn.go | 505 ++++ modules/proxyprotocol/errors.go | 44 + modules/proxyprotocol/listener.go | 46 + modules/proxyprotocol/util.go | 14 + modules/public/manifest.go | 184 ++ modules/public/manifest_test.go | 76 + modules/public/mime_types.go | 50 + modules/public/public.go | 124 + modules/public/public_bindata.go | 23 + modules/public/public_dynamic.go | 15 + modules/public/public_test.go | 34 + modules/public/vitedev.go | 190 ++ modules/queue/backoff.go | 64 + modules/queue/base.go | 67 + modules/queue/base_channel.go | 131 + modules/queue/base_channel_test.go | 11 + modules/queue/base_dummy.go | 38 + modules/queue/base_levelqueue.go | 78 + modules/queue/base_levelqueue_common.go | 98 + modules/queue/base_levelqueue_test.go | 79 + modules/queue/base_levelqueue_unique.go | 83 + modules/queue/base_redis.go | 147 ++ modules/queue/base_redis_test.go | 20 + modules/queue/base_test.go | 169 ++ modules/queue/config.go | 36 + modules/queue/lqinternal/lqinternal.go | 48 + modules/queue/manager.go | 115 + modules/queue/manager_test.go | 119 + modules/queue/queue.go | 68 + modules/queue/testhelper.go | 40 + modules/queue/workergroup.go | 374 +++ modules/queue/workerqueue.go | 260 ++ modules/queue/workerqueue_test.go | 284 +++ modules/recaptcha/recaptcha.go | 88 + modules/references/references.go | 611 +++++ modules/references/references_test.go | 616 +++++ modules/regexplru/regexplru.go | 41 + modules/regexplru/regexplru_test.go | 25 + modules/repository/branch.go | 169 ++ modules/repository/branch_test.go | 31 + modules/repository/commits.go | 152 ++ modules/repository/commits_test.go | 167 ++ modules/repository/create.go | 28 + modules/repository/create_test.go | 24 + modules/repository/env.go | 95 + modules/repository/fork.go | 43 + modules/repository/fork_test.go | 25 + modules/repository/init.go | 156 ++ modules/repository/init_test.go | 30 + modules/repository/license.go | 113 + modules/repository/license_test.go | 176 ++ modules/repository/main_test.go | 17 + modules/repository/push.go | 74 + modules/repository/repo.go | 317 +++ modules/repository/repo_test.go | 76 + modules/repository/temp.go | 24 + modules/reqctx/datastore.go | 160 ++ modules/secret/secret.go | 80 + modules/secret/secret_test.go | 31 + modules/session/db.go | 176 ++ modules/session/key.go | 18 + modules/session/mem.go | 68 + modules/session/redis.go | 219 ++ modules/session/store.go | 48 + modules/session/virtual.go | 202 ++ modules/setting/actions.go | 173 ++ modules/setting/actions_test.go | 311 +++ modules/setting/admin.go | 56 + modules/setting/api.go | 42 + modules/setting/asset_dynamic.go | 8 + modules/setting/asset_static.go | 8 + modules/setting/attachment.go | 35 + modules/setting/attachment_test.go | 133 + modules/setting/audit.go | 54 + modules/setting/audit_test.go | 37 + modules/setting/cache.go | 77 + modules/setting/cache_test.go | 71 + modules/setting/camo.go | 32 + modules/setting/config.go | 109 + modules/setting/config/getter.go | 49 + modules/setting/config/value.go | 186 ++ modules/setting/config_env.go | 179 ++ modules/setting/config_env_test.go | 146 ++ modules/setting/config_option_instance.go | 58 + modules/setting/config_provider.go | 410 +++ modules/setting/config_provider_test.go | 151 ++ modules/setting/cors.go | 27 + modules/setting/cron.go | 32 + modules/setting/cron_test.go | 96 + modules/setting/database.go | 112 + modules/setting/federation.go | 51 + modules/setting/git.go | 119 + modules/setting/git_test.go | 63 + modules/setting/glob.go | 32 + modules/setting/global.go | 18 + modules/setting/global_lock.go | 37 + modules/setting/global_lock_test.go | 74 + modules/setting/highlight.go | 17 + modules/setting/i18n.go | 67 + modules/setting/incoming_email.go | 63 + modules/setting/indexer.go | 110 + modules/setting/indexer_test.go | 71 + modules/setting/lfs.go | 99 + modules/setting/lfs_test.go | 130 + modules/setting/log.go | 270 ++ modules/setting/log_test.go | 387 +++ modules/setting/mailer.go | 300 +++ modules/setting/mailer_test.go | 70 + modules/setting/markup.go | 294 +++ modules/setting/markup_test.go | 51 + modules/setting/metrics.go | 21 + modules/setting/migrations.go | 28 + modules/setting/mime_type_map.go | 28 + modules/setting/mirror.go | 54 + modules/setting/oauth2.go | 170 ++ modules/setting/oauth2_test.go | 78 + modules/setting/other.go | 29 + modules/setting/packages.go | 108 + modules/setting/packages_test.go | 198 ++ modules/setting/path.go | 224 ++ modules/setting/path_test.go | 151 ++ modules/setting/picture.go | 88 + modules/setting/project.go | 19 + modules/setting/proxy.go | 37 + modules/setting/queue.go | 123 + modules/setting/queue_test.go | 92 + modules/setting/redis.go | 18 + modules/setting/repository.go | 392 +++ modules/setting/repository_archive.go | 25 + modules/setting/repository_archive_test.go | 111 + modules/setting/repository_test.go | 66 + modules/setting/security.go | 203 ++ modules/setting/security_test.go | 29 + modules/setting/server.go | 323 +++ modules/setting/service.go | 290 +++ modules/setting/service_test.go | 160 ++ modules/setting/session.go | 85 + modules/setting/session_test.go | 79 + modules/setting/setting.go | 262 ++ modules/setting/ssh.go | 187 ++ modules/setting/storage.go | 342 +++ modules/setting/storage_test.go | 592 +++++ modules/setting/task.go | 26 + modules/setting/testenv.go | 175 ++ modules/setting/time.go | 27 + modules/setting/ui.go | 182 ++ modules/setting/webhook.go | 53 + modules/setting/websocket.go | 34 + modules/setting/websocket_test.go | 63 + modules/sitemap/sitemap.go | 86 + modules/sitemap/sitemap_test.go | 166 ++ modules/ssh/init.go | 59 + modules/ssh/server.go | 144 ++ modules/ssh/ssh.go | 374 +++ modules/ssh/ssh_test.go | 124 + modules/storage/azureblob.go | 340 +++ modules/storage/azureblob_test.go | 76 + modules/storage/helper.go | 39 + modules/storage/helper_test.go | 50 + modules/storage/local.go | 178 ++ modules/storage/local_test.go | 107 + modules/storage/minio.go | 335 +++ modules/storage/minio_test.go | 160 ++ modules/storage/storage.go | 277 +++ modules/storage/storage_test.go | 148 ++ modules/storage/testdata/aws_credentials | 3 + modules/storage/testdata/minio.json | 12 + modules/structs/activity.go | 37 + modules/structs/admin_user.go | 79 + modules/structs/attachment.go | 35 + modules/structs/cron.go | 20 + modules/structs/doc.go | 4 + modules/structs/fork.go | 12 + modules/structs/form.go | 22 + modules/structs/git_blob.go | 23 + modules/structs/git_hook.go | 23 + modules/structs/hook.go | 674 +++++ modules/structs/hook_test.go | 59 + modules/structs/issue.go | 291 +++ modules/structs/issue_comment.go | 100 + modules/structs/issue_label.go | 73 + modules/structs/issue_milestone.go | 59 + modules/structs/issue_reaction.go | 25 + modules/structs/issue_stopwatch.go | 30 + modules/structs/issue_test.go | 105 + modules/structs/issue_tracked_time.go | 40 + modules/structs/lfs_lock.go | 81 + modules/structs/mirror.go | 41 + modules/structs/miscellaneous.go | 123 + modules/structs/nodeinfo.go | 62 + modules/structs/notifications.go | 79 + modules/structs/org.go | 93 + modules/structs/org_member.go | 10 + modules/structs/org_team.go | 89 + modules/structs/package.go | 49 + modules/structs/project.go | 121 + modules/structs/pull.go | 197 ++ modules/structs/pull_review.go | 118 + modules/structs/release.go | 78 + modules/structs/repo.go | 464 ++++ modules/structs/repo_actions.go | 262 ++ modules/structs/repo_branch.go | 167 ++ modules/structs/repo_collaborator.go | 45 + modules/structs/repo_commit.go | 99 + modules/structs/repo_compare.go | 10 + modules/structs/repo_file.go | 222 ++ modules/structs/repo_key.go | 60 + modules/structs/repo_note.go | 12 + modules/structs/repo_refs.go | 24 + modules/structs/repo_tag.go | 99 + modules/structs/repo_topic.go | 39 + modules/structs/repo_tree.go | 36 + modules/structs/repo_watch.go | 24 + modules/structs/repo_wiki.go | 59 + modules/structs/secret.go | 31 + modules/structs/settings.go | 56 + modules/structs/status.go | 62 + modules/structs/task.go | 30 + modules/structs/token.go | 31 + modules/structs/user.go | 137 + modules/structs/user_app.go | 76 + modules/structs/user_email.go | 32 + modules/structs/user_gpgkey.go | 71 + modules/structs/user_key.go | 33 + modules/structs/variable.go | 49 + modules/structs/visible_type.go | 69 + modules/svg/processor.go | 56 + modules/svg/processor_test.go | 29 + modules/svg/svg.go | 133 + modules/svg/svg_test.go | 54 + modules/system/appstate.go | 26 + modules/system/appstate_test.go | 60 + modules/system/db.go | 36 + modules/system/item_runtime.go | 15 + modules/tailmsg/talimsg.go | 73 + modules/tempdir/tempdir.go | 112 + modules/tempdir/tempdir_test.go | 75 + modules/templates/base.go | 17 + modules/templates/eval/eval.go | 344 +++ modules/templates/eval/eval_test.go | 97 + modules/templates/helper.go | 275 +++ modules/templates/helper_test.go | 197 ++ modules/templates/htmlrenderer.go | 221 ++ modules/templates/htmlrenderer_test.go | 106 + modules/templates/mail.go | 266 ++ modules/templates/page.go | 103 + modules/templates/scopedtmpl/scopedtmpl.go | 231 ++ .../templates/scopedtmpl/scopedtmpl_test.go | 95 + modules/templates/templates_bindata.go | 23 + modules/templates/templates_dynamic.go | 15 + modules/templates/util_actions.go | 23 + modules/templates/util_avatar.go | 79 + modules/templates/util_date.go | 133 + modules/templates/util_date_test.go | 64 + modules/templates/util_dict.go | 122 + modules/templates/util_format.go | 38 + modules/templates/util_format_test.go | 18 + modules/templates/util_json.go | 35 + modules/templates/util_misc.go | 230 ++ modules/templates/util_render.go | 432 ++++ modules/templates/util_render_comment.go | 47 + modules/templates/util_render_comment_test.go | 31 + modules/templates/util_render_test.go | 354 +++ modules/templates/util_slice.go | 66 + modules/templates/util_string.go | 66 + modules/templates/util_test.go | 89 + modules/templates/vars/vars.go | 99 + modules/templates/vars/vars_test.go | 87 + modules/test/file.go | 87 + modules/test/logchecker.go | 105 + modules/test/logchecker_test.go | 45 + modules/test/redis.go | 72 + modules/test/utils.go | 250 ++ modules/testlogger/testlogger.go | 180 ++ modules/timeutil/since.go | 108 + modules/timeutil/since_test.go | 83 + modules/timeutil/timestamp.go | 101 + modules/timeutil/timestampnano.go | 28 + modules/translation/i18n/format.go | 42 + modules/translation/i18n/i18n.go | 62 + modules/translation/i18n/i18n_test.go | 159 ++ modules/translation/i18n/localestore.go | 184 ++ modules/translation/mock.go | 63 + modules/translation/translation.go | 265 ++ modules/translation/translation_test.go | 32 + modules/turnstile/turnstile.go | 103 + modules/turnstile/turnstile_test.go | 38 + modules/typesniffer/typesniffer.go | 189 ++ modules/typesniffer/typesniffer_test.go | 179 ++ modules/updatechecker/update_checker.go | 97 + modules/uri/uri.go | 50 + modules/uri/uri_test.go | 69 + modules/user/user.go | 35 + modules/user/user_test.go | 41 + modules/util/buffer.go | 22 + modules/util/bytesize.go | 16 + modules/util/bytesize_test.go | 27 + modules/util/color.go | 74 + modules/util/color_test.go | 65 + modules/util/diff_slice_test.go | 74 + modules/util/error.go | 120 + modules/util/error_test.go | 51 + modules/util/file_retry.go | 60 + modules/util/file_retry_test.go | 44 + modules/util/file_unix.go | 27 + modules/util/file_unix_test.go | 35 + modules/util/file_windows.go | 15 + modules/util/filebuffer/file_backed_buffer.go | 133 + .../filebuffer/file_backed_buffer_test.go | 36 + modules/util/filename.go | 54 + modules/util/filename_test.go | 20 + modules/util/io.go | 105 + modules/util/io_test.go | 66 + modules/util/keypair.go | 57 + modules/util/keypair_test.go | 63 + modules/util/legacy.go | 91 + modules/util/legacy_test.go | 57 + modules/util/pack.go | 33 + modules/util/pack_test.go | 28 + modules/util/paginate.go | 26 + modules/util/paginate_test.go | 39 + modules/util/path.go | 366 +++ modules/util/path_test.go | 302 +++ modules/util/rotatingfilewriter/writer.go | 248 ++ .../util/rotatingfilewriter/writer_test.go | 48 + modules/util/runtime.go | 26 + modules/util/runtime_test.go | 32 + modules/util/sanitize.go | 128 + modules/util/sanitize_test.go | 107 + modules/util/sec_to_time.go | 42 + modules/util/sec_to_time_test.go | 28 + modules/util/shellquote.go | 101 + modules/util/shellquote_test.go | 93 + modules/util/slice.go | 79 + modules/util/slice_test.go | 55 + modules/util/string.go | 141 ++ modules/util/string_test.go | 52 + modules/util/sync.go | 51 + modules/util/sync_test.go | 49 + modules/util/time_str.go | 92 + modules/util/time_str_test.go | 58 + modules/util/timer.go | 36 + modules/util/timer_test.go | 30 + modules/util/trie.go | 56 + modules/util/trie_test.go | 34 + modules/util/truncate.go | 146 ++ modules/util/truncate_test.go | 131 + modules/util/url.go | 28 + modules/util/util.go | 324 +++ modules/util/util_test.go | 177 ++ modules/validation/binding.go | 150 ++ modules/validation/binding_test.go | 29 + modules/validation/glob_pattern_test.go | 56 + modules/validation/helpers.go | 110 + modules/validation/helpers_test.go | 162 ++ modules/validation/refname_test.go | 258 ++ modules/validation/regex_pattern_test.go | 53 + modules/validation/validurl_test.go | 101 + modules/web/handler.go | 196 ++ modules/web/middleware/binding.go | 200 ++ modules/web/middleware/binding_test.go | 28 + modules/web/middleware/cookie.go | 107 + modules/web/middleware/data.go | 48 + modules/web/middleware/flash.go | 92 + modules/web/middleware/locale.go | 71 + modules/web/middleware/locale_test.go | 27 + modules/web/routemock.go | 65 + modules/web/routemock_test.go | 70 + modules/web/router.go | 319 +++ modules/web/router_combo.go | 41 + modules/web/router_path.go | 185 ++ modules/web/router_test.go | 342 +++ modules/web/routing/context.go | 92 + modules/web/routing/funcinfo.go | 172 ++ modules/web/routing/funcinfo_test.go | 80 + modules/web/routing/logger.go | 104 + modules/web/routing/logger_manager.go | 90 + modules/web/routing/requestinfo.go | 43 + modules/web/routing/requestrecord.go | 31 + modules/web/types/premiddleware.go | 13 + modules/web/types/response.go | 10 + modules/webhook/events.go | 20 + modules/webhook/type.go | 141 ++ modules/zstd/option.go | 46 + modules/zstd/zstd.go | 163 ++ modules/zstd/zstd_test.go | 304 +++ options/gitignore/AL | 11 + options/gitignore/Actionscript | 18 + options/gitignore/Ada | 5 + options/gitignore/Agda | 2 + options/gitignore/Alteryx | 44 + options/gitignore/AltiumDesigner | 20 + options/gitignore/Android | 33 + options/gitignore/Anjuta | 3 + options/gitignore/Ansible | 1 + options/gitignore/AppEngine | 2 + options/gitignore/AppceleratorTitanium | 3 + options/gitignore/ArchLinuxPackages | 13 + options/gitignore/Archives | 34 + options/gitignore/AtmelStudio | 22 + options/gitignore/AutoIt | 8 + options/gitignore/AutomationStudio | 31 + options/gitignore/Autotools | 52 + options/gitignore/B4X | 3 + options/gitignore/Backup | 5 + options/gitignore/Ballerina | 11 + options/gitignore/Bazaar | 2 + options/gitignore/Bazel | 13 + options/gitignore/Beef | 3 + options/gitignore/Bitrix | 23 + options/gitignore/BricxCC | 4 + options/gitignore/C | 52 + options/gitignore/C++ | 32 + options/gitignore/CDK | 4 + options/gitignore/CFWheels | 12 + options/gitignore/CMake | 12 + options/gitignore/CUDA | 6 + options/gitignore/CVS | 4 + options/gitignore/CakePHP | 25 + options/gitignore/Calabash | 10 + options/gitignore/ChefCookbook | 9 + options/gitignore/Clojure | 14 + options/gitignore/Cloud9 | 3 + options/gitignore/CodeIgniter | 18 + options/gitignore/CodeKit | 4 + options/gitignore/CodeSniffer | 6 + options/gitignore/CommonLisp | 17 + options/gitignore/Composer | 6 + options/gitignore/Concrete5 | 21 + options/gitignore/Coq | 45 + options/gitignore/Cordova | 10 + options/gitignore/CraftCMS | 4 + options/gitignore/D | 24 + options/gitignore/DM | 5 + options/gitignore/Dart | 27 + options/gitignore/DartEditor | 2 + options/gitignore/Delphi | 81 + options/gitignore/Diff | 2 + options/gitignore/Dreamweaver | 7 + options/gitignore/Dropbox | 4 + options/gitignore/Drupal | 62 + options/gitignore/Drupal7 | 43 + options/gitignore/ECU-TEST | 62 + options/gitignore/EPiServer | 4 + options/gitignore/Eagle | 51 + options/gitignore/Eclipse | 60 + options/gitignore/EiffelStudio | 2 + options/gitignore/Elisp | 11 + options/gitignore/Elixir | 10 + options/gitignore/Elm | 4 + options/gitignore/Emacs | 49 + options/gitignore/Ensime | 4 + options/gitignore/Erlang | 17 + options/gitignore/Espresso | 1 + options/gitignore/Exercism | 5 + options/gitignore/ExpressionEngine | 19 + options/gitignore/ExtJs | 14 + options/gitignore/Fancy | 2 + options/gitignore/Finale | 13 + options/gitignore/Firebase | 28 + options/gitignore/FlaxEngine | 45 + options/gitignore/FlexBuilder | 3 + options/gitignore/Flutter | 119 + options/gitignore/ForceDotCom | 4 + options/gitignore/Fortran | 32 + options/gitignore/FuelPHP | 21 + options/gitignore/GNOMEShellExtension | 3 + options/gitignore/GPG | 2 + options/gitignore/GWT | 25 + options/gitignore/Gcov | 5 + options/gitignore/GitBook | 16 + options/gitignore/GitHubPages | 18 + options/gitignore/Go | 25 + options/gitignore/Go.AllowList | 23 + options/gitignore/Godot | 15 + options/gitignore/Gradle | 21 + options/gitignore/Grails | 33 + options/gitignore/Gretl | 8 + options/gitignore/Haskell | 23 + options/gitignore/Hexo | 14 + options/gitignore/Hugo | 13 + options/gitignore/IAR | 47 + options/gitignore/IAR_EWARM | 19 + options/gitignore/IGORPro | 5 + options/gitignore/Idris | 7 + options/gitignore/Images | 63 + options/gitignore/InforCMS | 15 + options/gitignore/JBoss | 19 + options/gitignore/JBoss4 | 19 + options/gitignore/JBoss6 | 33 + options/gitignore/JDeveloper | 13 + options/gitignore/JENKINS_HOME | 50 + options/gitignore/JEnv | 5 + options/gitignore/Java | 24 + options/gitignore/Jekyll | 7 + options/gitignore/JetBrains | 77 + options/gitignore/Jigsaw | 6 + options/gitignore/Joomla | 705 ++++++ options/gitignore/Julia | 24 + options/gitignore/JupyterNotebooks | 12 + options/gitignore/KDevelop4 | 2 + options/gitignore/Kate | 3 + options/gitignore/Kentico | 64 + options/gitignore/KiCad | 31 + options/gitignore/Kohana | 2 + options/gitignore/Kotlin | 27 + options/gitignore/LabVIEW | 17 + options/gitignore/Laravel | 30 + options/gitignore/Lazarus | 30 + options/gitignore/Leiningen | 14 + options/gitignore/LemonStand | 21 + options/gitignore/LensStudio | 16 + options/gitignore/LibreOffice | 2 + options/gitignore/Lilypond | 6 + options/gitignore/Linux | 13 + options/gitignore/Lithium | 2 + options/gitignore/Logtalk | 19 + options/gitignore/Lua | 41 + options/gitignore/LyX | 4 + options/gitignore/MATLAB | 31 + options/gitignore/Magento | 45 + options/gitignore/Magento1 | 45 + options/gitignore/Magento2 | 54 + options/gitignore/Maven | 17 + options/gitignore/Mercurial | 6 + options/gitignore/Mercury | 13 + options/gitignore/MetaProgrammingSystem | 16 + options/gitignore/Metals | 5 + options/gitignore/Meteor | 12 + options/gitignore/MicrosoftOffice | 19 + options/gitignore/ModelSim | 23 + options/gitignore/Modelica | 42 + options/gitignore/Momentics | 8 + options/gitignore/MonoDevelop | 8 + options/gitignore/Move | 6 + options/gitignore/NWjs | 29 + options/gitignore/Nanoc | 10 + options/gitignore/NasaSpecsIntact | 40 + options/gitignore/NetBeans | 8 + options/gitignore/Nikola | 7 + options/gitignore/Nim | 3 + options/gitignore/Ninja | 2 + options/gitignore/Nix | 6 + options/gitignore/Node | 136 + options/gitignore/NotepadPP | 2 + options/gitignore/NotesAndCoreConfiguration | 16 + .../gitignore/NotesAndExtendedConfiguration | 38 + options/gitignore/NotesOnly | 4 + options/gitignore/OCaml | 29 + options/gitignore/Objective-C | 44 + options/gitignore/Octave | 31 + options/gitignore/Opa | 13 + options/gitignore/OpenCart | 20 + options/gitignore/OpenSSL | 34 + options/gitignore/OpenTofu | 42 + options/gitignore/OracleForms | 8 + options/gitignore/Otto | 1 + options/gitignore/PSoCCreator | 18 + options/gitignore/Packer | 16 + options/gitignore/Patch | 2 + options/gitignore/Perl | 35 + options/gitignore/Perl6 | 7 + options/gitignore/Phalcon | 2 + options/gitignore/Phoenix | 22 + options/gitignore/Pimcore | 53 + options/gitignore/PlayFramework | 16 + options/gitignore/Plone | 18 + options/gitignore/Prestashop | 173 ++ options/gitignore/Processing | 11 + options/gitignore/PuTTY | 2 + options/gitignore/Puppet | 16 + options/gitignore/PureScript | 9 + options/gitignore/Python | 174 ++ options/gitignore/Qooxdoo | 5 + options/gitignore/Qt | 54 + options/gitignore/R | 49 + options/gitignore/ROS | 51 + options/gitignore/ROS2 | 29 + options/gitignore/Racket | 15 + options/gitignore/Rails | 69 + options/gitignore/Raku | 7 + options/gitignore/ReScript | 3 + options/gitignore/Red | 16 + options/gitignore/Redcar | 1 + options/gitignore/Redis | 3 + options/gitignore/RhodesRhomobile | 9 + options/gitignore/Ruby | 56 + options/gitignore/Rust | 17 + options/gitignore/SAM | 5 + options/gitignore/SBT | 12 + options/gitignore/SCons | 6 + options/gitignore/SPFx | 33 + options/gitignore/SVN | 1 + options/gitignore/Sass | 4 + options/gitignore/Scala | 5 + options/gitignore/Scheme | 7 + options/gitignore/Scrivener | 8 + options/gitignore/Sdcc | 8 + options/gitignore/SeamGen | 26 + options/gitignore/SketchUp | 1 + options/gitignore/SlickEdit | 11 + options/gitignore/Smalltalk | 31 + options/gitignore/Snap | 14 + options/gitignore/Splunk | 8 + options/gitignore/Stata | 24 + options/gitignore/Stella | 12 + options/gitignore/Strapi | 135 + options/gitignore/SublimeText | 31 + options/gitignore/SugarCRM | 27 + options/gitignore/Swift | 62 + options/gitignore/Symfony | 52 + options/gitignore/SymphonyCMS | 6 + options/gitignore/Syncthing | 2 + options/gitignore/SynopsysVCS | 36 + options/gitignore/Tags | 17 + options/gitignore/TeX | 307 +++ options/gitignore/Terraform | 37 + options/gitignore/Terragrunt | 3 + options/gitignore/TextMate | 3 + options/gitignore/Textpattern | 11 + options/gitignore/ThinkPHP | 8 + options/gitignore/Toit | 2 + options/gitignore/TortoiseGit | 2 + options/gitignore/TurboGears2 | 20 + options/gitignore/TwinCAT3 | 25 + options/gitignore/Typo3 | 23 + options/gitignore/UiPath | 11 + options/gitignore/Umbraco | 52 + options/gitignore/Unity | 73 + options/gitignore/UnrealEngine | 74 + options/gitignore/V | 11 + options/gitignore/VVVV | 6 + options/gitignore/Vagrant | 5 + options/gitignore/Vim | 19 + options/gitignore/VirtualEnv | 12 + options/gitignore/Virtuoso | 18 + options/gitignore/VisualStudio | 400 +++ options/gitignore/VisualStudioCode | 12 + options/gitignore/Vue | 9 + options/gitignore/Waf | 9 + options/gitignore/WebMethods | 14 + options/gitignore/Windows | 24 + options/gitignore/WordPress | 48 + options/gitignore/Xcode | 2 + options/gitignore/Xilinx | 105 + options/gitignore/XilinxISE | 67 + options/gitignore/Xojo | 11 + options/gitignore/Yeoman | 6 + options/gitignore/Yii | 6 + options/gitignore/ZendFramework | 24 + options/gitignore/Zephir | 26 + options/gitignore/Zig | 2 + options/gitignore/core | 38 + options/gitignore/esp-idf | 6 + options/gitignore/macOS | 26 + options/gitignore/uVision | 45 + options/label/Advanced.yaml | 81 + options/label/Default | 7 + options/license/0BSD | 5 + options/license/AGPL-3.0 | 235 ++ options/license/Apache-2.0 | 73 + options/license/BSD-2-Clause | 9 + options/license/BSD-3-Clause | 11 + options/license/BSD-3-Clause-Clear | 14 + options/license/BSL-1.0 | 7 + options/license/CC-BY-4.0 | 156 ++ options/license/CC-BY-SA-4.0 | 170 ++ options/license/CC0-1.0 | 121 + options/license/EPL-1.0 | 73 + options/license/EPL-2.0 | 80 + options/license/EUPL-1.2 | 190 ++ options/license/GPL-2.0 | 117 + options/license/GPL-3.0 | 232 ++ options/license/ISC | 8 + options/license/LGPL-2.1 | 176 ++ options/license/LGPL-3.0 | 304 +++ options/license/MIT | 18 + options/license/MIT-0 | 16 + options/license/MPL-2.0 | 373 +++ options/license/MulanPSL-2.0 | 130 + options/license/OFL-1.1 | 43 + options/license/OSL-3.0 | 47 + options/license/UPL-1.0 | 17 + options/license/Unlicense | 24 + options/license/WTFPL | 11 + options/license/Zlib | 11 + options/locale/TRANSLATORS | 80 + options/locale/locale_fi-FI.json | 1445 +++++++++++ options/locale/locale_hu-HU.json | 1358 ++++++++++ options/locale/locale_id-ID.json | 1186 +++++++++ options/locale/locale_is-IS.json | 1099 +++++++++ options/locale/locale_sk-SK.json | 1124 +++++++++ options/locale/locale_sv-SE.json | 1699 +++++++++++++ options/readme/Default | 3 + package.json | 115 + playwright.config.ts | 40 + pnpm-workspace.yaml | 5 + public/.well-known/security.txt | 6 + public/assets/img/apple-touch-icon.png | Bin 0 -> 3884 bytes public/assets/img/avatar_default.png | Bin 0 -> 4786 bytes public/assets/img/checkmark.png | Bin 0 -> 169 bytes public/assets/img/dingtalk.ico | Bin 0 -> 7886 bytes public/assets/img/discord.png | Bin 0 -> 1559 bytes public/assets/img/emoji/codeberg.png | Bin 0 -> 8317 bytes public/assets/img/emoji/git.png | Bin 0 -> 5007 bytes public/assets/img/emoji/gitea.png | Bin 0 -> 12918 bytes public/assets/img/emoji/github.png | Bin 0 -> 14111 bytes public/assets/img/emoji/gitlab.png | Bin 0 -> 6873 bytes public/assets/img/emoji/gogs.png | Bin 0 -> 11794 bytes public/assets/img/failed.png | Bin 0 -> 11009 bytes public/assets/img/favicon.png | Bin 0 -> 4335 bytes public/assets/img/favicon.svg | 1 + public/assets/img/gitea.svg | 1 + public/assets/img/gogs.png | Bin 0 -> 29157 bytes public/assets/img/loading.png | Bin 0 -> 12629 bytes public/assets/img/logo.png | Bin 0 -> 12927 bytes public/assets/img/logo.svg | 1 + public/assets/img/msteams.png | Bin 0 -> 4313 bytes public/assets/img/openid-16x16.png | Bin 0 -> 172 bytes public/assets/img/packagist.png | Bin 0 -> 4573 bytes public/assets/img/repo_default.png | Bin 0 -> 1645 bytes public/assets/img/slack.png | Bin 0 -> 1428 bytes public/assets/img/svg/fontawesome-openid.svg | 1 + public/assets/img/svg/fontawesome-save.svg | 1 + public/assets/img/svg/fontawesome-send.svg | 1 + public/assets/img/svg/fontawesome-windows.svg | 1 + public/assets/img/svg/gitea-alpine.svg | 1 + public/assets/img/svg/gitea-arch.svg | 1 + public/assets/img/svg/gitea-azuread.svg | 1 + public/assets/img/svg/gitea-azureadv2.svg | 1 + public/assets/img/svg/gitea-bitbucket.svg | 1 + public/assets/img/svg/gitea-cargo.svg | 1 + public/assets/img/svg/gitea-chef.svg | 1 + public/assets/img/svg/gitea-codebase.svg | 1 + public/assets/img/svg/gitea-codecommit.svg | 1 + .../img/svg/gitea-colorblind-blueyellow.svg | 1 + .../img/svg/gitea-colorblind-redgreen.svg | 1 + public/assets/img/svg/gitea-composer.svg | 1 + public/assets/img/svg/gitea-conan.svg | 1 + public/assets/img/svg/gitea-conda.svg | 1 + public/assets/img/svg/gitea-cran.svg | 1 + public/assets/img/svg/gitea-debian.svg | 1 + public/assets/img/svg/gitea-discord.svg | 1 + .../img/svg/gitea-double-chevron-left.svg | 1 + .../img/svg/gitea-double-chevron-right.svg | 1 + public/assets/img/svg/gitea-dropbox.svg | 1 + public/assets/img/svg/gitea-eclipse.svg | 1 + .../assets/img/svg/gitea-empty-checkbox.svg | 1 + public/assets/img/svg/gitea-exclamation.svg | 1 + public/assets/img/svg/gitea-facebook.svg | 1 + public/assets/img/svg/gitea-feishu.svg | 1 + public/assets/img/svg/gitea-git.svg | 1 + public/assets/img/svg/gitea-gitbucket.svg | 1 + public/assets/img/svg/gitea-gitea.svg | 1 + public/assets/img/svg/gitea-gitlab.svg | 1 + public/assets/img/svg/gitea-go.svg | 1 + public/assets/img/svg/gitea-gogs.svg | 1 + public/assets/img/svg/gitea-google.svg | 1 + public/assets/img/svg/gitea-helm.svg | 1 + public/assets/img/svg/gitea-jetbrains.svg | 1 + public/assets/img/svg/gitea-join.svg | 1 + public/assets/img/svg/gitea-lock-cog.svg | 1 + public/assets/img/svg/gitea-lock.svg | 1 + public/assets/img/svg/gitea-mastodon.svg | 1 + public/assets/img/svg/gitea-matrix.svg | 1 + public/assets/img/svg/gitea-maven.svg | 1 + .../assets/img/svg/gitea-microsoftonline.svg | 1 + public/assets/img/svg/gitea-nextcloud.svg | 1 + public/assets/img/svg/gitea-npm.svg | 1 + public/assets/img/svg/gitea-nuget.svg | 1 + public/assets/img/svg/gitea-onedev.svg | 1 + public/assets/img/svg/gitea-openid.svg | 1 + public/assets/img/svg/gitea-pub.svg | 1 + public/assets/img/svg/gitea-python.svg | 1 + public/assets/img/svg/gitea-rpm.svg | 1 + public/assets/img/svg/gitea-rubygems.svg | 1 + public/assets/img/svg/gitea-running.svg | 1 + public/assets/img/svg/gitea-split.svg | 1 + public/assets/img/svg/gitea-swift.svg | 1 + public/assets/img/svg/gitea-terraform.svg | 1 + public/assets/img/svg/gitea-twitter.svg | 1 + public/assets/img/svg/gitea-unlock.svg | 1 + public/assets/img/svg/gitea-vagrant.svg | 1 + public/assets/img/svg/gitea-vscodium.svg | 1 + public/assets/img/svg/gitea-whitespace.svg | 1 + public/assets/img/svg/gitea-yandex.svg | 1 + .../img/svg/material-folder-generic.svg | 1 + .../img/svg/material-folder-symlink.svg | 1 + .../assets/img/svg/material-invert-colors.svg | 1 + public/assets/img/svg/material-palette.svg | 1 + .../img/svg/octicon-accessibility-inset.svg | 1 + .../assets/img/svg/octicon-accessibility.svg | 1 + public/assets/img/svg/octicon-agent.svg | 1 + public/assets/img/svg/octicon-ai-model.svg | 1 + public/assets/img/svg/octicon-alert-fill.svg | 1 + public/assets/img/svg/octicon-alert.svg | 1 + public/assets/img/svg/octicon-apps.svg | 1 + public/assets/img/svg/octicon-archive.svg | 1 + public/assets/img/svg/octicon-arrow-both.svg | 1 + .../img/svg/octicon-arrow-down-left.svg | 1 + .../img/svg/octicon-arrow-down-right.svg | 1 + public/assets/img/svg/octicon-arrow-down.svg | 1 + public/assets/img/svg/octicon-arrow-left.svg | 1 + public/assets/img/svg/octicon-arrow-right.svg | 1 + .../assets/img/svg/octicon-arrow-switch.svg | 1 + .../assets/img/svg/octicon-arrow-up-left.svg | 1 + .../assets/img/svg/octicon-arrow-up-right.svg | 1 + public/assets/img/svg/octicon-arrow-up.svg | 1 + public/assets/img/svg/octicon-beaker.svg | 1 + public/assets/img/svg/octicon-bell-fill.svg | 1 + public/assets/img/svg/octicon-bell-slash.svg | 1 + public/assets/img/svg/octicon-bell.svg | 1 + public/assets/img/svg/octicon-blocked.svg | 1 + public/assets/img/svg/octicon-bold.svg | 1 + public/assets/img/svg/octicon-book-locked.svg | 1 + public/assets/img/svg/octicon-book.svg | 1 + .../img/svg/octicon-bookmark-filled.svg | 1 + .../img/svg/octicon-bookmark-slash-fill.svg | 1 + .../assets/img/svg/octicon-bookmark-slash.svg | 1 + public/assets/img/svg/octicon-bookmark.svg | 1 + public/assets/img/svg/octicon-boolean-off.svg | 1 + public/assets/img/svg/octicon-boolean-on.svg | 1 + public/assets/img/svg/octicon-briefcase.svg | 1 + public/assets/img/svg/octicon-broadcast.svg | 1 + public/assets/img/svg/octicon-browser.svg | 1 + public/assets/img/svg/octicon-bug.svg | 1 + public/assets/img/svg/octicon-cache.svg | 1 + public/assets/img/svg/octicon-calendar.svg | 1 + public/assets/img/svg/octicon-chat-locked.svg | 1 + .../assets/img/svg/octicon-chat-question.svg | 1 + public/assets/img/svg/octicon-chat.svg | 1 + .../img/svg/octicon-check-circle-fill.svg | 1 + .../assets/img/svg/octicon-check-circle.svg | 1 + public/assets/img/svg/octicon-check.svg | 1 + .../assets/img/svg/octicon-checkbox-fill.svg | 1 + public/assets/img/svg/octicon-checkbox.svg | 1 + public/assets/img/svg/octicon-checklist.svg | 1 + .../assets/img/svg/octicon-chevron-down.svg | 1 + .../assets/img/svg/octicon-chevron-left.svg | 1 + .../assets/img/svg/octicon-chevron-right.svg | 1 + public/assets/img/svg/octicon-chevron-up.svg | 1 + .../assets/img/svg/octicon-circle-slash.svg | 1 + public/assets/img/svg/octicon-circle.svg | 1 + public/assets/img/svg/octicon-clock-fill.svg | 1 + public/assets/img/svg/octicon-clock.svg | 1 + .../assets/img/svg/octicon-cloud-offline.svg | 1 + public/assets/img/svg/octicon-cloud.svg | 1 + .../img/svg/octicon-code-of-conduct.svg | 1 + public/assets/img/svg/octicon-code-review.svg | 1 + public/assets/img/svg/octicon-code-square.svg | 1 + public/assets/img/svg/octicon-code.svg | 1 + .../img/svg/octicon-codescan-checkmark.svg | 1 + public/assets/img/svg/octicon-codescan.svg | 1 + public/assets/img/svg/octicon-codespaces.svg | 1 + public/assets/img/svg/octicon-columns.svg | 1 + .../img/svg/octicon-command-palette.svg | 1 + public/assets/img/svg/octicon-comment-ai.svg | 1 + .../img/svg/octicon-comment-discussion.svg | 1 + .../assets/img/svg/octicon-comment-locked.svg | 1 + public/assets/img/svg/octicon-comment.svg | 1 + public/assets/img/svg/octicon-compose.svg | 1 + public/assets/img/svg/octicon-container.svg | 1 + .../assets/img/svg/octicon-copilot-error.svg | 1 + .../img/svg/octicon-copilot-warning.svg | 1 + public/assets/img/svg/octicon-copilot.svg | 1 + public/assets/img/svg/octicon-copy.svg | 1 + public/assets/img/svg/octicon-cpu.svg | 1 + public/assets/img/svg/octicon-credit-card.svg | 1 + .../img/svg/octicon-cross-reference.svg | 1 + public/assets/img/svg/octicon-crosshairs.svg | 1 + public/assets/img/svg/octicon-dash.svg | 1 + public/assets/img/svg/octicon-database.svg | 1 + public/assets/img/svg/octicon-dependabot.svg | 1 + .../img/svg/octicon-desktop-download.svg | 1 + .../img/svg/octicon-device-camera-video.svg | 1 + .../assets/img/svg/octicon-device-camera.svg | 1 + .../assets/img/svg/octicon-device-desktop.svg | 1 + .../assets/img/svg/octicon-device-mobile.svg | 1 + public/assets/img/svg/octicon-devices.svg | 1 + public/assets/img/svg/octicon-diamond.svg | 1 + public/assets/img/svg/octicon-dice.svg | 1 + public/assets/img/svg/octicon-diff-added.svg | 1 + .../assets/img/svg/octicon-diff-ignored.svg | 1 + .../assets/img/svg/octicon-diff-modified.svg | 1 + .../assets/img/svg/octicon-diff-removed.svg | 1 + .../assets/img/svg/octicon-diff-renamed.svg | 1 + public/assets/img/svg/octicon-diff.svg | 1 + .../img/svg/octicon-discussion-closed.svg | 1 + .../img/svg/octicon-discussion-duplicate.svg | 1 + .../img/svg/octicon-discussion-outdated.svg | 1 + public/assets/img/svg/octicon-dot-fill.svg | 1 + public/assets/img/svg/octicon-dot.svg | 1 + public/assets/img/svg/octicon-download.svg | 1 + public/assets/img/svg/octicon-duplicate.svg | 1 + public/assets/img/svg/octicon-ellipsis.svg | 1 + public/assets/img/svg/octicon-exclamation.svg | 1 + public/assets/img/svg/octicon-eye-closed.svg | 1 + public/assets/img/svg/octicon-eye.svg | 1 + .../img/svg/octicon-feed-discussion.svg | 1 + public/assets/img/svg/octicon-feed-forked.svg | 1 + public/assets/img/svg/octicon-feed-heart.svg | 1 + .../img/svg/octicon-feed-issue-closed.svg | 1 + .../img/svg/octicon-feed-issue-draft.svg | 1 + .../img/svg/octicon-feed-issue-open.svg | 1 + .../img/svg/octicon-feed-issue-reopen.svg | 1 + public/assets/img/svg/octicon-feed-merged.svg | 1 + public/assets/img/svg/octicon-feed-person.svg | 1 + public/assets/img/svg/octicon-feed-plus.svg | 1 + public/assets/img/svg/octicon-feed-public.svg | 1 + .../svg/octicon-feed-pull-request-closed.svg | 1 + .../svg/octicon-feed-pull-request-draft.svg | 1 + .../svg/octicon-feed-pull-request-open.svg | 1 + public/assets/img/svg/octicon-feed-repo.svg | 1 + public/assets/img/svg/octicon-feed-rocket.svg | 1 + public/assets/img/svg/octicon-feed-star.svg | 1 + public/assets/img/svg/octicon-feed-tag.svg | 1 + public/assets/img/svg/octicon-feed-trophy.svg | 1 + public/assets/img/svg/octicon-file-added.svg | 1 + public/assets/img/svg/octicon-file-badge.svg | 1 + public/assets/img/svg/octicon-file-binary.svg | 1 + public/assets/img/svg/octicon-file-check.svg | 1 + public/assets/img/svg/octicon-file-code.svg | 1 + public/assets/img/svg/octicon-file-diff.svg | 1 + .../img/svg/octicon-file-directory-fill.svg | 1 + .../svg/octicon-file-directory-open-fill.svg | 1 + .../svg/octicon-file-directory-symlink.svg | 1 + .../assets/img/svg/octicon-file-directory.svg | 1 + public/assets/img/svg/octicon-file-media.svg | 1 + public/assets/img/svg/octicon-file-moved.svg | 1 + .../assets/img/svg/octicon-file-removed.svg | 1 + .../assets/img/svg/octicon-file-submodule.svg | 1 + .../img/svg/octicon-file-symlink-file.svg | 1 + public/assets/img/svg/octicon-file-zip.svg | 1 + public/assets/img/svg/octicon-file.svg | 1 + .../assets/img/svg/octicon-filter-remove.svg | 1 + public/assets/img/svg/octicon-filter.svg | 1 + public/assets/img/svg/octicon-fiscal-host.svg | 1 + public/assets/img/svg/octicon-flag.svg | 1 + public/assets/img/svg/octicon-flame.svg | 1 + public/assets/img/svg/octicon-flowchart.svg | 1 + .../assets/img/svg/octicon-focus-center.svg | 1 + public/assets/img/svg/octicon-fold-down.svg | 1 + public/assets/img/svg/octicon-fold-up.svg | 1 + public/assets/img/svg/octicon-fold.svg | 1 + public/assets/img/svg/octicon-gear.svg | 1 + public/assets/img/svg/octicon-gift.svg | 1 + .../img/svg/octicon-git-branch-check.svg | 1 + public/assets/img/svg/octicon-git-branch.svg | 1 + public/assets/img/svg/octicon-git-commit.svg | 1 + public/assets/img/svg/octicon-git-compare.svg | 1 + .../img/svg/octicon-git-merge-queue.svg | 1 + public/assets/img/svg/octicon-git-merge.svg | 1 + .../svg/octicon-git-pull-request-closed.svg | 1 + .../svg/octicon-git-pull-request-draft.svg | 1 + .../svg/octicon-git-pull-request-locked.svg | 1 + .../img/svg/octicon-git-pull-request.svg | 1 + public/assets/img/svg/octicon-globe.svg | 1 + public/assets/img/svg/octicon-goal.svg | 1 + public/assets/img/svg/octicon-grabber.svg | 1 + .../img/svg/octicon-graph-bar-horizontal.svg | 1 + .../img/svg/octicon-graph-bar-vertical.svg | 1 + .../img/svg/octicon-graph-stacked-area.svg | 1 + public/assets/img/svg/octicon-graph.svg | 1 + public/assets/img/svg/octicon-hash.svg | 1 + public/assets/img/svg/octicon-heading.svg | 1 + public/assets/img/svg/octicon-heart-fill.svg | 1 + public/assets/img/svg/octicon-heart.svg | 1 + public/assets/img/svg/octicon-history.svg | 1 + public/assets/img/svg/octicon-home-fill.svg | 1 + public/assets/img/svg/octicon-home.svg | 1 + .../img/svg/octicon-horizontal-rule.svg | 1 + public/assets/img/svg/octicon-hourglass.svg | 1 + public/assets/img/svg/octicon-hubot.svg | 1 + public/assets/img/svg/octicon-id-badge.svg | 1 + public/assets/img/svg/octicon-image.svg | 1 + public/assets/img/svg/octicon-inbox-fill.svg | 1 + public/assets/img/svg/octicon-inbox.svg | 1 + public/assets/img/svg/octicon-infinity.svg | 1 + public/assets/img/svg/octicon-info.svg | 1 + .../assets/img/svg/octicon-issue-closed.svg | 1 + public/assets/img/svg/octicon-issue-draft.svg | 1 + .../assets/img/svg/octicon-issue-locked.svg | 1 + .../assets/img/svg/octicon-issue-opened.svg | 1 + .../img/svg/octicon-issue-relates-to.svg | 1 + .../assets/img/svg/octicon-issue-reopened.svg | 1 + .../img/svg/octicon-issue-tracked-by.svg | 1 + .../assets/img/svg/octicon-issue-tracks.svg | 1 + public/assets/img/svg/octicon-italic.svg | 1 + public/assets/img/svg/octicon-iterations.svg | 1 + .../img/svg/octicon-kebab-horizontal.svg | 1 + .../assets/img/svg/octicon-key-asterisk.svg | 1 + public/assets/img/svg/octicon-key.svg | 1 + public/assets/img/svg/octicon-law.svg | 1 + public/assets/img/svg/octicon-library.svg | 1 + public/assets/img/svg/octicon-light-bulb.svg | 1 + .../assets/img/svg/octicon-link-external.svg | 1 + public/assets/img/svg/octicon-link.svg | 1 + .../assets/img/svg/octicon-list-ordered.svg | 1 + .../assets/img/svg/octicon-list-unordered.svg | 1 + public/assets/img/svg/octicon-location.svg | 1 + public/assets/img/svg/octicon-lock.svg | 1 + .../assets/img/svg/octicon-lockup-github.svg | 1 + public/assets/img/svg/octicon-log.svg | 1 + public/assets/img/svg/octicon-logo-gist.svg | 1 + public/assets/img/svg/octicon-logo-github.svg | 1 + public/assets/img/svg/octicon-loop.svg | 1 + public/assets/img/svg/octicon-mail.svg | 1 + public/assets/img/svg/octicon-mark-github.svg | 1 + public/assets/img/svg/octicon-markdown.svg | 1 + public/assets/img/svg/octicon-maximize.svg | 1 + public/assets/img/svg/octicon-mcp.svg | 1 + public/assets/img/svg/octicon-megaphone.svg | 1 + public/assets/img/svg/octicon-mention.svg | 1 + public/assets/img/svg/octicon-meter.svg | 1 + public/assets/img/svg/octicon-milestone.svg | 1 + public/assets/img/svg/octicon-minimize.svg | 1 + public/assets/img/svg/octicon-mirror.svg | 1 + public/assets/img/svg/octicon-moon.svg | 1 + .../assets/img/svg/octicon-mortar-board.svg | 1 + .../assets/img/svg/octicon-move-to-bottom.svg | 1 + public/assets/img/svg/octicon-move-to-end.svg | 1 + .../assets/img/svg/octicon-move-to-start.svg | 1 + public/assets/img/svg/octicon-move-to-top.svg | 1 + .../assets/img/svg/octicon-multi-select.svg | 1 + public/assets/img/svg/octicon-mute.svg | 1 + public/assets/img/svg/octicon-no-entry.svg | 1 + public/assets/img/svg/octicon-node.svg | 1 + public/assets/img/svg/octicon-north-star.svg | 1 + public/assets/img/svg/octicon-note.svg | 1 + public/assets/img/svg/octicon-number.svg | 1 + .../assets/img/svg/octicon-organization.svg | 1 + .../img/svg/octicon-package-dependencies.svg | 1 + .../img/svg/octicon-package-dependents.svg | 1 + public/assets/img/svg/octicon-package.svg | 1 + public/assets/img/svg/octicon-paintbrush.svg | 1 + .../assets/img/svg/octicon-paper-airplane.svg | 1 + public/assets/img/svg/octicon-paperclip.svg | 1 + .../assets/img/svg/octicon-passkey-fill.svg | 1 + public/assets/img/svg/octicon-paste.svg | 1 + public/assets/img/svg/octicon-pause.svg | 1 + public/assets/img/svg/octicon-pencil-ai.svg | 1 + public/assets/img/svg/octicon-pencil.svg | 1 + public/assets/img/svg/octicon-people.svg | 1 + public/assets/img/svg/octicon-person-add.svg | 1 + public/assets/img/svg/octicon-person-fill.svg | 1 + public/assets/img/svg/octicon-person.svg | 1 + public/assets/img/svg/octicon-pin-slash.svg | 1 + public/assets/img/svg/octicon-pin.svg | 1 + .../assets/img/svg/octicon-pivot-column.svg | 1 + public/assets/img/svg/octicon-play.svg | 1 + public/assets/img/svg/octicon-plug.svg | 1 + public/assets/img/svg/octicon-plus-circle.svg | 1 + public/assets/img/svg/octicon-plus.svg | 1 + .../assets/img/svg/octicon-project-check.svg | 1 + .../img/svg/octicon-project-roadmap.svg | 1 + .../img/svg/octicon-project-symlink.svg | 1 + .../img/svg/octicon-project-template.svg | 1 + public/assets/img/svg/octicon-project.svg | 1 + public/assets/img/svg/octicon-pulse.svg | 1 + .../img/svg/octicon-question-bubble.svg | 1 + public/assets/img/svg/octicon-question.svg | 1 + public/assets/img/svg/octicon-quote.svg | 1 + public/assets/img/svg/octicon-read.svg | 1 + public/assets/img/svg/octicon-redo.svg | 1 + .../assets/img/svg/octicon-rel-file-path.svg | 1 + public/assets/img/svg/octicon-reply.svg | 1 + public/assets/img/svg/octicon-repo-clone.svg | 1 + .../assets/img/svg/octicon-repo-deleted.svg | 1 + .../img/svg/octicon-repo-forked-locked.svg | 1 + public/assets/img/svg/octicon-repo-forked.svg | 1 + public/assets/img/svg/octicon-repo-locked.svg | 1 + public/assets/img/svg/octicon-repo-pull.svg | 1 + public/assets/img/svg/octicon-repo-push.svg | 1 + .../assets/img/svg/octicon-repo-template.svg | 1 + public/assets/img/svg/octicon-repo.svg | 1 + public/assets/img/svg/octicon-report.svg | 1 + public/assets/img/svg/octicon-rocket.svg | 1 + public/assets/img/svg/octicon-rows.svg | 1 + public/assets/img/svg/octicon-rss.svg | 1 + public/assets/img/svg/octicon-ruby.svg | 1 + public/assets/img/svg/octicon-sandbox.svg | 1 + public/assets/img/svg/octicon-screen-full.svg | 1 + .../assets/img/svg/octicon-screen-normal.svg | 1 + public/assets/img/svg/octicon-search.svg | 1 + public/assets/img/svg/octicon-server.svg | 1 + .../assets/img/svg/octicon-share-android.svg | 1 + public/assets/img/svg/octicon-share.svg | 1 + .../assets/img/svg/octicon-shield-check.svg | 1 + public/assets/img/svg/octicon-shield-lock.svg | 1 + .../assets/img/svg/octicon-shield-slash.svg | 1 + public/assets/img/svg/octicon-shield-x.svg | 1 + public/assets/img/svg/octicon-shield.svg | 1 + .../img/svg/octicon-sidebar-collapse.svg | 1 + .../assets/img/svg/octicon-sidebar-expand.svg | 1 + public/assets/img/svg/octicon-sign-in.svg | 1 + public/assets/img/svg/octicon-sign-out.svg | 1 + .../assets/img/svg/octicon-single-select.svg | 1 + public/assets/img/svg/octicon-skip-fill.svg | 1 + public/assets/img/svg/octicon-skip.svg | 1 + public/assets/img/svg/octicon-sliders.svg | 1 + .../assets/img/svg/octicon-smiley-frown.svg | 1 + .../img/svg/octicon-smiley-frustrated.svg | 1 + public/assets/img/svg/octicon-smiley-grin.svg | 1 + .../assets/img/svg/octicon-smiley-neutral.svg | 1 + public/assets/img/svg/octicon-smiley.svg | 1 + public/assets/img/svg/octicon-sort-asc.svg | 1 + public/assets/img/svg/octicon-sort-desc.svg | 1 + public/assets/img/svg/octicon-space.svg | 1 + .../assets/img/svg/octicon-spacing-large.svg | 1 + .../assets/img/svg/octicon-spacing-medium.svg | 1 + .../assets/img/svg/octicon-spacing-small.svg | 1 + .../assets/img/svg/octicon-sparkle-fill.svg | 1 + public/assets/img/svg/octicon-sparkle.svg | 1 + .../assets/img/svg/octicon-sparkles-fill.svg | 1 + public/assets/img/svg/octicon-split-view.svg | 1 + .../assets/img/svg/octicon-sponsor-tiers.svg | 1 + .../assets/img/svg/octicon-square-circle.svg | 1 + public/assets/img/svg/octicon-square-fill.svg | 1 + public/assets/img/svg/octicon-square.svg | 1 + public/assets/img/svg/octicon-squirrel.svg | 1 + public/assets/img/svg/octicon-stack-add.svg | 1 + public/assets/img/svg/octicon-stack-check.svg | 1 + .../assets/img/svg/octicon-stack-remove.svg | 1 + public/assets/img/svg/octicon-stack.svg | 1 + public/assets/img/svg/octicon-star-fill.svg | 1 + public/assets/img/svg/octicon-star.svg | 1 + public/assets/img/svg/octicon-stop.svg | 1 + public/assets/img/svg/octicon-stopwatch.svg | 1 + .../assets/img/svg/octicon-strikethrough.svg | 1 + public/assets/img/svg/octicon-sun.svg | 1 + public/assets/img/svg/octicon-sync.svg | 1 + .../assets/img/svg/octicon-tab-external.svg | 1 + public/assets/img/svg/octicon-tab.svg | 1 + public/assets/img/svg/octicon-table.svg | 1 + public/assets/img/svg/octicon-tag.svg | 1 + public/assets/img/svg/octicon-tasklist.svg | 1 + .../assets/img/svg/octicon-telescope-fill.svg | 1 + public/assets/img/svg/octicon-telescope.svg | 1 + public/assets/img/svg/octicon-terminal.svg | 1 + public/assets/img/svg/octicon-three-bars.svg | 1 + public/assets/img/svg/octicon-thumbsdown.svg | 1 + public/assets/img/svg/octicon-thumbsup.svg | 1 + public/assets/img/svg/octicon-tools.svg | 1 + .../octicon-tracked-by-closed-completed.svg | 1 + .../octicon-tracked-by-closed-not-planned.svg | 1 + public/assets/img/svg/octicon-trash.svg | 1 + .../assets/img/svg/octicon-triangle-down.svg | 1 + .../assets/img/svg/octicon-triangle-left.svg | 1 + .../assets/img/svg/octicon-triangle-right.svg | 1 + public/assets/img/svg/octicon-triangle-up.svg | 1 + public/assets/img/svg/octicon-trophy.svg | 1 + public/assets/img/svg/octicon-typography.svg | 1 + public/assets/img/svg/octicon-undo.svg | 1 + public/assets/img/svg/octicon-unfold.svg | 1 + public/assets/img/svg/octicon-unlink.svg | 1 + public/assets/img/svg/octicon-unlock.svg | 1 + public/assets/img/svg/octicon-unmute.svg | 1 + public/assets/img/svg/octicon-unread.svg | 1 + public/assets/img/svg/octicon-unverified.svg | 1 + public/assets/img/svg/octicon-unwrap.svg | 1 + public/assets/img/svg/octicon-upload.svg | 1 + public/assets/img/svg/octicon-verified.svg | 1 + public/assets/img/svg/octicon-versions.svg | 1 + public/assets/img/svg/octicon-video.svg | 1 + public/assets/img/svg/octicon-view-files.svg | 1 + public/assets/img/svg/octicon-vscode.svg | 1 + public/assets/img/svg/octicon-webhook.svg | 1 + public/assets/img/svg/octicon-workflow.svg | 1 + public/assets/img/svg/octicon-wrap.svg | 1 + .../assets/img/svg/octicon-x-circle-fill.svg | 1 + public/assets/img/svg/octicon-x-circle.svg | 1 + public/assets/img/svg/octicon-x.svg | 1 + public/assets/img/svg/octicon-zap.svg | 1 + public/assets/img/svg/octicon-zoom-in.svg | 1 + public/assets/img/svg/octicon-zoom-out.svg | 1 + public/assets/img/telegram.png | Bin 0 -> 9857 bytes public/assets/img/wechatwork.png | Bin 0 -> 9341 bytes pyproject.toml | 15 + renovate.json5 | 143 ++ routers/api/actions/actions.go | 24 + routers/api/actions/artifact.pb.go | 831 +++++++ routers/api/actions/artifact.proto | 76 + routers/api/actions/artifacts.go | 525 ++++ routers/api/actions/artifacts_chunks.go | 399 +++ routers/api/actions/artifacts_utils.go | 105 + routers/api/actions/artifactsv4.go | 757 ++++++ routers/api/actions/job_summary.go | 104 + routers/api/actions/ping/ping.go | 36 + routers/api/actions/ping/ping_test.go | 61 + routers/api/actions/runner/interceptor.go | 93 + routers/api/actions/runner/runner.go | 357 +++ routers/api/actions/runner/runner_test.go | 57 + routers/api/packages/README.md | 50 + routers/api/packages/alpine/alpine.go | 284 +++ routers/api/packages/api.go | 597 +++++ routers/api/packages/arch/arch.go | 305 +++ routers/api/packages/auth.go | 59 + routers/api/packages/cargo/cargo.go | 304 +++ routers/api/packages/chef/auth.go | 280 +++ routers/api/packages/chef/chef.go | 402 +++ routers/api/packages/composer/api.go | 143 ++ routers/api/packages/composer/composer.go | 248 ++ routers/api/packages/conan/conan.go | 833 +++++++ routers/api/packages/conan/search.go | 164 ++ routers/api/packages/conda/conda.go | 322 +++ routers/api/packages/container/blob.go | 195 ++ routers/api/packages/container/container.go | 806 ++++++ routers/api/packages/container/errors.go | 52 + routers/api/packages/container/manifest.go | 416 ++++ routers/api/packages/cran/cran.go | 263 ++ routers/api/packages/debian/debian.go | 306 +++ routers/api/packages/generic/generic.go | 202 ++ routers/api/packages/generic/generic_test.go | 65 + routers/api/packages/goproxy/goproxy.go | 223 ++ routers/api/packages/helm/helm.go | 284 +++ routers/api/packages/helper/helper.go | 71 + routers/api/packages/maven/api.go | 47 + routers/api/packages/maven/maven.go | 490 ++++ routers/api/packages/maven/maven_test.go | 15 + routers/api/packages/npm/api.go | 141 ++ routers/api/packages/npm/api_test.go | 66 + routers/api/packages/npm/npm.go | 565 +++++ routers/api/packages/nuget/api_v2.go | 416 ++++ routers/api/packages/nuget/api_v3.go | 313 +++ routers/api/packages/nuget/auth.go | 26 + routers/api/packages/nuget/links.go | 52 + routers/api/packages/nuget/nuget.go | 698 ++++++ routers/api/packages/pub/pub.go | 280 +++ routers/api/packages/pypi/pypi.go | 229 ++ routers/api/packages/pypi/pypi_test.go | 48 + routers/api/packages/rpm/rpm.go | 471 ++++ routers/api/packages/rubygems/rubygems.go | 466 ++++ .../api/packages/rubygems/rubygems_test.go | 41 + routers/api/packages/swift/swift.go | 506 ++++ routers/api/packages/terraform/terraform.go | 434 ++++ .../api/packages/terraform/terraform_test.go | 36 + routers/api/packages/vagrant/vagrant.go | 238 ++ routers/api/v1/activitypub/person.go | 14 + routers/api/v1/admin/action.go | 103 + routers/api/v1/admin/adopt.go | 181 ++ routers/api/v1/admin/cron.go | 86 + routers/api/v1/admin/email.go | 87 + routers/api/v1/admin/hooks.go | 204 ++ routers/api/v1/admin/org.go | 127 + routers/api/v1/admin/packages.go | 87 + routers/api/v1/admin/repo.go | 49 + routers/api/v1/admin/runners.go | 126 + routers/api/v1/admin/user.go | 570 +++++ routers/api/v1/admin/user_badge.go | 124 + routers/api/v1/api.go | 1948 +++++++++++++++ routers/api/v1/api_test.go | 25 + routers/api/v1/misc/gitignore.go | 56 + routers/api/v1/misc/label_templates.go | 60 + routers/api/v1/misc/licenses.go | 75 + routers/api/v1/misc/markup.go | 90 + routers/api/v1/misc/markup_test.go | 196 ++ routers/api/v1/misc/signing.go | 101 + routers/api/v1/misc/version.go | 25 + routers/api/v1/notify/notifications.go | 77 + routers/api/v1/notify/repo.go | 227 ++ routers/api/v1/notify/threads.go | 119 + routers/api/v1/notify/user.go | 176 ++ routers/api/v1/org/action.go | 668 +++++ routers/api/v1/org/avatar.go | 80 + routers/api/v1/org/block.go | 116 + routers/api/v1/org/hook.go | 189 ++ routers/api/v1/org/label.go | 258 ++ routers/api/v1/org/member.go | 346 +++ routers/api/v1/org/org.go | 589 +++++ routers/api/v1/org/team.go | 894 +++++++ routers/api/v1/packages/package.go | 491 ++++ routers/api/v1/repo/action.go | 2196 +++++++++++++++++ routers/api/v1/repo/actions_run.go | 254 ++ routers/api/v1/repo/avatar.go | 88 + routers/api/v1/repo/blob.go | 55 + routers/api/v1/repo/branch.go | 1351 ++++++++++ routers/api/v1/repo/collaborators.go | 408 +++ routers/api/v1/repo/commits.go | 420 ++++ routers/api/v1/repo/compare.go | 131 + routers/api/v1/repo/download.go | 54 + routers/api/v1/repo/file.go | 958 +++++++ routers/api/v1/repo/fork.go | 181 ++ routers/api/v1/repo/git_hook.go | 197 ++ routers/api/v1/repo/git_ref.go | 108 + routers/api/v1/repo/hook.go | 308 +++ routers/api/v1/repo/hook_test.go | 42 + routers/api/v1/repo/issue.go | 997 ++++++++ routers/api/v1/repo/issue_assignee.go | 238 ++ routers/api/v1/repo/issue_attachment.go | 399 +++ routers/api/v1/repo/issue_comment.go | 694 ++++++ .../api/v1/repo/issue_comment_attachment.go | 420 ++++ routers/api/v1/repo/issue_dependency.go | 583 +++++ routers/api/v1/repo/issue_label.go | 370 +++ routers/api/v1/repo/issue_lock.go | 143 ++ routers/api/v1/repo/issue_pin.go | 309 +++ routers/api/v1/repo/issue_reaction.go | 460 ++++ routers/api/v1/repo/issue_stopwatch.go | 239 ++ routers/api/v1/repo/issue_subscription.go | 294 +++ routers/api/v1/repo/issue_tracked_time.go | 644 +++++ routers/api/v1/repo/key.go | 309 +++ routers/api/v1/repo/label.go | 277 +++ routers/api/v1/repo/language.go | 81 + routers/api/v1/repo/license.go | 51 + routers/api/v1/repo/main_test.go | 21 + routers/api/v1/repo/migrate.go | 277 +++ routers/api/v1/repo/milestone.go | 301 +++ routers/api/v1/repo/mirror.go | 419 ++++ routers/api/v1/repo/mirror_test.go | 40 + routers/api/v1/repo/notes.go | 90 + routers/api/v1/repo/patch.go | 66 + routers/api/v1/repo/pull.go | 1632 ++++++++++++ routers/api/v1/repo/pull_review.go | 1100 +++++++++ routers/api/v1/repo/release.go | 445 ++++ routers/api/v1/repo/release_attachment.go | 415 ++++ routers/api/v1/repo/release_tags.go | 132 + routers/api/v1/repo/repo.go | 1320 ++++++++++ routers/api/v1/repo/repo_test.go | 86 + routers/api/v1/repo/star.go | 62 + routers/api/v1/repo/status.go | 278 +++ routers/api/v1/repo/subscriber.go | 60 + routers/api/v1/repo/tag.go | 641 +++++ routers/api/v1/repo/teams.go | 244 ++ routers/api/v1/repo/topic.go | 312 +++ routers/api/v1/repo/transfer.go | 220 ++ routers/api/v1/repo/tree.go | 70 + routers/api/v1/repo/wiki.go | 508 ++++ routers/api/v1/settings/settings.go | 86 + routers/api/v1/shared/action.go | 247 ++ routers/api/v1/shared/block.go | 98 + routers/api/v1/shared/project.go | 2113 ++++++++++++++++ routers/api/v1/shared/runners.go | 146 ++ routers/api/v1/swagger/action.go | 48 + routers/api/v1/swagger/activity.go | 15 + routers/api/v1/swagger/app.go | 29 + routers/api/v1/swagger/cron.go | 15 + routers/api/v1/swagger/issue.go | 120 + routers/api/v1/swagger/key.go | 50 + routers/api/v1/swagger/misc.go | 64 + routers/api/v1/swagger/notify.go | 29 + routers/api/v1/swagger/options.go | 261 ++ routers/api/v1/swagger/org.go | 43 + routers/api/v1/swagger/package.go | 29 + routers/api/v1/swagger/project.go | 36 + routers/api/v1/swagger/repo.go | 511 ++++ routers/api/v1/swagger/settings.go | 34 + routers/api/v1/swagger/user.go | 57 + routers/api/v1/token/token.go | 88 + routers/api/v1/user/action.go | 428 ++++ routers/api/v1/user/app.go | 462 ++++ routers/api/v1/user/avatar.go | 65 + routers/api/v1/user/block.go | 96 + routers/api/v1/user/email.go | 143 ++ routers/api/v1/user/follower.go | 267 ++ routers/api/v1/user/gpg_key.go | 326 +++ routers/api/v1/user/helper.go | 34 + routers/api/v1/user/hook.go | 159 ++ routers/api/v1/user/key.go | 317 +++ routers/api/v1/user/repo.go | 169 ++ routers/api/v1/user/runners.go | 126 + routers/api/v1/user/settings.go | 65 + routers/api/v1/user/star.go | 220 ++ routers/api/v1/user/user.go | 221 ++ routers/api/v1/user/watch.go | 225 ++ routers/api/v1/utils/git.go | 89 + routers/api/v1/utils/hook.go | 418 ++++ routers/api/v1/utils/hook_test.go | 82 + routers/api/v1/utils/main_test.go | 21 + routers/api/v1/utils/page.go | 18 + routers/api/v1/utils/sort.go | 38 + routers/api/v1/utils/sort_test.go | 44 + routers/common/actions.go | 132 + routers/common/audit.go | 26 + routers/common/auth.go | 58 + routers/common/blockexpensive.go | 89 + routers/common/blockexpensive_test.go | 28 + routers/common/codesearch.go | 33 + routers/common/compare.go | 221 ++ routers/common/compare_test.go | 154 ++ routers/common/db.go | 58 + routers/common/deadline.go | 31 + routers/common/errpage.go | 76 + routers/common/errpage_test.go | 47 + routers/common/issue_filter.go | 87 + routers/common/issue_filter_test.go | 93 + routers/common/lfs.go | 31 + routers/common/maintenancemode.go | 53 + routers/common/markup.go | 110 + routers/common/middleware.go | 147 ++ routers/common/pagetmpl.go | 83 + routers/common/qos.go | 123 + routers/common/qos_test.go | 63 + routers/common/redirect.go | 27 + routers/common/redirect_test.go | 48 + routers/common/serve.go | 45 + routers/init.go | 215 ++ routers/install/install.go | 560 +++++ routers/install/install_test.go | 62 + routers/install/routes.go | 51 + routers/private/actions.go | 77 + routers/private/hook_post_receive.go | 293 +++ routers/private/hook_post_receive_test.go | 49 + routers/private/hook_pre_receive.go | 385 +++ routers/private/hook_pre_receive_test.go | 64 + routers/private/hook_proc_receive.go | 52 + routers/private/hook_verification.go | 92 + routers/private/hook_verification_test.go | 44 + routers/private/internal.go | 112 + routers/private/internal_repo.go | 60 + routers/private/key.go | 57 + routers/private/mail.go | 77 + routers/private/main_test.go | 14 + routers/private/manager.go | 80 + routers/private/manager_process.go | 152 ++ routers/private/manager_unix.go | 25 + routers/private/manager_windows.go | 24 + routers/private/restore_repo.go | 46 + routers/private/serv.go | 308 +++ routers/private/ssh_log.go | 33 + .../tests/repos/repo1_hook_verification/HEAD | 1 + .../repos/repo1_hook_verification/config | 6 + .../repos/repo1_hook_verification/info/refs | 1 + .../repos/repo1_hook_verification/logs/HEAD | 1 + .../logs/refs/heads/main | 1 + .../08/cbc8f0e903b0916025ae7577564b7ed39ecb2c | Bin 0 -> 86 bytes .../0b/5987362fe3fabdd4406babdc819642ee2f5a2a | Bin 0 -> 22 bytes .../13/b0f23f673b161f4b5cb66f051cb93c99729e1e | Bin 0 -> 86 bytes .../23/33a51fdb238b7023a62ae3dcc58994061a7c86 | Bin 0 -> 86 bytes .../2b/df04adb23d2b40b6085efb230856e5e2a775b7 | Bin 0 -> 55 bytes .../65/a457425a679cbe9adf0d2741785d3ceabb44a7 | Bin 0 -> 50 bytes .../72/920278f2f999e3005801e5d5b8ab8139d3641c | 2 + .../8b/903ede7c494725624bf842ec890f6342dababd | Bin 0 -> 86 bytes .../93/eac826f6188f34646cea81bf426aa5ba7d3bfe | Bin 0 -> 684 bytes .../97/79d17a04f1e2640583d35703c62460b2d86e0a | 2 + .../9c/e3f779ae33f31fce17fac3c512047b75d7498b | Bin 0 -> 153 bytes .../a9/f76e70a663e40091749a97eeac5f57a6fec141 | Bin 0 -> 24 bytes .../ba/0caedd359ebe310ef431335576e20f2b84e9b9 | Bin 0 -> 63 bytes .../bb/87653e0819460e79b5f075f2563f583cbbf18c | Bin 0 -> 682 bytes .../cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa | 3 + .../d7/66f2917716d45be24bfa968b8409544941be32 | 3 + .../e3/7e5d19823e42fad252f6341b1f77a7bc6ee451 | Bin 0 -> 59 bytes .../e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 | Bin 0 -> 15 bytes .../e8/4e452c3a20c02dee17ec2f63c0cb9ef6c759eb | Bin 0 -> 677 bytes .../objects/info/packs | 1 + .../08/cbc8f0e903b0916025ae7577564b7ed39ecb2c | Bin 0 -> 86 bytes .../0b/5987362fe3fabdd4406babdc819642ee2f5a2a | Bin 0 -> 22 bytes .../13/b0f23f673b161f4b5cb66f051cb93c99729e1e | Bin 0 -> 86 bytes .../23/33a51fdb238b7023a62ae3dcc58994061a7c86 | Bin 0 -> 86 bytes .../8b/903ede7c494725624bf842ec890f6342dababd | Bin 0 -> 86 bytes .../93/eac826f6188f34646cea81bf426aa5ba7d3bfe | Bin 0 -> 684 bytes .../9c/e3f779ae33f31fce17fac3c512047b75d7498b | Bin 0 -> 153 bytes .../a9/f76e70a663e40091749a97eeac5f57a6fec141 | Bin 0 -> 24 bytes .../bb/87653e0819460e79b5f075f2563f583cbbf18c | Bin 0 -> 682 bytes .../cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa | 3 + .../repo1_hook_verification/refs/heads/main | 1 + .../repo1_hook_verification_dummy_gpg_key.txt | 127 + routers/utils/utils.go | 15 + routers/utils/utils_test.go | 42 + routers/web/admin/admin.go | 281 +++ routers/web/admin/admin_test.go | 45 + routers/web/admin/applications.go | 89 + routers/web/admin/audit.go | 71 + routers/web/admin/auths.go | 462 ++++ routers/web/admin/badges.go | 315 +++ routers/web/admin/config.go | 177 ++ routers/web/admin/diagnosis.go | 76 + routers/web/admin/emails.go | 190 ++ routers/web/admin/hooks.go | 77 + routers/web/admin/main_test.go | 14 + routers/web/admin/notice.go | 75 + routers/web/admin/orgs.go | 37 + routers/web/admin/packages.go | 106 + routers/web/admin/perftrace.go | 18 + routers/web/admin/queue.go | 89 + routers/web/admin/queue_tester.go | 78 + routers/web/admin/repos.go | 156 ++ routers/web/admin/stacktrace.go | 53 + routers/web/admin/users.go | 607 +++++ routers/web/admin/users_test.go | 199 ++ routers/web/auth/2fa.go | 164 ++ routers/web/auth/auth.go | 951 +++++++ routers/web/auth/auth_test.go | 201 ++ routers/web/auth/linkaccount.go | 280 +++ routers/web/auth/main_test.go | 14 + routers/web/auth/oauth.go | 595 +++++ routers/web/auth/oauth2_provider.go | 739 ++++++ routers/web/auth/oauth2_wellknown.go | 70 + routers/web/auth/oauth_signin_sync.go | 103 + routers/web/auth/oauth_test.go | 121 + routers/web/auth/openid.go | 416 ++++ routers/web/auth/password.go | 324 +++ routers/web/auth/webauthn.go | 273 ++ routers/web/base.go | 94 + routers/web/devtest/devtest.go | 309 +++ routers/web/devtest/mail_preview.go | 103 + routers/web/devtest/mock_actions.go | 579 +++++ routers/web/explore/code.go | 130 + routers/web/explore/org.go | 52 + routers/web/explore/repo.go | 176 ++ routers/web/explore/topic.go | 41 + routers/web/explore/user.go | 155 ++ routers/web/feed/branch.go | 57 + routers/web/feed/convert.go | 315 +++ routers/web/feed/file.go | 64 + routers/web/feed/profile.go | 99 + routers/web/feed/profile_test.go | 36 + routers/web/feed/release.go | 55 + routers/web/feed/render.go | 34 + routers/web/feed/repo.go | 47 + routers/web/githttp.go | 26 + routers/web/goget.go | 136 + routers/web/healthcheck/check.go | 142 ++ routers/web/home.go | 111 + routers/web/metrics.go | 33 + routers/web/misc/markup.go | 20 + routers/web/misc/misc.go | 91 + routers/web/misc/swagger.go | 14 + routers/web/misc/webtheme.go | 42 + routers/web/nodeinfo.go | 31 + routers/web/org/block.go | 38 + routers/web/org/home.go | 211 ++ routers/web/org/main_test.go | 14 + routers/web/org/members.go | 133 + routers/web/org/mention.go | 42 + routers/web/org/org.go | 89 + routers/web/org/org_labels.go | 115 + routers/web/org/projects.go | 478 ++++ routers/web/org/projects_test.go | 29 + routers/web/org/setting.go | 266 ++ routers/web/org/setting/audit.go | 22 + routers/web/org/setting_oauth2.go | 101 + routers/web/org/setting_packages.go | 127 + routers/web/org/teams.go | 650 +++++ routers/web/org/worktime.go | 82 + routers/web/passkey.go | 24 + routers/web/repo/actions/actions.go | 847 +++++++ routers/web/repo/actions/actions_test.go | 222 ++ routers/web/repo/actions/badge.go | 65 + routers/web/repo/actions/main_test.go | 14 + routers/web/repo/actions/view.go | 1504 +++++++++++ routers/web/repo/actions/view_test.go | 185 ++ routers/web/repo/activity.go | 114 + routers/web/repo/attachment.go | 246 ++ routers/web/repo/blame.go | 281 +++ routers/web/repo/branch.go | 248 ++ routers/web/repo/code_frequency.go | 41 + routers/web/repo/commit.go | 469 ++++ routers/web/repo/common_recentbranches.go | 73 + routers/web/repo/compare.go | 792 ++++++ routers/web/repo/compare_test.go | 130 + routers/web/repo/contributors.go | 38 + routers/web/repo/download.go | 165 ++ routers/web/repo/editor.go | 484 ++++ routers/web/repo/editor_apply_patch.go | 52 + routers/web/repo/editor_cherry_pick.go | 83 + routers/web/repo/editor_error.go | 82 + routers/web/repo/editor_fork.go | 31 + routers/web/repo/editor_preview.go | 47 + routers/web/repo/editor_test.go | 31 + routers/web/repo/editor_uploader.go | 63 + routers/web/repo/editor_util.go | 134 + routers/web/repo/fork.go | 240 ++ routers/web/repo/githttp.go | 549 +++++ routers/web/repo/githttp_test.go | 42 + routers/web/repo/helper.go | 19 + routers/web/repo/issue.go | 650 +++++ routers/web/repo/issue_comment.go | 450 ++++ routers/web/repo/issue_content_history.go | 234 ++ routers/web/repo/issue_dependency.go | 156 ++ routers/web/repo/issue_label.go | 230 ++ routers/web/repo/issue_label_test.go | 209 ++ routers/web/repo/issue_list.go | 690 ++++++ routers/web/repo/issue_lock.go | 60 + routers/web/repo/issue_new.go | 397 +++ routers/web/repo/issue_page_meta.go | 528 ++++ routers/web/repo/issue_pin.go | 116 + routers/web/repo/issue_poster.go | 39 + routers/web/repo/issue_stopwatch.go | 77 + routers/web/repo/issue_suggestions.go | 36 + routers/web/repo/issue_test.go | 375 +++ routers/web/repo/issue_timetrack.go | 119 + routers/web/repo/issue_view.go | 1095 ++++++++ routers/web/repo/issue_watch.go | 57 + routers/web/repo/main_test.go | 14 + routers/web/repo/mention.go | 59 + routers/web/repo/middlewares.go | 114 + routers/web/repo/middlewares_test.go | 59 + routers/web/repo/migrate.go | 326 +++ routers/web/repo/milestone.go | 271 ++ routers/web/repo/packages.go | 71 + routers/web/repo/projects.go | 504 ++++ routers/web/repo/pull.go | 1601 ++++++++++++ routers/web/repo/pull_merge_box.go | 191 ++ routers/web/repo/pull_merge_form.go | 190 ++ routers/web/repo/pull_review.go | 452 ++++ routers/web/repo/pull_review_test.go | 91 + routers/web/repo/recent_commits.go | 26 + routers/web/repo/release.go | 678 +++++ routers/web/repo/release_test.go | 200 ++ routers/web/repo/render.go | 78 + routers/web/repo/repo.go | 615 +++++ routers/web/repo/search.go | 78 + routers/web/repo/setting/actions.go | 191 ++ routers/web/repo/setting/audit.go | 19 + routers/web/repo/setting/avatar.go | 75 + routers/web/repo/setting/collaboration.go | 229 ++ routers/web/repo/setting/default_branch.go | 54 + routers/web/repo/setting/deploy_key.go | 124 + routers/web/repo/setting/git_hooks.go | 67 + routers/web/repo/setting/lfs.go | 526 ++++ routers/web/repo/setting/main_test.go | 14 + routers/web/repo/setting/protected_branch.go | 403 +++ routers/web/repo/setting/protected_tag.go | 198 ++ routers/web/repo/setting/public_access.go | 155 ++ routers/web/repo/setting/secrets.go | 130 + routers/web/repo/setting/setting.go | 1126 +++++++++ routers/web/repo/setting/settings_test.go | 283 +++ routers/web/repo/setting/webhook.go | 770 ++++++ routers/web/repo/setting/webhook_test.go | 15 + routers/web/repo/star.go | 30 + routers/web/repo/topic.go | 60 + routers/web/repo/transfer.go | 38 + routers/web/repo/treelist.go | 155 ++ routers/web/repo/treelist_test.go | 83 + routers/web/repo/view.go | 410 +++ routers/web/repo/view_file.go | 301 +++ routers/web/repo/view_home.go | 521 ++++ routers/web/repo/view_home_test.go | 37 + routers/web/repo/view_readme.go | 226 ++ routers/web/repo/view_test.go | 62 + routers/web/repo/watch.go | 62 + routers/web/repo/webgit.go | 40 + routers/web/repo/wiki.go | 740 ++++++ routers/web/repo/wiki_test.go | 274 ++ routers/web/shared/actions/general.go | 160 ++ routers/web/shared/actions/runners.go | 471 ++++ .../web/shared/actions/scoped_workflows.go | 368 +++ .../shared/actions/scoped_workflows_test.go | 75 + routers/web/shared/actions/variables.go | 222 ++ routers/web/shared/audit/audit.go | 105 + routers/web/shared/issue/issue_label.go | 78 + routers/web/shared/label/label.go | 26 + routers/web/shared/mention/mention.go | 89 + routers/web/shared/packages/packages.go | 252 ++ routers/web/shared/project/project.go | 205 ++ routers/web/shared/project/project_test.go | 60 + routers/web/shared/secrets/secrets.go | 77 + routers/web/shared/user/block.go | 80 + routers/web/shared/user/header.go | 195 ++ routers/web/shared/user/helper.go | 73 + routers/web/shared/user/helper_test.go | 26 + routers/web/swagger_json.go | 37 + routers/web/user/avatar.go | 47 + routers/web/user/code.go | 110 + routers/web/user/heatmap.go | 66 + routers/web/user/home.go | 838 +++++++ routers/web/user/home_test.go | 131 + routers/web/user/main_test.go | 14 + routers/web/user/notification.go | 424 ++++ routers/web/user/notification_test.go | 37 + routers/web/user/package.go | 605 +++++ routers/web/user/profile.go | 353 +++ routers/web/user/search.go | 35 + routers/web/user/setting/account.go | 312 +++ routers/web/user/setting/account_test.go | 101 + routers/web/user/setting/adopt.go | 61 + routers/web/user/setting/applications.go | 176 ++ routers/web/user/setting/audit.go | 19 + routers/web/user/setting/block.go | 33 + routers/web/user/setting/keys.go | 367 +++ routers/web/user/setting/main_test.go | 14 + routers/web/user/setting/notifications.go | 89 + routers/web/user/setting/oauth2.go | 69 + routers/web/user/setting/oauth2_common.go | 224 ++ routers/web/user/setting/packages.go | 119 + routers/web/user/setting/profile.go | 419 ++++ routers/web/user/setting/security/2fa.go | 286 +++ .../web/user/setting/security/main_test.go | 14 + routers/web/user/setting/security/openid.go | 169 ++ .../web/user/setting/security/openid_test.go | 30 + routers/web/user/setting/security/security.go | 163 ++ routers/web/user/setting/security/webauthn.go | 162 ++ routers/web/user/setting/settings.go | 34 + routers/web/user/setting/webhooks.go | 53 + routers/web/user/stop_watch.go | 40 + routers/web/web.go | 1809 ++++++++++++++ routers/web/webfinger.go | 122 + routers/web/websocket/websocket.go | 117 + routers/web/websocket/websocket_test.go | 60 + services/actions/approve.go | 149 ++ services/actions/approve_test.go | 95 + services/actions/artifacts.go | 65 + services/actions/auth.go | 108 + services/actions/auth_test.go | 64 + services/actions/cancel.go | 54 + services/actions/cleanup.go | 325 +++ services/actions/cleanup_test.go | 150 ++ services/actions/clear_tasks.go | 194 ++ services/actions/clear_tasks_test.go | 143 ++ services/actions/commit_status.go | 343 +++ services/actions/commit_status_test.go | 433 ++++ services/actions/concurrency.go | 86 + services/actions/context.go | 367 +++ services/actions/context_test.go | 435 ++++ services/actions/helper.go | 148 ++ services/actions/helper_test.go | 137 + services/actions/init.go | 71 + services/actions/init_test.go | 77 + services/actions/interface.go | 40 + services/actions/job_emitter.go | 617 +++++ services/actions/job_emitter_test.go | 695 ++++++ services/actions/matrix.go | 217 ++ services/actions/matrix_test.go | 271 ++ services/actions/max_parallel.go | 87 + services/actions/max_parallel_test.go | 255 ++ services/actions/notifier.go | 831 +++++++ services/actions/notifier_helper.go | 766 ++++++ services/actions/notifier_helper_test.go | 126 + services/actions/notify.go | 147 ++ services/actions/permission_parser.go | 142 ++ services/actions/permission_parser_test.go | 226 ++ services/actions/rerun.go | 680 +++++ services/actions/rerun_test.go | 455 ++++ services/actions/reusable_workflow.go | 458 ++++ services/actions/reusable_workflow_test.go | 366 +++ services/actions/run.go | 312 +++ services/actions/schedule_tasks.go | 186 ++ services/actions/schedule_tasks_test.go | 125 + services/actions/scoped_workflow_cache.go | 84 + services/actions/task.go | 195 ++ services/actions/task_test.go | 74 + services/actions/token_permission_design.md | 123 + services/actions/variables.go | 63 + services/actions/workflow.go | 266 ++ services/actions/workflow_test.go | 59 + services/agit/agit.go | 330 +++ services/agit/agit_test.go | 61 + services/asymkey/commit.go | 455 ++++ services/asymkey/commit_test.go | 133 + services/asymkey/deploy_key.go | 77 + services/asymkey/main_test.go | 18 + services/asymkey/sign.go | 380 +++ services/asymkey/sign_test.go | 39 + services/asymkey/ssh_key.go | 47 + services/asymkey/ssh_key_authorized_keys.go | 79 + .../asymkey/ssh_key_authorized_principals.go | 129 + services/asymkey/ssh_key_principals.go | 48 + services/asymkey/ssh_key_test.go | 87 + services/attachment/attachment.go | 100 + services/attachment/attachment_test.go | 45 + services/audit/actor.go | 83 + services/audit/audit_test.go | 279 +++ services/audit/event.go | 165 ++ services/audit/export.go | 25 + services/audit/export_test.go | 48 + services/audit/main_test.go | 14 + services/audit/message.go | 66 + services/audit/notifier.go | 154 ++ services/audit/notifier_test.go | 81 + services/audit/refs.go | 109 + services/audit/scoped.go | 122 + services/audit/scoped_test.go | 70 + services/auth/auth.go | 68 + services/auth/auth_token.go | 113 + services/auth/auth_token_test.go | 109 + services/auth/basic.go | 208 ++ services/auth/credential.go | 21 + services/auth/deploy_token.go | 47 + services/auth/group.go | 72 + services/auth/httpsign.go | 219 ++ services/auth/impersonate.go | 73 + services/auth/interface.go | 41 + services/auth/main_test.go | 14 + services/auth/oauth2.go | 165 ++ services/auth/oauth2_test.go | 57 + services/auth/reverseproxy.go | 179 ++ services/auth/reverseproxy_test.go | 41 + services/auth/session.go | 68 + services/auth/signin.go | 143 ++ services/auth/source.go | 73 + .../auth/source/db/assert_interface_test.go | 20 + services/auth/source/db/authenticate.go | 87 + services/auth/source/db/source.go | 37 + services/auth/source/ldap/README.md | 131 + .../auth/source/ldap/assert_interface_test.go | 25 + .../auth/source/ldap/security_protocol.go | 31 + services/auth/source/ldap/source.go | 119 + .../auth/source/ldap/source_authenticate.go | 129 + services/auth/source/ldap/source_search.go | 526 ++++ services/auth/source/ldap/source_sync.go | 237 ++ services/auth/source/ldap/util.go | 18 + .../source/oauth2/assert_interface_test.go | 21 + services/auth/source/oauth2/init.go | 79 + services/auth/source/oauth2/main_test.go | 14 + services/auth/source/oauth2/providers.go | 246 ++ services/auth/source/oauth2/providers_base.go | 58 + .../auth/source/oauth2/providers_custom.go | 144 ++ .../auth/source/oauth2/providers_openid.go | 68 + .../auth/source/oauth2/providers_simple.go | 109 + services/auth/source/oauth2/providers_test.go | 62 + services/auth/source/oauth2/source.go | 48 + .../auth/source/oauth2/source_authenticate.go | 19 + services/auth/source/oauth2/source_callout.go | 47 + services/auth/source/oauth2/source_name.go | 18 + .../auth/source/oauth2/source_register.go | 50 + services/auth/source/oauth2/source_sync.go | 105 + .../auth/source/oauth2/source_sync_test.go | 91 + services/auth/source/oauth2/store.go | 97 + services/auth/source/oauth2/urlmapping.go | 77 + .../auth/source/pam/assert_interface_test.go | 20 + services/auth/source/pam/source.go | 38 + .../auth/source/pam/source_authenticate.go | 74 + .../auth/source/smtp/assert_interface_test.go | 23 + services/auth/source/smtp/auth.go | 106 + services/auth/source/smtp/source.go | 59 + .../auth/source/smtp/source_authenticate.go | 91 + services/auth/source/source_group_sync.go | 142 ++ .../auth/source/source_group_sync_test.go | 72 + .../auth/source/sspi/assert_interface_test.go | 18 + services/auth/source/sspi/source.go | 41 + services/auth/sspi.go | 218 ++ services/auth/sspiauth_posix.go | 30 + services/auth/sspiauth_windows.go | 19 + services/auth/sync.go | 43 + services/automerge/automerge.go | 248 ++ services/automerge/notify.go | 64 + services/automergequeue/automergequeue.go | 52 + services/context/access_log.go | 127 + services/context/access_log_test.go | 76 + services/context/api.go | 336 +++ services/context/api_org.go | 12 + services/context/api_test.go | 50 + services/context/base.go | 234 ++ services/context/base_form.go | 88 + services/context/base_path.go | 52 + services/context/base_test.go | 47 + services/context/captcha.go | 87 + services/context/context.go | 307 +++ services/context/context_cookie.go | 40 + services/context/context_model.go | 9 + services/context/context_request.go | 32 + services/context/context_response.go | 197 ++ services/context/context_template.go | 188 ++ services/context/context_test.go | 76 + services/context/org.go | 270 ++ services/context/package.go | 180 ++ services/context/package_test.go | 26 + services/context/pagination.go | 108 + services/context/pagination_test.go | 55 + services/context/permission.go | 123 + services/context/private.go | 102 + services/context/repo.go | 1124 +++++++++ services/context/repo_test.go | 28 + services/context/response.go | 104 + services/context/upload/upload.go | 148 ++ services/context/upload/upload_test.go | 194 ++ services/context/user.go | 60 + services/context/utils.go | 38 + services/contexttest/context_tests.go | 208 ++ services/convert/action_test.go | 189 ++ services/convert/activity.go | 52 + services/convert/attachment.go | 54 + services/convert/convert.go | 1054 ++++++++ services/convert/git_commit.go | 225 ++ services/convert/git_commit_test.go | 40 + services/convert/issue.go | 338 +++ services/convert/issue_comment.go | 192 ++ services/convert/issue_test.go | 126 + services/convert/main_test.go | 16 + services/convert/mirror.go | 26 + services/convert/notification.go | 107 + services/convert/notification_test.go | 162 ++ services/convert/package.go | 53 + services/convert/project.go | 197 ++ services/convert/pull.go | 481 ++++ services/convert/pull_review.go | 135 + services/convert/pull_review_test.go | 51 + services/convert/pull_test.go | 54 + services/convert/release.go | 34 + services/convert/release_test.go | 31 + services/convert/repository.go | 277 +++ services/convert/status.go | 63 + services/convert/user.go | 110 + services/convert/user_test.go | 39 + services/convert/utils.go | 46 + services/convert/utils_test.go | 45 + services/convert/wiki.go | 45 + services/cron/cron.go | 137 + services/cron/setting.go | 86 + services/cron/tasks.go | 244 ++ services/cron/tasks_actions.go | 87 + services/cron/tasks_basic.go | 179 ++ services/cron/tasks_extended.go | 256 ++ services/cron/tasks_extended_test.go | 51 + services/cron/tasks_test.go | 71 + services/doctor/dbconsistency.go | 277 +++ services/doctor/dbconsistency_test.go | 50 + services/doctor/doctor.go | 138 ++ services/doctor/lfs.go | 51 + services/doctor/main_test.go | 14 + services/doctor/misc.go | 225 ++ services/doctor/repository.go | 78 + services/doctor/storage.go | 269 ++ services/externalaccount/user.go | 100 + services/feed/feed.go | 164 ++ services/feed/feed_test.go | 220 ++ services/feed/notifier.go | 481 ++++ services/feed/notifier_test.go | 52 + services/forms/admin.go | 68 + services/forms/auth_form.go | 92 + services/forms/org.go | 61 + services/forms/package_form.go | 19 + services/forms/repo_branch_form.go | 21 + services/forms/repo_form.go | 585 +++++ services/forms/repo_form_editor.go | 52 + services/forms/repo_form_test.go | 87 + services/forms/repo_tag_form.go | 14 + services/forms/runner.go | 12 + services/forms/user_form.go | 314 +++ services/forms/user_form_auth_openid.go | 27 + services/forms/user_form_hidden_comments.go | 105 + services/forms/user_form_test.go | 110 + services/git/commit.go | 97 + services/git/compare.go | 112 + services/gitdiff/csv.go | 469 ++++ services/gitdiff/csv_test.go | 222 ++ services/gitdiff/git_diff_tree.go | 249 ++ services/gitdiff/git_diff_tree_test.go | 466 ++++ services/gitdiff/gitdiff.go | 1721 +++++++++++++ services/gitdiff/gitdiff_excerpt.go | 132 + services/gitdiff/gitdiff_excerpt_test.go | 39 + services/gitdiff/gitdiff_parse_test.go | 48 + services/gitdiff/gitdiff_render_test.go | 118 + services/gitdiff/gitdiff_test.go | 1417 +++++++++++ services/gitdiff/highlightdiff.go | 399 +++ services/gitdiff/highlightdiff_test.go | 186 ++ services/gitdiff/main_test.go | 18 + services/gitdiff/submodule.go | 66 + services/gitdiff/submodule_test.go | 233 ++ services/indexer/indexer.go | 20 + services/indexer/notify.go | 170 ++ services/issue/assignee.go | 285 +++ services/issue/assignee_test.go | 99 + services/issue/comments.go | 198 ++ services/issue/commit.go | 232 ++ services/issue/commit_test.go | 356 +++ services/issue/content.go | 36 + services/issue/issue.go | 311 +++ services/issue/issue_test.go | 85 + services/issue/label.go | 87 + services/issue/label_test.go | 61 + services/issue/main_test.go | 17 + services/issue/milestone.go | 80 + services/issue/milestone_test.go | 41 + services/issue/pull.go | 352 +++ services/issue/reaction.go | 50 + services/issue/reaction_test.go | 161 ++ services/issue/review_request.go | 287 +++ services/issue/review_request_test.go | 57 + services/issue/status.go | 59 + services/issue/suggestion.go | 73 + services/issue/suggestion_test.go | 56 + services/issue/template.go | 190 ++ services/lfs/locks.go | 320 +++ services/lfs/server.go | 635 +++++ services/lfs/server_test.go | 124 + services/mailer/icons/status-cancelled.png | Bin 0 -> 682 bytes services/mailer/icons/status-failure.png | Bin 0 -> 1042 bytes services/mailer/icons/status-skipped.png | Bin 0 -> 1433 bytes services/mailer/icons/status-success.png | Bin 0 -> 1028 bytes services/mailer/incoming/incoming.go | 361 +++ services/mailer/incoming/incoming_handler.go | 188 ++ services/mailer/incoming/incoming_test.go | 205 ++ services/mailer/incoming/payload/payload.go | 70 + services/mailer/incoming/reply.go | 137 + services/mailer/mail.go | 216 ++ services/mailer/mail_comment.go | 61 + services/mailer/mail_issue.go | 229 ++ services/mailer/mail_issue_common.go | 321 +++ services/mailer/mail_release.go | 111 + services/mailer/mail_release_test.go | 64 + services/mailer/mail_repo.go | 127 + services/mailer/mail_team_invite.go | 76 + services/mailer/mail_test.go | 606 +++++ services/mailer/mail_user.go | 128 + services/mailer/mail_workflow_run.go | 179 ++ services/mailer/mail_workflow_run_test.go | 124 + services/mailer/mailer.go | 80 + services/mailer/main_test.go | 17 + services/mailer/notify.go | 214 ++ services/mailer/sender/dummy.go | 26 + services/mailer/sender/main_test.go | 14 + services/mailer/sender/message.go | 125 + services/mailer/sender/message_test.go | 122 + services/mailer/sender/sender.go | 31 + services/mailer/sender/sendmail.go | 80 + services/mailer/sender/smtp.go | 171 ++ services/mailer/sender/smtp_auth.go | 68 + services/mailer/sender/smtp_test.go | 30 + services/mailer/token/token.go | 131 + services/markup/main_test.go | 16 + services/markup/renderhelper.go | 34 + services/markup/renderhelper_codepreview.go | 120 + .../markup/renderhelper_codepreview_test.go | 87 + .../markup/renderhelper_issueicontitle.go | 68 + .../renderhelper_issueicontitle_test.go | 68 + services/markup/renderhelper_mention_test.go | 51 + .../GET_%2Fgitea-test%2Fmilestones | 21 + .../GET_%2Fgitea-test%2Ftest | 10 + ...ET_%2Fgitea-test%2Ftest%2Fcommits%2Fmaster | 8 + ...%2Fgitea-test%2Ftest%2Fcommits%2Freadme-mr | 8 + ...2Fgitea-test%2Ftest%2Fmerge_requests%2F100 | 22 + ...arget%2BProject%2522%2Bis%2B%2522test%2522 | 8 + .../GET_%2Fgitea-test%2Ftickets | 41 + .../GET_%2Fgitea-test%2Ftickets%2F1%2Fnotes | 12 + .../GET_%2Fgitea-test%2Ftickets%2F2%2Fnotes | 19 + .../GET_%2Fgitea-test%2Ftickets%2Ftypes | 21 + .../TestCodebaseDownloadRepo/GET_%2Fusers | 12 + .../GET_%2Fapi%2Fv3%2Frate_limit | 22 + ...%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo | 24 + ...%2F1%2Freactions%3Fpage%3D1%26per_page%3D2 | 23 + ...%2F1%2Freactions%3Fpage%3D2%26per_page%3D2 | 25 + ...on%3Dasc%26per_page%3D100%26sort%3Dcreated | 23 + ...%2F2%2Freactions%3Fpage%3D1%26per_page%3D2 | 24 + ...%2F2%2Freactions%3Fpage%3D2%26per_page%3D2 | 24 + ...%2F2%2Freactions%3Fpage%3D3%26per_page%3D2 | 24 + ...%2F2%2Freactions%3Fpage%3D4%26per_page%3D2 | 25 + ...%2F3%2Freactions%3Fpage%3D1%26per_page%3D2 | 24 + ...%2F4%2Freactions%3Fpage%3D1%26per_page%3D2 | 23 + ...%2F4%2Freactions%3Fpage%3D2%26per_page%3D2 | 25 + ...66%2Freactions%3Fpage%3D1%26per_page%3D100 | 23 + ...66%2Freactions%3Fpage%3D2%26per_page%3D100 | 25 + ...56%2Freactions%3Fpage%3D1%26per_page%3D100 | 24 + ...er_page%3D2%26sort%3Dcreated%26state%3Dall | 24 + ..._repo%2Flabels%3Fpage%3D1%26per_page%3D100 | 23 + ...%3Fpage%3D1%26per_page%3D100%26state%3Dall | 23 + ...est_repo%2Fpulls%2F3%2Frequested_reviewers | 23 + ...ws%2F315859956%2Fcomments%3Fper_page%3D100 | 24 + ...ws%2F315860062%2Fcomments%3Fper_page%3D100 | 24 + ...ws%2F315861440%2Fcomments%3Fper_page%3D100 | 24 + ...epo%2Fpulls%2F3%2Freviews%3Fper_page%3D100 | 23 + ...est_repo%2Fpulls%2F4%2Frequested_reviewers | 23 + ...ws%2F338338740%2Fcomments%3Fper_page%3D100 | 23 + ...ws%2F338339651%2Fcomments%3Fper_page%3D100 | 24 + ...ws%2F338349019%2Fcomments%3Fper_page%3D100 | 23 + ...epo%2Fpulls%2F4%2Freviews%3Fper_page%3D100 | 23 + ...88%2Freactions%3Fpage%3D1%26per_page%3D100 | 24 + ...44%2Freactions%3Fpage%3D1%26per_page%3D100 | 24 + ...er_page%3D2%26sort%3Dcreated%26state%3Dall | 23 + ...epo%2Freleases%3Fpage%3D1%26per_page%3D100 | 23 + ...ET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo | 7 + ...es%2F1%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...s%2F10%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...s%2F11%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...s%2F12%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...s%2F13%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...es%2F2%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...es%2F3%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...ues%2F4%2Fcomments%3Flimit%3D50%26page%3D1 | 10 + ...es%2F4%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...es%2F5%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...es%2F6%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...es%2F7%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...es%2F8%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...es%2F9%2Freactions%3Flimit%3D50%26page%3D1 | 10 + ...o%2Fissues%2Fcomments%2F116550%2Freactions | 8 + ...o%2Fissues%2Fcomments%2F116552%2Freactions | 8 + ...2%26page%3D3%26state%3Dall%26type%3Dissues | 10 + ...0%26page%3D1%26state%3Dall%26type%3Dissues | 9 + ...test_repo%2Flabels%3Flimit%3D50%26page%3D1 | 10 + ...ones%3Flimit%3D50%26page%3D1%26state%3Dall | 10 + ...po%2Fpulls%2F7%2Freviews%2F1770%2Fcomments | 8 + ...po%2Fpulls%2F7%2Freviews%2F1771%2Fcomments | 8 + ...po%2Fpulls%2F7%2Freviews%2F1772%2Fcomments | 8 + ...ulls%2F7%2Freviews%3Flimit%3D50%26page%3D1 | 9 + ...pulls%3Flimit%3D3%26page%3D1%26state%3Dall | 10 + ...ulls%3Flimit%3D50%26page%3D1%26state%3Dall | 9 + ...st_repo%2Freleases%3Flimit%3D50%26page%3D1 | 9 + ...Ftest_repo%2Ftopics%3Flimit%3D0%26page%3D1 | 10 + .../GET_%2Fapi%2Fv1%2Fsettings%2Fapi | 8 + .../GET_%2Fapi%2Fv1%2Fversion | 8 + .../GET_%2Fapi%2Fv4%2Fprojects%2F15578026 | 21 + ...F1%2Faward_emoji%3Fpage%3D1%26per_page%3D2 | 28 + ...F1%2Faward_emoji%3Fpage%3D2%26per_page%3D2 | 30 + ...F2%2Faward_emoji%3Fpage%3D1%26per_page%3D2 | 28 + ...F2%2Faward_emoji%3Fpage%3D2%26per_page%3D2 | 28 + ...F2%2Faward_emoji%3Fpage%3D3%26per_page%3D2 | 28 + ...F2%2Faward_emoji%3Fpage%3D4%26per_page%3D2 | 30 + ...%2Fdiscussions%3Fpage%3D1%26per_page%3D100 | 28 + ...e_state_events%3Fpage%3D1%26per_page%3D100 | 30 + ...%26per_page%3D2%26sort%3Dasc%26state%3Dall | 28 + ...78026%2Flabels%3Fpage%3D1%26per_page%3D100 | 28 + ...F15578026%2Fmerge_requests%2F1%2Fapprovals | 21 + ...F15578026%2Fmerge_requests%2F2%2Fapprovals | 21 + ...2Fprojects%2F15578026%2Fmerge_requests%2F3 | 21 + ...F3%2Faward_emoji%3Fpage%3D1%26per_page%3D2 | 28 + ...2Fprojects%2F15578026%2Fmerge_requests%2F4 | 21 + ...F4%2Faward_emoji%3Fpage%3D1%26per_page%3D2 | 30 + ...%3Fpage%3D1%26per_page%3D2%26view%3Dsimple | 28 + ...%3Fpage%3D1%26per_page%3D100%26state%3Dall | 28 + ...026%2Freleases%3Fpage%3D1%26per_page%3D100 | 28 + ...Fapi%2Fv4%2Fprojects%2Fgitea%252Ftest_repo | 21 + .../GET_%2Fapi%2Fv4%2Fversion | 21 + ...%2Fapi%2Fv1%2Frepos%2Flunnytest%2FTESTREPO | 3 + ...nnytest%2FTESTREPO%2Fissues%2F1%2Fcomments | 3 + ...ESTREPO%2Fissues%3Fpage%3D1%26state%3Dopen | 3 + ...v1%2Frepos%2Flunnytest%2FTESTREPO%2Flabels | 3 + ...Frepos%2Flunnytest%2FTESTREPO%2Fmilestones | 3 + .../GET_%2F~api%2Fissues%2F397%2Fiterations | 3 + .../GET_%2F~api%2Fissues%2F398%2Fchanges | 3 + .../GET_%2F~api%2Fissues%2F398%2Fcomments | 3 + .../GET_%2F~api%2Fissues%2F398%2Fiterations | 3 + ...o-gitea-test_repo%2522%26withFields%3Dtrue | 3 + ...49%2Fiterations%3Fcount%3D100%26offset%3D0 | 3 + ...%2Fiterations%3Fcount%3D100%26offset%3D100 | 3 + ...h%2522%2Bis%2B%2522go-gitea-test_repo%2522 | 3 + .../GET_%2F~api%2Fpulls%2F186%2Fmerge-preview | 3 + .../GET_%2F~api%2Fpulls%2F186%2Freviews | 3 + ...t%2522%2Bis%2B%2522go-gitea-test_repo%2522 | 3 + .../GET_%2F~api%2Fusers%2F317 | 3 + ...ET_%2F~api%2Fusers%2F317%2Femail-addresses | 3 + .../GET_%2F~api%2Fusers%2F336 | 3 + ...ET_%2F~api%2Fusers%2F336%2Femail-addresses | 3 + .../GET_%2F~api%2Fversion%2Fserver | 3 + services/migrations/codebase.go | 653 +++++ services/migrations/codebase_test.go | 144 ++ services/migrations/codecommit.go | 266 ++ services/migrations/common.go | 86 + services/migrations/dump.go | 756 ++++++ services/migrations/error.go | 38 + services/migrations/git.go | 44 + services/migrations/gitbucket.go | 93 + services/migrations/gitea_downloader.go | 715 ++++++ services/migrations/gitea_downloader_test.go | 311 +++ services/migrations/gitea_uploader.go | 1028 ++++++++ services/migrations/gitea_uploader_test.go | 224 ++ services/migrations/github.go | 910 +++++++ services/migrations/github_test.go | 498 ++++ services/migrations/gitlab.go | 779 ++++++ services/migrations/gitlab_test.go | 653 +++++ services/migrations/gogs.go | 312 +++ services/migrations/gogs_test.go | 135 + services/migrations/http_client.go | 42 + services/migrations/main_test.go | 261 ++ services/migrations/migrate.go | 537 ++++ services/migrations/migrate_test.go | 144 ++ services/migrations/onedev.go | 695 ++++++ services/migrations/onedev_test.go | 172 ++ services/migrations/restore.go | 269 ++ services/migrations/restore_test.go | 74 + services/migrations/update.go | 77 + services/mirror/mirror.go | 124 + services/mirror/mirror_pull.go | 481 ++++ services/mirror/mirror_pull_test.go | 38 + services/mirror/mirror_push.go | 263 ++ services/mirror/notifier.go | 31 + services/mirror/queue.go | 77 + services/notifications/status.go | 60 + services/notify/notifier.go | 89 + services/notify/notify.go | 432 ++++ services/notify/null.go | 227 ++ services/oauth2_provider/access_token.go | 276 +++ .../oauth2_provider/additional_scopes_test.go | 38 + services/oauth2_provider/init.go | 19 + services/oauth2_provider/jwtsigningkey.go | 411 +++ .../oauth2_provider/jwtsigningkey_test.go | 62 + services/oauth2_provider/token.go | 94 + services/org/org.go | 210 ++ services/org/org_test.go | 90 + services/org/team.go | 395 +++ services/org/team_invite.go | 22 + services/org/team_test.go | 330 +++ services/org/user.go | 105 + services/org/user_test.go | 93 + services/packages/alpine/repository.go | 374 +++ services/packages/arch/repository.go | 425 ++++ services/packages/arch/repository_test.go | 17 + services/packages/arch/vercmp.go | 105 + services/packages/arch/vercmp_test.go | 27 + services/packages/auth.go | 88 + services/packages/cargo/index.go | 319 +++ services/packages/cleanup/cleanup.go | 211 ++ services/packages/container/blob_uploader.go | 142 ++ services/packages/container/cleanup.go | 108 + services/packages/container/common.go | 64 + services/packages/debian/repository.go | 422 ++++ services/packages/debian/spec.go | 87 + services/packages/package_update.go | 80 + services/packages/packages.go | 745 ++++++ services/packages/packages_test.go | 117 + services/packages/pkgspec/manager.go | 19 + services/packages/rpm/repository.go | 736 ++++++ services/packages/rpm/sign.go | 39 + services/packages/spec.go | 73 + services/packages/terraform/spec.go | 87 + services/packages/terraform/state.go | 44 + services/projects/issue.go | 291 +++ services/projects/issue_test.go | 256 ++ services/projects/main_test.go | 17 + services/projects/project.go | 38 + services/pubsub/broker.go | 39 + services/pubsub/broker_test.go | 193 ++ services/pubsub/init.go | 29 + services/pubsub/memory.go | 71 + services/pubsub/memory_test.go | 99 + services/pubsub/redis.go | 192 ++ services/pubsub/redis_test.go | 73 + services/pull/check.go | 529 ++++ services/pull/check_test.go | 147 ++ services/pull/comment.go | 169 ++ services/pull/comment_test.go | 123 + services/pull/commit_status.go | 201 ++ services/pull/commit_status_test.go | 166 ++ services/pull/edits.go | 40 + services/pull/lfs.go | 138 ++ services/pull/main_test.go | 17 + services/pull/merge.go | 786 ++++++ services/pull/merge_ff_only.go | 21 + services/pull/merge_merge.go | 25 + services/pull/merge_prepare.go | 306 +++ services/pull/merge_rebase.go | 122 + services/pull/merge_squash.go | 81 + services/pull/merge_test.go | 139 ++ services/pull/merge_tree.go | 143 ++ services/pull/merge_tree_test.go | 155 ++ services/pull/patch.go | 447 ++++ services/pull/patch_unmerged.go | 186 ++ services/pull/protected_branch.go | 47 + services/pull/pull.go | 1095 ++++++++ services/pull/pull_test.go | 118 + services/pull/ref.go | 55 + services/pull/ref_test.go | 22 + services/pull/review.go | 486 ++++ services/pull/review_test.go | 47 + services/pull/reviewer.go | 82 + services/pull/reviewer_test.go | 71 + services/pull/temp_repo.go | 162 ++ services/pull/update.go | 220 ++ services/pull/update_rebase.go | 106 + services/pull/update_test.go | 195 ++ services/release/notes.go | 215 ++ services/release/notes_test.go | 133 + services/release/release.go | 440 ++++ services/release/release_test.go | 435 ++++ services/release/tag.go | 61 + services/repository/adopt.go | 377 +++ services/repository/adopt_test.go | 130 + services/repository/archiver/archiver.go | 404 +++ services/repository/archiver/archiver_test.go | 156 ++ services/repository/avatar.go | 106 + services/repository/avatar_test.go | 70 + services/repository/branch.go | 900 +++++++ services/repository/cache.go | 19 + services/repository/check.go | 200 ++ services/repository/collaboration.go | 161 ++ services/repository/collaboration_test.go | 120 + services/repository/commit.go | 55 + .../repository/commitstatus/commitstatus.go | 205 ++ services/repository/contributors_graph.go | 296 +++ .../repository/contributors_graph_test.go | 85 + services/repository/create.go | 472 ++++ services/repository/create_test.go | 63 + services/repository/delete.go | 416 ++++ services/repository/delete_test.go | 93 + services/repository/files/cherry_pick.go | 78 + services/repository/files/commit.go | 35 + services/repository/files/content.go | 276 +++ services/repository/files/content_test.go | 48 + services/repository/files/diff.go | 42 + services/repository/files/diff_test.go | 155 ++ services/repository/files/file.go | 155 ++ services/repository/files/file_test.go | 27 + services/repository/files/patch.go | 233 ++ services/repository/files/patch_test.go | 38 + services/repository/files/temp_repo.go | 392 +++ services/repository/files/temp_repo_test.go | 45 + services/repository/files/tree.go | 191 ++ services/repository/files/tree_test.go | 121 + services/repository/files/update.go | 700 ++++++ services/repository/files/upload.go | 111 + services/repository/fork.go | 267 ++ services/repository/fork_test.go | 98 + services/repository/generate.go | 345 +++ services/repository/generate_test.go | 294 +++ services/repository/gitgraph/graph.go | 105 + services/repository/gitgraph/graph_models.go | 279 +++ services/repository/gitgraph/graph_test.go | 695 ++++++ services/repository/gitgraph/parser.go | 336 +++ services/repository/hooks.go | 110 + services/repository/init.go | 80 + services/repository/lfs.go | 136 + services/repository/lfs_test.go | 91 + services/repository/license.go | 229 ++ services/repository/license_test.go | 222 ++ services/repository/main_test.go | 14 + services/repository/merge_upstream.go | 140 ++ services/repository/migrate.go | 288 +++ services/repository/push.go | 415 ++++ services/repository/repo_team.go | 255 ++ services/repository/repo_team_test.go | 33 + services/repository/repository.go | 363 +++ services/repository/repository_test.go | 115 + services/repository/setting.go | 52 + services/repository/template.go | 194 ++ services/repository/transfer.go | 610 +++++ services/repository/transfer_test.go | 214 ++ services/secrets/secrets.go | 79 + services/secrets/validation.go | 37 + services/secrets/validation_test.go | 35 + services/task/migrate.go | 158 ++ services/task/task.go | 159 ++ services/uinotification/notify.go | 267 ++ services/user/avatar.go | 69 + services/user/block.go | 302 +++ services/user/block_test.go | 65 + services/user/delete.go | 206 ++ services/user/email.go | 160 ++ services/user/email_test.go | 76 + services/user/update.go | 281 +++ services/user/update_test.go | 155 ++ services/user/user.go | 334 +++ services/user/user_test.go | 274 ++ services/versioned_migration/migration.go | 23 + services/webhook/deliver.go | 360 +++ services/webhook/deliver_test.go | 346 +++ services/webhook/dingtalk.go | 217 ++ services/webhook/dingtalk_test.go | 251 ++ services/webhook/discord.go | 344 +++ services/webhook/discord_test.go | 318 +++ services/webhook/feishu.go | 224 ++ services/webhook/feishu_test.go | 196 ++ services/webhook/general.go | 429 ++++ services/webhook/general_test.go | 649 +++++ services/webhook/main_test.go | 27 + services/webhook/matrix.go | 305 +++ services/webhook/matrix_test.go | 258 ++ services/webhook/msteams.go | 415 ++++ services/webhook/msteams_test.go | 480 ++++ services/webhook/notifier.go | 1071 ++++++++ services/webhook/packagist.go | 138 ++ services/webhook/packagist_test.go | 214 ++ services/webhook/payloader.go | 122 + services/webhook/slack.go | 327 +++ services/webhook/slack_test.go | 199 ++ services/webhook/telegram.go | 226 ++ services/webhook/telegram_test.go | 212 ++ services/webhook/webhook.go | 264 ++ services/webhook/webhook_test.go | 169 ++ services/webhook/wechatwork.go | 206 ++ services/websocket/events.go | 53 + services/websocket/logout_publisher.go | 12 + services/websocket/notification_notifier.go | 39 + services/websocket/notifier.go | 18 + services/websocket/stopwatch_notifier.go | 37 + services/webtheme/webtheme.go | 274 ++ services/webtheme/webtheme_test.go | 43 + services/wiki/wiki.go | 413 ++++ services/wiki/wiki_path.go | 172 ++ services/wiki/wiki_test.go | 328 +++ snap/part-gitea-build.sh | 21 + snap/part-gitea-pull.sh | 62 + snap/snapcraft.yaml | 66 + stylelint.config.ts | 145 ++ tailwind.config.ts | 120 + templates/admin/actions.tmpl | 13 + templates/admin/applications/list.tmpl | 8 + templates/admin/applications/oauth2_edit.tmpl | 6 + templates/admin/audit/list.tmpl | 16 + templates/admin/auth/edit.tmpl | 451 ++++ templates/admin/auth/list.tmpl | 40 + templates/admin/auth/new.tmpl | 150 ++ templates/admin/auth/source/ldap.tmpl | 147 ++ templates/admin/auth/source/oauth.tmpl | 126 + templates/admin/auth/source/smtp.tmpl | 59 + templates/admin/auth/source/sspi.tmpl | 43 + templates/admin/badge/edit.tmpl | 44 + templates/admin/badge/list.tmpl | 67 + templates/admin/badge/new.tmpl | 26 + templates/admin/badge/users.tmpl | 40 + templates/admin/badge/view.tmpl | 44 + templates/admin/config.tmpl | 329 +++ templates/admin/config_settings/avatars.tmpl | 24 + .../config_settings/config_settings.tmpl | 7 + templates/admin/config_settings/instance.tmpl | 63 + .../admin/config_settings/repository.tmpl | 27 + templates/admin/cron.tmpl | 38 + templates/admin/dashboard.tmpl | 82 + templates/admin/emails/list.tmpl | 101 + templates/admin/hook_new.tmpl | 13 + templates/admin/hooks.tmpl | 6 + templates/admin/layout_footer.tmpl | 11 + templates/admin/layout_head.tmpl | 13 + templates/admin/navbar.tmpl | 125 + templates/admin/notice.tmpl | 69 + templates/admin/org/list.tmpl | 81 + templates/admin/packages/list.tmpl | 94 + templates/admin/perftrace.tmpl | 13 + templates/admin/queue.tmpl | 36 + templates/admin/queue_manage.tmpl | 59 + templates/admin/repo/list.tmpl | 109 + templates/admin/repo/unadopted.tmpl | 73 + templates/admin/runners/edit.tmpl | 5 + templates/admin/self_check.tmpl | 60 + templates/admin/shared/owner.tmpl | 5 + templates/admin/stacktrace-row.tmpl | 69 + templates/admin/stacktrace.tmpl | 22 + templates/admin/stats.tmpl | 17 + templates/admin/system_status.tmpl | 62 + templates/admin/trace_tabs.tmpl | 19 + templates/admin/user/edit.tmpl | 225 ++ templates/admin/user/list.tmpl | 121 + templates/admin/user/new.tmpl | 89 + templates/admin/user/view.tmpl | 36 + templates/admin/user/view_details.tmpl | 65 + templates/admin/user/view_emails.tmpl | 19 + templates/admin/user/view_orgs.tmpl | 53 + templates/api/packages/pypi/simple.tmpl | 16 + templates/base/alert.tmpl | 9 + templates/base/alert_details.tmpl | 9 + templates/base/disable_form_autofill.tmpl | 31 + templates/base/footer.tmpl | 19 + templates/base/footer_content.tmpl | 41 + templates/base/head.tmpl | 40 + templates/base/head_banner.tmpl | 11 + templates/base/head_impersonate_banner.tmpl | 10 + templates/base/head_navbar.tmpl | 182 ++ templates/base/head_navbar_icons.tmpl | 19 + templates/base/head_opengraph.tmpl | 46 + templates/base/head_script.tmpl | 11 + templates/base/head_style.tmpl | 2 + templates/base/markup_codepreview.tmpl | 22 + templates/base/modal_actions_confirm.tmpl | 26 + templates/base/paginate.tmpl | 46 + templates/custom/body_inner_post.tmpl | 0 templates/custom/body_inner_pre.tmpl | 0 templates/custom/body_outer_post.tmpl | 0 templates/custom/body_outer_pre.tmpl | 0 templates/custom/extra_links.tmpl | 0 templates/custom/extra_links_footer.tmpl | 0 templates/custom/extra_tabs.tmpl | 0 templates/custom/footer.tmpl | 0 templates/custom/header.tmpl | 0 templates/devtest/ansi-render.tmpl | 7 + templates/devtest/avatar-stack.tmpl | 18 + templates/devtest/badge-actions-svg.tmpl | 25 + templates/devtest/badge-commit-sign.tmpl | 13 + templates/devtest/devtest-footer.tmpl | 1 + templates/devtest/devtest-header.tmpl | 4 + templates/devtest/devtest-list.tmpl | 13 + templates/devtest/fetch-action.tmpl | 34 + templates/devtest/flex-list.tmpl | 146 ++ templates/devtest/fomantic-dropdown.tmpl | 130 + templates/devtest/fomantic-modal.tmpl | 95 + templates/devtest/form-fields.tmpl | 109 + templates/devtest/gitea-ui.tmpl | 158 ++ templates/devtest/global-button.tmpl | 16 + templates/devtest/keyboard-shortcut.tmpl | 23 + templates/devtest/label.tmpl | 26 + templates/devtest/mail-preview.tmpl | 30 + templates/devtest/markup-render.tmpl | 71 + templates/devtest/relative-time.tmpl | 67 + templates/devtest/repo-action-view.tmpl | 15 + templates/devtest/severity-colors.tmpl | 115 + templates/devtest/tmplerr-sub.tmpl | 3 + templates/devtest/tmplerr.tmpl | 12 + templates/devtest/toast-and-message.tmpl | 16 + templates/devtest/unicode-escape.tmpl | 17 + templates/explore/code.tmpl | 8 + templates/explore/navbar.tmpl | 22 + templates/explore/repos.tmpl | 11 + templates/explore/search.tmpl | 23 + templates/explore/user_list.tmpl | 33 + templates/explore/users.tmpl | 10 + templates/home.tmpl | 51 + templates/install.tmpl | 336 +++ templates/mail/base/footer.tmpl | 10 + templates/mail/base/head.tmpl | 17 + templates/mail/org/team_invite.devtest.yml | 15 + templates/mail/org/team_invite.tmpl | 14 + .../repo/actions/workflow_run.devtest.yml | 34 + templates/mail/repo/actions/workflow_run.tmpl | 42 + templates/mail/repo/collaborator.devtest.yml | 3 + templates/mail/repo/collaborator.tmpl | 11 + .../mail/repo/issue/assigned.devtest.yml | 11 + templates/mail/repo/issue/assigned.tmpl | 20 + templates/mail/repo/issue/default.devtest.yml | 7 + templates/mail/repo/issue/default.tmpl | 75 + templates/mail/repo/release.devtest.yml | 13 + templates/mail/repo/release.tmpl | 34 + templates/mail/repo/transfer.devtest.yml | 3 + templates/mail/repo/transfer.tmpl | 14 + templates/mail/user/auth/activate.devtest.yml | 4 + templates/mail/user/auth/activate.tmpl | 15 + .../mail/user/auth/activate_email.devtest.yml | 5 + templates/mail/user/auth/activate_email.tmpl | 15 + .../user/auth/register_notify.devtest.yml | 3 + templates/mail/user/auth/register_notify.tmpl | 16 + .../mail/user/auth/reset_passwd.devtest.yml | 4 + templates/mail/user/auth/reset_passwd.tmpl | 15 + templates/org/create.tmpl | 52 + templates/org/follow_unfollow.tmpl | 7 + templates/org/header.tmpl | 38 + templates/org/home.tmpl | 101 + templates/org/member/members.tmpl | 86 + templates/org/menu.tmpl | 60 + templates/org/projects/list.tmpl | 24 + templates/org/projects/new.tmpl | 24 + templates/org/projects/view.tmpl | 13 + templates/org/settings/actions.tmpl | 13 + templates/org/settings/actions_general.tmpl | 5 + templates/org/settings/applications.tmpl | 9 + .../settings/applications_oauth2_edit.tmpl | 5 + templates/org/settings/audit_logs.tmpl | 6 + templates/org/settings/blocked_users.tmpl | 5 + templates/org/settings/hook_new.tmpl | 7 + templates/org/settings/hooks.tmpl | 5 + templates/org/settings/labels.tmpl | 13 + templates/org/settings/layout_footer.tmpl | 11 + templates/org/settings/layout_head.tmpl | 14 + templates/org/settings/navbar.tmpl | 54 + templates/org/settings/options.tmpl | 72 + .../org/settings/options_dangerzone.tmpl | 142 ++ templates/org/settings/packages.tmpl | 6 + .../settings/packages_cleanup_rules_edit.tmpl | 5 + .../packages_cleanup_rules_preview.tmpl | 5 + templates/org/settings/runners_edit.tmpl | 5 + templates/org/team/invite.tmpl | 18 + templates/org/team/members.tmpl | 84 + templates/org/team/navbar.tmpl | 4 + templates/org/team/new.tmpl | 207 ++ templates/org/team/repositories.tmpl | 65 + templates/org/team/sidebar.tmpl | 102 + templates/org/team/teams.tmpl | 75 + templates/org/worktime.tmpl | 40 + templates/org/worktime/empty_placeholder.tmpl | 9 + templates/org/worktime/table_members.tmpl | 18 + templates/org/worktime/table_milestones.tmpl | 30 + templates/org/worktime/table_repos.tmpl | 18 + templates/package/content/alpine.tmpl | 52 + templates/package/content/arch.tmpl | 43 + templates/package/content/cargo.tmpl | 63 + templates/package/content/chef.tmpl | 48 + templates/package/content/composer.tmpl | 50 + .../content/composer_dependencies.tmpl | 19 + templates/package/content/conan.tmpl | 34 + templates/package/content/conda.tmpl | 30 + templates/package/content/container.tmpl | 119 + templates/package/content/cran.tmpl | 59 + templates/package/content/debian.tmpl | 63 + templates/package/content/generic.tmpl | 18 + templates/package/content/go.tmpl | 14 + templates/package/content/helm.tmpl | 57 + templates/package/content/maven.tmpl | 75 + templates/package/content/npm.tmpl | 58 + .../package/content/npm_dependencies.tmpl | 19 + templates/package/content/nuget.tmpl | 52 + templates/package/content/pub.tmpl | 19 + templates/package/content/pypi.tmpl | 31 + templates/package/content/rpm.tmpl | 62 + templates/package/content/rubygems.tmpl | 40 + .../content/rubygems_dependencies.tmpl | 19 + templates/package/content/swift.tmpl | 40 + templates/package/content/terraform.tmpl | 26 + templates/package/content/vagrant.tmpl | 18 + templates/package/metadata/alpine.tmpl | 5 + templates/package/metadata/arch.tmpl | 4 + templates/package/metadata/cargo.tmpl | 7 + templates/package/metadata/chef.tmpl | 5 + templates/package/metadata/composer.tmpl | 5 + templates/package/metadata/conan.tmpl | 6 + templates/package/metadata/conda.tmpl | 6 + templates/package/metadata/container.tmpl | 9 + templates/package/metadata/cran.tmpl | 5 + templates/package/metadata/debian.tmpl | 4 + templates/package/metadata/generic.tmpl | 0 templates/package/metadata/helm.tmpl | 4 + templates/package/metadata/maven.tmpl | 8 + templates/package/metadata/npm.tmpl | 8 + templates/package/metadata/nuget.tmpl | 5 + templates/package/metadata/pub.tmpl | 5 + templates/package/metadata/pypi.tmpl | 5 + templates/package/metadata/rpm.tmpl | 4 + templates/package/metadata/rubygems.tmpl | 5 + templates/package/metadata/swift.tmpl | 4 + templates/package/metadata/terraform.tmpl | 41 + templates/package/metadata/vagrant.tmpl | 5 + templates/package/settings.tmpl | 87 + templates/package/shared/cargo.tmpl | 22 + .../package/shared/cleanup_rules/edit.tmpl | 72 + .../package/shared/cleanup_rules/list.tmpl | 56 + .../package/shared/cleanup_rules/preview.tmpl | 39 + templates/package/shared/list.tmpl | 60 + templates/package/shared/versionlist.tmpl | 43 + templates/package/shared/view.tmpl | 131 + .../package/shared/visibility_badge.tmpl | 7 + templates/package/view.tmpl | 26 + templates/post-install.tmpl | 11 + templates/projects/list.tmpl | 102 + templates/projects/new.tmpl | 73 + templates/projects/view.tmpl | 159 ++ templates/repo/actions/list.tmpl | 215 ++ templates/repo/actions/no_workflows.tmpl | 8 + templates/repo/actions/runs_list.tmpl | 77 + templates/repo/actions/view.tmpl | 11 + templates/repo/actions/view_component.tmpl | 62 + templates/repo/actions/workflow_dispatch.tmpl | 58 + .../actions/workflow_dispatch_inputs.tmpl | 46 + templates/repo/activity.tmpl | 17 + templates/repo/blame.tmpl | 88 + templates/repo/branch/list.tmpl | 268 ++ templates/repo/branch_dropdown.tmpl | 67 + templates/repo/cite/cite_buttons.tmpl | 11 + templates/repo/cite/cite_modal.tmpl | 20 + templates/repo/clone_buttons.tmpl | 16 + templates/repo/clone_panel.tmpl | 56 + .../code/recently_pushed_new_branches.tmpl | 18 + .../repo/code/upstream_diverging_info.tmpl | 23 + templates/repo/code_frequency.tmpl | 9 + .../repo/commit_load_branches_and_tags.tmpl | 27 + templates/repo/commit_page.tmpl | 204 ++ templates/repo/commit_sign_badge.tmpl | 81 + templates/repo/commit_statuses.tmpl | 16 + templates/repo/commits.tmpl | 28 + templates/repo/commits_list.tmpl | 80 + templates/repo/commits_list_small.tmpl | 33 + templates/repo/commits_ref_name.tmpl | 9 + templates/repo/commits_search_dropdown.tmpl | 8 + templates/repo/commits_table.tmpl | 45 + templates/repo/contributors.tmpl | 16 + templates/repo/create.tmpl | 218 ++ templates/repo/create_helper.tmpl | 3 + templates/repo/diff/blob_excerpt.tmpl | 91 + templates/repo/diff/box.tmpl | 268 ++ templates/repo/diff/comment_form.tmpl | 47 + .../repo/diff/comment_form_datahandler.tmpl | 7 + templates/repo/diff/comments.tmpl | 79 + templates/repo/diff/compare.tmpl | 227 ++ templates/repo/diff/conversation.tmpl | 70 + .../repo/diff/conversation_outdated.tmpl | 3 + templates/repo/diff/csv_diff.tmpl | 60 + templates/repo/diff/image_diff.tmpl | 87 + templates/repo/diff/new_comment.tmpl | 5 + templates/repo/diff/new_review.tmpl | 57 + templates/repo/diff/options_dropdown.tmpl | 32 + templates/repo/diff/section_code.tmpl | 1 + templates/repo/diff/section_split.tmpl | 137 + templates/repo/diff/section_unified.tmpl | 51 + templates/repo/diff/stats.tmpl | 17 + templates/repo/diff/whitespace_dropdown.tmpl | 30 + templates/repo/editor/cherry_pick.tmpl | 28 + templates/repo/editor/commit_form.tmpl | 86 + templates/repo/editor/common_breadcrumb.tmpl | 16 + templates/repo/editor/common_top.tmpl | 6 + templates/repo/editor/delete.tmpl | 29 + templates/repo/editor/diff_preview.tmpl | 17 + templates/repo/editor/edit.tmpl | 61 + templates/repo/editor/fork.tmpl | 17 + templates/repo/editor/options.tmpl | 32 + templates/repo/editor/patch.tmpl | 45 + templates/repo/editor/upload.tmpl | 24 + templates/repo/empty.tmpl | 80 + templates/repo/file_info.tmpl | 48 + templates/repo/forks.tmpl | 12 + templates/repo/graph.tmpl | 55 + templates/repo/graph/commits.tmpl | 52 + templates/repo/graph/div.tmpl | 3 + templates/repo/graph/svgcontainer.tmpl | 24 + templates/repo/header.tmpl | 208 ++ templates/repo/header/fork.tmpl | 43 + templates/repo/header/star.tmpl | 14 + templates/repo/header/watch.tmpl | 57 + templates/repo/home.tmpl | 32 + templates/repo/home_sidebar_bottom.tmpl | 47 + templates/repo/home_sidebar_top.tmpl | 85 + templates/repo/icon.tmpl | 10 + templates/repo/icons/action_status.tmpl | 30 + templates/repo/icons/commit_status.tmpl | 19 + .../repo/issue/branch_selector_field.tmpl | 64 + templates/repo/issue/card.tmpl | 83 + templates/repo/issue/choose.tmpl | 59 + templates/repo/issue/comment_tab.tmpl | 21 + templates/repo/issue/fields/checkboxes.tmpl | 14 + templates/repo/issue/fields/dropdown.tmpl | 17 + templates/repo/issue/fields/header.tmpl | 6 + templates/repo/issue/fields/input.tmpl | 4 + templates/repo/issue/fields/markdown.tmpl | 3 + templates/repo/issue/fields/textarea.tmpl | 24 + templates/repo/issue/filter_actions.tmpl | 124 + templates/repo/issue/filter_item_label.tmpl | 48 + .../repo/issue/filter_item_milestone.tmpl | 42 + .../repo/issue/filter_item_user_assign.tmpl | 39 + .../repo/issue/filter_item_user_fetch.tmpl | 23 + templates/repo/issue/filter_list.tmpl | 123 + templates/repo/issue/filters.tmpl | 26 + templates/repo/issue/label_precolors.tmpl | 27 + templates/repo/issue/labels.tmpl | 18 + .../repo/issue/labels/label_archived.tmpl | 5 + .../repo/issue/labels/label_edit_modal.tmpl | 69 + templates/repo/issue/labels/label_list.tmpl | 89 + .../issue/labels/label_load_template.tmpl | 20 + templates/repo/issue/list.tmpl | 59 + .../repo/issue/milestone/filter_list.tmpl | 16 + templates/repo/issue/milestone_issues.tmpl | 65 + templates/repo/issue/milestone_new.tmpl | 62 + templates/repo/issue/milestones.tmpl | 102 + templates/repo/issue/navbar.tmpl | 4 + templates/repo/issue/new.tmpl | 8 + templates/repo/issue/new_form.tmpl | 73 + templates/repo/issue/openclose.tmpl | 24 + templates/repo/issue/search.tmpl | 19 + .../issue/sidebar/allow_maintainer_edit.tmpl | 18 + .../repo/issue/sidebar/assignee_list.tmpl | 40 + templates/repo/issue/sidebar/due_date.tmpl | 28 + .../issue/sidebar/issue_dependencies.tmpl | 144 ++ .../repo/issue/sidebar/issue_management.tmpl | 107 + templates/repo/issue/sidebar/label_list.tmpl | 57 + .../repo/issue/sidebar/label_list_item.tmpl | 12 + .../repo/issue/sidebar/milestone_list.tmpl | 54 + .../repo/issue/sidebar/participant_list.tmpl | 11 + .../repo/issue/sidebar/project_list.tmpl | 94 + .../repo/issue/sidebar/reference_link.tmpl | 6 + .../repo/issue/sidebar/reviewer_list.tmpl | 131 + .../issue/sidebar/stopwatch_timetracker.tmpl | 83 + .../issue/sidebar/watch_notification.tmpl | 9 + templates/repo/issue/sidebar/wip_switch.tmpl | 5 + templates/repo/issue/view.tmpl | 12 + templates/repo/issue/view_content.tmpl | 173 ++ .../repo/issue/view_content/add_reaction.tmpl | 10 + .../repo/issue/view_content/attachments.tmpl | 42 + .../repo/issue/view_content/comments.tmpl | 701 ++++++ .../view_content/comments_authorlink.tmpl | 11 + .../view_content/comments_delete_time.tmpl | 14 + .../repo/issue/view_content/context_menu.tmpl | 46 + .../repo/issue/view_content/conversation.tmpl | 148 ++ .../issue/view_content/pull_merge_box.tmpl | 82 + .../view_content/pull_merge_instruction.tmpl | 64 + .../pull_merge_status_checks.tmpl | 34 + .../repo/issue/view_content/reactions.tmpl | 17 + .../view_content/reference_issue_dialog.tmpl | 27 + .../repo/issue/view_content/show_role.tmpl | 10 + .../repo/issue/view_content/sidebar.tmpl | 26 + .../view_content/update_branch_by_merge.tmpl | 24 + .../repo/issue/view_content/watching.tmpl | 7 + templates/repo/issue/view_title.tmpl | 146 ++ templates/repo/latest_commit.tmpl | 20 + templates/repo/migrate/codebase.tmpl | 115 + templates/repo/migrate/codecommit.tmpl | 116 + templates/repo/migrate/git.tmpl | 89 + templates/repo/migrate/gitbucket.tmpl | 131 + templates/repo/migrate/gitea.tmpl | 127 + templates/repo/migrate/github.tmpl | 129 + templates/repo/migrate/gitlab.tmpl | 126 + templates/repo/migrate/gogs.tmpl | 129 + templates/repo/migrate/helper.tmpl | 0 templates/repo/migrate/migrate.tmpl | 32 + templates/repo/migrate/migrating.tmpl | 84 + templates/repo/migrate/onedev.tmpl | 116 + templates/repo/migrate/options.tmpl | 26 + templates/repo/navbar.tmpl | 19 + templates/repo/packages.tmpl | 8 + templates/repo/projects/list.tmpl | 8 + templates/repo/projects/new.tmpl | 8 + templates/repo/projects/view.tmpl | 14 + templates/repo/pulls/commits.tmpl | 10 + templates/repo/pulls/files.tmpl | 10 + templates/repo/pulls/fork.tmpl | 85 + templates/repo/pulls/status_items.tmpl | 38 + templates/repo/pulls/tab_menu.tmpl | 23 + templates/repo/pulse.tmpl | 231 ++ templates/repo/recent_commits.tmpl | 9 + templates/repo/release/label.tmpl | 13 + templates/repo/release/list.tmpl | 132 + templates/repo/release/new.tmpl | 161 ++ templates/repo/release_tag_header.tmpl | 29 + templates/repo/search.tmpl | 8 + templates/repo/search_name.tmpl | 1 + templates/repo/settings/actions.tmpl | 13 + templates/repo/settings/actions_general.tmpl | 100 + templates/repo/settings/audit_logs.tmpl | 6 + templates/repo/settings/branches.tmpl | 74 + templates/repo/settings/collaboration.tmpl | 127 + .../settings/dangerous_operation_form.tmpl | 22 + templates/repo/settings/deploy_key_list.tmpl | 44 + templates/repo/settings/deploy_keys.tmpl | 75 + templates/repo/settings/githook_edit.tmpl | 26 + templates/repo/settings/githooks.tmpl | 20 + templates/repo/settings/layout_footer.tmpl | 11 + templates/repo/settings/layout_head.tmpl | 14 + templates/repo/settings/lfs.tmpl | 48 + templates/repo/settings/lfs_file.tmpl | 44 + templates/repo/settings/lfs_file_find.tmpl | 46 + templates/repo/settings/lfs_locks.tmpl | 54 + templates/repo/settings/lfs_pointers.tmpl | 56 + templates/repo/settings/navbar.tmpl | 64 + templates/repo/settings/options.tmpl | 1086 ++++++++ templates/repo/settings/protected_branch.tmpl | 394 +++ templates/repo/settings/public_access.tmpl | 53 + .../repo/settings/push_mirror_sync_modal.tmpl | 27 + templates/repo/settings/runner_edit.tmpl | 5 + templates/repo/settings/secrets.tmpl | 5 + templates/repo/settings/tags.tmpl | 124 + templates/repo/settings/webhook/base.tmpl | 5 + .../repo/settings/webhook/base_list.tmpl | 29 + templates/repo/settings/webhook/dingtalk.tmpl | 11 + templates/repo/settings/webhook/discord.tmpl | 19 + templates/repo/settings/webhook/feishu.tmpl | 13 + templates/repo/settings/webhook/gitea.tmpl | 40 + templates/repo/settings/webhook/gogs.tmpl | 28 + templates/repo/settings/webhook/history.tmpl | 87 + .../repo/settings/webhook/link_menu.tmpl | 50 + templates/repo/settings/webhook/matrix.tmpl | 26 + templates/repo/settings/webhook/msteams.tmpl | 11 + templates/repo/settings/webhook/new.tmpl | 7 + .../repo/settings/webhook/packagist.tmpl | 19 + templates/repo/settings/webhook/settings.tmpl | 362 +++ templates/repo/settings/webhook/slack.tmpl | 28 + templates/repo/settings/webhook/telegram.tmpl | 19 + .../repo/settings/webhook/wechatwork.tmpl | 11 + templates/repo/sub_menu.tmpl | 19 + templates/repo/tag/list.tmpl | 81 + templates/repo/tag/name.tmpl | 3 + templates/repo/unicode_escape_prompt.tmpl | 22 + templates/repo/upload.tmpl | 19 + templates/repo/user_cards.tmpl | 30 + templates/repo/view.tmpl | 27 + templates/repo/view_content.tmpl | 140 ++ templates/repo/view_file.tmpl | 151 ++ templates/repo/view_file_tree.tmpl | 17 + .../repo/view_file_tree_toggle_button.tmpl | 6 + templates/repo/view_list.tmpl | 67 + templates/repo/watch_options_modal.tmpl | 22 + templates/repo/watchers.tmpl | 8 + templates/repo/wiki/new.tmpl | 43 + templates/repo/wiki/pages.tmpl | 31 + templates/repo/wiki/revision.tmpl | 37 + templates/repo/wiki/start.tmpl | 15 + templates/repo/wiki/view.tmpl | 105 + .../actions/owner_general_settings.tmpl | 58 + .../actions/permission_mode_select.tmpl | 18 + .../shared/actions/permissions_table.tmpl | 77 + .../actions/permissions_table_unit.tmpl | 24 + .../actions/runner_badge_flat-square.tmpl | 15 + .../shared/actions/runner_badge_flat.tmpl | 27 + templates/shared/actions/runner_edit.tmpl | 108 + templates/shared/actions/runner_list.tmpl | 117 + .../shared/actions/scoped_workflows.tmpl | 88 + templates/shared/audit/list.tmpl | 89 + templates/shared/avatar_upload_crop.tmpl | 8 + templates/shared/combomarkdowneditor.tmpl | 96 + templates/shared/fileisempty.tmpl | 3 + templates/shared/filetoolarge.tmpl | 4 + templates/shared/issueicon.tmpl | 26 + templates/shared/issuelist.tmpl | 175 ++ templates/shared/misc/tabtitle.tmpl | 1 + templates/shared/repo/list.tmpl | 91 + templates/shared/repo/search.tmpl | 63 + templates/shared/search/button.tmpl | 3 + templates/shared/search/code/results.tmpl | 36 + templates/shared/search/code/search.tmpl | 35 + templates/shared/search/combo.tmpl | 29 + templates/shared/search/input.tmpl | 4 + templates/shared/searchbottom.tmpl | 14 + templates/shared/searchfile.tmpl | 14 + templates/shared/secrets/add_list.tmpl | 108 + templates/shared/user/authorlink.tmpl | 1 + templates/shared/user/avatarlink.tmpl | 1 + templates/shared/user/block_user_dialog.tmpl | 22 + templates/shared/user/blocked_users.tmpl | 72 + templates/shared/user/name.tmpl | 1 + templates/shared/user/namelink.tmpl | 1 + templates/shared/user/org_profile_avatar.tmpl | 16 + templates/shared/user/profile_big_avatar.tmpl | 152 ++ templates/shared/variables/variable_list.tmpl | 107 + templates/shared/webhook/icon.tmpl | 27 + templates/status/404.tmpl | 17 + templates/status/500.tmpl | 69 + templates/status/503.tmpl | 12 + templates/swagger/openapi-viewer.tmpl | 21 + templates/swagger/v1-input.json | 17 + templates/user/auth/activate.tmpl | 39 + templates/user/auth/activate_prompt.tmpl | 15 + templates/user/auth/captcha.tmpl | 32 + templates/user/auth/change_passwd.tmpl | 7 + templates/user/auth/change_passwd_inner.tmpl | 21 + .../user/auth/external_auth_methods.tmpl | 17 + templates/user/auth/forgot_passwd.tmpl | 38 + templates/user/auth/grant.tmpl | 33 + templates/user/auth/grant_error.tmpl | 13 + templates/user/auth/link_account.tmpl | 34 + templates/user/auth/prohibit_login.tmpl | 16 + templates/user/auth/reset_passwd.tmpl | 64 + templates/user/auth/signin.tmpl | 10 + templates/user/auth/signin_inner.tmpl | 76 + templates/user/auth/signin_openid.tmpl | 50 + templates/user/auth/signup.tmpl | 9 + templates/user/auth/signup_inner.tmpl | 70 + .../user/auth/signup_openid_connect.tmpl | 35 + templates/user/auth/signup_openid_navbar.tmpl | 12 + .../user/auth/signup_openid_register.tmpl | 36 + templates/user/auth/twofa.tmpl | 25 + templates/user/auth/twofa_scratch.tmpl | 24 + templates/user/auth/webauthn.tmpl | 25 + templates/user/auth/webauthn_error.tmpl | 13 + templates/user/code.tmpl | 24 + templates/user/dashboard/dashboard.tmpl | 17 + templates/user/dashboard/feeds.tmpl | 130 + templates/user/dashboard/guide.tmpl | 10 + templates/user/dashboard/issues.tmpl | 114 + templates/user/dashboard/milestones.tmpl | 144 ++ templates/user/dashboard/navbar.tmpl | 107 + templates/user/dashboard/repolist.tmpl | 60 + templates/user/heatmap.tmpl | 13 + templates/user/notification/notification.tmpl | 3 + .../user/notification/notification_div.tmpl | 95 + .../notification_subscriptions.tmpl | 78 + templates/user/overview/header.tmpl | 50 + templates/user/overview/package_versions.tmpl | 24 + templates/user/overview/packages.tmpl | 24 + templates/user/profile.tmpl | 43 + templates/user/settings/account.tmpl | 155 ++ templates/user/settings/actions.tmpl | 14 + templates/user/settings/actions_general.tmpl | 3 + templates/user/settings/appearance.tmpl | 162 ++ templates/user/settings/applications.tmpl | 120 + .../user/settings/applications_oauth2.tmpl | 6 + .../settings/applications_oauth2_edit.tmpl | 6 + .../applications_oauth2_edit_form.tmpl | 57 + .../settings/applications_oauth2_list.tmpl | 79 + templates/user/settings/audit_logs.tmpl | 6 + templates/user/settings/blocked_users.tmpl | 5 + templates/user/settings/grants_oauth2.tmpl | 40 + templates/user/settings/hook_new.tmpl | 7 + templates/user/settings/hooks.tmpl | 5 + templates/user/settings/keys.tmpl | 11 + templates/user/settings/keys_gpg.tmpl | 136 + templates/user/settings/keys_principal.tmpl | 68 + templates/user/settings/keys_ssh.tmpl | 116 + templates/user/settings/layout_footer.tmpl | 11 + templates/user/settings/layout_head.tmpl | 13 + templates/user/settings/navbar.tmpl | 78 + templates/user/settings/notifications.tmpl | 63 + templates/user/settings/organization.tmpl | 51 + templates/user/settings/packages.tmpl | 23 + .../settings/packages_cleanup_rules_edit.tmpl | 5 + .../packages_cleanup_rules_preview.tmpl | 5 + templates/user/settings/profile.tmpl | 133 + templates/user/settings/repos.tmpl | 128 + templates/user/settings/runner_edit.tmpl | 5 + .../user/settings/security/accountlinks.tmpl | 62 + templates/user/settings/security/openid.tmpl | 61 + .../user/settings/security/security.tmpl | 17 + templates/user/settings/security/twofa.tmpl | 35 + .../user/settings/security/twofa_enroll.tmpl | 24 + .../user/settings/security/webauthn.tmpl | 43 + templates/webhook/new.tmpl | 25 + tests/e2e/codeeditor.test.ts | 16 + tests/e2e/env.d.ts | 9 + tests/e2e/events.test.ts | 138 ++ tests/e2e/explore.test.ts | 17 + tests/e2e/external-render.test.ts | 53 + tests/e2e/file-view-render.test.ts | 63 + tests/e2e/fork.test.ts | 18 + tests/e2e/heatmap.test.ts | 9 + tests/e2e/issue-comment.test.ts | 38 + tests/e2e/issue-project.test.ts | 443 ++++ tests/e2e/licenses.test.ts | 9 + tests/e2e/login.test.ts | 17 + tests/e2e/mermaid.test.ts | 17 + tests/e2e/milestone.test.ts | 12 + tests/e2e/org.test.ts | 52 + tests/e2e/pr-create.test.ts | 23 + tests/e2e/pr-diff-filter.test.ts | 87 + tests/e2e/pr-review.test.ts | 114 + tests/e2e/pull-merge-box.test.ts | 23 + tests/e2e/reactions.test.ts | 26 + tests/e2e/readme.test.ts | 10 + tests/e2e/register.test.ts | 67 + tests/e2e/relative-time.test.ts | 10 + tests/e2e/release.test.ts | 19 + tests/e2e/repo-collab.test.ts | 24 + tests/e2e/repo-star-watch.test.ts | 21 + tests/e2e/repo.test.ts | 12 + tests/e2e/user-settings.test.ts | 16 + tests/e2e/utils.ts | 244 ++ tests/e2e/webauthn.test.ts | 67 + tests/fuzz/fuzz_test.go | 34 + ...51df93acc2bdd9e6373be478b968e4cad6b6647351 | Bin 0 -> 107 bytes ...2daa20075cff1d97d9d2349a8977efe9c03661737c | Bin 0 -> 2048 bytes ...69b67fee2b698ef7e9399c4ca4f34a14ccbe39f623 | 1 + ...ec6afe9ea052f3c9b6ff9f4b07956d8c48c86fca82 | 1 + .../private_repo_on_limited_org.git/HEAD | 1 + .../private_repo_on_limited_org.git/config | 6 + .../74/8bf557dfc9c6457998b5118a6c8b2129f56c30 | Bin 0 -> 43 bytes .../a5/46f86c7dd182592b96639045e176dde8df76ef | Bin 0 -> 54 bytes .../b8/95782bd271fdd266dd06e5880ea4abdc3a0dc7 | Bin 0 -> 782 bytes .../refs/heads/master | 1 + .../public_repo_on_limited_org.git/HEAD | 1 + .../public_repo_on_limited_org.git/config | 6 + .../21/2f14c8b713de38bd0b3fb23bd288369b01668a | Bin 0 -> 43 bytes .../90/e402c3937a4639725fcc59ca1f529e7dc8506f | Bin 0 -> 783 bytes .../ed/d9c1000cd1444efd63e153e3554c8d5656bf65 | Bin 0 -> 54 bytes .../refs/heads/master | 1 + .../migration/lfs-test.git/HEAD | 1 + .../migration/lfs-test.git/config | 7 + .../lfs-test.git/hooks/post-checkout | 3 + .../migration/lfs-test.git/hooks/post-commit | 3 + .../migration/lfs-test.git/hooks/post-merge | 3 + .../migration/lfs-test.git/hooks/pre-push | 3 + .../migration/lfs-test.git/index | Bin 0 -> 305 bytes ...15326465fa96c3bfd54a4ea06cfd6dbbd8340e0152 | 1 + ...a92395be4d16f2f63116caf36c8ad35c60831ab041 | 1 + .../54/6244003622c64b2fc3c2cd544d7a29882c8383 | Bin 0 -> 128 bytes .../6a/6ccf5d874fec134ee712572cc03a0f2dd7afec | Bin 0 -> 51 bytes .../a6/7134b8484c2abe9fa954e1fd83b39b271383ed | Bin 0 -> 121 bytes .../b7/01ed6ffe410f0c3ac204b929ea47cfec6cef54 | Bin 0 -> 122 bytes .../f2/07b74f55cd7f9e800b7550d587cbc488f6eaf1 | Bin 0 -> 120 bytes .../migration/lfs-test.git/refs/heads/master | 1 + .../org26/repo_external_tracker.git/HEAD | 1 + .../org26/repo_external_tracker.git/config | 10 + .../org26/repo_external_tracker.git/index | Bin 0 -> 137 bytes .../ba/ea7d6e6b7773a80bcede323cfb21dfe9d4b855 | Bin 0 -> 54 bytes .../c2/a1ad4c931cebe27c7e39176fe7119b5557c9eb | Bin 0 -> 60 bytes .../cd/aca8cf1d36e1e4e508a940f6e157e239beccfa | 3 + .../refs/heads/branch1 | 1 + .../refs/heads/master | 1 + .../repo_external_tracker_alpha.git/HEAD | 1 + .../repo_external_tracker_alpha.git/config | 10 + .../repo_external_tracker_alpha.git/index | Bin 0 -> 137 bytes .../ba/ea7d6e6b7773a80bcede323cfb21dfe9d4b855 | Bin 0 -> 54 bytes .../c2/a1ad4c931cebe27c7e39176fe7119b5557c9eb | Bin 0 -> 60 bytes .../cd/aca8cf1d36e1e4e508a940f6e157e239beccfa | 3 + .../refs/heads/branch1 | 1 + .../refs/heads/master | 1 + .../repo_external_tracker_numeric.git/HEAD | 1 + .../repo_external_tracker_numeric.git/config | 10 + .../repo_external_tracker_numeric.git/index | Bin 0 -> 137 bytes .../ba/ea7d6e6b7773a80bcede323cfb21dfe9d4b855 | Bin 0 -> 54 bytes .../c2/a1ad4c931cebe27c7e39176fe7119b5557c9eb | Bin 0 -> 60 bytes .../cd/aca8cf1d36e1e4e508a940f6e157e239beccfa | 3 + .../refs/heads/branch1 | 1 + .../refs/heads/master | 1 + .../org3/repo3.git/HEAD | 1 + .../org3/repo3.git/config | 6 + .../org3/repo3.git/hooks/post-receive | 7 + .../org3/repo3.git/hooks/post-receive.d/gitea | 2 + .../org3/repo3.git/hooks/pre-receive | 7 + .../org3/repo3.git/hooks/pre-receive.d/gitea | 2 + .../org3/repo3.git/hooks/update | 7 + .../org3/repo3.git/hooks/update.d/gitea | 2 + .../20/ade30d25e0ecaeec84e7f542a8456900858240 | Bin 0 -> 84 bytes .../27/74debeea6dc742cc4971a92db0e08b95b60588 | Bin 0 -> 51 bytes .../2a/47ca4b614a9f5a43abbd5ad851a54a616ffee6 | Bin 0 -> 760 bytes .../2f/9b22fd3159a43b7b4e5dd806fcd544edf8716f | Bin 0 -> 37 bytes .../d2/2b4d4daa5be07329fcef6ed458f00cf3392da0 | Bin 0 -> 814 bytes .../d5/6a3073c1dbb7b15963110a049d50cdb5db99fc | Bin 0 -> 42 bytes .../ec/f0db3c1ec806522de4b491fb9a3c7457398c61 | Bin 0 -> 62 bytes .../ee/16d127df463aa491e08958120f2108b02468df | Bin 0 -> 84 bytes .../org3/repo3.git/refs/heads/master | 1 + .../org3/repo3.git/refs/heads/test_branch | 1 + .../org3/repo5.git/HEAD | 1 + .../org3/repo5.git/config | 6 + .../org3/repo5.git/hooks/post-receive | 7 + .../org3/repo5.git/hooks/post-receive.d/gitea | 2 + .../org3/repo5.git/hooks/pre-receive | 7 + .../org3/repo5.git/hooks/pre-receive.d/gitea | 2 + .../org3/repo5.git/hooks/update | 7 + .../org3/repo5.git/hooks/update.d/gitea | 2 + .../20/ade30d25e0ecaeec84e7f542a8456900858240 | Bin 0 -> 84 bytes .../27/74debeea6dc742cc4971a92db0e08b95b60588 | Bin 0 -> 51 bytes .../2a/47ca4b614a9f5a43abbd5ad851a54a616ffee6 | Bin 0 -> 760 bytes .../2f/9b22fd3159a43b7b4e5dd806fcd544edf8716f | Bin 0 -> 37 bytes .../d2/2b4d4daa5be07329fcef6ed458f00cf3392da0 | Bin 0 -> 814 bytes .../d5/6a3073c1dbb7b15963110a049d50cdb5db99fc | Bin 0 -> 42 bytes .../ec/f0db3c1ec806522de4b491fb9a3c7457398c61 | Bin 0 -> 62 bytes .../ee/16d127df463aa491e08958120f2108b02468df | Bin 0 -> 84 bytes .../org3/repo5.git/refs/heads/master | 1 + .../org3/repo5.git/refs/heads/test_branch | 1 + .../org41/repo61.git/HEAD | 1 + .../org41/repo61.git/config | 6 + .../org41/repo61.git/objects/.keep | 0 .../org41/repo61.git/refs/.keep | 0 .../org42/search-by-path.git/HEAD | 1 + .../org42/search-by-path.git/config | 4 + .../org42/search-by-path.git/description | 9 + .../search-by-path.git/hooks/post-receive | 7 + .../hooks/post-receive.d/gitea | 2 + .../search-by-path.git/hooks/pre-receive | 7 + .../hooks/pre-receive.d/gitea | 2 + .../search-by-path.git/hooks/proc-receive | 7 + .../hooks/proc-receive.d/gitea | 2 + .../org42/search-by-path.git/hooks/update | 7 + .../search-by-path.git/hooks/update.d/gitea | 2 + .../org42/search-by-path.git/info/refs | 13 + .../search-by-path.git/objects/info/packs | 2 + ...76cf6e2b46bc816936ab69306fb10aea571.bitmap | Bin 0 -> 678 bytes ...bef76cf6e2b46bc816936ab69306fb10aea571.idx | Bin 0 -> 2108 bytes ...ef76cf6e2b46bc816936ab69306fb10aea571.pack | Bin 0 -> 6545 bytes ...bef76cf6e2b46bc816936ab69306fb10aea571.rev | Bin 0 -> 200 bytes .../org42/search-by-path.git/packed-refs | 14 + .../org42/search-by-path.git/refs/.keep | 0 .../private_repo_on_private_org.git/HEAD | 1 + .../private_repo_on_private_org.git/config | 6 + .../6e/75c9f89da9a9b93f4f36e61ed092f7a1625ba0 | Bin 0 -> 785 bytes .../7f/eb6f9dd600e17a04f48a76cfa0a56a3f30e2c1 | Bin 0 -> 54 bytes .../b7/91b41c0ae8cb3c4b12f3fd8c3709c2481d9e37 | Bin 0 -> 43 bytes .../refs/heads/master | 1 + .../public_repo_on_private_org.git/HEAD | 1 + .../public_repo_on_private_org.git/config | 6 + .../04/f99c528b643b9175a4b156cdfc13aba6b43853 | Bin 0 -> 53 bytes .../86/de16d8658f5c0a17ec6aa313871295d7072f78 | Bin 0 -> 43 bytes .../bf/19fd4707acb403c4aca44f126ab69142ac59ce | Bin 0 -> 785 bytes .../refs/heads/master | 1 + .../user12/repo10.git/HEAD | 1 + .../user12/repo10.git/config | 4 + .../user12/repo10.git/hooks/post-receive | 7 + .../repo10.git/hooks/post-receive.d/gitea | 2 + .../user12/repo10.git/hooks/pre-receive | 7 + .../repo10.git/hooks/pre-receive.d/gitea | 2 + .../user12/repo10.git/hooks/update | 7 + .../user12/repo10.git/hooks/update.d/gitea | 2 + .../user12/repo10.git/info/refs | 1 + .../2a/2f1d4670728a2e10049e345bd7a276468beab6 | Bin 0 -> 54 bytes .../4b/4851ad51df6a7d9f25c979345979eaeb5b349f | Bin 0 -> 42 bytes .../65/f1bf27bc3bf70f64657658635e66094edbcb4d | Bin 0 -> 150 bytes .../user12/repo10.git/objects/info/packs | 1 + .../repo10.git/refs/heads/DefaultBranch | 1 + .../user12/repo10.git/refs/heads/develop | 1 + .../user12/repo10.git/refs/heads/feature/1 | 1 + .../user12/repo10.git/refs/heads/master | 1 + .../user12/repo10.git/refs/tags/v1.1 | 1 + .../user13/repo11.git/HEAD | 1 + .../user13/repo11.git/config | 4 + .../user13/repo11.git/hooks/post-receive | 7 + .../repo11.git/hooks/post-receive.d/gitea | 2 + .../user13/repo11.git/hooks/pre-receive | 7 + .../repo11.git/hooks/pre-receive.d/gitea | 2 + .../user13/repo11.git/hooks/update | 7 + .../user13/repo11.git/hooks/update.d/gitea | 2 + .../user13/repo11.git/info/refs | 1 + .../0a/bcb056019adb8336cf9db3ad9d9cf80cd4b141 | Bin 0 -> 818 bytes .../2a/2f1d4670728a2e10049e345bd7a276468beab6 | Bin 0 -> 54 bytes .../4b/4851ad51df6a7d9f25c979345979eaeb5b349f | Bin 0 -> 42 bytes .../65/f1bf27bc3bf70f64657658635e66094edbcb4d | Bin 0 -> 150 bytes .../75/d1afd00e111c8dbd9e3d96a27b431ac5ae6d74 | Bin 0 -> 44 bytes .../ed/447543e0c85d628b91f7f466f4921908f4c5ea | Bin 0 -> 54 bytes .../user13/repo11.git/objects/info/packs | 1 + .../repo11.git/refs/heads/DefaultBranch | 1 + .../user13/repo11.git/refs/heads/branch2 | 1 + .../user13/repo11.git/refs/heads/develop | 1 + .../user13/repo11.git/refs/heads/feature/1 | 1 + .../user13/repo11.git/refs/heads/master | 1 + .../user13/repo11.git/refs/tags/v1.1 | 1 + .../user2/commits_search_test.git/HEAD | 1 + .../user2/commits_search_test.git/config | 8 + .../0a/8499a22ad32a80beda9d75efe15f9f94582468 | 2 + .../0c/cf1fcd4d1717c22de0707619a5577ea0acebf0 | Bin 0 -> 32 bytes .../3e/a192a6466793d4b7cd8641801ca0c6bec3979c | Bin 0 -> 54 bytes .../3f/6594f108842b7c50772510e53ce113d3583c4a | Bin 0 -> 54 bytes .../58/e97d1a24fb9e1599d8a467ec409430f3d3569e | Bin 0 -> 154 bytes .../67/68c1fc1d9448422f05cc84267d94ee62085fe8 | Bin 0 -> 54 bytes .../6e/8eabd9a7f8d6acd2a1219facfd37415564b144 | 2 + .../85/f46d747a68adf79cc01e2c25ba6a56932d298d | Bin 0 -> 32 bytes .../8d/dd8d1ad1fdc21ab629e906711fa9bc27aa1c52 | Bin 0 -> 32 bytes .../95/fd0c4138480e4b3913e7cf71a90623fb926fe8 | Bin 0 -> 32 bytes .../98/00fe78cabf4fe774fcf376f97fa2a0ed06987b | Bin 0 -> 149 bytes .../9f/cdb7d53bdef786d2e5577948a0c0d4b321fe5a | Bin 0 -> 32 bytes .../c2/0caf78b5f9dd2d0d183876c5cd0e761c13f7f8 | 2 + .../c5/2ba74685f5c8c593efbbb38f62fe024110adef | Bin 0 -> 54 bytes .../d6/ae8023a10ff446b6a4e7f441554834008e99c3 | Bin 0 -> 54 bytes .../user2/commits_search_test.git/packed-refs | 2 + .../commits_search_test.git/refs/heads/master | 1 + .../user2/commitsonpr.git/HEAD | 1 + .../user2/commitsonpr.git/config | 4 + .../user2/commitsonpr.git/info/refs | 3 + .../0a/6dda431c72a6a4aac05b98e319972a1a55e01c | Bin 0 -> 32 bytes .../0c/396a509b64fd4e2e55649d100b86e8b96cc0e5 | Bin 0 -> 32 bytes .../10/0ef49565829e7bd83057d2dab88f58b00db831 | Bin 0 -> 271 bytes .../10/9ab1c0b84e088d7edcf018379518b49361f285 | Bin 0 -> 32 bytes .../19/78192d98bb1b65e11c2cf37da854fbf94bffd6 | Bin 0 -> 162 bytes .../1e/67d753ac1f9097eff26f9d33eb80182344b72c | Bin 0 -> 87 bytes .../23/576dd018294e476c06e569b6b0f170d0558705 | 2 + .../28/16bffda09c0f23775ea4be279de004d28a3803 | Bin 0 -> 245 bytes .../35/f03b5e176ee6d24c86b5cca7009a5b0ba2a026 | Bin 0 -> 32 bytes .../38/cdad2e40c989aabab3f2d0a27faf0f7be617d5 | Bin 0 -> 167 bytes .../3e/64625bd6eb5bcba69ac97de6c8f507402df861 | Bin 0 -> 162 bytes .../4b/860706d3eec5858324d4ba00db0423ca4cbf50 | Bin 0 -> 54 bytes .../4c/a8bcaf27e28504df7bf996819665986b01c847 | Bin 0 -> 163 bytes .../52/84ca7f5757816e67c098224a8367aa2544222d | Bin 0 -> 193 bytes .../53/9a24812705f77484568e6ad7db84764c1903c8 | Bin 0 -> 32 bytes .../70/8605e8984e7fb9be58818e0e6d9f21bcefd63e | Bin 0 -> 33 bytes .../74/7ddb3506a4fa04a7747808eb56ae16f9e933dc | Bin 0 -> 162 bytes .../7e/d1d42eda9110676d5c3a7721965d6ed1afe83c | Bin 0 -> 324 bytes .../81/1d46c7e518f4f180afb862c0db5cb8c80529ce | 2 + .../83/2d33e438d2b4a86fba81cb67b32d1d61a828cb | Bin 0 -> 32 bytes .../83/7d5c8125633d7d258f93b998e867eab0145520 | 3 + .../87/cdc1333f5f117a92f3cef78ebe0301114b3610 | 2 + .../92/70b08497106eaa65fce8aa91f37c4780f76909 | Bin 0 -> 140 bytes .../96/cef4a7b72b3c208340ae6f0cf55a93e9077c93 | Bin 0 -> 163 bytes .../97/0c5deb117526983f554eaaa1b59102d3e3e0f7 | Bin 0 -> 32 bytes .../c5/626fc9eff57eb1bb7b796b01d4d0f2f3f792a2 | 2 + .../c7/04db5794097441aa2d9dd834d5b7e2f8f08108 | Bin 0 -> 163 bytes .../cb/ff181af4c9c7fee3cf6c106699e07d9a3f54e6 | Bin 0 -> 128 bytes .../d1/8e427f4011e74e96a31823c938be26eebab53b | Bin 0 -> 114 bytes .../d2/5795e38fbc1b4839697e834b957d61c83d994f | Bin 0 -> 32 bytes .../d6/6f456f0813a5841fbc03e5f1c47304dc675695 | Bin 0 -> 32 bytes .../e1/7e0fa20f3d2125916f2fb2f51f19240678cb83 | Bin 0 -> 219 bytes .../ec/d9fdda5c814055ee619513e1c388ba1bbcb280 | Bin 0 -> 32 bytes .../user2/commitsonpr.git/objects/info/packs | 1 + .../user2/commitsonpr.git/refs/heads/branch1 | 1 + .../user2/commitsonpr.git/refs/heads/main | 1 + .../user2/commitsonpr.git/refs/heads/master | 1 + .../user2/commitsonpr.git/refs/pull/1/head | 1 + .../user2/git_hooks_test.git/HEAD | 1 + .../user2/git_hooks_test.git/config | 4 + .../git_hooks_test.git/hooks/post-receive | 7 + .../hooks/post-receive.d/gitea | 2 + .../git_hooks_test.git/hooks/pre-receive | 7 + .../hooks/pre-receive.d/gitea | 2 + .../hooks/pre-receive.d/pre-receive | 2 + .../user2/git_hooks_test.git/hooks/update | 7 + .../git_hooks_test.git/hooks/update.d/gitea | 2 + .../user2/git_hooks_test.git/info/refs | 1 + .../2a/2f1d4670728a2e10049e345bd7a276468beab6 | Bin 0 -> 54 bytes .../4b/4851ad51df6a7d9f25c979345979eaeb5b349f | Bin 0 -> 42 bytes .../65/f1bf27bc3bf70f64657658635e66094edbcb4d | Bin 0 -> 150 bytes .../git_hooks_test.git/objects/info/packs | 1 + .../refs/heads/DefaultBranch | 1 + .../git_hooks_test.git/refs/heads/develop | 1 + .../git_hooks_test.git/refs/heads/feature/1 | 1 + .../git_hooks_test.git/refs/heads/master | 1 + .../user2/git_hooks_test.git/refs/tags/v1.1 | 1 + .../user2/glob.git/HEAD | 1 + .../user2/glob.git/config | 4 + .../48/06cb9df135782b818c968c2fadbd2c150d23d6 | Bin 0 -> 21 bytes .../59/fee614e09d1f1cd1e15e4b2a7e9c8873a81498 | Bin 0 -> 34 bytes .../7c/8ac2f8d82a1eb5f6aaece6629ff11015f91eb4 | Bin 0 -> 21 bytes .../8e/592e636d27ac144f92f7fe8c33631cbdea594d | Bin 0 -> 78 bytes .../95/aff026f99a9ab76fbd01decb63dd3dbc03e498 | Bin 0 -> 34 bytes .../ae/d1ffed24cc3cf9b80490795e893cae4bddd684 | Bin 0 -> 108 bytes .../bf/d6a6583f9a9ac59bd726c1df26c64a89427ede | Bin 0 -> 21 bytes .../c8/eb3b6c767ccb68411d0a1f6c769be69fb4d95a | 1 + .../de/6be43fe8eb19ca3f4e934cb8b9a9a0b20fe865 | Bin 0 -> 50 bytes .../ef/6b814b610d8e7717aa0f71fbe5842bcf814697 | 2 + .../user2/glob.git/refs/heads/master | 1 + .../user2/lfs.git/HEAD | 1 + .../user2/lfs.git/config | 4 + .../15/2de0f78bc6815b58cd9f08aebe3f66fb0f172e | Bin 0 -> 228 bytes .../23/10e4a07f9314a1a92fdfbdcd3d2884f01e96ab | Bin 0 -> 123 bytes .../2b/6c6c4eaefa24b22f2092c3d54b263ff26feb58 | Bin 0 -> 122 bytes .../73/cf03db6ece34e12bf91e8853dc58f678f2f82d | 2 + .../74/21a018a7e3f15ee5691f162d0ed87dc19882f0 | Bin 0 -> 123 bytes .../82/76d2a29779af982c0afa976bdb793b52d442a8 | Bin 0 -> 38 bytes .../bc/e50ea8f203ee923d5a640d05208abf3206486e | Bin 0 -> 92 bytes .../d4/a41a0d4db4949e129bd22f871171ea988103ef | Bin 0 -> 123 bytes .../d7/ce0013ced38b0696dd2d68d69a5d8b652f7148 | Bin 0 -> 55 bytes .../df/d8105b264d304c49ed9f1d56bd90189ecdf833 | Bin 0 -> 75 bytes .../user2/lfs.git/refs/heads/master | 1 + .../user2/readme-test.git/HEAD | 1 + .../user2/readme-test.git/config | 4 + .../user2/readme-test.git/objects/.keep | 0 .../user2/readme-test.git/refs/.keep | 0 .../user2/repo-release.git/HEAD | 1 + .../user2/repo-release.git/config | 4 + .../08/9ba8b2f324d89b74f6853374a0476b312a46f6 | Bin 0 -> 54 bytes .../18/4288e5acffbcb17160b990e8efe83b12dfaaba | Bin 0 -> 127 bytes .../24/3cdd85d09ce4104855edf219c05b74c65350fc | Bin 0 -> 85 bytes .../43/80f99290b2b3922733ff82c57afad915ace907 | Bin 0 -> 158 bytes .../6d/fe48a18ce2fb47d3a75e13c7ab35f935077535 | Bin 0 -> 50 bytes .../71/97b56fdc75b453f47c9110938cb46a303579fd | Bin 0 -> 153 bytes .../79/f9d88f1b054d650f88da0bd658e21f7b0cf6ec | Bin 0 -> 156 bytes .../7c/055ef1678b03b831bbe7b9ca5aed33b1a8dea0 | Bin 0 -> 53 bytes .../80/abeef37c96b85b83a916f5f295f04f4d380a42 | Bin 0 -> 85 bytes .../a8/a700e8c644c783ba2c6e742bb81bf91e244bff | Bin 0 -> 153 bytes .../bc/7068d1eb2f93a04e3ec73521473444ceec0961 | Bin 0 -> 58 bytes .../c4/a4e1a72a2098d687b4280e7c6972280c1f9c39 | Bin 0 -> 166 bytes .../cd/7f28e1b404377eadbe0d54234ba861883e6930 | Bin 0 -> 96 bytes .../ce/f06e48f2642cd0dc9597b4bea09f4b3f74aad6 | Bin 0 -> 159 bytes .../d0/718fe871fbb54da104ff201f75f62a6ced2e29 | Bin 0 -> 54 bytes .../d4/613f8dad1fa61e415922f6eb33244358fca85d | Bin 0 -> 84 bytes .../dd/5488178fc8a5c62430b3fb3017203b917b95ab | Bin 0 -> 38 bytes .../e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 | Bin 0 -> 15 bytes .../ef/3c849ed54b22bb1f500da91b789c40cb0915da | Bin 0 -> 97 bytes .../f3/f1c90ac949aa1b0f129d30f338d408663c8a83 | 2 + .../f9/3e3a1a1525fb5b91020da86e44810c87a2d7bc | Bin 0 -> 54 bytes .../user2/repo-release.git/refs/heads/main | 1 + .../user2/repo-release.git/refs/heads/master | 1 + .../user2/repo-release.git/refs/tags/v1.0 | 1 + .../user2/repo-release.git/refs/tags/v1.1 | 1 + .../user2/repo-release.git/refs/tags/v2.0 | 1 + .../user2/repo1.git/HEAD | 1 + .../user2/repo1.git/config | 4 + .../user2/repo1.git/hooks/post-receive | 7 + .../repo1.git/hooks/post-receive.d/gitea | 2 + .../user2/repo1.git/hooks/pre-receive | 7 + .../user2/repo1.git/hooks/pre-receive.d/gitea | 2 + .../user2/repo1.git/hooks/proc-receive | 7 + .../repo1.git/hooks/proc-receive.d/gitea | 2 + .../user2/repo1.git/hooks/update | 7 + .../user2/repo1.git/hooks/update.d/gitea | 2 + .../user2/repo1.git/info/refs | 3 + .../00/750edc07d6415dcc07ae0351e9397b0222b7ba | Bin 0 -> 17 bytes .../08/51b61d9f8ca0e9e63617e11907988ee88b1ca6 | Bin 0 -> 85 bytes .../12/8105ae73669ac2a4cb42751538f0c65c54e28a | Bin 0 -> 643 bytes .../16/633238d370a441f98dca532e4296a619c4c85f | Bin 0 -> 47 bytes .../2a/2f1d4670728a2e10049e345bd7a276468beab6 | Bin 0 -> 54 bytes .../3f/a2f829675543ecfc16b2891aebe8bf0608a8f4 | Bin 0 -> 138 bytes .../40/3d76c604cb569323864e06a07b85d466924802 | Bin 0 -> 68 bytes .../46/49299398e4d39a5c09eb4f534df6f1e1eb87cc | 4 + .../4a/357436d925b5c974181ff12a994538ddc5a269 | Bin 0 -> 840 bytes .../4b/4851ad51df6a7d9f25c979345979eaeb5b349f | Bin 0 -> 42 bytes .../5c/050d3b6d2db231ab1f64e324f1b6b9a0b181c2 | Bin 0 -> 833 bytes .../5f/22f7d0d95d614d25a5b68592adb345a4b5c7fd | Bin 0 -> 185 bytes .../62/fb502a7172d4453f0322a2cc85bddffa57f07a | Bin 0 -> 839 bytes .../65/f1bf27bc3bf70f64657658635e66094edbcb4d | Bin 0 -> 150 bytes .../6a/a3a5385611c5eb8986c9961a9c34a93cbaadfb | Bin 0 -> 86 bytes .../78/fb907e3a3309eae4fe8fef030874cebbf1cd5e | Bin 0 -> 158 bytes .../7c/4df115542e05c700f297519e906fd63c9c9804 | Bin 0 -> 54 bytes .../90/c1019714259b24fb81711d4416ac0f18667dfa | 2 + .../90/dcd07da077d1e7cd6032b52d1f79ae2b5f19b2 | 2 + .../94/922e1295c678267de1193b7b84ad8a086c27f9 | Bin 0 -> 54 bytes .../98/5f0301dba5e7b34be866819cd15ad3d8f508ee | Bin 0 -> 842 bytes .../a6/9277c81e90b98a7c0ab25b042a6e296da8eb9a | Bin 0 -> 76 bytes .../a7/57c0ea621e63d0fd6fc353a175fdc7199e5d1d | Bin 0 -> 61 bytes .../b2/60587271671842af0b036e4fe643c9d45b7ddd | Bin 0 -> 20 bytes .../d4/a1a6dcf7bd42891f264d484e80dac7e66b5410 | Bin 0 -> 34 bytes .../d7/bd5b8cfb680f460e37b6fd7cf74c284e059118 | Bin 0 -> 85 bytes .../dc/7a8ba127fee870dd683310ce660dfe59333a1b | Bin 0 -> 78 bytes .../dd/59742c0f6672911f2b64cba5711ac00593ed32 | Bin 0 -> 118 bytes .../f3/fa0f5cc797fc4c02a1b8bec9de4b2072fcdbdf | Bin 0 -> 53 bytes .../user2/repo1.git/objects/info/packs | 1 + .../user2/repo1.git/refs/heads/DefaultBranch | 1 + .../user2/repo1.git/refs/heads/branch2 | 1 + .../user2/repo1.git/refs/heads/develop | 1 + .../user2/repo1.git/refs/heads/feature/1 | 1 + .../repo1.git/refs/heads/home-md-img-check | 1 + .../user2/repo1.git/refs/heads/master | 1 + .../user2/repo1.git/refs/heads/pr-to-update | 1 + .../refs/heads/sub-home-md-img-check | 1 + .../user2/repo1.git/refs/notes/commits | 1 + .../user2/repo1.git/refs/pull/2/head | 1 + .../user2/repo1.git/refs/pull/3/head | 1 + .../user2/repo1.git/refs/pull/5/head | 1 + .../user2/repo1.git/refs/tags/v1.1 | 1 + .../user2/repo1.wiki.git/HEAD | 1 + .../user2/repo1.wiki.git/config | 4 + .../0c/f15c3f66ec8384480ed9c3cf87c9e97fbb0ec3 | 2 + .../0d/ca5bd9b5d7ef937710e056f575e86c0184ba85 | Bin 0 -> 820 bytes .../2c/54faec6c45d31c1abfaecdab471eac6633738a | Bin 0 -> 131 bytes .../32/5dc4f8e9344e6668f21536a69d5f1d4ed53ca3 | Bin 0 -> 63 bytes .../42/3313fbd38093bb10d0c8387db9105409c6f196 | Bin 0 -> 830 bytes .../71/911bf48766c7181518c1070911019fbb00b1fc | 1 + .../72/fc6251cc648e914c10009d31431fa2e38b9a20 | Bin 0 -> 94 bytes .../74/d5a0d73db9b9ef7aa9978eb7a099b08f54d45e | Bin 0 -> 53 bytes .../7c/d7c8fa852973c72c66eb120a6677c54a8697f7 | Bin 0 -> 95 bytes .../81/a1c039774e337621609336c0e44ed9f92278f7 | Bin 0 -> 68 bytes .../89/43a1d5f93c00439d5ffc0f8e36f5d60abae46c | Bin 0 -> 206 bytes .../91/dc55f9de16a558e859123f2b99668469b1a1dc | Bin 0 -> 234 bytes .../a5/bbc0fd39a696feabed2d4cccaf05abbcaf3b02 | 1 + .../c1/0d10b7e655b3dab1f53176db57c8219a5488d6 | 2 + .../c4/b38c3e1395393f75bbbc2ed10c7eeb577d3b64 | Bin 0 -> 189 bytes .../cf/19952a40b92eb2f86689146a65ac2d87c0818a | Bin 0 -> 255 bytes .../e1/6da91326b845f1ba86a7df0a67db352f96dcb0 | Bin 0 -> 149 bytes .../e5/3d079e581fbfdea1075a54d5b621eab0090e52 | Bin 0 -> 52 bytes .../ea/82fc8777a24b07c26b3a4bf4e2742c03733eab | Bin 0 -> 44 bytes .../f5/05ec9b5c7a45a10259c1dda7f18434e5d55940 | Bin 0 -> 157 bytes .../user2/repo1.wiki.git/refs/heads/master | 1 + .../user2/repo15.git/HEAD | 1 + .../user2/repo15.git/config | 4 + .../user2/repo15.git/hooks/post-receive | 7 + .../repo15.git/hooks/post-receive.d/gitea | 2 + .../user2/repo15.git/hooks/pre-receive | 7 + .../repo15.git/hooks/pre-receive.d/gitea | 2 + .../user2/repo15.git/hooks/update | 7 + .../user2/repo15.git/hooks/update.d/gitea | 2 + .../user2/repo16.git/HEAD | 1 + .../user2/repo16.git/config | 4 + .../0c/3d59dea27b97aa3cb66072745d7a2c51a7a8b1 | Bin 0 -> 54 bytes .../24/f83a471f77579fea57bac7255d6e64e70fce1c | Bin 0 -> 54 bytes .../27/566bd5738fc8b4e3fef3c5e72cce608537bd95 | Bin 0 -> 575 bytes .../3b/2b54fe3d9a8279d5b926124dccdf279b8eff2f | 1 + .../45/8121ce9a6b855c9733bae62093caf3f39685de | Bin 0 -> 26 bytes .../50/99b81332712fe655e34e8dd63574f503f61811 | 2 + .../69/554a64c1e6030f051e5c3f94bfbd773cd6a324 | Bin 0 -> 158 bytes .../a4/3476a501516e065c5a82f05fd58fd319598bc1 | Bin 0 -> 57 bytes .../e9/4083fcdf1f10c545e9253a23c5e44a2ff68aac | Bin 0 -> 524 bytes .../f2/7c2b2b03dcab38beaf89b0ab4ff61f6de63441 | Bin 0 -> 522 bytes .../f9/0451c72ef61a7645293d17b47be7a8e983da57 | Bin 0 -> 27 bytes .../user2/repo16.git/refs/heads/good-sign | 1 + .../refs/heads/good-sign-not-yet-validated | 1 + .../user2/repo16.git/refs/heads/master | 1 + .../user2/repo16.git/refs/heads/not-signed | 1 + .../user2/repo2.git/HEAD | 1 + .../user2/repo2.git/config | 4 + .../user2/repo2.git/info/refs | 1 + .../0a/7d8b41ae9763e9a1743917396839d1791d49d0 | Bin 0 -> 188 bytes .../0c/f15c3f66ec8384480ed9c3cf87c9e97fbb0ec3 | 2 + .../10/32bbf17fbc0d9c95bb5418dabe8f8c99278700 | 2 + .../1c/887eaa8d81fa86da7695d8f635cf17813eb422 | 1 + .../26/f842bcad37fa40a1bb34cbb5ee219ee35d863d | Bin 0 -> 75 bytes .../32/5dc4f8e9344e6668f21536a69d5f1d4ed53ca3 | Bin 0 -> 63 bytes .../36/fff01c8c9f722d49d53186abd27b5be8d85338 | Bin 0 -> 155 bytes .../42/3313fbd38093bb10d0c8387db9105409c6f196 | Bin 0 -> 830 bytes .../71/911bf48766c7181518c1070911019fbb00b1fc | 1 + .../72/fc6251cc648e914c10009d31431fa2e38b9a20 | Bin 0 -> 94 bytes .../74/d5a0d73db9b9ef7aa9978eb7a099b08f54d45e | Bin 0 -> 53 bytes .../7c/d7c8fa852973c72c66eb120a6677c54a8697f7 | Bin 0 -> 95 bytes .../ba/1aed4e2ea2443d76cec241b96be4ec990852ec | Bin 0 -> 117 bytes .../c1/0d10b7e655b3dab1f53176db57c8219a5488d6 | 2 + .../c4/b38c3e1395393f75bbbc2ed10c7eeb577d3b64 | Bin 0 -> 189 bytes .../f5/05ec9b5c7a45a10259c1dda7f18434e5d55940 | Bin 0 -> 157 bytes .../user2/repo2.git/objects/info/commit-graph | Bin 0 -> 1212 bytes .../user2/repo2.git/objects/info/packs | 2 + ...d943b3d857eb3ebdb4b35eeef38f63cf5d2.bitmap | Bin 0 -> 248 bytes ...f7ad943b3d857eb3ebdb4b35eeef38f63cf5d2.idx | Bin 0 -> 1240 bytes ...7ad943b3d857eb3ebdb4b35eeef38f63cf5d2.pack | Bin 0 -> 637 bytes .../user2/repo2.git/packed-refs | 2 + .../user2/repo2.git/refs/heads/master | 1 + .../user2/repo2.git/refs/tags/v1.1 | 1 + .../user2/repo20.git/HEAD | 1 + .../user2/repo20.git/config | 4 + .../user2/repo20.git/hooks/post-receive | 15 + .../repo20.git/hooks/post-receive.d/gitea | 2 + .../user2/repo20.git/hooks/pre-receive | 15 + .../repo20.git/hooks/pre-receive.d/gitea | 2 + .../user2/repo20.git/hooks/update | 14 + .../user2/repo20.git/hooks/update.d/gitea | 2 + .../user2/repo20.git/info/refs | 1 + .../02/15cbe13d2695a2c3464ab5e59f47f37c3ff5d5 | Bin 0 -> 21 bytes .../05/81d7edf45206787ff93956ea892e8a2ae77604 | Bin 0 -> 47 bytes .../07/0b2e783a6b3e521a23fdead377a3e41a04410d | Bin 0 -> 128 bytes .../1b/271d83842d348b1ee71d8e6ead400aaeb4d1b5 | Bin 0 -> 19 bytes .../29/5ba6ac57fdd46f62a51272f40e60b6dea697b2 | Bin 0 -> 21 bytes .../2c/ec0f7069ed09d934e904c49f414d8bdf818ce4 | Bin 0 -> 49 bytes .../41/4a282859758ba7b159bfbd9c2b193eb8f135ee | Bin 0 -> 18 bytes .../79/adb592126eddce5f656f56db797910db025af0 | Bin 0 -> 165 bytes .../80/8038d2f71b0ab020991439cffd24309c7bc530 | Bin 0 -> 138 bytes .../83/70977f63979e140b6b58992b1fdb4098b24cd9 | Bin 0 -> 104 bytes .../8b/abce967f21b9dfa6987f943b91093dac58a4f0 | 1 + .../8c/e1dee41e1a3700819a9a309f275f8dc7b7e0b6 | Bin 0 -> 154 bytes .../a4/202876cd8bbc3f38b7d99594edbe1bb7f97a6f | Bin 0 -> 191 bytes .../b0/246d5964a3630491bd06c756be46513e3d7035 | Bin 0 -> 21 bytes .../b6/7e43a07d48243a5f670ace063acd5e13f719df | Bin 0 -> 173 bytes .../ba/3aeafe10402c6b29535a58d91def7e43638d9d | Bin 0 -> 22 bytes .../c5/0ac6b9e25abb8200bb377755367d7265c581cf | Bin 0 -> 75 bytes .../c8/e31bc7688741a5287fcde4fbb8fc129ca07027 | 2 + .../ce/013625030ba8dba906f756967f9e9ca394464a | Bin 0 -> 21 bytes .../cf/e3b3c1fd36fba04f9183287b106497e1afe986 | 3 + .../db/89c972fc57862eae378f45b74aca228037d415 | Bin 0 -> 18 bytes .../ea/f5f7510320b6a327fb308379de2f94d8859a54 | Bin 0 -> 30 bytes .../user2/repo20.git/objects/info/packs | 1 + .../user2/repo20.git/refs/heads/add-csv | 1 + .../user2/repo20.git/refs/heads/master | 1 + .../repo20.git/refs/heads/remove-files-a | 1 + .../repo20.git/refs/heads/remove-files-b | 1 + .../user2/utf8.git/HEAD | 1 + .../user2/utf8.git/config | 4 + .../user2/utf8.git/hooks/post-receive | 7 + .../user2/utf8.git/hooks/post-receive.d/gitea | 2 + .../user2/utf8.git/hooks/pre-receive | 7 + .../user2/utf8.git/hooks/pre-receive.d/gitea | 2 + .../user2/utf8.git/hooks/update | 7 + .../user2/utf8.git/hooks/update.d/gitea | 2 + .../user2/utf8.git/info/refs | 9 + .../14/c42687126acae9d1ad41d7bdb528f811065a6a | Bin 0 -> 39 bytes .../1d/5e00f305a7ca6a8a94e65456820a6d260adab8 | Bin 0 -> 127 bytes .../28/d579e4920fbf4f66e71dab3e779d9fbf41422a | 3 + .../3a/810dbf6b96afaa8c5f69a8b6ec1dabfca7368b | Bin 0 -> 176 bytes .../3a/a73c3499bff049a352b4e265575373e964b89a | Bin 0 -> 137 bytes .../3a/c6084110205f98174c4f1ec7e78cb21a15dfc2 | Bin 0 -> 23 bytes .../4c/61dd0a799e0830e77edfe6c74f7c349bc8e62a | Bin 0 -> 40 bytes .../50/4d9fe743979d4e9785a25a363c7007293f0838 | Bin 0 -> 40 bytes .../56/92bcf9f7c9eacb1ad68442161f2573877f96f4 | Bin 0 -> 49 bytes .../59/e2c41e8f5140bb0182acebec17c8ad9831cc62 | Bin 0 -> 847 bytes .../64/89894ad11093fdc49c0ed857d80682344a7264 | Bin 0 -> 39 bytes .../6d/0c79ce3401c67d1ad522e61c47083a9fdee16c | Bin 0 -> 54 bytes .../84/7c6d93c6860dd377651245711b7fbcd34a18d4 | Bin 0 -> 41 bytes .../9b/9cc8f558d1c4f815592496fa24308ba2a9c824 | Bin 0 -> 47 bytes .../a4/f1bb3f2f8c6a0e840e935812ef4903ce515dad | Bin 0 -> 394 bytes .../a9/a61830fbf4e84999d3b20cf178954366701fe5 | Bin 0 -> 129 bytes .../c7/85b65bf16928b58567cb23669125c0ccd25a4f | Bin 0 -> 44 bytes .../e9/63733b8a355cf860c465b4af7b236a6ef08783 | Bin 0 -> 47 bytes .../eb/f146f803fccbc1471ef01d8fa0fe12c14e61a5 | 1 + .../ee/9686cb562f492f64381bff7f298b2a1c67a141 | Bin 0 -> 88 bytes .../f4/02ff67c0b3161c3988dbf6188e6e0df257fd75 | Bin 0 -> 52 bytes .../user2/utf8.git/objects/info/packs | 1 + .../user2/utf8.git/refs/heads/Grüßen | 1 + .../utf8.git/refs/heads/Plus+Is+Not+Space | 1 + .../user2/utf8.git/refs/heads/master | 1 + .../user2/utf8.git/refs/heads/ГлавнаяВетка | 1 + .../user2/utf8.git/refs/heads/а/б/в | 1 + .../user2/utf8.git/refs/heads/ブランチ | 1 + .../user2/utf8.git/refs/tags/Ё/人 | 1 + .../user2/utf8.git/refs/tags/Тэг | 1 + .../user2/utf8.git/refs/tags/タグ | 1 + .../user27/repo49.git/HEAD | 1 + .../user27/repo49.git/config | 6 + .../user27/repo49.git/hooks/post-receive | 15 + .../repo49.git/hooks/post-receive.d/gitea | 2 + .../user27/repo49.git/hooks/pre-receive | 15 + .../repo49.git/hooks/pre-receive.d/gitea | 2 + .../user27/repo49.git/hooks/update | 14 + .../user27/repo49.git/hooks/update.d/gitea | 2 + .../user27/repo49.git/info/refs | 1 + .../47/34b1f84a367fa1b81c31aa4234a5bad11cafa3 | Bin 0 -> 84 bytes .../4d/31f3a12656368a8d9180f431d40d0fc408be2d | Bin 0 -> 29 bytes .../51/f84af231345367fd5d61ceb89efb3b6d757061 | Bin 0 -> 121 bytes .../79/3aa682b06ae032641abf70c5dfeade28c07c52 | Bin 0 -> 28 bytes .../aa/cbdfe9e1c4b47f60abe81849045fa4e96f1d75 | Bin 0 -> 154 bytes .../dd/392e939ea4936b2459219c9c9a1f25547ccaeb | Bin 0 -> 53 bytes .../f2/8eeca3df7614fd4f10c1030f13feb418ef3c6f | Bin 0 -> 54 bytes .../user27/repo49.git/objects/info/packs | 1 + .../user27/repo49.git/refs/heads/master | 1 + .../user27/repo49.git/refs/heads/test/archive | 1 + .../user27/template1.git/HEAD | 1 + .../user27/template1.git/config | 6 + .../user27/template1.git/hooks/post-receive | 15 + .../template1.git/hooks/post-receive.d/gitea | 2 + .../user27/template1.git/hooks/pre-receive | 15 + .../template1.git/hooks/pre-receive.d/gitea | 2 + .../user27/template1.git/hooks/update | 14 + .../user27/template1.git/hooks/update.d/gitea | 2 + .../user27/template1.git/info/refs | 1 + .../2a/83b349fa234131fc5db6f2a0498d3f4d3d6038 | 2 + .../3d/0bc64f2521cfc7ffce6c175c1c846c88eb6df7 | Bin 0 -> 192 bytes .../47/34b1f84a367fa1b81c31aa4234a5bad11cafa3 | Bin 0 -> 84 bytes .../4d/31f3a12656368a8d9180f431d40d0fc408be2d | Bin 0 -> 29 bytes .../51/f84af231345367fd5d61ceb89efb3b6d757061 | Bin 0 -> 121 bytes .../79/3aa682b06ae032641abf70c5dfeade28c07c52 | Bin 0 -> 28 bytes .../83/77b2196e99ac8635aae79df3db76959ccd1094 | Bin 0 -> 53 bytes .../99/45b93bcb5b70af06e0322bd2caa6180680991f | Bin 0 -> 28 bytes .../aa/cbdfe9e1c4b47f60abe81849045fa4e96f1d75 | Bin 0 -> 154 bytes .../af/f5b10402b4e0479d1e76bc41a42d29fe7f28fa | Bin 0 -> 106 bytes .../b9/04864fd6cd0c8e9054351fd39a980bfd214229 | Bin 0 -> 90 bytes .../c5/10abf4c7c3e0dc4bf07db9344c61c4e6ee7cbc | Bin 0 -> 50 bytes .../dd/392e939ea4936b2459219c9c9a1f25547ccaeb | Bin 0 -> 53 bytes .../e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 | Bin 0 -> 15 bytes .../f2/8eeca3df7614fd4f10c1030f13feb418ef3c6f | Bin 0 -> 54 bytes .../user27/template1.git/objects/info/packs | 1 + .../user27/template1.git/refs/heads/master | 1 + .../user30/empty.git/HEAD | 1 + .../user30/empty.git/config | 6 + .../user30/empty.git/objects/.keep | 0 .../user30/empty.git/refs/.keep | 0 .../user40/repo60.git/HEAD | 1 + .../user40/repo60.git/config | 6 + .../user40/repo60.git/objects/.keep | 0 .../user40/repo60.git/refs/.keep | 0 .../user5/repo4.git/HEAD | 1 + .../user5/repo4.git/config | 4 + .../16/dfebd1ed3905d78d7e061e945fc9c34afe4e81 | Bin 0 -> 24 bytes .../c1/202ad022ae7d3a6d2474dc76d5a0c8e87cdc0f | Bin 0 -> 54 bytes .../c7/cd3cd144e6d23c9d6f3d07e52b2c1a956e0338 | Bin 0 -> 818 bytes .../user5/repo4.git/refs/heads/master | 1 + ...ET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo | 3 + ...ues%2F1%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...es%2F1%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...es%2F10%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...s%2F10%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...es%2F11%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...s%2F11%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...es%2F12%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...s%2F12%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...es%2F13%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...s%2F13%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...ues%2F2%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...es%2F2%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...ues%2F3%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...es%2F3%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...ues%2F4%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...es%2F4%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...ues%2F5%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...es%2F5%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...ues%2F6%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...es%2F6%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...ues%2F7%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...es%2F7%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...ues%2F8%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...es%2F8%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...ues%2F9%2Fcomments%3Flimit%3D50%26page%3D1 | 3 + ...es%2F9%2Freactions%3Flimit%3D50%26page%3D1 | 3 + ...epo%2Fissues%2Fcomments%2F1553%2Freactions | 3 + ...epo%2Fissues%2Fcomments%2F1554%2Freactions | 3 + ...7%26page%3D1%26state%3Dall%26type%3Dissues | 3 + ...test_repo%2Flabels%3Flimit%3D50%26page%3D1 | 3 + ...ones%3Flimit%3D50%26page%3D1%26state%3Dall | 3 + ...lls%2F10%2Freviews%3Flimit%3D50%26page%3D1 | 3 + ...lls%2F11%2Freviews%3Flimit%3D50%26page%3D1 | 3 + ...lls%2F12%2Freviews%3Flimit%3D50%26page%3D1 | 3 + ...lls%2F13%2Freviews%3Flimit%3D50%26page%3D1 | 3 + ...ulls%2F8%2Freviews%3Flimit%3D50%26page%3D1 | 3 + ...ulls%2F9%2Freviews%3Flimit%3D50%26page%3D1 | 3 + ...ulls%3Flimit%3D49%26page%3D1%26state%3Dall | 3 + ...st_repo%2Freleases%3Flimit%3D50%26page%3D1 | 3 + ...Ftest_repo%2Ftopics%3Flimit%3D0%26page%3D1 | 3 + .../GET_%2Fapi%2Fv1%2Fsettings%2Fapi | 3 + .../GET_%2Fapi%2Fv1%2Fversion | 3 + ...ET_%2Fgitea%2Ftest_repo%2Fpulls%2F10.patch | 2 + ...ET_%2Fgitea%2Ftest_repo%2Fpulls%2F11.patch | 2 + ...ET_%2Fgitea%2Ftest_repo%2Fpulls%2F12.patch | 2 + ...ET_%2Fgitea%2Ftest_repo%2Fpulls%2F13.patch | 2 + ...GET_%2Fgitea%2Ftest_repo%2Fpulls%2F8.patch | 2 + ...GET_%2Fgitea%2Ftest_repo%2Fpulls%2F9.patch | 2 + tests/integration/actions_approve_test.go | 245 ++ tests/integration/actions_concurrency_test.go | 1854 ++++++++++++++ .../actions_concurrent_claim_test.go | 118 + tests/integration/actions_delete_run_test.go | 178 ++ .../actions_dynamic_matrix_test.go | 198 ++ tests/integration/actions_inputs_test.go | 84 + tests/integration/actions_job_test.go | 916 +++++++ tests/integration/actions_job_token_test.go | 475 ++++ tests/integration/actions_list_filter_test.go | 68 + .../integration/actions_log_finalize_test.go | 91 + tests/integration/actions_log_test.go | 309 +++ tests/integration/actions_rerun_test.go | 557 +++++ .../actions_reusable_workflow_test.go | 985 ++++++++ tests/integration/actions_route_test.go | 332 +++ .../integration/actions_runner_modify_test.go | 226 ++ tests/integration/actions_runner_sort_test.go | 130 + tests/integration/actions_runner_test.go | 176 ++ tests/integration/actions_schedule_test.go | 304 +++ .../actions_scoped_workflow_test.go | 548 ++++ tests/integration/actions_settings_test.go | 81 + tests/integration/actions_trigger_test.go | 1975 +++++++++++++++ tests/integration/actions_variables_test.go | 145 ++ tests/integration/admin_audit_test.go | 172 ++ tests/integration/admin_config_test.go | 92 + tests/integration/admin_user_org_test.go | 53 + tests/integration/admin_user_test.go | 147 ++ .../integration/api_actions_artifact_test.go | 798 ++++++ .../api_actions_artifact_v4_test.go | 1061 ++++++++ .../api_actions_permission_test.go | 53 + tests/integration/api_actions_run_test.go | 768 ++++++ tests/integration/api_actions_runner_test.go | 432 ++++ tests/integration/api_admin_org_test.go | 85 + tests/integration/api_admin_packages_test.go | 44 + tests/integration/api_admin_test.go | 362 +++ tests/integration/api_auth_test.go | 32 + tests/integration/api_branch_test.go | 474 ++++ .../api_comment_attachment_test.go | 204 ++ tests/integration/api_comment_test.go | 278 +++ tests/integration/api_fork_test.go | 164 ++ .../api_gitignore_templates_test.go | 51 + tests/integration/api_gpg_keys_test.go | 273 ++ .../api_helper_for_declarative_test.go | 442 ++++ tests/integration/api_httpsig_test.go | 143 ++ .../integration/api_issue_attachment_test.go | 179 ++ tests/integration/api_issue_config_test.go | 195 ++ .../integration/api_issue_dependency_test.go | 191 ++ tests/integration/api_issue_label_test.go | 294 +++ tests/integration/api_issue_lock_test.go | 74 + tests/integration/api_issue_milestone_test.go | 85 + tests/integration/api_issue_pin_test.go | 181 ++ tests/integration/api_issue_reaction_test.go | 173 ++ tests/integration/api_issue_stopwatch_test.go | 94 + .../api_issue_subscription_test.go | 80 + tests/integration/api_issue_templates_test.go | 69 + tests/integration/api_issue_test.go | 645 +++++ .../api_issue_tracked_time_test.go | 205 ++ tests/integration/api_keys_test.go | 235 ++ tests/integration/api_label_templates_test.go | 58 + .../integration/api_license_templates_test.go | 57 + tests/integration/api_notification_test.go | 231 ++ tests/integration/api_oauth2_apps_test.go | 139 ++ tests/integration/api_org_avatar_test.go | 72 + tests/integration/api_org_test.go | 385 +++ tests/integration/api_packages_alpine_test.go | 323 +++ tests/integration/api_packages_arch_test.go | 308 +++ tests/integration/api_packages_cargo_test.go | 379 +++ tests/integration/api_packages_chef_test.go | 555 +++++ .../integration/api_packages_composer_test.go | 330 +++ tests/integration/api_packages_conan_test.go | 866 +++++++ tests/integration/api_packages_conda_test.go | 273 ++ .../api_packages_container_test.go | 831 +++++++ tests/integration/api_packages_cran_test.go | 229 ++ tests/integration/api_packages_debian_test.go | 304 +++ .../integration/api_packages_generic_test.go | 293 +++ .../integration/api_packages_goproxy_test.go | 149 ++ tests/integration/api_packages_helm_test.go | 237 ++ tests/integration/api_packages_maven_test.go | 331 +++ tests/integration/api_packages_npm_test.go | 553 +++++ tests/integration/api_packages_nuget_test.go | 947 +++++++ tests/integration/api_packages_pub_test.go | 173 ++ tests/integration/api_packages_pypi_test.go | 242 ++ tests/integration/api_packages_rpm_test.go | 881 +++++++ .../integration/api_packages_rubygems_test.go | 353 +++ tests/integration/api_packages_swift_test.go | 479 ++++ .../api_packages_terraform_test.go | 302 +++ tests/integration/api_packages_test.go | 766 ++++++ .../integration/api_packages_vagrant_test.go | 172 ++ tests/integration/api_private_serv_test.go | 153 ++ tests/integration/api_project_test.go | 319 +++ tests/integration/api_public_only_test.go | 184 ++ tests/integration/api_pull_commits_test.go | 42 + tests/integration/api_pull_review_test.go | 611 +++++ tests/integration/api_pull_test.go | 662 +++++ .../api_releases_attachment_test.go | 70 + tests/integration/api_releases_test.go | 454 ++++ tests/integration/api_repo_archive_test.go | 112 + tests/integration/api_repo_avatar_test.go | 76 + tests/integration/api_repo_branch_test.go | 147 ++ .../integration/api_repo_collaborator_test.go | 149 ++ tests/integration/api_repo_compare_test.go | 229 ++ tests/integration/api_repo_delete_test.go | 54 + tests/integration/api_repo_edit_test.go | 534 ++++ .../integration/api_repo_file_create_test.go | 304 +++ .../integration/api_repo_file_delete_test.go | 165 ++ .../api_repo_file_diffpatch_test.go | 88 + tests/integration/api_repo_file_get_test.go | 49 + tests/integration/api_repo_file_helpers.go | 88 + .../integration/api_repo_file_update_test.go | 290 +++ .../integration/api_repo_files_change_test.go | 391 +++ tests/integration/api_repo_files_get_test.go | 172 ++ .../api_repo_get_contents_list_test.go | 172 ++ .../integration/api_repo_get_contents_test.go | 292 +++ tests/integration/api_repo_git_blobs_test.go | 79 + .../integration/api_repo_git_commits_test.go | 262 ++ tests/integration/api_repo_git_hook_test.go | 195 ++ tests/integration/api_repo_git_notes_test.go | 45 + tests/integration/api_repo_git_ref_test.go | 39 + tests/integration/api_repo_git_tags_test.go | 85 + tests/integration/api_repo_git_trees_test.go | 87 + tests/integration/api_repo_hook_test.go | 98 + tests/integration/api_repo_languages_test.go | 50 + tests/integration/api_repo_lfs_locks_test.go | 176 ++ .../integration/api_repo_lfs_migrate_test.go | 48 + tests/integration/api_repo_lfs_test.go | 495 ++++ tests/integration/api_repo_license_test.go | 80 + tests/integration/api_repo_raw_test.go | 40 + tests/integration/api_repo_secrets_test.go | 135 + tests/integration/api_repo_tags_test.go | 83 + tests/integration/api_repo_teams_test.go | 93 + tests/integration/api_repo_test.go | 745 ++++++ tests/integration/api_repo_topic_test.go | 215 ++ tests/integration/api_repo_variables_test.go | 149 ++ ...pi_repository_creation_token_scope_test.go | 55 + tests/integration/api_settings_test.go | 61 + tests/integration/api_team_test.go | 345 +++ tests/integration/api_team_user_test.go | 49 + tests/integration/api_token_self_test.go | 98 + tests/integration/api_token_test.go | 621 +++++ tests/integration/api_twofa_test.go | 111 + tests/integration/api_user_avatar_test.go | 72 + tests/integration/api_user_block_test.go | 238 ++ tests/integration/api_user_email_test.go | 148 ++ tests/integration/api_user_follow_test.go | 125 + tests/integration/api_user_heatmap_test.go | 38 + tests/integration/api_user_info_test.go | 67 + tests/integration/api_user_org_perm_test.go | 189 ++ tests/integration/api_user_orgs_test.go | 200 ++ .../integration/api_user_public_only_test.go | 177 ++ tests/integration/api_user_search_test.go | 153 ++ tests/integration/api_user_secrets_test.go | 96 + tests/integration/api_user_star_test.go | 246 ++ tests/integration/api_user_update_test.go | 25 + tests/integration/api_user_variables_test.go | 143 ++ tests/integration/api_user_visibility_test.go | 76 + tests/integration/api_user_watch_test.go | 119 + tests/integration/api_wiki_test.go | 250 ++ tests/integration/attachment_test.go | 334 +++ tests/integration/auth_ldap_test.go | 550 +++++ tests/integration/auth_oauth2_test.go | 637 +++++ tests/integration/avatar.png | Bin 0 -> 7787 bytes tests/integration/branches_test.go | 72 + .../integration/change_default_branch_test.go | 131 + tests/integration/cmd_keys_test.go | 61 + tests/integration/compare_test.go | 318 +++ tests/integration/config_instance_test.go | 126 + tests/integration/cors_test.go | 94 + tests/integration/create_no_session_test.go | 105 + tests/integration/db_collation_test.go | 120 + tests/integration/delete_user_test.go | 57 + tests/integration/deploy_token_test.go | 76 + tests/integration/download_test.go | 211 ++ tests/integration/dump_restore_test.go | 317 +++ tests/integration/editor_test.go | 488 ++++ tests/integration/empty_repo_test.go | 195 ++ .../ephemeral_actions_runner_deletion_test.go | 77 + tests/integration/explore_repos_test.go | 25 + tests/integration/explore_user_test.go | 44 + tests/integration/feed_repo_test.go | 74 + tests/integration/feed_user_test.go | 109 + tests/integration/git_general_test.go | 965 ++++++++ .../git_helper_for_declarative_test.go | 291 +++ tests/integration/git_lfs_ssh_test.go | 82 + tests/integration/git_misc_test.go | 244 ++ tests/integration/git_push_test.go | 256 ++ tests/integration/git_smart_http_test.go | 180 ++ tests/integration/git_ssh_redirect_test.go | 81 + tests/integration/goget_test.go | 141 ++ tests/integration/gpg_ssh_git_test.go | 429 ++++ tests/integration/heatmap_test.go | 56 + tests/integration/html_helper.go | 62 + tests/integration/incoming_email_test.go | 266 ++ tests/integration/integration_test.go | 338 +++ tests/integration/issue_label_scope_test.go | 34 + tests/integration/issue_test.go | 752 ++++++ tests/integration/issue_timetrack_test.go | 32 + tests/integration/lfs_getobject_test.go | 203 ++ tests/integration/lfs_local_endpoint_test.go | 112 + tests/integration/lfs_view_test.go | 166 ++ tests/integration/linguist_test.go | 281 +++ tests/integration/links_test.go | 250 ++ tests/integration/manual_merge_test.go | 104 + tests/integration/markup_external_test.go | 150 ++ tests/integration/migrate_test.go | 389 +++ .../migration-test/gitea-v1.6.4.mssql.sql.gz | Bin 0 -> 13260 bytes .../migration-test/gitea-v1.6.4.mysql.sql.gz | Bin 0 -> 9423 bytes .../gitea-v1.6.4.postgres.sql.gz | Bin 0 -> 17517 bytes .../gitea-v1.6.4.sqlite3.sql.gz | Bin 0 -> 3995 bytes .../migration-test/gitea-v1.7.0.mssql.sql.gz | Bin 0 -> 13369 bytes .../migration-test/gitea-v1.7.0.mysql.sql.gz | Bin 0 -> 9682 bytes .../gitea-v1.7.0.postgres.sql.gz | Bin 0 -> 17831 bytes .../gitea-v1.7.0.sqlite3.sql.gz | Bin 0 -> 8165 bytes .../migration-test/migration_test.go | 197 ++ tests/integration/mirror_pull_test.go | 178 ++ tests/integration/mirror_push_test.go | 193 ++ tests/integration/nonascii_branches_test.go | 209 ++ tests/integration/oauth_avatar_test.go | 96 + tests/integration/oauth_test.go | 1428 +++++++++++ tests/integration/org_count_test.go | 149 ++ tests/integration/org_profile_test.go | 132 + tests/integration/org_project_test.go | 62 + tests/integration/org_team_invite_test.go | 309 +++ tests/integration/org_test.go | 333 +++ tests/integration/org_worktime_test.go | 292 +++ tests/integration/packages_service_test.go | 118 + tests/integration/private-testing.key | 81 + tests/integration/privateactivity_test.go | 415 ++++ tests/integration/project_test.go | 394 +++ tests/integration/pull_comment_test.go | 108 + tests/integration/pull_commit_test.go | 40 + tests/integration/pull_compare_test.go | 172 ++ tests/integration/pull_create_test.go | 455 ++++ tests/integration/pull_diff_test.go | 132 + tests/integration/pull_merge_test.go | 1333 ++++++++++ tests/integration/pull_review_test.go | 406 +++ tests/integration/pull_status_test.go | 238 ++ tests/integration/pull_update_test.go | 318 +++ tests/integration/release_test.go | 282 +++ tests/integration/rename_branch_test.go | 105 + tests/integration/repo_activity_test.go | 79 + tests/integration/repo_archive_test.go | 58 + tests/integration/repo_branch_test.go | 250 ++ tests/integration/repo_commits_search_test.go | 44 + tests/integration/repo_commits_test.go | 245 ++ tests/integration/repo_fork_test.go | 130 + tests/integration/repo_generate_test.go | 92 + .../integration/repo_home_token_scope_test.go | 43 + tests/integration/repo_merge_upstream_test.go | 188 ++ tests/integration/repo_migrate_test.go | 44 + tests/integration/repo_search_test.go | 61 + tests/integration/repo_tag_test.go | 172 ++ tests/integration/repo_test.go | 675 +++++ tests/integration/repo_topic_test.go | 76 + tests/integration/repo_visibility_test.go | 59 + tests/integration/repo_watch_test.go | 25 + tests/integration/repo_webhook_test.go | 1864 ++++++++++++++ tests/integration/repofiles_change_test.go | 694 ++++++ tests/integration/session_test.go | 36 + tests/integration/setting_test.go | 112 + tests/integration/signin_test.go | 213 ++ tests/integration/signout_test.go | 45 + tests/integration/signup_test.go | 156 ++ tests/integration/ssh_key_test.go | 244 ++ tests/integration/timetracking_test.go | 78 + tests/integration/user_avatar_test.go | 70 + .../user_settings_oauth2_grant_test.go | 27 + tests/integration/user_settings_test.go | 488 ++++ tests/integration/user_test.go | 371 +++ tests/integration/version_test.go | 26 + tests/integration/view_test.go | 131 + tests/integration/webauthn_test.go | 38 + tests/integration/wiki_test.go | 110 + .../workflow_run_api_check_test.go | 266 ++ tests/mssql.ini.tmpl | 100 + tests/mysql.ini.tmpl | 106 + tests/pgsql.ini.tmpl | 116 + tests/sqlite.ini.tmpl | 109 + tests/test_utils.go | 135 + .../artifacts/26/1/1712166500347189545.chunk | 1 + .../artifacts/26/19/1712348022422036662.chunk | 1 + .../artifacts/26/20/1712348022423431524.chunk | 1 + .../artifacts/27/5/1730330775594233150.chunk | 1 + .../a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22 | 1 + tools/build-release.sh | 72 + tools/ci-tools.ts | 181 ++ .../unescaped-html-literal.test.ts | 85 + tools/eslint-rules/unescaped-html-literal.ts | 39 + tools/generate-codemirror-languages.ts | 84 + tools/generate-images.ts | 77 + tools/generate-svg-vscode-extensions.json | 570 +++++ tools/generate-svg.ts | 130 + tools/gocovmerge.go | 118 + tools/lint-go-all.go | 131 + tools/lint-shell.sh | 11 + tools/lint-templates-svg.ts | 25 + tools/playwright.sh | 9 + tools/shared.ts | 32 + tools/test-e2e.sh | 194 ++ tools/test-echo.go | 20 + tools/test-integration.sh | 31 + tools/watch.sh | 8 + tsconfig.json | 48 + types.d.ts | 43 + uv.lock | 479 ++++ vite.config.ts | 335 +++ vitest.config.ts | 43 + web_src/css/actions.css | 96 + web_src/css/admin.css | 53 + web_src/css/avatar.css | 125 + web_src/css/base.css | 882 +++++++ web_src/css/dashboard.css | 60 + web_src/css/devtest.css | 20 + web_src/css/easymde.css | 432 ++++ web_src/css/editor/combomarkdowneditor.css | 102 + web_src/css/explore.css | 13 + web_src/css/features/captcha.css | 32 + web_src/css/features/colorpicker.css | 65 + web_src/css/features/console.css | 364 +++ web_src/css/features/cropper.css | 6 + web_src/css/features/dropzone.css | 104 + web_src/css/features/expander.css | 92 + web_src/css/features/gitgraph.css | 165 ++ web_src/css/features/heatmap.css | 60 + web_src/css/features/imagediff.css | 112 + web_src/css/features/projects.css | 122 + web_src/css/font_i18n.css | 393 +++ web_src/css/helpers.css | 78 + web_src/css/home.css | 106 + web_src/css/index.css | 90 + web_src/css/install.css | 55 + web_src/css/markup/codeblock.css | 10 + web_src/css/markup/codepreview.css | 36 + web_src/css/markup/content.css | 513 ++++ web_src/css/markup/jupyter.css | 93 + web_src/css/modules/animations.css | 112 + web_src/css/modules/breadcrumb.css | 11 + web_src/css/modules/button.css | 358 +++ web_src/css/modules/card.css | 107 + web_src/css/modules/charescape.css | 55 + web_src/css/modules/checkbox.css | 206 ++ web_src/css/modules/chroma.css | 79 + web_src/css/modules/codeeditor.css | 552 +++++ web_src/css/modules/comment.css | 47 + web_src/css/modules/container.css | 39 + web_src/css/modules/dimmer.css | 27 + web_src/css/modules/divider.css | 46 + web_src/css/modules/dropdown.css | 939 +++++++ web_src/css/modules/flexcontainer.css | 33 + web_src/css/modules/form.css | 413 ++++ web_src/css/modules/grid.css | 226 ++ web_src/css/modules/header.css | 143 ++ web_src/css/modules/input.css | 167 ++ web_src/css/modules/label.css | 294 +++ web_src/css/modules/list.css | 161 ++ web_src/css/modules/menu.css | 594 +++++ web_src/css/modules/message.css | 100 + web_src/css/modules/modal.css | 305 +++ web_src/css/modules/navbar.css | 151 ++ web_src/css/modules/normalize.css | 204 ++ web_src/css/modules/search.css | 86 + web_src/css/modules/segment.css | 180 ++ web_src/css/modules/shortcut.css | 26 + web_src/css/modules/svg.css | 54 + web_src/css/modules/tab.css | 7 + web_src/css/modules/table.css | 272 ++ web_src/css/modules/tippy.css | 195 ++ web_src/css/modules/toast.css | 121 + web_src/css/modules/transition.css | 23 + web_src/css/org.css | 12 + web_src/css/repo.css | 1814 ++++++++++++++ web_src/css/repo/clone.css | 39 + web_src/css/repo/commit-sign.css | 65 + web_src/css/repo/file-view.css | 97 + web_src/css/repo/home-file-list.css | 99 + web_src/css/repo/home.css | 110 + web_src/css/repo/issue-card.css | 44 + web_src/css/repo/issue-label.css | 58 + web_src/css/repo/issue-list.css | 130 + web_src/css/repo/list-header.css | 44 + web_src/css/repo/packages.css | 43 + web_src/css/repo/reactions.css | 70 + web_src/css/repo/release-tag.css | 97 + web_src/css/repo/wiki.css | 57 + web_src/css/review.css | 197 ++ web_src/css/shared/flex-list.css | 124 + web_src/css/shared/settings.css | 53 + web_src/css/swagger-render.css | 13 + web_src/css/swagger-standalone.css | 60 + ...eme-gitea-auto-protanopia-deuteranopia.css | 8 + .../themes/theme-gitea-auto-tritanopia.css | 8 + web_src/css/themes/theme-gitea-auto.css | 7 + ...eme-gitea-dark-protanopia-deuteranopia.css | 19 + .../themes/theme-gitea-dark-tritanopia.css | 15 + web_src/css/themes/theme-gitea-dark.css | 337 +++ ...me-gitea-light-protanopia-deuteranopia.css | 19 + .../themes/theme-gitea-light-tritanopia.css | 15 + web_src/css/themes/theme-gitea-light.css | 313 +++ web_src/css/user.css | 87 + web_src/fomantic/_site/globals/site.variables | 61 + web_src/fomantic/build/components/api.js | 1155 +++++++++ web_src/fomantic/build/components/modal.js | 1209 +++++++++ web_src/fomantic/build/fomantic.js | 5 + web_src/fomantic/semantic.json | 27 + web_src/fomantic/theme.config.less | 85 + web_src/js/bootstrap.ts | 25 + .../js/components/ActionRunArtifacts.test.ts | 36 + web_src/js/components/ActionRunArtifacts.ts | 24 + web_src/js/components/ActionRunJobView.vue | 829 +++++++ .../js/components/ActionRunSummaryView.vue | 264 ++ web_src/js/components/ActionRunView.test.ts | 42 + web_src/js/components/ActionRunView.ts | 215 ++ web_src/js/components/ActionStatusIcon.vue | 32 + web_src/js/components/ActivityHeatmap.vue | 241 ++ web_src/js/components/ChartCanvas.vue | 71 + web_src/js/components/ContextPopup.vue | 49 + web_src/js/components/DashboardRepoList.vue | 617 +++++ web_src/js/components/DiffCommitSelector.vue | 331 +++ .../js/components/DiffFileExtensionFilter.vue | 168 ++ web_src/js/components/DiffFileTree.vue | 187 ++ .../js/components/DiffFileTreeItem.test.ts | 23 + web_src/js/components/DiffFileTreeItem.vue | 103 + .../js/components/PullRequestMergeForm.vue | 292 +++ web_src/js/components/RepoActionView.vue | 620 +++++ .../js/components/RepoActivityTopAuthors.vue | 84 + .../js/components/RepoBranchTagSelector.vue | 286 +++ web_src/js/components/RepoCodeFrequency.vue | 168 ++ web_src/js/components/RepoContributors.vue | 481 ++++ web_src/js/components/RepoFileSearch.vue | 233 ++ web_src/js/components/RepoRecentCommits.vue | 145 ++ web_src/js/components/SvgIcon.test.ts | 10 + web_src/js/components/SvgIcon.vue | 37 + web_src/js/components/ViewFileTree.vue | 38 + web_src/js/components/ViewFileTreeItem.vue | 122 + web_src/js/components/ViewFileTreeStore.ts | 63 + .../js/components/WorkflowGraph.utils.test.ts | 210 ++ web_src/js/components/WorkflowGraph.utils.ts | 538 ++++ web_src/js/components/WorkflowGraph.vue | 712 ++++++ web_src/js/external-render-frontend.ts | 73 + web_src/js/external-render-helper.test.ts | 25 + web_src/js/external-render-helper.ts | 93 + web_src/js/features/admin/common.ts | 320 +++ web_src/js/features/admin/config.test.ts | 48 + web_src/js/features/admin/config.ts | 219 ++ web_src/js/features/admin/selfcheck.ts | 31 + web_src/js/features/admin/users.ts | 37 + web_src/js/features/captcha.ts | 45 + web_src/js/features/citation.ts | 71 + web_src/js/features/code-frequency.ts | 21 + web_src/js/features/colorpicker.ts | 74 + .../js/features/common-actions-permissions.ts | 34 + web_src/js/features/common-button.test.ts | 27 + web_src/js/features/common-button.ts | 146 ++ web_src/js/features/common-form.ts | 22 + web_src/js/features/common-issue-list.test.ts | 16 + web_src/js/features/common-issue-list.ts | 58 + web_src/js/features/common-organization.ts | 16 + web_src/js/features/common-page.ts | 171 ++ .../js/features/comp/ComboMarkdownEditor.ts | 462 ++++ web_src/js/features/comp/ConfirmModal.ts | 45 + web_src/js/features/comp/Cropper.ts | 48 + .../js/features/comp/EasyMDEToolbarActions.ts | 161 ++ .../js/features/comp/EditorMarkdown.test.ts | 203 ++ web_src/js/features/comp/EditorMarkdown.ts | 238 ++ web_src/js/features/comp/EditorUpload.test.ts | 14 + web_src/js/features/comp/EditorUpload.ts | 171 ++ web_src/js/features/comp/LabelEdit.ts | 93 + web_src/js/features/comp/QuickSubmit.ts | 25 + web_src/js/features/comp/ReactionSelector.ts | 30 + .../js/features/comp/ScopedWorkflows.test.ts | 104 + web_src/js/features/comp/ScopedWorkflows.ts | 38 + web_src/js/features/comp/SearchRepoBox.ts | 18 + web_src/js/features/comp/SearchUserBox.ts | 30 + web_src/js/features/comp/TextExpander.ts | 138 ++ web_src/js/features/comp/WebHookEditor.ts | 42 + web_src/js/features/contributors.ts | 31 + web_src/js/features/copycontent.ts | 25 + web_src/js/features/dashboard.ts | 9 + web_src/js/features/dropzone.ts | 194 ++ web_src/js/features/emoji.ts | 38 + web_src/js/features/file-fold.ts | 19 + web_src/js/features/file-view.ts | 64 + web_src/js/features/heatmap.ts | 57 + web_src/js/features/imagediff.ts | 304 +++ web_src/js/features/install.ts | 102 + web_src/js/features/issue.ts | 41 + web_src/js/features/notification.ts | 110 + web_src/js/features/oauth2-settings.ts | 9 + web_src/js/features/org-team.ts | 17 + web_src/js/features/packages.test.ts | 18 + web_src/js/features/packages.ts | 24 + web_src/js/features/pull-view-file.ts | 78 + web_src/js/features/recent-commits.ts | 21 + web_src/js/features/ref-issue.ts | 75 + web_src/js/features/repo-actions.test.ts | 30 + web_src/js/features/repo-actions.ts | 135 + web_src/js/features/repo-branch.ts | 42 + web_src/js/features/repo-code.ts | 146 ++ web_src/js/features/repo-commit.ts | 52 + web_src/js/features/repo-common.test.ts | 22 + web_src/js/features/repo-common.ts | 163 ++ web_src/js/features/repo-diff-commit.ts | 56 + web_src/js/features/repo-diff-commitselect.ts | 10 + web_src/js/features/repo-diff-filetree.ts | 11 + web_src/js/features/repo-diff.ts | 266 ++ web_src/js/features/repo-editor.ts | 227 ++ web_src/js/features/repo-findfile.test.ts | 34 + web_src/js/features/repo-findfile.ts | 81 + web_src/js/features/repo-graph.ts | 85 + web_src/js/features/repo-home.ts | 147 ++ web_src/js/features/repo-issue-content.ts | 158 ++ web_src/js/features/repo-issue-edit.ts | 158 ++ web_src/js/features/repo-issue-list.ts | 201 ++ web_src/js/features/repo-issue-pull.ts | 73 + .../repo-issue-sidebar-combolist.test.ts | 61 + .../features/repo-issue-sidebar-combolist.ts | 214 ++ web_src/js/features/repo-issue-sidebar.md | 47 + web_src/js/features/repo-issue-sidebar.ts | 119 + web_src/js/features/repo-issue.ts | 518 ++++ web_src/js/features/repo-legacy.ts | 76 + web_src/js/features/repo-migrate.ts | 61 + web_src/js/features/repo-migration.ts | 80 + web_src/js/features/repo-milestone.ts | 9 + web_src/js/features/repo-new.ts | 97 + web_src/js/features/repo-projects.ts | 188 ++ web_src/js/features/repo-release.test.ts | 9 + web_src/js/features/repo-release.ts | 152 ++ web_src/js/features/repo-search.ts | 12 + .../features/repo-settings-branches.test.ts | 48 + web_src/js/features/repo-settings-branches.ts | 32 + web_src/js/features/repo-settings.ts | 147 ++ web_src/js/features/repo-switcher.ts | 39 + web_src/js/features/repo-unicode-escape.ts | 28 + web_src/js/features/repo-view-file-tree.ts | 41 + web_src/js/features/repo-watch.ts | 18 + web_src/js/features/repo-wiki.ts | 79 + web_src/js/features/sshkey-helper.ts | 10 + web_src/js/features/stopwatch.ts | 114 + web_src/js/features/tablesort.ts | 22 + web_src/js/features/tribute.ts | 60 + web_src/js/features/user-auth-webauthn.ts | 271 ++ web_src/js/features/user-auth.ts | 27 + web_src/js/features/user-settings.ts | 19 + web_src/js/globals.d.ts | 85 + web_src/js/globals.ts | 5 + web_src/js/iife.ts | 11 + web_src/js/index.ts | 179 ++ web_src/js/markup/anchors.ts | 83 + web_src/js/markup/codecopy.ts | 25 + web_src/js/markup/common.ts | 10 + web_src/js/markup/content.ts | 27 + web_src/js/markup/html2markdown.test.ts | 24 + web_src/js/markup/html2markdown.ts | 109 + web_src/js/markup/math.ts | 48 + web_src/js/markup/mermaid.test.ts | 59 + web_src/js/markup/mermaid.ts | 250 ++ web_src/js/markup/render-iframe.test.ts | 35 + web_src/js/markup/render-iframe.ts | 94 + web_src/js/markup/tasklist.test.ts | 9 + web_src/js/markup/tasklist.ts | 100 + web_src/js/modules/action-status-icon.test.ts | 9 + web_src/js/modules/action-status-icon.ts | 37 + web_src/js/modules/are-you-sure.test.ts | 98 + web_src/js/modules/are-you-sure.ts | 57 + web_src/js/modules/clipboard.ts | 90 + .../js/modules/codeeditor/command-palette.ts | 216 ++ web_src/js/modules/codeeditor/context-menu.ts | 249 ++ web_src/js/modules/codeeditor/linter.ts | 39 + web_src/js/modules/codeeditor/main.test.ts | 54 + web_src/js/modules/codeeditor/main.ts | 371 +++ web_src/js/modules/codeeditor/utils.test.ts | 10 + web_src/js/modules/codeeditor/utils.ts | 119 + web_src/js/modules/devtest.ts | 114 + web_src/js/modules/diff-file.test.ts | 140 ++ web_src/js/modules/diff-file.ts | 236 ++ web_src/js/modules/errors.test.ts | 49 + web_src/js/modules/errors.ts | 88 + web_src/js/modules/favicon-status.test.ts | 29 + web_src/js/modules/favicon-status.ts | 90 + web_src/js/modules/fetch-action.test.ts | 61 + web_src/js/modules/fetch-action.ts | 446 ++++ web_src/js/modules/fetch.test.ts | 10 + web_src/js/modules/fetch.ts | 33 + web_src/js/modules/fomantic.ts | 28 + web_src/js/modules/fomantic/aria.md | 117 + web_src/js/modules/fomantic/base.ts | 47 + web_src/js/modules/fomantic/dimmer.ts | 31 + web_src/js/modules/fomantic/dropdown.test.ts | 93 + web_src/js/modules/fomantic/dropdown.ts | 368 +++ web_src/js/modules/fomantic/modal.ts | 84 + web_src/js/modules/fomantic/tab.ts | 18 + web_src/js/modules/fomantic/transition.ts | 52 + web_src/js/modules/gitea-actions.ts | 91 + web_src/js/modules/i18n.test.ts | 17 + web_src/js/modules/i18n.ts | 22 + web_src/js/modules/init.ts | 26 + web_src/js/modules/observer.ts | 117 + web_src/js/modules/search.ts | 116 + web_src/js/modules/shortcut.ts | 76 + web_src/js/modules/sortable.ts | 23 + web_src/js/modules/tippy.test.ts | 16 + web_src/js/modules/tippy.ts | 285 +++ web_src/js/modules/toast.test.ts | 21 + web_src/js/modules/toast.ts | 113 + web_src/js/modules/user-settings.ts | 73 + web_src/js/modules/worker.test.ts | 69 + web_src/js/modules/worker.ts | 130 + web_src/js/render/ansi.test.ts | 82 + web_src/js/render/ansi.ts | 273 ++ web_src/js/render/plugin.ts | 21 + .../js/render/plugins/frontend-asciicast.ts | 23 + .../plugins/frontend-openapi-swagger.ts | 15 + .../js/render/plugins/frontend-viewer-3d.ts | 36 + .../js/render/plugins/inplace-pdf-viewer.ts | 19 + web_src/js/render/swagger.ts | 54 + web_src/js/svg.test.ts | 14 + web_src/js/svg.ts | 231 ++ web_src/js/swagger.ts | 12 + web_src/js/types.ts | 116 + web_src/js/user-events.sharedworker.ts | 243 ++ web_src/js/utils.test.ts | 142 ++ web_src/js/utils.ts | 212 ++ web_src/js/utils/color.test.ts | 24 + web_src/js/utils/color.ts | 38 + web_src/js/utils/dom.test.ts | 72 + web_src/js/utils/dom.ts | 429 ++++ web_src/js/utils/func.test.ts | 74 + web_src/js/utils/func.ts | 82 + web_src/js/utils/glob.test.ts | 126 + web_src/js/utils/glob.test.txt | 44 + web_src/js/utils/glob.ts | 127 + web_src/js/utils/html.test.ts | 8 + web_src/js/utils/html.ts | 32 + web_src/js/utils/image.test.ts | 30 + web_src/js/utils/image.ts | 58 + web_src/js/utils/match.test.ts | 76 + web_src/js/utils/match.ts | 82 + web_src/js/utils/string.test.ts | 13 + web_src/js/utils/string.ts | 5 + web_src/js/utils/testhelper.ts | 37 + web_src/js/utils/time.test.ts | 15 + web_src/js/utils/time.ts | 90 + web_src/js/utils/url.test.ts | 54 + web_src/js/utils/url.ts | 46 + web_src/js/vitest.setup.ts | 16 + web_src/js/webcomponents/README.md | 11 + web_src/js/webcomponents/index.ts | 14 + web_src/js/webcomponents/overflow-menu.ts | 265 ++ web_src/js/webcomponents/polyfill.test.ts | 7 + web_src/js/webcomponents/polyfills.ts | 15 + .../js/webcomponents/relative-time.test.ts | 178 ++ web_src/js/webcomponents/relative-time.ts | 508 ++++ web_src/svg/fontawesome-openid.svg | 3 + web_src/svg/fontawesome-save.svg | 1 + web_src/svg/fontawesome-send.svg | 1 + web_src/svg/fontawesome-windows.svg | 3 + web_src/svg/gitea-alpine.svg | 2 + web_src/svg/gitea-arch.svg | 1 + web_src/svg/gitea-azuread.svg | 1 + web_src/svg/gitea-azureadv2.svg | 1 + web_src/svg/gitea-bitbucket.svg | 1 + web_src/svg/gitea-cargo.svg | 3 + web_src/svg/gitea-chef.svg | 20 + web_src/svg/gitea-codebase.svg | 13 + web_src/svg/gitea-codecommit.svg | 6 + web_src/svg/gitea-colorblind-blueyellow.svg | 13 + web_src/svg/gitea-colorblind-redgreen.svg | 13 + web_src/svg/gitea-composer.svg | 50 + web_src/svg/gitea-conan.svg | 9 + web_src/svg/gitea-conda.svg | 20 + web_src/svg/gitea-cran.svg | 15 + web_src/svg/gitea-debian.svg | 9 + web_src/svg/gitea-discord.svg | 1 + web_src/svg/gitea-double-chevron-left.svg | 1 + web_src/svg/gitea-double-chevron-right.svg | 1 + web_src/svg/gitea-dropbox.svg | 1 + web_src/svg/gitea-eclipse.svg | 1 + web_src/svg/gitea-empty-checkbox.svg | 1 + web_src/svg/gitea-exclamation.svg | 1 + web_src/svg/gitea-facebook.svg | 1 + web_src/svg/gitea-feishu.svg | 1 + web_src/svg/gitea-git.svg | 1 + web_src/svg/gitea-gitbucket.svg | 39 + web_src/svg/gitea-gitlab.svg | 1 + web_src/svg/gitea-go.svg | 10 + web_src/svg/gitea-gogs.svg | 60 + web_src/svg/gitea-google.svg | 1 + web_src/svg/gitea-helm.svg | 3 + web_src/svg/gitea-jetbrains.svg | 62 + web_src/svg/gitea-join.svg | 1 + web_src/svg/gitea-lock-cog.svg | 1 + web_src/svg/gitea-lock.svg | 1 + web_src/svg/gitea-mastodon.svg | 1 + web_src/svg/gitea-matrix.svg | 14 + web_src/svg/gitea-maven.svg | 1 + web_src/svg/gitea-microsoftonline.svg | 1 + web_src/svg/gitea-nextcloud.svg | 1 + web_src/svg/gitea-npm.svg | 7 + web_src/svg/gitea-nuget.svg | 15 + web_src/svg/gitea-onedev.svg | 42 + web_src/svg/gitea-openid.svg | 1 + web_src/svg/gitea-pub.svg | 19 + web_src/svg/gitea-python.svg | 19 + web_src/svg/gitea-rpm.svg | 9 + web_src/svg/gitea-rubygems.svg | 3 + web_src/svg/gitea-running.svg | 5 + web_src/svg/gitea-split.svg | 1 + web_src/svg/gitea-swift.svg | 5 + web_src/svg/gitea-terraform.svg | 2 + web_src/svg/gitea-twitter.svg | 1 + web_src/svg/gitea-unlock.svg | 1 + web_src/svg/gitea-vagrant.svg | 6 + web_src/svg/gitea-vscodium.svg | 1 + web_src/svg/gitea-whitespace.svg | 1 + web_src/svg/gitea-yandex.svg | 1 + web_src/svg/material-folder-generic.svg | 1 + web_src/svg/material-folder-symlink.svg | 1 + web_src/svg/material-invert-colors.svg | 1 + web_src/svg/material-palette.svg | 1 + 6280 files changed, 624193 insertions(+) create mode 100644 .air.toml create mode 100644 .changelog.yml create mode 100644 .devcontainer/devcontainer.json create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .envrc create mode 100644 .gitattributes create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yaml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yaml create mode 100644 .github/actionlint.yaml create mode 100644 .github/actions/docker-dryrun/action.yml create mode 100644 .github/actions/free-disk-space/action.yml create mode 100644 .github/actions/go-cache/action.yml create mode 100644 .github/actions/go-setup/action.yml create mode 100644 .github/actions/node-setup/action.yml create mode 100644 .github/actions/pgsql-shard/action.yml create mode 100644 .github/actions/python-setup/action.yml create mode 100644 .github/labeler.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/cache-prune.yml create mode 100644 .github/workflows/cache-seeder.yml create mode 100644 .github/workflows/cron-licenses.yml create mode 100644 .github/workflows/cron-renovate.yml create mode 100644 .github/workflows/cron-translations.yml create mode 100644 .github/workflows/files-changed.yml create mode 100644 .github/workflows/giteabot-backport.yml create mode 100644 .github/workflows/giteabot.yml create mode 100644 .github/workflows/pull-compliance.yml create mode 100644 .github/workflows/pull-db-tests.yml create mode 100644 .github/workflows/pull-docker-dryrun.yml create mode 100644 .github/workflows/pull-e2e-tests.yml create mode 100644 .github/workflows/pull-labeler.yml create mode 100644 .github/workflows/release-nightly-snapcraft.yml create mode 100644 .github/workflows/release-nightly.yml create mode 100644 .github/workflows/release-tag-rc.yml create mode 100644 .github/workflows/release-tag-version.yml create mode 100644 .github/zizmor.yml create mode 100644 .gitignore create mode 100644 .golangci.yml create mode 100644 .ignore create mode 100644 .mailmap create mode 100644 .markdownlint.yaml create mode 100644 .shellcheckrc create mode 100644 .spectral.yaml create mode 100644 .yamllint.yaml create mode 100644 AGENTS.md create mode 100644 BSDmakefile create mode 100644 CLAUDE.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 DCO create mode 100644 Dockerfile create mode 100644 Dockerfile.rootless create mode 100644 LICENSE create mode 100644 MAINTAINERS create mode 100644 Makefile create mode 100644 README.md create mode 100644 README.zh-cn.md create mode 100644 README.zh-tw.md create mode 100644 SECURITY.md create mode 100644 assets/codemirror-languages.json create mode 100644 assets/favicon.svg create mode 100644 assets/logo.svg create mode 100644 assets/misspellings.csv create mode 100644 build/generate-bindata.go create mode 100644 build/generate-emoji.go create mode 100644 build/generate-gitignores.go create mode 100644 build/generate-go-licenses.go create mode 100644 build/generate-openapi.go create mode 100644 build/openapi3gen/convert.go create mode 100644 build/openapi3gen/convert_test.go create mode 100644 build/openapi3gen/enumscan.go create mode 100644 build/openapi3gen/enumscan_test.go create mode 100755 build/update-locales.sh create mode 100644 cmd/actions.go create mode 100644 cmd/admin.go create mode 100644 cmd/admin_auth.go create mode 100644 cmd/admin_auth_ldap.go create mode 100644 cmd/admin_auth_ldap_test.go create mode 100644 cmd/admin_auth_oauth.go create mode 100644 cmd/admin_auth_oauth_test.go create mode 100644 cmd/admin_auth_smtp.go create mode 100644 cmd/admin_auth_smtp_test.go create mode 100644 cmd/admin_regenerate.go create mode 100644 cmd/admin_user.go create mode 100644 cmd/admin_user_change_password.go create mode 100644 cmd/admin_user_change_password_test.go create mode 100644 cmd/admin_user_create.go create mode 100644 cmd/admin_user_create_test.go create mode 100644 cmd/admin_user_delete.go create mode 100644 cmd/admin_user_delete_test.go create mode 100644 cmd/admin_user_disable_2fa.go create mode 100644 cmd/admin_user_disable_2fa_test.go create mode 100644 cmd/admin_user_generate_access_token.go create mode 100644 cmd/admin_user_list.go create mode 100644 cmd/admin_user_must_change_password.go create mode 100644 cmd/admin_user_must_change_password_test.go create mode 100644 cmd/audit.go create mode 100644 cmd/cert.go create mode 100644 cmd/cert_test.go create mode 100644 cmd/cmdtest/cmd_test.go create mode 100644 cmd/config.go create mode 100644 cmd/config_test.go create mode 100644 cmd/docs.go create mode 100644 cmd/doctor.go create mode 100644 cmd/doctor_convert.go create mode 100644 cmd/doctor_test.go create mode 100644 cmd/dump.go create mode 100644 cmd/dump_repo.go create mode 100644 cmd/embedded.go create mode 100644 cmd/generate.go create mode 100644 cmd/helper.go create mode 100644 cmd/hook.go create mode 100644 cmd/hook_test.go create mode 100644 cmd/keys.go create mode 100644 cmd/mailer.go create mode 100644 cmd/main.go create mode 100644 cmd/main_test.go create mode 100644 cmd/manager.go create mode 100644 cmd/manager_logging.go create mode 100644 cmd/migrate.go create mode 100644 cmd/migrate_storage.go create mode 100644 cmd/migrate_storage_test.go create mode 100644 cmd/restore_repo.go create mode 100644 cmd/serv.go create mode 100644 cmd/serv_test.go create mode 100644 cmd/web.go create mode 100644 cmd/web_acme.go create mode 100644 cmd/web_graceful.go create mode 100644 cmd/web_https.go create mode 100644 cmd/web_https_test.go create mode 100644 contrib/README create mode 100644 contrib/development/README.md create mode 100644 contrib/development/vscode/launch.json create mode 100644 contrib/development/vscode/settings.json create mode 100644 contrib/development/vscode/tasks.json create mode 100755 contrib/fhs-compliant-script/gitea create mode 100644 contrib/grafana-monitoring-mixin/.gitignore create mode 100644 contrib/grafana-monitoring-mixin/Makefile create mode 100644 contrib/grafana-monitoring-mixin/README.md create mode 100644 contrib/grafana-monitoring-mixin/config.libsonnet create mode 100644 contrib/grafana-monitoring-mixin/dashboards/dashboards.libsonnet create mode 100644 contrib/grafana-monitoring-mixin/dashboards/overview.libsonnet create mode 100644 contrib/grafana-monitoring-mixin/jsonnetfile.json create mode 100644 contrib/grafana-monitoring-mixin/jsonnetfile.lock.json create mode 100644 contrib/grafana-monitoring-mixin/lib/alerts.jsonnet create mode 100644 contrib/grafana-monitoring-mixin/lib/dashboards.jsonnet create mode 100644 contrib/grafana-monitoring-mixin/lib/rules.jsonnet create mode 100644 contrib/grafana-monitoring-mixin/mixin.libsonnet create mode 100644 contrib/options/label/Advanced create mode 100644 contrib/sample-page/privacy.html.sample create mode 100644 contrib/sample-page/tos.html.sample create mode 100644 contrib/service/freebsd/gitea create mode 100644 contrib/service/gentoo/gitea create mode 100644 contrib/service/launchd/io.gitea.web.plist create mode 100755 contrib/service/openbsd/gitea create mode 100644 contrib/service/openwrt/gitea create mode 100644 contrib/service/sunos/gitea.xml create mode 100644 contrib/service/supervisor/gitea create mode 100644 contrib/service/systemd/gitea.service create mode 100644 contrib/service/sysvinit/gitea create mode 100755 contrib/upgrade.sh create mode 100644 crowdin.yml create mode 100644 docker/README.md create mode 100644 docker/manifest.rootless.tmpl create mode 100644 docker/manifest.tmpl create mode 100644 docker/root/etc/nsswitch.conf create mode 100755 docker/root/etc/s6/.s6-svscan/finish create mode 100755 docker/root/etc/s6/gitea/finish create mode 100755 docker/root/etc/s6/gitea/run create mode 100755 docker/root/etc/s6/gitea/setup create mode 100755 docker/root/etc/s6/openssh/finish create mode 100755 docker/root/etc/s6/openssh/run create mode 100755 docker/root/etc/s6/openssh/setup create mode 100644 docker/root/etc/templates/app.ini create mode 100644 docker/root/etc/templates/sshd_config create mode 100755 docker/root/usr/bin/entrypoint create mode 100644 docker/root/usr/local/bin/environment-to-ini create mode 100644 docker/root/usr/local/bin/gitea create mode 100644 docker/rootless/etc/templates/app.ini create mode 100755 docker/rootless/usr/local/bin/docker-entrypoint.sh create mode 100755 docker/rootless/usr/local/bin/docker-setup.sh create mode 100644 docker/rootless/usr/local/bin/environment-to-ini create mode 100644 docker/rootless/usr/local/bin/gitea create mode 100644 docs/build-setup.md create mode 100644 docs/build-source.md create mode 100644 docs/community-governance.md create mode 100644 docs/development.md create mode 100644 docs/guidelines-backend.md create mode 100644 docs/guidelines-frontend.md create mode 100644 docs/guidelines-refactoring.md create mode 100644 docs/release-management.md create mode 100644 docs/testing.md create mode 100644 eslint.config.ts create mode 100644 eslint.json.config.ts create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 main_timezones.go create mode 100644 modelmigration/base/db.go create mode 100644 modelmigration/base/db_test.go create mode 100644 modelmigration/base/hash.go create mode 100644 modelmigration/base/shared.go create mode 100644 modelmigration/fixtures/Test_AddCombinedIndexToIssueUser/issue_user.yml create mode 100644 modelmigration/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml create mode 100644 modelmigration/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/expected_webhook.yml create mode 100644 modelmigration/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/hook_task.yml create mode 100644 modelmigration/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/webhook.yml create mode 100644 modelmigration/fixtures/Test_AddIssueResourceIndexTable/issue.yml create mode 100644 modelmigration/fixtures/Test_AddRepoIDForAttachment/attachment.yml create mode 100644 modelmigration/fixtures/Test_AddRepoIDForAttachment/issue.yml create mode 100644 modelmigration/fixtures/Test_AddRepoIDForAttachment/release.yml create mode 100644 modelmigration/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml create mode 100644 modelmigration/fixtures/Test_CheckProjectColumnsConsistency/project.yml create mode 100644 modelmigration/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml create mode 100644 modelmigration/fixtures/Test_DeleteOrphanedIssueLabels/issue_label.yml create mode 100644 modelmigration/fixtures/Test_DeleteOrphanedIssueLabels/label.yml create mode 100644 modelmigration/fixtures/Test_FixCommitStatusTargetURLToUseRunAndJobID/action_run.yml create mode 100644 modelmigration/fixtures/Test_FixCommitStatusTargetURLToUseRunAndJobID/action_run_job.yml create mode 100644 modelmigration/fixtures/Test_FixCommitStatusTargetURLToUseRunAndJobID/commit_status.yml create mode 100644 modelmigration/fixtures/Test_FixCommitStatusTargetURLToUseRunAndJobID/commit_status_summary.yml create mode 100644 modelmigration/fixtures/Test_FixCommitStatusTargetURLToUseRunAndJobID/repository.yml create mode 100644 modelmigration/fixtures/Test_RemigrateU2FCredentials/expected_webauthn_credential.yml create mode 100644 modelmigration/fixtures/Test_RemigrateU2FCredentials/u2f_registration.yml create mode 100644 modelmigration/fixtures/Test_RemigrateU2FCredentials/webauthn_credential.yml create mode 100644 modelmigration/fixtures/Test_RemoveInvalidLabels/comment.yml create mode 100644 modelmigration/fixtures/Test_RemoveInvalidLabels/issue.yml create mode 100644 modelmigration/fixtures/Test_RemoveInvalidLabels/issue_label.yml create mode 100644 modelmigration/fixtures/Test_RemoveInvalidLabels/label.yml create mode 100644 modelmigration/fixtures/Test_RemoveInvalidLabels/repository.yml create mode 100644 modelmigration/fixtures/Test_RepositoryFormat/comment.yml create mode 100644 modelmigration/fixtures/Test_RepositoryFormat/commit_status.yml create mode 100644 modelmigration/fixtures/Test_RepositoryFormat/pull_request.yml create mode 100644 modelmigration/fixtures/Test_RepositoryFormat/release.yml create mode 100644 modelmigration/fixtures/Test_RepositoryFormat/repo_archiver.yml create mode 100644 modelmigration/fixtures/Test_RepositoryFormat/repo_indexer_status.yml create mode 100644 modelmigration/fixtures/Test_RepositoryFormat/repository.yml create mode 100644 modelmigration/fixtures/Test_RepositoryFormat/review_state.yml create mode 100644 modelmigration/fixtures/Test_StoreWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml create mode 100644 modelmigration/fixtures/Test_StoreWebauthnCredentialIDAsBytes/webauthn_credential.yml create mode 100644 modelmigration/fixtures/Test_UnwrapLDAPSourceCfg/login_source.yml create mode 100644 modelmigration/fixtures/Test_UpdateBadgeColName/badge.yml create mode 100644 modelmigration/fixtures/Test_UpdateOpenMilestoneCounts/expected_milestone.yml create mode 100644 modelmigration/fixtures/Test_UpdateOpenMilestoneCounts/issue.yml create mode 100644 modelmigration/fixtures/Test_UpdateOpenMilestoneCounts/milestone.yml create mode 100644 modelmigration/migrations.go create mode 100644 modelmigration/migrations_test.go create mode 100644 modelmigration/migrationtest/tests.go create mode 100644 modelmigration/v1_10/v100.go create mode 100644 modelmigration/v1_10/v101.go create mode 100644 modelmigration/v1_10/v88.go create mode 100644 modelmigration/v1_10/v89.go create mode 100644 modelmigration/v1_10/v90.go create mode 100644 modelmigration/v1_10/v91.go create mode 100644 modelmigration/v1_10/v92.go create mode 100644 modelmigration/v1_10/v93.go create mode 100644 modelmigration/v1_10/v94.go create mode 100644 modelmigration/v1_10/v95.go create mode 100644 modelmigration/v1_10/v96.go create mode 100644 modelmigration/v1_10/v97.go create mode 100644 modelmigration/v1_10/v98.go create mode 100644 modelmigration/v1_10/v99.go create mode 100644 modelmigration/v1_11/v102.go create mode 100644 modelmigration/v1_11/v103.go create mode 100644 modelmigration/v1_11/v104.go create mode 100644 modelmigration/v1_11/v105.go create mode 100644 modelmigration/v1_11/v106.go create mode 100644 modelmigration/v1_11/v107.go create mode 100644 modelmigration/v1_11/v108.go create mode 100644 modelmigration/v1_11/v109.go create mode 100644 modelmigration/v1_11/v110.go create mode 100644 modelmigration/v1_11/v111.go create mode 100644 modelmigration/v1_11/v112.go create mode 100644 modelmigration/v1_11/v113.go create mode 100644 modelmigration/v1_11/v114.go create mode 100644 modelmigration/v1_11/v115.go create mode 100644 modelmigration/v1_11/v116.go create mode 100644 modelmigration/v1_12/v117.go create mode 100644 modelmigration/v1_12/v118.go create mode 100644 modelmigration/v1_12/v119.go create mode 100644 modelmigration/v1_12/v120.go create mode 100644 modelmigration/v1_12/v121.go create mode 100644 modelmigration/v1_12/v122.go create mode 100644 modelmigration/v1_12/v123.go create mode 100644 modelmigration/v1_12/v124.go create mode 100644 modelmigration/v1_12/v125.go create mode 100644 modelmigration/v1_12/v126.go create mode 100644 modelmigration/v1_12/v127.go create mode 100644 modelmigration/v1_12/v128.go create mode 100644 modelmigration/v1_12/v129.go create mode 100644 modelmigration/v1_12/v130.go create mode 100644 modelmigration/v1_12/v131.go create mode 100644 modelmigration/v1_12/v132.go create mode 100644 modelmigration/v1_12/v133.go create mode 100644 modelmigration/v1_12/v134.go create mode 100644 modelmigration/v1_12/v135.go create mode 100644 modelmigration/v1_12/v136.go create mode 100644 modelmigration/v1_12/v137.go create mode 100644 modelmigration/v1_12/v138.go create mode 100644 modelmigration/v1_12/v139.go create mode 100644 modelmigration/v1_13/v140.go create mode 100644 modelmigration/v1_13/v141.go create mode 100644 modelmigration/v1_13/v142.go create mode 100644 modelmigration/v1_13/v143.go create mode 100644 modelmigration/v1_13/v144.go create mode 100644 modelmigration/v1_13/v145.go create mode 100644 modelmigration/v1_13/v146.go create mode 100644 modelmigration/v1_13/v147.go create mode 100644 modelmigration/v1_13/v148.go create mode 100644 modelmigration/v1_13/v149.go create mode 100644 modelmigration/v1_13/v150.go create mode 100644 modelmigration/v1_13/v151.go create mode 100644 modelmigration/v1_13/v152.go create mode 100644 modelmigration/v1_13/v153.go create mode 100644 modelmigration/v1_13/v154.go create mode 100644 modelmigration/v1_14/main_test.go create mode 100644 modelmigration/v1_14/v155.go create mode 100644 modelmigration/v1_14/v156.go create mode 100644 modelmigration/v1_14/v157.go create mode 100644 modelmigration/v1_14/v158.go create mode 100644 modelmigration/v1_14/v159.go create mode 100644 modelmigration/v1_14/v160.go create mode 100644 modelmigration/v1_14/v161.go create mode 100644 modelmigration/v1_14/v162.go create mode 100644 modelmigration/v1_14/v163.go create mode 100644 modelmigration/v1_14/v164.go create mode 100644 modelmigration/v1_14/v165.go create mode 100644 modelmigration/v1_14/v166.go create mode 100644 modelmigration/v1_14/v167.go create mode 100644 modelmigration/v1_14/v168.go create mode 100644 modelmigration/v1_14/v169.go create mode 100644 modelmigration/v1_14/v170.go create mode 100644 modelmigration/v1_14/v171.go create mode 100644 modelmigration/v1_14/v172.go create mode 100644 modelmigration/v1_14/v173.go create mode 100644 modelmigration/v1_14/v174.go create mode 100644 modelmigration/v1_14/v175.go create mode 100644 modelmigration/v1_14/v176.go create mode 100644 modelmigration/v1_14/v176_test.go create mode 100644 modelmigration/v1_14/v177.go create mode 100644 modelmigration/v1_14/v177_test.go create mode 100644 modelmigration/v1_15/main_test.go create mode 100644 modelmigration/v1_15/v178.go create mode 100644 modelmigration/v1_15/v179.go create mode 100644 modelmigration/v1_15/v180.go create mode 100644 modelmigration/v1_15/v181.go create mode 100644 modelmigration/v1_15/v181_test.go create mode 100644 modelmigration/v1_15/v182.go create mode 100644 modelmigration/v1_15/v182_test.go create mode 100644 modelmigration/v1_15/v183.go create mode 100644 modelmigration/v1_15/v184.go create mode 100644 modelmigration/v1_15/v185.go create mode 100644 modelmigration/v1_15/v186.go create mode 100644 modelmigration/v1_15/v187.go create mode 100644 modelmigration/v1_15/v188.go create mode 100644 modelmigration/v1_16/main_test.go create mode 100644 modelmigration/v1_16/v189.go create mode 100644 modelmigration/v1_16/v189_test.go create mode 100644 modelmigration/v1_16/v190.go create mode 100644 modelmigration/v1_16/v191.go create mode 100644 modelmigration/v1_16/v192.go create mode 100644 modelmigration/v1_16/v193.go create mode 100644 modelmigration/v1_16/v193_test.go create mode 100644 modelmigration/v1_16/v194.go create mode 100644 modelmigration/v1_16/v195.go create mode 100644 modelmigration/v1_16/v195_test.go create mode 100644 modelmigration/v1_16/v196.go create mode 100644 modelmigration/v1_16/v197.go create mode 100644 modelmigration/v1_16/v198.go create mode 100644 modelmigration/v1_16/v199.go create mode 100644 modelmigration/v1_16/v200.go create mode 100644 modelmigration/v1_16/v201.go create mode 100644 modelmigration/v1_16/v202.go create mode 100644 modelmigration/v1_16/v203.go create mode 100644 modelmigration/v1_16/v204.go create mode 100644 modelmigration/v1_16/v205.go create mode 100644 modelmigration/v1_16/v206.go create mode 100644 modelmigration/v1_16/v207.go create mode 100644 modelmigration/v1_16/v208.go create mode 100644 modelmigration/v1_16/v209.go create mode 100644 modelmigration/v1_16/v210.go create mode 100644 modelmigration/v1_16/v210_test.go create mode 100644 modelmigration/v1_17/main_test.go create mode 100644 modelmigration/v1_17/v211.go create mode 100644 modelmigration/v1_17/v212.go create mode 100644 modelmigration/v1_17/v213.go create mode 100644 modelmigration/v1_17/v214.go create mode 100644 modelmigration/v1_17/v215.go create mode 100644 modelmigration/v1_17/v216.go create mode 100644 modelmigration/v1_17/v217.go create mode 100644 modelmigration/v1_17/v218.go create mode 100644 modelmigration/v1_17/v219.go create mode 100644 modelmigration/v1_17/v220.go create mode 100644 modelmigration/v1_17/v221.go create mode 100644 modelmigration/v1_17/v221_test.go create mode 100644 modelmigration/v1_17/v222.go create mode 100644 modelmigration/v1_17/v223.go create mode 100644 modelmigration/v1_18/main_test.go create mode 100644 modelmigration/v1_18/v224.go create mode 100644 modelmigration/v1_18/v225.go create mode 100644 modelmigration/v1_18/v226.go create mode 100644 modelmigration/v1_18/v227.go create mode 100644 modelmigration/v1_18/v228.go create mode 100644 modelmigration/v1_18/v229.go create mode 100644 modelmigration/v1_18/v229_test.go create mode 100644 modelmigration/v1_18/v230.go create mode 100644 modelmigration/v1_18/v230_test.go create mode 100644 modelmigration/v1_19/main_test.go create mode 100644 modelmigration/v1_19/v231.go create mode 100644 modelmigration/v1_19/v232.go create mode 100644 modelmigration/v1_19/v233.go create mode 100644 modelmigration/v1_19/v233_test.go create mode 100644 modelmigration/v1_19/v234.go create mode 100644 modelmigration/v1_19/v235.go create mode 100644 modelmigration/v1_19/v236.go create mode 100644 modelmigration/v1_19/v237.go create mode 100644 modelmigration/v1_19/v238.go create mode 100644 modelmigration/v1_19/v239.go create mode 100644 modelmigration/v1_19/v240.go create mode 100644 modelmigration/v1_19/v241.go create mode 100644 modelmigration/v1_19/v242.go create mode 100644 modelmigration/v1_19/v243.go create mode 100644 modelmigration/v1_20/main_test.go create mode 100644 modelmigration/v1_20/v244.go create mode 100644 modelmigration/v1_20/v245.go create mode 100644 modelmigration/v1_20/v246.go create mode 100644 modelmigration/v1_20/v247.go create mode 100644 modelmigration/v1_20/v248.go create mode 100644 modelmigration/v1_20/v249.go create mode 100644 modelmigration/v1_20/v250.go create mode 100644 modelmigration/v1_20/v251.go create mode 100644 modelmigration/v1_20/v252.go create mode 100644 modelmigration/v1_20/v253.go create mode 100644 modelmigration/v1_20/v254.go create mode 100644 modelmigration/v1_20/v255.go create mode 100644 modelmigration/v1_20/v256.go create mode 100644 modelmigration/v1_20/v257.go create mode 100644 modelmigration/v1_20/v258.go create mode 100644 modelmigration/v1_20/v259.go create mode 100644 modelmigration/v1_20/v259_test.go create mode 100644 modelmigration/v1_21/main_test.go create mode 100644 modelmigration/v1_21/v260.go create mode 100644 modelmigration/v1_21/v261.go create mode 100644 modelmigration/v1_21/v262.go create mode 100644 modelmigration/v1_21/v263.go create mode 100644 modelmigration/v1_21/v264.go create mode 100644 modelmigration/v1_21/v265.go create mode 100644 modelmigration/v1_21/v266.go create mode 100644 modelmigration/v1_21/v267.go create mode 100644 modelmigration/v1_21/v268.go create mode 100644 modelmigration/v1_21/v269.go create mode 100644 modelmigration/v1_21/v270.go create mode 100644 modelmigration/v1_21/v271.go create mode 100644 modelmigration/v1_21/v272.go create mode 100644 modelmigration/v1_21/v273.go create mode 100644 modelmigration/v1_21/v274.go create mode 100644 modelmigration/v1_21/v275.go create mode 100644 modelmigration/v1_21/v276.go create mode 100644 modelmigration/v1_21/v277.go create mode 100644 modelmigration/v1_21/v278.go create mode 100644 modelmigration/v1_21/v279.go create mode 100644 modelmigration/v1_22/main_test.go create mode 100644 modelmigration/v1_22/v280.go create mode 100644 modelmigration/v1_22/v281.go create mode 100644 modelmigration/v1_22/v282.go create mode 100644 modelmigration/v1_22/v283.go create mode 100644 modelmigration/v1_22/v283_test.go create mode 100644 modelmigration/v1_22/v284.go create mode 100644 modelmigration/v1_22/v285.go create mode 100644 modelmigration/v1_22/v286.go create mode 100644 modelmigration/v1_22/v286_test.go create mode 100644 modelmigration/v1_22/v287.go create mode 100644 modelmigration/v1_22/v287_test.go create mode 100644 modelmigration/v1_22/v288.go create mode 100644 modelmigration/v1_22/v289.go create mode 100644 modelmigration/v1_22/v290.go create mode 100644 modelmigration/v1_22/v291.go create mode 100644 modelmigration/v1_22/v292.go create mode 100644 modelmigration/v1_22/v293.go create mode 100644 modelmigration/v1_22/v293_test.go create mode 100644 modelmigration/v1_22/v294.go create mode 100644 modelmigration/v1_22/v294_test.go create mode 100644 modelmigration/v1_22/v295.go create mode 100644 modelmigration/v1_22/v296.go create mode 100644 modelmigration/v1_22/v297.go create mode 100644 modelmigration/v1_22/v298.go create mode 100644 modelmigration/v1_23/main_test.go create mode 100644 modelmigration/v1_23/v299.go create mode 100644 modelmigration/v1_23/v300.go create mode 100644 modelmigration/v1_23/v301.go create mode 100644 modelmigration/v1_23/v302.go create mode 100644 modelmigration/v1_23/v302_test.go create mode 100644 modelmigration/v1_23/v303.go create mode 100644 modelmigration/v1_23/v304.go create mode 100644 modelmigration/v1_23/v304_test.go create mode 100644 modelmigration/v1_23/v305.go create mode 100644 modelmigration/v1_23/v306.go create mode 100644 modelmigration/v1_23/v307.go create mode 100644 modelmigration/v1_23/v308.go create mode 100644 modelmigration/v1_23/v309.go create mode 100644 modelmigration/v1_23/v310.go create mode 100644 modelmigration/v1_23/v311.go create mode 100644 modelmigration/v1_24/v312.go create mode 100644 modelmigration/v1_24/v313.go create mode 100644 modelmigration/v1_24/v314.go create mode 100644 modelmigration/v1_24/v315.go create mode 100644 modelmigration/v1_24/v316.go create mode 100644 modelmigration/v1_24/v317.go create mode 100644 modelmigration/v1_24/v318.go create mode 100644 modelmigration/v1_24/v319.go create mode 100644 modelmigration/v1_24/v320.go create mode 100644 modelmigration/v1_25/main_test.go create mode 100644 modelmigration/v1_25/v321.go create mode 100644 modelmigration/v1_25/v321_test.go create mode 100644 modelmigration/v1_25/v322.go create mode 100644 modelmigration/v1_25/v322_test.go create mode 100644 modelmigration/v1_26/main_test.go create mode 100644 modelmigration/v1_26/v323.go create mode 100644 modelmigration/v1_26/v324.go create mode 100644 modelmigration/v1_26/v325.go create mode 100644 modelmigration/v1_26/v325_test.go create mode 100644 modelmigration/v1_26/v326.go create mode 100644 modelmigration/v1_26/v326_test.go create mode 100644 modelmigration/v1_26/v327.go create mode 100644 modelmigration/v1_26/v327_test.go create mode 100644 modelmigration/v1_26/v328.go create mode 100644 modelmigration/v1_26/v329.go create mode 100644 modelmigration/v1_26/v329_test.go create mode 100644 modelmigration/v1_26/v330.go create mode 100644 modelmigration/v1_27/main_test.go create mode 100644 modelmigration/v1_27/v331.go create mode 100644 modelmigration/v1_27/v331_test.go create mode 100644 modelmigration/v1_27/v332.go create mode 100644 modelmigration/v1_27/v333.go create mode 100644 modelmigration/v1_27/v333_test.go create mode 100644 modelmigration/v1_27/v334.go create mode 100644 modelmigration/v1_27/v334_test.go create mode 100644 modelmigration/v1_27/v335.go create mode 100644 modelmigration/v1_27/v336.go create mode 100644 modelmigration/v1_27/v337.go create mode 100644 modelmigration/v1_27/v338.go create mode 100644 modelmigration/v1_27/v338_test.go create mode 100644 modelmigration/v1_27/v339.go create mode 100644 modelmigration/v1_27/v339_test.go create mode 100644 modelmigration/v1_27/v340.go create mode 100644 modelmigration/v1_27/v341.go create mode 100644 modelmigration/v1_27/v341_test.go create mode 100644 modelmigration/v1_27/v342.go create mode 100644 modelmigration/v1_6/v70.go create mode 100644 modelmigration/v1_6/v71.go create mode 100644 modelmigration/v1_6/v72.go create mode 100644 modelmigration/v1_7/v73.go create mode 100644 modelmigration/v1_7/v74.go create mode 100644 modelmigration/v1_7/v75.go create mode 100644 modelmigration/v1_8/v76.go create mode 100644 modelmigration/v1_8/v77.go create mode 100644 modelmigration/v1_8/v78.go create mode 100644 modelmigration/v1_8/v79.go create mode 100644 modelmigration/v1_8/v80.go create mode 100644 modelmigration/v1_8/v81.go create mode 100644 modelmigration/v1_9/v82.go create mode 100644 modelmigration/v1_9/v83.go create mode 100644 modelmigration/v1_9/v84.go create mode 100644 modelmigration/v1_9/v85.go create mode 100644 modelmigration/v1_9/v86.go create mode 100644 modelmigration/v1_9/v87.go create mode 100644 modelmigration/v28/main_test.go create mode 100644 modelmigration/v28/v343.go create mode 100644 modelmigration/v28/v343_test.go create mode 100644 modelmigration/v28/v344.go create mode 100644 modelmigration/v28/v345.go create mode 100644 modelmigration/v28/v346.go create mode 100644 modelmigration/v28/v346_test.go create mode 100644 modelmigration/v28/v347.go create mode 100644 modelmigration/v28/v348.go create mode 100644 modelmigration/v28/v348_test.go create mode 100644 modelmigration/v28/v349.go create mode 100644 modelmigration/v28/v349_test.go create mode 100644 modelmigration/v28/v350.go create mode 100644 modelmigration/v28/v350_test.go create mode 100644 modelmigration/v28/v351.go create mode 100644 modelmigration/v28/v352.go create mode 100644 modelmigration/v28/v353.go create mode 100644 modelmigration/v28/v353_test.go create mode 100644 models/actions/artifact.go create mode 100644 models/actions/artifact_test.go create mode 100644 models/actions/config.go create mode 100644 models/actions/main_test.go create mode 100644 models/actions/run.go create mode 100644 models/actions/run_attempt.go create mode 100644 models/actions/run_attempt_list.go create mode 100644 models/actions/run_job.go create mode 100644 models/actions/run_job_list.go create mode 100644 models/actions/run_job_list_test.go create mode 100644 models/actions/run_job_status_test.go create mode 100644 models/actions/run_job_summary.go create mode 100644 models/actions/run_job_test.go create mode 100644 models/actions/run_list.go create mode 100644 models/actions/run_list_test.go create mode 100644 models/actions/run_test.go create mode 100644 models/actions/runner.go create mode 100644 models/actions/runner_list.go create mode 100644 models/actions/runner_test.go create mode 100644 models/actions/runner_token.go create mode 100644 models/actions/runner_token_test.go create mode 100644 models/actions/schedule.go create mode 100644 models/actions/schedule_list.go create mode 100644 models/actions/schedule_spec.go create mode 100644 models/actions/schedule_spec_test.go create mode 100644 models/actions/scoped_workflow.go create mode 100644 models/actions/scoped_workflow_test.go create mode 100644 models/actions/status.go create mode 100644 models/actions/status_test.go create mode 100644 models/actions/task.go create mode 100644 models/actions/task_list.go create mode 100644 models/actions/task_output.go create mode 100644 models/actions/task_step.go create mode 100644 models/actions/task_test.go create mode 100644 models/actions/tasks_version.go create mode 100644 models/actions/token_permissions.go create mode 100644 models/actions/utils.go create mode 100644 models/actions/utils_test.go create mode 100644 models/actions/variable.go create mode 100644 models/activities/action.go create mode 100644 models/activities/action_list.go create mode 100644 models/activities/action_test.go create mode 100644 models/activities/main_test.go create mode 100644 models/activities/notification.go create mode 100644 models/activities/notification_list.go create mode 100644 models/activities/notification_test.go create mode 100644 models/activities/repo_activity.go create mode 100644 models/activities/statistic.go create mode 100644 models/activities/user_heatmap.go create mode 100644 models/activities/user_heatmap_test.go create mode 100644 models/admin/task.go create mode 100644 models/asymkey/error.go create mode 100644 models/asymkey/gpg_key.go create mode 100644 models/asymkey/gpg_key_add.go create mode 100644 models/asymkey/gpg_key_commit_verification.go create mode 100644 models/asymkey/gpg_key_common.go create mode 100644 models/asymkey/gpg_key_import.go create mode 100644 models/asymkey/gpg_key_list.go create mode 100644 models/asymkey/gpg_key_test.go create mode 100644 models/asymkey/gpg_key_verify.go create mode 100644 models/asymkey/key_display.go create mode 100644 models/asymkey/main_test.go create mode 100644 models/asymkey/ssh_key.go create mode 100644 models/asymkey/ssh_key_authorized_keys.go create mode 100644 models/asymkey/ssh_key_authorized_keys_test.go create mode 100644 models/asymkey/ssh_key_fingerprint.go create mode 100644 models/asymkey/ssh_key_parse.go create mode 100644 models/asymkey/ssh_key_principals.go create mode 100644 models/asymkey/ssh_key_test.go create mode 100644 models/asymkey/ssh_key_verify.go create mode 100644 models/audit/action.go create mode 100644 models/audit/audit_event.go create mode 100644 models/audit/audit_event_benchmark_test.go create mode 100644 models/audit/audit_event_test.go create mode 100644 models/audit/entity.go create mode 100644 models/audit/main_test.go create mode 100644 models/audit/origin.go create mode 100644 models/audit/scopes.go create mode 100644 models/auth/access_token.go create mode 100644 models/auth/access_token_scope.go create mode 100644 models/auth/access_token_scope_test.go create mode 100644 models/auth/access_token_test.go create mode 100644 models/auth/auth_token.go create mode 100644 models/auth/main_test.go create mode 100644 models/auth/oauth2.go create mode 100644 models/auth/oauth2_list.go create mode 100644 models/auth/oauth2_test.go create mode 100644 models/auth/session.go create mode 100644 models/auth/source.go create mode 100644 models/auth/source_test.go create mode 100644 models/auth/token_cache.go create mode 100644 models/auth/twofactor.go create mode 100644 models/auth/twofactor_test.go create mode 100644 models/auth/webauthn.go create mode 100644 models/auth/webauthn_test.go create mode 100644 models/avatars/avatar.go create mode 100644 models/avatars/avatar_test.go create mode 100644 models/avatars/main_test.go create mode 100644 models/db/collation.go create mode 100644 models/db/common.go create mode 100644 models/db/conn.go create mode 100644 models/db/conn_test.go create mode 100644 models/db/consistency.go create mode 100644 models/db/context.go create mode 100644 models/db/context_committer_test.go create mode 100644 models/db/context_test.go create mode 100644 models/db/convert.go create mode 100644 models/db/driver_postgresschema.go create mode 100644 models/db/driver_sqlite_mattn.go create mode 100644 models/db/driver_sqlite_modernc.go create mode 100755 models/db/engine.go create mode 100644 models/db/engine_dump.go create mode 100644 models/db/engine_hook.go create mode 100644 models/db/engine_init.go create mode 100644 models/db/engine_test.go create mode 100644 models/db/error.go create mode 100644 models/db/index.go create mode 100644 models/db/index_test.go create mode 100644 models/db/install/db.go create mode 100644 models/db/iterate.go create mode 100644 models/db/iterate_test.go create mode 100644 models/db/list.go create mode 100644 models/db/list_test.go create mode 100644 models/db/log.go create mode 100644 models/db/main_test.go create mode 100644 models/db/name.go create mode 100644 models/db/paginator/main_test.go create mode 100644 models/db/paginator/paginator.go create mode 100644 models/db/paginator/paginator_test.go create mode 100644 models/db/search.go create mode 100644 models/db/sequence.go create mode 100644 models/dbfs/dbfile.go create mode 100644 models/dbfs/dbfs.go create mode 100644 models/dbfs/dbfs_test.go create mode 100644 models/dbfs/main_test.go create mode 100644 models/deploykey/deploykey.go create mode 100644 models/deploykey/deploykey_ssh.go create mode 100644 models/deploykey/deploykey_token.go create mode 100644 models/deploykey/deploykey_token_test.go create mode 100644 models/deploykey/error.go create mode 100644 models/deploykey/main_test.go create mode 100644 models/fixtures/access.yml create mode 100644 models/fixtures/access_token.yml create mode 100644 models/fixtures/action.yml create mode 100644 models/fixtures/action_artifact.yml create mode 100644 models/fixtures/action_run.yml create mode 100644 models/fixtures/action_run_job.yml create mode 100644 models/fixtures/action_runner.yml create mode 100644 models/fixtures/action_runner_token.yml create mode 100644 models/fixtures/action_task.yml create mode 100644 models/fixtures/action_task_output.yml create mode 100644 models/fixtures/attachment.yml create mode 100644 models/fixtures/badge.yml create mode 100644 models/fixtures/branch.yml create mode 100644 models/fixtures/collaboration.yml create mode 100644 models/fixtures/comment.yml create mode 100644 models/fixtures/commit_status.yml create mode 100644 models/fixtures/commit_status_index.yml create mode 100644 models/fixtures/deploy_key.yml create mode 100644 models/fixtures/email_address.yml create mode 100644 models/fixtures/external_login_user.yml create mode 100644 models/fixtures/follow.yml create mode 100644 models/fixtures/gpg_key.yml create mode 100644 models/fixtures/gpg_key_import.yml create mode 100644 models/fixtures/hook_task.yml create mode 100644 models/fixtures/issue.yml create mode 100644 models/fixtures/issue_assignees.yml create mode 100644 models/fixtures/issue_index.yml create mode 100644 models/fixtures/issue_label.yml create mode 100644 models/fixtures/issue_pin.yml create mode 100644 models/fixtures/issue_user.yml create mode 100644 models/fixtures/issue_watch.yml create mode 100644 models/fixtures/label.yml create mode 100644 models/fixtures/lfs_meta_object.yml create mode 100644 models/fixtures/login_source.yml create mode 100644 models/fixtures/milestone.yml create mode 100644 models/fixtures/mirror.yml create mode 100644 models/fixtures/notice.yml create mode 100644 models/fixtures/notification.yml create mode 100644 models/fixtures/oauth2_application.yml create mode 100644 models/fixtures/oauth2_authorization_code.yml create mode 100644 models/fixtures/oauth2_grant.yml create mode 100644 models/fixtures/org_user.yml create mode 100644 models/fixtures/project.yml create mode 100644 models/fixtures/project_board.yml create mode 100644 models/fixtures/project_issue.yml create mode 100644 models/fixtures/protected_branch.yml create mode 100644 models/fixtures/protected_tag.yml create mode 100644 models/fixtures/public_key.yml create mode 100644 models/fixtures/pull_request.yml create mode 100644 models/fixtures/reaction.yml create mode 100644 models/fixtures/release.yml create mode 100644 models/fixtures/renamed_branch.yml create mode 100644 models/fixtures/repo_archiver.yml create mode 100644 models/fixtures/repo_indexer_status.yml create mode 100644 models/fixtures/repo_license.yml create mode 100644 models/fixtures/repo_redirect.yml create mode 100644 models/fixtures/repo_topic.yml create mode 100644 models/fixtures/repo_transfer.yml create mode 100644 models/fixtures/repo_unit.yml create mode 100644 models/fixtures/repository.yml create mode 100644 models/fixtures/review.yml create mode 100644 models/fixtures/star.yml create mode 100644 models/fixtures/stopwatch.yml create mode 100644 models/fixtures/system_setting.yml create mode 100644 models/fixtures/team.yml create mode 100644 models/fixtures/team_repo.yml create mode 100644 models/fixtures/team_unit.yml create mode 100644 models/fixtures/team_user.yml create mode 100644 models/fixtures/topic.yml create mode 100644 models/fixtures/tracked_time.yml create mode 100644 models/fixtures/two_factor.yml create mode 100644 models/fixtures/user.yml create mode 100644 models/fixtures/user_blocking.yml create mode 100644 models/fixtures/user_open_id.yml create mode 100644 models/fixtures/user_redirect.yml create mode 100644 models/fixtures/watch.yml create mode 100644 models/fixtures/webauthn_credential.yml create mode 100644 models/fixtures/webhook.yml create mode 100644 models/git/branch.go create mode 100644 models/git/branch_list.go create mode 100644 models/git/branch_test.go create mode 100644 models/git/commit_status.go create mode 100644 models/git/commit_status_summary.go create mode 100644 models/git/commit_status_test.go create mode 100644 models/git/lfs.go create mode 100644 models/git/lfs_lock.go create mode 100644 models/git/lfs_lock_list.go create mode 100644 models/git/lfs_lock_test.go create mode 100644 models/git/lfs_test.go create mode 100644 models/git/main_test.go create mode 100644 models/git/protected_branch.go create mode 100644 models/git/protected_branch_list.go create mode 100644 models/git/protected_branch_list_test.go create mode 100644 models/git/protected_branch_test.go create mode 100644 models/git/protected_tag.go create mode 100644 models/git/protected_tag_test.go create mode 100644 models/git/ref.go create mode 100644 models/gituser/avatar_stack.go create mode 100644 models/gituser/gituser.go create mode 100644 models/init.go create mode 100644 models/issues/assignees.go create mode 100644 models/issues/assignees_test.go create mode 100644 models/issues/comment.go create mode 100644 models/issues/comment_code.go create mode 100644 models/issues/comment_list.go create mode 100644 models/issues/comment_test.go create mode 100644 models/issues/content_history.go create mode 100644 models/issues/content_history_test.go create mode 100644 models/issues/dependency.go create mode 100644 models/issues/dependency_test.go create mode 100644 models/issues/issue.go create mode 100644 models/issues/issue_index.go create mode 100644 models/issues/issue_label.go create mode 100644 models/issues/issue_label_test.go create mode 100644 models/issues/issue_list.go create mode 100644 models/issues/issue_list_test.go create mode 100644 models/issues/issue_lock.go create mode 100644 models/issues/issue_pin.go create mode 100644 models/issues/issue_project.go create mode 100644 models/issues/issue_project_multi_test.go create mode 100644 models/issues/issue_search.go create mode 100644 models/issues/issue_stats.go create mode 100644 models/issues/issue_test.go create mode 100644 models/issues/issue_update.go create mode 100644 models/issues/issue_update_test.go create mode 100644 models/issues/issue_user.go create mode 100644 models/issues/issue_user_test.go create mode 100644 models/issues/issue_watch.go create mode 100644 models/issues/issue_watch_test.go create mode 100644 models/issues/issue_xref.go create mode 100644 models/issues/issue_xref_test.go create mode 100644 models/issues/label.go create mode 100644 models/issues/label_test.go create mode 100644 models/issues/main_test.go create mode 100644 models/issues/milestone.go create mode 100644 models/issues/milestone_list.go create mode 100644 models/issues/milestone_test.go create mode 100644 models/issues/pull.go create mode 100644 models/issues/pull_list.go create mode 100644 models/issues/pull_list_test.go create mode 100644 models/issues/pull_test.go create mode 100644 models/issues/reaction.go create mode 100644 models/issues/review.go create mode 100644 models/issues/review_list.go create mode 100644 models/issues/review_test.go create mode 100644 models/issues/stopwatch.go create mode 100644 models/issues/stopwatch_test.go create mode 100644 models/issues/tracked_time.go create mode 100644 models/issues/tracked_time_test.go create mode 100644 models/organization/main_test.go create mode 100644 models/organization/org.go create mode 100644 models/organization/org_list.go create mode 100644 models/organization/org_list_test.go create mode 100644 models/organization/org_test.go create mode 100644 models/organization/org_user.go create mode 100644 models/organization/org_user_test.go create mode 100644 models/organization/org_worktime.go create mode 100644 models/organization/team.go create mode 100644 models/organization/team_invite.go create mode 100644 models/organization/team_invite_test.go create mode 100644 models/organization/team_list.go create mode 100644 models/organization/team_list_test.go create mode 100644 models/organization/team_repo.go create mode 100644 models/organization/team_repo_test.go create mode 100644 models/organization/team_test.go create mode 100644 models/organization/team_unit.go create mode 100644 models/organization/team_user.go create mode 100644 models/packages/alpine/search.go create mode 100644 models/packages/arch/search.go create mode 100644 models/packages/conan/references.go create mode 100644 models/packages/conan/search.go create mode 100644 models/packages/conda/search.go create mode 100644 models/packages/container/search.go create mode 100644 models/packages/cran/search.go create mode 100644 models/packages/debian/search.go create mode 100644 models/packages/descriptor.go create mode 100644 models/packages/nuget/search.go create mode 100644 models/packages/package.go create mode 100644 models/packages/package_blob.go create mode 100644 models/packages/package_blob_test.go create mode 100644 models/packages/package_blob_upload.go create mode 100644 models/packages/package_cleanup_rule.go create mode 100644 models/packages/package_file.go create mode 100644 models/packages/package_property.go create mode 100644 models/packages/package_test.go create mode 100644 models/packages/package_version.go create mode 100644 models/packages/rpm/search.go create mode 100644 models/perm/access/access.go create mode 100644 models/perm/access/access_test.go create mode 100644 models/perm/access/actions_repo_permission_test.go create mode 100644 models/perm/access/main_test.go create mode 100644 models/perm/access/repo_permission.go create mode 100644 models/perm/access/repo_permission_test.go create mode 100644 models/perm/access_mode.go create mode 100644 models/perm/access_mode_test.go create mode 100644 models/project/column.go create mode 100644 models/project/column_list.go create mode 100644 models/project/column_list_test.go create mode 100644 models/project/column_test.go create mode 100644 models/project/issue.go create mode 100644 models/project/main_test.go create mode 100644 models/project/project.go create mode 100644 models/project/project_test.go create mode 100644 models/project/template.go create mode 100644 models/pull/automerge.go create mode 100644 models/pull/review_state.go create mode 100644 models/renderhelper/commit_checker.go create mode 100644 models/renderhelper/main_test.go create mode 100644 models/renderhelper/repo_comment.go create mode 100644 models/renderhelper/repo_comment_test.go create mode 100644 models/renderhelper/repo_file.go create mode 100644 models/renderhelper/repo_file_test.go create mode 100644 models/renderhelper/repo_wiki.go create mode 100644 models/renderhelper/repo_wiki_test.go create mode 100644 models/renderhelper/simple_document.go create mode 100644 models/renderhelper/simple_document_test.go create mode 100644 models/repo/archive_test.go create mode 100644 models/repo/archiver.go create mode 100644 models/repo/attachment.go create mode 100644 models/repo/attachment_test.go create mode 100644 models/repo/avatar.go create mode 100644 models/repo/avatar_test.go create mode 100644 models/repo/collaboration.go create mode 100644 models/repo/collaboration_test.go create mode 100644 models/repo/fork.go create mode 100644 models/repo/fork_test.go create mode 100644 models/repo/git.go create mode 100644 models/repo/issue.go create mode 100644 models/repo/language_stats.go create mode 100644 models/repo/license.go create mode 100644 models/repo/main_test.go create mode 100644 models/repo/mirror.go create mode 100644 models/repo/org_repo.go create mode 100644 models/repo/pull_request_default.go create mode 100644 models/repo/pull_request_default_test.go create mode 100644 models/repo/pushmirror.go create mode 100644 models/repo/pushmirror_test.go create mode 100644 models/repo/redirect.go create mode 100644 models/repo/redirect_test.go create mode 100644 models/repo/release.go create mode 100644 models/repo/release_test.go create mode 100644 models/repo/repo.go create mode 100644 models/repo/repo_indexer.go create mode 100644 models/repo/repo_list.go create mode 100644 models/repo/repo_list_test.go create mode 100644 models/repo/repo_location.go create mode 100644 models/repo/repo_location_test.go create mode 100644 models/repo/repo_test.go create mode 100644 models/repo/repo_unit.go create mode 100644 models/repo/repo_unit_actions.go create mode 100644 models/repo/repo_unit_actions_test.go create mode 100644 models/repo/repo_unit_test.go create mode 100644 models/repo/search.go create mode 100644 models/repo/star.go create mode 100644 models/repo/star_test.go create mode 100644 models/repo/topic.go create mode 100644 models/repo/topic_test.go create mode 100644 models/repo/transfer.go create mode 100644 models/repo/update.go create mode 100644 models/repo/upload.go create mode 100644 models/repo/user_repo.go create mode 100644 models/repo/user_repo_test.go create mode 100644 models/repo/watch.go create mode 100644 models/repo/watch_test.go create mode 100644 models/repo/wiki.go create mode 100644 models/repo/wiki_test.go create mode 100644 models/repostats/main_test.go create mode 100644 models/repostats/repo.go create mode 100644 models/repostats/repo_test.go create mode 100644 models/secret/main_test.go create mode 100644 models/secret/secret.go create mode 100644 models/secret/secret_test.go create mode 100644 models/shared/types/ownertype.go create mode 100644 models/system/appstate.go create mode 100644 models/system/main_test.go create mode 100644 models/system/notice.go create mode 100644 models/system/notice_test.go create mode 100644 models/system/setting.go create mode 100644 models/system/setting_test.go create mode 100644 models/unit/unit.go create mode 100644 models/unit/unit_test.go create mode 100644 models/unittest/consistency.go create mode 100644 models/unittest/fixtures.go create mode 100644 models/unittest/fixtures_loader.go create mode 100644 models/unittest/fixtures_test.go create mode 100644 models/unittest/fscopy.go create mode 100644 models/unittest/mock_http.go create mode 100644 models/unittest/reflection.go create mode 100644 models/unittest/testdb.go create mode 100644 models/unittest/unit_tests.go create mode 100644 models/user/avatar.go create mode 100644 models/user/avatar_test.go create mode 100644 models/user/badge.go create mode 100644 models/user/badge_test.go create mode 100644 models/user/block.go create mode 100644 models/user/email_address.go create mode 100644 models/user/email_address_test.go create mode 100644 models/user/error.go create mode 100644 models/user/external_login_user.go create mode 100644 models/user/follow.go create mode 100644 models/user/follow_test.go create mode 100644 models/user/list.go create mode 100644 models/user/main_test.go create mode 100644 models/user/must_change_password.go create mode 100644 models/user/openid.go create mode 100644 models/user/openid_test.go create mode 100644 models/user/redirect.go create mode 100644 models/user/redirect_test.go create mode 100644 models/user/search.go create mode 100644 models/user/setting.go create mode 100644 models/user/setting_options.go create mode 100644 models/user/setting_test.go create mode 100644 models/user/user.go create mode 100644 models/user/user_extdata.go create mode 100644 models/user/user_list.go create mode 100644 models/user/user_system.go create mode 100644 models/user/user_system_test.go create mode 100644 models/user/user_test.go create mode 100644 models/user/user_update.go create mode 100644 models/webhook/hooktask.go create mode 100644 models/webhook/main_test.go create mode 100644 models/webhook/webhook.go create mode 100644 models/webhook/webhook_system.go create mode 100644 models/webhook/webhook_system_test.go create mode 100644 models/webhook/webhook_test.go create mode 100644 modules/actions/artifacts.go create mode 100644 modules/actions/artifacts_test.go create mode 100644 modules/actions/commit_status_info.go create mode 100644 modules/actions/github.go create mode 100644 modules/actions/github_test.go create mode 100644 modules/actions/jobparser/aliases.go create mode 100644 modules/actions/jobparser/aliases_test.go create mode 100644 modules/actions/jobparser/interpeter.go create mode 100644 modules/actions/jobparser/jobparser.go create mode 100644 modules/actions/jobparser/jobparser_test.go create mode 100644 modules/actions/jobparser/model.go create mode 100644 modules/actions/jobparser/model_test.go create mode 100644 modules/actions/jobparser/roundtrip_test.go create mode 100644 modules/actions/jobparser/testdata/continue_on_error_expr.in.yaml create mode 100644 modules/actions/jobparser/testdata/continue_on_error_expr.out.yaml create mode 100644 modules/actions/jobparser/testdata/empty_step.in.yaml create mode 100644 modules/actions/jobparser/testdata/empty_step.out.yaml create mode 100644 modules/actions/jobparser/testdata/erase_needs.in.yaml create mode 100644 modules/actions/jobparser/testdata/erase_needs.out.yaml create mode 100644 modules/actions/jobparser/testdata/has_needs.in.yaml create mode 100644 modules/actions/jobparser/testdata/has_needs.out.yaml create mode 100644 modules/actions/jobparser/testdata/has_secrets.in.yaml create mode 100644 modules/actions/jobparser/testdata/has_secrets.out.yaml create mode 100644 modules/actions/jobparser/testdata/has_with.in.yaml create mode 100644 modules/actions/jobparser/testdata/has_with.out.yaml create mode 100644 modules/actions/jobparser/testdata/job_name_with_matrix.in.yaml create mode 100644 modules/actions/jobparser/testdata/job_name_with_matrix.out.yaml create mode 100644 modules/actions/jobparser/testdata/multiple_jobs.in.yaml create mode 100644 modules/actions/jobparser/testdata/multiple_jobs.out.yaml create mode 100644 modules/actions/jobparser/testdata/multiple_matrix.in.yaml create mode 100644 modules/actions/jobparser/testdata/multiple_matrix.out.yaml create mode 100644 modules/actions/jobparser/testdata/null_job_explicit.in.yaml create mode 100644 modules/actions/jobparser/testdata/null_job_implicit.in.yaml create mode 100644 modules/actions/jobparser/testdata/prefixed_newline.in.yaml create mode 100644 modules/actions/jobparser/testdata/prefixed_newline.out.yaml create mode 100644 modules/actions/jobparser/testdata_test.go create mode 100644 modules/actions/jobparser/uses.go create mode 100644 modules/actions/jobparser/uses_test.go create mode 100644 modules/actions/jobparser/workflow_call.go create mode 100644 modules/actions/jobparser/workflow_call_test.go create mode 100644 modules/actions/log.go create mode 100644 modules/actions/scoped_workflows.go create mode 100644 modules/actions/scoped_workflows_test.go create mode 100644 modules/actions/task_state.go create mode 100644 modules/actions/task_state_test.go create mode 100644 modules/actions/workflowpattern/workflow_pattern.go create mode 100644 modules/actions/workflowpattern/workflow_pattern_test.go create mode 100644 modules/actions/workflows.go create mode 100644 modules/actions/workflows_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/embed.go create mode 100644 modules/assetfs/embed_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/httpauth/httpauth.go create mode 100644 modules/auth/httpauth/httpauth_test.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/openid/openid_test.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/federated.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/testdata/animated.webp create mode 100644 modules/avatar/testdata/avatar.jpeg create mode 100644 modules/avatar/testdata/avatar.png create mode 100644 modules/badge/badge.go create mode 100644 modules/badge/badge_glyph_width.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/cache/ephemeral.go create mode 100644 modules/cache/string_cache.go create mode 100644 modules/cachegroup/cachegroup.go create mode 100644 modules/charset/ambiguous.go create mode 100644 modules/charset/ambiguous_gen.go create mode 100644 modules/charset/ambiguous_gen_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/generate/ambiguous.json create mode 100644 modules/charset/generate/generate.go create mode 100644 modules/charset/invisible_gen.go create mode 100644 modules/commitstatus/commit_status.go create mode 100644 modules/commitstatus/commit_status_test.go create mode 100644 modules/consts/asymkey.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/dump/dumper.go create mode 100644 modules/dump/dumper_test.go create mode 100644 modules/emoji/emoji.go create mode 100644 modules/emoji/emoji_test.go create mode 100644 modules/fileicon/basic.go create mode 100644 modules/fileicon/entry.go create mode 100644 modules/fileicon/material.go create mode 100644 modules/fileicon/material_test.go create mode 100644 modules/fileicon/render.go create mode 100644 modules/fileicon/render_test.go create mode 100644 modules/generate/generate.go create mode 100644 modules/generate/generate_test.go create mode 100644 modules/git/archive.go create mode 100644 modules/git/archive_test.go create mode 100644 modules/git/attribute/attribute.go create mode 100644 modules/git/attribute/attribute_test.go create mode 100644 modules/git/attribute/batch.go create mode 100644 modules/git/attribute/batch_test.go create mode 100644 modules/git/attribute/checker.go create mode 100644 modules/git/attribute/checker_test.go create mode 100644 modules/git/attribute/main_test.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_gogit.go create mode 100644 modules/git/blob_nogogit.go create mode 100644 modules/git/blob_test.go create mode 100644 modules/git/branch.go create mode 100644 modules/git/catfile_batch.go create mode 100644 modules/git/catfile_batch_command.go create mode 100644 modules/git/catfile_batch_legacy.go create mode 100644 modules/git/catfile_batch_reader.go create mode 100644 modules/git/catfile_batch_test.go create mode 100644 modules/git/clone.go create mode 100644 modules/git/cmdverb.go create mode 100644 modules/git/commit.go create mode 100644 modules/git/commit2.go create mode 100644 modules/git/commit2_test.go create mode 100644 modules/git/commit_convert_gogit.go create mode 100644 modules/git/commit_count_cache.go create mode 100644 modules/git/commit_file.go create mode 100644 modules/git/commit_file_test.go create mode 100644 modules/git/commit_info.go create mode 100644 modules/git/commit_info_gogit.go create mode 100644 modules/git/commit_info_nogogit.go create mode 100644 modules/git/commit_info_nogogit_test.go create mode 100644 modules/git/commit_info_test.go create mode 100644 modules/git/commit_message.go create mode 100644 modules/git/commit_message_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_submodule.go create mode 100644 modules/git/commit_submodule_file.go create mode 100644 modules/git/commit_submodule_file_test.go create mode 100644 modules/git/commit_test.go create mode 100644 modules/git/compare.go create mode 100644 modules/git/compare_test.go create mode 100644 modules/git/config.go create mode 100644 modules/git/config_managed.go create mode 100644 modules/git/config_submodule.go create mode 100644 modules/git/config_submodule_test.go create mode 100644 modules/git/config_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/fastimport.go create mode 100644 modules/git/fetch.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/fsck.go create mode 100644 modules/git/git.go create mode 100644 modules/git/git_test.go create mode 100644 modules/git/gitcmd/command.go create mode 100644 modules/git/gitcmd/command_test.go create mode 100644 modules/git/gitcmd/context.go create mode 100644 modules/git/gitcmd/env.go create mode 100644 modules/git/gitcmd/error.go create mode 100644 modules/git/gitcmd/error_test.go create mode 100644 modules/git/gitcmd/pipe.go create mode 100644 modules/git/gitrepo.go create mode 100644 modules/git/gitrepo/dbrepo.go create mode 100644 modules/git/gitrepo/gitrepo.go create mode 100644 modules/git/gpg.go create mode 100644 modules/git/gpg_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/hooks.go create mode 100644 modules/git/hooks_test.go create mode 100644 modules/git/internal/cmdarg.go create mode 100644 modules/git/key.go create mode 100644 modules/git/languagestats/language_stats.go create mode 100644 modules/git/languagestats/language_stats_get.go create mode 100644 modules/git/languagestats/language_stats_test.go create mode 100644 modules/git/languagestats/main_test.go create mode 100644 modules/git/last_commit_cache.go create mode 100644 modules/git/last_commit_cache_gogit.go create mode 100644 modules/git/last_commit_cache_nogogit.go create mode 100644 modules/git/localfs.go create mode 100644 modules/git/log_name_status_nogogit.go create mode 100644 modules/git/main_test.go create mode 100644 modules/git/merge.go create mode 100644 modules/git/merge_tree.go create mode 100644 modules/git/merge_tree_test.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_gogit.go create mode 100644 modules/git/object_id_test.go create mode 100644 modules/git/parse.go create mode 100644 modules/git/parse_treeentry.go create mode 100644 modules/git/parse_treeentry_test.go create mode 100644 modules/git/pipeline/catfile.go create mode 100644 modules/git/pipeline/lfs_common.go create mode 100644 modules/git/pipeline/lfs_find.go create mode 100644 modules/git/pipeline/lfs_test.go create mode 100644 modules/git/pipeline/main_test.go create mode 100644 modules/git/pipeline/namerev.go create mode 100644 modules/git/pipeline/revlist.go create mode 100644 modules/git/push.go create mode 100644 modules/git/redirection.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/remote_managed.go create mode 100644 modules/git/repo.go create mode 100644 modules/git/repo_base_gogit.go create mode 100644 modules/git/repo_base_nogogit.go create mode 100644 modules/git/repo_base_nogogit_test.go create mode 100644 modules/git/repo_blob.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_gogit.go create mode 100644 modules/git/repo_branch_nogogit.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_gogit.go create mode 100644 modules/git/repo_commit_nogogit.go create mode 100644 modules/git/repo_commit_test.go create mode 100644 modules/git/repo_commitgraph.go create mode 100644 modules/git/repo_commitgraph_gogit.go create mode 100644 modules/git/repo_compare.go create mode 100644 modules/git/repo_compare_test.go create mode 100644 modules/git/repo_index.go create mode 100644 modules/git/repo_object.go create mode 100644 modules/git/repo_ref.go create mode 100644 modules/git/repo_ref_gogit.go create mode 100644 modules/git/repo_ref_nogogit.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_gogit.go create mode 100644 modules/git/repo_tag_nogogit.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/repo_tree_gogit.go create mode 100644 modules/git/repo_tree_nogogit.go create mode 100644 modules/git/signature.go create mode 100644 modules/git/signature_gogit.go create mode 100644 modules/git/signature_nogogit.go create mode 100644 modules/git/signature_test.go create mode 100644 modules/git/size.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/HEAD create mode 100644 modules/git/tests/repos/language_stats_repo/config create mode 100644 modules/git/tests/repos/language_stats_repo/index 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/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/index 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/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/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/HEAD create mode 100644 modules/git/tests/repos/repo3_notes/config 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/repo4_submodules/HEAD create mode 100644 modules/git/tests/repos/repo4_submodules/config create mode 100644 modules/git/tests/repos/repo4_submodules/objects/97/c3d30df0e6492348292600920a6482feaebb74 create mode 100644 modules/git/tests/repos/repo4_submodules/objects/c7/e064ed49b44523cba8a5dfbc37d2ce1bb41d34 create mode 100644 modules/git/tests/repos/repo4_submodules/objects/e1/e59caba97193d48862d6809912043871f37437 create mode 100644 modules/git/tests/repos/repo4_submodules/refs/heads/master 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/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/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/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/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_blob_gogit.go create mode 100644 modules/git/tree_blob_nogogit.go create mode 100644 modules/git/tree_entry.go create mode 100644 modules/git/tree_entry_common_test.go create mode 100644 modules/git/tree_entry_gogit.go create mode 100644 modules/git/tree_entry_gogit_test.go create mode 100644 modules/git/tree_entry_mode.go create mode 100644 modules/git/tree_entry_nogogit.go create mode 100644 modules/git/tree_entry_test.go create mode 100644 modules/git/tree_gogit.go create mode 100644 modules/git/tree_nogogit.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/glob/glob.go create mode 100644 modules/glob/glob_test.go create mode 100644 modules/globallock/globallock.go create mode 100644 modules/globallock/globallock_test.go create mode 100644 modules/globallock/locker.go create mode 100644 modules/globallock/locker_test.go create mode 100644 modules/globallock/memory_locker.go create mode 100644 modules/globallock/redis_locker.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/gtprof/event.go create mode 100644 modules/gtprof/gtprof.go create mode 100644 modules/gtprof/trace.go create mode 100644 modules/gtprof/trace_builtin.go create mode 100644 modules/gtprof/trace_const.go create mode 100644 modules/gtprof/trace_test.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/benchmark_test.go create mode 100644 modules/highlight/highlight.go create mode 100644 modules/highlight/highlight_test.go create mode 100644 modules/highlight/lexerdetect.go create mode 100644 modules/highlight/lexerdetect_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/htmlutil/html.go create mode 100644 modules/htmlutil/html_test.go create mode 100644 modules/httpcache/httpcache.go create mode 100644 modules/httpcache/httpcache_test.go create mode 100644 modules/httplib/content_disposition.go create mode 100644 modules/httplib/content_disposition_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/bleve/bleve_test.go create mode 100644 modules/indexer/code/bleve/code_token.go create mode 100644 modules/indexer/code/bleve/token/path/path.go create mode 100644 modules/indexer/code/bleve/token/path/path_test.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/gitgrep/gitgrep.go create mode 100644 modules/indexer/code/gitgrep/gitgrep_test.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/indexer.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/query.go create mode 100644 modules/indexer/internal/bleve/util.go create mode 100644 modules/indexer/internal/bleve/util_test.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/indexer_test.go create mode 100644 modules/indexer/internal/elasticsearch/query.go create mode 100644 modules/indexer/internal/elasticsearch/types.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/bleve/camelcase_keep_whole.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/json/json_test.go create mode 100644 modules/json/jsonv2.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_gogit.go create mode 100644 modules/lfs/pointer_scanner_nogogit.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/lfstransfer/backend/backend.go create mode 100644 modules/lfstransfer/backend/lock.go create mode 100644 modules/lfstransfer/backend/util.go create mode 100644 modules/lfstransfer/backend/util_test.go create mode 100644 modules/lfstransfer/logger.go create mode 100644 modules/lfstransfer/main.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/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/camo.go create mode 100644 modules/markup/camo_test.go create mode 100644 modules/markup/common/common.go create mode 100644 modules/markup/common/common_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/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/external/external_test.go create mode 100644 modules/markup/external/frontend.go create mode 100644 modules/markup/html.go create mode 100644 modules/markup/html_codepreview.go create mode 100644 modules/markup/html_codepreview_test.go create mode 100644 modules/markup/html_commit.go create mode 100644 modules/markup/html_email.go create mode 100644 modules/markup/html_emoji.go create mode 100644 modules/markup/html_internal_test.go create mode 100644 modules/markup/html_issue.go create mode 100644 modules/markup/html_issue_test.go create mode 100644 modules/markup/html_link.go create mode 100644 modules/markup/html_link_test.go create mode 100644 modules/markup/html_mention.go create mode 100644 modules/markup/html_node.go create mode 100644 modules/markup/html_node_test.go create mode 100644 modules/markup/html_test.go create mode 100644 modules/markup/html_toc_test.go create mode 100644 modules/markup/internal/finalprocessor.go create mode 100644 modules/markup/internal/internal_test.go create mode 100644 modules/markup/internal/renderinternal.go create mode 100644 modules/markup/jupyter/jupyter-test.ipynb create mode 100644 modules/markup/jupyter/jupyter.go create mode 100644 modules/markup/jupyter/jupyter_test.go create mode 100644 modules/markup/main_test.go create mode 100644 modules/markup/markdown/ast.go create mode 100644 modules/markup/markdown/convertyaml.go create mode 100644 modules/markup/markdown/goldmark.go create mode 100644 modules/markup/markdown/main_test.go create mode 100644 modules/markup/markdown/markdown.go create mode 100644 modules/markup/markdown/markdown_attention_test.go create mode 100644 modules/markup/markdown/markdown_benchmark_test.go create mode 100644 modules/markup/markdown/markdown_math_test.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_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/renderconfig.go create mode 100644 modules/markup/markdown/renderconfig_test.go create mode 100644 modules/markup/markdown/transform_blockquote.go create mode 100644 modules/markup/markdown/transform_codeblock.go create mode 100644 modules/markup/markdown/transform_codespan.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/render.go create mode 100644 modules/markup/render_helper.go create mode 100644 modules/markup/render_link.go create mode 100644 modules/markup/render_link_test.go create mode 100644 modules/markup/render_test.go create mode 100644 modules/markup/renderer.go create mode 100644 modules/markup/sanitizer.go create mode 100644 modules/markup/sanitizer_custom.go create mode 100644 modules/markup/sanitizer_default.go create mode 100644 modules/markup/sanitizer_default_test.go create mode 100644 modules/markup/sanitizer_description.go create mode 100644 modules/markup/sanitizer_description_test.go create mode 100644 modules/mcaptcha/mcaptcha.go create mode 100644 modules/mcaptcha/mcaptcha_test.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/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/options_bindata.go create mode 100644 modules/options/options_dynamic.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/const.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/filelist.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/terraform/lock.go create mode 100644 modules/packages/terraform/state.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/hook.go create mode 100644 modules/private/internal.go create mode 100644 modules/private/internal_test.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/pushoptions.go create mode 100644 modules/private/pushoptions_test.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/command.go create mode 100644 modules/process/command_unix.go create mode 100644 modules/process/command_unix_test.go create mode 100644 modules/process/command_windows.go create mode 100644 modules/process/context.go create mode 100644 modules/process/manager.go create mode 100644 modules/process/manager_stacktraces.go create mode 100644 modules/process/manager_test.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/manifest.go create mode 100644 modules/public/manifest_test.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_dynamic.go create mode 100644 modules/public/public_test.go create mode 100644 modules/public/vitedev.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_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/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/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/env.go create mode 100644 modules/repository/fork.go create mode 100644 modules/repository/fork_test.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/reqctx/datastore.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/key.go create mode 100644 modules/session/mem.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/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/audit.go create mode 100644 modules/setting/audit_test.go create mode 100644 modules/setting/cache.go create mode 100644 modules/setting/cache_test.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_option_instance.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/federation.go create mode 100644 modules/setting/git.go create mode 100644 modules/setting/git_test.go create mode 100644 modules/setting/glob.go create mode 100644 modules/setting/global.go create mode 100644 modules/setting/global_lock.go create mode 100644 modules/setting/global_lock_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/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/markup_test.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/queue_test.go create mode 100644 modules/setting/redis.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/repository_test.go create mode 100644 modules/setting/security.go create mode 100644 modules/setting/security_test.go create mode 100644 modules/setting/server.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/session_test.go create mode 100644 modules/setting/setting.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/testenv.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/setting/websocket.go create mode 100644 modules/setting/websocket_test.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/server.go create mode 100644 modules/ssh/ssh.go create mode 100644 modules/ssh/ssh_test.go create mode 100644 modules/storage/azureblob.go create mode 100644 modules/storage/azureblob_test.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/admin_user.go create mode 100644 modules/structs/attachment.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/form.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/hook_test.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/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/project.go create mode 100644 modules/structs/pull.go create mode 100644 modules/structs/pull_review.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_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/token.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/svg/processor.go create mode 100644 modules/svg/processor_test.go create mode 100644 modules/svg/svg.go create mode 100644 modules/svg/svg_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/tailmsg/talimsg.go create mode 100644 modules/tempdir/tempdir.go create mode 100644 modules/tempdir/tempdir_test.go create mode 100644 modules/templates/base.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/mail.go create mode 100644 modules/templates/page.go create mode 100644 modules/templates/scopedtmpl/scopedtmpl.go create mode 100644 modules/templates/scopedtmpl/scopedtmpl_test.go create mode 100644 modules/templates/templates_bindata.go create mode 100644 modules/templates/templates_dynamic.go create mode 100644 modules/templates/util_actions.go create mode 100644 modules/templates/util_avatar.go create mode 100644 modules/templates/util_date.go create mode 100644 modules/templates/util_date_test.go create mode 100644 modules/templates/util_dict.go create mode 100644 modules/templates/util_format.go create mode 100644 modules/templates/util_format_test.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_comment.go create mode 100644 modules/templates/util_render_comment_test.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_test.go create mode 100644 modules/templates/vars/vars.go create mode 100644 modules/templates/vars/vars_test.go create mode 100644 modules/test/file.go create mode 100644 modules/test/logchecker.go create mode 100644 modules/test/logchecker_test.go create mode 100644 modules/test/redis.go create mode 100644 modules/test/utils.go create mode 100644 modules/testlogger/testlogger.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/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/turnstile/turnstile_test.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/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/buffer.go create mode 100644 modules/util/bytesize.go create mode 100644 modules/util/bytesize_test.go create mode 100644 modules/util/color.go create mode 100644 modules/util/color_test.go create mode 100644 modules/util/diff_slice_test.go create mode 100644 modules/util/error.go create mode 100644 modules/util/error_test.go create mode 100644 modules/util/file_retry.go create mode 100644 modules/util/file_retry_test.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/filename.go create mode 100644 modules/util/filename_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/rotatingfilewriter/writer.go create mode 100644 modules/util/rotatingfilewriter/writer_test.go create mode 100644 modules/util/runtime.go create mode 100644 modules/util/runtime_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/sync.go create mode 100644 modules/util/sync_test.go create mode 100644 modules/util/time_str.go create mode 100644 modules/util/time_str_test.go create mode 100644 modules/util/timer.go create mode 100644 modules/util/timer_test.go create mode 100644 modules/util/trie.go create mode 100644 modules/util/trie_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/validurl_test.go create mode 100644 modules/web/handler.go create mode 100644 modules/web/middleware/binding.go create mode 100644 modules/web/middleware/binding_test.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/locale_test.go create mode 100644 modules/web/routemock.go create mode 100644 modules/web/routemock_test.go create mode 100644 modules/web/router.go create mode 100644 modules/web/router_combo.go create mode 100644 modules/web/router_path.go create mode 100644 modules/web/router_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/requestinfo.go create mode 100644 modules/web/routing/requestrecord.go create mode 100644 modules/web/types/premiddleware.go create mode 100644 modules/web/types/response.go create mode 100644 modules/webhook/events.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 create mode 100644 options/gitignore/AL create mode 100644 options/gitignore/Actionscript create mode 100644 options/gitignore/Ada create mode 100644 options/gitignore/Agda create mode 100644 options/gitignore/Alteryx create mode 100644 options/gitignore/AltiumDesigner create mode 100644 options/gitignore/Android create mode 100644 options/gitignore/Anjuta create mode 100644 options/gitignore/Ansible create mode 100644 options/gitignore/AppEngine create mode 100644 options/gitignore/AppceleratorTitanium create mode 100644 options/gitignore/ArchLinuxPackages create mode 100644 options/gitignore/Archives create mode 100644 options/gitignore/AtmelStudio create mode 100644 options/gitignore/AutoIt create mode 100644 options/gitignore/AutomationStudio create mode 100644 options/gitignore/Autotools create mode 100644 options/gitignore/B4X create mode 100644 options/gitignore/Backup create mode 100644 options/gitignore/Ballerina create mode 100644 options/gitignore/Bazaar create mode 100644 options/gitignore/Bazel create mode 100644 options/gitignore/Beef create mode 100644 options/gitignore/Bitrix create mode 100644 options/gitignore/BricxCC create mode 100644 options/gitignore/C create mode 100644 options/gitignore/C++ create mode 100644 options/gitignore/CDK create mode 100644 options/gitignore/CFWheels create mode 100644 options/gitignore/CMake create mode 100644 options/gitignore/CUDA create mode 100644 options/gitignore/CVS create mode 100644 options/gitignore/CakePHP create mode 100644 options/gitignore/Calabash create mode 100644 options/gitignore/ChefCookbook create mode 100644 options/gitignore/Clojure create mode 100644 options/gitignore/Cloud9 create mode 100644 options/gitignore/CodeIgniter create mode 100644 options/gitignore/CodeKit create mode 100644 options/gitignore/CodeSniffer create mode 100644 options/gitignore/CommonLisp create mode 100644 options/gitignore/Composer create mode 100644 options/gitignore/Concrete5 create mode 100644 options/gitignore/Coq create mode 100644 options/gitignore/Cordova create mode 100644 options/gitignore/CraftCMS create mode 100644 options/gitignore/D create mode 100644 options/gitignore/DM create mode 100644 options/gitignore/Dart create mode 100644 options/gitignore/DartEditor create mode 100644 options/gitignore/Delphi create mode 100644 options/gitignore/Diff create mode 100644 options/gitignore/Dreamweaver create mode 100644 options/gitignore/Dropbox create mode 100644 options/gitignore/Drupal create mode 100644 options/gitignore/Drupal7 create mode 100644 options/gitignore/ECU-TEST create mode 100644 options/gitignore/EPiServer create mode 100644 options/gitignore/Eagle create mode 100644 options/gitignore/Eclipse create mode 100644 options/gitignore/EiffelStudio create mode 100644 options/gitignore/Elisp create mode 100644 options/gitignore/Elixir create mode 100644 options/gitignore/Elm create mode 100644 options/gitignore/Emacs create mode 100644 options/gitignore/Ensime create mode 100644 options/gitignore/Erlang create mode 100644 options/gitignore/Espresso create mode 100644 options/gitignore/Exercism create mode 100644 options/gitignore/ExpressionEngine create mode 100644 options/gitignore/ExtJs create mode 100644 options/gitignore/Fancy create mode 100644 options/gitignore/Finale create mode 100644 options/gitignore/Firebase create mode 100644 options/gitignore/FlaxEngine create mode 100644 options/gitignore/FlexBuilder create mode 100644 options/gitignore/Flutter create mode 100644 options/gitignore/ForceDotCom create mode 100644 options/gitignore/Fortran create mode 100644 options/gitignore/FuelPHP create mode 100644 options/gitignore/GNOMEShellExtension create mode 100644 options/gitignore/GPG create mode 100644 options/gitignore/GWT create mode 100644 options/gitignore/Gcov create mode 100644 options/gitignore/GitBook create mode 100644 options/gitignore/GitHubPages create mode 100644 options/gitignore/Go create mode 100644 options/gitignore/Go.AllowList create mode 100644 options/gitignore/Godot create mode 100644 options/gitignore/Gradle create mode 100644 options/gitignore/Grails create mode 100644 options/gitignore/Gretl create mode 100644 options/gitignore/Haskell create mode 100644 options/gitignore/Hexo create mode 100644 options/gitignore/Hugo create mode 100644 options/gitignore/IAR create mode 100644 options/gitignore/IAR_EWARM create mode 100644 options/gitignore/IGORPro create mode 100644 options/gitignore/Idris create mode 100644 options/gitignore/Images create mode 100644 options/gitignore/InforCMS create mode 100644 options/gitignore/JBoss create mode 100644 options/gitignore/JBoss4 create mode 100644 options/gitignore/JBoss6 create mode 100644 options/gitignore/JDeveloper create mode 100644 options/gitignore/JENKINS_HOME create mode 100644 options/gitignore/JEnv create mode 100644 options/gitignore/Java create mode 100644 options/gitignore/Jekyll create mode 100644 options/gitignore/JetBrains create mode 100644 options/gitignore/Jigsaw create mode 100644 options/gitignore/Joomla create mode 100644 options/gitignore/Julia create mode 100644 options/gitignore/JupyterNotebooks create mode 100644 options/gitignore/KDevelop4 create mode 100644 options/gitignore/Kate create mode 100644 options/gitignore/Kentico create mode 100644 options/gitignore/KiCad create mode 100644 options/gitignore/Kohana create mode 100644 options/gitignore/Kotlin create mode 100644 options/gitignore/LabVIEW create mode 100644 options/gitignore/Laravel create mode 100644 options/gitignore/Lazarus create mode 100644 options/gitignore/Leiningen create mode 100644 options/gitignore/LemonStand create mode 100644 options/gitignore/LensStudio create mode 100644 options/gitignore/LibreOffice create mode 100644 options/gitignore/Lilypond create mode 100644 options/gitignore/Linux create mode 100644 options/gitignore/Lithium create mode 100644 options/gitignore/Logtalk create mode 100644 options/gitignore/Lua create mode 100644 options/gitignore/LyX create mode 100644 options/gitignore/MATLAB create mode 100644 options/gitignore/Magento create mode 100644 options/gitignore/Magento1 create mode 100644 options/gitignore/Magento2 create mode 100644 options/gitignore/Maven create mode 100644 options/gitignore/Mercurial create mode 100644 options/gitignore/Mercury create mode 100644 options/gitignore/MetaProgrammingSystem create mode 100644 options/gitignore/Metals create mode 100644 options/gitignore/Meteor create mode 100644 options/gitignore/MicrosoftOffice create mode 100644 options/gitignore/ModelSim create mode 100644 options/gitignore/Modelica create mode 100644 options/gitignore/Momentics create mode 100644 options/gitignore/MonoDevelop create mode 100644 options/gitignore/Move create mode 100644 options/gitignore/NWjs create mode 100644 options/gitignore/Nanoc create mode 100644 options/gitignore/NasaSpecsIntact create mode 100644 options/gitignore/NetBeans create mode 100644 options/gitignore/Nikola create mode 100644 options/gitignore/Nim create mode 100644 options/gitignore/Ninja create mode 100644 options/gitignore/Nix create mode 100644 options/gitignore/Node create mode 100644 options/gitignore/NotepadPP create mode 100644 options/gitignore/NotesAndCoreConfiguration create mode 100644 options/gitignore/NotesAndExtendedConfiguration create mode 100644 options/gitignore/NotesOnly create mode 100644 options/gitignore/OCaml create mode 100644 options/gitignore/Objective-C create mode 100644 options/gitignore/Octave create mode 100644 options/gitignore/Opa create mode 100644 options/gitignore/OpenCart create mode 100644 options/gitignore/OpenSSL create mode 100644 options/gitignore/OpenTofu create mode 100644 options/gitignore/OracleForms create mode 100644 options/gitignore/Otto create mode 100644 options/gitignore/PSoCCreator create mode 100644 options/gitignore/Packer create mode 100644 options/gitignore/Patch create mode 100644 options/gitignore/Perl create mode 100644 options/gitignore/Perl6 create mode 100644 options/gitignore/Phalcon create mode 100644 options/gitignore/Phoenix create mode 100644 options/gitignore/Pimcore create mode 100644 options/gitignore/PlayFramework create mode 100644 options/gitignore/Plone create mode 100644 options/gitignore/Prestashop create mode 100644 options/gitignore/Processing create mode 100644 options/gitignore/PuTTY create mode 100644 options/gitignore/Puppet create mode 100644 options/gitignore/PureScript create mode 100644 options/gitignore/Python create mode 100644 options/gitignore/Qooxdoo create mode 100644 options/gitignore/Qt create mode 100644 options/gitignore/R create mode 100644 options/gitignore/ROS create mode 100644 options/gitignore/ROS2 create mode 100644 options/gitignore/Racket create mode 100644 options/gitignore/Rails create mode 100644 options/gitignore/Raku create mode 100644 options/gitignore/ReScript create mode 100644 options/gitignore/Red create mode 100644 options/gitignore/Redcar create mode 100644 options/gitignore/Redis create mode 100644 options/gitignore/RhodesRhomobile create mode 100644 options/gitignore/Ruby create mode 100644 options/gitignore/Rust create mode 100644 options/gitignore/SAM create mode 100644 options/gitignore/SBT create mode 100644 options/gitignore/SCons create mode 100644 options/gitignore/SPFx create mode 100644 options/gitignore/SVN create mode 100644 options/gitignore/Sass create mode 100644 options/gitignore/Scala create mode 100644 options/gitignore/Scheme create mode 100644 options/gitignore/Scrivener create mode 100644 options/gitignore/Sdcc create mode 100644 options/gitignore/SeamGen create mode 100644 options/gitignore/SketchUp create mode 100644 options/gitignore/SlickEdit create mode 100644 options/gitignore/Smalltalk create mode 100644 options/gitignore/Snap create mode 100644 options/gitignore/Splunk create mode 100644 options/gitignore/Stata create mode 100644 options/gitignore/Stella create mode 100644 options/gitignore/Strapi create mode 100644 options/gitignore/SublimeText create mode 100644 options/gitignore/SugarCRM create mode 100644 options/gitignore/Swift create mode 100644 options/gitignore/Symfony create mode 100644 options/gitignore/SymphonyCMS create mode 100644 options/gitignore/Syncthing create mode 100644 options/gitignore/SynopsysVCS create mode 100644 options/gitignore/Tags create mode 100644 options/gitignore/TeX create mode 100644 options/gitignore/Terraform create mode 100644 options/gitignore/Terragrunt create mode 100644 options/gitignore/TextMate create mode 100644 options/gitignore/Textpattern create mode 100644 options/gitignore/ThinkPHP create mode 100644 options/gitignore/Toit create mode 100644 options/gitignore/TortoiseGit create mode 100644 options/gitignore/TurboGears2 create mode 100644 options/gitignore/TwinCAT3 create mode 100644 options/gitignore/Typo3 create mode 100644 options/gitignore/UiPath create mode 100644 options/gitignore/Umbraco create mode 100644 options/gitignore/Unity create mode 100644 options/gitignore/UnrealEngine create mode 100644 options/gitignore/V create mode 100644 options/gitignore/VVVV create mode 100644 options/gitignore/Vagrant create mode 100644 options/gitignore/Vim create mode 100644 options/gitignore/VirtualEnv create mode 100644 options/gitignore/Virtuoso create mode 100644 options/gitignore/VisualStudio create mode 100644 options/gitignore/VisualStudioCode create mode 100644 options/gitignore/Vue create mode 100644 options/gitignore/Waf create mode 100644 options/gitignore/WebMethods create mode 100644 options/gitignore/Windows create mode 100644 options/gitignore/WordPress create mode 100644 options/gitignore/Xcode create mode 100644 options/gitignore/Xilinx create mode 100644 options/gitignore/XilinxISE create mode 100644 options/gitignore/Xojo create mode 100644 options/gitignore/Yeoman create mode 100644 options/gitignore/Yii create mode 100644 options/gitignore/ZendFramework create mode 100644 options/gitignore/Zephir create mode 100644 options/gitignore/Zig create mode 100644 options/gitignore/core create mode 100644 options/gitignore/esp-idf create mode 100644 options/gitignore/macOS create mode 100644 options/gitignore/uVision create mode 100644 options/label/Advanced.yaml create mode 100644 options/label/Default create mode 100644 options/license/0BSD create mode 100644 options/license/AGPL-3.0 create mode 100644 options/license/Apache-2.0 create mode 100644 options/license/BSD-2-Clause create mode 100644 options/license/BSD-3-Clause create mode 100644 options/license/BSD-3-Clause-Clear create mode 100644 options/license/BSL-1.0 create mode 100644 options/license/CC-BY-4.0 create mode 100644 options/license/CC-BY-SA-4.0 create mode 100644 options/license/CC0-1.0 create mode 100644 options/license/EPL-1.0 create mode 100644 options/license/EPL-2.0 create mode 100644 options/license/EUPL-1.2 create mode 100644 options/license/GPL-2.0 create mode 100644 options/license/GPL-3.0 create mode 100644 options/license/ISC create mode 100644 options/license/LGPL-2.1 create mode 100644 options/license/LGPL-3.0 create mode 100644 options/license/MIT create mode 100644 options/license/MIT-0 create mode 100644 options/license/MPL-2.0 create mode 100644 options/license/MulanPSL-2.0 create mode 100644 options/license/OFL-1.1 create mode 100644 options/license/OSL-3.0 create mode 100644 options/license/UPL-1.0 create mode 100644 options/license/Unlicense create mode 100644 options/license/WTFPL create mode 100644 options/license/Zlib create mode 100644 options/locale/TRANSLATORS create mode 100644 options/locale/locale_fi-FI.json create mode 100644 options/locale/locale_hu-HU.json create mode 100644 options/locale/locale_id-ID.json create mode 100644 options/locale/locale_is-IS.json create mode 100644 options/locale/locale_sk-SK.json create mode 100644 options/locale/locale_sv-SE.json create mode 100644 options/readme/Default create mode 100644 package.json create mode 100644 playwright.config.ts create mode 100644 pnpm-workspace.yaml create mode 100644 public/.well-known/security.txt create mode 100644 public/assets/img/apple-touch-icon.png create mode 100644 public/assets/img/avatar_default.png create mode 100644 public/assets/img/checkmark.png create mode 100644 public/assets/img/dingtalk.ico create mode 100644 public/assets/img/discord.png create mode 100644 public/assets/img/emoji/codeberg.png create mode 100644 public/assets/img/emoji/git.png create mode 100644 public/assets/img/emoji/gitea.png create mode 100644 public/assets/img/emoji/github.png create mode 100644 public/assets/img/emoji/gitlab.png create mode 100644 public/assets/img/emoji/gogs.png create mode 100644 public/assets/img/failed.png create mode 100644 public/assets/img/favicon.png create mode 100644 public/assets/img/favicon.svg create mode 100644 public/assets/img/gitea.svg create mode 100644 public/assets/img/gogs.png create mode 100644 public/assets/img/loading.png create mode 100644 public/assets/img/logo.png create mode 100644 public/assets/img/logo.svg create mode 100644 public/assets/img/msteams.png create mode 100644 public/assets/img/openid-16x16.png create mode 100644 public/assets/img/packagist.png create mode 100644 public/assets/img/repo_default.png create mode 100644 public/assets/img/slack.png create mode 100644 public/assets/img/svg/fontawesome-openid.svg create mode 100644 public/assets/img/svg/fontawesome-save.svg create mode 100644 public/assets/img/svg/fontawesome-send.svg create mode 100644 public/assets/img/svg/fontawesome-windows.svg create mode 100644 public/assets/img/svg/gitea-alpine.svg create mode 100644 public/assets/img/svg/gitea-arch.svg create mode 100644 public/assets/img/svg/gitea-azuread.svg create mode 100644 public/assets/img/svg/gitea-azureadv2.svg create mode 100644 public/assets/img/svg/gitea-bitbucket.svg create mode 100644 public/assets/img/svg/gitea-cargo.svg create mode 100644 public/assets/img/svg/gitea-chef.svg create mode 100644 public/assets/img/svg/gitea-codebase.svg create mode 100644 public/assets/img/svg/gitea-codecommit.svg create mode 100644 public/assets/img/svg/gitea-colorblind-blueyellow.svg create mode 100644 public/assets/img/svg/gitea-colorblind-redgreen.svg create mode 100644 public/assets/img/svg/gitea-composer.svg create mode 100644 public/assets/img/svg/gitea-conan.svg create mode 100644 public/assets/img/svg/gitea-conda.svg create mode 100644 public/assets/img/svg/gitea-cran.svg create mode 100644 public/assets/img/svg/gitea-debian.svg create mode 100644 public/assets/img/svg/gitea-discord.svg create mode 100644 public/assets/img/svg/gitea-double-chevron-left.svg create mode 100644 public/assets/img/svg/gitea-double-chevron-right.svg create mode 100644 public/assets/img/svg/gitea-dropbox.svg create mode 100644 public/assets/img/svg/gitea-eclipse.svg create mode 100644 public/assets/img/svg/gitea-empty-checkbox.svg create mode 100644 public/assets/img/svg/gitea-exclamation.svg create mode 100644 public/assets/img/svg/gitea-facebook.svg create mode 100644 public/assets/img/svg/gitea-feishu.svg create mode 100644 public/assets/img/svg/gitea-git.svg create mode 100644 public/assets/img/svg/gitea-gitbucket.svg create mode 100644 public/assets/img/svg/gitea-gitea.svg create mode 100644 public/assets/img/svg/gitea-gitlab.svg create mode 100644 public/assets/img/svg/gitea-go.svg create mode 100644 public/assets/img/svg/gitea-gogs.svg create mode 100644 public/assets/img/svg/gitea-google.svg create mode 100644 public/assets/img/svg/gitea-helm.svg create mode 100644 public/assets/img/svg/gitea-jetbrains.svg create mode 100644 public/assets/img/svg/gitea-join.svg create mode 100644 public/assets/img/svg/gitea-lock-cog.svg create mode 100644 public/assets/img/svg/gitea-lock.svg create mode 100644 public/assets/img/svg/gitea-mastodon.svg create mode 100644 public/assets/img/svg/gitea-matrix.svg create mode 100644 public/assets/img/svg/gitea-maven.svg create mode 100644 public/assets/img/svg/gitea-microsoftonline.svg create mode 100644 public/assets/img/svg/gitea-nextcloud.svg create mode 100644 public/assets/img/svg/gitea-npm.svg create mode 100644 public/assets/img/svg/gitea-nuget.svg create mode 100644 public/assets/img/svg/gitea-onedev.svg create mode 100644 public/assets/img/svg/gitea-openid.svg create mode 100644 public/assets/img/svg/gitea-pub.svg create mode 100644 public/assets/img/svg/gitea-python.svg create mode 100644 public/assets/img/svg/gitea-rpm.svg create mode 100644 public/assets/img/svg/gitea-rubygems.svg create mode 100644 public/assets/img/svg/gitea-running.svg create mode 100644 public/assets/img/svg/gitea-split.svg create mode 100644 public/assets/img/svg/gitea-swift.svg create mode 100644 public/assets/img/svg/gitea-terraform.svg create mode 100644 public/assets/img/svg/gitea-twitter.svg create mode 100644 public/assets/img/svg/gitea-unlock.svg create mode 100644 public/assets/img/svg/gitea-vagrant.svg create mode 100644 public/assets/img/svg/gitea-vscodium.svg create mode 100644 public/assets/img/svg/gitea-whitespace.svg create mode 100644 public/assets/img/svg/gitea-yandex.svg create mode 100644 public/assets/img/svg/material-folder-generic.svg create mode 100644 public/assets/img/svg/material-folder-symlink.svg create mode 100644 public/assets/img/svg/material-invert-colors.svg create mode 100644 public/assets/img/svg/material-palette.svg create mode 100644 public/assets/img/svg/octicon-accessibility-inset.svg create mode 100644 public/assets/img/svg/octicon-accessibility.svg create mode 100644 public/assets/img/svg/octicon-agent.svg create mode 100644 public/assets/img/svg/octicon-ai-model.svg create mode 100644 public/assets/img/svg/octicon-alert-fill.svg create mode 100644 public/assets/img/svg/octicon-alert.svg create mode 100644 public/assets/img/svg/octicon-apps.svg create mode 100644 public/assets/img/svg/octicon-archive.svg create mode 100644 public/assets/img/svg/octicon-arrow-both.svg create mode 100644 public/assets/img/svg/octicon-arrow-down-left.svg create mode 100644 public/assets/img/svg/octicon-arrow-down-right.svg create mode 100644 public/assets/img/svg/octicon-arrow-down.svg create mode 100644 public/assets/img/svg/octicon-arrow-left.svg create mode 100644 public/assets/img/svg/octicon-arrow-right.svg create mode 100644 public/assets/img/svg/octicon-arrow-switch.svg create mode 100644 public/assets/img/svg/octicon-arrow-up-left.svg create mode 100644 public/assets/img/svg/octicon-arrow-up-right.svg create mode 100644 public/assets/img/svg/octicon-arrow-up.svg create mode 100644 public/assets/img/svg/octicon-beaker.svg create mode 100644 public/assets/img/svg/octicon-bell-fill.svg create mode 100644 public/assets/img/svg/octicon-bell-slash.svg create mode 100644 public/assets/img/svg/octicon-bell.svg create mode 100644 public/assets/img/svg/octicon-blocked.svg create mode 100644 public/assets/img/svg/octicon-bold.svg create mode 100644 public/assets/img/svg/octicon-book-locked.svg create mode 100644 public/assets/img/svg/octicon-book.svg create mode 100644 public/assets/img/svg/octicon-bookmark-filled.svg create mode 100644 public/assets/img/svg/octicon-bookmark-slash-fill.svg create mode 100644 public/assets/img/svg/octicon-bookmark-slash.svg create mode 100644 public/assets/img/svg/octicon-bookmark.svg create mode 100644 public/assets/img/svg/octicon-boolean-off.svg create mode 100644 public/assets/img/svg/octicon-boolean-on.svg create mode 100644 public/assets/img/svg/octicon-briefcase.svg create mode 100644 public/assets/img/svg/octicon-broadcast.svg create mode 100644 public/assets/img/svg/octicon-browser.svg create mode 100644 public/assets/img/svg/octicon-bug.svg create mode 100644 public/assets/img/svg/octicon-cache.svg create mode 100644 public/assets/img/svg/octicon-calendar.svg create mode 100644 public/assets/img/svg/octicon-chat-locked.svg create mode 100644 public/assets/img/svg/octicon-chat-question.svg create mode 100644 public/assets/img/svg/octicon-chat.svg create mode 100644 public/assets/img/svg/octicon-check-circle-fill.svg create mode 100644 public/assets/img/svg/octicon-check-circle.svg create mode 100644 public/assets/img/svg/octicon-check.svg create mode 100644 public/assets/img/svg/octicon-checkbox-fill.svg create mode 100644 public/assets/img/svg/octicon-checkbox.svg create mode 100644 public/assets/img/svg/octicon-checklist.svg create mode 100644 public/assets/img/svg/octicon-chevron-down.svg create mode 100644 public/assets/img/svg/octicon-chevron-left.svg create mode 100644 public/assets/img/svg/octicon-chevron-right.svg create mode 100644 public/assets/img/svg/octicon-chevron-up.svg create mode 100644 public/assets/img/svg/octicon-circle-slash.svg create mode 100644 public/assets/img/svg/octicon-circle.svg create mode 100644 public/assets/img/svg/octicon-clock-fill.svg create mode 100644 public/assets/img/svg/octicon-clock.svg create mode 100644 public/assets/img/svg/octicon-cloud-offline.svg create mode 100644 public/assets/img/svg/octicon-cloud.svg create mode 100644 public/assets/img/svg/octicon-code-of-conduct.svg create mode 100644 public/assets/img/svg/octicon-code-review.svg create mode 100644 public/assets/img/svg/octicon-code-square.svg create mode 100644 public/assets/img/svg/octicon-code.svg create mode 100644 public/assets/img/svg/octicon-codescan-checkmark.svg create mode 100644 public/assets/img/svg/octicon-codescan.svg create mode 100644 public/assets/img/svg/octicon-codespaces.svg create mode 100644 public/assets/img/svg/octicon-columns.svg create mode 100644 public/assets/img/svg/octicon-command-palette.svg create mode 100644 public/assets/img/svg/octicon-comment-ai.svg create mode 100644 public/assets/img/svg/octicon-comment-discussion.svg create mode 100644 public/assets/img/svg/octicon-comment-locked.svg create mode 100644 public/assets/img/svg/octicon-comment.svg create mode 100644 public/assets/img/svg/octicon-compose.svg create mode 100644 public/assets/img/svg/octicon-container.svg create mode 100644 public/assets/img/svg/octicon-copilot-error.svg create mode 100644 public/assets/img/svg/octicon-copilot-warning.svg create mode 100644 public/assets/img/svg/octicon-copilot.svg create mode 100644 public/assets/img/svg/octicon-copy.svg create mode 100644 public/assets/img/svg/octicon-cpu.svg create mode 100644 public/assets/img/svg/octicon-credit-card.svg create mode 100644 public/assets/img/svg/octicon-cross-reference.svg create mode 100644 public/assets/img/svg/octicon-crosshairs.svg create mode 100644 public/assets/img/svg/octicon-dash.svg create mode 100644 public/assets/img/svg/octicon-database.svg create mode 100644 public/assets/img/svg/octicon-dependabot.svg create mode 100644 public/assets/img/svg/octicon-desktop-download.svg create mode 100644 public/assets/img/svg/octicon-device-camera-video.svg create mode 100644 public/assets/img/svg/octicon-device-camera.svg create mode 100644 public/assets/img/svg/octicon-device-desktop.svg create mode 100644 public/assets/img/svg/octicon-device-mobile.svg create mode 100644 public/assets/img/svg/octicon-devices.svg create mode 100644 public/assets/img/svg/octicon-diamond.svg create mode 100644 public/assets/img/svg/octicon-dice.svg create mode 100644 public/assets/img/svg/octicon-diff-added.svg create mode 100644 public/assets/img/svg/octicon-diff-ignored.svg create mode 100644 public/assets/img/svg/octicon-diff-modified.svg create mode 100644 public/assets/img/svg/octicon-diff-removed.svg create mode 100644 public/assets/img/svg/octicon-diff-renamed.svg create mode 100644 public/assets/img/svg/octicon-diff.svg create mode 100644 public/assets/img/svg/octicon-discussion-closed.svg create mode 100644 public/assets/img/svg/octicon-discussion-duplicate.svg create mode 100644 public/assets/img/svg/octicon-discussion-outdated.svg create mode 100644 public/assets/img/svg/octicon-dot-fill.svg create mode 100644 public/assets/img/svg/octicon-dot.svg create mode 100644 public/assets/img/svg/octicon-download.svg create mode 100644 public/assets/img/svg/octicon-duplicate.svg create mode 100644 public/assets/img/svg/octicon-ellipsis.svg create mode 100644 public/assets/img/svg/octicon-exclamation.svg create mode 100644 public/assets/img/svg/octicon-eye-closed.svg create mode 100644 public/assets/img/svg/octicon-eye.svg create mode 100644 public/assets/img/svg/octicon-feed-discussion.svg create mode 100644 public/assets/img/svg/octicon-feed-forked.svg create mode 100644 public/assets/img/svg/octicon-feed-heart.svg create mode 100644 public/assets/img/svg/octicon-feed-issue-closed.svg create mode 100644 public/assets/img/svg/octicon-feed-issue-draft.svg create mode 100644 public/assets/img/svg/octicon-feed-issue-open.svg create mode 100644 public/assets/img/svg/octicon-feed-issue-reopen.svg create mode 100644 public/assets/img/svg/octicon-feed-merged.svg create mode 100644 public/assets/img/svg/octicon-feed-person.svg create mode 100644 public/assets/img/svg/octicon-feed-plus.svg create mode 100644 public/assets/img/svg/octicon-feed-public.svg create mode 100644 public/assets/img/svg/octicon-feed-pull-request-closed.svg create mode 100644 public/assets/img/svg/octicon-feed-pull-request-draft.svg create mode 100644 public/assets/img/svg/octicon-feed-pull-request-open.svg create mode 100644 public/assets/img/svg/octicon-feed-repo.svg create mode 100644 public/assets/img/svg/octicon-feed-rocket.svg create mode 100644 public/assets/img/svg/octicon-feed-star.svg create mode 100644 public/assets/img/svg/octicon-feed-tag.svg create mode 100644 public/assets/img/svg/octicon-feed-trophy.svg create mode 100644 public/assets/img/svg/octicon-file-added.svg create mode 100644 public/assets/img/svg/octicon-file-badge.svg create mode 100644 public/assets/img/svg/octicon-file-binary.svg create mode 100644 public/assets/img/svg/octicon-file-check.svg create mode 100644 public/assets/img/svg/octicon-file-code.svg create mode 100644 public/assets/img/svg/octicon-file-diff.svg create mode 100644 public/assets/img/svg/octicon-file-directory-fill.svg create mode 100644 public/assets/img/svg/octicon-file-directory-open-fill.svg create mode 100644 public/assets/img/svg/octicon-file-directory-symlink.svg create mode 100644 public/assets/img/svg/octicon-file-directory.svg create mode 100644 public/assets/img/svg/octicon-file-media.svg create mode 100644 public/assets/img/svg/octicon-file-moved.svg create mode 100644 public/assets/img/svg/octicon-file-removed.svg create mode 100644 public/assets/img/svg/octicon-file-submodule.svg create mode 100644 public/assets/img/svg/octicon-file-symlink-file.svg create mode 100644 public/assets/img/svg/octicon-file-zip.svg create mode 100644 public/assets/img/svg/octicon-file.svg create mode 100644 public/assets/img/svg/octicon-filter-remove.svg create mode 100644 public/assets/img/svg/octicon-filter.svg create mode 100644 public/assets/img/svg/octicon-fiscal-host.svg create mode 100644 public/assets/img/svg/octicon-flag.svg create mode 100644 public/assets/img/svg/octicon-flame.svg create mode 100644 public/assets/img/svg/octicon-flowchart.svg create mode 100644 public/assets/img/svg/octicon-focus-center.svg create mode 100644 public/assets/img/svg/octicon-fold-down.svg create mode 100644 public/assets/img/svg/octicon-fold-up.svg create mode 100644 public/assets/img/svg/octicon-fold.svg create mode 100644 public/assets/img/svg/octicon-gear.svg create mode 100644 public/assets/img/svg/octicon-gift.svg create mode 100644 public/assets/img/svg/octicon-git-branch-check.svg create mode 100644 public/assets/img/svg/octicon-git-branch.svg create mode 100644 public/assets/img/svg/octicon-git-commit.svg create mode 100644 public/assets/img/svg/octicon-git-compare.svg create mode 100644 public/assets/img/svg/octicon-git-merge-queue.svg create mode 100644 public/assets/img/svg/octicon-git-merge.svg create mode 100644 public/assets/img/svg/octicon-git-pull-request-closed.svg create mode 100644 public/assets/img/svg/octicon-git-pull-request-draft.svg create mode 100644 public/assets/img/svg/octicon-git-pull-request-locked.svg create mode 100644 public/assets/img/svg/octicon-git-pull-request.svg create mode 100644 public/assets/img/svg/octicon-globe.svg create mode 100644 public/assets/img/svg/octicon-goal.svg create mode 100644 public/assets/img/svg/octicon-grabber.svg create mode 100644 public/assets/img/svg/octicon-graph-bar-horizontal.svg create mode 100644 public/assets/img/svg/octicon-graph-bar-vertical.svg create mode 100644 public/assets/img/svg/octicon-graph-stacked-area.svg create mode 100644 public/assets/img/svg/octicon-graph.svg create mode 100644 public/assets/img/svg/octicon-hash.svg create mode 100644 public/assets/img/svg/octicon-heading.svg create mode 100644 public/assets/img/svg/octicon-heart-fill.svg create mode 100644 public/assets/img/svg/octicon-heart.svg create mode 100644 public/assets/img/svg/octicon-history.svg create mode 100644 public/assets/img/svg/octicon-home-fill.svg create mode 100644 public/assets/img/svg/octicon-home.svg create mode 100644 public/assets/img/svg/octicon-horizontal-rule.svg create mode 100644 public/assets/img/svg/octicon-hourglass.svg create mode 100644 public/assets/img/svg/octicon-hubot.svg create mode 100644 public/assets/img/svg/octicon-id-badge.svg create mode 100644 public/assets/img/svg/octicon-image.svg create mode 100644 public/assets/img/svg/octicon-inbox-fill.svg create mode 100644 public/assets/img/svg/octicon-inbox.svg create mode 100644 public/assets/img/svg/octicon-infinity.svg create mode 100644 public/assets/img/svg/octicon-info.svg create mode 100644 public/assets/img/svg/octicon-issue-closed.svg create mode 100644 public/assets/img/svg/octicon-issue-draft.svg create mode 100644 public/assets/img/svg/octicon-issue-locked.svg create mode 100644 public/assets/img/svg/octicon-issue-opened.svg create mode 100644 public/assets/img/svg/octicon-issue-relates-to.svg create mode 100644 public/assets/img/svg/octicon-issue-reopened.svg create mode 100644 public/assets/img/svg/octicon-issue-tracked-by.svg create mode 100644 public/assets/img/svg/octicon-issue-tracks.svg create mode 100644 public/assets/img/svg/octicon-italic.svg create mode 100644 public/assets/img/svg/octicon-iterations.svg create mode 100644 public/assets/img/svg/octicon-kebab-horizontal.svg create mode 100644 public/assets/img/svg/octicon-key-asterisk.svg create mode 100644 public/assets/img/svg/octicon-key.svg create mode 100644 public/assets/img/svg/octicon-law.svg create mode 100644 public/assets/img/svg/octicon-library.svg create mode 100644 public/assets/img/svg/octicon-light-bulb.svg create mode 100644 public/assets/img/svg/octicon-link-external.svg create mode 100644 public/assets/img/svg/octicon-link.svg create mode 100644 public/assets/img/svg/octicon-list-ordered.svg create mode 100644 public/assets/img/svg/octicon-list-unordered.svg create mode 100644 public/assets/img/svg/octicon-location.svg create mode 100644 public/assets/img/svg/octicon-lock.svg create mode 100644 public/assets/img/svg/octicon-lockup-github.svg create mode 100644 public/assets/img/svg/octicon-log.svg create mode 100644 public/assets/img/svg/octicon-logo-gist.svg create mode 100644 public/assets/img/svg/octicon-logo-github.svg create mode 100644 public/assets/img/svg/octicon-loop.svg create mode 100644 public/assets/img/svg/octicon-mail.svg create mode 100644 public/assets/img/svg/octicon-mark-github.svg create mode 100644 public/assets/img/svg/octicon-markdown.svg create mode 100644 public/assets/img/svg/octicon-maximize.svg create mode 100644 public/assets/img/svg/octicon-mcp.svg create mode 100644 public/assets/img/svg/octicon-megaphone.svg create mode 100644 public/assets/img/svg/octicon-mention.svg create mode 100644 public/assets/img/svg/octicon-meter.svg create mode 100644 public/assets/img/svg/octicon-milestone.svg create mode 100644 public/assets/img/svg/octicon-minimize.svg create mode 100644 public/assets/img/svg/octicon-mirror.svg create mode 100644 public/assets/img/svg/octicon-moon.svg create mode 100644 public/assets/img/svg/octicon-mortar-board.svg create mode 100644 public/assets/img/svg/octicon-move-to-bottom.svg create mode 100644 public/assets/img/svg/octicon-move-to-end.svg create mode 100644 public/assets/img/svg/octicon-move-to-start.svg create mode 100644 public/assets/img/svg/octicon-move-to-top.svg create mode 100644 public/assets/img/svg/octicon-multi-select.svg create mode 100644 public/assets/img/svg/octicon-mute.svg create mode 100644 public/assets/img/svg/octicon-no-entry.svg create mode 100644 public/assets/img/svg/octicon-node.svg create mode 100644 public/assets/img/svg/octicon-north-star.svg create mode 100644 public/assets/img/svg/octicon-note.svg create mode 100644 public/assets/img/svg/octicon-number.svg create mode 100644 public/assets/img/svg/octicon-organization.svg create mode 100644 public/assets/img/svg/octicon-package-dependencies.svg create mode 100644 public/assets/img/svg/octicon-package-dependents.svg create mode 100644 public/assets/img/svg/octicon-package.svg create mode 100644 public/assets/img/svg/octicon-paintbrush.svg create mode 100644 public/assets/img/svg/octicon-paper-airplane.svg create mode 100644 public/assets/img/svg/octicon-paperclip.svg create mode 100644 public/assets/img/svg/octicon-passkey-fill.svg create mode 100644 public/assets/img/svg/octicon-paste.svg create mode 100644 public/assets/img/svg/octicon-pause.svg create mode 100644 public/assets/img/svg/octicon-pencil-ai.svg create mode 100644 public/assets/img/svg/octicon-pencil.svg create mode 100644 public/assets/img/svg/octicon-people.svg create mode 100644 public/assets/img/svg/octicon-person-add.svg create mode 100644 public/assets/img/svg/octicon-person-fill.svg create mode 100644 public/assets/img/svg/octicon-person.svg create mode 100644 public/assets/img/svg/octicon-pin-slash.svg create mode 100644 public/assets/img/svg/octicon-pin.svg create mode 100644 public/assets/img/svg/octicon-pivot-column.svg create mode 100644 public/assets/img/svg/octicon-play.svg create mode 100644 public/assets/img/svg/octicon-plug.svg create mode 100644 public/assets/img/svg/octicon-plus-circle.svg create mode 100644 public/assets/img/svg/octicon-plus.svg create mode 100644 public/assets/img/svg/octicon-project-check.svg create mode 100644 public/assets/img/svg/octicon-project-roadmap.svg create mode 100644 public/assets/img/svg/octicon-project-symlink.svg create mode 100644 public/assets/img/svg/octicon-project-template.svg create mode 100644 public/assets/img/svg/octicon-project.svg create mode 100644 public/assets/img/svg/octicon-pulse.svg create mode 100644 public/assets/img/svg/octicon-question-bubble.svg create mode 100644 public/assets/img/svg/octicon-question.svg create mode 100644 public/assets/img/svg/octicon-quote.svg create mode 100644 public/assets/img/svg/octicon-read.svg create mode 100644 public/assets/img/svg/octicon-redo.svg create mode 100644 public/assets/img/svg/octicon-rel-file-path.svg create mode 100644 public/assets/img/svg/octicon-reply.svg create mode 100644 public/assets/img/svg/octicon-repo-clone.svg create mode 100644 public/assets/img/svg/octicon-repo-deleted.svg create mode 100644 public/assets/img/svg/octicon-repo-forked-locked.svg create mode 100644 public/assets/img/svg/octicon-repo-forked.svg create mode 100644 public/assets/img/svg/octicon-repo-locked.svg create mode 100644 public/assets/img/svg/octicon-repo-pull.svg create mode 100644 public/assets/img/svg/octicon-repo-push.svg create mode 100644 public/assets/img/svg/octicon-repo-template.svg create mode 100644 public/assets/img/svg/octicon-repo.svg create mode 100644 public/assets/img/svg/octicon-report.svg create mode 100644 public/assets/img/svg/octicon-rocket.svg create mode 100644 public/assets/img/svg/octicon-rows.svg create mode 100644 public/assets/img/svg/octicon-rss.svg create mode 100644 public/assets/img/svg/octicon-ruby.svg create mode 100644 public/assets/img/svg/octicon-sandbox.svg create mode 100644 public/assets/img/svg/octicon-screen-full.svg create mode 100644 public/assets/img/svg/octicon-screen-normal.svg create mode 100644 public/assets/img/svg/octicon-search.svg create mode 100644 public/assets/img/svg/octicon-server.svg create mode 100644 public/assets/img/svg/octicon-share-android.svg create mode 100644 public/assets/img/svg/octicon-share.svg create mode 100644 public/assets/img/svg/octicon-shield-check.svg create mode 100644 public/assets/img/svg/octicon-shield-lock.svg create mode 100644 public/assets/img/svg/octicon-shield-slash.svg create mode 100644 public/assets/img/svg/octicon-shield-x.svg create mode 100644 public/assets/img/svg/octicon-shield.svg create mode 100644 public/assets/img/svg/octicon-sidebar-collapse.svg create mode 100644 public/assets/img/svg/octicon-sidebar-expand.svg create mode 100644 public/assets/img/svg/octicon-sign-in.svg create mode 100644 public/assets/img/svg/octicon-sign-out.svg create mode 100644 public/assets/img/svg/octicon-single-select.svg create mode 100644 public/assets/img/svg/octicon-skip-fill.svg create mode 100644 public/assets/img/svg/octicon-skip.svg create mode 100644 public/assets/img/svg/octicon-sliders.svg create mode 100644 public/assets/img/svg/octicon-smiley-frown.svg create mode 100644 public/assets/img/svg/octicon-smiley-frustrated.svg create mode 100644 public/assets/img/svg/octicon-smiley-grin.svg create mode 100644 public/assets/img/svg/octicon-smiley-neutral.svg create mode 100644 public/assets/img/svg/octicon-smiley.svg create mode 100644 public/assets/img/svg/octicon-sort-asc.svg create mode 100644 public/assets/img/svg/octicon-sort-desc.svg create mode 100644 public/assets/img/svg/octicon-space.svg create mode 100644 public/assets/img/svg/octicon-spacing-large.svg create mode 100644 public/assets/img/svg/octicon-spacing-medium.svg create mode 100644 public/assets/img/svg/octicon-spacing-small.svg create mode 100644 public/assets/img/svg/octicon-sparkle-fill.svg create mode 100644 public/assets/img/svg/octicon-sparkle.svg create mode 100644 public/assets/img/svg/octicon-sparkles-fill.svg create mode 100644 public/assets/img/svg/octicon-split-view.svg create mode 100644 public/assets/img/svg/octicon-sponsor-tiers.svg create mode 100644 public/assets/img/svg/octicon-square-circle.svg create mode 100644 public/assets/img/svg/octicon-square-fill.svg create mode 100644 public/assets/img/svg/octicon-square.svg create mode 100644 public/assets/img/svg/octicon-squirrel.svg create mode 100644 public/assets/img/svg/octicon-stack-add.svg create mode 100644 public/assets/img/svg/octicon-stack-check.svg create mode 100644 public/assets/img/svg/octicon-stack-remove.svg create mode 100644 public/assets/img/svg/octicon-stack.svg create mode 100644 public/assets/img/svg/octicon-star-fill.svg create mode 100644 public/assets/img/svg/octicon-star.svg create mode 100644 public/assets/img/svg/octicon-stop.svg create mode 100644 public/assets/img/svg/octicon-stopwatch.svg create mode 100644 public/assets/img/svg/octicon-strikethrough.svg create mode 100644 public/assets/img/svg/octicon-sun.svg create mode 100644 public/assets/img/svg/octicon-sync.svg create mode 100644 public/assets/img/svg/octicon-tab-external.svg create mode 100644 public/assets/img/svg/octicon-tab.svg create mode 100644 public/assets/img/svg/octicon-table.svg create mode 100644 public/assets/img/svg/octicon-tag.svg create mode 100644 public/assets/img/svg/octicon-tasklist.svg create mode 100644 public/assets/img/svg/octicon-telescope-fill.svg create mode 100644 public/assets/img/svg/octicon-telescope.svg create mode 100644 public/assets/img/svg/octicon-terminal.svg create mode 100644 public/assets/img/svg/octicon-three-bars.svg create mode 100644 public/assets/img/svg/octicon-thumbsdown.svg create mode 100644 public/assets/img/svg/octicon-thumbsup.svg create mode 100644 public/assets/img/svg/octicon-tools.svg create mode 100644 public/assets/img/svg/octicon-tracked-by-closed-completed.svg create mode 100644 public/assets/img/svg/octicon-tracked-by-closed-not-planned.svg create mode 100644 public/assets/img/svg/octicon-trash.svg create mode 100644 public/assets/img/svg/octicon-triangle-down.svg create mode 100644 public/assets/img/svg/octicon-triangle-left.svg create mode 100644 public/assets/img/svg/octicon-triangle-right.svg create mode 100644 public/assets/img/svg/octicon-triangle-up.svg create mode 100644 public/assets/img/svg/octicon-trophy.svg create mode 100644 public/assets/img/svg/octicon-typography.svg create mode 100644 public/assets/img/svg/octicon-undo.svg create mode 100644 public/assets/img/svg/octicon-unfold.svg create mode 100644 public/assets/img/svg/octicon-unlink.svg create mode 100644 public/assets/img/svg/octicon-unlock.svg create mode 100644 public/assets/img/svg/octicon-unmute.svg create mode 100644 public/assets/img/svg/octicon-unread.svg create mode 100644 public/assets/img/svg/octicon-unverified.svg create mode 100644 public/assets/img/svg/octicon-unwrap.svg create mode 100644 public/assets/img/svg/octicon-upload.svg create mode 100644 public/assets/img/svg/octicon-verified.svg create mode 100644 public/assets/img/svg/octicon-versions.svg create mode 100644 public/assets/img/svg/octicon-video.svg create mode 100644 public/assets/img/svg/octicon-view-files.svg create mode 100644 public/assets/img/svg/octicon-vscode.svg create mode 100644 public/assets/img/svg/octicon-webhook.svg create mode 100644 public/assets/img/svg/octicon-workflow.svg create mode 100644 public/assets/img/svg/octicon-wrap.svg create mode 100644 public/assets/img/svg/octicon-x-circle-fill.svg create mode 100644 public/assets/img/svg/octicon-x-circle.svg create mode 100644 public/assets/img/svg/octicon-x.svg create mode 100644 public/assets/img/svg/octicon-zap.svg create mode 100644 public/assets/img/svg/octicon-zoom-in.svg create mode 100644 public/assets/img/svg/octicon-zoom-out.svg create mode 100644 public/assets/img/telegram.png create mode 100644 public/assets/img/wechatwork.png create mode 100644 pyproject.toml create mode 100644 renovate.json5 create mode 100644 routers/api/actions/actions.go create mode 100644 routers/api/actions/artifact.pb.go create mode 100644 routers/api/actions/artifact.proto create mode 100644 routers/api/actions/artifacts.go create mode 100644 routers/api/actions/artifacts_chunks.go create mode 100644 routers/api/actions/artifacts_utils.go create mode 100644 routers/api/actions/artifactsv4.go create mode 100644 routers/api/actions/job_summary.go create mode 100644 routers/api/actions/ping/ping.go create mode 100644 routers/api/actions/ping/ping_test.go create mode 100644 routers/api/actions/runner/interceptor.go create mode 100644 routers/api/actions/runner/runner.go create mode 100644 routers/api/actions/runner/runner_test.go create mode 100644 routers/api/packages/README.md create mode 100644 routers/api/packages/alpine/alpine.go create mode 100644 routers/api/packages/api.go create mode 100644 routers/api/packages/arch/arch.go create mode 100644 routers/api/packages/auth.go create mode 100644 routers/api/packages/cargo/cargo.go create mode 100644 routers/api/packages/chef/auth.go create mode 100644 routers/api/packages/chef/chef.go create mode 100644 routers/api/packages/composer/api.go create mode 100644 routers/api/packages/composer/composer.go create mode 100644 routers/api/packages/conan/conan.go create mode 100644 routers/api/packages/conan/search.go create mode 100644 routers/api/packages/conda/conda.go create mode 100644 routers/api/packages/container/blob.go create mode 100644 routers/api/packages/container/container.go create mode 100644 routers/api/packages/container/errors.go create mode 100644 routers/api/packages/container/manifest.go create mode 100644 routers/api/packages/cran/cran.go create mode 100644 routers/api/packages/debian/debian.go create mode 100644 routers/api/packages/generic/generic.go create mode 100644 routers/api/packages/generic/generic_test.go create mode 100644 routers/api/packages/goproxy/goproxy.go create mode 100644 routers/api/packages/helm/helm.go create mode 100644 routers/api/packages/helper/helper.go create mode 100644 routers/api/packages/maven/api.go create mode 100644 routers/api/packages/maven/maven.go create mode 100644 routers/api/packages/maven/maven_test.go create mode 100644 routers/api/packages/npm/api.go create mode 100644 routers/api/packages/npm/api_test.go create mode 100644 routers/api/packages/npm/npm.go create mode 100644 routers/api/packages/nuget/api_v2.go create mode 100644 routers/api/packages/nuget/api_v3.go create mode 100644 routers/api/packages/nuget/auth.go create mode 100644 routers/api/packages/nuget/links.go create mode 100644 routers/api/packages/nuget/nuget.go create mode 100644 routers/api/packages/pub/pub.go create mode 100644 routers/api/packages/pypi/pypi.go create mode 100644 routers/api/packages/pypi/pypi_test.go create mode 100644 routers/api/packages/rpm/rpm.go create mode 100644 routers/api/packages/rubygems/rubygems.go create mode 100644 routers/api/packages/rubygems/rubygems_test.go create mode 100644 routers/api/packages/swift/swift.go create mode 100644 routers/api/packages/terraform/terraform.go create mode 100644 routers/api/packages/terraform/terraform_test.go create mode 100644 routers/api/packages/vagrant/vagrant.go create mode 100644 routers/api/v1/activitypub/person.go create mode 100644 routers/api/v1/admin/action.go create mode 100644 routers/api/v1/admin/adopt.go create mode 100644 routers/api/v1/admin/cron.go create mode 100644 routers/api/v1/admin/email.go create mode 100644 routers/api/v1/admin/hooks.go create mode 100644 routers/api/v1/admin/org.go create mode 100644 routers/api/v1/admin/packages.go create mode 100644 routers/api/v1/admin/repo.go create mode 100644 routers/api/v1/admin/runners.go create mode 100644 routers/api/v1/admin/user.go create mode 100644 routers/api/v1/admin/user_badge.go create mode 100644 routers/api/v1/api.go create mode 100644 routers/api/v1/api_test.go create mode 100644 routers/api/v1/misc/gitignore.go create mode 100644 routers/api/v1/misc/label_templates.go create mode 100644 routers/api/v1/misc/licenses.go create mode 100644 routers/api/v1/misc/markup.go create mode 100644 routers/api/v1/misc/markup_test.go create mode 100644 routers/api/v1/misc/signing.go create mode 100644 routers/api/v1/misc/version.go create mode 100644 routers/api/v1/notify/notifications.go create mode 100644 routers/api/v1/notify/repo.go create mode 100644 routers/api/v1/notify/threads.go create mode 100644 routers/api/v1/notify/user.go create mode 100644 routers/api/v1/org/action.go create mode 100644 routers/api/v1/org/avatar.go create mode 100644 routers/api/v1/org/block.go create mode 100644 routers/api/v1/org/hook.go create mode 100644 routers/api/v1/org/label.go create mode 100644 routers/api/v1/org/member.go create mode 100644 routers/api/v1/org/org.go create mode 100644 routers/api/v1/org/team.go create mode 100644 routers/api/v1/packages/package.go create mode 100644 routers/api/v1/repo/action.go create mode 100644 routers/api/v1/repo/actions_run.go create mode 100644 routers/api/v1/repo/avatar.go create mode 100644 routers/api/v1/repo/blob.go create mode 100644 routers/api/v1/repo/branch.go create mode 100644 routers/api/v1/repo/collaborators.go create mode 100644 routers/api/v1/repo/commits.go create mode 100644 routers/api/v1/repo/compare.go create mode 100644 routers/api/v1/repo/download.go create mode 100644 routers/api/v1/repo/file.go create mode 100644 routers/api/v1/repo/fork.go create mode 100644 routers/api/v1/repo/git_hook.go create mode 100644 routers/api/v1/repo/git_ref.go create mode 100644 routers/api/v1/repo/hook.go create mode 100644 routers/api/v1/repo/hook_test.go create mode 100644 routers/api/v1/repo/issue.go create mode 100644 routers/api/v1/repo/issue_assignee.go create mode 100644 routers/api/v1/repo/issue_attachment.go create mode 100644 routers/api/v1/repo/issue_comment.go create mode 100644 routers/api/v1/repo/issue_comment_attachment.go create mode 100644 routers/api/v1/repo/issue_dependency.go create mode 100644 routers/api/v1/repo/issue_label.go create mode 100644 routers/api/v1/repo/issue_lock.go create mode 100644 routers/api/v1/repo/issue_pin.go create mode 100644 routers/api/v1/repo/issue_reaction.go create mode 100644 routers/api/v1/repo/issue_stopwatch.go create mode 100644 routers/api/v1/repo/issue_subscription.go create mode 100644 routers/api/v1/repo/issue_tracked_time.go create mode 100644 routers/api/v1/repo/key.go create mode 100644 routers/api/v1/repo/label.go create mode 100644 routers/api/v1/repo/language.go create mode 100644 routers/api/v1/repo/license.go create mode 100644 routers/api/v1/repo/main_test.go create mode 100644 routers/api/v1/repo/migrate.go create mode 100644 routers/api/v1/repo/milestone.go create mode 100644 routers/api/v1/repo/mirror.go create mode 100644 routers/api/v1/repo/mirror_test.go create mode 100644 routers/api/v1/repo/notes.go create mode 100644 routers/api/v1/repo/patch.go create mode 100644 routers/api/v1/repo/pull.go create mode 100644 routers/api/v1/repo/pull_review.go create mode 100644 routers/api/v1/repo/release.go create mode 100644 routers/api/v1/repo/release_attachment.go create mode 100644 routers/api/v1/repo/release_tags.go create mode 100644 routers/api/v1/repo/repo.go create mode 100644 routers/api/v1/repo/repo_test.go create mode 100644 routers/api/v1/repo/star.go create mode 100644 routers/api/v1/repo/status.go create mode 100644 routers/api/v1/repo/subscriber.go create mode 100644 routers/api/v1/repo/tag.go create mode 100644 routers/api/v1/repo/teams.go create mode 100644 routers/api/v1/repo/topic.go create mode 100644 routers/api/v1/repo/transfer.go create mode 100644 routers/api/v1/repo/tree.go create mode 100644 routers/api/v1/repo/wiki.go create mode 100644 routers/api/v1/settings/settings.go create mode 100644 routers/api/v1/shared/action.go create mode 100644 routers/api/v1/shared/block.go create mode 100644 routers/api/v1/shared/project.go create mode 100644 routers/api/v1/shared/runners.go create mode 100644 routers/api/v1/swagger/action.go create mode 100644 routers/api/v1/swagger/activity.go create mode 100644 routers/api/v1/swagger/app.go create mode 100644 routers/api/v1/swagger/cron.go create mode 100644 routers/api/v1/swagger/issue.go create mode 100644 routers/api/v1/swagger/key.go create mode 100644 routers/api/v1/swagger/misc.go create mode 100644 routers/api/v1/swagger/notify.go create mode 100644 routers/api/v1/swagger/options.go create mode 100644 routers/api/v1/swagger/org.go create mode 100644 routers/api/v1/swagger/package.go create mode 100644 routers/api/v1/swagger/project.go create mode 100644 routers/api/v1/swagger/repo.go create mode 100644 routers/api/v1/swagger/settings.go create mode 100644 routers/api/v1/swagger/user.go create mode 100644 routers/api/v1/token/token.go create mode 100644 routers/api/v1/user/action.go create mode 100644 routers/api/v1/user/app.go create mode 100644 routers/api/v1/user/avatar.go create mode 100644 routers/api/v1/user/block.go create mode 100644 routers/api/v1/user/email.go create mode 100644 routers/api/v1/user/follower.go create mode 100644 routers/api/v1/user/gpg_key.go create mode 100644 routers/api/v1/user/helper.go create mode 100644 routers/api/v1/user/hook.go create mode 100644 routers/api/v1/user/key.go create mode 100644 routers/api/v1/user/repo.go create mode 100644 routers/api/v1/user/runners.go create mode 100644 routers/api/v1/user/settings.go create mode 100644 routers/api/v1/user/star.go create mode 100644 routers/api/v1/user/user.go create mode 100644 routers/api/v1/user/watch.go create mode 100644 routers/api/v1/utils/git.go create mode 100644 routers/api/v1/utils/hook.go create mode 100644 routers/api/v1/utils/hook_test.go create mode 100644 routers/api/v1/utils/main_test.go create mode 100644 routers/api/v1/utils/page.go create mode 100644 routers/api/v1/utils/sort.go create mode 100644 routers/api/v1/utils/sort_test.go create mode 100644 routers/common/actions.go create mode 100644 routers/common/audit.go create mode 100644 routers/common/auth.go create mode 100644 routers/common/blockexpensive.go create mode 100644 routers/common/blockexpensive_test.go create mode 100644 routers/common/codesearch.go create mode 100644 routers/common/compare.go create mode 100644 routers/common/compare_test.go create mode 100644 routers/common/db.go create mode 100644 routers/common/deadline.go create mode 100644 routers/common/errpage.go create mode 100644 routers/common/errpage_test.go create mode 100644 routers/common/issue_filter.go create mode 100644 routers/common/issue_filter_test.go create mode 100644 routers/common/lfs.go create mode 100644 routers/common/maintenancemode.go create mode 100644 routers/common/markup.go create mode 100644 routers/common/middleware.go create mode 100644 routers/common/pagetmpl.go create mode 100644 routers/common/qos.go create mode 100644 routers/common/qos_test.go create mode 100644 routers/common/redirect.go create mode 100644 routers/common/redirect_test.go create mode 100644 routers/common/serve.go create mode 100644 routers/init.go create mode 100644 routers/install/install.go create mode 100644 routers/install/install_test.go create mode 100644 routers/install/routes.go create mode 100644 routers/private/actions.go create mode 100644 routers/private/hook_post_receive.go create mode 100644 routers/private/hook_post_receive_test.go create mode 100644 routers/private/hook_pre_receive.go create mode 100644 routers/private/hook_pre_receive_test.go create mode 100644 routers/private/hook_proc_receive.go create mode 100644 routers/private/hook_verification.go create mode 100644 routers/private/hook_verification_test.go create mode 100644 routers/private/internal.go create mode 100644 routers/private/internal_repo.go create mode 100644 routers/private/key.go create mode 100644 routers/private/mail.go create mode 100644 routers/private/main_test.go create mode 100644 routers/private/manager.go create mode 100644 routers/private/manager_process.go create mode 100644 routers/private/manager_unix.go create mode 100644 routers/private/manager_windows.go create mode 100644 routers/private/restore_repo.go create mode 100644 routers/private/serv.go create mode 100644 routers/private/ssh_log.go create mode 100644 routers/private/tests/repos/repo1_hook_verification/HEAD create mode 100644 routers/private/tests/repos/repo1_hook_verification/config create mode 100644 routers/private/tests/repos/repo1_hook_verification/info/refs create mode 100644 routers/private/tests/repos/repo1_hook_verification/logs/HEAD create mode 100644 routers/private/tests/repos/repo1_hook_verification/logs/refs/heads/main create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/0b/5987362fe3fabdd4406babdc819642ee2f5a2a create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/13/b0f23f673b161f4b5cb66f051cb93c99729e1e create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/23/33a51fdb238b7023a62ae3dcc58994061a7c86 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/2b/df04adb23d2b40b6085efb230856e5e2a775b7 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/72/920278f2f999e3005801e5d5b8ab8139d3641c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/8b/903ede7c494725624bf842ec890f6342dababd create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/93/eac826f6188f34646cea81bf426aa5ba7d3bfe create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/97/79d17a04f1e2640583d35703c62460b2d86e0a create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/9c/e3f779ae33f31fce17fac3c512047b75d7498b create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/a9/f76e70a663e40091749a97eeac5f57a6fec141 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/ba/0caedd359ebe310ef431335576e20f2b84e9b9 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/bb/87653e0819460e79b5f075f2563f583cbbf18c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/d7/66f2917716d45be24bfa968b8409544941be32 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/e3/7e5d19823e42fad252f6341b1f77a7bc6ee451 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/e8/4e452c3a20c02dee17ec2f63c0cb9ef6c759eb create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/info/packs create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/0b/5987362fe3fabdd4406babdc819642ee2f5a2a create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/13/b0f23f673b161f4b5cb66f051cb93c99729e1e create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/23/33a51fdb238b7023a62ae3dcc58994061a7c86 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/8b/903ede7c494725624bf842ec890f6342dababd create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/93/eac826f6188f34646cea81bf426aa5ba7d3bfe create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/9c/e3f779ae33f31fce17fac3c512047b75d7498b create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/a9/f76e70a663e40091749a97eeac5f57a6fec141 create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/bb/87653e0819460e79b5f075f2563f583cbbf18c create mode 100644 routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa create mode 100644 routers/private/tests/repos/repo1_hook_verification/refs/heads/main create mode 100644 routers/private/tests/repos/repo1_hook_verification_dummy_gpg_key.txt create mode 100644 routers/utils/utils.go create mode 100644 routers/utils/utils_test.go create mode 100644 routers/web/admin/admin.go create mode 100644 routers/web/admin/admin_test.go create mode 100644 routers/web/admin/applications.go create mode 100644 routers/web/admin/audit.go create mode 100644 routers/web/admin/auths.go create mode 100644 routers/web/admin/badges.go create mode 100644 routers/web/admin/config.go create mode 100644 routers/web/admin/diagnosis.go create mode 100644 routers/web/admin/emails.go create mode 100644 routers/web/admin/hooks.go create mode 100644 routers/web/admin/main_test.go create mode 100644 routers/web/admin/notice.go create mode 100644 routers/web/admin/orgs.go create mode 100644 routers/web/admin/packages.go create mode 100644 routers/web/admin/perftrace.go create mode 100644 routers/web/admin/queue.go create mode 100644 routers/web/admin/queue_tester.go create mode 100644 routers/web/admin/repos.go create mode 100644 routers/web/admin/stacktrace.go create mode 100644 routers/web/admin/users.go create mode 100644 routers/web/admin/users_test.go create mode 100644 routers/web/auth/2fa.go create mode 100644 routers/web/auth/auth.go create mode 100644 routers/web/auth/auth_test.go create mode 100644 routers/web/auth/linkaccount.go create mode 100644 routers/web/auth/main_test.go create mode 100644 routers/web/auth/oauth.go create mode 100644 routers/web/auth/oauth2_provider.go create mode 100644 routers/web/auth/oauth2_wellknown.go create mode 100644 routers/web/auth/oauth_signin_sync.go create mode 100644 routers/web/auth/oauth_test.go create mode 100644 routers/web/auth/openid.go create mode 100644 routers/web/auth/password.go create mode 100644 routers/web/auth/webauthn.go create mode 100644 routers/web/base.go create mode 100644 routers/web/devtest/devtest.go create mode 100644 routers/web/devtest/mail_preview.go create mode 100644 routers/web/devtest/mock_actions.go create mode 100644 routers/web/explore/code.go create mode 100644 routers/web/explore/org.go create mode 100644 routers/web/explore/repo.go create mode 100644 routers/web/explore/topic.go create mode 100644 routers/web/explore/user.go create mode 100644 routers/web/feed/branch.go create mode 100644 routers/web/feed/convert.go create mode 100644 routers/web/feed/file.go create mode 100644 routers/web/feed/profile.go create mode 100644 routers/web/feed/profile_test.go create mode 100644 routers/web/feed/release.go create mode 100644 routers/web/feed/render.go create mode 100644 routers/web/feed/repo.go create mode 100644 routers/web/githttp.go create mode 100644 routers/web/goget.go create mode 100644 routers/web/healthcheck/check.go create mode 100644 routers/web/home.go create mode 100644 routers/web/metrics.go create mode 100644 routers/web/misc/markup.go create mode 100644 routers/web/misc/misc.go create mode 100644 routers/web/misc/swagger.go create mode 100644 routers/web/misc/webtheme.go create mode 100644 routers/web/nodeinfo.go create mode 100644 routers/web/org/block.go create mode 100644 routers/web/org/home.go create mode 100644 routers/web/org/main_test.go create mode 100644 routers/web/org/members.go create mode 100644 routers/web/org/mention.go create mode 100644 routers/web/org/org.go create mode 100644 routers/web/org/org_labels.go create mode 100644 routers/web/org/projects.go create mode 100644 routers/web/org/projects_test.go create mode 100644 routers/web/org/setting.go create mode 100644 routers/web/org/setting/audit.go create mode 100644 routers/web/org/setting_oauth2.go create mode 100644 routers/web/org/setting_packages.go create mode 100644 routers/web/org/teams.go create mode 100644 routers/web/org/worktime.go create mode 100644 routers/web/passkey.go create mode 100644 routers/web/repo/actions/actions.go create mode 100644 routers/web/repo/actions/actions_test.go create mode 100644 routers/web/repo/actions/badge.go create mode 100644 routers/web/repo/actions/main_test.go create mode 100644 routers/web/repo/actions/view.go create mode 100644 routers/web/repo/actions/view_test.go create mode 100644 routers/web/repo/activity.go create mode 100644 routers/web/repo/attachment.go create mode 100644 routers/web/repo/blame.go create mode 100644 routers/web/repo/branch.go create mode 100644 routers/web/repo/code_frequency.go create mode 100644 routers/web/repo/commit.go create mode 100644 routers/web/repo/common_recentbranches.go create mode 100644 routers/web/repo/compare.go create mode 100644 routers/web/repo/compare_test.go create mode 100644 routers/web/repo/contributors.go create mode 100644 routers/web/repo/download.go create mode 100644 routers/web/repo/editor.go create mode 100644 routers/web/repo/editor_apply_patch.go create mode 100644 routers/web/repo/editor_cherry_pick.go create mode 100644 routers/web/repo/editor_error.go create mode 100644 routers/web/repo/editor_fork.go create mode 100644 routers/web/repo/editor_preview.go create mode 100644 routers/web/repo/editor_test.go create mode 100644 routers/web/repo/editor_uploader.go create mode 100644 routers/web/repo/editor_util.go create mode 100644 routers/web/repo/fork.go create mode 100644 routers/web/repo/githttp.go create mode 100644 routers/web/repo/githttp_test.go create mode 100644 routers/web/repo/helper.go create mode 100644 routers/web/repo/issue.go create mode 100644 routers/web/repo/issue_comment.go create mode 100644 routers/web/repo/issue_content_history.go create mode 100644 routers/web/repo/issue_dependency.go create mode 100644 routers/web/repo/issue_label.go create mode 100644 routers/web/repo/issue_label_test.go create mode 100644 routers/web/repo/issue_list.go create mode 100644 routers/web/repo/issue_lock.go create mode 100644 routers/web/repo/issue_new.go create mode 100644 routers/web/repo/issue_page_meta.go create mode 100644 routers/web/repo/issue_pin.go create mode 100644 routers/web/repo/issue_poster.go create mode 100644 routers/web/repo/issue_stopwatch.go create mode 100644 routers/web/repo/issue_suggestions.go create mode 100644 routers/web/repo/issue_test.go create mode 100644 routers/web/repo/issue_timetrack.go create mode 100644 routers/web/repo/issue_view.go create mode 100644 routers/web/repo/issue_watch.go create mode 100644 routers/web/repo/main_test.go create mode 100644 routers/web/repo/mention.go create mode 100644 routers/web/repo/middlewares.go create mode 100644 routers/web/repo/middlewares_test.go create mode 100644 routers/web/repo/migrate.go create mode 100644 routers/web/repo/milestone.go create mode 100644 routers/web/repo/packages.go create mode 100644 routers/web/repo/projects.go create mode 100644 routers/web/repo/pull.go create mode 100644 routers/web/repo/pull_merge_box.go create mode 100644 routers/web/repo/pull_merge_form.go create mode 100644 routers/web/repo/pull_review.go create mode 100644 routers/web/repo/pull_review_test.go create mode 100644 routers/web/repo/recent_commits.go create mode 100644 routers/web/repo/release.go create mode 100644 routers/web/repo/release_test.go create mode 100644 routers/web/repo/render.go create mode 100644 routers/web/repo/repo.go create mode 100644 routers/web/repo/search.go create mode 100644 routers/web/repo/setting/actions.go create mode 100644 routers/web/repo/setting/audit.go create mode 100644 routers/web/repo/setting/avatar.go create mode 100644 routers/web/repo/setting/collaboration.go create mode 100644 routers/web/repo/setting/default_branch.go create mode 100644 routers/web/repo/setting/deploy_key.go create mode 100644 routers/web/repo/setting/git_hooks.go create mode 100644 routers/web/repo/setting/lfs.go create mode 100644 routers/web/repo/setting/main_test.go create mode 100644 routers/web/repo/setting/protected_branch.go create mode 100644 routers/web/repo/setting/protected_tag.go create mode 100644 routers/web/repo/setting/public_access.go create mode 100644 routers/web/repo/setting/secrets.go create mode 100644 routers/web/repo/setting/setting.go create mode 100644 routers/web/repo/setting/settings_test.go create mode 100644 routers/web/repo/setting/webhook.go create mode 100644 routers/web/repo/setting/webhook_test.go create mode 100644 routers/web/repo/star.go create mode 100644 routers/web/repo/topic.go create mode 100644 routers/web/repo/transfer.go create mode 100644 routers/web/repo/treelist.go create mode 100644 routers/web/repo/treelist_test.go create mode 100644 routers/web/repo/view.go create mode 100644 routers/web/repo/view_file.go create mode 100644 routers/web/repo/view_home.go create mode 100644 routers/web/repo/view_home_test.go create mode 100644 routers/web/repo/view_readme.go create mode 100644 routers/web/repo/view_test.go create mode 100644 routers/web/repo/watch.go create mode 100644 routers/web/repo/webgit.go create mode 100644 routers/web/repo/wiki.go create mode 100644 routers/web/repo/wiki_test.go create mode 100644 routers/web/shared/actions/general.go create mode 100644 routers/web/shared/actions/runners.go create mode 100644 routers/web/shared/actions/scoped_workflows.go create mode 100644 routers/web/shared/actions/scoped_workflows_test.go create mode 100644 routers/web/shared/actions/variables.go create mode 100644 routers/web/shared/audit/audit.go create mode 100644 routers/web/shared/issue/issue_label.go create mode 100644 routers/web/shared/label/label.go create mode 100644 routers/web/shared/mention/mention.go create mode 100644 routers/web/shared/packages/packages.go create mode 100644 routers/web/shared/project/project.go create mode 100644 routers/web/shared/project/project_test.go create mode 100644 routers/web/shared/secrets/secrets.go create mode 100644 routers/web/shared/user/block.go create mode 100644 routers/web/shared/user/header.go create mode 100644 routers/web/shared/user/helper.go create mode 100644 routers/web/shared/user/helper_test.go create mode 100644 routers/web/swagger_json.go create mode 100644 routers/web/user/avatar.go create mode 100644 routers/web/user/code.go create mode 100644 routers/web/user/heatmap.go create mode 100644 routers/web/user/home.go create mode 100644 routers/web/user/home_test.go create mode 100644 routers/web/user/main_test.go create mode 100644 routers/web/user/notification.go create mode 100644 routers/web/user/notification_test.go create mode 100644 routers/web/user/package.go create mode 100644 routers/web/user/profile.go create mode 100644 routers/web/user/search.go create mode 100644 routers/web/user/setting/account.go create mode 100644 routers/web/user/setting/account_test.go create mode 100644 routers/web/user/setting/adopt.go create mode 100644 routers/web/user/setting/applications.go create mode 100644 routers/web/user/setting/audit.go create mode 100644 routers/web/user/setting/block.go create mode 100644 routers/web/user/setting/keys.go create mode 100644 routers/web/user/setting/main_test.go create mode 100644 routers/web/user/setting/notifications.go create mode 100644 routers/web/user/setting/oauth2.go create mode 100644 routers/web/user/setting/oauth2_common.go create mode 100644 routers/web/user/setting/packages.go create mode 100644 routers/web/user/setting/profile.go create mode 100644 routers/web/user/setting/security/2fa.go create mode 100644 routers/web/user/setting/security/main_test.go create mode 100644 routers/web/user/setting/security/openid.go create mode 100644 routers/web/user/setting/security/openid_test.go create mode 100644 routers/web/user/setting/security/security.go create mode 100644 routers/web/user/setting/security/webauthn.go create mode 100644 routers/web/user/setting/settings.go create mode 100644 routers/web/user/setting/webhooks.go create mode 100644 routers/web/user/stop_watch.go create mode 100644 routers/web/web.go create mode 100644 routers/web/webfinger.go create mode 100644 routers/web/websocket/websocket.go create mode 100644 routers/web/websocket/websocket_test.go create mode 100644 services/actions/approve.go create mode 100644 services/actions/approve_test.go create mode 100644 services/actions/artifacts.go create mode 100644 services/actions/auth.go create mode 100644 services/actions/auth_test.go create mode 100644 services/actions/cancel.go create mode 100644 services/actions/cleanup.go create mode 100644 services/actions/cleanup_test.go create mode 100644 services/actions/clear_tasks.go create mode 100644 services/actions/clear_tasks_test.go create mode 100644 services/actions/commit_status.go create mode 100644 services/actions/commit_status_test.go create mode 100644 services/actions/concurrency.go create mode 100644 services/actions/context.go create mode 100644 services/actions/context_test.go create mode 100644 services/actions/helper.go create mode 100644 services/actions/helper_test.go create mode 100644 services/actions/init.go create mode 100644 services/actions/init_test.go create mode 100644 services/actions/interface.go create mode 100644 services/actions/job_emitter.go create mode 100644 services/actions/job_emitter_test.go create mode 100644 services/actions/matrix.go create mode 100644 services/actions/matrix_test.go create mode 100644 services/actions/max_parallel.go create mode 100644 services/actions/max_parallel_test.go create mode 100644 services/actions/notifier.go create mode 100644 services/actions/notifier_helper.go create mode 100644 services/actions/notifier_helper_test.go create mode 100644 services/actions/notify.go create mode 100644 services/actions/permission_parser.go create mode 100644 services/actions/permission_parser_test.go create mode 100644 services/actions/rerun.go create mode 100644 services/actions/rerun_test.go create mode 100644 services/actions/reusable_workflow.go create mode 100644 services/actions/reusable_workflow_test.go create mode 100644 services/actions/run.go create mode 100644 services/actions/schedule_tasks.go create mode 100644 services/actions/schedule_tasks_test.go create mode 100644 services/actions/scoped_workflow_cache.go create mode 100644 services/actions/task.go create mode 100644 services/actions/task_test.go create mode 100644 services/actions/token_permission_design.md create mode 100644 services/actions/variables.go create mode 100644 services/actions/workflow.go create mode 100644 services/actions/workflow_test.go create mode 100644 services/agit/agit.go create mode 100644 services/agit/agit_test.go create mode 100644 services/asymkey/commit.go create mode 100644 services/asymkey/commit_test.go create mode 100644 services/asymkey/deploy_key.go create mode 100644 services/asymkey/main_test.go create mode 100644 services/asymkey/sign.go create mode 100644 services/asymkey/sign_test.go create mode 100644 services/asymkey/ssh_key.go create mode 100644 services/asymkey/ssh_key_authorized_keys.go create mode 100644 services/asymkey/ssh_key_authorized_principals.go create mode 100644 services/asymkey/ssh_key_principals.go create mode 100644 services/asymkey/ssh_key_test.go create mode 100644 services/attachment/attachment.go create mode 100644 services/attachment/attachment_test.go create mode 100644 services/audit/actor.go create mode 100644 services/audit/audit_test.go create mode 100644 services/audit/event.go create mode 100644 services/audit/export.go create mode 100644 services/audit/export_test.go create mode 100644 services/audit/main_test.go create mode 100644 services/audit/message.go create mode 100644 services/audit/notifier.go create mode 100644 services/audit/notifier_test.go create mode 100644 services/audit/refs.go create mode 100644 services/audit/scoped.go create mode 100644 services/audit/scoped_test.go create mode 100644 services/auth/auth.go create mode 100644 services/auth/auth_token.go create mode 100644 services/auth/auth_token_test.go create mode 100644 services/auth/basic.go create mode 100644 services/auth/credential.go create mode 100644 services/auth/deploy_token.go create mode 100644 services/auth/group.go create mode 100644 services/auth/httpsign.go create mode 100644 services/auth/impersonate.go create mode 100644 services/auth/interface.go create mode 100644 services/auth/main_test.go create mode 100644 services/auth/oauth2.go create mode 100644 services/auth/oauth2_test.go create mode 100644 services/auth/reverseproxy.go create mode 100644 services/auth/reverseproxy_test.go create mode 100644 services/auth/session.go create mode 100644 services/auth/signin.go create mode 100644 services/auth/source.go create mode 100644 services/auth/source/db/assert_interface_test.go create mode 100644 services/auth/source/db/authenticate.go create mode 100644 services/auth/source/db/source.go create mode 100644 services/auth/source/ldap/README.md create mode 100644 services/auth/source/ldap/assert_interface_test.go create mode 100644 services/auth/source/ldap/security_protocol.go create mode 100644 services/auth/source/ldap/source.go create mode 100644 services/auth/source/ldap/source_authenticate.go create mode 100644 services/auth/source/ldap/source_search.go create mode 100644 services/auth/source/ldap/source_sync.go create mode 100644 services/auth/source/ldap/util.go create mode 100644 services/auth/source/oauth2/assert_interface_test.go create mode 100644 services/auth/source/oauth2/init.go create mode 100644 services/auth/source/oauth2/main_test.go create mode 100644 services/auth/source/oauth2/providers.go create mode 100644 services/auth/source/oauth2/providers_base.go create mode 100644 services/auth/source/oauth2/providers_custom.go create mode 100644 services/auth/source/oauth2/providers_openid.go create mode 100644 services/auth/source/oauth2/providers_simple.go create mode 100644 services/auth/source/oauth2/providers_test.go create mode 100644 services/auth/source/oauth2/source.go create mode 100644 services/auth/source/oauth2/source_authenticate.go create mode 100644 services/auth/source/oauth2/source_callout.go create mode 100644 services/auth/source/oauth2/source_name.go create mode 100644 services/auth/source/oauth2/source_register.go create mode 100644 services/auth/source/oauth2/source_sync.go create mode 100644 services/auth/source/oauth2/source_sync_test.go create mode 100644 services/auth/source/oauth2/store.go create mode 100644 services/auth/source/oauth2/urlmapping.go create mode 100644 services/auth/source/pam/assert_interface_test.go create mode 100644 services/auth/source/pam/source.go create mode 100644 services/auth/source/pam/source_authenticate.go create mode 100644 services/auth/source/smtp/assert_interface_test.go create mode 100644 services/auth/source/smtp/auth.go create mode 100644 services/auth/source/smtp/source.go create mode 100644 services/auth/source/smtp/source_authenticate.go create mode 100644 services/auth/source/source_group_sync.go create mode 100644 services/auth/source/source_group_sync_test.go create mode 100644 services/auth/source/sspi/assert_interface_test.go create mode 100644 services/auth/source/sspi/source.go create mode 100644 services/auth/sspi.go create mode 100644 services/auth/sspiauth_posix.go create mode 100644 services/auth/sspiauth_windows.go create mode 100644 services/auth/sync.go create mode 100644 services/automerge/automerge.go create mode 100644 services/automerge/notify.go create mode 100644 services/automergequeue/automergequeue.go create mode 100644 services/context/access_log.go create mode 100644 services/context/access_log_test.go create mode 100644 services/context/api.go create mode 100644 services/context/api_org.go create mode 100644 services/context/api_test.go create mode 100644 services/context/base.go create mode 100644 services/context/base_form.go create mode 100644 services/context/base_path.go create mode 100644 services/context/base_test.go create mode 100644 services/context/captcha.go create mode 100644 services/context/context.go create mode 100644 services/context/context_cookie.go create mode 100644 services/context/context_model.go create mode 100644 services/context/context_request.go create mode 100644 services/context/context_response.go create mode 100644 services/context/context_template.go create mode 100644 services/context/context_test.go create mode 100644 services/context/org.go create mode 100644 services/context/package.go create mode 100644 services/context/package_test.go create mode 100644 services/context/pagination.go create mode 100644 services/context/pagination_test.go create mode 100644 services/context/permission.go create mode 100644 services/context/private.go create mode 100644 services/context/repo.go create mode 100644 services/context/repo_test.go create mode 100644 services/context/response.go create mode 100644 services/context/upload/upload.go create mode 100644 services/context/upload/upload_test.go create mode 100644 services/context/user.go create mode 100644 services/context/utils.go create mode 100644 services/contexttest/context_tests.go create mode 100644 services/convert/action_test.go create mode 100644 services/convert/activity.go create mode 100644 services/convert/attachment.go create mode 100644 services/convert/convert.go create mode 100644 services/convert/git_commit.go create mode 100644 services/convert/git_commit_test.go create mode 100644 services/convert/issue.go create mode 100644 services/convert/issue_comment.go create mode 100644 services/convert/issue_test.go create mode 100644 services/convert/main_test.go create mode 100644 services/convert/mirror.go create mode 100644 services/convert/notification.go create mode 100644 services/convert/notification_test.go create mode 100644 services/convert/package.go create mode 100644 services/convert/project.go create mode 100644 services/convert/pull.go create mode 100644 services/convert/pull_review.go create mode 100644 services/convert/pull_review_test.go create mode 100644 services/convert/pull_test.go create mode 100644 services/convert/release.go create mode 100644 services/convert/release_test.go create mode 100644 services/convert/repository.go create mode 100644 services/convert/status.go create mode 100644 services/convert/user.go create mode 100644 services/convert/user_test.go create mode 100644 services/convert/utils.go create mode 100644 services/convert/utils_test.go create mode 100644 services/convert/wiki.go create mode 100644 services/cron/cron.go create mode 100644 services/cron/setting.go create mode 100644 services/cron/tasks.go create mode 100644 services/cron/tasks_actions.go create mode 100644 services/cron/tasks_basic.go create mode 100644 services/cron/tasks_extended.go create mode 100644 services/cron/tasks_extended_test.go create mode 100644 services/cron/tasks_test.go create mode 100644 services/doctor/dbconsistency.go create mode 100644 services/doctor/dbconsistency_test.go create mode 100644 services/doctor/doctor.go create mode 100644 services/doctor/lfs.go create mode 100644 services/doctor/main_test.go create mode 100644 services/doctor/misc.go create mode 100644 services/doctor/repository.go create mode 100644 services/doctor/storage.go create mode 100644 services/externalaccount/user.go create mode 100644 services/feed/feed.go create mode 100644 services/feed/feed_test.go create mode 100644 services/feed/notifier.go create mode 100644 services/feed/notifier_test.go create mode 100644 services/forms/admin.go create mode 100644 services/forms/auth_form.go create mode 100644 services/forms/org.go create mode 100644 services/forms/package_form.go create mode 100644 services/forms/repo_branch_form.go create mode 100644 services/forms/repo_form.go create mode 100644 services/forms/repo_form_editor.go create mode 100644 services/forms/repo_form_test.go create mode 100644 services/forms/repo_tag_form.go create mode 100644 services/forms/runner.go create mode 100644 services/forms/user_form.go create mode 100644 services/forms/user_form_auth_openid.go create mode 100644 services/forms/user_form_hidden_comments.go create mode 100644 services/forms/user_form_test.go create mode 100644 services/git/commit.go create mode 100644 services/git/compare.go create mode 100644 services/gitdiff/csv.go create mode 100644 services/gitdiff/csv_test.go create mode 100644 services/gitdiff/git_diff_tree.go create mode 100644 services/gitdiff/git_diff_tree_test.go create mode 100644 services/gitdiff/gitdiff.go create mode 100644 services/gitdiff/gitdiff_excerpt.go create mode 100644 services/gitdiff/gitdiff_excerpt_test.go create mode 100644 services/gitdiff/gitdiff_parse_test.go create mode 100644 services/gitdiff/gitdiff_render_test.go create mode 100644 services/gitdiff/gitdiff_test.go create mode 100644 services/gitdiff/highlightdiff.go create mode 100644 services/gitdiff/highlightdiff_test.go create mode 100644 services/gitdiff/main_test.go create mode 100644 services/gitdiff/submodule.go create mode 100644 services/gitdiff/submodule_test.go create mode 100644 services/indexer/indexer.go create mode 100644 services/indexer/notify.go create mode 100644 services/issue/assignee.go create mode 100644 services/issue/assignee_test.go create mode 100644 services/issue/comments.go create mode 100644 services/issue/commit.go create mode 100644 services/issue/commit_test.go create mode 100644 services/issue/content.go create mode 100644 services/issue/issue.go create mode 100644 services/issue/issue_test.go create mode 100644 services/issue/label.go create mode 100644 services/issue/label_test.go create mode 100644 services/issue/main_test.go create mode 100644 services/issue/milestone.go create mode 100644 services/issue/milestone_test.go create mode 100644 services/issue/pull.go create mode 100644 services/issue/reaction.go create mode 100644 services/issue/reaction_test.go create mode 100644 services/issue/review_request.go create mode 100644 services/issue/review_request_test.go create mode 100644 services/issue/status.go create mode 100644 services/issue/suggestion.go create mode 100644 services/issue/suggestion_test.go create mode 100644 services/issue/template.go create mode 100644 services/lfs/locks.go create mode 100644 services/lfs/server.go create mode 100644 services/lfs/server_test.go create mode 100644 services/mailer/icons/status-cancelled.png create mode 100644 services/mailer/icons/status-failure.png create mode 100644 services/mailer/icons/status-skipped.png create mode 100644 services/mailer/icons/status-success.png create mode 100644 services/mailer/incoming/incoming.go create mode 100644 services/mailer/incoming/incoming_handler.go create mode 100644 services/mailer/incoming/incoming_test.go create mode 100644 services/mailer/incoming/payload/payload.go create mode 100644 services/mailer/incoming/reply.go create mode 100644 services/mailer/mail.go create mode 100644 services/mailer/mail_comment.go create mode 100644 services/mailer/mail_issue.go create mode 100644 services/mailer/mail_issue_common.go create mode 100644 services/mailer/mail_release.go create mode 100644 services/mailer/mail_release_test.go create mode 100644 services/mailer/mail_repo.go create mode 100644 services/mailer/mail_team_invite.go create mode 100644 services/mailer/mail_test.go create mode 100644 services/mailer/mail_user.go create mode 100644 services/mailer/mail_workflow_run.go create mode 100644 services/mailer/mail_workflow_run_test.go create mode 100644 services/mailer/mailer.go create mode 100644 services/mailer/main_test.go create mode 100644 services/mailer/notify.go create mode 100644 services/mailer/sender/dummy.go create mode 100644 services/mailer/sender/main_test.go create mode 100644 services/mailer/sender/message.go create mode 100644 services/mailer/sender/message_test.go create mode 100644 services/mailer/sender/sender.go create mode 100644 services/mailer/sender/sendmail.go create mode 100644 services/mailer/sender/smtp.go create mode 100644 services/mailer/sender/smtp_auth.go create mode 100644 services/mailer/sender/smtp_test.go create mode 100644 services/mailer/token/token.go create mode 100644 services/markup/main_test.go create mode 100644 services/markup/renderhelper.go create mode 100644 services/markup/renderhelper_codepreview.go create mode 100644 services/markup/renderhelper_codepreview_test.go create mode 100644 services/markup/renderhelper_issueicontitle.go create mode 100644 services/markup/renderhelper_issueicontitle_test.go create mode 100644 services/markup/renderhelper_mention_test.go create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Fmilestones create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Ftest create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Ftest%2Fcommits%2Fmaster create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Ftest%2Fcommits%2Freadme-mr create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Ftest%2Fmerge_requests%2F100 create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Ftest%2Fmerge_requests%3Fcount%3D1%26offset%3D0%26query%3D%2522Target%2BProject%2522%2Bis%2B%2522test%2522 create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Ftickets create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Ftickets%2F1%2Fnotes create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Ftickets%2F2%2Fnotes create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fgitea-test%2Ftickets%2Ftypes create mode 100644 services/migrations/_mock_data/TestCodebaseDownloadRepo/GET_%2Fusers create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frate_limit create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage%3D1%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage%3D2%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Fcomments%3Fdirection%3Dasc%26per_page%3D100%26sort%3Dcreated create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage%3D1%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage%3D2%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage%3D3%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage%3D4%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage%3D1%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage%3D1%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage%3D2%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage%3D2%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553138856%2Freactions%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%3Fdirection%3Dasc%26page%3D1%26per_page%3D2%26sort%3Dcreated%26state%3Dall create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Flabels%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fmilestones%3Fpage%3D1%26per_page%3D100%26state%3Dall create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Frequested_reviewers create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315859956%2Fcomments%3Fper_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315860062%2Fcomments%3Fper_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315861440%2Fcomments%3Fper_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%3Fper_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Frequested_reviewers create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338338740%2Fcomments%3Fper_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338339651%2Fcomments%3Fper_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338349019%2Fcomments%3Fper_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%3Fper_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363017488%2Freactions%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363029944%2Freactions%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%3Fdirection%3Dasc%26page%3D1%26per_page%3D2%26sort%3Dcreated%26state%3Dall create mode 100644 services/migrations/_mock_data/TestGitHubDownloadRepo/GET_%2Fapi%2Fv3%2Frepos%2Fgo-gitea%2Ftest_repo%2Freleases%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F10%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F11%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F12%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F13%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F3%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F5%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F6%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F7%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F8%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F9%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2Fcomments%2F116550%2Freactions create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2Fcomments%2F116552%2Freactions create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit%3D2%26page%3D3%26state%3Dall%26type%3Dissues create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit%3D50%26page%3D1%26state%3Dall%26type%3Dissues create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Flabels%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fmilestones%3Flimit%3D50%26page%3D1%26state%3Dall create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%2F1770%2Fcomments create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%2F1771%2Fcomments create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%2F1772%2Fcomments create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F7%2Freviews%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit%3D3%26page%3D1%26state%3Dall create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit%3D50%26page%3D1%26state%3Dall create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Freleases%3Flimit%3D50%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Ftopics%3Flimit%3D0%26page%3D1 create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Fsettings%2Fapi create mode 100644 services/migrations/_mock_data/TestGiteaDownloadRepo/GET_%2Fapi%2Fv1%2Fversion create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F1%2Faward_emoji%3Fpage%3D1%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F1%2Faward_emoji%3Fpage%3D2%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage%3D1%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage%3D2%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage%3D3%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage%3D4%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Fdiscussions%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Fresource_state_events%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%3Fpage%3D1%26per_page%3D2%26sort%3Dasc%26state%3Dall create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Flabels%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F1%2Fapprovals create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Fapprovals create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F3 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F3%2Faward_emoji%3Fpage%3D1%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F4 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F4%2Faward_emoji%3Fpage%3D1%26per_page%3D2 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%3Fpage%3D1%26per_page%3D2%26view%3Dsimple create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmilestones%3Fpage%3D1%26per_page%3D100%26state%3Dall create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Freleases%3Fpage%3D1%26per_page%3D100 create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fprojects%2Fgitea%252Ftest_repo create mode 100644 services/migrations/_mock_data/TestGitlabDownloadRepo/GET_%2Fapi%2Fv4%2Fversion create mode 100644 services/migrations/_mock_data/TestGogsDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Flunnytest%2FTESTREPO create mode 100644 services/migrations/_mock_data/TestGogsDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Flunnytest%2FTESTREPO%2Fissues%2F1%2Fcomments create mode 100644 services/migrations/_mock_data/TestGogsDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Flunnytest%2FTESTREPO%2Fissues%3Fpage%3D1%26state%3Dopen create mode 100644 services/migrations/_mock_data/TestGogsDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Flunnytest%2FTESTREPO%2Flabels create mode 100644 services/migrations/_mock_data/TestGogsDownloadRepo/GET_%2Fapi%2Fv1%2Frepos%2Flunnytest%2FTESTREPO%2Fmilestones create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fissues%2F397%2Fiterations create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fissues%2F398%2Fchanges create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fissues%2F398%2Fcomments create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fissues%2F398%2Fiterations create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fissues%3Fcount%3D2%26offset%3D0%26query%3D%2522Project%2522%2Bis%2B%2522go-gitea-test_repo%2522%26withFields%3Dtrue create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fprojects%2F149%2Fiterations%3Fcount%3D100%26offset%3D0 create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fprojects%2F149%2Fiterations%3Fcount%3D100%26offset%3D100 create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fprojects%3Fcount%3D1%26offset%3D0%26query%3D%2522Path%2522%2Bis%2B%2522go-gitea-test_repo%2522 create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fpulls%2F186%2Fmerge-preview create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fpulls%2F186%2Freviews create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fpulls%3Fcount%3D1%26offset%3D0%26query%3D%2522Target%2BProject%2522%2Bis%2B%2522go-gitea-test_repo%2522 create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fusers%2F317 create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fusers%2F317%2Femail-addresses create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fusers%2F336 create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fusers%2F336%2Femail-addresses create mode 100644 services/migrations/_mock_data/TestOneDevDownloadRepo/GET_%2F~api%2Fversion%2Fserver create mode 100644 services/migrations/codebase.go create mode 100644 services/migrations/codebase_test.go create mode 100644 services/migrations/codecommit.go create mode 100644 services/migrations/common.go create mode 100644 services/migrations/dump.go create mode 100644 services/migrations/error.go create mode 100644 services/migrations/git.go create mode 100644 services/migrations/gitbucket.go create mode 100644 services/migrations/gitea_downloader.go create mode 100644 services/migrations/gitea_downloader_test.go create mode 100644 services/migrations/gitea_uploader.go create mode 100644 services/migrations/gitea_uploader_test.go create mode 100644 services/migrations/github.go create mode 100644 services/migrations/github_test.go create mode 100644 services/migrations/gitlab.go create mode 100644 services/migrations/gitlab_test.go create mode 100644 services/migrations/gogs.go create mode 100644 services/migrations/gogs_test.go create mode 100644 services/migrations/http_client.go create mode 100644 services/migrations/main_test.go create mode 100644 services/migrations/migrate.go create mode 100644 services/migrations/migrate_test.go create mode 100644 services/migrations/onedev.go create mode 100644 services/migrations/onedev_test.go create mode 100644 services/migrations/restore.go create mode 100644 services/migrations/restore_test.go create mode 100644 services/migrations/update.go create mode 100644 services/mirror/mirror.go create mode 100644 services/mirror/mirror_pull.go create mode 100644 services/mirror/mirror_pull_test.go create mode 100644 services/mirror/mirror_push.go create mode 100644 services/mirror/notifier.go create mode 100644 services/mirror/queue.go create mode 100644 services/notifications/status.go create mode 100644 services/notify/notifier.go create mode 100644 services/notify/notify.go create mode 100644 services/notify/null.go create mode 100644 services/oauth2_provider/access_token.go create mode 100644 services/oauth2_provider/additional_scopes_test.go create mode 100644 services/oauth2_provider/init.go create mode 100644 services/oauth2_provider/jwtsigningkey.go create mode 100644 services/oauth2_provider/jwtsigningkey_test.go create mode 100644 services/oauth2_provider/token.go create mode 100644 services/org/org.go create mode 100644 services/org/org_test.go create mode 100644 services/org/team.go create mode 100644 services/org/team_invite.go create mode 100644 services/org/team_test.go create mode 100644 services/org/user.go create mode 100644 services/org/user_test.go create mode 100644 services/packages/alpine/repository.go create mode 100644 services/packages/arch/repository.go create mode 100644 services/packages/arch/repository_test.go create mode 100644 services/packages/arch/vercmp.go create mode 100644 services/packages/arch/vercmp_test.go create mode 100644 services/packages/auth.go create mode 100644 services/packages/cargo/index.go create mode 100644 services/packages/cleanup/cleanup.go create mode 100644 services/packages/container/blob_uploader.go create mode 100644 services/packages/container/cleanup.go create mode 100644 services/packages/container/common.go create mode 100644 services/packages/debian/repository.go create mode 100644 services/packages/debian/spec.go create mode 100644 services/packages/package_update.go create mode 100644 services/packages/packages.go create mode 100644 services/packages/packages_test.go create mode 100644 services/packages/pkgspec/manager.go create mode 100644 services/packages/rpm/repository.go create mode 100644 services/packages/rpm/sign.go create mode 100644 services/packages/spec.go create mode 100644 services/packages/terraform/spec.go create mode 100644 services/packages/terraform/state.go create mode 100644 services/projects/issue.go create mode 100644 services/projects/issue_test.go create mode 100644 services/projects/main_test.go create mode 100644 services/projects/project.go create mode 100644 services/pubsub/broker.go create mode 100644 services/pubsub/broker_test.go create mode 100644 services/pubsub/init.go create mode 100644 services/pubsub/memory.go create mode 100644 services/pubsub/memory_test.go create mode 100644 services/pubsub/redis.go create mode 100644 services/pubsub/redis_test.go create mode 100644 services/pull/check.go create mode 100644 services/pull/check_test.go create mode 100644 services/pull/comment.go create mode 100644 services/pull/comment_test.go create mode 100644 services/pull/commit_status.go create mode 100644 services/pull/commit_status_test.go create mode 100644 services/pull/edits.go create mode 100644 services/pull/lfs.go create mode 100644 services/pull/main_test.go create mode 100644 services/pull/merge.go create mode 100644 services/pull/merge_ff_only.go create mode 100644 services/pull/merge_merge.go create mode 100644 services/pull/merge_prepare.go create mode 100644 services/pull/merge_rebase.go create mode 100644 services/pull/merge_squash.go create mode 100644 services/pull/merge_test.go create mode 100644 services/pull/merge_tree.go create mode 100644 services/pull/merge_tree_test.go create mode 100644 services/pull/patch.go create mode 100644 services/pull/patch_unmerged.go create mode 100644 services/pull/protected_branch.go create mode 100644 services/pull/pull.go create mode 100644 services/pull/pull_test.go create mode 100644 services/pull/ref.go create mode 100644 services/pull/ref_test.go create mode 100644 services/pull/review.go create mode 100644 services/pull/review_test.go create mode 100644 services/pull/reviewer.go create mode 100644 services/pull/reviewer_test.go create mode 100644 services/pull/temp_repo.go create mode 100644 services/pull/update.go create mode 100644 services/pull/update_rebase.go create mode 100644 services/pull/update_test.go create mode 100644 services/release/notes.go create mode 100644 services/release/notes_test.go create mode 100644 services/release/release.go create mode 100644 services/release/release_test.go create mode 100644 services/release/tag.go create mode 100644 services/repository/adopt.go create mode 100644 services/repository/adopt_test.go create mode 100644 services/repository/archiver/archiver.go create mode 100644 services/repository/archiver/archiver_test.go create mode 100644 services/repository/avatar.go create mode 100644 services/repository/avatar_test.go create mode 100644 services/repository/branch.go create mode 100644 services/repository/cache.go create mode 100644 services/repository/check.go create mode 100644 services/repository/collaboration.go create mode 100644 services/repository/collaboration_test.go create mode 100644 services/repository/commit.go create mode 100644 services/repository/commitstatus/commitstatus.go create mode 100644 services/repository/contributors_graph.go create mode 100644 services/repository/contributors_graph_test.go create mode 100644 services/repository/create.go create mode 100644 services/repository/create_test.go create mode 100644 services/repository/delete.go create mode 100644 services/repository/delete_test.go create mode 100644 services/repository/files/cherry_pick.go create mode 100644 services/repository/files/commit.go create mode 100644 services/repository/files/content.go create mode 100644 services/repository/files/content_test.go create mode 100644 services/repository/files/diff.go create mode 100644 services/repository/files/diff_test.go create mode 100644 services/repository/files/file.go create mode 100644 services/repository/files/file_test.go create mode 100644 services/repository/files/patch.go create mode 100644 services/repository/files/patch_test.go create mode 100644 services/repository/files/temp_repo.go create mode 100644 services/repository/files/temp_repo_test.go create mode 100644 services/repository/files/tree.go create mode 100644 services/repository/files/tree_test.go create mode 100644 services/repository/files/update.go create mode 100644 services/repository/files/upload.go create mode 100644 services/repository/fork.go create mode 100644 services/repository/fork_test.go create mode 100644 services/repository/generate.go create mode 100644 services/repository/generate_test.go create mode 100644 services/repository/gitgraph/graph.go create mode 100644 services/repository/gitgraph/graph_models.go create mode 100644 services/repository/gitgraph/graph_test.go create mode 100644 services/repository/gitgraph/parser.go create mode 100644 services/repository/hooks.go create mode 100644 services/repository/init.go create mode 100644 services/repository/lfs.go create mode 100644 services/repository/lfs_test.go create mode 100644 services/repository/license.go create mode 100644 services/repository/license_test.go create mode 100644 services/repository/main_test.go create mode 100644 services/repository/merge_upstream.go create mode 100644 services/repository/migrate.go create mode 100644 services/repository/push.go create mode 100644 services/repository/repo_team.go create mode 100644 services/repository/repo_team_test.go create mode 100644 services/repository/repository.go create mode 100644 services/repository/repository_test.go create mode 100644 services/repository/setting.go create mode 100644 services/repository/template.go create mode 100644 services/repository/transfer.go create mode 100644 services/repository/transfer_test.go create mode 100644 services/secrets/secrets.go create mode 100644 services/secrets/validation.go create mode 100644 services/secrets/validation_test.go create mode 100644 services/task/migrate.go create mode 100644 services/task/task.go create mode 100644 services/uinotification/notify.go create mode 100644 services/user/avatar.go create mode 100644 services/user/block.go create mode 100644 services/user/block_test.go create mode 100644 services/user/delete.go create mode 100644 services/user/email.go create mode 100644 services/user/email_test.go create mode 100644 services/user/update.go create mode 100644 services/user/update_test.go create mode 100644 services/user/user.go create mode 100644 services/user/user_test.go create mode 100644 services/versioned_migration/migration.go create mode 100644 services/webhook/deliver.go create mode 100644 services/webhook/deliver_test.go create mode 100644 services/webhook/dingtalk.go create mode 100644 services/webhook/dingtalk_test.go create mode 100644 services/webhook/discord.go create mode 100644 services/webhook/discord_test.go create mode 100644 services/webhook/feishu.go create mode 100644 services/webhook/feishu_test.go create mode 100644 services/webhook/general.go create mode 100644 services/webhook/general_test.go create mode 100644 services/webhook/main_test.go create mode 100644 services/webhook/matrix.go create mode 100644 services/webhook/matrix_test.go create mode 100644 services/webhook/msteams.go create mode 100644 services/webhook/msteams_test.go create mode 100644 services/webhook/notifier.go create mode 100644 services/webhook/packagist.go create mode 100644 services/webhook/packagist_test.go create mode 100644 services/webhook/payloader.go create mode 100644 services/webhook/slack.go create mode 100644 services/webhook/slack_test.go create mode 100644 services/webhook/telegram.go create mode 100644 services/webhook/telegram_test.go create mode 100644 services/webhook/webhook.go create mode 100644 services/webhook/webhook_test.go create mode 100644 services/webhook/wechatwork.go create mode 100644 services/websocket/events.go create mode 100644 services/websocket/logout_publisher.go create mode 100644 services/websocket/notification_notifier.go create mode 100644 services/websocket/notifier.go create mode 100644 services/websocket/stopwatch_notifier.go create mode 100644 services/webtheme/webtheme.go create mode 100644 services/webtheme/webtheme_test.go create mode 100644 services/wiki/wiki.go create mode 100644 services/wiki/wiki_path.go create mode 100644 services/wiki/wiki_test.go create mode 100755 snap/part-gitea-build.sh create mode 100755 snap/part-gitea-pull.sh create mode 100644 snap/snapcraft.yaml create mode 100644 stylelint.config.ts create mode 100644 tailwind.config.ts create mode 100644 templates/admin/actions.tmpl create mode 100644 templates/admin/applications/list.tmpl create mode 100644 templates/admin/applications/oauth2_edit.tmpl create mode 100644 templates/admin/audit/list.tmpl create mode 100644 templates/admin/auth/edit.tmpl create mode 100644 templates/admin/auth/list.tmpl create mode 100644 templates/admin/auth/new.tmpl create mode 100644 templates/admin/auth/source/ldap.tmpl create mode 100644 templates/admin/auth/source/oauth.tmpl create mode 100644 templates/admin/auth/source/smtp.tmpl create mode 100644 templates/admin/auth/source/sspi.tmpl create mode 100644 templates/admin/badge/edit.tmpl create mode 100644 templates/admin/badge/list.tmpl create mode 100644 templates/admin/badge/new.tmpl create mode 100644 templates/admin/badge/users.tmpl create mode 100644 templates/admin/badge/view.tmpl create mode 100644 templates/admin/config.tmpl create mode 100644 templates/admin/config_settings/avatars.tmpl create mode 100644 templates/admin/config_settings/config_settings.tmpl create mode 100644 templates/admin/config_settings/instance.tmpl create mode 100644 templates/admin/config_settings/repository.tmpl create mode 100644 templates/admin/cron.tmpl create mode 100644 templates/admin/dashboard.tmpl create mode 100644 templates/admin/emails/list.tmpl create mode 100644 templates/admin/hook_new.tmpl create mode 100644 templates/admin/hooks.tmpl create mode 100644 templates/admin/layout_footer.tmpl create mode 100644 templates/admin/layout_head.tmpl create mode 100644 templates/admin/navbar.tmpl create mode 100644 templates/admin/notice.tmpl create mode 100644 templates/admin/org/list.tmpl create mode 100644 templates/admin/packages/list.tmpl create mode 100644 templates/admin/perftrace.tmpl create mode 100644 templates/admin/queue.tmpl create mode 100644 templates/admin/queue_manage.tmpl create mode 100644 templates/admin/repo/list.tmpl create mode 100644 templates/admin/repo/unadopted.tmpl create mode 100644 templates/admin/runners/edit.tmpl create mode 100644 templates/admin/self_check.tmpl create mode 100644 templates/admin/shared/owner.tmpl create mode 100644 templates/admin/stacktrace-row.tmpl create mode 100644 templates/admin/stacktrace.tmpl create mode 100644 templates/admin/stats.tmpl create mode 100644 templates/admin/system_status.tmpl create mode 100644 templates/admin/trace_tabs.tmpl create mode 100644 templates/admin/user/edit.tmpl create mode 100644 templates/admin/user/list.tmpl create mode 100644 templates/admin/user/new.tmpl create mode 100644 templates/admin/user/view.tmpl create mode 100644 templates/admin/user/view_details.tmpl create mode 100644 templates/admin/user/view_emails.tmpl create mode 100644 templates/admin/user/view_orgs.tmpl create mode 100644 templates/api/packages/pypi/simple.tmpl create mode 100644 templates/base/alert.tmpl create mode 100644 templates/base/alert_details.tmpl create mode 100644 templates/base/disable_form_autofill.tmpl create mode 100644 templates/base/footer.tmpl create mode 100644 templates/base/footer_content.tmpl create mode 100644 templates/base/head.tmpl create mode 100644 templates/base/head_banner.tmpl create mode 100644 templates/base/head_impersonate_banner.tmpl create mode 100644 templates/base/head_navbar.tmpl create mode 100644 templates/base/head_navbar_icons.tmpl create mode 100644 templates/base/head_opengraph.tmpl create mode 100644 templates/base/head_script.tmpl create mode 100644 templates/base/head_style.tmpl create mode 100644 templates/base/markup_codepreview.tmpl create mode 100644 templates/base/modal_actions_confirm.tmpl create mode 100644 templates/base/paginate.tmpl create mode 100644 templates/custom/body_inner_post.tmpl create mode 100644 templates/custom/body_inner_pre.tmpl create mode 100644 templates/custom/body_outer_post.tmpl create mode 100644 templates/custom/body_outer_pre.tmpl create mode 100644 templates/custom/extra_links.tmpl create mode 100644 templates/custom/extra_links_footer.tmpl create mode 100644 templates/custom/extra_tabs.tmpl create mode 100644 templates/custom/footer.tmpl create mode 100644 templates/custom/header.tmpl create mode 100644 templates/devtest/ansi-render.tmpl create mode 100644 templates/devtest/avatar-stack.tmpl create mode 100644 templates/devtest/badge-actions-svg.tmpl create mode 100644 templates/devtest/badge-commit-sign.tmpl create mode 100644 templates/devtest/devtest-footer.tmpl create mode 100644 templates/devtest/devtest-header.tmpl create mode 100644 templates/devtest/devtest-list.tmpl create mode 100644 templates/devtest/fetch-action.tmpl create mode 100644 templates/devtest/flex-list.tmpl create mode 100644 templates/devtest/fomantic-dropdown.tmpl create mode 100644 templates/devtest/fomantic-modal.tmpl create mode 100644 templates/devtest/form-fields.tmpl create mode 100644 templates/devtest/gitea-ui.tmpl create mode 100644 templates/devtest/global-button.tmpl create mode 100644 templates/devtest/keyboard-shortcut.tmpl create mode 100644 templates/devtest/label.tmpl create mode 100644 templates/devtest/mail-preview.tmpl create mode 100644 templates/devtest/markup-render.tmpl create mode 100644 templates/devtest/relative-time.tmpl create mode 100644 templates/devtest/repo-action-view.tmpl create mode 100644 templates/devtest/severity-colors.tmpl create mode 100644 templates/devtest/tmplerr-sub.tmpl create mode 100644 templates/devtest/tmplerr.tmpl create mode 100644 templates/devtest/toast-and-message.tmpl create mode 100644 templates/devtest/unicode-escape.tmpl create mode 100644 templates/explore/code.tmpl create mode 100644 templates/explore/navbar.tmpl create mode 100644 templates/explore/repos.tmpl create mode 100644 templates/explore/search.tmpl create mode 100644 templates/explore/user_list.tmpl create mode 100644 templates/explore/users.tmpl create mode 100644 templates/home.tmpl create mode 100644 templates/install.tmpl create mode 100644 templates/mail/base/footer.tmpl create mode 100644 templates/mail/base/head.tmpl create mode 100644 templates/mail/org/team_invite.devtest.yml create mode 100644 templates/mail/org/team_invite.tmpl create mode 100644 templates/mail/repo/actions/workflow_run.devtest.yml create mode 100644 templates/mail/repo/actions/workflow_run.tmpl create mode 100644 templates/mail/repo/collaborator.devtest.yml create mode 100644 templates/mail/repo/collaborator.tmpl create mode 100644 templates/mail/repo/issue/assigned.devtest.yml create mode 100644 templates/mail/repo/issue/assigned.tmpl create mode 100644 templates/mail/repo/issue/default.devtest.yml create mode 100644 templates/mail/repo/issue/default.tmpl create mode 100644 templates/mail/repo/release.devtest.yml create mode 100644 templates/mail/repo/release.tmpl create mode 100644 templates/mail/repo/transfer.devtest.yml create mode 100644 templates/mail/repo/transfer.tmpl create mode 100644 templates/mail/user/auth/activate.devtest.yml create mode 100644 templates/mail/user/auth/activate.tmpl create mode 100644 templates/mail/user/auth/activate_email.devtest.yml create mode 100644 templates/mail/user/auth/activate_email.tmpl create mode 100644 templates/mail/user/auth/register_notify.devtest.yml create mode 100644 templates/mail/user/auth/register_notify.tmpl create mode 100644 templates/mail/user/auth/reset_passwd.devtest.yml create mode 100644 templates/mail/user/auth/reset_passwd.tmpl create mode 100644 templates/org/create.tmpl create mode 100644 templates/org/follow_unfollow.tmpl create mode 100644 templates/org/header.tmpl create mode 100644 templates/org/home.tmpl create mode 100644 templates/org/member/members.tmpl create mode 100644 templates/org/menu.tmpl create mode 100644 templates/org/projects/list.tmpl create mode 100644 templates/org/projects/new.tmpl create mode 100644 templates/org/projects/view.tmpl create mode 100644 templates/org/settings/actions.tmpl create mode 100644 templates/org/settings/actions_general.tmpl create mode 100644 templates/org/settings/applications.tmpl create mode 100644 templates/org/settings/applications_oauth2_edit.tmpl create mode 100644 templates/org/settings/audit_logs.tmpl create mode 100644 templates/org/settings/blocked_users.tmpl create mode 100644 templates/org/settings/hook_new.tmpl create mode 100644 templates/org/settings/hooks.tmpl create mode 100644 templates/org/settings/labels.tmpl create mode 100644 templates/org/settings/layout_footer.tmpl create mode 100644 templates/org/settings/layout_head.tmpl create mode 100644 templates/org/settings/navbar.tmpl create mode 100644 templates/org/settings/options.tmpl create mode 100644 templates/org/settings/options_dangerzone.tmpl create mode 100644 templates/org/settings/packages.tmpl create mode 100644 templates/org/settings/packages_cleanup_rules_edit.tmpl create mode 100644 templates/org/settings/packages_cleanup_rules_preview.tmpl create mode 100644 templates/org/settings/runners_edit.tmpl create mode 100644 templates/org/team/invite.tmpl create mode 100644 templates/org/team/members.tmpl create mode 100644 templates/org/team/navbar.tmpl create mode 100644 templates/org/team/new.tmpl create mode 100644 templates/org/team/repositories.tmpl create mode 100644 templates/org/team/sidebar.tmpl create mode 100644 templates/org/team/teams.tmpl create mode 100644 templates/org/worktime.tmpl create mode 100644 templates/org/worktime/empty_placeholder.tmpl create mode 100644 templates/org/worktime/table_members.tmpl create mode 100644 templates/org/worktime/table_milestones.tmpl create mode 100644 templates/org/worktime/table_repos.tmpl create mode 100644 templates/package/content/alpine.tmpl create mode 100644 templates/package/content/arch.tmpl create mode 100644 templates/package/content/cargo.tmpl create mode 100644 templates/package/content/chef.tmpl create mode 100644 templates/package/content/composer.tmpl create mode 100644 templates/package/content/composer_dependencies.tmpl create mode 100644 templates/package/content/conan.tmpl create mode 100644 templates/package/content/conda.tmpl create mode 100644 templates/package/content/container.tmpl create mode 100644 templates/package/content/cran.tmpl create mode 100644 templates/package/content/debian.tmpl create mode 100644 templates/package/content/generic.tmpl create mode 100644 templates/package/content/go.tmpl create mode 100644 templates/package/content/helm.tmpl create mode 100644 templates/package/content/maven.tmpl create mode 100644 templates/package/content/npm.tmpl create mode 100644 templates/package/content/npm_dependencies.tmpl create mode 100644 templates/package/content/nuget.tmpl create mode 100644 templates/package/content/pub.tmpl create mode 100644 templates/package/content/pypi.tmpl create mode 100644 templates/package/content/rpm.tmpl create mode 100644 templates/package/content/rubygems.tmpl create mode 100644 templates/package/content/rubygems_dependencies.tmpl create mode 100644 templates/package/content/swift.tmpl create mode 100644 templates/package/content/terraform.tmpl create mode 100644 templates/package/content/vagrant.tmpl create mode 100644 templates/package/metadata/alpine.tmpl create mode 100644 templates/package/metadata/arch.tmpl create mode 100644 templates/package/metadata/cargo.tmpl create mode 100644 templates/package/metadata/chef.tmpl create mode 100644 templates/package/metadata/composer.tmpl create mode 100644 templates/package/metadata/conan.tmpl create mode 100644 templates/package/metadata/conda.tmpl create mode 100644 templates/package/metadata/container.tmpl create mode 100644 templates/package/metadata/cran.tmpl create mode 100644 templates/package/metadata/debian.tmpl create mode 100644 templates/package/metadata/generic.tmpl create mode 100644 templates/package/metadata/helm.tmpl create mode 100644 templates/package/metadata/maven.tmpl create mode 100644 templates/package/metadata/npm.tmpl create mode 100644 templates/package/metadata/nuget.tmpl create mode 100644 templates/package/metadata/pub.tmpl create mode 100644 templates/package/metadata/pypi.tmpl create mode 100644 templates/package/metadata/rpm.tmpl create mode 100644 templates/package/metadata/rubygems.tmpl create mode 100644 templates/package/metadata/swift.tmpl create mode 100644 templates/package/metadata/terraform.tmpl create mode 100644 templates/package/metadata/vagrant.tmpl create mode 100644 templates/package/settings.tmpl create mode 100644 templates/package/shared/cargo.tmpl create mode 100644 templates/package/shared/cleanup_rules/edit.tmpl create mode 100644 templates/package/shared/cleanup_rules/list.tmpl create mode 100644 templates/package/shared/cleanup_rules/preview.tmpl create mode 100644 templates/package/shared/list.tmpl create mode 100644 templates/package/shared/versionlist.tmpl create mode 100644 templates/package/shared/view.tmpl create mode 100644 templates/package/shared/visibility_badge.tmpl create mode 100644 templates/package/view.tmpl create mode 100644 templates/post-install.tmpl create mode 100644 templates/projects/list.tmpl create mode 100644 templates/projects/new.tmpl create mode 100644 templates/projects/view.tmpl create mode 100644 templates/repo/actions/list.tmpl create mode 100644 templates/repo/actions/no_workflows.tmpl create mode 100644 templates/repo/actions/runs_list.tmpl create mode 100644 templates/repo/actions/view.tmpl create mode 100644 templates/repo/actions/view_component.tmpl create mode 100644 templates/repo/actions/workflow_dispatch.tmpl create mode 100644 templates/repo/actions/workflow_dispatch_inputs.tmpl create mode 100644 templates/repo/activity.tmpl create mode 100644 templates/repo/blame.tmpl create mode 100644 templates/repo/branch/list.tmpl create mode 100644 templates/repo/branch_dropdown.tmpl create mode 100644 templates/repo/cite/cite_buttons.tmpl create mode 100644 templates/repo/cite/cite_modal.tmpl create mode 100644 templates/repo/clone_buttons.tmpl create mode 100644 templates/repo/clone_panel.tmpl create mode 100644 templates/repo/code/recently_pushed_new_branches.tmpl create mode 100644 templates/repo/code/upstream_diverging_info.tmpl create mode 100644 templates/repo/code_frequency.tmpl create mode 100644 templates/repo/commit_load_branches_and_tags.tmpl create mode 100644 templates/repo/commit_page.tmpl create mode 100644 templates/repo/commit_sign_badge.tmpl create mode 100644 templates/repo/commit_statuses.tmpl create mode 100644 templates/repo/commits.tmpl create mode 100644 templates/repo/commits_list.tmpl create mode 100644 templates/repo/commits_list_small.tmpl create mode 100644 templates/repo/commits_ref_name.tmpl create mode 100644 templates/repo/commits_search_dropdown.tmpl create mode 100644 templates/repo/commits_table.tmpl create mode 100644 templates/repo/contributors.tmpl create mode 100644 templates/repo/create.tmpl create mode 100644 templates/repo/create_helper.tmpl create mode 100644 templates/repo/diff/blob_excerpt.tmpl create mode 100644 templates/repo/diff/box.tmpl create mode 100644 templates/repo/diff/comment_form.tmpl create mode 100644 templates/repo/diff/comment_form_datahandler.tmpl create mode 100644 templates/repo/diff/comments.tmpl create mode 100644 templates/repo/diff/compare.tmpl create mode 100644 templates/repo/diff/conversation.tmpl create mode 100644 templates/repo/diff/conversation_outdated.tmpl create mode 100644 templates/repo/diff/csv_diff.tmpl create mode 100644 templates/repo/diff/image_diff.tmpl create mode 100644 templates/repo/diff/new_comment.tmpl create mode 100644 templates/repo/diff/new_review.tmpl create mode 100644 templates/repo/diff/options_dropdown.tmpl create mode 100644 templates/repo/diff/section_code.tmpl create mode 100644 templates/repo/diff/section_split.tmpl create mode 100644 templates/repo/diff/section_unified.tmpl create mode 100644 templates/repo/diff/stats.tmpl create mode 100644 templates/repo/diff/whitespace_dropdown.tmpl create mode 100644 templates/repo/editor/cherry_pick.tmpl create mode 100644 templates/repo/editor/commit_form.tmpl create mode 100644 templates/repo/editor/common_breadcrumb.tmpl create mode 100644 templates/repo/editor/common_top.tmpl create mode 100644 templates/repo/editor/delete.tmpl create mode 100644 templates/repo/editor/diff_preview.tmpl create mode 100644 templates/repo/editor/edit.tmpl create mode 100644 templates/repo/editor/fork.tmpl create mode 100644 templates/repo/editor/options.tmpl create mode 100644 templates/repo/editor/patch.tmpl create mode 100644 templates/repo/editor/upload.tmpl create mode 100644 templates/repo/empty.tmpl create mode 100644 templates/repo/file_info.tmpl create mode 100644 templates/repo/forks.tmpl create mode 100644 templates/repo/graph.tmpl create mode 100644 templates/repo/graph/commits.tmpl create mode 100644 templates/repo/graph/div.tmpl create mode 100644 templates/repo/graph/svgcontainer.tmpl create mode 100644 templates/repo/header.tmpl create mode 100644 templates/repo/header/fork.tmpl create mode 100644 templates/repo/header/star.tmpl create mode 100644 templates/repo/header/watch.tmpl create mode 100644 templates/repo/home.tmpl create mode 100644 templates/repo/home_sidebar_bottom.tmpl create mode 100644 templates/repo/home_sidebar_top.tmpl create mode 100644 templates/repo/icon.tmpl create mode 100644 templates/repo/icons/action_status.tmpl create mode 100644 templates/repo/icons/commit_status.tmpl create mode 100644 templates/repo/issue/branch_selector_field.tmpl create mode 100644 templates/repo/issue/card.tmpl create mode 100644 templates/repo/issue/choose.tmpl create mode 100644 templates/repo/issue/comment_tab.tmpl create mode 100644 templates/repo/issue/fields/checkboxes.tmpl create mode 100644 templates/repo/issue/fields/dropdown.tmpl create mode 100644 templates/repo/issue/fields/header.tmpl create mode 100644 templates/repo/issue/fields/input.tmpl create mode 100644 templates/repo/issue/fields/markdown.tmpl create mode 100644 templates/repo/issue/fields/textarea.tmpl create mode 100644 templates/repo/issue/filter_actions.tmpl create mode 100644 templates/repo/issue/filter_item_label.tmpl create mode 100644 templates/repo/issue/filter_item_milestone.tmpl create mode 100644 templates/repo/issue/filter_item_user_assign.tmpl create mode 100644 templates/repo/issue/filter_item_user_fetch.tmpl create mode 100644 templates/repo/issue/filter_list.tmpl create mode 100644 templates/repo/issue/filters.tmpl create mode 100644 templates/repo/issue/label_precolors.tmpl create mode 100644 templates/repo/issue/labels.tmpl create mode 100644 templates/repo/issue/labels/label_archived.tmpl create mode 100644 templates/repo/issue/labels/label_edit_modal.tmpl create mode 100644 templates/repo/issue/labels/label_list.tmpl create mode 100644 templates/repo/issue/labels/label_load_template.tmpl create mode 100644 templates/repo/issue/list.tmpl create mode 100644 templates/repo/issue/milestone/filter_list.tmpl create mode 100644 templates/repo/issue/milestone_issues.tmpl create mode 100644 templates/repo/issue/milestone_new.tmpl create mode 100644 templates/repo/issue/milestones.tmpl create mode 100644 templates/repo/issue/navbar.tmpl create mode 100644 templates/repo/issue/new.tmpl create mode 100644 templates/repo/issue/new_form.tmpl create mode 100644 templates/repo/issue/openclose.tmpl create mode 100644 templates/repo/issue/search.tmpl create mode 100644 templates/repo/issue/sidebar/allow_maintainer_edit.tmpl create mode 100644 templates/repo/issue/sidebar/assignee_list.tmpl create mode 100644 templates/repo/issue/sidebar/due_date.tmpl create mode 100644 templates/repo/issue/sidebar/issue_dependencies.tmpl create mode 100644 templates/repo/issue/sidebar/issue_management.tmpl create mode 100644 templates/repo/issue/sidebar/label_list.tmpl create mode 100644 templates/repo/issue/sidebar/label_list_item.tmpl create mode 100644 templates/repo/issue/sidebar/milestone_list.tmpl create mode 100644 templates/repo/issue/sidebar/participant_list.tmpl create mode 100644 templates/repo/issue/sidebar/project_list.tmpl create mode 100644 templates/repo/issue/sidebar/reference_link.tmpl create mode 100644 templates/repo/issue/sidebar/reviewer_list.tmpl create mode 100644 templates/repo/issue/sidebar/stopwatch_timetracker.tmpl create mode 100644 templates/repo/issue/sidebar/watch_notification.tmpl create mode 100644 templates/repo/issue/sidebar/wip_switch.tmpl create mode 100644 templates/repo/issue/view.tmpl create mode 100644 templates/repo/issue/view_content.tmpl create mode 100644 templates/repo/issue/view_content/add_reaction.tmpl create mode 100644 templates/repo/issue/view_content/attachments.tmpl create mode 100644 templates/repo/issue/view_content/comments.tmpl create mode 100644 templates/repo/issue/view_content/comments_authorlink.tmpl create mode 100644 templates/repo/issue/view_content/comments_delete_time.tmpl create mode 100644 templates/repo/issue/view_content/context_menu.tmpl create mode 100644 templates/repo/issue/view_content/conversation.tmpl create mode 100644 templates/repo/issue/view_content/pull_merge_box.tmpl create mode 100644 templates/repo/issue/view_content/pull_merge_instruction.tmpl create mode 100644 templates/repo/issue/view_content/pull_merge_status_checks.tmpl create mode 100644 templates/repo/issue/view_content/reactions.tmpl create mode 100644 templates/repo/issue/view_content/reference_issue_dialog.tmpl create mode 100644 templates/repo/issue/view_content/show_role.tmpl create mode 100644 templates/repo/issue/view_content/sidebar.tmpl create mode 100644 templates/repo/issue/view_content/update_branch_by_merge.tmpl create mode 100644 templates/repo/issue/view_content/watching.tmpl create mode 100644 templates/repo/issue/view_title.tmpl create mode 100644 templates/repo/latest_commit.tmpl create mode 100644 templates/repo/migrate/codebase.tmpl create mode 100644 templates/repo/migrate/codecommit.tmpl create mode 100644 templates/repo/migrate/git.tmpl create mode 100644 templates/repo/migrate/gitbucket.tmpl create mode 100644 templates/repo/migrate/gitea.tmpl create mode 100644 templates/repo/migrate/github.tmpl create mode 100644 templates/repo/migrate/gitlab.tmpl create mode 100644 templates/repo/migrate/gogs.tmpl create mode 100644 templates/repo/migrate/helper.tmpl create mode 100644 templates/repo/migrate/migrate.tmpl create mode 100644 templates/repo/migrate/migrating.tmpl create mode 100644 templates/repo/migrate/onedev.tmpl create mode 100644 templates/repo/migrate/options.tmpl create mode 100644 templates/repo/navbar.tmpl create mode 100644 templates/repo/packages.tmpl create mode 100644 templates/repo/projects/list.tmpl create mode 100644 templates/repo/projects/new.tmpl create mode 100644 templates/repo/projects/view.tmpl create mode 100644 templates/repo/pulls/commits.tmpl create mode 100644 templates/repo/pulls/files.tmpl create mode 100644 templates/repo/pulls/fork.tmpl create mode 100644 templates/repo/pulls/status_items.tmpl create mode 100644 templates/repo/pulls/tab_menu.tmpl create mode 100644 templates/repo/pulse.tmpl create mode 100644 templates/repo/recent_commits.tmpl create mode 100644 templates/repo/release/label.tmpl create mode 100644 templates/repo/release/list.tmpl create mode 100644 templates/repo/release/new.tmpl create mode 100644 templates/repo/release_tag_header.tmpl create mode 100644 templates/repo/search.tmpl create mode 100644 templates/repo/search_name.tmpl create mode 100644 templates/repo/settings/actions.tmpl create mode 100644 templates/repo/settings/actions_general.tmpl create mode 100644 templates/repo/settings/audit_logs.tmpl create mode 100644 templates/repo/settings/branches.tmpl create mode 100644 templates/repo/settings/collaboration.tmpl create mode 100644 templates/repo/settings/dangerous_operation_form.tmpl create mode 100644 templates/repo/settings/deploy_key_list.tmpl create mode 100644 templates/repo/settings/deploy_keys.tmpl create mode 100644 templates/repo/settings/githook_edit.tmpl create mode 100644 templates/repo/settings/githooks.tmpl create mode 100644 templates/repo/settings/layout_footer.tmpl create mode 100644 templates/repo/settings/layout_head.tmpl create mode 100644 templates/repo/settings/lfs.tmpl create mode 100644 templates/repo/settings/lfs_file.tmpl create mode 100644 templates/repo/settings/lfs_file_find.tmpl create mode 100644 templates/repo/settings/lfs_locks.tmpl create mode 100644 templates/repo/settings/lfs_pointers.tmpl create mode 100644 templates/repo/settings/navbar.tmpl create mode 100644 templates/repo/settings/options.tmpl create mode 100644 templates/repo/settings/protected_branch.tmpl create mode 100644 templates/repo/settings/public_access.tmpl create mode 100644 templates/repo/settings/push_mirror_sync_modal.tmpl create mode 100644 templates/repo/settings/runner_edit.tmpl create mode 100644 templates/repo/settings/secrets.tmpl create mode 100644 templates/repo/settings/tags.tmpl create mode 100644 templates/repo/settings/webhook/base.tmpl create mode 100644 templates/repo/settings/webhook/base_list.tmpl create mode 100644 templates/repo/settings/webhook/dingtalk.tmpl create mode 100644 templates/repo/settings/webhook/discord.tmpl create mode 100644 templates/repo/settings/webhook/feishu.tmpl create mode 100644 templates/repo/settings/webhook/gitea.tmpl create mode 100644 templates/repo/settings/webhook/gogs.tmpl create mode 100644 templates/repo/settings/webhook/history.tmpl create mode 100644 templates/repo/settings/webhook/link_menu.tmpl create mode 100644 templates/repo/settings/webhook/matrix.tmpl create mode 100644 templates/repo/settings/webhook/msteams.tmpl create mode 100644 templates/repo/settings/webhook/new.tmpl create mode 100644 templates/repo/settings/webhook/packagist.tmpl create mode 100644 templates/repo/settings/webhook/settings.tmpl create mode 100644 templates/repo/settings/webhook/slack.tmpl create mode 100644 templates/repo/settings/webhook/telegram.tmpl create mode 100644 templates/repo/settings/webhook/wechatwork.tmpl create mode 100644 templates/repo/sub_menu.tmpl create mode 100644 templates/repo/tag/list.tmpl create mode 100644 templates/repo/tag/name.tmpl create mode 100644 templates/repo/unicode_escape_prompt.tmpl create mode 100644 templates/repo/upload.tmpl create mode 100644 templates/repo/user_cards.tmpl create mode 100644 templates/repo/view.tmpl create mode 100644 templates/repo/view_content.tmpl create mode 100644 templates/repo/view_file.tmpl create mode 100644 templates/repo/view_file_tree.tmpl create mode 100644 templates/repo/view_file_tree_toggle_button.tmpl create mode 100644 templates/repo/view_list.tmpl create mode 100644 templates/repo/watch_options_modal.tmpl create mode 100644 templates/repo/watchers.tmpl create mode 100644 templates/repo/wiki/new.tmpl create mode 100644 templates/repo/wiki/pages.tmpl create mode 100644 templates/repo/wiki/revision.tmpl create mode 100644 templates/repo/wiki/start.tmpl create mode 100644 templates/repo/wiki/view.tmpl create mode 100644 templates/shared/actions/owner_general_settings.tmpl create mode 100644 templates/shared/actions/permission_mode_select.tmpl create mode 100644 templates/shared/actions/permissions_table.tmpl create mode 100644 templates/shared/actions/permissions_table_unit.tmpl create mode 100644 templates/shared/actions/runner_badge_flat-square.tmpl create mode 100644 templates/shared/actions/runner_badge_flat.tmpl create mode 100644 templates/shared/actions/runner_edit.tmpl create mode 100644 templates/shared/actions/runner_list.tmpl create mode 100644 templates/shared/actions/scoped_workflows.tmpl create mode 100644 templates/shared/audit/list.tmpl create mode 100644 templates/shared/avatar_upload_crop.tmpl create mode 100644 templates/shared/combomarkdowneditor.tmpl create mode 100644 templates/shared/fileisempty.tmpl create mode 100644 templates/shared/filetoolarge.tmpl create mode 100644 templates/shared/issueicon.tmpl create mode 100644 templates/shared/issuelist.tmpl create mode 100644 templates/shared/misc/tabtitle.tmpl create mode 100644 templates/shared/repo/list.tmpl create mode 100644 templates/shared/repo/search.tmpl create mode 100644 templates/shared/search/button.tmpl create mode 100644 templates/shared/search/code/results.tmpl create mode 100644 templates/shared/search/code/search.tmpl create mode 100644 templates/shared/search/combo.tmpl create mode 100644 templates/shared/search/input.tmpl create mode 100644 templates/shared/searchbottom.tmpl create mode 100644 templates/shared/searchfile.tmpl create mode 100644 templates/shared/secrets/add_list.tmpl create mode 100644 templates/shared/user/authorlink.tmpl create mode 100644 templates/shared/user/avatarlink.tmpl create mode 100644 templates/shared/user/block_user_dialog.tmpl create mode 100644 templates/shared/user/blocked_users.tmpl create mode 100644 templates/shared/user/name.tmpl create mode 100644 templates/shared/user/namelink.tmpl create mode 100644 templates/shared/user/org_profile_avatar.tmpl create mode 100644 templates/shared/user/profile_big_avatar.tmpl create mode 100644 templates/shared/variables/variable_list.tmpl create mode 100644 templates/shared/webhook/icon.tmpl create mode 100644 templates/status/404.tmpl create mode 100644 templates/status/500.tmpl create mode 100644 templates/status/503.tmpl create mode 100644 templates/swagger/openapi-viewer.tmpl create mode 100644 templates/swagger/v1-input.json create mode 100644 templates/user/auth/activate.tmpl create mode 100644 templates/user/auth/activate_prompt.tmpl create mode 100644 templates/user/auth/captcha.tmpl create mode 100644 templates/user/auth/change_passwd.tmpl create mode 100644 templates/user/auth/change_passwd_inner.tmpl create mode 100644 templates/user/auth/external_auth_methods.tmpl create mode 100644 templates/user/auth/forgot_passwd.tmpl create mode 100644 templates/user/auth/grant.tmpl create mode 100644 templates/user/auth/grant_error.tmpl create mode 100644 templates/user/auth/link_account.tmpl create mode 100644 templates/user/auth/prohibit_login.tmpl create mode 100644 templates/user/auth/reset_passwd.tmpl create mode 100644 templates/user/auth/signin.tmpl create mode 100644 templates/user/auth/signin_inner.tmpl create mode 100644 templates/user/auth/signin_openid.tmpl create mode 100644 templates/user/auth/signup.tmpl create mode 100644 templates/user/auth/signup_inner.tmpl create mode 100644 templates/user/auth/signup_openid_connect.tmpl create mode 100644 templates/user/auth/signup_openid_navbar.tmpl create mode 100644 templates/user/auth/signup_openid_register.tmpl create mode 100644 templates/user/auth/twofa.tmpl create mode 100644 templates/user/auth/twofa_scratch.tmpl create mode 100644 templates/user/auth/webauthn.tmpl create mode 100644 templates/user/auth/webauthn_error.tmpl create mode 100644 templates/user/code.tmpl create mode 100644 templates/user/dashboard/dashboard.tmpl create mode 100644 templates/user/dashboard/feeds.tmpl create mode 100644 templates/user/dashboard/guide.tmpl create mode 100644 templates/user/dashboard/issues.tmpl create mode 100644 templates/user/dashboard/milestones.tmpl create mode 100644 templates/user/dashboard/navbar.tmpl create mode 100644 templates/user/dashboard/repolist.tmpl create mode 100644 templates/user/heatmap.tmpl create mode 100644 templates/user/notification/notification.tmpl create mode 100644 templates/user/notification/notification_div.tmpl create mode 100644 templates/user/notification/notification_subscriptions.tmpl create mode 100644 templates/user/overview/header.tmpl create mode 100644 templates/user/overview/package_versions.tmpl create mode 100644 templates/user/overview/packages.tmpl create mode 100644 templates/user/profile.tmpl create mode 100644 templates/user/settings/account.tmpl create mode 100644 templates/user/settings/actions.tmpl create mode 100644 templates/user/settings/actions_general.tmpl create mode 100644 templates/user/settings/appearance.tmpl create mode 100644 templates/user/settings/applications.tmpl create mode 100644 templates/user/settings/applications_oauth2.tmpl create mode 100644 templates/user/settings/applications_oauth2_edit.tmpl create mode 100644 templates/user/settings/applications_oauth2_edit_form.tmpl create mode 100644 templates/user/settings/applications_oauth2_list.tmpl create mode 100644 templates/user/settings/audit_logs.tmpl create mode 100644 templates/user/settings/blocked_users.tmpl create mode 100644 templates/user/settings/grants_oauth2.tmpl create mode 100644 templates/user/settings/hook_new.tmpl create mode 100644 templates/user/settings/hooks.tmpl create mode 100644 templates/user/settings/keys.tmpl create mode 100644 templates/user/settings/keys_gpg.tmpl create mode 100644 templates/user/settings/keys_principal.tmpl create mode 100644 templates/user/settings/keys_ssh.tmpl create mode 100644 templates/user/settings/layout_footer.tmpl create mode 100644 templates/user/settings/layout_head.tmpl create mode 100644 templates/user/settings/navbar.tmpl create mode 100644 templates/user/settings/notifications.tmpl create mode 100644 templates/user/settings/organization.tmpl create mode 100644 templates/user/settings/packages.tmpl create mode 100644 templates/user/settings/packages_cleanup_rules_edit.tmpl create mode 100644 templates/user/settings/packages_cleanup_rules_preview.tmpl create mode 100644 templates/user/settings/profile.tmpl create mode 100644 templates/user/settings/repos.tmpl create mode 100644 templates/user/settings/runner_edit.tmpl create mode 100644 templates/user/settings/security/accountlinks.tmpl create mode 100644 templates/user/settings/security/openid.tmpl create mode 100644 templates/user/settings/security/security.tmpl create mode 100644 templates/user/settings/security/twofa.tmpl create mode 100644 templates/user/settings/security/twofa_enroll.tmpl create mode 100644 templates/user/settings/security/webauthn.tmpl create mode 100644 templates/webhook/new.tmpl create mode 100644 tests/e2e/codeeditor.test.ts create mode 100644 tests/e2e/env.d.ts create mode 100644 tests/e2e/events.test.ts create mode 100644 tests/e2e/explore.test.ts create mode 100644 tests/e2e/external-render.test.ts create mode 100644 tests/e2e/file-view-render.test.ts create mode 100644 tests/e2e/fork.test.ts create mode 100644 tests/e2e/heatmap.test.ts create mode 100644 tests/e2e/issue-comment.test.ts create mode 100644 tests/e2e/issue-project.test.ts create mode 100644 tests/e2e/licenses.test.ts create mode 100644 tests/e2e/login.test.ts create mode 100644 tests/e2e/mermaid.test.ts create mode 100644 tests/e2e/milestone.test.ts create mode 100644 tests/e2e/org.test.ts create mode 100644 tests/e2e/pr-create.test.ts create mode 100644 tests/e2e/pr-diff-filter.test.ts create mode 100644 tests/e2e/pr-review.test.ts create mode 100644 tests/e2e/pull-merge-box.test.ts create mode 100644 tests/e2e/reactions.test.ts create mode 100644 tests/e2e/readme.test.ts create mode 100644 tests/e2e/register.test.ts create mode 100644 tests/e2e/relative-time.test.ts create mode 100644 tests/e2e/release.test.ts create mode 100644 tests/e2e/repo-collab.test.ts create mode 100644 tests/e2e/repo-star-watch.test.ts create mode 100644 tests/e2e/repo.test.ts create mode 100644 tests/e2e/user-settings.test.ts create mode 100644 tests/e2e/utils.ts create mode 100644 tests/e2e/webauthn.test.ts create mode 100644 tests/fuzz/fuzz_test.go create mode 100644 tests/gitea-lfs-meta/0b/8d/8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351 create mode 100644 tests/gitea-lfs-meta/2e/cc/db43825d2a49d99d542daa20075cff1d97d9d2349a8977efe9c03661737c create mode 100644 tests/gitea-lfs-meta/7b/6b/2c88dba9f760a1a58469b67fee2b698ef7e9399c4ca4f34a14ccbe39f623 create mode 100644 tests/gitea-lfs-meta/9d/17/2e5c64b4f0024b9901ec6afe9ea052f3c9b6ff9f4b07956d8c48c86fca82 create mode 100644 tests/gitea-repositories-meta/limited_org/private_repo_on_limited_org.git/HEAD create mode 100644 tests/gitea-repositories-meta/limited_org/private_repo_on_limited_org.git/config create mode 100644 tests/gitea-repositories-meta/limited_org/private_repo_on_limited_org.git/objects/74/8bf557dfc9c6457998b5118a6c8b2129f56c30 create mode 100644 tests/gitea-repositories-meta/limited_org/private_repo_on_limited_org.git/objects/a5/46f86c7dd182592b96639045e176dde8df76ef create mode 100644 tests/gitea-repositories-meta/limited_org/private_repo_on_limited_org.git/objects/b8/95782bd271fdd266dd06e5880ea4abdc3a0dc7 create mode 100644 tests/gitea-repositories-meta/limited_org/private_repo_on_limited_org.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/limited_org/public_repo_on_limited_org.git/HEAD create mode 100644 tests/gitea-repositories-meta/limited_org/public_repo_on_limited_org.git/config create mode 100644 tests/gitea-repositories-meta/limited_org/public_repo_on_limited_org.git/objects/21/2f14c8b713de38bd0b3fb23bd288369b01668a create mode 100644 tests/gitea-repositories-meta/limited_org/public_repo_on_limited_org.git/objects/90/e402c3937a4639725fcc59ca1f529e7dc8506f create mode 100644 tests/gitea-repositories-meta/limited_org/public_repo_on_limited_org.git/objects/ed/d9c1000cd1444efd63e153e3554c8d5656bf65 create mode 100644 tests/gitea-repositories-meta/limited_org/public_repo_on_limited_org.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/HEAD create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/config create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/hooks/post-checkout create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/hooks/post-commit create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/hooks/post-merge create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/hooks/pre-push create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/index create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/lfs/objects/d6/f1/d6f175817f886ec6fbbc1515326465fa96c3bfd54a4ea06cfd6dbbd8340e0152 create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/lfs/objects/fb/8f/fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab041 create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/objects/54/6244003622c64b2fc3c2cd544d7a29882c8383 create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/objects/6a/6ccf5d874fec134ee712572cc03a0f2dd7afec create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/objects/a6/7134b8484c2abe9fa954e1fd83b39b271383ed create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/objects/b7/01ed6ffe410f0c3ac204b929ea47cfec6cef54 create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/objects/f2/07b74f55cd7f9e800b7550d587cbc488f6eaf1 create mode 100644 tests/gitea-repositories-meta/migration/lfs-test.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker.git/HEAD create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker.git/config create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker.git/index create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker.git/objects/ba/ea7d6e6b7773a80bcede323cfb21dfe9d4b855 create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker.git/objects/c2/a1ad4c931cebe27c7e39176fe7119b5557c9eb create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker.git/objects/cd/aca8cf1d36e1e4e508a940f6e157e239beccfa create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker.git/refs/heads/branch1 create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_alpha.git/HEAD create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_alpha.git/config create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_alpha.git/index create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_alpha.git/objects/ba/ea7d6e6b7773a80bcede323cfb21dfe9d4b855 create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_alpha.git/objects/c2/a1ad4c931cebe27c7e39176fe7119b5557c9eb create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_alpha.git/objects/cd/aca8cf1d36e1e4e508a940f6e157e239beccfa create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_alpha.git/refs/heads/branch1 create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_alpha.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_numeric.git/HEAD create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_numeric.git/config create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_numeric.git/index create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_numeric.git/objects/ba/ea7d6e6b7773a80bcede323cfb21dfe9d4b855 create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_numeric.git/objects/c2/a1ad4c931cebe27c7e39176fe7119b5557c9eb create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_numeric.git/objects/cd/aca8cf1d36e1e4e508a940f6e157e239beccfa create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_numeric.git/refs/heads/branch1 create mode 100644 tests/gitea-repositories-meta/org26/repo_external_tracker_numeric.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/HEAD create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/config create mode 100755 tests/gitea-repositories-meta/org3/repo3.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/org3/repo3.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/org3/repo3.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/org3/repo3.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/org3/repo3.git/hooks/update create mode 100755 tests/gitea-repositories-meta/org3/repo3.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/objects/20/ade30d25e0ecaeec84e7f542a8456900858240 create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/objects/27/74debeea6dc742cc4971a92db0e08b95b60588 create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/objects/2a/47ca4b614a9f5a43abbd5ad851a54a616ffee6 create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/objects/2f/9b22fd3159a43b7b4e5dd806fcd544edf8716f create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/objects/d2/2b4d4daa5be07329fcef6ed458f00cf3392da0 create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/objects/d5/6a3073c1dbb7b15963110a049d50cdb5db99fc create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/objects/ec/f0db3c1ec806522de4b491fb9a3c7457398c61 create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/objects/ee/16d127df463aa491e08958120f2108b02468df create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/org3/repo3.git/refs/heads/test_branch create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/HEAD create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/config create mode 100755 tests/gitea-repositories-meta/org3/repo5.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/org3/repo5.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/org3/repo5.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/org3/repo5.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/org3/repo5.git/hooks/update create mode 100755 tests/gitea-repositories-meta/org3/repo5.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/objects/20/ade30d25e0ecaeec84e7f542a8456900858240 create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/objects/27/74debeea6dc742cc4971a92db0e08b95b60588 create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/objects/2a/47ca4b614a9f5a43abbd5ad851a54a616ffee6 create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/objects/2f/9b22fd3159a43b7b4e5dd806fcd544edf8716f create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/objects/d2/2b4d4daa5be07329fcef6ed458f00cf3392da0 create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/objects/d5/6a3073c1dbb7b15963110a049d50cdb5db99fc create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/objects/ec/f0db3c1ec806522de4b491fb9a3c7457398c61 create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/objects/ee/16d127df463aa491e08958120f2108b02468df create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/org3/repo5.git/refs/heads/test_branch create mode 100644 tests/gitea-repositories-meta/org41/repo61.git/HEAD create mode 100644 tests/gitea-repositories-meta/org41/repo61.git/config create mode 100644 tests/gitea-repositories-meta/org41/repo61.git/objects/.keep create mode 100644 tests/gitea-repositories-meta/org41/repo61.git/refs/.keep create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/HEAD create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/config create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/description create mode 100755 tests/gitea-repositories-meta/org42/search-by-path.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/org42/search-by-path.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/org42/search-by-path.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/org42/search-by-path.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/org42/search-by-path.git/hooks/proc-receive create mode 100755 tests/gitea-repositories-meta/org42/search-by-path.git/hooks/proc-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/org42/search-by-path.git/hooks/update create mode 100755 tests/gitea-repositories-meta/org42/search-by-path.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/info/refs create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-a7bef76cf6e2b46bc816936ab69306fb10aea571.bitmap create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-a7bef76cf6e2b46bc816936ab69306fb10aea571.idx create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-a7bef76cf6e2b46bc816936ab69306fb10aea571.pack create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-a7bef76cf6e2b46bc816936ab69306fb10aea571.rev create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/packed-refs create mode 100644 tests/gitea-repositories-meta/org42/search-by-path.git/refs/.keep create mode 100644 tests/gitea-repositories-meta/privated_org/private_repo_on_private_org.git/HEAD create mode 100644 tests/gitea-repositories-meta/privated_org/private_repo_on_private_org.git/config create mode 100644 tests/gitea-repositories-meta/privated_org/private_repo_on_private_org.git/objects/6e/75c9f89da9a9b93f4f36e61ed092f7a1625ba0 create mode 100644 tests/gitea-repositories-meta/privated_org/private_repo_on_private_org.git/objects/7f/eb6f9dd600e17a04f48a76cfa0a56a3f30e2c1 create mode 100644 tests/gitea-repositories-meta/privated_org/private_repo_on_private_org.git/objects/b7/91b41c0ae8cb3c4b12f3fd8c3709c2481d9e37 create mode 100644 tests/gitea-repositories-meta/privated_org/private_repo_on_private_org.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/privated_org/public_repo_on_private_org.git/HEAD create mode 100644 tests/gitea-repositories-meta/privated_org/public_repo_on_private_org.git/config create mode 100644 tests/gitea-repositories-meta/privated_org/public_repo_on_private_org.git/objects/04/f99c528b643b9175a4b156cdfc13aba6b43853 create mode 100644 tests/gitea-repositories-meta/privated_org/public_repo_on_private_org.git/objects/86/de16d8658f5c0a17ec6aa313871295d7072f78 create mode 100644 tests/gitea-repositories-meta/privated_org/public_repo_on_private_org.git/objects/bf/19fd4707acb403c4aca44f126ab69142ac59ce create mode 100644 tests/gitea-repositories-meta/privated_org/public_repo_on_private_org.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/HEAD create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/config create mode 100755 tests/gitea-repositories-meta/user12/repo10.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/user12/repo10.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user12/repo10.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/user12/repo10.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user12/repo10.git/hooks/update create mode 100755 tests/gitea-repositories-meta/user12/repo10.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/info/refs create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/objects/2a/2f1d4670728a2e10049e345bd7a276468beab6 create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/objects/4b/4851ad51df6a7d9f25c979345979eaeb5b349f create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/objects/65/f1bf27bc3bf70f64657658635e66094edbcb4d create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/refs/heads/DefaultBranch create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/refs/heads/develop create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/refs/heads/feature/1 create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user12/repo10.git/refs/tags/v1.1 create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/HEAD create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/config create mode 100755 tests/gitea-repositories-meta/user13/repo11.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/user13/repo11.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user13/repo11.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/user13/repo11.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user13/repo11.git/hooks/update create mode 100755 tests/gitea-repositories-meta/user13/repo11.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/info/refs create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/objects/0a/bcb056019adb8336cf9db3ad9d9cf80cd4b141 create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/objects/2a/2f1d4670728a2e10049e345bd7a276468beab6 create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/objects/4b/4851ad51df6a7d9f25c979345979eaeb5b349f create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/objects/65/f1bf27bc3bf70f64657658635e66094edbcb4d create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/objects/75/d1afd00e111c8dbd9e3d96a27b431ac5ae6d74 create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/objects/ed/447543e0c85d628b91f7f466f4921908f4c5ea create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/refs/heads/DefaultBranch create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/refs/heads/branch2 create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/refs/heads/develop create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/refs/heads/feature/1 create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user13/repo11.git/refs/tags/v1.1 create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/config create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/0a/8499a22ad32a80beda9d75efe15f9f94582468 create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/0c/cf1fcd4d1717c22de0707619a5577ea0acebf0 create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/3e/a192a6466793d4b7cd8641801ca0c6bec3979c create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/3f/6594f108842b7c50772510e53ce113d3583c4a create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/58/e97d1a24fb9e1599d8a467ec409430f3d3569e create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/67/68c1fc1d9448422f05cc84267d94ee62085fe8 create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/6e/8eabd9a7f8d6acd2a1219facfd37415564b144 create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/85/f46d747a68adf79cc01e2c25ba6a56932d298d create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/8d/dd8d1ad1fdc21ab629e906711fa9bc27aa1c52 create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/95/fd0c4138480e4b3913e7cf71a90623fb926fe8 create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/98/00fe78cabf4fe774fcf376f97fa2a0ed06987b create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/9f/cdb7d53bdef786d2e5577948a0c0d4b321fe5a create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/c2/0caf78b5f9dd2d0d183876c5cd0e761c13f7f8 create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/c5/2ba74685f5c8c593efbbb38f62fe024110adef create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/objects/d6/ae8023a10ff446b6a4e7f441554834008e99c3 create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/packed-refs create mode 100644 tests/gitea-repositories-meta/user2/commits_search_test.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/config create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/info/refs create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/0a/6dda431c72a6a4aac05b98e319972a1a55e01c create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/0c/396a509b64fd4e2e55649d100b86e8b96cc0e5 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/10/0ef49565829e7bd83057d2dab88f58b00db831 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/10/9ab1c0b84e088d7edcf018379518b49361f285 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/19/78192d98bb1b65e11c2cf37da854fbf94bffd6 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/1e/67d753ac1f9097eff26f9d33eb80182344b72c create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/23/576dd018294e476c06e569b6b0f170d0558705 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/28/16bffda09c0f23775ea4be279de004d28a3803 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/35/f03b5e176ee6d24c86b5cca7009a5b0ba2a026 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/38/cdad2e40c989aabab3f2d0a27faf0f7be617d5 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/3e/64625bd6eb5bcba69ac97de6c8f507402df861 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/4b/860706d3eec5858324d4ba00db0423ca4cbf50 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/4c/a8bcaf27e28504df7bf996819665986b01c847 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/52/84ca7f5757816e67c098224a8367aa2544222d create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/53/9a24812705f77484568e6ad7db84764c1903c8 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/70/8605e8984e7fb9be58818e0e6d9f21bcefd63e create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/74/7ddb3506a4fa04a7747808eb56ae16f9e933dc create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/7e/d1d42eda9110676d5c3a7721965d6ed1afe83c create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/81/1d46c7e518f4f180afb862c0db5cb8c80529ce create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/83/2d33e438d2b4a86fba81cb67b32d1d61a828cb create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/83/7d5c8125633d7d258f93b998e867eab0145520 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/87/cdc1333f5f117a92f3cef78ebe0301114b3610 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/92/70b08497106eaa65fce8aa91f37c4780f76909 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/96/cef4a7b72b3c208340ae6f0cf55a93e9077c93 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/97/0c5deb117526983f554eaaa1b59102d3e3e0f7 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/c5/626fc9eff57eb1bb7b796b01d4d0f2f3f792a2 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/c7/04db5794097441aa2d9dd834d5b7e2f8f08108 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/cb/ff181af4c9c7fee3cf6c106699e07d9a3f54e6 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/d1/8e427f4011e74e96a31823c938be26eebab53b create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/d2/5795e38fbc1b4839697e834b957d61c83d994f create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/d6/6f456f0813a5841fbc03e5f1c47304dc675695 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/e1/7e0fa20f3d2125916f2fb2f51f19240678cb83 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/ec/d9fdda5c814055ee619513e1c388ba1bbcb280 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/refs/heads/branch1 create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/refs/heads/main create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/commitsonpr.git/refs/pull/1/head create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/config create mode 100755 tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/pre-receive.d/pre-receive create mode 100755 tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/update create mode 100755 tests/gitea-repositories-meta/user2/git_hooks_test.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/info/refs create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/objects/2a/2f1d4670728a2e10049e345bd7a276468beab6 create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/objects/4b/4851ad51df6a7d9f25c979345979eaeb5b349f create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/objects/65/f1bf27bc3bf70f64657658635e66094edbcb4d create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/refs/heads/DefaultBranch create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/refs/heads/develop create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/refs/heads/feature/1 create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/git_hooks_test.git/refs/tags/v1.1 create mode 100644 tests/gitea-repositories-meta/user2/glob.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/glob.git/config create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/48/06cb9df135782b818c968c2fadbd2c150d23d6 create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/59/fee614e09d1f1cd1e15e4b2a7e9c8873a81498 create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/7c/8ac2f8d82a1eb5f6aaece6629ff11015f91eb4 create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/8e/592e636d27ac144f92f7fe8c33631cbdea594d create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/95/aff026f99a9ab76fbd01decb63dd3dbc03e498 create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/ae/d1ffed24cc3cf9b80490795e893cae4bddd684 create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/bf/d6a6583f9a9ac59bd726c1df26c64a89427ede create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/c8/eb3b6c767ccb68411d0a1f6c769be69fb4d95a create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/de/6be43fe8eb19ca3f4e934cb8b9a9a0b20fe865 create mode 100644 tests/gitea-repositories-meta/user2/glob.git/objects/ef/6b814b610d8e7717aa0f71fbe5842bcf814697 create mode 100644 tests/gitea-repositories-meta/user2/glob.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/config create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/15/2de0f78bc6815b58cd9f08aebe3f66fb0f172e create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/23/10e4a07f9314a1a92fdfbdcd3d2884f01e96ab create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/2b/6c6c4eaefa24b22f2092c3d54b263ff26feb58 create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/73/cf03db6ece34e12bf91e8853dc58f678f2f82d create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/74/21a018a7e3f15ee5691f162d0ed87dc19882f0 create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/82/76d2a29779af982c0afa976bdb793b52d442a8 create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/bc/e50ea8f203ee923d5a640d05208abf3206486e create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/d4/a41a0d4db4949e129bd22f871171ea988103ef create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/d7/ce0013ced38b0696dd2d68d69a5d8b652f7148 create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/objects/df/d8105b264d304c49ed9f1d56bd90189ecdf833 create mode 100644 tests/gitea-repositories-meta/user2/lfs.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/readme-test.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/readme-test.git/config create mode 100644 tests/gitea-repositories-meta/user2/readme-test.git/objects/.keep create mode 100644 tests/gitea-repositories-meta/user2/readme-test.git/refs/.keep create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/config create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/08/9ba8b2f324d89b74f6853374a0476b312a46f6 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/18/4288e5acffbcb17160b990e8efe83b12dfaaba create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/24/3cdd85d09ce4104855edf219c05b74c65350fc create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/43/80f99290b2b3922733ff82c57afad915ace907 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/6d/fe48a18ce2fb47d3a75e13c7ab35f935077535 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/71/97b56fdc75b453f47c9110938cb46a303579fd create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/79/f9d88f1b054d650f88da0bd658e21f7b0cf6ec create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/7c/055ef1678b03b831bbe7b9ca5aed33b1a8dea0 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/80/abeef37c96b85b83a916f5f295f04f4d380a42 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/a8/a700e8c644c783ba2c6e742bb81bf91e244bff create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/bc/7068d1eb2f93a04e3ec73521473444ceec0961 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/c4/a4e1a72a2098d687b4280e7c6972280c1f9c39 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/cd/7f28e1b404377eadbe0d54234ba861883e6930 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/ce/f06e48f2642cd0dc9597b4bea09f4b3f74aad6 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/d0/718fe871fbb54da104ff201f75f62a6ced2e29 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/d4/613f8dad1fa61e415922f6eb33244358fca85d create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/dd/5488178fc8a5c62430b3fb3017203b917b95ab create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/ef/3c849ed54b22bb1f500da91b789c40cb0915da create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/f3/f1c90ac949aa1b0f129d30f338d408663c8a83 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/objects/f9/3e3a1a1525fb5b91020da86e44810c87a2d7bc create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/refs/heads/main create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/refs/tags/v1.0 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/refs/tags/v1.1 create mode 100644 tests/gitea-repositories-meta/user2/repo-release.git/refs/tags/v2.0 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/config create mode 100755 tests/gitea-repositories-meta/user2/repo1.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/user2/repo1.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/repo1.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/user2/repo1.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/repo1.git/hooks/proc-receive create mode 100755 tests/gitea-repositories-meta/user2/repo1.git/hooks/proc-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/repo1.git/hooks/update create mode 100755 tests/gitea-repositories-meta/user2/repo1.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/info/refs create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/00/750edc07d6415dcc07ae0351e9397b0222b7ba create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/08/51b61d9f8ca0e9e63617e11907988ee88b1ca6 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/12/8105ae73669ac2a4cb42751538f0c65c54e28a create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/16/633238d370a441f98dca532e4296a619c4c85f create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/2a/2f1d4670728a2e10049e345bd7a276468beab6 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/3f/a2f829675543ecfc16b2891aebe8bf0608a8f4 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/40/3d76c604cb569323864e06a07b85d466924802 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/46/49299398e4d39a5c09eb4f534df6f1e1eb87cc create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/4a/357436d925b5c974181ff12a994538ddc5a269 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/4b/4851ad51df6a7d9f25c979345979eaeb5b349f create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/5c/050d3b6d2db231ab1f64e324f1b6b9a0b181c2 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/5f/22f7d0d95d614d25a5b68592adb345a4b5c7fd create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/62/fb502a7172d4453f0322a2cc85bddffa57f07a create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/65/f1bf27bc3bf70f64657658635e66094edbcb4d create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/6a/a3a5385611c5eb8986c9961a9c34a93cbaadfb create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/78/fb907e3a3309eae4fe8fef030874cebbf1cd5e create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/7c/4df115542e05c700f297519e906fd63c9c9804 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/90/c1019714259b24fb81711d4416ac0f18667dfa create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/90/dcd07da077d1e7cd6032b52d1f79ae2b5f19b2 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/94/922e1295c678267de1193b7b84ad8a086c27f9 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/98/5f0301dba5e7b34be866819cd15ad3d8f508ee create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/a6/9277c81e90b98a7c0ab25b042a6e296da8eb9a create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/a7/57c0ea621e63d0fd6fc353a175fdc7199e5d1d create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/b2/60587271671842af0b036e4fe643c9d45b7ddd create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/d4/a1a6dcf7bd42891f264d484e80dac7e66b5410 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/d7/bd5b8cfb680f460e37b6fd7cf74c284e059118 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/dc/7a8ba127fee870dd683310ce660dfe59333a1b create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/dd/59742c0f6672911f2b64cba5711ac00593ed32 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/f3/fa0f5cc797fc4c02a1b8bec9de4b2072fcdbdf create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/heads/DefaultBranch create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/heads/branch2 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/heads/develop create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/heads/feature/1 create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/heads/home-md-img-check create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/heads/pr-to-update create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/heads/sub-home-md-img-check create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/notes/commits create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/pull/2/head create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/pull/3/head create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/pull/5/head create mode 100644 tests/gitea-repositories-meta/user2/repo1.git/refs/tags/v1.1 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/config create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/0c/f15c3f66ec8384480ed9c3cf87c9e97fbb0ec3 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/0d/ca5bd9b5d7ef937710e056f575e86c0184ba85 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/2c/54faec6c45d31c1abfaecdab471eac6633738a create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/32/5dc4f8e9344e6668f21536a69d5f1d4ed53ca3 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/42/3313fbd38093bb10d0c8387db9105409c6f196 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/71/911bf48766c7181518c1070911019fbb00b1fc create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/72/fc6251cc648e914c10009d31431fa2e38b9a20 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/74/d5a0d73db9b9ef7aa9978eb7a099b08f54d45e create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/7c/d7c8fa852973c72c66eb120a6677c54a8697f7 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/81/a1c039774e337621609336c0e44ed9f92278f7 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/89/43a1d5f93c00439d5ffc0f8e36f5d60abae46c create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/91/dc55f9de16a558e859123f2b99668469b1a1dc create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/a5/bbc0fd39a696feabed2d4cccaf05abbcaf3b02 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/c1/0d10b7e655b3dab1f53176db57c8219a5488d6 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/c4/b38c3e1395393f75bbbc2ed10c7eeb577d3b64 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/cf/19952a40b92eb2f86689146a65ac2d87c0818a create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/e1/6da91326b845f1ba86a7df0a67db352f96dcb0 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/e5/3d079e581fbfdea1075a54d5b621eab0090e52 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/ea/82fc8777a24b07c26b3a4bf4e2742c03733eab create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/objects/f5/05ec9b5c7a45a10259c1dda7f18434e5d55940 create mode 100644 tests/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/repo15.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/repo15.git/config create mode 100755 tests/gitea-repositories-meta/user2/repo15.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/user2/repo15.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/repo15.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/user2/repo15.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/repo15.git/hooks/update create mode 100755 tests/gitea-repositories-meta/user2/repo15.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/config create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/0c/3d59dea27b97aa3cb66072745d7a2c51a7a8b1 create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/24/f83a471f77579fea57bac7255d6e64e70fce1c create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/27/566bd5738fc8b4e3fef3c5e72cce608537bd95 create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/3b/2b54fe3d9a8279d5b926124dccdf279b8eff2f create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/45/8121ce9a6b855c9733bae62093caf3f39685de create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/50/99b81332712fe655e34e8dd63574f503f61811 create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/69/554a64c1e6030f051e5c3f94bfbd773cd6a324 create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/a4/3476a501516e065c5a82f05fd58fd319598bc1 create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/e9/4083fcdf1f10c545e9253a23c5e44a2ff68aac create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/f2/7c2b2b03dcab38beaf89b0ab4ff61f6de63441 create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/objects/f9/0451c72ef61a7645293d17b47be7a8e983da57 create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/refs/heads/good-sign create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/refs/heads/good-sign-not-yet-validated create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/repo16.git/refs/heads/not-signed create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/config create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/info/refs create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/0a/7d8b41ae9763e9a1743917396839d1791d49d0 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/0c/f15c3f66ec8384480ed9c3cf87c9e97fbb0ec3 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/10/32bbf17fbc0d9c95bb5418dabe8f8c99278700 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/1c/887eaa8d81fa86da7695d8f635cf17813eb422 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/26/f842bcad37fa40a1bb34cbb5ee219ee35d863d create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/32/5dc4f8e9344e6668f21536a69d5f1d4ed53ca3 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/36/fff01c8c9f722d49d53186abd27b5be8d85338 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/42/3313fbd38093bb10d0c8387db9105409c6f196 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/71/911bf48766c7181518c1070911019fbb00b1fc create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/72/fc6251cc648e914c10009d31431fa2e38b9a20 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/74/d5a0d73db9b9ef7aa9978eb7a099b08f54d45e create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/7c/d7c8fa852973c72c66eb120a6677c54a8697f7 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/ba/1aed4e2ea2443d76cec241b96be4ec990852ec create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/c1/0d10b7e655b3dab1f53176db57c8219a5488d6 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/c4/b38c3e1395393f75bbbc2ed10c7eeb577d3b64 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/f5/05ec9b5c7a45a10259c1dda7f18434e5d55940 create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/info/commit-graph create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/pack/pack-a2f7ad943b3d857eb3ebdb4b35eeef38f63cf5d2.bitmap create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/pack/pack-a2f7ad943b3d857eb3ebdb4b35eeef38f63cf5d2.idx create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/objects/pack/pack-a2f7ad943b3d857eb3ebdb4b35eeef38f63cf5d2.pack create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/packed-refs create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/repo2.git/refs/tags/v1.1 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/config create mode 100755 tests/gitea-repositories-meta/user2/repo20.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/user2/repo20.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/repo20.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/user2/repo20.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/repo20.git/hooks/update create mode 100755 tests/gitea-repositories-meta/user2/repo20.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/info/refs create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/02/15cbe13d2695a2c3464ab5e59f47f37c3ff5d5 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/05/81d7edf45206787ff93956ea892e8a2ae77604 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/07/0b2e783a6b3e521a23fdead377a3e41a04410d create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/1b/271d83842d348b1ee71d8e6ead400aaeb4d1b5 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/29/5ba6ac57fdd46f62a51272f40e60b6dea697b2 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/2c/ec0f7069ed09d934e904c49f414d8bdf818ce4 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/41/4a282859758ba7b159bfbd9c2b193eb8f135ee create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/79/adb592126eddce5f656f56db797910db025af0 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/80/8038d2f71b0ab020991439cffd24309c7bc530 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/83/70977f63979e140b6b58992b1fdb4098b24cd9 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/8b/abce967f21b9dfa6987f943b91093dac58a4f0 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/8c/e1dee41e1a3700819a9a309f275f8dc7b7e0b6 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/a4/202876cd8bbc3f38b7d99594edbe1bb7f97a6f create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/b0/246d5964a3630491bd06c756be46513e3d7035 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/b6/7e43a07d48243a5f670ace063acd5e13f719df create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/ba/3aeafe10402c6b29535a58d91def7e43638d9d create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/c5/0ac6b9e25abb8200bb377755367d7265c581cf create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/c8/e31bc7688741a5287fcde4fbb8fc129ca07027 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/cf/e3b3c1fd36fba04f9183287b106497e1afe986 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/db/89c972fc57862eae378f45b74aca228037d415 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/ea/f5f7510320b6a327fb308379de2f94d8859a54 create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/refs/heads/add-csv create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/refs/heads/remove-files-a create mode 100644 tests/gitea-repositories-meta/user2/repo20.git/refs/heads/remove-files-b create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/HEAD create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/config create mode 100755 tests/gitea-repositories-meta/user2/utf8.git/hooks/post-receive create mode 100755 tests/gitea-repositories-meta/user2/utf8.git/hooks/post-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/utf8.git/hooks/pre-receive create mode 100755 tests/gitea-repositories-meta/user2/utf8.git/hooks/pre-receive.d/gitea create mode 100755 tests/gitea-repositories-meta/user2/utf8.git/hooks/update create mode 100755 tests/gitea-repositories-meta/user2/utf8.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/info/refs create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/14/c42687126acae9d1ad41d7bdb528f811065a6a create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/1d/5e00f305a7ca6a8a94e65456820a6d260adab8 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/28/d579e4920fbf4f66e71dab3e779d9fbf41422a create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/3a/810dbf6b96afaa8c5f69a8b6ec1dabfca7368b create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/3a/a73c3499bff049a352b4e265575373e964b89a create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/3a/c6084110205f98174c4f1ec7e78cb21a15dfc2 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/4c/61dd0a799e0830e77edfe6c74f7c349bc8e62a create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/50/4d9fe743979d4e9785a25a363c7007293f0838 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/56/92bcf9f7c9eacb1ad68442161f2573877f96f4 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/59/e2c41e8f5140bb0182acebec17c8ad9831cc62 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/64/89894ad11093fdc49c0ed857d80682344a7264 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/6d/0c79ce3401c67d1ad522e61c47083a9fdee16c create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/84/7c6d93c6860dd377651245711b7fbcd34a18d4 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/9b/9cc8f558d1c4f815592496fa24308ba2a9c824 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/a4/f1bb3f2f8c6a0e840e935812ef4903ce515dad create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/a9/a61830fbf4e84999d3b20cf178954366701fe5 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/c7/85b65bf16928b58567cb23669125c0ccd25a4f create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/e9/63733b8a355cf860c465b4af7b236a6ef08783 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/eb/f146f803fccbc1471ef01d8fa0fe12c14e61a5 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/ee/9686cb562f492f64381bff7f298b2a1c67a141 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/f4/02ff67c0b3161c3988dbf6188e6e0df257fd75 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/refs/heads/Grüßen create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/refs/heads/Plus+Is+Not+Space create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/refs/heads/ГлавнаяВетка create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/refs/heads/а/б/в create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/refs/heads/ブランチ create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/refs/tags/Ё/人 create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/refs/tags/Тэг create mode 100644 tests/gitea-repositories-meta/user2/utf8.git/refs/tags/タグ create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/HEAD create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/config create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/hooks/post-receive create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/hooks/post-receive.d/gitea create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/hooks/pre-receive create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/hooks/pre-receive.d/gitea create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/hooks/update create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/info/refs create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/objects/47/34b1f84a367fa1b81c31aa4234a5bad11cafa3 create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/objects/4d/31f3a12656368a8d9180f431d40d0fc408be2d create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/objects/51/f84af231345367fd5d61ceb89efb3b6d757061 create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/objects/79/3aa682b06ae032641abf70c5dfeade28c07c52 create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/objects/aa/cbdfe9e1c4b47f60abe81849045fa4e96f1d75 create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/objects/dd/392e939ea4936b2459219c9c9a1f25547ccaeb create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/objects/f2/8eeca3df7614fd4f10c1030f13feb418ef3c6f create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user27/repo49.git/refs/heads/test/archive create mode 100644 tests/gitea-repositories-meta/user27/template1.git/HEAD create mode 100644 tests/gitea-repositories-meta/user27/template1.git/config create mode 100644 tests/gitea-repositories-meta/user27/template1.git/hooks/post-receive create mode 100644 tests/gitea-repositories-meta/user27/template1.git/hooks/post-receive.d/gitea create mode 100644 tests/gitea-repositories-meta/user27/template1.git/hooks/pre-receive create mode 100644 tests/gitea-repositories-meta/user27/template1.git/hooks/pre-receive.d/gitea create mode 100644 tests/gitea-repositories-meta/user27/template1.git/hooks/update create mode 100644 tests/gitea-repositories-meta/user27/template1.git/hooks/update.d/gitea create mode 100644 tests/gitea-repositories-meta/user27/template1.git/info/refs create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/2a/83b349fa234131fc5db6f2a0498d3f4d3d6038 create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/3d/0bc64f2521cfc7ffce6c175c1c846c88eb6df7 create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/47/34b1f84a367fa1b81c31aa4234a5bad11cafa3 create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/4d/31f3a12656368a8d9180f431d40d0fc408be2d create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/51/f84af231345367fd5d61ceb89efb3b6d757061 create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/79/3aa682b06ae032641abf70c5dfeade28c07c52 create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/83/77b2196e99ac8635aae79df3db76959ccd1094 create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/99/45b93bcb5b70af06e0322bd2caa6180680991f create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/aa/cbdfe9e1c4b47f60abe81849045fa4e96f1d75 create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/af/f5b10402b4e0479d1e76bc41a42d29fe7f28fa create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/b9/04864fd6cd0c8e9054351fd39a980bfd214229 create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/c5/10abf4c7c3e0dc4bf07db9344c61c4e6ee7cbc create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/dd/392e939ea4936b2459219c9c9a1f25547ccaeb create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/f2/8eeca3df7614fd4f10c1030f13feb418ef3c6f create mode 100644 tests/gitea-repositories-meta/user27/template1.git/objects/info/packs create mode 100644 tests/gitea-repositories-meta/user27/template1.git/refs/heads/master create mode 100644 tests/gitea-repositories-meta/user30/empty.git/HEAD create mode 100644 tests/gitea-repositories-meta/user30/empty.git/config create mode 100644 tests/gitea-repositories-meta/user30/empty.git/objects/.keep create mode 100644 tests/gitea-repositories-meta/user30/empty.git/refs/.keep create mode 100644 tests/gitea-repositories-meta/user40/repo60.git/HEAD create mode 100644 tests/gitea-repositories-meta/user40/repo60.git/config create mode 100644 tests/gitea-repositories-meta/user40/repo60.git/objects/.keep create mode 100644 tests/gitea-repositories-meta/user40/repo60.git/refs/.keep create mode 100644 tests/gitea-repositories-meta/user5/repo4.git/HEAD create mode 100644 tests/gitea-repositories-meta/user5/repo4.git/config create mode 100644 tests/gitea-repositories-meta/user5/repo4.git/objects/16/dfebd1ed3905d78d7e061e945fc9c34afe4e81 create mode 100644 tests/gitea-repositories-meta/user5/repo4.git/objects/c1/202ad022ae7d3a6d2474dc76d5a0c8e87cdc0f create mode 100644 tests/gitea-repositories-meta/user5/repo4.git/objects/c7/cd3cd144e6d23c9d6f3d07e52b2c1a956e0338 create mode 100644 tests/gitea-repositories-meta/user5/repo4.git/refs/heads/master create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F1%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F10%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F10%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F11%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F11%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F12%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F12%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F13%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F13%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F2%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F3%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F3%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F5%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F5%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F6%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F6%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F7%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F7%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F8%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F8%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F9%2Fcomments%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2F9%2Freactions%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2Fcomments%2F1553%2Freactions create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%2Fcomments%2F1554%2Freactions create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fissues%3Flimit%3D47%26page%3D1%26state%3Dall%26type%3Dissues create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Flabels%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fmilestones%3Flimit%3D50%26page%3D1%26state%3Dall create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F10%2Freviews%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F11%2Freviews%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F12%2Freviews%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F13%2Freviews%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F8%2Freviews%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%2F9%2Freviews%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Fpulls%3Flimit%3D49%26page%3D1%26state%3Dall create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Freleases%3Flimit%3D50%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Frepos%2Fgitea%2Ftest_repo%2Ftopics%3Flimit%3D0%26page%3D1 create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Fsettings%2Fapi create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fapi%2Fv1%2Fversion create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fgitea%2Ftest_repo%2Fpulls%2F10.patch create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fgitea%2Ftest_repo%2Fpulls%2F11.patch create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fgitea%2Ftest_repo%2Fpulls%2F12.patch create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fgitea%2Ftest_repo%2Fpulls%2F13.patch create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fgitea%2Ftest_repo%2Fpulls%2F8.patch create mode 100644 tests/integration/_mock_data/Test_MigrateFromGiteaToGitea/GET_%2Fgitea%2Ftest_repo%2Fpulls%2F9.patch create mode 100644 tests/integration/actions_approve_test.go create mode 100644 tests/integration/actions_concurrency_test.go create mode 100644 tests/integration/actions_concurrent_claim_test.go create mode 100644 tests/integration/actions_delete_run_test.go create mode 100644 tests/integration/actions_dynamic_matrix_test.go create mode 100644 tests/integration/actions_inputs_test.go create mode 100644 tests/integration/actions_job_test.go create mode 100644 tests/integration/actions_job_token_test.go create mode 100644 tests/integration/actions_list_filter_test.go create mode 100644 tests/integration/actions_log_finalize_test.go create mode 100644 tests/integration/actions_log_test.go create mode 100644 tests/integration/actions_rerun_test.go create mode 100644 tests/integration/actions_reusable_workflow_test.go create mode 100644 tests/integration/actions_route_test.go create mode 100644 tests/integration/actions_runner_modify_test.go create mode 100644 tests/integration/actions_runner_sort_test.go create mode 100644 tests/integration/actions_runner_test.go create mode 100644 tests/integration/actions_schedule_test.go create mode 100644 tests/integration/actions_scoped_workflow_test.go create mode 100644 tests/integration/actions_settings_test.go create mode 100644 tests/integration/actions_trigger_test.go create mode 100644 tests/integration/actions_variables_test.go create mode 100644 tests/integration/admin_audit_test.go create mode 100644 tests/integration/admin_config_test.go create mode 100644 tests/integration/admin_user_org_test.go create mode 100644 tests/integration/admin_user_test.go create mode 100644 tests/integration/api_actions_artifact_test.go create mode 100644 tests/integration/api_actions_artifact_v4_test.go create mode 100644 tests/integration/api_actions_permission_test.go create mode 100644 tests/integration/api_actions_run_test.go create mode 100644 tests/integration/api_actions_runner_test.go create mode 100644 tests/integration/api_admin_org_test.go create mode 100644 tests/integration/api_admin_packages_test.go create mode 100644 tests/integration/api_admin_test.go create mode 100644 tests/integration/api_auth_test.go create mode 100644 tests/integration/api_branch_test.go create mode 100644 tests/integration/api_comment_attachment_test.go create mode 100644 tests/integration/api_comment_test.go create mode 100644 tests/integration/api_fork_test.go create mode 100644 tests/integration/api_gitignore_templates_test.go create mode 100644 tests/integration/api_gpg_keys_test.go create mode 100644 tests/integration/api_helper_for_declarative_test.go create mode 100644 tests/integration/api_httpsig_test.go create mode 100644 tests/integration/api_issue_attachment_test.go create mode 100644 tests/integration/api_issue_config_test.go create mode 100644 tests/integration/api_issue_dependency_test.go create mode 100644 tests/integration/api_issue_label_test.go create mode 100644 tests/integration/api_issue_lock_test.go create mode 100644 tests/integration/api_issue_milestone_test.go create mode 100644 tests/integration/api_issue_pin_test.go create mode 100644 tests/integration/api_issue_reaction_test.go create mode 100644 tests/integration/api_issue_stopwatch_test.go create mode 100644 tests/integration/api_issue_subscription_test.go create mode 100644 tests/integration/api_issue_templates_test.go create mode 100644 tests/integration/api_issue_test.go create mode 100644 tests/integration/api_issue_tracked_time_test.go create mode 100644 tests/integration/api_keys_test.go create mode 100644 tests/integration/api_label_templates_test.go create mode 100644 tests/integration/api_license_templates_test.go create mode 100644 tests/integration/api_notification_test.go create mode 100644 tests/integration/api_oauth2_apps_test.go create mode 100644 tests/integration/api_org_avatar_test.go create mode 100644 tests/integration/api_org_test.go create mode 100644 tests/integration/api_packages_alpine_test.go create mode 100644 tests/integration/api_packages_arch_test.go create mode 100644 tests/integration/api_packages_cargo_test.go create mode 100644 tests/integration/api_packages_chef_test.go create mode 100644 tests/integration/api_packages_composer_test.go create mode 100644 tests/integration/api_packages_conan_test.go create mode 100644 tests/integration/api_packages_conda_test.go create mode 100644 tests/integration/api_packages_container_test.go create mode 100644 tests/integration/api_packages_cran_test.go create mode 100644 tests/integration/api_packages_debian_test.go create mode 100644 tests/integration/api_packages_generic_test.go create mode 100644 tests/integration/api_packages_goproxy_test.go create mode 100644 tests/integration/api_packages_helm_test.go create mode 100644 tests/integration/api_packages_maven_test.go create mode 100644 tests/integration/api_packages_npm_test.go create mode 100644 tests/integration/api_packages_nuget_test.go create mode 100644 tests/integration/api_packages_pub_test.go create mode 100644 tests/integration/api_packages_pypi_test.go create mode 100644 tests/integration/api_packages_rpm_test.go create mode 100644 tests/integration/api_packages_rubygems_test.go create mode 100644 tests/integration/api_packages_swift_test.go create mode 100644 tests/integration/api_packages_terraform_test.go create mode 100644 tests/integration/api_packages_test.go create mode 100644 tests/integration/api_packages_vagrant_test.go create mode 100644 tests/integration/api_private_serv_test.go create mode 100644 tests/integration/api_project_test.go create mode 100644 tests/integration/api_public_only_test.go create mode 100644 tests/integration/api_pull_commits_test.go create mode 100644 tests/integration/api_pull_review_test.go create mode 100644 tests/integration/api_pull_test.go create mode 100644 tests/integration/api_releases_attachment_test.go create mode 100644 tests/integration/api_releases_test.go create mode 100644 tests/integration/api_repo_archive_test.go create mode 100644 tests/integration/api_repo_avatar_test.go create mode 100644 tests/integration/api_repo_branch_test.go create mode 100644 tests/integration/api_repo_collaborator_test.go create mode 100644 tests/integration/api_repo_compare_test.go create mode 100644 tests/integration/api_repo_delete_test.go create mode 100644 tests/integration/api_repo_edit_test.go create mode 100644 tests/integration/api_repo_file_create_test.go create mode 100644 tests/integration/api_repo_file_delete_test.go create mode 100644 tests/integration/api_repo_file_diffpatch_test.go create mode 100644 tests/integration/api_repo_file_get_test.go create mode 100644 tests/integration/api_repo_file_helpers.go create mode 100644 tests/integration/api_repo_file_update_test.go create mode 100644 tests/integration/api_repo_files_change_test.go create mode 100644 tests/integration/api_repo_files_get_test.go create mode 100644 tests/integration/api_repo_get_contents_list_test.go create mode 100644 tests/integration/api_repo_get_contents_test.go create mode 100644 tests/integration/api_repo_git_blobs_test.go create mode 100644 tests/integration/api_repo_git_commits_test.go create mode 100644 tests/integration/api_repo_git_hook_test.go create mode 100644 tests/integration/api_repo_git_notes_test.go create mode 100644 tests/integration/api_repo_git_ref_test.go create mode 100644 tests/integration/api_repo_git_tags_test.go create mode 100644 tests/integration/api_repo_git_trees_test.go create mode 100644 tests/integration/api_repo_hook_test.go create mode 100644 tests/integration/api_repo_languages_test.go create mode 100644 tests/integration/api_repo_lfs_locks_test.go create mode 100644 tests/integration/api_repo_lfs_migrate_test.go create mode 100644 tests/integration/api_repo_lfs_test.go create mode 100644 tests/integration/api_repo_license_test.go create mode 100644 tests/integration/api_repo_raw_test.go create mode 100644 tests/integration/api_repo_secrets_test.go create mode 100644 tests/integration/api_repo_tags_test.go create mode 100644 tests/integration/api_repo_teams_test.go create mode 100644 tests/integration/api_repo_test.go create mode 100644 tests/integration/api_repo_topic_test.go create mode 100644 tests/integration/api_repo_variables_test.go create mode 100644 tests/integration/api_repository_creation_token_scope_test.go create mode 100644 tests/integration/api_settings_test.go create mode 100644 tests/integration/api_team_test.go create mode 100644 tests/integration/api_team_user_test.go create mode 100644 tests/integration/api_token_self_test.go create mode 100644 tests/integration/api_token_test.go create mode 100644 tests/integration/api_twofa_test.go create mode 100644 tests/integration/api_user_avatar_test.go create mode 100644 tests/integration/api_user_block_test.go create mode 100644 tests/integration/api_user_email_test.go create mode 100644 tests/integration/api_user_follow_test.go create mode 100644 tests/integration/api_user_heatmap_test.go create mode 100644 tests/integration/api_user_info_test.go create mode 100644 tests/integration/api_user_org_perm_test.go create mode 100644 tests/integration/api_user_orgs_test.go create mode 100644 tests/integration/api_user_public_only_test.go create mode 100644 tests/integration/api_user_search_test.go create mode 100644 tests/integration/api_user_secrets_test.go create mode 100644 tests/integration/api_user_star_test.go create mode 100644 tests/integration/api_user_update_test.go create mode 100644 tests/integration/api_user_variables_test.go create mode 100644 tests/integration/api_user_visibility_test.go create mode 100644 tests/integration/api_user_watch_test.go create mode 100644 tests/integration/api_wiki_test.go create mode 100644 tests/integration/attachment_test.go create mode 100644 tests/integration/auth_ldap_test.go create mode 100644 tests/integration/auth_oauth2_test.go create mode 100644 tests/integration/avatar.png create mode 100644 tests/integration/branches_test.go create mode 100644 tests/integration/change_default_branch_test.go create mode 100644 tests/integration/cmd_keys_test.go create mode 100644 tests/integration/compare_test.go create mode 100644 tests/integration/config_instance_test.go create mode 100644 tests/integration/cors_test.go create mode 100644 tests/integration/create_no_session_test.go create mode 100644 tests/integration/db_collation_test.go create mode 100644 tests/integration/delete_user_test.go create mode 100644 tests/integration/deploy_token_test.go create mode 100644 tests/integration/download_test.go create mode 100644 tests/integration/dump_restore_test.go create mode 100644 tests/integration/editor_test.go create mode 100644 tests/integration/empty_repo_test.go create mode 100644 tests/integration/ephemeral_actions_runner_deletion_test.go create mode 100644 tests/integration/explore_repos_test.go create mode 100644 tests/integration/explore_user_test.go create mode 100644 tests/integration/feed_repo_test.go create mode 100644 tests/integration/feed_user_test.go create mode 100644 tests/integration/git_general_test.go create mode 100644 tests/integration/git_helper_for_declarative_test.go create mode 100644 tests/integration/git_lfs_ssh_test.go create mode 100644 tests/integration/git_misc_test.go create mode 100644 tests/integration/git_push_test.go create mode 100644 tests/integration/git_smart_http_test.go create mode 100644 tests/integration/git_ssh_redirect_test.go create mode 100644 tests/integration/goget_test.go create mode 100644 tests/integration/gpg_ssh_git_test.go create mode 100644 tests/integration/heatmap_test.go create mode 100644 tests/integration/html_helper.go create mode 100644 tests/integration/incoming_email_test.go create mode 100644 tests/integration/integration_test.go create mode 100644 tests/integration/issue_label_scope_test.go create mode 100644 tests/integration/issue_test.go create mode 100644 tests/integration/issue_timetrack_test.go create mode 100644 tests/integration/lfs_getobject_test.go create mode 100644 tests/integration/lfs_local_endpoint_test.go create mode 100644 tests/integration/lfs_view_test.go create mode 100644 tests/integration/linguist_test.go create mode 100644 tests/integration/links_test.go create mode 100644 tests/integration/manual_merge_test.go create mode 100644 tests/integration/markup_external_test.go create mode 100644 tests/integration/migrate_test.go create mode 100644 tests/integration/migration-test/gitea-v1.6.4.mssql.sql.gz create mode 100644 tests/integration/migration-test/gitea-v1.6.4.mysql.sql.gz create mode 100644 tests/integration/migration-test/gitea-v1.6.4.postgres.sql.gz create mode 100644 tests/integration/migration-test/gitea-v1.6.4.sqlite3.sql.gz create mode 100644 tests/integration/migration-test/gitea-v1.7.0.mssql.sql.gz create mode 100644 tests/integration/migration-test/gitea-v1.7.0.mysql.sql.gz create mode 100644 tests/integration/migration-test/gitea-v1.7.0.postgres.sql.gz create mode 100644 tests/integration/migration-test/gitea-v1.7.0.sqlite3.sql.gz create mode 100644 tests/integration/migration-test/migration_test.go create mode 100644 tests/integration/mirror_pull_test.go create mode 100644 tests/integration/mirror_push_test.go create mode 100644 tests/integration/nonascii_branches_test.go create mode 100644 tests/integration/oauth_avatar_test.go create mode 100644 tests/integration/oauth_test.go create mode 100644 tests/integration/org_count_test.go create mode 100644 tests/integration/org_profile_test.go create mode 100644 tests/integration/org_project_test.go create mode 100644 tests/integration/org_team_invite_test.go create mode 100644 tests/integration/org_test.go create mode 100644 tests/integration/org_worktime_test.go create mode 100644 tests/integration/packages_service_test.go create mode 100644 tests/integration/private-testing.key create mode 100644 tests/integration/privateactivity_test.go create mode 100644 tests/integration/project_test.go create mode 100644 tests/integration/pull_comment_test.go create mode 100644 tests/integration/pull_commit_test.go create mode 100644 tests/integration/pull_compare_test.go create mode 100644 tests/integration/pull_create_test.go create mode 100644 tests/integration/pull_diff_test.go create mode 100644 tests/integration/pull_merge_test.go create mode 100644 tests/integration/pull_review_test.go create mode 100644 tests/integration/pull_status_test.go create mode 100644 tests/integration/pull_update_test.go create mode 100644 tests/integration/release_test.go create mode 100644 tests/integration/rename_branch_test.go create mode 100644 tests/integration/repo_activity_test.go create mode 100644 tests/integration/repo_archive_test.go create mode 100644 tests/integration/repo_branch_test.go create mode 100644 tests/integration/repo_commits_search_test.go create mode 100644 tests/integration/repo_commits_test.go create mode 100644 tests/integration/repo_fork_test.go create mode 100644 tests/integration/repo_generate_test.go create mode 100644 tests/integration/repo_home_token_scope_test.go create mode 100644 tests/integration/repo_merge_upstream_test.go create mode 100644 tests/integration/repo_migrate_test.go create mode 100644 tests/integration/repo_search_test.go create mode 100644 tests/integration/repo_tag_test.go create mode 100644 tests/integration/repo_test.go create mode 100644 tests/integration/repo_topic_test.go create mode 100644 tests/integration/repo_visibility_test.go create mode 100644 tests/integration/repo_watch_test.go create mode 100644 tests/integration/repo_webhook_test.go create mode 100644 tests/integration/repofiles_change_test.go create mode 100644 tests/integration/session_test.go create mode 100644 tests/integration/setting_test.go create mode 100644 tests/integration/signin_test.go create mode 100644 tests/integration/signout_test.go create mode 100644 tests/integration/signup_test.go create mode 100644 tests/integration/ssh_key_test.go create mode 100644 tests/integration/timetracking_test.go create mode 100644 tests/integration/user_avatar_test.go create mode 100644 tests/integration/user_settings_oauth2_grant_test.go create mode 100644 tests/integration/user_settings_test.go create mode 100644 tests/integration/user_test.go create mode 100644 tests/integration/version_test.go create mode 100644 tests/integration/view_test.go create mode 100644 tests/integration/webauthn_test.go create mode 100644 tests/integration/wiki_test.go create mode 100644 tests/integration/workflow_run_api_check_test.go create mode 100644 tests/mssql.ini.tmpl create mode 100644 tests/mysql.ini.tmpl create mode 100644 tests/pgsql.ini.tmpl create mode 100644 tests/sqlite.ini.tmpl create mode 100644 tests/test_utils.go create mode 100644 tests/testdata/data/artifacts/26/1/1712166500347189545.chunk create mode 100644 tests/testdata/data/artifacts/26/19/1712348022422036662.chunk create mode 100644 tests/testdata/data/artifacts/26/20/1712348022423431524.chunk create mode 100644 tests/testdata/data/artifacts/27/5/1730330775594233150.chunk create mode 100644 tests/testdata/data/attachments/a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22 create mode 100755 tools/build-release.sh create mode 100644 tools/ci-tools.ts create mode 100644 tools/eslint-rules/unescaped-html-literal.test.ts create mode 100644 tools/eslint-rules/unescaped-html-literal.ts create mode 100755 tools/generate-codemirror-languages.ts create mode 100755 tools/generate-images.ts create mode 100644 tools/generate-svg-vscode-extensions.json create mode 100755 tools/generate-svg.ts create mode 100644 tools/gocovmerge.go create mode 100644 tools/lint-go-all.go create mode 100755 tools/lint-shell.sh create mode 100755 tools/lint-templates-svg.ts create mode 100755 tools/playwright.sh create mode 100644 tools/shared.ts create mode 100755 tools/test-e2e.sh create mode 100644 tools/test-echo.go create mode 100755 tools/test-integration.sh create mode 100644 tools/watch.sh create mode 100644 tsconfig.json create mode 100644 types.d.ts create mode 100644 uv.lock create mode 100644 vite.config.ts create mode 100644 vitest.config.ts create mode 100644 web_src/css/actions.css create mode 100644 web_src/css/admin.css create mode 100644 web_src/css/avatar.css create mode 100644 web_src/css/base.css create mode 100644 web_src/css/dashboard.css create mode 100644 web_src/css/devtest.css create mode 100644 web_src/css/easymde.css create mode 100644 web_src/css/editor/combomarkdowneditor.css create mode 100644 web_src/css/explore.css create mode 100644 web_src/css/features/captcha.css create mode 100644 web_src/css/features/colorpicker.css create mode 100644 web_src/css/features/console.css create mode 100644 web_src/css/features/cropper.css create mode 100644 web_src/css/features/dropzone.css create mode 100644 web_src/css/features/expander.css create mode 100644 web_src/css/features/gitgraph.css create mode 100644 web_src/css/features/heatmap.css create mode 100644 web_src/css/features/imagediff.css create mode 100644 web_src/css/features/projects.css create mode 100644 web_src/css/font_i18n.css create mode 100644 web_src/css/helpers.css create mode 100644 web_src/css/home.css create mode 100644 web_src/css/index.css create mode 100644 web_src/css/install.css create mode 100644 web_src/css/markup/codeblock.css create mode 100644 web_src/css/markup/codepreview.css create mode 100644 web_src/css/markup/content.css create mode 100644 web_src/css/markup/jupyter.css create mode 100644 web_src/css/modules/animations.css create mode 100644 web_src/css/modules/breadcrumb.css create mode 100644 web_src/css/modules/button.css create mode 100644 web_src/css/modules/card.css create mode 100644 web_src/css/modules/charescape.css create mode 100644 web_src/css/modules/checkbox.css create mode 100644 web_src/css/modules/chroma.css create mode 100644 web_src/css/modules/codeeditor.css create mode 100644 web_src/css/modules/comment.css create mode 100644 web_src/css/modules/container.css create mode 100644 web_src/css/modules/dimmer.css create mode 100644 web_src/css/modules/divider.css create mode 100644 web_src/css/modules/dropdown.css create mode 100644 web_src/css/modules/flexcontainer.css create mode 100644 web_src/css/modules/form.css create mode 100644 web_src/css/modules/grid.css create mode 100644 web_src/css/modules/header.css create mode 100644 web_src/css/modules/input.css create mode 100644 web_src/css/modules/label.css create mode 100644 web_src/css/modules/list.css create mode 100644 web_src/css/modules/menu.css create mode 100644 web_src/css/modules/message.css create mode 100644 web_src/css/modules/modal.css create mode 100644 web_src/css/modules/navbar.css create mode 100644 web_src/css/modules/normalize.css create mode 100644 web_src/css/modules/search.css create mode 100644 web_src/css/modules/segment.css create mode 100644 web_src/css/modules/shortcut.css create mode 100644 web_src/css/modules/svg.css create mode 100644 web_src/css/modules/tab.css create mode 100644 web_src/css/modules/table.css create mode 100644 web_src/css/modules/tippy.css create mode 100644 web_src/css/modules/toast.css create mode 100644 web_src/css/modules/transition.css create mode 100644 web_src/css/org.css create mode 100644 web_src/css/repo.css create mode 100644 web_src/css/repo/clone.css create mode 100644 web_src/css/repo/commit-sign.css create mode 100644 web_src/css/repo/file-view.css create mode 100644 web_src/css/repo/home-file-list.css create mode 100644 web_src/css/repo/home.css create mode 100644 web_src/css/repo/issue-card.css create mode 100644 web_src/css/repo/issue-label.css create mode 100644 web_src/css/repo/issue-list.css create mode 100644 web_src/css/repo/list-header.css create mode 100644 web_src/css/repo/packages.css create mode 100644 web_src/css/repo/reactions.css create mode 100644 web_src/css/repo/release-tag.css create mode 100644 web_src/css/repo/wiki.css create mode 100644 web_src/css/review.css create mode 100644 web_src/css/shared/flex-list.css create mode 100644 web_src/css/shared/settings.css create mode 100644 web_src/css/swagger-render.css create mode 100644 web_src/css/swagger-standalone.css create mode 100644 web_src/css/themes/theme-gitea-auto-protanopia-deuteranopia.css create mode 100644 web_src/css/themes/theme-gitea-auto-tritanopia.css create mode 100644 web_src/css/themes/theme-gitea-auto.css create mode 100644 web_src/css/themes/theme-gitea-dark-protanopia-deuteranopia.css create mode 100644 web_src/css/themes/theme-gitea-dark-tritanopia.css create mode 100644 web_src/css/themes/theme-gitea-dark.css create mode 100644 web_src/css/themes/theme-gitea-light-protanopia-deuteranopia.css create mode 100644 web_src/css/themes/theme-gitea-light-tritanopia.css create mode 100644 web_src/css/themes/theme-gitea-light.css create mode 100644 web_src/css/user.css create mode 100644 web_src/fomantic/_site/globals/site.variables create mode 100644 web_src/fomantic/build/components/api.js create mode 100644 web_src/fomantic/build/components/modal.js create mode 100644 web_src/fomantic/build/fomantic.js create mode 100644 web_src/fomantic/semantic.json create mode 100644 web_src/fomantic/theme.config.less create mode 100644 web_src/js/bootstrap.ts create mode 100644 web_src/js/components/ActionRunArtifacts.test.ts create mode 100644 web_src/js/components/ActionRunArtifacts.ts create mode 100644 web_src/js/components/ActionRunJobView.vue create mode 100644 web_src/js/components/ActionRunSummaryView.vue create mode 100644 web_src/js/components/ActionRunView.test.ts create mode 100644 web_src/js/components/ActionRunView.ts create mode 100644 web_src/js/components/ActionStatusIcon.vue create mode 100644 web_src/js/components/ActivityHeatmap.vue create mode 100644 web_src/js/components/ChartCanvas.vue create mode 100644 web_src/js/components/ContextPopup.vue create mode 100644 web_src/js/components/DashboardRepoList.vue create mode 100644 web_src/js/components/DiffCommitSelector.vue create mode 100644 web_src/js/components/DiffFileExtensionFilter.vue create mode 100644 web_src/js/components/DiffFileTree.vue create mode 100644 web_src/js/components/DiffFileTreeItem.test.ts create mode 100644 web_src/js/components/DiffFileTreeItem.vue create mode 100644 web_src/js/components/PullRequestMergeForm.vue create mode 100644 web_src/js/components/RepoActionView.vue create mode 100644 web_src/js/components/RepoActivityTopAuthors.vue create mode 100644 web_src/js/components/RepoBranchTagSelector.vue create mode 100644 web_src/js/components/RepoCodeFrequency.vue create mode 100644 web_src/js/components/RepoContributors.vue create mode 100644 web_src/js/components/RepoFileSearch.vue create mode 100644 web_src/js/components/RepoRecentCommits.vue create mode 100644 web_src/js/components/SvgIcon.test.ts create mode 100644 web_src/js/components/SvgIcon.vue create mode 100644 web_src/js/components/ViewFileTree.vue create mode 100644 web_src/js/components/ViewFileTreeItem.vue create mode 100644 web_src/js/components/ViewFileTreeStore.ts create mode 100644 web_src/js/components/WorkflowGraph.utils.test.ts create mode 100644 web_src/js/components/WorkflowGraph.utils.ts create mode 100644 web_src/js/components/WorkflowGraph.vue create mode 100644 web_src/js/external-render-frontend.ts create mode 100644 web_src/js/external-render-helper.test.ts create mode 100644 web_src/js/external-render-helper.ts create mode 100644 web_src/js/features/admin/common.ts create mode 100644 web_src/js/features/admin/config.test.ts create mode 100644 web_src/js/features/admin/config.ts create mode 100644 web_src/js/features/admin/selfcheck.ts create mode 100644 web_src/js/features/admin/users.ts create mode 100644 web_src/js/features/captcha.ts create mode 100644 web_src/js/features/citation.ts create mode 100644 web_src/js/features/code-frequency.ts create mode 100644 web_src/js/features/colorpicker.ts create mode 100644 web_src/js/features/common-actions-permissions.ts create mode 100644 web_src/js/features/common-button.test.ts create mode 100644 web_src/js/features/common-button.ts create mode 100644 web_src/js/features/common-form.ts create mode 100644 web_src/js/features/common-issue-list.test.ts create mode 100644 web_src/js/features/common-issue-list.ts create mode 100644 web_src/js/features/common-organization.ts create mode 100644 web_src/js/features/common-page.ts create mode 100644 web_src/js/features/comp/ComboMarkdownEditor.ts create mode 100644 web_src/js/features/comp/ConfirmModal.ts create mode 100644 web_src/js/features/comp/Cropper.ts create mode 100644 web_src/js/features/comp/EasyMDEToolbarActions.ts create mode 100644 web_src/js/features/comp/EditorMarkdown.test.ts create mode 100644 web_src/js/features/comp/EditorMarkdown.ts create mode 100644 web_src/js/features/comp/EditorUpload.test.ts create mode 100644 web_src/js/features/comp/EditorUpload.ts create mode 100644 web_src/js/features/comp/LabelEdit.ts create mode 100644 web_src/js/features/comp/QuickSubmit.ts create mode 100644 web_src/js/features/comp/ReactionSelector.ts create mode 100644 web_src/js/features/comp/ScopedWorkflows.test.ts create mode 100644 web_src/js/features/comp/ScopedWorkflows.ts create mode 100644 web_src/js/features/comp/SearchRepoBox.ts create mode 100644 web_src/js/features/comp/SearchUserBox.ts create mode 100644 web_src/js/features/comp/TextExpander.ts create mode 100644 web_src/js/features/comp/WebHookEditor.ts create mode 100644 web_src/js/features/contributors.ts create mode 100644 web_src/js/features/copycontent.ts create mode 100644 web_src/js/features/dashboard.ts create mode 100644 web_src/js/features/dropzone.ts create mode 100644 web_src/js/features/emoji.ts create mode 100644 web_src/js/features/file-fold.ts create mode 100644 web_src/js/features/file-view.ts create mode 100644 web_src/js/features/heatmap.ts create mode 100644 web_src/js/features/imagediff.ts create mode 100644 web_src/js/features/install.ts create mode 100644 web_src/js/features/issue.ts create mode 100644 web_src/js/features/notification.ts create mode 100644 web_src/js/features/oauth2-settings.ts create mode 100644 web_src/js/features/org-team.ts create mode 100644 web_src/js/features/packages.test.ts create mode 100644 web_src/js/features/packages.ts create mode 100644 web_src/js/features/pull-view-file.ts create mode 100644 web_src/js/features/recent-commits.ts create mode 100644 web_src/js/features/ref-issue.ts create mode 100644 web_src/js/features/repo-actions.test.ts create mode 100644 web_src/js/features/repo-actions.ts create mode 100644 web_src/js/features/repo-branch.ts create mode 100644 web_src/js/features/repo-code.ts create mode 100644 web_src/js/features/repo-commit.ts create mode 100644 web_src/js/features/repo-common.test.ts create mode 100644 web_src/js/features/repo-common.ts create mode 100644 web_src/js/features/repo-diff-commit.ts create mode 100644 web_src/js/features/repo-diff-commitselect.ts create mode 100644 web_src/js/features/repo-diff-filetree.ts create mode 100644 web_src/js/features/repo-diff.ts create mode 100644 web_src/js/features/repo-editor.ts create mode 100644 web_src/js/features/repo-findfile.test.ts create mode 100644 web_src/js/features/repo-findfile.ts create mode 100644 web_src/js/features/repo-graph.ts create mode 100644 web_src/js/features/repo-home.ts create mode 100644 web_src/js/features/repo-issue-content.ts create mode 100644 web_src/js/features/repo-issue-edit.ts create mode 100644 web_src/js/features/repo-issue-list.ts create mode 100644 web_src/js/features/repo-issue-pull.ts create mode 100644 web_src/js/features/repo-issue-sidebar-combolist.test.ts create mode 100644 web_src/js/features/repo-issue-sidebar-combolist.ts create mode 100644 web_src/js/features/repo-issue-sidebar.md create mode 100644 web_src/js/features/repo-issue-sidebar.ts create mode 100644 web_src/js/features/repo-issue.ts create mode 100644 web_src/js/features/repo-legacy.ts create mode 100644 web_src/js/features/repo-migrate.ts create mode 100644 web_src/js/features/repo-migration.ts create mode 100644 web_src/js/features/repo-milestone.ts create mode 100644 web_src/js/features/repo-new.ts create mode 100644 web_src/js/features/repo-projects.ts create mode 100644 web_src/js/features/repo-release.test.ts create mode 100644 web_src/js/features/repo-release.ts create mode 100644 web_src/js/features/repo-search.ts create mode 100644 web_src/js/features/repo-settings-branches.test.ts create mode 100644 web_src/js/features/repo-settings-branches.ts create mode 100644 web_src/js/features/repo-settings.ts create mode 100644 web_src/js/features/repo-switcher.ts create mode 100644 web_src/js/features/repo-unicode-escape.ts create mode 100644 web_src/js/features/repo-view-file-tree.ts create mode 100644 web_src/js/features/repo-watch.ts create mode 100644 web_src/js/features/repo-wiki.ts create mode 100644 web_src/js/features/sshkey-helper.ts create mode 100644 web_src/js/features/stopwatch.ts create mode 100644 web_src/js/features/tablesort.ts create mode 100644 web_src/js/features/tribute.ts create mode 100644 web_src/js/features/user-auth-webauthn.ts create mode 100644 web_src/js/features/user-auth.ts create mode 100644 web_src/js/features/user-settings.ts create mode 100644 web_src/js/globals.d.ts create mode 100644 web_src/js/globals.ts create mode 100644 web_src/js/iife.ts create mode 100644 web_src/js/index.ts create mode 100644 web_src/js/markup/anchors.ts create mode 100644 web_src/js/markup/codecopy.ts create mode 100644 web_src/js/markup/common.ts create mode 100644 web_src/js/markup/content.ts create mode 100644 web_src/js/markup/html2markdown.test.ts create mode 100644 web_src/js/markup/html2markdown.ts create mode 100644 web_src/js/markup/math.ts create mode 100644 web_src/js/markup/mermaid.test.ts create mode 100644 web_src/js/markup/mermaid.ts create mode 100644 web_src/js/markup/render-iframe.test.ts create mode 100644 web_src/js/markup/render-iframe.ts create mode 100644 web_src/js/markup/tasklist.test.ts create mode 100644 web_src/js/markup/tasklist.ts create mode 100644 web_src/js/modules/action-status-icon.test.ts create mode 100644 web_src/js/modules/action-status-icon.ts create mode 100644 web_src/js/modules/are-you-sure.test.ts create mode 100644 web_src/js/modules/are-you-sure.ts create mode 100644 web_src/js/modules/clipboard.ts create mode 100644 web_src/js/modules/codeeditor/command-palette.ts create mode 100644 web_src/js/modules/codeeditor/context-menu.ts create mode 100644 web_src/js/modules/codeeditor/linter.ts create mode 100644 web_src/js/modules/codeeditor/main.test.ts create mode 100644 web_src/js/modules/codeeditor/main.ts create mode 100644 web_src/js/modules/codeeditor/utils.test.ts create mode 100644 web_src/js/modules/codeeditor/utils.ts create mode 100644 web_src/js/modules/devtest.ts create mode 100644 web_src/js/modules/diff-file.test.ts create mode 100644 web_src/js/modules/diff-file.ts create mode 100644 web_src/js/modules/errors.test.ts create mode 100644 web_src/js/modules/errors.ts create mode 100644 web_src/js/modules/favicon-status.test.ts create mode 100644 web_src/js/modules/favicon-status.ts create mode 100644 web_src/js/modules/fetch-action.test.ts create mode 100644 web_src/js/modules/fetch-action.ts create mode 100644 web_src/js/modules/fetch.test.ts create mode 100644 web_src/js/modules/fetch.ts create mode 100644 web_src/js/modules/fomantic.ts create mode 100644 web_src/js/modules/fomantic/aria.md create mode 100644 web_src/js/modules/fomantic/base.ts create mode 100644 web_src/js/modules/fomantic/dimmer.ts create mode 100644 web_src/js/modules/fomantic/dropdown.test.ts create mode 100644 web_src/js/modules/fomantic/dropdown.ts create mode 100644 web_src/js/modules/fomantic/modal.ts create mode 100644 web_src/js/modules/fomantic/tab.ts create mode 100644 web_src/js/modules/fomantic/transition.ts create mode 100644 web_src/js/modules/gitea-actions.ts create mode 100644 web_src/js/modules/i18n.test.ts create mode 100644 web_src/js/modules/i18n.ts create mode 100644 web_src/js/modules/init.ts create mode 100644 web_src/js/modules/observer.ts create mode 100644 web_src/js/modules/search.ts create mode 100644 web_src/js/modules/shortcut.ts create mode 100644 web_src/js/modules/sortable.ts create mode 100644 web_src/js/modules/tippy.test.ts create mode 100644 web_src/js/modules/tippy.ts create mode 100644 web_src/js/modules/toast.test.ts create mode 100644 web_src/js/modules/toast.ts create mode 100644 web_src/js/modules/user-settings.ts create mode 100644 web_src/js/modules/worker.test.ts create mode 100644 web_src/js/modules/worker.ts create mode 100644 web_src/js/render/ansi.test.ts create mode 100644 web_src/js/render/ansi.ts create mode 100644 web_src/js/render/plugin.ts create mode 100644 web_src/js/render/plugins/frontend-asciicast.ts create mode 100644 web_src/js/render/plugins/frontend-openapi-swagger.ts create mode 100644 web_src/js/render/plugins/frontend-viewer-3d.ts create mode 100644 web_src/js/render/plugins/inplace-pdf-viewer.ts create mode 100644 web_src/js/render/swagger.ts create mode 100644 web_src/js/svg.test.ts create mode 100644 web_src/js/svg.ts create mode 100644 web_src/js/swagger.ts create mode 100644 web_src/js/types.ts create mode 100644 web_src/js/user-events.sharedworker.ts create mode 100644 web_src/js/utils.test.ts create mode 100644 web_src/js/utils.ts create mode 100644 web_src/js/utils/color.test.ts create mode 100644 web_src/js/utils/color.ts create mode 100644 web_src/js/utils/dom.test.ts create mode 100644 web_src/js/utils/dom.ts create mode 100644 web_src/js/utils/func.test.ts create mode 100644 web_src/js/utils/func.ts create mode 100644 web_src/js/utils/glob.test.ts create mode 100644 web_src/js/utils/glob.test.txt create mode 100644 web_src/js/utils/glob.ts create mode 100644 web_src/js/utils/html.test.ts create mode 100644 web_src/js/utils/html.ts create mode 100644 web_src/js/utils/image.test.ts create mode 100644 web_src/js/utils/image.ts create mode 100644 web_src/js/utils/match.test.ts create mode 100644 web_src/js/utils/match.ts create mode 100644 web_src/js/utils/string.test.ts create mode 100644 web_src/js/utils/string.ts create mode 100644 web_src/js/utils/testhelper.ts create mode 100644 web_src/js/utils/time.test.ts create mode 100644 web_src/js/utils/time.ts create mode 100644 web_src/js/utils/url.test.ts create mode 100644 web_src/js/utils/url.ts create mode 100644 web_src/js/vitest.setup.ts create mode 100644 web_src/js/webcomponents/README.md create mode 100644 web_src/js/webcomponents/index.ts create mode 100644 web_src/js/webcomponents/overflow-menu.ts create mode 100644 web_src/js/webcomponents/polyfill.test.ts create mode 100644 web_src/js/webcomponents/polyfills.ts create mode 100644 web_src/js/webcomponents/relative-time.test.ts create mode 100644 web_src/js/webcomponents/relative-time.ts create mode 100644 web_src/svg/fontawesome-openid.svg create mode 100644 web_src/svg/fontawesome-save.svg create mode 100644 web_src/svg/fontawesome-send.svg create mode 100644 web_src/svg/fontawesome-windows.svg create mode 100644 web_src/svg/gitea-alpine.svg create mode 100644 web_src/svg/gitea-arch.svg create mode 100644 web_src/svg/gitea-azuread.svg create mode 100644 web_src/svg/gitea-azureadv2.svg create mode 100644 web_src/svg/gitea-bitbucket.svg create mode 100644 web_src/svg/gitea-cargo.svg create mode 100644 web_src/svg/gitea-chef.svg create mode 100644 web_src/svg/gitea-codebase.svg create mode 100644 web_src/svg/gitea-codecommit.svg create mode 100644 web_src/svg/gitea-colorblind-blueyellow.svg create mode 100644 web_src/svg/gitea-colorblind-redgreen.svg create mode 100644 web_src/svg/gitea-composer.svg create mode 100644 web_src/svg/gitea-conan.svg create mode 100644 web_src/svg/gitea-conda.svg create mode 100644 web_src/svg/gitea-cran.svg create mode 100644 web_src/svg/gitea-debian.svg create mode 100644 web_src/svg/gitea-discord.svg create mode 100644 web_src/svg/gitea-double-chevron-left.svg create mode 100644 web_src/svg/gitea-double-chevron-right.svg create mode 100644 web_src/svg/gitea-dropbox.svg create mode 100644 web_src/svg/gitea-eclipse.svg create mode 100644 web_src/svg/gitea-empty-checkbox.svg create mode 100644 web_src/svg/gitea-exclamation.svg create mode 100644 web_src/svg/gitea-facebook.svg create mode 100644 web_src/svg/gitea-feishu.svg create mode 100644 web_src/svg/gitea-git.svg create mode 100644 web_src/svg/gitea-gitbucket.svg create mode 100644 web_src/svg/gitea-gitlab.svg create mode 100644 web_src/svg/gitea-go.svg create mode 100644 web_src/svg/gitea-gogs.svg create mode 100644 web_src/svg/gitea-google.svg create mode 100644 web_src/svg/gitea-helm.svg create mode 100644 web_src/svg/gitea-jetbrains.svg create mode 100644 web_src/svg/gitea-join.svg create mode 100644 web_src/svg/gitea-lock-cog.svg create mode 100644 web_src/svg/gitea-lock.svg create mode 100644 web_src/svg/gitea-mastodon.svg create mode 100644 web_src/svg/gitea-matrix.svg create mode 100644 web_src/svg/gitea-maven.svg create mode 100644 web_src/svg/gitea-microsoftonline.svg create mode 100644 web_src/svg/gitea-nextcloud.svg create mode 100644 web_src/svg/gitea-npm.svg create mode 100644 web_src/svg/gitea-nuget.svg create mode 100644 web_src/svg/gitea-onedev.svg create mode 100644 web_src/svg/gitea-openid.svg create mode 100644 web_src/svg/gitea-pub.svg create mode 100644 web_src/svg/gitea-python.svg create mode 100644 web_src/svg/gitea-rpm.svg create mode 100644 web_src/svg/gitea-rubygems.svg create mode 100644 web_src/svg/gitea-running.svg create mode 100644 web_src/svg/gitea-split.svg create mode 100644 web_src/svg/gitea-swift.svg create mode 100644 web_src/svg/gitea-terraform.svg create mode 100644 web_src/svg/gitea-twitter.svg create mode 100644 web_src/svg/gitea-unlock.svg create mode 100644 web_src/svg/gitea-vagrant.svg create mode 100644 web_src/svg/gitea-vscodium.svg create mode 100644 web_src/svg/gitea-whitespace.svg create mode 100644 web_src/svg/gitea-yandex.svg create mode 100644 web_src/svg/material-folder-generic.svg create mode 100644 web_src/svg/material-folder-symlink.svg create mode 100644 web_src/svg/material-invert-colors.svg create mode 100644 web_src/svg/material-palette.svg diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..c7fdf45 --- /dev/null +++ b/.air.toml @@ -0,0 +1,26 @@ +root = "." +tmp_dir = ".air" + +[build] +pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs +cmd = "make --no-print-directory backend" +entrypoint = ["./gitea"] +delay = 2000 +include_ext = ["go", "tmpl"] +include_file = ["main.go"] +include_dir = ["cmd", "models", "modules", "options", "routers", "services"] +exclude_dir = [ + "models/fixtures", + "modelmigration/fixtures", + "modules/avatar/identicon/testdata", + "modules/avatar/testdata", + "modules/git/tests", + "modules/migration/file_format_testdata", + "routers/private/tests", + "services/gitdiff/testdata", +] +exclude_regex = ["_test.go$", "_gen.go$"] +stop_on_error = true + +[log] +main_only = true diff --git a/.changelog.yml b/.changelog.yml new file mode 100644 index 0000000..7486765 --- /dev/null +++ b/.changelog.yml @@ -0,0 +1,59 @@ +# The full repository name +repo: go-gitea/gitea + +# Service type (gitea or github) +service: github + +# Base URL for Gitea instance if using gitea service type (optional) +# Default: https://gitea.com +base-url: + +# Changelog groups and which labeled PRs to add to each group +groups: + - + name: BREAKING + labels: + - pr/breaking + - + name: SECURITY + labels: + - topic/security + - + name: FEATURES + labels: + - type/feature + - + name: ENHANCEMENTS + labels: + - type/enhancement + - + name: PERFORMANCE + labels: + - performance/memory + - performance/speed + - performance/bigrepo + - performance/cpu + - + name: BUGFIXES + labels: + - type/bug + + - + name: TESTING + labels: + - type/testing + - + name: BUILD + labels: + - topic/build + - topic/code-linting + - + name: DOCS + labels: + - type/docs + - + name: MISC + default: true + +# regex indicating which labels to skip for the changelog +skip-labels: skip-changelog|backport\/.+ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..4863391 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,45 @@ +{ + "name": "Gitea DevContainer", + "image": "mcr.microsoft.com/devcontainers/go:1.26-trixie", + "containerEnv": { + // override "local" from packaged version + "GOTOOLCHAIN": "auto" + }, + "features": { + // installs nodejs into container + "ghcr.io/devcontainers/features/node:1": { + "version": "latest" + }, + "ghcr.io/devcontainers/features/git-lfs:1.2.5": {}, + "ghcr.io/jsburckhardt/devcontainer-features/uv:1": {}, + "ghcr.io/devcontainers/features/python:1": { + "version": "3.14" + }, + "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} + }, + "customizations": { + "vscode": { + "settings": {}, + "extensions": [ + "editorconfig.editorconfig", + "dbaeumer.vscode-eslint", + "golang.go", + "stylelint.vscode-stylelint", + "DavidAnson.vscode-markdownlint", + "Vue.volar", + "ms-azuretools.vscode-docker", + "vitest.explorer", + "cweijan.vscode-database-client2", + "GitHub.vscode-pull-request-github", + "Azurite.azurite" + ] + } + }, + "portsAttributes": { + "3000": { + "label": "Gitea Web", + "onAutoForward": "notify" + } + }, + "postCreateCommand": "make deps" +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f61d8ae --- /dev/null +++ b/.dockerignore @@ -0,0 +1,90 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# IntelliJ +.idea +# Goland's output filename can not be set manually +/go_build_* + +# MS VSCode +.vscode +__debug_bin* + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +*coverage.out +coverage.all +cpu.out + +*.db +*.log + +/gitea +/debug + +/bin +/dist +/custom/* +!/custom/conf +/custom/conf/* +!/custom/conf/app.example.ini +/data +/indexers +/log +/tests/integration/gitea-integration-* +/tests/*.ini +/node_modules +/yarn.lock +/yarn-error.log +/npm-debug.log* +/pnpm-debug.log* +/public/assets/js +/public/assets/css +/public/assets/fonts +/public/assets/img/avatar +/vendor +/VERSION +/.air +/.go-licenses +/Dockerfile +/Dockerfile.rootless +/.venv + +# Files and folders that were previously generated +/public/assets/img/webpack + +# Snapcraft +snap/.snapcraft/ +parts/ +stage/ +prime/ +*.snap +*.snap-build +*_source.tar.bz2 +.DS_Store + +# Make evidence files +/.make_evidence + +# Manpage +/man diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..85b9cb9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,33 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +tab_width = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{go,tmpl,html}] +indent_style = tab + +[go.*] +indent_style = tab + +[templates/custom/*.tmpl] +insert_final_newline = false + +[templates/swagger/*.generated.json] +indent_style = space +insert_final_newline = false + +[templates/shared/actions/runner_badge_*.tmpl] +# editconfig lint requires these XML-like files to have charset defined, but the files don't have. +charset = unset + +[Makefile] +indent_style = tab + +[*.svg] +insert_final_newline = false diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f1795e4 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +* text=auto eol=lf +*.tmpl linguist-language=Handlebars +*.pb.go linguist-generated +/assets/*.json linguist-generated +/public/assets/img/svg/*.svg linguist-generated +/templates/swagger/*.generated.json linguist-generated +/options/fileicon/** linguist-generated +/vendor/** -text -eol linguist-vendored +Dockerfile.* linguist-language=Dockerfile +Makefile.* linguist-language=Makefile diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..1447a6e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: gitea diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml new file mode 100644 index 0000000..e793e06 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -0,0 +1,28 @@ +name: Bug Report +description: Something isn't working as expected. +labels: ["type/bug"] +body: + - type: markdown + attributes: + value: | + - **Security issue?** Email security@gitea.io instead of opening a public issue. + - **Need help** with setup or configuration? Ask on [Discord](https://discord.gg/Gitea) or the [forum](https://forum.gitea.com). + - Search [existing issues](https://github.com/go-gitea/gitea/issues?q=is%3Aissue) first. + - type: input + id: gitea-ver + attributes: + label: Gitea Version + validations: + required: true + - type: textarea + id: description + attributes: + label: What happened? + description: What you did, what you expected to happen, and what happened instead. Include logs if relevant. + validations: + required: true + - type: textarea + id: environment + attributes: + label: How are you running Gitea? + description: Install method (binary, Docker, package), operating system, and database. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..d409f18 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,17 @@ +blank_issues_enabled: false +contact_links: + - name: Security Concern + url: https://tinyurl.com/security-gitea + about: For security concerns, please send a mail to security@gitea.io instead of opening a public issue. + - name: Discord Server + url: https://discord.gg/Gitea + about: Please ask questions and discuss configuration or deployment problems here. + - name: Discourse Forum + url: https://forum.gitea.com + about: Questions and configuration or deployment problems can also be discussed on our forum. + - name: Frequently Asked Questions + url: https://docs.gitea.com/help/faq + about: Please check if your question isn't mentioned here. + - name: Crowdin Translations + url: https://translate.gitea.com + about: Translations are managed here. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml new file mode 100644 index 0000000..3ad0fb9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -0,0 +1,20 @@ +name: Feature Request +description: Suggest an idea for Gitea. +labels: ["type/proposal"] +body: + - type: markdown + attributes: + value: | + Search [existing issues](https://github.com/go-gitea/gitea/issues?q=is%3Aissue) first. + - type: textarea + id: problem + attributes: + label: What problem would this solve? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: What do you propose? + validations: + required: true diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 0000000..2adea23 --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,7 @@ +self-hosted-runner: + labels: + - actuated-4cpu-8gb + - actuated-4cpu-16gb + - nscloud + - namespace-profile-gitea-release-docker + - namespace-profile-gitea-release-binary diff --git a/.github/actions/docker-dryrun/action.yml b/.github/actions/docker-dryrun/action.yml new file mode 100644 index 0000000..08056d9 --- /dev/null +++ b/.github/actions/docker-dryrun/action.yml @@ -0,0 +1,31 @@ +name: docker-dryrun +description: Composite action that performs the container build steps for a single platform. + +inputs: + platform: + description: "The target platform: linux/amd64, linux/arm64, linux/riscv64." + required: true + +runs: + using: composite + steps: + - uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0 + with: + cache-image: false + - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + - name: Build regular image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + platforms: ${{ inputs.platform }} + push: false + file: Dockerfile + cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful + - name: Build rootless image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + platforms: ${{ inputs.platform }} + push: false + file: Dockerfile.rootless + cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless diff --git a/.github/actions/free-disk-space/action.yml b/.github/actions/free-disk-space/action.yml new file mode 100644 index 0000000..c3b7e45 --- /dev/null +++ b/.github/actions/free-disk-space/action.yml @@ -0,0 +1,22 @@ +name: free-disk-space +description: Free space on / before large cache restores + +# Delete preinstalled toolchains which gitea doesn't use and show disk space usage +runs: + using: composite + steps: + - shell: bash + run: | + avail=$(df --output=avail -BG / | tr -dc '0-9') + if [ "$avail" -ge 50 ]; then + echo "skipping cleanup, ${avail}G free" + exit 0 + fi + echo "free space before cleanup:" + df -h / + for dir in /usr/local/lib/android /usr/local/.ghcup /opt/ghc /usr/share/dotnet; do + sudo rm -rf "$dir" & + done + wait + echo "free space after cleanup:" + df -h / diff --git a/.github/actions/go-cache/action.yml b/.github/actions/go-cache/action.yml new file mode 100644 index 0000000..347cd0a --- /dev/null +++ b/.github/actions/go-cache/action.yml @@ -0,0 +1,56 @@ +name: go-caches +description: Restore the go module, build, and golangci-lint caches. Save only on the cache-seeder workflow. + +# Only the cache-seeder workflow saves; rename requires updating cache-seeder.yml. +# The lint job restores but does not save the gobuild and gomod caches, so every key +# has a single writer and there is no contention on it. +# Seeder restores by exact key only (no restore-keys) so each go.sum seeds a clean +# cache and size stays bounded; do not add restore-keys here. PR runs keep them. +# The go version is part of the gobuild and golint keys because a toolchain bump +# invalidates every entry in them, so a cross-version fallback would restore +# gigabytes that no build can use. The module cache is version-independent. + +inputs: + go-version: + description: Go version the build caches belong to + required: true + lint-cache: + description: Restore (and save in cache-seeder) ~/.cache/golangci-lint + default: "false" + +runs: + using: composite + steps: + - if: ${{ github.workflow == 'cache-seeder' && inputs.lint-cache != 'true' }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/go/pkg/mod + key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }} + - if: ${{ github.workflow != 'cache-seeder' || inputs.lint-cache == 'true' }} + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/go/pkg/mod + key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }} + restore-keys: gomod-${{ runner.os }}-${{ runner.arch }}- + - if: ${{ github.workflow == 'cache-seeder' && inputs.lint-cache != 'true' }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/go-build + key: gobuild-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum') }} + - if: ${{ github.workflow != 'cache-seeder' || inputs.lint-cache == 'true' }} + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/go-build + key: gobuild-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum') }} + restore-keys: gobuild-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}- + - if: ${{ inputs.lint-cache == 'true' && github.workflow == 'cache-seeder' }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/golangci-lint + key: golint-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum', '.golangci.yml') }} + - if: ${{ inputs.lint-cache == 'true' && github.workflow != 'cache-seeder' }} + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/golangci-lint + key: golint-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum', '.golangci.yml') }} + restore-keys: golint-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}- diff --git a/.github/actions/go-setup/action.yml b/.github/actions/go-setup/action.yml new file mode 100644 index 0000000..108f340 --- /dev/null +++ b/.github/actions/go-setup/action.yml @@ -0,0 +1,26 @@ +name: go-setup +description: Set up go and restore caches + +inputs: + cache: + description: Restore go caches + default: "true" + lint-cache: + description: Also restore the golangci-lint cache + default: "false" + +runs: + using: composite + steps: + - uses: ./.github/actions/free-disk-space + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7 + id: setup-go + with: + go-version-file: go.mod + check-latest: true + cache: false + - if: ${{ inputs.cache == 'true' }} + uses: ./.github/actions/go-cache + with: + go-version: ${{ steps.setup-go.outputs.go-version }} + lint-cache: ${{ inputs.lint-cache }} diff --git a/.github/actions/node-setup/action.yml b/.github/actions/node-setup/action.yml new file mode 100644 index 0000000..8f81b93 --- /dev/null +++ b/.github/actions/node-setup/action.yml @@ -0,0 +1,30 @@ +name: node-setup +description: Set up pnpm and node and restore the pnpm store cache + +inputs: + cache: + description: Restore the pnpm store cache + default: "true" + +runs: + using: composite + steps: + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version: 26 + - if: ${{ inputs.cache == 'true' }} + id: store + shell: bash + run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT" + - if: ${{ inputs.cache == 'true' && github.workflow == 'cache-seeder' }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.store.outputs.path }} + key: pnpm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }} + - if: ${{ inputs.cache == 'true' && github.workflow != 'cache-seeder' }} + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.store.outputs.path }} + key: pnpm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: pnpm-${{ runner.os }}-${{ runner.arch }}- diff --git a/.github/actions/pgsql-shard/action.yml b/.github/actions/pgsql-shard/action.yml new file mode 100644 index 0000000..9a5a03a --- /dev/null +++ b/.github/actions/pgsql-shard/action.yml @@ -0,0 +1,40 @@ +name: pgsql-shard +description: Run one pgsql integration test shard + +inputs: + shard: + description: Shard index + required: true + total-shards: + description: Total shard count + required: true + run-migration: + description: Also run migration tests + default: "false" + +runs: + using: composite + steps: + - name: Add hosts to /etc/hosts + shell: bash + run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 pgsql ldap minio" | sudo tee -a /etc/hosts' + - shell: bash + run: make deps-backend + - shell: bash + run: make backend + env: + TAGS: bindata + - name: run migration tests + if: ${{ inputs.run-migration == 'true' }} + shell: bash + run: GITEA_TEST_DATABASE=pgsql make test-migration + - name: run tests + shell: bash + run: GITEA_TEST_DATABASE=pgsql make test-integration + env: + # pgsql is chosen to be the unlucky one to run with the slow "race detector", it is about 60% slower. + GOTEST_FLAGS: -race -timeout=40m + TAGS: bindata gogit + TEST_LDAP: 1 + TEST_SHARD: ${{ inputs.shard }} + TEST_TOTAL_SHARDS: ${{ inputs.total-shards }} diff --git a/.github/actions/python-setup/action.yml b/.github/actions/python-setup/action.yml new file mode 100644 index 0000000..52488fa --- /dev/null +++ b/.github/actions/python-setup/action.yml @@ -0,0 +1,10 @@ +name: python-setup +description: Set up uv and python and restore the uv cache + +runs: + using: composite + steps: + - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + python-version: 3.14 + save-cache: ${{ github.workflow == 'cache-seeder' }} diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..937e69e --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,14 @@ +docs-update-needed: + - changed-files: + - any-glob-to-any-file: + - "custom/conf/app.example.ini" + +topic/code-linting: + - changed-files: + - any-glob-to-any-file: + - ".golangci.yml" + - ".markdownlint.yaml" + - ".spectral.yaml" + - ".yamllint.yaml" + - "eslint*.config.*" + - "stylelint.config.*" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..6ae22da --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,9 @@ + diff --git a/.github/workflows/cache-prune.yml b/.github/workflows/cache-prune.yml new file mode 100644 index 0000000..b0affe3 --- /dev/null +++ b/.github/workflows/cache-prune.yml @@ -0,0 +1,48 @@ +name: cache-prune + +# Keeps the repository's total cache size below a fixed limit, so that GitHub never +# rejects a save for being over the allowance. + +on: + schedule: + - cron: "37 */6 * * *" # every six hours at :37 + workflow_dispatch: + workflow_call: + +permissions: {} + +concurrency: + group: cache-prune + +jobs: + prune: + runs-on: ubuntu-latest + if: github.repository == 'go-gitea/gitea' + permissions: + actions: write # to delete caches + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + steps: + # Keep the newest generation per key, restores never reach the older ones. + - name: delete superseded caches + run: | + gh cache list --limit 1000 --json id,key,ref,createdAt | + jq -r 'group_by([.ref, (.key | sub("(-go[0-9.]+)?-[0-9a-f]{40,64}(-[0-9]+-[0-9]+)?$"; ""))])[] + | sort_by(.createdAt)[:-1][] | "\(.id) \(.key)"' | + while read -r id key; do + echo "deleting $key" + gh cache delete "$id" || true + done + + # Deletes least recently used first, the order GitHub itself evicts in. + - name: delete caches over the size limit + run: | + caches=$(gh cache list --limit 1000 --sort last_accessed_at --order asc --json id,key,sizeInBytes) + size=$(jq '[.[].sizeInBytes] | add // 0' <<< "$caches") + echo "cache usage: $((size / 1000000)) MB" + while [ "$size" -gt 6000000000 ] && read -r id bytes key; do + echo "deleting $key" + gh cache delete "$id" || true + size=$((size - bytes)) + done <<< "$(jq -r '.[] | "\(.id) \(.sizeInBytes) \(.key)"' <<< "$caches")" diff --git a/.github/workflows/cache-seeder.yml b/.github/workflows/cache-seeder.yml new file mode 100644 index 0000000..4a597b6 --- /dev/null +++ b/.github/workflows/cache-seeder.yml @@ -0,0 +1,97 @@ +# Populates main's cache scope so PR runs warm-start from it. Caches are ref-scoped: +# PR runs read their own scope then fall back to the base branch, and only this +# workflow saves, so push-to-main is the only chance to populate the fallback scope. + +name: cache-seeder + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - "go.mod" # a toolchain bump invalidates the build caches + - "go.sum" + - ".golangci.yml" + - "pnpm-lock.yaml" + - "pyproject.toml" + - "uv.lock" + - ".github/actions/go-cache/action.yml" + - ".github/actions/go-setup/action.yml" + - ".github/actions/node-setup/action.yml" + - ".github/actions/python-setup/action.yml" + - ".github/workflows/cache-seeder.yml" + +concurrency: + group: cache-seeder + cancel-in-progress: true + +permissions: + contents: read + +jobs: + gobuild: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - run: make deps-backend deps-tools + - run: TAGS="bindata" make backend + - run: TAGS="bindata gogit" make backend + - name: warm test compile cache (bindata) + env: + TAGS: bindata + GOTEST_FLAGS: -race -list=^$$ -count=1 + run: make test-backend + - name: warm test compile cache (bindata gogit) + env: + TAGS: bindata gogit + GOTEST_FLAGS: -race -list=^$$ -count=1 + run: make test-backend + - name: warm integration compile cache + run: | + TAGS="bindata" make test-integration-compile + TAGS="bindata gogit" make test-integration-compile + TAGS="bindata gogit" GOTEST_FLAGS="-race" make test-integration-compile + + lint: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - { tags: "bindata", target: "lint-backend" } + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + with: + lint-cache: "true" + - run: make deps-backend deps-tools + - run: make generate-go + env: + TAGS: ${{ matrix.tags }} + - run: make "$TARGET" + env: + TAGS: ${{ matrix.tags }} + TARGET: ${{ matrix.target }} + + frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/node-setup + - run: make deps-frontend + + python: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/python-setup + - run: make deps-py + + # reclaims the caches this run superseded, so the next save still fits in the allowance + prune: + needs: [gobuild, lint, frontend, python] + permissions: + actions: write + uses: ./.github/workflows/cache-prune.yml diff --git a/.github/workflows/cron-licenses.yml b/.github/workflows/cron-licenses.yml new file mode 100644 index 0000000..4022778 --- /dev/null +++ b/.github/workflows/cron-licenses.yml @@ -0,0 +1,34 @@ +name: cron-licenses + +on: + # schedule: + # - cron: "7 0 * * 1" # every Monday at 00:07 UTC + workflow_dispatch: + +permissions: {} + +jobs: + cron-licenses: + runs-on: ubuntu-latest + if: github.repository == 'go-gitea/gitea' + permissions: + contents: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7 + with: + go-version-file: go.mod + check-latest: true + cache: false + - run: make generate-gitignore + timeout-minutes: 40 + - name: push translations to repo + uses: appleboy/git-push-action@3b2c8661652360dbf1afe1b319a49dbb739c39f1 # v1.2.0 + with: + author_email: "teabot@gitea.io" + author_name: GiteaBot + branch: main + commit: true + commit_message: "[skip ci] Updated licenses and gitignores" + remote: "git@github.com:go-gitea/gitea.git" + ssh_key: ${{ secrets.DEPLOY_KEY }} diff --git a/.github/workflows/cron-renovate.yml b/.github/workflows/cron-renovate.yml new file mode 100644 index 0000000..1da0612 --- /dev/null +++ b/.github/workflows/cron-renovate.yml @@ -0,0 +1,32 @@ +name: cron-renovate + +on: + schedule: + - cron: "23 * * * *" # hourly at :23 + workflow_dispatch: + +concurrency: + group: cron-renovate + +env: + RENOVATE_VERSION: 44.71.1 # renovate: datasource=npm depName=renovate + +permissions: + contents: read + +jobs: + cron-renovate: + runs-on: ubuntu-latest + if: github.repository == 'go-gitea/gitea' # prevent running on forks + timeout-minutes: 30 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: renovatebot/github-action@37beffda261423addd537c33f2d126df7f6ffbab # v46.2.6 + with: + renovate-version: ${{ env.RENOVATE_VERSION }} + configurationFile: renovate.json5 + token: ${{ secrets.RENOVATE_TOKEN }} + env: + RENOVATE_BINARY_SOURCE: install # auto-install go/node toolchains needed by post-upgrade tasks. + RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^make (tidy|svg|generate-codemirror-languages)$"]' + RENOVATE_REPOSITORIES: '["go-gitea/gitea"]' diff --git a/.github/workflows/cron-translations.yml b/.github/workflows/cron-translations.yml new file mode 100644 index 0000000..098e974 --- /dev/null +++ b/.github/workflows/cron-translations.yml @@ -0,0 +1,42 @@ +name: cron-translations + +on: + schedule: + - cron: "7 0 * * *" # every day at 00:07 UTC + workflow_dispatch: + +permissions: {} + +jobs: + crowdin-pull: + runs-on: ubuntu-latest + if: github.repository == 'go-gitea/gitea' + permissions: + contents: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: crowdin/github-action@0d5670f539973aea2f01abce61a8989934df0025 # v3.0.2 + with: + upload_sources: true + upload_translations: false + download_sources: false + download_translations: true + push_translations: false + push_sources: false + create_pull_request: false + config: crowdin.yml + env: + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }} + - name: update locales + run: ./build/update-locales.sh + - name: push translations to repo + uses: appleboy/git-push-action@3b2c8661652360dbf1afe1b319a49dbb739c39f1 # v1.2.0 + with: + author_email: "teabot@gitea.io" + author_name: GiteaBot + branch: main + commit: true + commit_message: "[skip ci] Updated translations via Crowdin" + remote: "git@github.com:go-gitea/gitea.git" + ssh_key: ${{ secrets.DEPLOY_KEY }} diff --git a/.github/workflows/files-changed.yml b/.github/workflows/files-changed.yml new file mode 100644 index 0000000..697ab16 --- /dev/null +++ b/.github/workflows/files-changed.yml @@ -0,0 +1,143 @@ +name: files-changed + +on: + workflow_call: + outputs: + backend: + value: ${{ jobs.detect.outputs.backend }} + frontend: + value: ${{ jobs.detect.outputs.frontend }} + docs: + value: ${{ jobs.detect.outputs.docs }} + actions: + value: ${{ jobs.detect.outputs.actions }} + templates: + value: ${{ jobs.detect.outputs.templates }} + docker: + value: ${{ jobs.detect.outputs.docker }} + dockerfile: + value: ${{ jobs.detect.outputs.dockerfile }} + swagger: + value: ${{ jobs.detect.outputs.swagger }} + yaml: + value: ${{ jobs.detect.outputs.yaml }} + json: + value: ${{ jobs.detect.outputs.json }} + e2e: + value: ${{ jobs.detect.outputs.e2e }} + shell: + value: ${{ jobs.detect.outputs.shell }} + +permissions: + contents: read + +jobs: + detect: + runs-on: ubuntu-latest + timeout-minutes: 3 + outputs: + backend: ${{ steps.changes.outputs.backend }} + frontend: ${{ steps.changes.outputs.frontend }} + docs: ${{ steps.changes.outputs.docs }} + actions: ${{ steps.changes.outputs.actions }} + templates: ${{ steps.changes.outputs.templates }} + docker: ${{ steps.changes.outputs.docker }} + dockerfile: ${{ steps.changes.outputs.dockerfile }} + swagger: ${{ steps.changes.outputs.swagger }} + yaml: ${{ steps.changes.outputs.yaml }} + json: ${{ steps.changes.outputs.json }} + e2e: ${{ steps.changes.outputs.e2e }} + shell: ${{ steps.changes.outputs.shell }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 + id: changes + with: + filters: | + backend: + - "**/*.go" + - "templates/**/*.tmpl" + - "assets/emoji.json" + - "go.mod" + - "go.sum" + - "Makefile" + - ".golangci.yml" + - ".editorconfig" + - "options/locale/locale_en-US.json" + - "models/fixtures/**" + - "tests/*.ini.tmpl" + - "tests/gitea-repositories-meta/**" + - "tests/testdata/**" + - "tools/test-integration.sh" + + frontend: + - "*.ts" + - "web_src/**" + - "tools/**/*.ts" + - "tools/**/*.json" + - "tools/playwright.sh" + - "tsconfig.json" + - "assets/emoji.json" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + - "Makefile" + + docs: + - "**/*.md" + - ".markdownlint.yaml" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + + actions: + - ".github/workflows/*" + - ".github/actions/**" + - "Makefile" + + templates: + - "tools/lint-templates-*.ts" + - "templates/**/*.tmpl" + - "pyproject.toml" + - "uv.lock" + + docker: + - ".github/workflows/pull-docker-dryrun.yml" + - ".github/actions/docker-dryrun/**" + - "Dockerfile" + - "Dockerfile.rootless" + - "docker/**" + - "Makefile" + + dockerfile: + - "Dockerfile" + - "Dockerfile.rootless" + + swagger: + - "templates/swagger/*.json" + - "Makefile" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + - ".spectral.yaml" + + yaml: + - "**/*.yml" + - "**/*.yaml" + - ".yamllint.yaml" + - "pyproject.toml" + + json: + - "**/*.json" + - "**/*.json5" + - "eslint.json.config.ts" + + e2e: + - "tests/e2e/**" + - "tools/test-e2e.sh" + - "tools/playwright.sh" + - "playwright.config.ts" + + shell: + - "**/*.sh" + - ".shellcheckrc" diff --git a/.github/workflows/giteabot-backport.yml b/.github/workflows/giteabot-backport.yml new file mode 100644 index 0000000..7e05729 --- /dev/null +++ b/.github/workflows/giteabot-backport.yml @@ -0,0 +1,26 @@ +name: giteabot backport + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +jobs: + giteabot: + if: github.repository == 'go-gitea/gitea' + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: go-gitea/giteabot@c214e4be0efc926a2d6ce93b7a3911fe7fa65ab1 # v1.0.6 + with: + github_token: ${{ secrets.GITEABOT_TOKEN }} + gitea_fork: giteabot/gitea + checks: backport diff --git a/.github/workflows/giteabot.yml b/.github/workflows/giteabot.yml new file mode 100644 index 0000000..19da653 --- /dev/null +++ b/.github/workflows/giteabot.yml @@ -0,0 +1,65 @@ +name: giteabot + +on: + # When main advances, rerun merge queue maintenance so the oldest + # reviewed/wait-merge PR can be updated against the new base promptly. + push: + branches: + - main + # pull_request_target gives this workflow access to GITEABOT_TOKEN on PRs from + # forks, which the bot needs to write labels, statuses and comments. Safe here + # because the job only runs a pinned action and never checks out PR HEAD. + # These PR lifecycle events drive label maintenance, queue maintenance, and + # explicit bot actions triggered by relevant label changes. + pull_request_target: # zizmor: ignore[dangerous-triggers] + types: + - opened + - synchronize + - labeled + - unlabeled + - closed + - review_requested + - review_request_removed + # Review events keep review-derived state such as lgtm labels and status checks + # in sync after approvals, edits, or dismissals. + pull_request_review: + types: + - submitted + - edited + - dismissed + # Periodic maintenance is still useful as a backstop for queue cleanup and + # other housekeeping, even though main pushes now trigger it promptly. + schedule: + - cron: "15 3 * * *" + # Allow maintainers to rerun selected checks manually when debugging bot + # behavior without waiting for another repository event. + workflow_dispatch: + inputs: + checks: + description: Comma-separated list of non-backport checks to run + required: false + default: labels,merge_queue,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions + +permissions: {} + +concurrency: + group: ${{ format('{0}-{1}', github.workflow, (github.event_name == 'pull_request_target' || github.event_name == 'pull_request_review') && format('pr-{0}', github.event.pull_request.number) || 'maintenance') }} + cancel-in-progress: false + +jobs: + giteabot: + if: github.repository == 'go-gitea/gitea' + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + issues: write + pull-requests: write + statuses: write + steps: + # pull_request_review runs without repository secrets on fork PRs, so fall + # back to the workflow token for the non-backport checks handled here. + - uses: go-gitea/giteabot@c214e4be0efc926a2d6ce93b7a3911fe7fa65ab1 # v1.0.6 + with: + github_token: ${{ secrets.GITEABOT_TOKEN || github.token }} + checks: ${{ github.event.inputs.checks || 'labels,merge_queue,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions' }} diff --git a/.github/workflows/pull-compliance.yml b/.github/workflows/pull-compliance.yml new file mode 100644 index 0000000..be133fb --- /dev/null +++ b/.github/workflows/pull-compliance.yml @@ -0,0 +1,109 @@ +name: compliance + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + files-changed: + uses: ./.github/workflows/files-changed.yml + + lint-backend: + if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' + needs: files-changed + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + with: + lint-cache: "true" + - run: make deps-backend deps-tools + - run: TAGS="bindata" make generate-go lint-backend # lint-go can lint with "bindata" tags + + lint-on-demand: + needs: files-changed + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + with: + cache: "false" + - uses: ./.github/actions/node-setup + with: + cache: "false" + + - run: make lint-spell + + - if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true' || needs.files-changed.outputs.actions == 'true' + uses: ./.github/actions/python-setup + - if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true' + run: make deps-py lint-templates lint-yaml + + - if: needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.swagger == 'true' || needs.files-changed.outputs.json == 'true' + run: make deps-frontend lint-md lint-swagger lint-json + + - if: needs.files-changed.outputs.actions == 'true' + run: make lint-actions + + - if: needs.files-changed.outputs.shell == 'true' + run: make lint-shell + + checks-backend: + if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' + needs: files-changed + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - run: make deps-backend deps-tools + - run: make --always-make checks-backend # ensure the "go-licenses" make target runs + + frontend: + if: needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true' + needs: files-changed + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/node-setup + - run: make deps-frontend + - run: make lint-frontend + - run: make checks-frontend + - run: make playwright + - run: make test-frontend + - run: make frontend + + backend: + if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' + needs: files-changed + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - run: make deps-backend generate-go + # no frontend build here as backend should be able to build, even without any frontend files + # CGO is not used when cross-compile, so these steps also test if the code is compatible with CGO disabled + - name: build-backend-arm64 + run: go build -ldflags '-s -w' -o gitea-linux-arm64 + env: + GOOS: linux + GOARCH: arm64 + TAGS: bindata gogit + - name: build-backend-windows + run: go build -ldflags '-s -w' -o gitea-windows + env: + GOOS: windows + GOARCH: amd64 + TAGS: bindata gogit + - name: build-backend-386 + run: go build -ldflags '-s -w' -o gitea-linux-386 + env: + GOOS: linux + GOARCH: 386 + - name: binary-sizes + run: ls -l gitea-* diff --git a/.github/workflows/pull-db-tests.yml b/.github/workflows/pull-db-tests.yml new file mode 100644 index 0000000..ff1b9fa --- /dev/null +++ b/.github/workflows/pull-db-tests.yml @@ -0,0 +1,254 @@ +name: db-tests + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + files-changed: + uses: ./.github/workflows/files-changed.yml + + test-pgsql-shard-1: + if: needs.files-changed.outputs.backend == 'true' + needs: files-changed + runs-on: ubuntu-latest + timeout-minutes: 50 + services: + pgsql: + image: postgres:14@sha256:156f0b253fd61366d5fc2107ad45955027d5612f695a8436ce20167f3fa79bff + env: + POSTGRES_DB: test + POSTGRES_PASSWORD: postgres + ports: + - "5432:5432" + ldap: + image: gitea/test-openldap:latest@sha256:4ac633b01d684e6b2a458cc0c8530c92f9b3702f6e040ce5f365607df34fbda0 + ports: + - "389:389" + - "636:636" + minio: + # as github actions doesn't support "entrypoint", we need to use a non-official image + # that has a custom entrypoint set to "minio server /data" + image: bitnamilegacy/minio:2025.7.23@sha256:8935e75fa5d11295c17171e4aa49efe390a1193cd7f12e4d21b92af9ffef09d7 + env: + MINIO_ROOT_USER: 123456 + MINIO_ROOT_PASSWORD: 12345678 + ports: + - "9000:9000" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - uses: ./.github/actions/pgsql-shard + with: + shard: 1 + total-shards: 2 + run-migration: "true" + + test-pgsql-shard-2: + if: needs.files-changed.outputs.backend == 'true' + needs: files-changed + runs-on: ubuntu-latest + timeout-minutes: 50 + services: + pgsql: + image: postgres:14@sha256:156f0b253fd61366d5fc2107ad45955027d5612f695a8436ce20167f3fa79bff + env: + POSTGRES_DB: test + POSTGRES_PASSWORD: postgres + ports: + - "5432:5432" + ldap: + image: gitea/test-openldap:latest@sha256:4ac633b01d684e6b2a458cc0c8530c92f9b3702f6e040ce5f365607df34fbda0 + ports: + - "389:389" + - "636:636" + minio: + # as github actions doesn't support "entrypoint", we need to use a non-official image + # that has a custom entrypoint set to "minio server /data" + image: bitnamilegacy/minio:2025.7.23@sha256:8935e75fa5d11295c17171e4aa49efe390a1193cd7f12e4d21b92af9ffef09d7 + env: + MINIO_ROOT_USER: 123456 + MINIO_ROOT_PASSWORD: 12345678 + ports: + - "9000:9000" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - uses: ./.github/actions/pgsql-shard + with: + shard: 2 + total-shards: 2 + + test-sqlite: + if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' + needs: files-changed + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - run: make deps-backend + - run: make backend + env: + TAGS: bindata gogit + - run: GITEA_TEST_DATABASE=sqlite make test-migration + env: + TAGS: bindata gogit + - name: run tests + run: GITEA_TEST_DATABASE=sqlite make test-integration + timeout-minutes: 50 + env: + # sqlite driver can contain large amount of Golang code, so don't use race detector for it, otherwise, extremely slow + GOTEST_FLAGS: -timeout=40m + TAGS: bindata gogit + + test-unit: + if: needs.files-changed.outputs.backend == 'true' + needs: files-changed + runs-on: ubuntu-latest + services: + elasticsearch: + image: elasticsearch:8.19.15@sha256:8ff844933190136cec601777d633453f4008cc126a652257d5f63cd8e92207ef + env: + discovery.type: single-node + xpack.security.enabled: false + xpack.ml.enabled: false + ingest.geoip.downloader.enabled: false + ES_JAVA_OPTS: "-Xms1g -Xmx1g" + ports: + - "9200:9200" + meilisearch: + image: getmeili/meilisearch:v1@sha256:c94e58ca09662dd6e65e8f1b0fd145767be3da7d5422a863a27b8d2b68e090c9 + env: + MEILI_ENV: development # disable auth + ports: + - "7700:7700" + redis: + image: redis:8@sha256:298e5b3bc566bade82f46ad5511777a4a07a294097ce16ada2f6a42be5239df5 + options: >- # wait until redis has started + --health-cmd "redis-cli ping" + --health-interval 5s + --health-timeout 3s + --health-retries 10 + ports: + - 6379:6379 + minio: + image: bitnamilegacy/minio:2025.7.23@sha256:8935e75fa5d11295c17171e4aa49efe390a1193cd7f12e4d21b92af9ffef09d7 + env: + MINIO_ROOT_USER: 123456 + MINIO_ROOT_PASSWORD: 12345678 + ports: + - "9000:9000" + devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583 + image: mcr.microsoft.com/azure-storage/azurite:latest@sha256:f9791d0a0613e93057c06ae31f7ac0c0222f0530590e67e020b0986c66a36fa6 + ports: + - 10000:10000 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - name: Add hosts to /etc/hosts + run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 minio devstoreaccount1.azurite.local mysql elasticsearch meilisearch smtpimap" | sudo tee -a /etc/hosts' + - run: make deps-backend + - run: make generate-go + env: + TAGS: bindata + - name: unit-tests + run: make test-backend + env: + GOTEST_FLAGS: -race -timeout=20m + TAGS: bindata + GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }} + - name: unit-tests-gogit + run: make test-backend + env: + GOTEST_FLAGS: -race -timeout=20m + TAGS: bindata gogit + GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }} + GITEA_TEST_CI_SKIP_EXTERNAL: true + - run: make test-check + + test-mysql: + if: needs.files-changed.outputs.backend == 'true' + needs: files-changed + runs-on: ubuntu-latest + services: + mysql: + # the bitnami mysql image has more options than the official one, it's easier to customize + image: bitnamilegacy/mysql:8.4@sha256:7089d796fc9b4629a628bd445e4afabe607351ee665444c3197bdeaed812ea65 + env: + ALLOW_EMPTY_PASSWORD: true + MYSQL_DATABASE: testgitea + ports: + - "3306:3306" + options: >- + --mount type=tmpfs,destination=/bitnami/mysql/data + elasticsearch: + image: elasticsearch:8.19.15@sha256:8ff844933190136cec601777d633453f4008cc126a652257d5f63cd8e92207ef + env: + discovery.type: single-node + xpack.security.enabled: false + xpack.ml.enabled: false + ingest.geoip.downloader.enabled: false + ES_JAVA_OPTS: "-Xms1g -Xmx1g" + ports: + - "9200:9200" + smtpimap: + image: tabascoterrier/docker-imap-devel:latest@sha256:3fb7cf50b47693e7b80f6f74abea2def4d7386016931d61359864de8a0aba551 + ports: + - "25:25" + - "143:143" + - "587:587" + - "993:993" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - name: Add hosts to /etc/hosts + run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql elasticsearch smtpimap" | sudo tee -a /etc/hosts' + - run: make deps-backend + - run: make backend + env: + TAGS: bindata + - run: GITEA_TEST_DATABASE=mysql make test-migration + - name: run tests + run: GITEA_TEST_DATABASE=mysql make test-integration + env: + TAGS: bindata + TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200" + + test-mssql: + if: needs.files-changed.outputs.backend == 'true' + needs: files-changed + runs-on: ubuntu-latest + services: + mssql: + image: mcr.microsoft.com/mssql/server:2019-latest@sha256:ef0b8db33970ecd01bed49c3a84a1d083c435a9891718df619298b67b352e74a + env: + ACCEPT_EULA: Y + MSSQL_PID: Standard + SA_PASSWORD: MwantsaSecurePassword1 + ports: + - "1433:1433" + devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583 + image: mcr.microsoft.com/azure-storage/azurite:latest@sha256:f9791d0a0613e93057c06ae31f7ac0c0222f0530590e67e020b0986c66a36fa6 + ports: + - 10000:10000 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - name: Add hosts to /etc/hosts + run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mssql devstoreaccount1.azurite.local" | sudo tee -a /etc/hosts' + - run: make deps-backend + - run: make backend + env: + TAGS: bindata + - run: GITEA_TEST_DATABASE=mssql make test-migration + - name: run tests + run: GITEA_TEST_DATABASE=mssql make test-integration + timeout-minutes: 50 + env: + TAGS: bindata diff --git a/.github/workflows/pull-docker-dryrun.yml b/.github/workflows/pull-docker-dryrun.yml new file mode 100644 index 0000000..ce29a15 --- /dev/null +++ b/.github/workflows/pull-docker-dryrun.yml @@ -0,0 +1,47 @@ +name: docker-dryrun + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + files-changed: + uses: ./.github/workflows/files-changed.yml + + # QEMU-based build is slow (40-50 minutes), so run arm64 and riscv64 when dockerfile changes. + # Run amd64 when any docker-related files change, which is fast (4 minutes). + container-amd64: + if: needs.files-changed.outputs.docker == 'true' + needs: [files-changed] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/docker-dryrun + with: + platform: linux/amd64 + + container-arm64: + if: needs.files-changed.outputs.dockerfile == 'true' + needs: [files-changed] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/docker-dryrun + with: + platform: linux/arm64 + + container-riscv64: + if: needs.files-changed.outputs.dockerfile == 'true' + needs: [files-changed] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/docker-dryrun + with: + platform: linux/riscv64 diff --git a/.github/workflows/pull-e2e-tests.yml b/.github/workflows/pull-e2e-tests.yml new file mode 100644 index 0000000..0468b9b --- /dev/null +++ b/.github/workflows/pull-e2e-tests.yml @@ -0,0 +1,37 @@ +name: e2e-tests + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + files-changed: + uses: ./.github/workflows/files-changed.yml + + test-e2e: + if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.e2e == 'true' + needs: files-changed + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: ./.github/actions/go-setup + - uses: ./.github/actions/node-setup + - run: make deps-frontend + - run: make frontend + - run: make deps-backend + - run: make backend + env: + TAGS: bindata + - run: make playwright + - run: make test-e2e + timeout-minutes: 10 + env: + TAGS: bindata + FORCE_COLOR: 1 + GITEA_TEST_E2E_DEBUG: 1 diff --git a/.github/workflows/pull-labeler.yml b/.github/workflows/pull-labeler.yml new file mode 100644 index 0000000..aa4cdff --- /dev/null +++ b/.github/workflows/pull-labeler.yml @@ -0,0 +1,50 @@ +name: labeler + +on: + # pull_request_target is required to label PRs from forks; jobs only use pinned + # actions or base-branch checkout, never PR-head code. + pull_request_target: # zizmor: ignore[dangerous-triggers] + types: [opened, synchronize, reopened, edited, ready_for_review] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: {} + +jobs: + labeler: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7 + with: + sync-labels: true + + pr-title: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + pull-requests: write + steps: + # Base-branch checkout only: pull_request_target runs with elevated token; never run PR-head code here. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ github.event.pull_request.base.sha }} + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version: 26 + # Labels are only synced after the title lints, so an invalid title never reaches the label diff. + - run: node ./tools/ci-tools.ts lint-pr-title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + - run: node ./tools/ci-tools.ts set-pr-labels + env: + PR_TITLE: ${{ github.event.pull_request.title }} + PR_TITLE_BEFORE: ${{ github.event.changes.title.from }} + PR_NUMBER: ${{ github.event.pull_request.number }} + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/release-nightly-snapcraft.yml b/.github/workflows/release-nightly-snapcraft.yml new file mode 100644 index 0000000..eaee386 --- /dev/null +++ b/.github/workflows/release-nightly-snapcraft.yml @@ -0,0 +1,48 @@ +name: release-nightly-snapcraft + +on: + push: + branches: + - main + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build-and-publish: + strategy: + fail-fast: false + matrix: + runner: [ubuntu-24.04, ubuntu-24.04-arm] + runs-on: ${{ matrix.runner }} + + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + - name: Build snap + id: build + env: + SNAPCRAFT_IMAGE_INFO: '{"build_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' + run: | + # snapcraft installs build-snaps and apt packages system-wide, which needs root + sudo snap install --classic snapcraft + sudo SNAPCRAFT_BUILD_INFO=1 SNAPCRAFT_IMAGE_INFO="$SNAPCRAFT_IMAGE_INFO" snapcraft pack --destructive-mode + echo "snap=$(ls ./*.snap)" >> "$GITHUB_OUTPUT" + # retry snapcraft uploads which can be flaky + - name: Publish snap + run: | + for attempt in 1 2 3 4 5; do + snapcraft upload "$SNAP" --release latest/edge && exit 0 + echo "::warning::snap upload attempt $attempt failed, retrying in 15s" + sleep 15 + done + exit 1 + env: + SNAP: ${{ steps.build.outputs.snap }} + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml new file mode 100644 index 0000000..aefe7bf --- /dev/null +++ b/.github/workflows/release-nightly.yml @@ -0,0 +1,144 @@ +name: release-nightly + +on: + push: + branches: [main, release/v*] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + nightly-binary: + runs-on: namespace-profile-gitea-release-binary + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + # fetch all commits instead of only the last as some branches are long lived and could have many between versions + # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 + - run: git fetch --unshallow --quiet --tags --force + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7 + with: + go-version-file: go.mod + check-latest: true + cache: false + - uses: ./.github/actions/node-setup + - run: make deps-frontend deps-backend + - run: make release + - name: Install Cosign + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 + - name: import gpg key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0 + with: + gpg_private_key: ${{ secrets.GPGSIGN_KEY }} + passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }} + - name: sign binaries + env: + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + GPG_PASSPHRASE: ${{ secrets.GPGSIGN_PASSPHRASE }} + run: | + for f in dist/release/*; do + cosign sign-blob "$f" --bundle "$f.sigstore.json" --yes + echo "$GPG_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u "$GPG_FINGERPRINT" --output "$f.asc" "$f" + done + # clean branch name to get the folder name in the object storage + - name: Get cleaned branch name + id: clean_name + env: + REF: ${{ github.ref }} + run: | + REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//') + echo "Cleaned name is ${REF_NAME}" + echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT" + - name: upload binaries to cloudflare r2 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: auto + CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }} + CLOUDFLARE_R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_BUCKET }} + BRANCH: ${{ steps.clean_name.outputs.branch }} + run: | + aws s3 sync dist/release "s3://$CLOUDFLARE_R2_BUCKET/gitea/$BRANCH" --endpoint-url "https://$CLOUDFLARE_R2_ACCOUNT_ID.r2.cloudflarestorage.com" --no-progress + + nightly-container: + runs-on: namespace-profile-gitea-release-docker + permissions: + contents: read + packages: write # to publish to ghcr.io + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + # fetch all commits instead of only the last as some branches are long lived and could have many between versions + # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 + - run: git fetch --unshallow --quiet --tags --force + - uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0 + with: + cache-image: false + - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + - name: Get cleaned branch name + id: clean_name + env: + REF: ${{ github.ref }} + run: | + REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//') + echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT" + - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + id: meta + with: + images: |- + gitea/gitea + ghcr.io/go-gitea/gitea + tags: | + type=raw,value=${{ steps.clean_name.outputs.branch }} + annotations: | + org.opencontainers.image.authors="maintainers@gitea.io" + - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + id: meta_rootless + with: + images: |- + gitea/gitea + ghcr.io/go-gitea/gitea + # each tag below will have the suffix of -rootless + flavor: | + suffix=-rootless + tags: | + type=raw,value=${{ steps.clean_name.outputs.branch }} + annotations: | + org.opencontainers.image.authors="maintainers@gitea.io" + - name: Login to Docker Hub + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Login to GHCR using PAT + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: build regular docker image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + platforms: linux/amd64,linux/arm64,linux/riscv64 + push: true + tags: ${{ steps.meta.outputs.tags }} + annotations: ${{ steps.meta.outputs.annotations }} + cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful + cache-to: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful,mode=max + - name: build rootless docker image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + platforms: linux/amd64,linux/arm64,linux/riscv64 + push: true + file: Dockerfile.rootless + tags: ${{ steps.meta_rootless.outputs.tags }} + annotations: ${{ steps.meta_rootless.outputs.annotations }} + cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless + cache-to: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless,mode=max diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml new file mode 100644 index 0000000..4fcfed0 --- /dev/null +++ b/.github/workflows/release-tag-rc.yml @@ -0,0 +1,149 @@ +name: release-tag-rc + +on: + push: + tags: + - "v1*-rc*" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +permissions: {} + +jobs: + binary: + runs-on: namespace-profile-gitea-release-binary + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + # fetch all commits instead of only the last as some branches are long lived and could have many between versions + # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 + - run: git fetch --unshallow --quiet --tags --force + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7 + with: + go-version-file: go.mod + check-latest: true + cache: false + - uses: ./.github/actions/node-setup + - run: make deps-frontend deps-backend + - run: make release + - name: Install Cosign + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 + - name: import gpg key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0 + with: + gpg_private_key: ${{ secrets.GPGSIGN_KEY }} + passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }} + - name: sign binaries + env: + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + GPG_PASSPHRASE: ${{ secrets.GPGSIGN_PASSPHRASE }} + run: | + for f in dist/release/*; do + cosign sign-blob "$f" --bundle "$f.sigstore.json" --yes + echo "$GPG_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u "$GPG_FINGERPRINT" --output "$f.asc" "$f" + done + # clean branch name to get the folder name in the object storage + - name: Get cleaned branch name + id: clean_name + env: + REF: ${{ github.ref }} + run: | + REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//') + echo "Cleaned name is ${REF_NAME}" + echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT" + - name: upload binaries to cloudflare r2 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: auto + CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }} + CLOUDFLARE_R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_BUCKET }} + BRANCH: ${{ steps.clean_name.outputs.branch }} + run: | + aws s3 sync dist/release "s3://$CLOUDFLARE_R2_BUCKET/gitea/$BRANCH" --endpoint-url "https://$CLOUDFLARE_R2_ACCOUNT_ID.r2.cloudflarestorage.com" --no-progress + - name: Install GH CLI + uses: dev-hanz-ops/install-gh-cli-action@6089bdde54118ad7ca3d22053eb2d69387fd2779 # v0.3.0 + with: + gh-cli-version: 2.39.1 + - name: create github release + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + TAG: ${{ github.ref_name }} + run: | + gh release create "$TAG" --title "$TAG" --draft --notes-from-tag dist/release/* + + container: + runs-on: namespace-profile-gitea-release-docker + permissions: + contents: read + packages: write # to publish to ghcr.io + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + # fetch all commits instead of only the last as some branches are long lived and could have many between versions + # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 + - run: git fetch --unshallow --quiet --tags --force + - uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0 + with: + cache-image: false + - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + id: meta + with: + images: |- + gitea/gitea + ghcr.io/go-gitea/gitea + flavor: | + latest=false + # 1.2.3-rc0 + tags: | + type=semver,pattern={{version}} + annotations: | + org.opencontainers.image.authors="maintainers@gitea.io" + - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + id: meta_rootless + with: + images: |- + gitea/gitea + ghcr.io/go-gitea/gitea + # each tag below will have the suffix of -rootless + flavor: | + latest=false + suffix=-rootless + # 1.2.3-rc0 + tags: | + type=semver,pattern={{version}} + annotations: | + org.opencontainers.image.authors="maintainers@gitea.io" + - name: Login to Docker Hub + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Login to GHCR using PAT + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: build regular container image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + platforms: linux/amd64,linux/arm64,linux/riscv64 + push: true + tags: ${{ steps.meta.outputs.tags }} + annotations: ${{ steps.meta.outputs.annotations }} + - name: build rootless container image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + platforms: linux/amd64,linux/arm64,linux/riscv64 + push: true + file: Dockerfile.rootless + tags: ${{ steps.meta_rootless.outputs.tags }} + annotations: ${{ steps.meta_rootless.outputs.annotations }} diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml new file mode 100644 index 0000000..fe90ee1 --- /dev/null +++ b/.github/workflows/release-tag-version.yml @@ -0,0 +1,161 @@ +name: release-tag-version + +on: + push: + tags: + - "v1.*" + - "!v1*-rc*" + - "!v1*-dev" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +permissions: {} + +jobs: + binary: + runs-on: namespace-profile-gitea-release-binary + permissions: + contents: read + packages: write # to publish to ghcr.io + id-token: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + # fetch all commits instead of only the last as some branches are long lived and could have many between versions + # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 + - run: git fetch --unshallow --quiet --tags --force + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7 + with: + go-version-file: go.mod + check-latest: true + cache: false + - uses: ./.github/actions/node-setup + - run: make deps-frontend deps-backend + - run: make release + - name: Install Cosign + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 + - name: import gpg key + id: import_gpg + uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0 + with: + gpg_private_key: ${{ secrets.GPGSIGN_KEY }} + passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }} + - name: sign binaries + env: + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + GPG_PASSPHRASE: ${{ secrets.GPGSIGN_PASSPHRASE }} + run: | + for f in dist/release/*; do + cosign sign-blob "$f" --bundle "$f.sigstore.json" --yes + echo "$GPG_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u "$GPG_FINGERPRINT" --output "$f.asc" "$f" + done + # clean branch name to get the folder name in the object storage + - name: Get cleaned branch name + id: clean_name + env: + REF: ${{ github.ref }} + run: | + REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//') + echo "Cleaned name is ${REF_NAME}" + echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT" + - name: upload binaries to cloudflare r2 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: auto + CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }} + CLOUDFLARE_R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_BUCKET }} + BRANCH: ${{ steps.clean_name.outputs.branch }} + run: | + aws s3 sync dist/release "s3://$CLOUDFLARE_R2_BUCKET/gitea/$BRANCH" --endpoint-url "https://$CLOUDFLARE_R2_ACCOUNT_ID.r2.cloudflarestorage.com" --no-progress + - name: Install GH CLI + uses: dev-hanz-ops/install-gh-cli-action@6089bdde54118ad7ca3d22053eb2d69387fd2779 # v0.3.0 + with: + gh-cli-version: 2.39.1 + - name: create github release + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + TAG: ${{ github.ref_name }} + run: | + gh release create "$TAG" --title "$TAG" --notes-from-tag dist/release/* + + container: + runs-on: namespace-profile-gitea-release-docker + permissions: + contents: read + packages: write # to publish to ghcr.io + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + # fetch all commits instead of only the last as some branches are long lived and could have many between versions + # fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567 + - run: git fetch --unshallow --quiet --tags --force + - uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0 + with: + cache-image: false + - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + id: meta + with: + images: |- + gitea/gitea + ghcr.io/go-gitea/gitea + # this will generate tags in the following format: + # latest + # 1 + # 1.2 + # 1.2.3 + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}} + type=semver,pattern={{major}}.{{minor}} + annotations: | + org.opencontainers.image.authors="maintainers@gitea.io" + - uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 + id: meta_rootless + with: + images: |- + gitea/gitea + ghcr.io/go-gitea/gitea + # each tag below will have the suffix of -rootless + flavor: | + suffix=-rootless,onlatest=true + # this will generate tags in the following format (with -rootless suffix added): + # latest + # 1 + # 1.2 + # 1.2.3 + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}} + type=semver,pattern={{major}}.{{minor}} + annotations: | + org.opencontainers.image.authors="maintainers@gitea.io" + - name: Login to Docker Hub + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Login to GHCR using PAT + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: build regular container image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + platforms: linux/amd64,linux/arm64,linux/riscv64 + push: true + tags: ${{ steps.meta.outputs.tags }} + annotations: ${{ steps.meta.outputs.annotations }} + - name: build rootless container image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: . + platforms: linux/amd64,linux/arm64,linux/riscv64 + push: true + file: Dockerfile.rootless + tags: ${{ steps.meta_rootless.outputs.tags }} + annotations: ${{ steps.meta_rootless.outputs.annotations }} diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000..7b82636 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,9 @@ +rules: + anonymous-definition: + disable: true + undocumented-permissions: + disable: true + concurrency-limits: + disable: true + self-repository: + disable: true # actionlint rejects "$/" uses, https://github.com/rhysd/actionlint/issues/711 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76a7578 --- /dev/null +++ b/.gitignore @@ -0,0 +1,122 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# IntelliJ +.idea +.run + +# IntelliJ Gateway +.uuid + +# Goland's output filename can not be set manually +/go_build_* +/gitea_* + +# MS VSCode +.vscode +__debug_bin* + +# Visual Studio +/.vs/ + +# mise version managment tool +mise.toml + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +*.tsbuildinfo + +*coverage.out +coverage.all +cpu.out + +/modules/migration/bindata.* +/modules/options/bindata.* +/modules/public/bindata.* +/modules/templates/bindata.* + +*.db +*.log +*.log.*.gz + +/gitea +/debug + +/bin +/dist +/custom/* +!/custom/conf/app.example.ini +/data +/indexers +/log +/public/assets/img/avatar +/tests/e2e-output +/tests/integration/gitea-integration-* +/tests/integration/indexers-* +/tests/*.ini +/tests/**/*.git/**/*.sample +/node_modules +/.venv +/yarn.lock +/yarn-error.log +/npm-debug.log* +/.pnpm-store +/public/assets/.vite +/public/assets/js +/public/assets/css +/public/assets/fonts +/public/assets/licenses.txt +/vendor +/VERSION +/.air + + +# Snapcraft +/gitea_a*.txt +snap/.snapcraft/ +parts/ +stage/ +prime/ +*.snap +*.snap-build +*_source.tar.bz2 +.DS_Store + +# nix-direnv generated files +.direnv/ + +# Make evidence files +/.make_evidence + +# Manpage +/man + +# Ignore AI/LLM instruction files +/.claude/ +/.cursorrules +/.cursor/ +/.goosehints +/.windsurfrules +/.github/copilot-instructions.md +/llms.txt + +# Ignore worktrees when working on multiple branches +.worktrees/ + +# A Makefile for custom make targets +Makefile.local diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..1a674eb --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,201 @@ +version: "2" +output: + sort-order: + - file +linters: + default: none + enable: + - bidichk + - bodyclose + - depguard + - dupl + - errcheck + - forbidigo + - forcetypeassert + - gocheckcompilerdirectives + - gocritic + - govet + - ineffassign + - mirror + - modernize + - nakedret + - nilnil + - nolintlint + - perfsprint + - revive + - staticcheck + - testifylint + - unconvert + - unparam + - unused + - usestdlibvars + - usetesting + - wastedassign + settings: + depguard: + rules: + main: + deny: + - pkg: encoding/json + desc: use gitea's modules/json instead of encoding/json + - pkg: github.com/unknwon/com + desc: use gitea's util and replacements + - pkg: io/ioutil + desc: use os or io instead + - pkg: golang.org/x/exp + desc: it's experimental and unreliable + - pkg: gitea.dev/modules/git/internal + desc: do not use the internal package, use AddXxx function instead + - pkg: gopkg.in/ini.v1 + desc: do not use the ini package, use gitea's config system instead + - pkg: gitea.com/go-chi/cache + desc: do not use the go-chi cache package, use gitea's cache system + - pkg: github.com/pkg/errors + desc: use builtin errors package instead + - pkg: gitea.com/go-chi/binding + desc: use our wrapper + migrations: + files: + - '**/modelmigration/**/*.go' + deny: + - pkg: gitea.dev/models + desc: "migrations must not depend on the models package. HINT: MIGRATION-STRUCT-FROZEN" + - pkg: gitea.dev/modules/structs + desc: "migrations must not depend on modules/structs. HINT: MIGRATION-STRUCT-FROZEN" + forbidigo: + forbid: + - pattern: '^(fmt\.Print(|f|ln)|print|println)$' # default + - pattern: '^exec\.CommandContext$' # use our wrapper for graceful termination + modernize: + disable: + - embedlit + nolintlint: + allow-unused: false + require-explanation: true + require-specific: true + gocritic: + enabled-checks: + - equalFold + disabled-checks: + - ifElseChain + - singleCaseSwitch # Every time this occurred in the code, there was no other way. + - deprecatedComment # conflicts with go-swagger comments + revive: + severity: error + rules: + - name: blank-imports + - name: constant-logical-expr + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: empty-lines + - name: error-return + - name: error-strings + - name: exported + - name: identical-branches + - name: if-return + - name: increment-decrement + - name: modifies-value-receiver + - name: package-comments + - name: package-naming + arguments: + - convention-name-check-regex: '^[a-z][a-z0-9_]*$' # underscores are used by migration packages + skip-default-bad-name-check: true + skip-collision-with-common-std: true + - name: redefines-builtin-id + - name: superfluous-else + - name: time-naming + - name: unexported-return + - name: var-declaration + - name: var-naming + arguments: + - [] # AllowList - do not remove as args for the rule are positional and won't work without lists first + - [] # DenyList + - - skip-initialism-name-checks: true + staticcheck: + checks: + - all + - -ST1003 + - -ST1005 + - -QF1001 + - -QF1006 + - -QF1008 + testifylint: + disable: + - empty + - go-require + - require-error + usetesting: + os-temp-dir: true + perfsprint: + concat-loop: false + govet: + enable: + - nilness + - unusedwrite + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - dupl + - errcheck + - forbidigo + - staticcheck + - unparam + path: _test\.go + - linters: + - dupl + path: modelmigration/v + - linters: + - dupl + text: (?i)webhook + - linters: + - gocritic + text: (?i)`ID' should not be capitalized + - linters: + - unused + text: (?i)swagger + - linters: + - gocritic + text: '(?i)commentFormatting: put a space between `//` and comment text' + - linters: + - gocritic + text: '(?i)exitAfterDefer:' + paths: + - ^node_modules/ + - ^\.venv/ + - ^public/ + - ^web_src/ +issues: + max-issues-per-linter: 0 + max-same-issues: 0 +formatters: + enable: + - gci + - gofumpt + settings: + gci: + custom-order: true + sections: + - standard + - prefix(gitea.dev) + - blank + - default + gofumpt: + extra: + group-params: true + exclusions: + generated: lax + paths: + - ^node_modules/ + - ^\.venv/ + - ^public/ + - ^web_src/ + +run: + timeout: 10m diff --git a/.ignore b/.ignore new file mode 100644 index 0000000..29912ad --- /dev/null +++ b/.ignore @@ -0,0 +1,8 @@ +*.min.css +*.min.js +/assets/*.json +/options/gitignore +/options/license +/public/assets +/vendor +node_modules diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..88ff159 --- /dev/null +++ b/.mailmap @@ -0,0 +1,2 @@ +Unknwon +Unknwon 无闻 diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..b251ff7 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,15 @@ +commands-show-output: false +fenced-code-language: false +first-line-h1: false +heading-increment: false +line-length: {code_blocks: false, tables: false, stern: true, line_length: -1} +no-alt-text: false +no-bare-urls: false +no-emphasis-as-heading: false +no-empty-links: false +no-hard-tabs: {code_blocks: false} +no-inline-html: false +no-space-in-code: false +no-space-in-emphasis: false +no-trailing-spaces: {br_spaces: 0} +single-h1: false diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 0000000..de3da8e --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,2 @@ +# SC2153: false-alert "Possible misspelling: TAGS may not be assigned. Did you mean tags?". We already use strict mode. +disable=SC1091,SC2001,SC2002,SC2016,SC2028,SC2046,SC2124,SC2128,SC2129,SC2153,SC2154,SC2155,SC2164,SC2181,SC2207 diff --git a/.spectral.yaml b/.spectral.yaml new file mode 100644 index 0000000..54e5f1c --- /dev/null +++ b/.spectral.yaml @@ -0,0 +1,7 @@ +extends: [[spectral:oas, all]] + +rules: + info-contact: off + oas2-api-host: off + oas2-parameter-description: off + operation-description: off diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..1d33ece --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,42 @@ +extends: default + +rules: + braces: + min-spaces-inside: 0 + max-spaces-inside: 1 + min-spaces-inside-empty: 0 + max-spaces-inside-empty: 0 + + brackets: + min-spaces-inside: 0 + max-spaces-inside: 1 + min-spaces-inside-empty: 0 + max-spaces-inside-empty: 0 + + comments: + require-starting-space: true + ignore-shebangs: true + min-spaces-from-content: 1 + + comments-indentation: + level: error + + document-start: disable + + document-end: + present: false + + empty-lines: + max: 1 + + indentation: + spaces: 2 + + line-length: disable + + truthy: + allowed-values: ["true", "false", "on", "off"] + +ignore: | + .venv + node_modules diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7a7034a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,20 @@ +- Never assume, verify before claiming +- List development targets with `make help` +- Read relevant developer documentation in the `docs` folder +- PR descriptions: minimal, only what and why, no task or file listings. Include screenshots for UI changes, before and after when modifying existing UI. Aim for less than 1000 characters +- Reference issues and PRs by full URL, not by number +- Use Conventional Commits for commit messages and PR titles, plus Gitea's `enhance` type for user-facing enhancements +- Add an `Assisted-by: AGENT_NAME:MODEL_VERSION` trailer to commit messages, never `Co-Authored-By` or `Signed-off-by` +- Attribute agent authorship on one trailing line in issue and PR comments, never as a PR description section +- Never rewrite git history unless asked, update PRs with new commits and normal push +- Comments: write almost none, short and preferably same-line, explaining why for a future reader. Never narrate code, the change or the prompt. Preserve existing ones that still apply. If you need to write a paragraph-long comment, rethink your implementation, it is likely too complicated +- Add the current year to copyright headers of new `.go` files +- In `options/locale`, only edit `locale_en-US.json`, other locales are synced automatically +- In TS, use `!` instead of `?.`/`??` when a value always exists +- In Go, prefer to use modern language features wherever possible +- Prefer `tw-*` utilities over inline `style` and `flex-*` helpers over per-child `tw-ml-*`/`tw-mr-*` margins, falling back to `tw-*` where specificity requires `!important` +- Run `make fmt` after `.go` edits, `make tidy` after `go.mod` edits, `make generate-swagger` after API changes, and lint what changed with `make lint-go`, `lint-js`, `lint-css` or `lint-templates` +- Fix the cause rather than disabling a linter or weakening a test. Where unavoidable, use the narrowest scope with a trailing comment giving the reason +- Run single tests with `go test -run '^TestName$' ./modulepath/` for Go, `pnpm exec vitest ` for TS and `GITEA_TEST_E2E_FLAGS='' make test-e2e` for e2e +- Write the fewest, fastest tests covering the behavior, extending an existing one where possible. Prefer unit tests where logic is testable in isolation +- Aim for sub-2s per integration test and sub-4s per e2e test. Wait on a deterministic condition rather than `sleep`, and prefer semantic locators in e2e tests diff --git a/BSDmakefile b/BSDmakefile new file mode 100644 index 0000000..79696ea --- /dev/null +++ b/BSDmakefile @@ -0,0 +1,58 @@ +# GNU makefile proxy script for BSD make +# +# Written and maintained by Mahmoud Al-Qudsi +# Copyright NeoSmart Technologies 2014-2019 +# Obtain updates from +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +JARG = +GMAKE = "gmake" +# When gmake is called from another make instance, -w is automatically added +# which causes extraneous messages about directory changes to be emitted. +# Running with --no-print-directory silences these messages. +GARGS = "--no-print-directory" + +.if "$(.MAKE.JOBS)" != "" + JARG = -j$(.MAKE.JOBS) +.endif + +# bmake prefers out-of-source builds and tries to cd into ./obj (among others) +# where possible. GNU Make doesn't, so override that value. +.OBJDIR: ./ + +# The GNU convention is to use the lowercased `prefix` variable/macro to +# specify the installation directory. Humor them. +GPREFIX = +.if defined(PREFIX) && ! defined(prefix) + GPREFIX = 'prefix = "$(PREFIX)"' +.endif + +.BEGIN: .SILENT + which $(GMAKE) || (printf "Error: GNU Make is required!\n\n" 1>&2 && false) + +.PHONY: FRC +$(.TARGETS): FRC + $(GMAKE) $(GPREFIX) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG) + +.DONE .DEFAULT: .SILENT + $(GMAKE) $(GPREFIX) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..6a71263 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,96 @@ +# Gitea Community Code of Conduct + +## About + +Online communities include people from many different backgrounds. The Gitea contributors are committed to providing a friendly, safe and welcoming environment for all, regardless of gender identity and expression, sexual orientation, disabilities, neurodiversity, physical appearance, body size, ethnicity, nationality, race, age, religion, or similar personal characteristics. + +The first goal of the Code of Conduct is to specify a baseline standard of behavior so that people with different social values and communication styles can talk about Gitea effectively, productively, and respectfully. + +The second goal is to provide a mechanism for resolving conflicts in the community when they arise. + +The third goal of the Code of Conduct is to make our community welcoming to people from different backgrounds. Diversity is critical to the project; for Gitea to be successful, it needs contributors and users from all backgrounds. + +We believe that healthy debate and disagreement are essential to a healthy project and community. However, it is never ok to be disrespectful. We value diverse opinions, but we value respectful behavior more. + +## Community values + +These are the values to which people in the Gitea community should aspire. + +- **Be friendly and welcoming.** +- **Be patient.** + - Remember that people have varying communication styles and that not everyone is using their native language. (Meaning and tone can be lost in translation.) +- **Be thoughtful.** + - Productive communication requires effort. Think about how your words will be interpreted. + - Remember that sometimes it is best to refrain entirely from commenting. +- **Be respectful.** + - In particular, respect differences of opinion. +- **Be charitable.** + - Interpret the arguments of others in good faith, do not seek to disagree. + - When we do disagree, try to understand why. +- **Be constructive.** + - Avoid derailing: stay on topic; if you want to talk about something else, start a new conversation. + - Avoid unconstructive criticism: don't merely decry the current state of affairs; offer—or at least solicit—suggestions as to how things may be improved. + - Avoid snarking (pithy, unproductive, sniping comments). + - Avoid discussing potentially offensive or sensitive issues; this all too often leads to unnecessary conflict. + - Avoid microaggressions (brief and commonplace verbal, behavioral and environmental indignities that communicate hostile, derogatory or negative slights and insults to a person or group). +- **Be responsible.** + - What you say and do matters. Take responsibility for your words and actions, including their consequences, whether intended or otherwise. + +People are complicated. You should expect to be misunderstood and to misunderstand others; when this inevitably occurs, resist the urge to be defensive or assign blame. Try not to take offense where no offense was intended. Give people the benefit of the doubt. Even if the intent was to provoke, do not rise to it. It is the responsibility of all parties to de-escalate conflict when it arises. + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others’ private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject: comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, as well as to ban (temporarily or permanently) any contributor for behaviors that they deem inappropriate, threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +This Code of Conduct also applies outside the project spaces when the Project Stewards have a reasonable belief that an individual’s behavior may have a negative impact on the project or its community. + +### Conflict Resolution + +We do not believe that all conflict is bad; healthy debate and disagreement often yield positive results. However, it is never okay to be disrespectful or to engage in behavior that violates the project’s code of conduct. + +If you see someone violating the code of conduct, you are encouraged to address the behavior directly with those involved. Many issues can be resolved quickly and easily, and this gives people more control over the outcome of their dispute. If you are unable to resolve the matter for any reason, or if the behavior is threatening or harassing, report it. We are dedicated to providing an environment where participants feel welcome and safe. + +Reports should be directed to the Gitea Project Stewards at conduct@gitea.com. It is the Project Stewards’ duty to receive and address reported violations of the code of conduct. They will then work with a committee consisting of representatives from the technical-oversight-committee. + +We will investigate every complaint, but you may not receive a direct response. We will use our discretion in determining when and how to follow up on reported incidents, which may range from not taking action to permanent expulsion from the project and project-sponsored spaces. Under normal circumstances, we will notify the accused of the report and provide them an opportunity to discuss it before any action is taken. If there is a consensus between maintainers that such an endeavor would be useless (i.e. in case of an obvious spammer), we reserve the right to take action without notifying the accused first. The identity of the reporter will be omitted from the details of the report supplied to the accused. In potentially harmful situations, such as ongoing harassment or threats to anyone’s safety, we may take action without notice. + +### Attribution + +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +## Summary + +- Treat everyone with respect and kindness. +- Be thoughtful in how you communicate. +- Don’t be destructive or inflammatory. +- If you encounter an issue, please mail conduct@gitea.com. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2f00c0a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,293 @@ +# Contribution Guidelines + +This document explains how to contribute changes to the Gitea project. Topic-specific guides live in separate files so the essentials are easier to find. + +| Topic | Document | +|:-----------------------|:-----------------------------------------------------------------| +| Setup and requirements | [docs/build-setup.md](docs/build-setup.md) | +| Development workflow | [docs/development.md](docs/development.md) | +| Build from source | [docs/build-source.md](docs/build-source.md) | +| Running the tests | [docs/testing.md](docs/testing.md) | +| Frontend guidelines | [docs/guidelines-frontend.md](docs/guidelines-frontend.md) | +| Backend guidelines | [docs/guidelines-backend.md](docs/guidelines-backend.md) | +| Refactoring | [docs/guidelines-refactoring.md](docs/guidelines-refactoring.md) | +| Community Governance | [docs/community-governance.md](docs/community-governance.md) | +| Release management | [docs/release-management.md](docs/release-management.md) | + +
Table of Contents + +- [Contribution Guidelines](#contribution-guidelines) + - [Introduction](#introduction) + - [AI Contribution Policy](#ai-contribution-policy) + - [Issues](#issues) + - [How to report issues](#how-to-report-issues) + - [Types of issues](#types-of-issues) + - [Discuss your design before the implementation](#discuss-your-design-before-the-implementation) + - [Issue locking](#issue-locking) + - [Building Gitea](#building-gitea) + - [Styleguide](#styleguide) + - [Copyright](#copyright) + - [Testing](#testing) + - [Translation](#translation) + - [Code review](#code-review) + - [Pull request format](#pull-request-format) + - [PR title and summary](#pr-title-and-summary) + - [Breaking PRs](#breaking-prs) + - [What is a breaking PR?](#what-is-a-breaking-pr) + - [How to handle breaking PRs?](#how-to-handle-breaking-prs) + - [Maintaining open PRs](#maintaining-open-prs) + - [Reviewing PRs](#reviewing-prs) + - [For PR authors](#for-pr-authors) + - [Documentation](#documentation) + - [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco) + +
+ +## Introduction + +It assumes you have followed the [installation instructions](https://docs.gitea.com/category/installation). \ +Sensitive security-related issues should be reported to [security@gitea.io](mailto:security@gitea.io). + +For configuring IDEs for Gitea development, see the [IDE setup notes](docs/development.md#ide-configuration) and the [contributed configurations](contrib/development/). + +## AI Contribution Policy + +Contributions made with the assistance of AI tools are welcome, but contributors must use them responsibly and disclose that use clearly. + +1. Review AI-generated code closely before marking a pull request ready for review. +2. Manually test the changes and add appropriate automated tests where feasible. +3. Only use AI to assist in contributions that you understand well enough to explain, defend, and revise yourself during review. +4. Disclose AI-assisted content clearly. +5. Do not use AI to reply to questions about your issue or pull request. The questions are for you, not an AI model. +6. AI may be used to help draft issues and pull requests, but contributors remain responsible for the accuracy, completeness, and intent of what they submit. + +Maintainers reserve the right to close pull requests and issues that do not disclose AI assistance, that appear to be low-quality AI-generated content, or where the contributor cannot explain or defend the proposed changes themselves. + +We welcome new contributors, but cannot sustain the effort of supporting contributors who primarily defer to AI rather than engaging substantively with the review process. + +## Issues + +### How to report issues + +Please search the issues on the issue tracker with a variety of related keywords to ensure that your issue has not already been reported. + +If your issue has not been reported yet, [open an issue](https://github.com/go-gitea/gitea/issues/new) +and answer the questions so we can understand and reproduce the problematic behavior. \ +Please write clear and concise instructions so that we can reproduce the behavior — even if it seems obvious. \ +The more detailed and specific you are, the faster we can fix the issue. \ +It is really helpful if you can reproduce your problem on a site running on the latest commits, i.e. , as perhaps your problem has already been fixed on a current version. \ +Please follow the guidelines described in [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) for your report. + +Please be kind—remember that Gitea comes at no cost to you, and you're getting free help. + +### Types of issues + +Typically, issues fall in one of the following categories: + +- `bug`: Something in the frontend or backend behaves unexpectedly +- `security issue`: bug that has serious implications such as leaking another users data. Please do not file such issues on the public tracker and send a mail to security@gitea.io instead +- `feature`: Completely new functionality. You should describe this feature in enough detail that anyone who reads the issue can understand how it is supposed to be implemented +- `enhancement`: An existing feature should get an upgrade +- `refactoring`: Parts of the code base don't conform with other parts and should be changed to improve Gitea's maintainability + +### Discuss your design before the implementation + +We welcome submissions. \ +If you want to change or add something, please let everyone know what you're working on — [file an issue](https://github.com/go-gitea/gitea/issues/new) or comment on an existing one before starting your work! + +Significant changes such as new features must go through the change proposal process before they can be accepted. \ +This is mainly to save yourself the trouble of implementing it, only to find out that your proposed implementation has some potential problems. \ +Furthermore, this process gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits inside +the goals for the project and tools. + +Pull requests should not be the place for architecture discussions. + +### Issue locking + +Commenting on closed or merged issues/PRs is strongly discouraged. +Such comments will likely be overlooked as some maintainers may not view notifications on closed issues, thinking that the item is resolved. +As such, commenting on closed/merged issues/PRs may be disabled prior to the scheduled auto-locking if a discussion starts or if unrelated comments are posted. +If further discussion is needed, we encourage you to open a new issue instead and we recommend linking to the issue/PR in question for context. + +## Building Gitea + +See [docs/build-setup.md](docs/build-setup.md) for prerequisites and [docs/development.md](docs/development.md) +for building Gitea and the development workflow. + +## Styleguide + +You should always run `make fmt` before committing to conform to Gitea's styleguide. + +## Copyright + +New code files that you contribute should use the standard copyright header: + +``` +// Copyright The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT +``` + +Afterwards, copyright should only be modified when the copyright author changes. + +## Testing + +Before submitting a pull request, run the linters (`make lint`, or the scoped `make lint-backend` / `make lint-frontend`) and the tests to make sure your changes don't cause a regression elsewhere. See [docs/testing.md](docs/testing.md) for how to run the unit, integration, end-to-end, and migration tests. + +## Translation + +All translation work happens on [Crowdin](https://translate.gitea.com). +The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.json). +It is synced regularly with Crowdin. \ +Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \ +Once a language has reached a **satisfactory percentage** of translated keys (~25%), it will be synced back into this repo and included in the next released version. + +The tool `go run build/backport-locale.go` can be used to backport locales from the main branch to release branches that were missed. + +## Code review + +How labels, milestones, and the merge queue work is documented in [docs/community-governance.md](docs/community-governance.md). + +### Pull request format + +Please try to make your pull request easy to review for us. \ +For that, please read the [*Best Practices for Faster Reviews*](https://github.com/kubernetes/community/blob/261cb0fd089b64002c91e8eddceebf032462ccd6/contributors/guide/pull-requests.md#best-practices-for-faster-reviews) guide. \ +It has lots of useful tips for any project you may want to contribute to. \ +Some of the key points: + +- Make small pull requests. \ + The smaller, the faster to review and the more likely it will be merged soon. +- Don't make changes unrelated to your PR. \ + Maybe there are typos on some comments, maybe refactoring would be welcome on a function... \ + but if that is not related to your PR, please make *another* PR for that. +- Split big pull requests into multiple small ones. \ + An incremental change will be faster to review than a huge PR. +- Allow edits by maintainers. This way, the maintainers will take care of merging the PR later on instead of you. + +### PR title and summary + +In the PR title, describe the problem you are fixing, not how you are fixing it. \ +Use the first comment as a summary of your PR. \ +In the PR summary, you can describe exactly how you are fixing this problem. + +PR titles must follow the [Conventional Commits](https://www.conventionalcommits.org/) format, because PRs are squash-merged and the PR title becomes the resulting commit message: + +```text +type(scope)!: subject +``` + +The scope in parentheses is optional. A `!` immediately before the colon marks a [breaking change](https://www.conventionalcommits.org/en/v1.0.0/#summary): either `type!:` or `type(scope)!:` (not `type!(scope):`). + +Use one of these types: + +- `build`: Changes affecting the build system, packaging, or external dependencies +- `ci`: Changes to CI/CD configuration files and scripts +- `chore`: Maintenance changes that do not affect production code or should not appear in the changelog +- `docs`: Documentation-only changes +- `feat`: A larger user-facing feature, improvement, or new functionality +- `enhance`: Small or trivial user-facing improvements or UX polish (for example wording changes, color adjustments, spacing or padding tweaks, placeholders, small UI behavior improvements) +- `fix`: A bug fix, UX correction, or security-related dependency update +- `perf`: Performance improvements (speed, memory, scalability) +- `refactor`: A code change that neither fixes a bug nor adds a feature +- `revert`: Reverts a previous change +- `style`: Formatting or style-only changes that do not affect code behavior (for example lint-driven edits) +- `test`: Adding or correcting tests + +Examples: + +```text +fix(web): prevent avatar upload crash on empty file +feat(api): add pagination to repo hooks list +enhance(repo): improve diff toolbar spacing +ci(workflows): lint PR titles in CI +``` + +Keep this summary up-to-date as the PR evolves. \ +If your PR changes the UI, you must add **after** screenshots in the PR summary. \ +If you are not implementing a new feature, you should also post **before** screenshots for comparison. + +If you are implementing a new feature, your PR will only be merged if your screenshots are up to date.\ +Furthermore, feature PRs will only be merged if their summary contains a clear usage description (understandable for users) and testing description (understandable for reviewers). +You should strive to combine both into a single description. + +Another requirement for merging PRs is that the PR is labeled correctly.\ +However, this is not your job as a contributor, but the job of the person merging your PR.\ +If you think that your PR was labeled incorrectly, or notice that it was merged without labels, please let us know. + +For pull requests that use a valid Conventional Commits title, CI automatically applies a matching `type/…` label when the title prefix is `feat`, `enhance`, `fix`, `docs`, or `test` (for example `enhance(web): …` receives `type/enhancement`).\ +That label is kept in sync with the PR title when the title is edited.\ +Other title prefixes do not get an automatic `type/…` label; the merger still assigns the correct labels (including `type/…` when needed) for changelog and backport decisions. + +If your PR closes some issues, you must note that in a way that both GitHub and Gitea understand, i.e. by appending a paragraph like + +```text +Fixes/Closes/Resolves #. +Fixes/Closes/Resolves #. +``` + +to your summary. \ +Each issue that will be closed must stand on a separate line. + +### Breaking PRs + +#### What is a breaking PR? + +A PR is breaking if it meets one of the following criteria: + +- It changes API output in an incompatible way for existing users +- It removes a setting that an admin could previously set (i.e. via `app.ini`) +- An admin must do something manually to restore the old behavior + +In particular, this means that adding new settings is not breaking.\ +Changing the default value of a setting or replacing the setting with another one is breaking, however. + +#### How to handle breaking PRs? + +If your PR has a breaking change, you must add two things to the summary of your PR: + +1. A reasoning why this breaking change is necessary +2. A `BREAKING` section explaining in simple terms (understandable for a typical user) how this PR affects users and how to mitigate these changes. This section can look for example like + +```md +## :warning: BREAKING :warning: +``` + +Breaking PRs will not be merged as long as not both of these requirements are met. + +### Maintaining open PRs + +Code review starts when you open a non-draft PR or move a draft out of draft state. After that, do not rebase or squash your branch; it makes new changes harder to review. + +Merge the base branch into yours only when you need to, for example because of conflicting changes elsewhere. That limits unnecessary CI runs. + +Every PR is squash-merged, so merge commits on your branch do not matter for final history. The squash produces a single commit; mergers follow the [commit message format](docs/community-governance.md#commit-messages) in the governance guide. + +### Reviewing PRs + +Maintainers are encouraged to review pull requests in areas where they have expertise or particular interest. + +#### For PR authors + +- **Response**: When answering reviewer questions, use real-world cases or examples and avoid speculation. +- **Discussion**: A discussion is always welcome and should be used to clarify the changes and the intent of the PR. +- **Help**: If you need help with the PR or comments are unclear, ask for clarification. + +Guidance for reviewers, the merge queue, and the squash commit message format is in [docs/community-governance.md](docs/community-governance.md). + +## Documentation + +If you add a new feature or change an existing aspect of Gitea, the documentation for that feature must be created or updated in another PR at [https://gitea.com/gitea/docs](https://gitea.com/gitea/docs). +**The docs directory on main repository will be removed at some time. We will have a yaml file to store configuration file's meta data. After that completed, configuration documentation should be in the main repository.** + +## Developer Certificate of Origin (DCO) + +We consider the act of contributing to the code by submitting a Pull Request as the "Sign off" or agreement to the certifications and terms of the [DCO](DCO) and [MIT license](LICENSE). \ +No further action is required. \ +You can also decide to sign off your commits by adding the following line at the end of your commit messages: + +``` +Signed-off-by: Joe Smith +``` + +If you set the `user.name` and `user.email` Git config options, you can add the line to the end of your commits automatically with `git commit -s`. + +We assume in good faith that the information you provide is legally binding. diff --git a/DCO b/DCO new file mode 100644 index 0000000..49b8cb0 --- /dev/null +++ b/DCO @@ -0,0 +1,34 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6210aef --- /dev/null +++ b/Dockerfile @@ -0,0 +1,86 @@ +# syntax=docker/dockerfile:1 +# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem +FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS frontend-build +RUN apk --no-cache add build-base git nodejs pnpm +WORKDIR /src +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +RUN --mount=type=cache,target=/root/.local/share/pnpm/store pnpm install --frozen-lockfile +COPY --exclude=.git/ . . +RUN make frontend + +# Build backend for each target platform +FROM docker.io/library/golang:1.27-alpine3.24 AS build-env + +ARG GITEA_VERSION +ARG TAGS="" +ENV TAGS="bindata timetzdata $TAGS" +ARG CGO_EXTRA_CFLAGS + +# Build deps +RUN apk --no-cache add \ + build-base \ + git + +WORKDIR ${GOPATH}/src/gitea.dev +COPY go.mod go.sum ./ +RUN go mod download +# Use COPY instead of bind mount as read-only one breaks makefile state tracking and read-write one needs binary to be moved as it's discarded. +# ".git" directory is mounted separately later only for version data extraction. +COPY --exclude=.git/ . . +COPY --from=frontend-build /src/public/assets public/assets + +# Build gitea, .git mount is required for version data +RUN --mount=type=cache,target="/root/.cache/go-build" \ + --mount=type=bind,source=".git/",target=".git/" \ + make backend + +COPY docker/root /tmp/local + +# Set permissions for builds that made under windows which strips the executable bit from file +RUN chmod 755 /tmp/local/usr/bin/entrypoint \ + /tmp/local/usr/local/bin/* \ + /tmp/local/etc/s6/gitea/* \ + /tmp/local/etc/s6/openssh/* \ + /tmp/local/etc/s6/.s6-svscan/* \ + /go/src/gitea.dev/gitea + +FROM docker.io/library/alpine:3.24 AS gitea + +EXPOSE 22 3000 + +RUN apk --no-cache add \ + bash \ + ca-certificates \ + curl \ + gettext \ + git \ + linux-pam \ + openssh \ + s6 \ + sqlite \ + su-exec \ + gnupg + +RUN addgroup \ + -S -g 1000 \ + git && \ + adduser \ + -S -H -D \ + -h /data/git \ + -s /bin/bash \ + -u 1000 \ + -G git \ + git && \ + echo "git:*" | chpasswd -e + +COPY --from=build-env /tmp/local / +COPY --from=build-env /go/src/gitea.dev/gitea /app/gitea/gitea + +ENV USER=git +ENV GITEA_CUSTOM=/data/gitea + +VOLUME ["/data"] + +# HINT: HEALTH-CHECK-ENDPOINT: don't use HEALTHCHECK, search this hint keyword for more information +ENTRYPOINT ["/usr/bin/entrypoint"] +CMD ["/usr/bin/s6-svscan", "/etc/s6"] diff --git a/Dockerfile.rootless b/Dockerfile.rootless new file mode 100644 index 0000000..d7ce351 --- /dev/null +++ b/Dockerfile.rootless @@ -0,0 +1,88 @@ +# syntax=docker/dockerfile:1 +# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem +FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS frontend-build +RUN apk --no-cache add build-base git nodejs pnpm +WORKDIR /src +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +RUN --mount=type=cache,target=/root/.local/share/pnpm/store pnpm install --frozen-lockfile +COPY --exclude=.git/ . . +RUN make frontend + +# Build backend for each target platform +FROM docker.io/library/golang:1.27-alpine3.24 AS build-env + +ARG GITEA_VERSION +ARG TAGS="" +ENV TAGS="bindata timetzdata $TAGS" +ARG CGO_EXTRA_CFLAGS + +# Build deps +RUN apk --no-cache add \ + build-base \ + git + +WORKDIR ${GOPATH}/src/gitea.dev +COPY go.mod go.sum ./ +RUN go mod download +# See the comments in Dockerfile +COPY --exclude=.git/ . . +COPY --from=frontend-build /src/public/assets public/assets + +# Build gitea, .git mount is required for version data +RUN --mount=type=cache,target="/root/.cache/go-build" \ + --mount=type=bind,source=".git/",target=".git/" \ + make backend + +COPY docker/rootless /tmp/local + +# Set permissions for builds that made under windows which strips the executable bit from file +RUN chmod 755 /tmp/local/usr/local/bin/* \ + /go/src/gitea.dev/gitea + +FROM docker.io/library/alpine:3.24 AS gitea-rootless + +EXPOSE 2222 3000 + +RUN apk --no-cache add \ + bash \ + ca-certificates \ + dumb-init \ + gettext \ + git \ + curl \ + gnupg \ + openssh-keygen + +RUN addgroup \ + -S -g 1000 \ + git && \ + adduser \ + -S -H -D \ + -h /var/lib/gitea/git \ + -s /bin/bash \ + -u 1000 \ + -G git \ + git + +RUN mkdir -p /var/lib/gitea /etc/gitea +RUN chown git:git /var/lib/gitea /etc/gitea + +COPY --from=build-env /tmp/local / +COPY --from=build-env --chown=root:root /go/src/gitea.dev/gitea /app/gitea/gitea + +# git:git +USER 1000:1000 +ENV GITEA_WORK_DIR=/var/lib/gitea +ENV GITEA_CUSTOM=/var/lib/gitea/custom +ENV GITEA_TEMP=/tmp/gitea +ENV TMPDIR=/tmp/gitea + +# TODO add to docs the ability to define the ini to load (useful to test and revert a config) +ENV GITEA_APP_INI=/etc/gitea/app.ini +ENV HOME="/var/lib/gitea/git" +VOLUME ["/var/lib/gitea", "/etc/gitea"] +WORKDIR /var/lib/gitea + +# HINT: HEALTH-CHECK-ENDPOINT: don't use HEALTHCHECK, search this hint keyword for more information +ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/docker-entrypoint.sh"] +CMD [] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a8d4b49 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 The Gitea Authors +Copyright (c) 2015 The Gogs Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..87463a3 --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,67 @@ +Alexey Makhov (@makhov) +Bo-Yi Wu (@appleboy) +Ethan Koenig (@ethantkoenig) +Kees de Vries (@Bwko) +Kim Carlbäcker (@bkcsoft) +LefsFlare (@LefsFlarey) +Lunny Xiao (@lunny) +Rachid Zarouali (@xinity) +Rémy Boulanouar (@DblK) +Sandro Santilli (@strk) +Thibault Meyer (@0xbaadf00d) +Thomas Boerger (@tboerger) +Patrick G (@geek1011) +Antoine Girard (@sapk) +Lauris Bukšis-Haberkorns (@lafriks) +Jonas Östanbäck (@cez81) +David Schneiderbauer (@daviian) +Peter Žeby (@morlinest) +Matti Ranta (@techknowlogick) +Jonas Franz (@jonasfranz) +Alexey Terentyev (@axifive) +Lanre Adelowo (@adelowo) +Konrad Langenberg (@kolaente) +He-Long Zhang (@BetaCat0) +Andrew Thornton (@zeripath) +John Olheiser (@jolheiser) +Richard Mahn (@richmahn) +Mrsdizzie (@mrsdizzie) +silverwind (@silverwind) +Gary Kim (@gary-kim) +Guillermo Prandi (@guillep2k) +Mura Li (@typeless) +6543 <6543@obermui.de> (@6543) +David Svantesson (@davidsvantesson) +a1012112796 <1012112796@qq.com> (@a1012112796) +Karl Heinz Marbaise (@khmarbaise) +Norwin Roosen (@noerw) +Kyle Dumont (@kdumontnu) +Janis Estelmann (@KN4CK3R) +Jimmy Praet (@jpraet) +Leon Hofmeister (@delvh) +Wim (@42wim) +Jason Song (@wolfogre) +Yu Tian (@Zettat123) +Dong Ge (@sillyguodong) +Xinyi Gong (@HesterG) +wxiaoguang (@wxiaoguang) +Gary Moon (@garymoon) +Philip Peterson (@philip-peterson) +Denys Konovalov (@denyskon) +Punit Inani (@puni9869) +CaiCandong <1290147055@qq.com> (@caicandong) +Rui Chen (@chenrui333) +Nanguan Lin (@lng2020) +kerwin612 (@kerwin612) +Gary Wang (@BLumia) +Tim-Niclas Oelschläger (@zokkis) +Yu Liu <1240335630@qq.com> (@HEREYUA) +Kemal Zebari (@kemzeb) +Rowan Bohde (@bohde) +hiifong (@hiifong) +metiftikci (@metiftikci) +Christopher Homberger (@ChristopherHX) +Tobias Balle-Petersen (@tobiasbp) +TheFox (@TheFox0x7) +Nicolas (@bircni) +Minjie Fang (@wingsallen) \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..363a8be --- /dev/null +++ b/Makefile @@ -0,0 +1,659 @@ +DIST := dist +DIST_DIRS := $(DIST)/binaries $(DIST)/release + +GO ?= go +SHASUM ?= shasum -a 256 + +AIR_PACKAGE ?= github.com/air-verse/air@v1.67.4 # renovate: datasource=go +EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v4/cmd/editorconfig-checker@v4.0.1 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.2 # renovate: datasource=go +GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.16 # renovate: datasource=go +MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.8.0 # renovate: datasource=go +SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.36.5 # renovate: datasource=go +GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.8.0 # renovate: datasource=go +ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 # renovate: datasource=go +SHELLCHECK_IMAGE ?= docker.io/koalaman/shellcheck:v0.11.0@sha256:61862eba1fcf09a484ebcc6feea46f1782532571a34ed51fedf90dd25f925a8d # renovate: datasource=docker + +CONTAINER_RUNTIME ?= $(shell hash docker >/dev/null 2>&1 && echo docker || echo podman) + +PLAYWRIGHT_BROWSERS ?= chromium firefox +PLAYWRIGHT_FLAGS ?= + +HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes) +ifeq ($(HAS_GO), yes) + CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766 + CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS) +endif + +MAKE_EVIDENCE_DIR := .make_evidence + +# Use sqlite as default database if running tests, only do so for local tests, not in CI. +# CI should explicitly set the database to avoid unexpected results. +ifneq ($(findstring test-,$(MAKECMDGOALS)),) + ifeq ($(CI),) + GITEA_TEST_DATABASE ?= sqlite + endif +endif + +TAGS ?= +TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags +CGO_TAGS := sqlite_mattn pam + +CGO_ENABLED ?= 0 +ifneq ($(strip $(filter $(CGO_TAGS),$(TAGS))),) + CGO_ENABLED = 1 +endif + +ifeq ($(GOOS),windows) + IS_WINDOWS := yes +else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows) + ifeq ($(GOOS),) + IS_WINDOWS := yes + endif +endif + +ifeq ($(IS_WINDOWS),yes) + EXECUTABLE ?= gitea.exe +else + EXECUTABLE ?= gitea +endif + +# EXTRA_GOFLAGS is for the 'go build' command only +EXTRA_GOFLAGS ?= + +ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu) + SED_INPLACE := sed -i +else + SED_INPLACE := sed -i '' +endif + +# GOTEST_FLAGS is for unit test and integration test +GOTEST_FLAGS ?= -timeout 40m + +STORED_VERSION_FILE := VERSION + +GITHUB_REF_TYPE ?= branch +GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) + +# VERSION: the branch name for the build and filenames, e.g.: "feature/foo-bar", "main" +# branch name "release/v1.27.2" is stripped to "1.27.2". +# GITEA_VERSION: the Gitea's internal version for display, e.g. "1.28.0+dev-356-ge47d0b66ea" +ifeq ($(GITHUB_REF_TYPE),tag) + # convert tag "v1.2.3" to "1.2.3" + VERSION ?= $(subst v,,$(GITHUB_REF_NAME)) + GITEA_VERSION ?= $(VERSION) +else ifeq ($(GITHUB_REF_TYPE),branch) + ifneq ($(GITHUB_REF_NAME),) + # convert branch "release/v1.2" to "1.2-nightly" + VERSION ?= $(subst release/v,,$(GITHUB_REF_NAME))-nightly + else + # no branch name info, use git ref name "HEAD" instead + VERSION ?= HEAD + endif + + STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null) + ifneq ($(STORED_VERSION),) + GITEA_VERSION ?= $(STORED_VERSION) + else + GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') + endif +else + $(error unsupported ref type $(GITHUB_REF_TYPE)) +endif + +# if version == "main" then add "-nightly" to the version for nightly builds: "main-nightly" +ifeq ($(VERSION),main) + VERSION := main-nightly +endif + +LDFLAGS := $(LDFLAGS) -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)" +RELEASE_ENV = GO="$(GO)" TAGS="$(TAGS)" LDFLAGS="$(LDFLAGS)" DIST="$(DIST)" VERSION="$(VERSION)" + +GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list gitea.dev/modelmigration/...) gitea.dev/tests/integration/migration-test gitea.dev/tests gitea.dev/tests/integration,$(shell $(GO) list ./... | grep -v /vendor/)) +MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list gitea.dev/modelmigration/...) + +FRONTEND_SOURCES := $(shell find web_src/js web_src/css -type f) +FRONTEND_CONFIGS := vite.config.ts tailwind.config.ts +FRONTEND_DEST := public/assets/.vite/manifest.json +FRONTEND_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts public/assets/.vite +FRONTEND_DEV_LOG_LEVEL ?= warn + +BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.* + +GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go + +SVG_DEST_DIR := public/assets/img/svg +SVG_DEST_DIRS := $(SVG_DEST_DIR) options/fileicon + +AIR_TMP_DIR := .air + +GO_LICENSE_FILE := assets/go-licenses.json + +TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) + +GO_DIRS := build cmd modelmigration models modules routers services tests tools +WEB_DIRS := web_src/js web_src/css + +ESLINT_FILES := web_src/js tools *.ts tests/e2e +STYLELINT_FILES := web_src/css web_src/js/components/*.vue +SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.json .github $(filter-out CHANGELOG.md, $(wildcard *.go *.md *.yml *.yaml *.toml)) +EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.json + +GO_SOURCES := $(wildcard *.go) +GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go") +GO_SOURCES += $(GENERATED_GO_DEST) + +ESLINT_CONCURRENCY ?= 2 +ESLINT_ARGS := --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) + +SWAGGER_EXCLUDE := gitea.dev/sdk +SWAGGER_SPEC_INPUT := templates/swagger/v1-input.json +SWAGGER_SPEC := templates/swagger/v1-swagger.generated.json +OPENAPI3_SPEC := templates/swagger/v1-openapi3.generated.json + +TEST_MYSQL_HOST ?= mysql:3306 +TEST_MYSQL_DBNAME ?= testgitea +TEST_MYSQL_USERNAME ?= root +TEST_MYSQL_PASSWORD ?= +TEST_PGSQL_HOST ?= pgsql:5432 +TEST_PGSQL_DBNAME ?= testgitea +TEST_PGSQL_USERNAME ?= postgres +TEST_PGSQL_PASSWORD ?= postgres +TEST_PGSQL_SCHEMA ?= gtestschema +TEST_MINIO_ENDPOINT ?= minio:9000 +TEST_MSSQL_HOST ?= mssql:1433 +TEST_MSSQL_DBNAME ?= testgitea +TEST_MSSQL_USERNAME ?= sa +TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1 + +# Include local Makefile +# Makefile.local is listed in .gitignore +ifneq ("$(wildcard Makefile.local)","") + include Makefile.local +endif + +$(foreach v, $(filter TEST_%, $(.VARIABLES)), $(eval MAKEFILE_VARS+=$v=$($v))) +$(foreach v, $(filter GITEA_TEST_%, $(.VARIABLES)), $(eval MAKEFILE_VARS+=$v=$($v))) +export MAKEFILE_VARS + +.PHONY: all +all: build + +.PHONY: help +help: Makefile ## print Makefile help information. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile #$(MAKEFILE_LIST) + @printf " \033[36m%-46s\033[0m %s\n" "test-e2e" "test end to end using playwright" + @printf " \033[36m%-46s\033[0m %s\n" "test-backend[#TestSpecificName]" "run unit test (sqlite only)" + @printf " \033[36m%-46s\033[0m %s\n" "test-integration[#TestSpecificName]" "run integration test for GITEA_TEST_DATABASE (sqlite, mysql, pgsql, mssql)" + +.PHONY: clean-all +clean-all: clean ## delete backend, frontend and integration files + rm -rf $(FRONTEND_DEST_ENTRIES) node_modules + +.PHONY: clean +clean: ## delete backend and integration files + rm -f $(EXECUTABLE) test-*.test tests/*.ini + rm -rf $(DIST) $(BINDATA_DEST_WILDCARD) man tests/integration/gitea-integration-* + +.PHONY: fmt +fmt: ## format the Go and template code + $(GO) run $(GOLANGCI_LINT_PACKAGE) fmt + $(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl')) + @# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only + @# whitespace before it + @$(SED_INPLACE) \ + -e 's/{{[ ]\{1,\}/{{/g' -e '/^[ ]\{1,\}}}/! s/[ ]\{1,\}}}/}}/g' \ + -e 's/([ ]\{1,\}/(/g' -e '/^[ ]\{1,\})/! s/[ ]\{1,\})/)/g' \ + $(TEMPLATES) + +.PHONY: fmt-check +fmt-check: fmt + @diff=$$(git diff --color=always $(GO_SOURCES) templates); \ + if [ -n "$$diff" ]; then \ + echo "Please run 'make fmt' and commit the result:"; \ + printf "%s" "$${diff}"; \ + exit 1; \ + fi + +.PHONY: $(TAGS_EVIDENCE) +$(TAGS_EVIDENCE): + @mkdir -p $(MAKE_EVIDENCE_DIR) + @echo "$(TAGS)" > $(TAGS_EVIDENCE) + +ifneq "$(TAGS)" "$(shell cat $(TAGS_EVIDENCE) 2>/dev/null)" +TAGS_PREREQ := $(TAGS_EVIDENCE) +endif + +.PHONY: generate-swagger +generate-swagger: $(SWAGGER_SPEC) $(OPENAPI3_SPEC) ## generate the swagger spec from code comments + +$(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT) + @output="$$($(GO) run $(SWAGGER_PACKAGE) generate spec --enable-allof-compounding --skip-enum-desc --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)' 2>&1)" || { printf '%s\n' "$$output" >&2; exit 1; }; \ + warnings="$$(printf '%s\n' "$$output" | grep -v '^go: ')"; \ + if [ -n "$$warnings" ]; then printf '%s\n' "$$warnings" >&2; exit 1; fi + +.PHONY: swagger-check +swagger-check: generate-swagger + @diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \ + if [ -n "$$diff" ]; then \ + echo "Please run 'make generate-swagger' and commit the result:"; \ + printf "%s" "$${diff}"; \ + exit 1; \ + fi + +.PHONY: swagger-validate +swagger-validate: ## check if the swagger spec is valid + @# ensure no warnings + @output="$$($(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)' 2>&1)"; status=$$?; \ + printf '%s\n' "$$output" | grep -v '^go: '; \ + case "$$output" in *WARNING:*) exit 1;; esac; exit $$status + +.PHONY: generate-openapi3 +generate-openapi3: $(OPENAPI3_SPEC) ## generate the OpenAPI 3.0 spec from the Swagger 2.0 spec + +$(OPENAPI3_SPEC): $(SWAGGER_SPEC) build/generate-openapi.go $(wildcard build/openapi3gen/*.go) + $(GO) run build/generate-openapi.go + +.PHONY: openapi3-check +openapi3-check: generate-openapi3 + @diff=$$(git diff --color=always '$(OPENAPI3_SPEC)'); \ + if [ -n "$$diff" ]; then \ + echo "Please run 'make generate-openapi3' and commit the result:"; \ + printf "%s" "$${diff}"; \ + exit 1; \ + fi + +.PHONY: checks +checks: checks-frontend checks-backend ## run various consistency checks + +.PHONY: checks-frontend +checks-frontend: lockfile-check svg-check ## check frontend files + +.PHONY: checks-backend +checks-backend: tidy-check swagger-check openapi3-check fmt-check swagger-validate security-check ## check backend files + +.PHONY: lint +lint: lint-frontend lint-backend lint-templates lint-swagger lint-spell lint-md lint-actions lint-json lint-yaml lint-shell ## lint everything + +.PHONY: lint-fix +lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix ## lint everything and fix issues + +.PHONY: lint-frontend +lint-frontend: lint-js lint-css ## lint frontend files + +.PHONY: lint-frontend-fix +lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues + +.PHONY: lint-backend +lint-backend: lint-go lint-editorconfig ## lint backend files + +.PHONY: lint-backend-fix +lint-backend-fix: lint-go-fix lint-editorconfig ## lint backend files and fix issues + +.PHONY: lint-js +lint-js: node_modules ## lint js and ts files + pnpm exec eslint $(ESLINT_ARGS) $(ESLINT_FILES) + pnpm exec vue-tsc + +.PHONY: lint-js-fix +lint-js-fix: node_modules ## lint js and ts files and fix issues + pnpm exec eslint $(ESLINT_ARGS) $(ESLINT_FILES) --fix + pnpm exec vue-tsc + +.PHONY: lint-css +lint-css: node_modules ## lint css files + pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES) + +.PHONY: lint-css-fix +lint-css-fix: node_modules ## lint css files and fix issues + pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix + +.PHONY: lint-swagger +lint-swagger: node_modules ## lint swagger files + pnpm exec spectral lint -q -F hint $(SWAGGER_SPEC) $(OPENAPI3_SPEC) + +.PHONY: lint-md +lint-md: node_modules ## lint markdown files + pnpm exec markdownlint *.md + +.PHONY: lint-md-fix +lint-md-fix: node_modules ## lint markdown files and fix issues + pnpm exec markdownlint --fix *.md + +.PHONY: lint-spell +lint-spell: ## lint spelling + @git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -error + +.PHONY: lint-spell-fix +lint-spell-fix: ## lint spelling and fix issues + @git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -w + +.PHONY: lint-go +lint-go: ## lint go files + GO=$(GO) GOLANGCI_LINT_PACKAGE=$(GOLANGCI_LINT_PACKAGE) $(GO) run ./tools/lint-go-all.go + +.PHONY: lint-go-fix +lint-go-fix: ## lint go files and fix issues + GO=$(GO) GOLANGCI_LINT_PACKAGE=$(GOLANGCI_LINT_PACKAGE) $(GO) run ./tools/lint-go-all.go --fix + +.PHONY: lint-editorconfig +lint-editorconfig: + @echo "Running editorconfig check..." + @$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) $(EDITORCONFIG_FILES) + +.PHONY: lint-actions +lint-actions: .venv ## lint action workflow files + @$(GO) run $(ACTIONLINT_PACKAGE) + @uv run --frozen zizmor --quiet --persona=pedantic --min-confidence=medium .github + +.PHONY: lint-shell +lint-shell: ## lint shell scripts + @SHELLCHECK_IMAGE=$(SHELLCHECK_IMAGE) CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) ./tools/lint-shell.sh $$(git ls-files '*.sh') + +.PHONY: lint-templates +lint-templates: .venv node_modules ## lint template files + @node tools/lint-templates-svg.ts + @uv run --frozen djlint $(shell find templates -type f -iname '*.tmpl') + +.PHONY: lint-yaml +lint-yaml: .venv ## lint yaml files + @uv run --frozen yamllint -s . + +.PHONY: lint-json +lint-json: node_modules ## lint json files + pnpm exec eslint -c eslint.json.config.ts $(ESLINT_ARGS) + +.PHONY: lint-json-fix +lint-json-fix: node_modules ## lint and fix json files + pnpm exec eslint -c eslint.json.config.ts $(ESLINT_ARGS) --fix + +.PHONY: watch +watch: ## watch everything and continuously rebuild + @bash tools/watch.sh + +.PHONY: watch-frontend +watch-frontend: node_modules ## start vite dev server for frontend + NODE_ENV=development pnpm exec vite --logLevel $(FRONTEND_DEV_LOG_LEVEL) + +.PHONY: watch-backend +watch-backend: ## watch backend files and continuously rebuild + GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml + +.PHONY: test-backend +test-backend: ## test backend files + @echo "Running go test with $(GOTEST_FLAGS) -tags '$(TAGS)'..." + @$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' $(GO_TEST_PACKAGES) + +.PHONY: test-frontend +test-frontend: playwright ## test frontend files + pnpm exec vitest + +.PHONY: test-check +test-check: + @echo "Running test-check..."; + @diff=$$(git status -s); \ + if [ -n "$$diff" ]; then \ + echo "make test-backend has changed files in the source tree:"; \ + printf "%s" "$${diff}"; \ + echo "You should change the tests to create these files in a temporary directory."; \ + echo "Do not simply add these files to .gitignore"; \ + exit 1; \ + fi + +.PHONY: test-backend\#% +test-backend\#%: + @echo "Running go test with -tags '$(TAGS)'..." + @$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES) + +.PHONY: coverage +coverage: + grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' coverage.out > coverage-bodged.out + grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' integration.coverage.out > integration.coverage-bodged.out + $(GO) run tools/gocovmerge.go integration.coverage-bodged.out coverage-bodged.out > coverage.all + +.PHONY: unit-test-coverage +unit-test-coverage: + @echo "Running unit-test-coverage $(GOTEST_FLAGS) -tags '$(TAGS)'..." + @$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 + +.PHONY: tidy +tidy: ## run go mod tidy + $(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2)) + $(eval GO_TOOLCHAIN := $(shell grep -Eo '^toolchain\s+go[0-9.]+' go.mod | cut -d' ' -f2)) + $(GO) mod tidy -compat=$(MIN_GO_VERSION) + @# workaround https://github.com/golang/go/issues/75331: restore toolchain if tidy dropped it + @if [ -n "$(GO_TOOLCHAIN)" ] && ! grep -qE '^toolchain\s' go.mod; then \ + $(GO) mod edit -toolchain=$(GO_TOOLCHAIN); \ + fi + @$(MAKE) --no-print-directory $(GO_LICENSE_FILE) + +vendor: go.mod go.sum + $(GO) mod vendor + @touch vendor + +.PHONY: tidy-check +tidy-check: tidy + @diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \ + if [ -n "$$diff" ]; then \ + echo "Please run 'make tidy' and commit the result:"; \ + printf "%s" "$${diff}"; \ + exit 1; \ + fi + +.PHONY: go-licenses +go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses + +$(GO_LICENSE_FILE): go.mod go.sum + GO=$(GO) $(GO) run build/generate-go-licenses.go $(GO_LICENSE_FILE) + +.PHONY: test-integration +test-integration: $(EXECUTABLE) + @# Use a compiled binary: testlogger forwards gitea logs to t.Log, so `go test -v` + @# would flood output per passing test. testcache can't help these tests anyway — + @# they mutate the work directory, so cache inputs change between runs. + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -c gitea.dev/tests/integration -o ./test-integration-$(GITEA_TEST_DATABASE).test + ./tools/test-integration.sh ./test-integration-$(GITEA_TEST_DATABASE).test + +.PHONY: test-integration-compile +test-integration-compile: + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -c -o /dev/null gitea.dev/tests/integration + +.PHONY: test-integration\#% +test-integration\#%: $(EXECUTABLE) + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -run $(subst .,/,$*) gitea.dev/tests/integration + +.PHONY: test-migration +test-migration: migrations.integration.test migrations.individual.test + +.PHONY: migrations.integration.test +migrations.integration.test: + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' gitea.dev/tests/integration/migration-test + +.PHONY: migrations.individual.test +migrations.individual.test: + @# tests of multiple packages use the same database, don't run in parallel + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES) + +.PHONY: migrations.individual.test\#% +migrations.individual.test\#%: + $(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' gitea.dev/modelmigration/$* + +.PHONY: playwright +playwright: deps-frontend + @./tools/playwright.sh $(PLAYWRIGHT_FLAGS) $(PLAYWRIGHT_BROWSERS) + +.PHONY: test-e2e +test-e2e: playwright frontend backend + @CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) EXECUTABLE=$(EXECUTABLE) ./tools/test-e2e.sh $(GITEA_TEST_E2E_FLAGS) + +.PHONY: build +build: frontend backend ## build everything + +.PHONY: frontend +frontend: $(FRONTEND_DEST) ## build frontend files + +.PHONY: backend +backend: generate-backend $(EXECUTABLE) ## build backend files + +# We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend +.PHONY: generate +generate: generate-backend ## run "go generate" + +.PHONY: generate-backend +generate-backend: $(TAGS_PREREQ) generate-go + +.PHONY: generate-go +generate-go: $(TAGS_PREREQ) + @echo "Running go generate..." + @CC= GOOS= GOARCH= CGO_ENABLED=0 $(GO) generate -tags '$(TAGS)' ./... + +.PHONY: security-check +security-check: + go run $(GOVULNCHECK_PACKAGE) -show color ./... || true + +$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ) + CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build -v $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@ + +$(DIST_DIRS): + mkdir -p $(DIST_DIRS) + +# Release builds always use Go's native cross compilation. To cross-compile with CGO, +# use "build" target with proper TAGS/LDFLAGS/CGO_CFLAGS to make "$(EXECUTABLE)" target run the "go build" command. +.PHONY: release +release: frontend release-binaries release-copy release-compress vendor release-sources release-check + +.PHONY: release-binaries +release-binaries: | $(DIST_DIRS) + @$(RELEASE_ENV) ./tools/build-release.sh + +.PHONY: release-windows +release-windows: | $(DIST_DIRS) + @$(RELEASE_ENV) ./tools/build-release.sh windows + +.PHONY: release-linux +release-linux: | $(DIST_DIRS) + @$(RELEASE_ENV) ./tools/build-release.sh linux + +.PHONY: release-darwin +release-darwin: | $(DIST_DIRS) + @$(RELEASE_ENV) ./tools/build-release.sh darwin + +.PHONY: release-freebsd +release-freebsd: | $(DIST_DIRS) + @$(RELEASE_ENV) ./tools/build-release.sh freebsd + +.PHONY: release-copy +release-copy: | $(DIST_DIRS) + cd $(DIST); for file in `find . -type f -name "*"`; do cp $${file} ./release/; done; + +.PHONY: release-check +release-check: | $(DIST_DIRS) + cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done; + +.PHONY: release-compress +release-compress: | $(DIST_DIRS) + cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PACKAGE) -k -9 $${file}; done; + +.PHONY: release-sources +release-sources: | $(DIST_DIRS) + echo $(VERSION) > $(STORED_VERSION_FILE) +# bsdtar needs a ^ to prevent matching subdirectories + $(eval EXCL := --exclude=$(shell tar --help | grep -q bsdtar && echo "^")./) +# use transform to a add a release-folder prefix; in bsdtar the transform parameter equivalent is -s + $(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '|^./|gitea-src-$(VERSION)/|'" || echo "--transform 's|^./|gitea-src-$(VERSION)/|'")) + tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz . + rm -f $(STORED_VERSION_FILE) + +.PHONY: deps +deps: deps-frontend deps-backend deps-tools deps-py ## install dependencies + +.PHONY: deps-py +deps-py: .venv ## install python dependencies + +.PHONY: deps-frontend +deps-frontend: node_modules ## install frontend dependencies + +.PHONY: deps-backend +deps-backend: ## install backend dependencies + $(GO) mod download + +.PHONY: deps-tools +deps-tools: ## install tool dependencies + $(GO) install $(AIR_PACKAGE) & \ + $(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \ + $(GO) install $(GOLANGCI_LINT_PACKAGE) & \ + $(GO) install $(GXZ_PACKAGE) & \ + $(GO) install $(MISSPELL_PACKAGE) & \ + $(GO) install $(SWAGGER_PACKAGE) & \ + $(GO) install $(GOVULNCHECK_PACKAGE) & \ + $(GO) install $(ACTIONLINT_PACKAGE) & \ + wait + +node_modules: pnpm-lock.yaml + pnpm install --frozen-lockfile + @touch node_modules + +.venv: uv.lock + uv sync + @touch .venv + +.PHONY: vite +vite: $(FRONTEND_DEST) ## build vite files + +$(FRONTEND_DEST): $(FRONTEND_SOURCES) $(FRONTEND_CONFIGS) pnpm-lock.yaml + @$(MAKE) -s node_modules + @rm -rf $(FRONTEND_DEST_ENTRIES) + @echo "Running vite build..." + @pnpm exec vite build + @touch $(FRONTEND_DEST) + +.PHONY: svg +svg: node_modules ## build svg files + rm -rf $(SVG_DEST_DIR) + node tools/generate-svg.ts + +.PHONY: svg-check +svg-check: svg + @git add $(SVG_DEST_DIRS) + @diff=$$(git diff --color=always --cached $(SVG_DEST_DIRS)); \ + if [ -n "$$diff" ]; then \ + echo "Please run 'make svg' and 'git add $(SVG_DEST_DIRS)' and commit the result:"; \ + printf "%s" "$${diff}"; \ + exit 1; \ + fi + +.PHONY: lockfile-check +lockfile-check: + pnpm install --frozen-lockfile + @diff=$$(git diff --color=always pnpm-lock.yaml); \ + if [ -n "$$diff" ]; then \ + echo "pnpm-lock.yaml is inconsistent with package.json"; \ + echo "Please run 'pnpm install --frozen-lockfile' and commit the result:"; \ + printf "%s" "$${diff}"; \ + exit 1; \ + fi + +.PHONY: generate-gitignore +generate-gitignore: ## update gitignore files + $(GO) run build/generate-gitignores.go + +.PHONY: generate-images +generate-images: | node_modules ## generate images + cd tools && node generate-images.ts $(TAGS) + +.PHONY: generate-codemirror-languages +generate-codemirror-languages: | node_modules ## generate codemirror languages + node tools/generate-codemirror-languages.ts + +.PHONY: generate-manpage +generate-manpage: ## generate manpage + @[ -f gitea ] || make backend + @mkdir -p man/man1/ man/man5 + @./gitea docs --man > man/man1/gitea.1 + @gzip -9 man/man1/gitea.1 && echo man/man1/gitea.1.gz created + @#TODO A small script that formats config-cheat-sheet.en-us.md nicely for use as a config man page + +# Disable parallel execution because it would break some targets that don't +# specify exact dependencies like 'backend' which does currently not depend +# on 'frontend' to enable Node.js-less builds from source tarballs. +.NOTPARALLEL: diff --git a/README.md b/README.md new file mode 100644 index 0000000..e581ea6 --- /dev/null +++ b/README.md @@ -0,0 +1,198 @@ +# Gitea + +[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly") +[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea") +[![](https://pkg.go.dev/badge/gitea.dev?status.svg)](https://pkg.go.dev/gitea.dev "GoDoc") +[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release") +[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source") +[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea") +[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT") +[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin") + +[繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md) + +## Purpose + +The goal of Gitea is to make the easiest, fastest, and most painless way of +setting up a self-hosted all-in-one software development service, +including Git hosting, code management, code review, issue tracking, project kanban, wiki, +team collaboration, package registry and CI/CD which can reuse GitHub Actions. + +As Gitea is written in Go, it works across **all** the platforms and +architectures that are supported by Go, including Linux, macOS, FreeBSD/OpenBSD and Windows +on x86, amd64, ARM, RISC-V 64 and PowerPC architectures. + +For online demonstrations, you can visit [demo.gitea.com](https://demo.gitea.com). + +For accessing free Gitea service (with a limited number of repositories), you can visit [gitea.com](https://gitea.com/user/login). + +To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com), +or use container (docker/podman/etc) to deploy on your own server with the [official image](https://hub.docker.com/r/gitea/gitea). + +## Documentation + +You can find comprehensive documentation on our official [documentation website](https://docs.gitea.com/). + +It includes installation, administration, usage, development, contributing guides, and more to help you get started and explore all features effectively. + +If you have any suggestions or would like to contribute to it, you can visit the [documentation repository](https://gitea.com/gitea/docs) + +## Building + +See [docs/build-setup.md](docs/build-setup.md) for prerequisites +and [docs/development.md](docs/development.md) for setting up a local development environment, linting, and testing. + +If you'd like to build from source or make a distribution package, see [docs/build-source.md](docs/build-source.md) for more information. + +After building, you can run `./gitea web` to start the server, or `./gitea help` to see all available commands. + +## Contributing + +Expected workflow is: Fork -> Patch -> Push -> Pull Request + +> [!NOTE] +> +> 1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.** +> 2. New to the codebase? The [development guide](docs/development.md) walks through setting up a local environment and building from source. +> 3. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks! + +## Translating + +[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com) + +Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language, ask one of the managers in the Crowdin project to add a new language there. + +You can also just create an issue for adding a language or ask on Discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. + +Get more information from [the translation section of our contributing guide](CONTRIBUTING.md#translation). + +## Official and Third-Party Projects + +We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/runner) for Gitea Action. + +We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more. + +## Communication + +[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea") + +If you have questions that are not covered by the [documentation](https://docs.gitea.com/), you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/). + +## Authors + +- [Maintainers](https://github.com/orgs/go-gitea/people) +- [Contributors](https://github.com/go-gitea/gitea/graphs/contributors) +- [Translators](options/locale/TRANSLATORS) + +## Backers + +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/gitea#backer)] + + + +## Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/gitea#sponsor)] + + + + + + + + + + + + +## FAQ + +**How do you pronounce Gitea?** + +Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea" with a hard g. + +**How do I configure Gitea?** + +For dynamic config options, you can change it on your admin panel's configuration section. + +For static config options, you can edit your `app.ini` file and restart the instance. +See [app.example.ini](https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini) or [configuration documentation](https://docs.gitea.com/administration/config-cheat-sheet) for more details. + +**Where can I find the security patches?** + +In the [release log](https://github.com/go-gitea/gitea/releases) or the [change log](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches. + +(more FAQs are listed in [FAQ documentation](https://docs.gitea.com/help/faq)) + +## License + +This project is licensed under the MIT License. +See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file +for the full license text. + +## Further information + +
+Looking for an overview of the interface? Check it out the screenshots! + +### Login/Register Page + +![Login](https://dl.gitea.com/screenshots/login.png) +![Register](https://dl.gitea.com/screenshots/register.png) + +### User Dashboard + +![Home](https://dl.gitea.com/screenshots/home.png) +![Issues](https://dl.gitea.com/screenshots/issues.png) +![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png) +![Milestones](https://dl.gitea.com/screenshots/milestones.png) + +### User Profile + +![Profile](https://dl.gitea.com/screenshots/user_profile.png) + +### Explore + +![Repos](https://dl.gitea.com/screenshots/explore_repos.png) +![Users](https://dl.gitea.com/screenshots/explore_users.png) +![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png) + +### Repository + +![Home](https://dl.gitea.com/screenshots/repo_home.png) +![Commits](https://dl.gitea.com/screenshots/repo_commits.png) +![Branches](https://dl.gitea.com/screenshots/repo_branches.png) +![Labels](https://dl.gitea.com/screenshots/repo_labels.png) +![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png) +![Releases](https://dl.gitea.com/screenshots/repo_releases.png) +![Tags](https://dl.gitea.com/screenshots/repo_tags.png) + +#### Repository Issue + +![List](https://dl.gitea.com/screenshots/repo_issues.png) +![Issue](https://dl.gitea.com/screenshots/repo_issue.png) + +#### Repository Pull Requests + +![List](https://dl.gitea.com/screenshots/repo_pull_requests.png) +![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png) +![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png) +![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png) + +#### Repository Actions + +![List](https://dl.gitea.com/screenshots/repo_actions.png) +![Details](https://dl.gitea.com/screenshots/repo_actions_run.png) + +#### Repository Activity + +![Activity](https://dl.gitea.com/screenshots/repo_activity.png) +![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png) +![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png) +![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png) + +### Organization + +![Home](https://dl.gitea.com/screenshots/org_home.png) + +
diff --git a/README.zh-cn.md b/README.zh-cn.md new file mode 100644 index 0000000..46c4db3 --- /dev/null +++ b/README.zh-cn.md @@ -0,0 +1,200 @@ +# Gitea + +[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly") +[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea") +[![](https://pkg.go.dev/badge/gitea.dev?status.svg)](https://pkg.go.dev/gitea.dev "GoDoc") +[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release") +[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source") +[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea") +[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT") +[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin") + +[English](./README.md) | [繁體中文](./README.zh-tw.md) + +## 目的 + +这个项目的目标是提供最简单、最快速、最无痛的方式来设置自托管的 Git 服务。 + +由于 Gitea 是用 Go 语言编写的,它可以在 Go 支持的所有平台和架构上运行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架构。这个项目自 2016 年 11 月从 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而来,但已经有了很多变化。 + +在线演示可以访问 [demo.gitea.com](https://demo.gitea.com)。 + +要访问免费的 Gitea 服务(有一定数量的仓库限制),可以访问 [gitea.com](https://gitea.com/user/login)。 + +要快速部署您自己的专用 Gitea 实例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。 + +## 文件 + +您可以在我们的官方 [文件网站](https://docs.gitea.com/) 上找到全面的文件。 + +它包括安装、管理、使用、开发、贡献指南等,帮助您快速入门并有效地探索所有功能。 + +如果您有任何建议或想要贡献,可以访问 [文件仓库](https://gitea.com/gitea/docs) + +## 构建 + +从源代码树的根目录运行: + + TAGS="bindata" make build + +`build` 目标分为两个子目标: + +- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定义。 +- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本以及 [pnpm](https://pnpm.io/installation)。 + +需要互联网连接来下载 go 和 npm 模块。从包含预构建前端文件的官方源代码压缩包构建时,不会触发 `frontend` 目标,因此可以在没有 Node.js 的情况下构建。 + +更多信息:https://docs.gitea.com/installation/install-from-source + +## 使用 + +构建后,默认情况下会在源代码树的根目录生成一个名为 `gitea` 的二进制文件。要运行它,请使用: + + ./gitea web + +> [!注意] +> 如果您对使用我们的 API 感兴趣,我们提供了实验性支持,并附有 [文件](https://docs.gitea.com/api)。 + +## 贡献 + +预期的工作流程是:Fork -> Patch -> Push -> Pull Request + +> [!注意] +> +> 1. **在开始进行 Pull Request 之前,您必须阅读 [贡献者指南](CONTRIBUTING.md)。** +> 2. 如果您在项目中发现了漏洞,请私下写信给 **security@gitea.io**。谢谢! + +## 翻译 + +[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com) + +翻译通过 [Crowdin](https://translate.gitea.com) 进行。如果您想翻译成新的语言,请在 Crowdin 项目中请求管理员添加新语言。 + +您也可以创建一个 issue 来添加语言,或者在 discord 的 #translation 频道上询问。如果您需要上下文或发现一些翻译问题,可以在字符串上留言或在 Discord 上询问。对于一般的翻译问题,文档中有一个部分。目前有点空,但我们希望随着问题的出现而填充它。 + +更多信息请参阅 [文件](https://docs.gitea.com/zh-cn/contributing/localization)。 + +## 官方和第三方项目 + +我们提供了一个官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一个名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一个 Gitea Action 的 [action runner](https://gitea.com/gitea/runner)。 + +我们在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 维护了一个 Gitea 相关项目的列表,您可以在那里发现更多的第三方项目,包括 SDK、插件、主题等。 + +## 通讯 + +[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea") + +如果您有任何文件未涵盖的问题,可以在我们的 [Discord 服务器](https://discord.gg/Gitea) 上与我们联系,或者在 [discourse 论坛](https://forum.gitea.com/) 上创建帖子。 + +## 作者 + +- [维护者](https://github.com/orgs/go-gitea/people) +- [贡献者](https://github.com/go-gitea/gitea/graphs/contributors) +- [翻译者](options/locale/TRANSLATORS) + +## 支持者 + +感谢所有支持者! 🙏 [[成为支持者](https://opencollective.com/gitea#backer)] + + + +## 赞助商 + +通过成为赞助商来支持这个项目。您的标志将显示在这里,并带有链接到您的网站。 [[成为赞助商](https://opencollective.com/gitea#sponsor)] + + + + + + + + + + + + +## 常见问题 + +**Gitea 怎么发音?** + +Gitea 的发音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一样,g 是硬音。 + +**为什么这个项目没有托管在 Gitea 实例上?** + +我们正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。 + +**在哪里可以找到安全补丁?** + +在 [发布日志](https://github.com/go-gitea/gitea/releases) 或 [变更日志](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索关键词 `SECURITY` 以找到安全补丁。 + +## 许可证 + +这个项目是根据 MIT 许可证授权的。 +请参阅 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以获取完整的许可证文本。 + +## 进一步信息 + +
+寻找界面概述?查看这里! + +### 登录/注册页面 + +![Login](https://dl.gitea.com/screenshots/login.png) +![Register](https://dl.gitea.com/screenshots/register.png) + +### 用户仪表板 + +![Home](https://dl.gitea.com/screenshots/home.png) +![Issues](https://dl.gitea.com/screenshots/issues.png) +![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png) +![Milestones](https://dl.gitea.com/screenshots/milestones.png) + +### 用户资料 + +![Profile](https://dl.gitea.com/screenshots/user_profile.png) + +### 探索 + +![Repos](https://dl.gitea.com/screenshots/explore_repos.png) +![Users](https://dl.gitea.com/screenshots/explore_users.png) +![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png) + +### 仓库 + +![Home](https://dl.gitea.com/screenshots/repo_home.png) +![Commits](https://dl.gitea.com/screenshots/repo_commits.png) +![Branches](https://dl.gitea.com/screenshots/repo_branches.png) +![Labels](https://dl.gitea.com/screenshots/repo_labels.png) +![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png) +![Releases](https://dl.gitea.com/screenshots/repo_releases.png) +![Tags](https://dl.gitea.com/screenshots/repo_tags.png) + +#### 仓库问题 + +![List](https://dl.gitea.com/screenshots/repo_issues.png) +![Issue](https://dl.gitea.com/screenshots/repo_issue.png) + +#### 仓库拉取请求 + +![List](https://dl.gitea.com/screenshots/repo_pull_requests.png) +![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png) +![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png) +![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png) + +#### 仓库操作 + +![List](https://dl.gitea.com/screenshots/repo_actions.png) +![Details](https://dl.gitea.com/screenshots/repo_actions_run.png) + +#### 仓库活动 + +![Activity](https://dl.gitea.com/screenshots/repo_activity.png) +![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png) +![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png) +![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png) + +### 组织 + +![Home](https://dl.gitea.com/screenshots/org_home.png) + +
diff --git a/README.zh-tw.md b/README.zh-tw.md new file mode 100644 index 0000000..2257ca1 --- /dev/null +++ b/README.zh-tw.md @@ -0,0 +1,200 @@ +# Gitea + +[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly") +[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea") +[![](https://pkg.go.dev/badge/gitea.dev?status.svg)](https://pkg.go.dev/gitea.dev "GoDoc") +[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release") +[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source") +[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea") +[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT") +[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin") + +[English](./README.md) | [简体中文](./README.zh-cn.md) + +## 目的 + +這個項目的目標是提供最簡單、最快速、最無痛的方式來設置自託管的 Git 服務。 + +由於 Gitea 是用 Go 語言編寫的,它可以在 Go 支援的所有平台和架構上運行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架構。這個項目自 2016 年 11 月從 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而來,但已經有了很多變化。 + +在線演示可以訪問 [demo.gitea.com](https://demo.gitea.com)。 + +要訪問免費的 Gitea 服務(有一定數量的倉庫限制),可以訪問 [gitea.com](https://gitea.com/user/login)。 + +要快速部署您自己的專用 Gitea 實例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 開始免費試用。 + +## 文件 + +您可以在我們的官方 [文件網站](https://docs.gitea.com/) 上找到全面的文件。 + +它包括安裝、管理、使用、開發、貢獻指南等,幫助您快速入門並有效地探索所有功能。 + +如果您有任何建議或想要貢獻,可以訪問 [文件倉庫](https://gitea.com/gitea/docs) + +## 構建 + +從源代碼樹的根目錄運行: + + TAGS="bindata" make build + +`build` 目標分為兩個子目標: + +- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定義。 +- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本以及 [pnpm](https://pnpm.io/installation)。 + +需要互聯網連接來下載 go 和 npm 模塊。從包含預構建前端文件的官方源代碼壓縮包構建時,不會觸發 `frontend` 目標,因此可以在沒有 Node.js 的情況下構建。 + +更多信息:https://docs.gitea.com/installation/install-from-source + +## 使用 + +構建後,默認情況下會在源代碼樹的根目錄生成一個名為 `gitea` 的二進制文件。要運行它,請使用: + + ./gitea web + +> [!注意] +> 如果您對使用我們的 API 感興趣,我們提供了實驗性支援,並附有 [文件](https://docs.gitea.com/api)。 + +## 貢獻 + +預期的工作流程是:Fork -> Patch -> Push -> Pull Request + +> [!注意] +> +> 1. **在開始進行 Pull Request 之前,您必須閱讀 [貢獻者指南](CONTRIBUTING.md)。** +> 2. 如果您在項目中發現了漏洞,請私下寫信給 **security@gitea.io**。謝謝! + +## 翻譯 + +[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com) + +翻譯通過 [Crowdin](https://translate.gitea.com) 進行。如果您想翻譯成新的語言,請在 Crowdin 項目中請求管理員添加新語言。 + +您也可以創建一個 issue 來添加語言,或者在 discord 的 #translation 頻道上詢問。如果您需要上下文或發現一些翻譯問題,可以在字符串上留言或在 Discord 上詢問。對於一般的翻譯問題,文檔中有一個部分。目前有點空,但我們希望隨著問題的出現而填充它。 + +更多信息請參閱 [文件](https://docs.gitea.com/zh-tw/contributing/localization)。 + +## 官方和第三方項目 + +我們提供了一個官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一個名為 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一個 Gitea Action 的 [action runner](https://gitea.com/gitea/runner)。 + +我們在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 維護了一個 Gitea 相關項目的列表,您可以在那裡發現更多的第三方項目,包括 SDK、插件、主題等。 + +## 通訊 + +[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea") + +如果您有任何文件未涵蓋的問題,可以在我們的 [Discord 服務器](https://discord.gg/Gitea) 上與我們聯繫,或者在 [discourse 論壇](https://forum.gitea.com/) 上創建帖子。 + +## 作者 + +- [維護者](https://github.com/orgs/go-gitea/people) +- [貢獻者](https://github.com/go-gitea/gitea/graphs/contributors) +- [翻譯者](options/locale/TRANSLATORS) + +## 支持者 + +感謝所有支持者! 🙏 [[成為支持者](https://opencollective.com/gitea#backer)] + + + +## 贊助商 + +通過成為贊助商來支持這個項目。您的標誌將顯示在這裡,並帶有鏈接到您的網站。 [[成為贊助商](https://opencollective.com/gitea#sponsor)] + + + + + + + + + + + + +## 常見問題 + +**Gitea 怎麼發音?** + +Gitea 的發音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一樣,g 是硬音。 + +**為什麼這個項目沒有託管在 Gitea 實例上?** + +我們正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。 + +**在哪裡可以找到安全補丁?** + +在 [發佈日誌](https://github.com/go-gitea/gitea/releases) 或 [變更日誌](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索關鍵詞 `SECURITY` 以找到安全補丁。 + +## 許可證 + +這個項目是根據 MIT 許可證授權的。 +請參閱 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以獲取完整的許可證文本。 + +## 進一步信息 + +
+尋找界面概述?查看這裡! + +### 登錄/註冊頁面 + +![Login](https://dl.gitea.com/screenshots/login.png) +![Register](https://dl.gitea.com/screenshots/register.png) + +### 用戶儀表板 + +![Home](https://dl.gitea.com/screenshots/home.png) +![Issues](https://dl.gitea.com/screenshots/issues.png) +![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png) +![Milestones](https://dl.gitea.com/screenshots/milestones.png) + +### 用戶資料 + +![Profile](https://dl.gitea.com/screenshots/user_profile.png) + +### 探索 + +![Repos](https://dl.gitea.com/screenshots/explore_repos.png) +![Users](https://dl.gitea.com/screenshots/explore_users.png) +![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png) + +### 倉庫 + +![Home](https://dl.gitea.com/screenshots/repo_home.png) +![Commits](https://dl.gitea.com/screenshots/repo_commits.png) +![Branches](https://dl.gitea.com/screenshots/repo_branches.png) +![Labels](https://dl.gitea.com/screenshots/repo_labels.png) +![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png) +![Releases](https://dl.gitea.com/screenshots/repo_releases.png) +![Tags](https://dl.gitea.com/screenshots/repo_tags.png) + +#### 倉庫問題 + +![List](https://dl.gitea.com/screenshots/repo_issues.png) +![Issue](https://dl.gitea.com/screenshots/repo_issue.png) + +#### 倉庫拉取請求 + +![List](https://dl.gitea.com/screenshots/repo_pull_requests.png) +![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png) +![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png) +![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png) + +#### 倉庫操作 + +![List](https://dl.gitea.com/screenshots/repo_actions.png) +![Details](https://dl.gitea.com/screenshots/repo_actions_run.png) + +#### 倉庫活動 + +![Activity](https://dl.gitea.com/screenshots/repo_activity.png) +![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png) +![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png) +![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png) + +### 組織 + +![Home](https://dl.gitea.com/screenshots/org_home.png) + +
diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..5195370 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,94 @@ +# Reporting security issues + +The Gitea maintainers take security seriously. + +If you discover a security issue, please bring it to their attention right away! + +Previous vulnerabilities are listed at https://about.gitea.com/security. + +## Supported Versions + +Security fixes follow our [support policy](docs/release-management.md#end-of-life-eol): only the most recent major release and the `main` branch receive them. + +Reports must be filed against a supported version. Before reporting, verify that the issue reproduces on a supported version; any report that only affects an unsupported version will be closed as out of scope. + +## Reporting a Vulnerability + +Please **DO NOT** file a public issue. Instead, report the vulnerability privately by opening a [GitHub security advisory](https://github.com/go-gitea/gitea/security/advisories/new), or optionally by sending your report to `security@gitea.io`. +If reporting via GitHub, open an advisory per found issue. You can group the found issues if they relate to a single area (same feature, same fault present in multiple routes) but do not group unrelated reports into one as it makes it much harder to assess and track for duplicates. +Do not use LLM to write the reports. It's meant for people to read so take your time and use your own words to write it. If you have used LLM to find the bug, be transparent about it and verify that it is not hallucination before submitting the report. + +If your report turns out to be a duplicate, you will be credited in the original report. + +## Protecting Security Information + +Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body. + +The PGP key is valid until July 23, 2027. + +``` +Key ID: 6FCD2D5B +Key Type: RSA +Expires: 7/23/2027 +Key Size: 4096/4096 +Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B +``` + +UserID: Gitea Security + +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGK1Z/4BEADFMqXA9DeeChmSxUjF0Be5sq99ZUhgrZjcN/wOzz0wuCJZC0l8 +4uC+d6mfv7JpJYlzYzOK97/x5UguKHkYNZ6mm1G9KHaXmoIBDLKDzfPdJopVNv2r +OajijaE0uMCnMjadlg5pbhMLRQG8a9J32yyaz7ZEAw72Ab31fvvcA53NkuqO4j2w +k7dtFQzhbNOYV0VffQT90WDZdalYHB1JHyEQ+70U9OjVD5ggNYSzX98Eu3Hjn7V7 +kqFrcAxr5TE1elf0IXJcuBJtFzQSTUGlQldKOHtGTGgGjj9r/FFAE5ioBgVD05bV +rEEgIMM/GqYaG/nbNpWE6P3mEc2Mnn3pZaRJL0LuF26TLjnqEcMMDp5iIhLdFzXR +3tMdtKgQFu+Mtzs3ipwWARYgHyU09RJsI2HeBx7RmZO/Xqrec763Z7zdJ7SpCn0Z +q+pHZl24JYR0Kf3T/ZiOC0cGd2QJqpJtg5J6S/OqfX9NH6MsCczO8pUC1N/aHH2X +CTme2nF56izORqDWKoiICteL3GpYsCV9nyCidcCmoQsS+DKvE86YhIhVIVWGRY2F +lzpAjnN9/KLtQroutrm+Ft0mdjDiJUeFVl1cOHDhoyfCsQh62HumoyZoZvqzQd6e +AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB +tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBAhsD +BQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEEPeA9HhRKfwaTWZncqv0jgW/N +LVsFAmpi3IkFCQmOqAsACgkQqv0jgW/NLVsgyxAAuTyj4+PImOFr7ZkEJcseWUy+ +gWQrJDagj+Ks2SKGneVroJ63q5Vngx44b7JNabauKel4IJJCScLdwA7k0DKFHPOM +rUS6aavW5+961CMQxJ13vWz7qxLxhI4FXle255xFSXgOo9T1W7+wtm/wtze8+sws +8XKAVBZGuVI1/KzVpE6bWxpIIYK79DC07NWGDAZzMbuO92TFHBRblXsicPWExBjC +/oFmT70YMcA0wOwU/YKYMg2eK2XYUr5+uYX6FnP7iUhDj1vZmwkF33Cj9v9D3Hv9 +NzAquj97uLht4wL2sqsIgZd4Vc37uipPDFaddzTb/lon7lPbJPMLObcd4HjSTkIo +nEMM0wdOQZU7mIIgRM9o3mr/gyVigl3uARmUKaP9PhBaOGTjkni0nrv90hSHBHeu +ayM76vl2tzAB2KpDxHIHbI2SEdytV28l/rxWrsbk2kG7s7QB6VKiThTatYevh1kg +6Ujgl2xNSntG6t6PWjQ65OMSq5Iiwe7RfOZkNmPRGP/rbzMbgWXB3iCR2gVQvsxA +ctYdDZdUYAeQ5g6xSKJBloygDQdLzk9m0hqNUyl6gxhRNRw69hqp9E4mxFF3L1Gi +Z2LspLX1ZZWMh0bp0AniWzZuzLuNI0KYhlok6fnshjidJ1qYM/lbz5eJTYWxoJZW +rczn8gB3+MZbULc/iiO5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen +T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN +GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46 +XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E +ZDMkn/O6xnwWNzy/+bpg43qH/Gk0eakOmz5NmQLRkV58SZLiJvuCUtkttf6CyhnX +03OcWaajv5W8qA39dBYQgDrrPbBWUnwfO3yMveqhwV4JjDoe8sPAyn1NwzakNYqP +RzsWyLrLS7R7J9s3FkZXhQw/QQcsaSMcGNQO047dm1P83N8JY5aEpiRo9zSWjoiw +qoExANj5lUTZPe8M50lI182FrcjAN7dClO3QI6pg7wy0erMxfFly3j8UQ91ysS9T +s+GsP9I3cmWWQcKYxWHtE8xTXnNCVPFZQj2nwhJzae8ypfOtulBRA3dUKWGKuDH/ +axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt +D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x +Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYCGwwWIQQ94D0eFEp/BpNZmdyq/SOBb80t +WwUCamLcngUJCY6oIAAKCRCq/SOBb80tWxilD/41xF1pwD2lGqjWLmlXcd0Ok5xm +67TSGiKiFZUgI3ftkRVlqmeJqjVmRjpc2dLfCuVBzlaP0Oyv5Jn+2CPqgmWIk5tw +pqlxqmsXiIZ9Uv2wWItxrXIwQ6DHc1qcIOu7feTnt1HVhxp2GqlXl3tA/+MTTDmn +4rqrHffI7ps9eCbK/SfSuoQk3+x/x4GPqWDbsNw1E9dDjPPhsKfdwzZ6dFR2Yhzu +2U0GDc2aeBA0Gjy6VFBj1ksmGxQCL3TqtGEHOonoPpTIjmDd35Xq6BSu119onOQS +THZ7+3JQRpizBVrIklKf33UOikZy9WHuffFddP46m8qC2Alh1aB7xcD8K3P9gzd5 +OJO9Zr8JR+rvi8eOsCHvJqGhe0JMMf9bgXKjQ1tMTDTe6r0Jnw7HbkDghjpj9xG7 +GYmDB0CL4kP5nAZ1OPfIasIu3Llhcy6mS52ECw0X6PNwUINwBn7F6EKAiciw8/OS +CXoGJDI5alwNhh+3DL4oBH1aq1lnmxmhFNpwpRDvtbRGmZeeASTeC4LMAtqP85Er +8W9ulIgQ4Ub8QuoxvE5hzWEB1QUkJgozI8vO4DoGlrsz7P8cPzrOT7WqUfUPTYE6 +oZEfEYDSj1IZHnPYPS+yLBJMJ/Lc1h8OCetmbJZx4QWaX3Fgar7Zks8FfJQOTN6i +HnOlrsaMDYNyO76uBQ== +=4kVJ +-----END PGP PUBLIC KEY BLOCK----- +``` + +Security reports are greatly appreciated and we will publicly thank you for it, although we keep your name confidential if you request it. diff --git a/assets/codemirror-languages.json b/assets/codemirror-languages.json new file mode 100644 index 0000000..359c0ac --- /dev/null +++ b/assets/codemirror-languages.json @@ -0,0 +1,1296 @@ +[ + { + "name": "APL", + "extensions": [ + "apl", + "dyalog" + ], + "filenames": [] + }, + { + "name": "ASN.1", + "extensions": [ + "asn", + "asn1" + ], + "filenames": [] + }, + { + "name": "Brainfuck", + "extensions": [ + "b", + "bf" + ], + "filenames": [] + }, + { + "name": "C", + "extensions": [ + "c", + "cats", + "h", + "idc" + ], + "filenames": [] + }, + { + "name": "C#", + "extensions": [ + "cs", + "cake", + "csx", + "linq" + ], + "filenames": [] + }, + { + "name": "C++", + "extensions": [ + "cpp", + "c++", + "cc", + "cp", + "cppm", + "cxx", + "h++", + "hh", + "hpp", + "hxx", + "inl", + "ipp", + "ixx", + "re", + "tcc", + "tpp", + "txx" + ], + "filenames": [] + }, + { + "name": "Clojure", + "extensions": [ + "clj", + "bb", + "boot", + "cl2", + "cljc", + "cljscm", + "cljx", + "hic" + ], + "filenames": [ + "riemann.config" + ] + }, + { + "name": "CMake", + "extensions": [ + "cmake" + ], + "filenames": [ + "CMakeLists.txt" + ] + }, + { + "name": "Cobol", + "extensions": [ + "cob", + "cbl", + "ccp", + "cobol", + "cpy" + ], + "filenames": [] + }, + { + "name": "CoffeeScript", + "extensions": [ + "coffee", + "_coffee", + "cake", + "cjsx", + "iced" + ], + "filenames": [ + "Cakefile" + ] + }, + { + "name": "Common Lisp", + "extensions": [ + "lisp", + "asd", + "cl", + "l", + "lsp", + "ny", + "podsl", + "sexp" + ], + "filenames": [] + }, + { + "name": "CQL", + "extensions": [ + "cql" + ], + "filenames": [] + }, + { + "name": "Crystal", + "extensions": [ + "cr" + ], + "filenames": [] + }, + { + "name": "CSS", + "extensions": [ + "css" + ], + "filenames": [] + }, + { + "name": "Cypher", + "extensions": [ + "cyp", + "cypher" + ], + "filenames": [] + }, + { + "name": "Cython", + "extensions": [ + "pyx", + "pxd", + "pxi" + ], + "filenames": [] + }, + { + "name": "D", + "extensions": [ + "d", + "di" + ], + "filenames": [] + }, + { + "name": "Dart", + "extensions": [ + "dart" + ], + "filenames": [] + }, + { + "name": "diff", + "extensions": [ + "diff", + "patch" + ], + "filenames": [] + }, + { + "name": "Dylan", + "extensions": [ + "dylan", + "dyl", + "intr", + "lid" + ], + "filenames": [] + }, + { + "name": "EBNF", + "extensions": [ + "ebnf" + ], + "filenames": [] + }, + { + "name": "ECL", + "extensions": [ + "ecl", + "eclxml" + ], + "filenames": [] + }, + { + "name": "edn", + "extensions": [ + "edn" + ], + "filenames": [] + }, + { + "name": "Eiffel", + "extensions": [ + "e" + ], + "filenames": [] + }, + { + "name": "Elm", + "extensions": [ + "elm" + ], + "filenames": [] + }, + { + "name": "Erlang", + "extensions": [ + "erl", + "app", + "es", + "escript", + "hrl", + "xrl", + "yrl" + ], + "filenames": [ + "Emakefile", + "rebar.config", + "rebar.config.lock", + "rebar.lock" + ] + }, + { + "name": "F#", + "extensions": [ + "fs", + "fsi", + "fsx" + ], + "filenames": [] + }, + { + "name": "Factor", + "extensions": [ + "factor" + ], + "filenames": [ + ".factor-boot-rc", + ".factor-rc" + ] + }, + { + "name": "Forth", + "extensions": [ + "fth", + "4th", + "forth", + "fr", + "frt" + ], + "filenames": [] + }, + { + "name": "Fortran", + "extensions": [ + "f", + "f77", + "for", + "fpp" + ], + "filenames": [] + }, + { + "name": "Gherkin", + "extensions": [ + "feature", + "story" + ], + "filenames": [] + }, + { + "name": "Go", + "extensions": [ + "go" + ], + "filenames": [] + }, + { + "name": "Groovy", + "extensions": [ + "groovy", + "grt", + "gtpl", + "gvy" + ], + "filenames": [ + "Jenkinsfile" + ] + }, + { + "name": "Haskell", + "extensions": [ + "hs", + "hs-boot", + "hsc" + ], + "filenames": [] + }, + { + "name": "Haxe", + "extensions": [ + "hx", + "hxsl" + ], + "filenames": [] + }, + { + "name": "HTML", + "extensions": [ + "html", + "hta", + "htm", + "xht", + "xhtml" + ], + "filenames": [] + }, + { + "name": "HTTP", + "extensions": [ + "http" + ], + "filenames": [] + }, + { + "name": "HXML", + "extensions": [ + "hxml" + ], + "filenames": [] + }, + { + "name": "IDL", + "extensions": [ + "pro", + "dlm" + ], + "filenames": [] + }, + { + "name": "Java", + "extensions": [ + "java", + "jav", + "jsh" + ], + "filenames": [] + }, + { + "name": "JavaScript", + "extensions": [ + "js", + "_js", + "bones", + "cjs", + "es", + "es6", + "frag", + "gs", + "jake", + "javascript", + "jsb", + "jscad", + "jsfl", + "jslib", + "jsm", + "jspre", + "jss", + "mjs", + "njs", + "pac", + "sjs", + "ssjs", + "xsjs", + "xsjslib" + ], + "filenames": [ + "Jakefile" + ] + }, + { + "name": "Jinja", + "extensions": [ + "jinja", + "j2", + "jinja2" + ], + "filenames": [] + }, + { + "name": "JSON", + "extensions": [ + "json", + "4DForm", + "4DProject", + "avsc", + "geojson", + "gltf", + "har", + "ice", + "JSON-tmLanguage", + "jsonl", + "mcmeta", + "sarif", + "slnlaunch", + "tact", + "tfstate", + "topojson", + "webapp", + "webmanifest", + "yy", + "yyp" + ], + "filenames": [ + ".all-contributorsrc", + ".arcconfig", + ".auto-changelog", + ".c8rc", + ".htmlhintrc", + ".imgbotconfig", + ".nycrc", + ".releaserc", + ".secrets.baseline", + ".tern-config", + ".tern-project", + ".watchmanconfig", + "MODULE.bazel.lock", + "Package.resolved", + "Pipfile.lock", + "bun.lock", + "composer.lock", + "deno.lock", + "flake.lock", + "mcmod.info" + ] + }, + { + "name": "JSON-LD", + "extensions": [ + "jsonld" + ], + "filenames": [] + }, + { + "name": "Julia", + "extensions": [ + "jl" + ], + "filenames": [] + }, + { + "name": "Kotlin", + "extensions": [ + "kt", + "ktm", + "kts" + ], + "filenames": [] + }, + { + "name": "LaTeX", + "extensions": [ + "tex", + "aux", + "bbx", + "cbx", + "cls", + "dtx", + "ins", + "lbx", + "ltx", + "mkii", + "mkiv", + "mkvi", + "sty", + "toc" + ], + "filenames": [] + }, + { + "name": "LESS", + "extensions": [ + "less" + ], + "filenames": [] + }, + { + "name": "Liquid", + "extensions": [ + "liquid" + ], + "filenames": [] + }, + { + "name": "LiveScript", + "extensions": [ + "ls", + "_ls" + ], + "filenames": [ + "Slakefile" + ] + }, + { + "name": "Lua", + "extensions": [ + "lua", + "nse", + "p8", + "pd_lua", + "rbxs", + "rockspec", + "wlua" + ], + "filenames": [ + ".luacheckrc" + ] + }, + { + "name": "Modelica", + "extensions": [ + "mo" + ], + "filenames": [] + }, + { + "name": "Nginx", + "extensions": [ + "nginx", + "nginxconf", + "vhost" + ], + "filenames": [ + "nginx.conf" + ] + }, + { + "name": "NSIS", + "extensions": [ + "nsi", + "nsh" + ], + "filenames": [] + }, + { + "name": "Objective-C", + "extensions": [], + "filenames": [] + }, + { + "name": "Objective-C++", + "extensions": [ + "mm" + ], + "filenames": [] + }, + { + "name": "OCaml", + "extensions": [ + "ml", + "eliom", + "eliomi", + "ml4", + "mli", + "mll", + "mly" + ], + "filenames": [] + }, + { + "name": "Oz", + "extensions": [ + "oz" + ], + "filenames": [] + }, + { + "name": "Pascal", + "extensions": [ + "pas", + "dfm", + "dpr", + "lpr", + "pascal" + ], + "filenames": [] + }, + { + "name": "Perl", + "extensions": [ + "pl", + "al", + "perl", + "ph", + "plx", + "pm", + "psgi", + "t" + ], + "filenames": [ + ".latexmkrc", + "Makefile.PL", + "Rexfile", + "ack", + "cpanfile", + "latexmkrc" + ] + }, + { + "name": "PHP", + "extensions": [ + "php", + "aw", + "ctp", + "php3", + "php4", + "php5", + "phps", + "phpt" + ], + "filenames": [ + ".php", + ".php_cs", + ".php_cs.dist", + "Phakefile" + ] + }, + { + "name": "PLSQL", + "extensions": [ + "pls", + "bdy", + "ddl", + "fnc", + "pck", + "pkb", + "pks", + "plb", + "plsql", + "prc", + "spc", + "tpb", + "tps", + "trg", + "vw" + ], + "filenames": [] + }, + { + "name": "PowerShell", + "extensions": [ + "ps1", + "psd1", + "psm1" + ], + "filenames": [] + }, + { + "name": "Properties files", + "extensions": [ + "ini", + "cnf", + "container", + "dof", + "lektorproject", + "mount", + "network", + "prefs", + "properties", + "service", + "socket", + "target", + "timer", + "url", + "conf" + ], + "filenames": [ + ".buckconfig", + ".coveragerc", + ".flake8", + ".gitreview", + ".pylintrc", + "HOSTS", + "buildozer.spec", + "hosts", + "pylintrc", + "vlcrc", + ".editorconfig", + ".gitconfig", + ".npmrc" + ] + }, + { + "name": "ProtoBuf", + "extensions": [ + "proto" + ], + "filenames": [] + }, + { + "name": "Pug", + "extensions": [ + "jade", + "pug" + ], + "filenames": [] + }, + { + "name": "Puppet", + "extensions": [ + "pp" + ], + "filenames": [ + "Modulefile" + ] + }, + { + "name": "Python", + "extensions": [ + "py", + "gyp", + "gypi", + "lmi", + "py3", + "pyde", + "pyi", + "pyp", + "pyt", + "pyw", + "rpy", + "tac", + "wsgi", + "xpy" + ], + "filenames": [ + ".gclient", + "DEPS", + "SConscript", + "SConstruct", + "wscript", + "Snakefile" + ] + }, + { + "name": "Q", + "extensions": [ + "q" + ], + "filenames": [] + }, + { + "name": "R", + "extensions": [ + "r", + "rd", + "rhistory", + "rsx" + ], + "filenames": [ + ".Rapp.history", + ".Rhistory", + ".Rprofile", + "expr-dist" + ] + }, + { + "name": "RPM Spec", + "extensions": [ + "spec" + ], + "filenames": [] + }, + { + "name": "Ruby", + "extensions": [ + "rb", + "builder", + "eye", + "gemspec", + "god", + "jbuilder", + "mspec", + "pluginspec", + "podspec", + "prawn", + "rabl", + "rake", + "rbi", + "rbuild", + "rbw", + "rbx", + "ru", + "ruby", + "thor", + "watchr" + ], + "filenames": [ + ".irbrc", + ".pryrc", + ".simplecov", + "Appraisals", + "Berksfile", + "Brewfile", + "Buildfile", + "Capfile", + "Dangerfile", + "Deliverfile", + "Fastfile", + "Gemfile", + "Guardfile", + "Jarfile", + "Mavenfile", + "Podfile", + "Puppetfile", + "Rakefile", + "Snapfile", + "Steepfile", + "Thorfile", + "Vagrantfile", + "buildfile" + ] + }, + { + "name": "Rust", + "extensions": [ + "rs" + ], + "filenames": [] + }, + { + "name": "SAS", + "extensions": [ + "sas" + ], + "filenames": [] + }, + { + "name": "Sass", + "extensions": [ + "sass" + ], + "filenames": [] + }, + { + "name": "Scala", + "extensions": [ + "scala", + "kojo", + "sbt", + "sc" + ], + "filenames": [] + }, + { + "name": "Scheme", + "extensions": [ + "scm", + "sch", + "sld", + "sls", + "sps", + "ss" + ], + "filenames": [] + }, + { + "name": "SCSS", + "extensions": [ + "scss" + ], + "filenames": [] + }, + { + "name": "Shell", + "extensions": [ + "sh", + "bash", + "bats", + "command", + "ksh", + "pacscript", + "sbatch", + "slurm", + "tmux", + "tool", + "trigger", + "zsh", + "zsh-theme" + ], + "filenames": [ + ".bash_aliases", + ".bash_functions", + ".bash_history", + ".bash_logout", + ".bash_profile", + ".bashrc", + ".cshrc", + ".envrc", + ".flaskenv", + ".kshrc", + ".login", + ".profile", + ".tmux.conf", + ".xinitrc", + ".xsession", + ".zlogin", + ".zlogout", + ".zprofile", + ".zshenv", + ".zshrc", + "9fs", + "PKGBUILD", + "bash_aliases", + "bash_logout", + "bash_profile", + "bashrc", + "cshrc", + "gradlew", + "kshrc", + "login", + "man", + "mvnw", + "profile", + "tmux.conf", + "xinitrc", + "xsession", + "zlogin", + "zlogout", + "zprofile", + "zshenv", + "zshrc" + ] + }, + { + "name": "Sieve", + "extensions": [ + "sieve" + ], + "filenames": [] + }, + { + "name": "Smalltalk", + "extensions": [ + "st" + ], + "filenames": [] + }, + { + "name": "SPARQL", + "extensions": [ + "sparql", + "rq" + ], + "filenames": [] + }, + { + "name": "SQL", + "extensions": [ + "sql", + "ddl", + "mysql", + "prc", + "tab", + "udf", + "viw" + ], + "filenames": [] + }, + { + "name": "Squirrel", + "extensions": [ + "nut" + ], + "filenames": [] + }, + { + "name": "Stylus", + "extensions": [ + "styl" + ], + "filenames": [] + }, + { + "name": "Swift", + "extensions": [ + "swift" + ], + "filenames": [] + }, + { + "name": "SystemVerilog", + "extensions": [ + "sv", + "svh", + "vh" + ], + "filenames": [] + }, + { + "name": "Tcl", + "extensions": [ + "tcl", + "adp", + "sdc", + "tm", + "xdc" + ], + "filenames": [ + "owh", + "starfield" + ] + }, + { + "name": "Textile", + "extensions": [ + "textile" + ], + "filenames": [] + }, + { + "name": "TOML", + "extensions": [ + "toml" + ], + "filenames": [ + "Cargo.lock", + "Cargo.toml.orig", + "Gopkg.lock", + "Pipfile", + "mise.local.lock", + "mise.lock", + "pdm.lock", + "poetry.lock", + "uv.lock" + ] + }, + { + "name": "TSX", + "extensions": [ + "tsx" + ], + "filenames": [] + }, + { + "name": "Turtle", + "extensions": [ + "ttl" + ], + "filenames": [] + }, + { + "name": "TypeScript", + "extensions": [ + "ts", + "cts", + "mts" + ], + "filenames": [] + }, + { + "name": "VBScript", + "extensions": [ + "vbs" + ], + "filenames": [] + }, + { + "name": "Verilog", + "extensions": [ + "veo" + ], + "filenames": [] + }, + { + "name": "VHDL", + "extensions": [ + "vhdl", + "vhd", + "vhf", + "vhi", + "vho", + "vhs", + "vht", + "vhw" + ], + "filenames": [] + }, + { + "name": "Vue", + "extensions": [ + "vue" + ], + "filenames": [] + }, + { + "name": "WebAssembly", + "extensions": [ + "wast", + "wat" + ], + "filenames": [] + }, + { + "name": "XML", + "extensions": [ + "xml", + "adml", + "admx", + "ant", + "axaml", + "axml", + "builds", + "ccproj", + "ccxml", + "clixml", + "cproject", + "cscfg", + "csdef", + "csl", + "csproj", + "ct", + "depproj", + "dita", + "ditamap", + "ditaval", + "dotsettings", + "filters", + "fsproj", + "fxml", + "glade", + "gml", + "gmx", + "gpx", + "grxml", + "gst", + "hzp", + "icls", + "iml", + "ivy", + "jelly", + "jsproj", + "kml", + "launch", + "mdpolicy", + "meta4", + "mjml", + "mod", + "mojo", + "mxml", + "natvis", + "ncl", + "ndproj", + "nproj", + "nuspec", + "odd", + "osm", + "pkgproj", + "pluginspec", + "proj", + "props", + "ps1xml", + "psc1", + "pt", + "pubxml", + "qhelp", + "rbxmx", + "rdf", + "res", + "resx", + "rss", + "sch", + "scxml", + "sfproj", + "shproj", + "slnx", + "srdf", + "storyboard", + "sublime-snippet", + "sw", + "targets", + "tml", + "typ", + "ui", + "urdf", + "ux", + "vbproj", + "vcxproj", + "vsixmanifest", + "vssettings", + "vstemplate", + "vxml", + "wixproj", + "workflow", + "wsdl", + "wsf", + "wxi", + "wxl", + "wxs", + "x3d", + "xacro", + "xaml", + "xib", + "xlf", + "xliff", + "xmi", + "xmp", + "xproj", + "xsd", + "xspec", + "xul", + "zcml" + ], + "filenames": [ + ".classpath", + ".cproject", + ".project", + "App.config", + "NuGet.config", + "Settings.StyleCop", + "Web.Debug.config", + "Web.Release.config", + "Web.config", + "packages.config" + ] + }, + { + "name": "XQuery", + "extensions": [ + "xquery", + "xq", + "xql", + "xqm", + "xqy" + ], + "filenames": [] + }, + { + "name": "YAML", + "extensions": [ + "yml", + "mir", + "reek", + "rviz", + "sublime-syntax", + "syntax", + "yaml", + "yaml-tmlanguage" + ], + "filenames": [ + ".clang-format", + ".clang-tidy", + ".clangd", + ".eslintrc", + ".gemrc", + ".releaserc", + "CITATION.cff", + "glide.lock", + "pixi.lock", + "yarn.lock" + ] + } +] diff --git a/assets/favicon.svg b/assets/favicon.svg new file mode 100644 index 0000000..9df6b83 --- /dev/null +++ b/assets/favicon.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 0000000..9df6b83 --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/assets/misspellings.csv b/assets/misspellings.csv new file mode 100644 index 0000000..645fb78 --- /dev/null +++ b/assets/misspellings.csv @@ -0,0 +1,21 @@ +acounts,accounts +canidate,candidate +comfirm,confirm +converage,coverage +currrently,currently +delimeter,delimiter +differrent,different +exclusing,excluding +finshed,finished +formated,formatted +inderect,indirect +insuficient,insufficient +likly,likely +mergable,mergeable +overrided,overridden +priortized,prioritized +registeration,registration +reuqest,request +reviwer,reviewer +superceded,superseded +underlaying,underlying diff --git a/build/generate-bindata.go b/build/generate-bindata.go new file mode 100644 index 0000000..3e14eba --- /dev/null +++ b/build/generate-bindata.go @@ -0,0 +1,27 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build ignore + +package main + +import ( + "fmt" + "os" + + "gitea.dev/modules/assetfs" +) + +func main() { + if len(os.Args) != 3 { + fmt.Println("usage: ./generate-bindata {local-directory} {bindata-filename}") + os.Exit(1) + } + + dir, filename := os.Args[1], os.Args[2] + fmt.Printf("generating bindata for %s to %s\n", dir, filename) + if err := assetfs.GenerateEmbedBindata(dir, filename); err != nil { + fmt.Printf("failed: %s\n", err.Error()) + os.Exit(1) + } +} diff --git a/build/generate-emoji.go b/build/generate-emoji.go new file mode 100644 index 0000000..7d38a4c --- /dev/null +++ b/build/generate-emoji.go @@ -0,0 +1,219 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Copyright 2015 Kenneth Shaw +// SPDX-License-Identifier: MIT + +//go:build ignore + +package main + +import ( + "flag" + "fmt" + "go/format" + "io" + "log" + "net/http" + "os" + "regexp" + "sort" + "strconv" + "strings" + "unicode/utf8" + + "gitea.dev/modules/json" +) + +const ( + gemojiURL = "https://raw.githubusercontent.com/rhysd/gemoji/537ff2d7e0496e9964824f7f73ec7ece88c9765a/db/emoji.json" + maxUnicodeVersion = 16 +) + +var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out") + +// Gemoji is a set of emoji data. +type Gemoji []Emoji + +// Emoji represents a single emoji and associated data. +type Emoji struct { + Emoji string `json:"emoji"` + Description string `json:"description,omitempty"` + Aliases []string `json:"aliases"` + UnicodeVersion string `json:"unicode_version,omitempty"` + SkinTones bool `json:"skin_tones,omitempty"` +} + +// Don't include some fields in JSON +func (e Emoji) MarshalJSON() ([]byte, error) { + type emoji Emoji + x := emoji(e) + x.UnicodeVersion = "" + x.Description = "" + x.SkinTones = false + return json.Marshal(x) +} + +func main() { + flag.Parse() + + // generate data + buf, err := generate() + if err != nil { + log.Fatalf("generate err: %v", err) + } + + // write + err = os.WriteFile(*flagOut, buf, 0o644) + if err != nil { + log.Fatalf("WriteFile err: %v", err) + } +} + +var replacer = strings.NewReplacer( + "main.Gemoji", "Gemoji", + "main.Emoji", "\n", + "}}", "},\n}", + ", Description:", ", ", + ", Aliases:", ", ", + ", UnicodeVersion:", ", ", + ", SkinTones:", ", ", +) + +var emojiRE = regexp.MustCompile(`\{Emoji:"([^"]*)"`) + +func generate() ([]byte, error) { + // load gemoji data + res, err := http.Get(gemojiURL) + if err != nil { + return nil, err + } + defer res.Body.Close() + + // read all + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + // unmarshal + var data Gemoji + err = json.Unmarshal(body, &data) + if err != nil { + return nil, err + } + + skinTones := make(map[string]string) + + skinTones["\U0001f3fb"] = "Light Skin Tone" + skinTones["\U0001f3fc"] = "Medium-Light Skin Tone" + skinTones["\U0001f3fd"] = "Medium Skin Tone" + skinTones["\U0001f3fe"] = "Medium-Dark Skin Tone" + skinTones["\U0001f3ff"] = "Dark Skin Tone" + + var tmp Gemoji + + // filter out emoji that require greater than max unicode version + for i := range data { + val, _ := strconv.ParseFloat(data[i].UnicodeVersion, 64) + if int(val) <= maxUnicodeVersion { + tmp = append(tmp, data[i]) + } + } + data = tmp + + sort.Slice(data, func(i, j int) bool { + return data[i].Aliases[0] < data[j].Aliases[0] + }) + + aliasMap := make(map[string]int, len(data)) + + for i, e := range data { + if e.Emoji == "" || len(e.Aliases) == 0 { + continue + } + for _, a := range e.Aliases { + if a == "" { + continue + } + aliasMap[a] = i + } + } + + // gitea customizations + i, ok := aliasMap["tada"] + if ok { + data[i].Aliases = append(data[i].Aliases, "hooray") + } + i, ok = aliasMap["laughing"] + if ok { + data[i].Aliases = append(data[i].Aliases, "laugh") + } + + // write a JSON file to use with tribute (write before adding skin tones since we can't support them there yet) + file, _ := json.MarshalIndent(data, "", " ") + _ = os.WriteFile("assets/emoji.json", append(file, '\n'), 0o644) + + // Add skin tones to emoji that support it + var ( + s []string + newEmoji string + newDescription string + newData Emoji + ) + + for i := range data { + if data[i].SkinTones { + for k, v := range skinTones { + s = strings.Split(data[i].Emoji, "") + + if utf8.RuneCountInString(data[i].Emoji) == 1 { + s = append(s, k) + } else { + // insert into slice after first element because all emoji that support skin tones + // have that modifier placed at this spot + s = append(s, "") + copy(s[2:], s[1:]) + s[1] = k + } + + newEmoji = strings.Join(s, "") + newDescription = data[i].Description + ": " + v + newAlias := data[i].Aliases[0] + "_" + strings.ReplaceAll(v, " ", "_") + + newData = Emoji{newEmoji, newDescription, []string{newAlias}, "12.0", false} + data = append(data, newData) + } + } + } + + sort.Slice(data, func(i, j int) bool { + return data[i].Aliases[0] < data[j].Aliases[0] + }) + + // add header + str := replacer.Replace(fmt.Sprintf(hdr, gemojiURL, data)) + + // change the format of the unicode string + str = emojiRE.ReplaceAllStringFunc(str, func(s string) string { + var err error + s, err = strconv.Unquote(s[len("{Emoji:"):]) + if err != nil { + panic(err) + } + return "{" + strconv.QuoteToASCII(s) + }) + + // format + return format.Source([]byte(str)) +} + +const hdr = ` +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + + +package emoji + +// Code generated by build/generate-emoji.go. DO NOT EDIT. +// Sourced from %s +var GemojiData = %#v +` diff --git a/build/generate-gitignores.go b/build/generate-gitignores.go new file mode 100644 index 0000000..11af4eb --- /dev/null +++ b/build/generate-gitignores.go @@ -0,0 +1,129 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build ignore + +package main + +import ( + "archive/tar" + "compress/gzip" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "path" + "path/filepath" + "strings" + + "gitea.dev/modules/util" +) + +func main() { + var ( + prefix = "gitea-gitignore" + url = "https://api.github.com/repos/github/gitignore/tarball" + githubApiToken = "" + githubUsername = "" + destination = "" + ) + + flag.StringVar(&destination, "dest", "options/gitignore/", "destination for the gitignores") + flag.StringVar(&githubUsername, "username", "", "github username") + flag.StringVar(&githubApiToken, "token", "", "github api token") + flag.Parse() + + file, err := os.CreateTemp(os.TempDir(), prefix) + if err != nil { + log.Fatalf("Failed to create temp file. %s", err) + } + + defer util.Remove(file.Name()) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Fatalf("Failed to download archive. %s", err) + } + + if len(githubApiToken) > 0 && len(githubUsername) > 0 { + req.SetBasicAuth(githubUsername, githubApiToken) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Fatalf("Failed to download archive. %s", err) + } + defer resp.Body.Close() + + if _, err := io.Copy(file, resp.Body); err != nil { + log.Fatalf("Failed to copy archive to file. %s", err) + } + + if _, err := file.Seek(0, 0); err != nil { + log.Fatalf("Failed to reset seek on archive. %s", err) + } + + gz, err := gzip.NewReader(file) + if err != nil { + log.Fatalf("Failed to gunzip the archive. %s", err) + } + + tr := tar.NewReader(gz) + + filesToCopy := make(map[string]string, 0) + + for { + hdr, err := tr.Next() + + if err == io.EOF { + break + } + + if err != nil { + log.Fatalf("Failed to iterate archive. %s", err) + } + + if filepath.Ext(hdr.Name) != ".gitignore" { + continue + } + + if hdr.Typeflag == tar.TypeSymlink { + fmt.Printf("Found symlink %s -> %s\n", hdr.Name, hdr.Linkname) + filesToCopy[strings.TrimSuffix(filepath.Base(hdr.Name), ".gitignore")] = strings.TrimSuffix(filepath.Base(hdr.Linkname), ".gitignore") + continue + } + + out, err := os.Create(path.Join(destination, strings.TrimSuffix(filepath.Base(hdr.Name), ".gitignore"))) + if err != nil { + log.Fatalf("Failed to create new file. %s", err) + } + + defer out.Close() + + if _, err := io.Copy(out, tr); err != nil { + log.Fatalf("Failed to write new file. %s", err) + } else { + fmt.Printf("Written %s\n", out.Name()) + } + } + + for dst, src := range filesToCopy { + // Read all content of src to data + src = path.Join(destination, src) + data, err := os.ReadFile(src) + if err != nil { + log.Fatalf("Failed to read src file. %s", err) + } + // Write data to dst + dst = path.Join(destination, dst) + err = os.WriteFile(dst, data, 0o644) + if err != nil { + log.Fatalf("Failed to write new file. %s", err) + } + fmt.Printf("Written (copy of %s) %s\n", src, dst) + } + + fmt.Println("Done") +} diff --git a/build/generate-go-licenses.go b/build/generate-go-licenses.go new file mode 100644 index 0000000..a4aa5b0 --- /dev/null +++ b/build/generate-go-licenses.go @@ -0,0 +1,239 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build ignore + +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "slices" + "sort" + "strings" +) + +// regexp is based on go-license, excluding README and NOTICE +// https://github.com/google/go-licenses/blob/master/licenses/find.go +// also defined in vite.config.ts +var licenseRe = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING).*$`) + +// primaryLicenseRe matches exact primary license filenames without suffixes. +// When a directory has both primary and variant files (e.g. LICENSE and +// LICENSE.docs), only the primary files are kept. +var primaryLicenseRe = regexp.MustCompile(`^(?i)(LICEN[SC]E|COPYING)$`) + +// ignoredNames are LicenseEntry.Name values to exclude from the output. +var ignoredNames = map[string]bool{ + "gitea.dev": true, + "gitea.dev/options/license": true, +} + +var excludedExt = map[string]bool{ + ".gitignore": true, + ".go": true, + ".mod": true, + ".sum": true, + ".toml": true, + ".yaml": true, + ".yml": true, +} + +type ModuleInfo struct { + Path string + Dir string + PkgDirs []string // directories of packages imported from this module +} + +type LicenseEntry struct { + Name string `json:"name"` + Path string `json:"path"` + LicenseText string `json:"licenseText"` +} + +// getModules returns all dependency modules with their local directory paths +// and the package directories used from each module. +func getModules(goCmd string) []ModuleInfo { + cmd := exec.Command(goCmd, "list", "-deps", "-f", + "{{if .Module}}{{.Module.Path}}\t{{.Module.Dir}}\t{{.Dir}}{{end}}", "./...") + cmd.Stderr = os.Stderr + // Use GOOS=linux with CGO to ensure we capture all platform-specific + // dependencies, matching the CI environment. + cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=amd64", "CGO_ENABLED=1") + output, err := cmd.Output() + if err != nil { + fmt.Fprintf(os.Stderr, "failed to run 'go list -deps': %v\n", err) + os.Exit(1) + } + + var modules []ModuleInfo + seen := make(map[string]int) // module path -> index in modules + for _, line := range strings.Split(string(output), "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + parts := strings.Split(line, "\t") + if len(parts) != 3 { + continue + } + modPath, modDir, pkgDir := parts[0], parts[1], parts[2] + if idx, ok := seen[modPath]; ok { + modules[idx].PkgDirs = append(modules[idx].PkgDirs, pkgDir) + } else { + seen[modPath] = len(modules) + modules = append(modules, ModuleInfo{ + Path: modPath, + Dir: modDir, + PkgDirs: []string{pkgDir}, + }) + } + } + return modules +} + +// findLicenseFiles scans a module's root directory and its used package +// directories for license files. It also walks up from each package directory +// to the module root, scanning intermediate directories. Subdirectory licenses +// are only included if their text differs from the root license(s). +func findLicenseFiles(mod ModuleInfo) []LicenseEntry { + var entries []LicenseEntry + seenTexts := make(map[string]bool) + + // First, collect root-level license files. + entries = append(entries, scanDirForLicenses(mod.Dir, mod.Path, "")...) + for _, e := range entries { + seenTexts[e.LicenseText] = true + } + + // Then check each package directory and all intermediate parent directories + // up to the module root for license files with unique text. + seenDirs := map[string]bool{mod.Dir: true} + for _, pkgDir := range mod.PkgDirs { + for dir := pkgDir; dir != mod.Dir && strings.HasPrefix(dir, mod.Dir); dir = filepath.Dir(dir) { + if seenDirs[dir] { + continue + } + seenDirs[dir] = true + for _, e := range scanDirForLicenses(dir, mod.Path, mod.Dir) { + if !seenTexts[e.LicenseText] { + seenTexts[e.LicenseText] = true + entries = append(entries, e) + } + } + } + } + return entries +} + +// scanDirForLicenses reads a single directory for license files and returns entries. +// If moduleRoot is non-empty, paths are made relative to it. +func scanDirForLicenses(dir, modulePath, moduleRoot string) []LicenseEntry { + dirEntries, err := os.ReadDir(dir) + if err != nil { + return nil + } + + var entries []LicenseEntry + for _, entry := range dirEntries { + if entry.IsDir() { + continue + } + name := entry.Name() + if !licenseRe.MatchString(name) { + continue + } + if excludedExt[strings.ToLower(filepath.Ext(name))] { + continue + } + + content, err := os.ReadFile(filepath.Join(dir, name)) + if err != nil { + continue + } + + entryName := modulePath + entryPath := modulePath + "/" + name + if moduleRoot != "" { + rel, _ := filepath.Rel(moduleRoot, dir) + if rel != "." { + relSlash := filepath.ToSlash(rel) + entryName = modulePath + "/" + relSlash + entryPath = modulePath + "/" + relSlash + "/" + name + } + } + + entries = append(entries, LicenseEntry{ + Name: entryName, + Path: entryPath, + LicenseText: string(content), + }) + } + + // When multiple license files exist, prefer primary files (e.g. LICENSE) + // over variants with suffixes (e.g. LICENSE.docs, LICENSE-2.0.txt). + // If no primary file exists, keep only the first variant. + if len(entries) > 1 { + var primary []LicenseEntry + for _, e := range entries { + fileName := e.Path[strings.LastIndex(e.Path, "/")+1:] + if primaryLicenseRe.MatchString(fileName) { + primary = append(primary, e) + } + } + if len(primary) > 0 { + return primary + } + return entries[:1] + } + + return entries +} + +func main() { + if len(os.Args) != 2 { + fmt.Println("usage: go run generate-go-licenses.go ") + os.Exit(1) + } + + out := os.Args[1] + + goCmd := "go" + if env := os.Getenv("GO"); env != "" { + goCmd = env + } + + modules := getModules(goCmd) + + var entries []LicenseEntry + for _, mod := range modules { + entries = append(entries, findLicenseFiles(mod)...) + } + + entries = slices.DeleteFunc(entries, func(e LicenseEntry) bool { + return ignoredNames[e.Name] + }) + + sort.Slice(entries, func(i, j int) bool { + return entries[i].Path < entries[j].Path + }) + + jsonBytes, err := json.MarshalIndent(entries, "", " ") + if err != nil { + panic(err) + } + + // Ensure file has a final newline + if jsonBytes[len(jsonBytes)-1] != '\n' { + jsonBytes = append(jsonBytes, '\n') + } + + err = os.WriteFile(out, jsonBytes, 0o644) + if err != nil { + panic(err) + } +} diff --git a/build/generate-openapi.go b/build/generate-openapi.go new file mode 100644 index 0000000..dd73043 --- /dev/null +++ b/build/generate-openapi.go @@ -0,0 +1,72 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// generate-openapi converts Gitea's Swagger 2.0 spec into an OpenAPI 3.0 spec. +// +// Gitea generates a Swagger 2.0 spec from code annotations (make generate-swagger). +// This tool converts it to OAS3 so that SDK generators and tools that require +// OAS3 (e.g. progenitor for Rust) can consume it directly. The conversion also +// deduplicates inline enum definitions into named schema components, producing +// cleaner SDK output with proper enum types instead of anonymous strings. +// +// Run: go run build/generate-openapi.go +// Output: templates/swagger/v1-openapi3.generated.json + +//go:build ignore + +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" + "sort" + "strings" + + "gitea.dev/build/openapi3gen" +) + +const ( + swaggerSpecPath = "templates/swagger/v1-swagger.generated.json" + openapi3OutPath = "templates/swagger/v1-openapi3.generated.json" +) + +var enumScanDirs = []string{ + "modules/structs", + "modules/commitstatus", +} + +func main() { + astEnumMap, err := openapi3gen.ScanSwaggerEnumTypes(enumScanDirs) + if err != nil { + log.Fatalf("scanning swagger:enum annotations: %v", err) + } + names := make([]string, 0, len(astEnumMap)) + for _, ns := range astEnumMap { + names = append(names, ns...) + } + sort.Strings(names) + fmt.Fprintf(os.Stderr, "discovered %d swagger:enum types: %s\n", len(names), strings.Join(names, ", ")) + + data, err := os.ReadFile(swaggerSpecPath) + if err != nil { + log.Fatalf("reading swagger spec: %v", err) + } + + oas3, err := openapi3gen.Convert(data, astEnumMap) + if err != nil { + log.Fatalf("converting to openapi 3.0: %v", err) + } + + out, err := json.MarshalIndent(oas3, "", " ") + if err != nil { + log.Fatalf("marshaling openapi 3.0: %v", err) + } + + if err := os.WriteFile(openapi3OutPath, out, 0o644); err != nil { + log.Fatalf("writing openapi 3.0 spec: %v", err) + } + + fmt.Printf("Generated %s\n", openapi3OutPath) +} diff --git a/build/openapi3gen/convert.go b/build/openapi3gen/convert.go new file mode 100644 index 0000000..922e055 --- /dev/null +++ b/build/openapi3gen/convert.go @@ -0,0 +1,387 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openapi3gen + +import ( + "fmt" + "regexp" + "sort" + "strings" + + "gitea.dev/modules/json" + + "github.com/getkin/kin-openapi/openapi2" + "github.com/getkin/kin-openapi/openapi2conv" + "github.com/getkin/kin-openapi/openapi3" +) + +// rxDeprecated matches "deprecated" as a word at the start of a description +// or preceded by whitespace/punctuation that indicates a leading marker (e.g. +// "Deprecated: true", "deprecated (use X instead)"). Rejects negated phrases +// like "not deprecated" or "previously deprecated, now supported". +var rxDeprecated = regexp.MustCompile(`(?i)(?:^|[\n.;])\s*deprecated\b`) + +// Convert parses a Swagger 2.0 spec and returns an OAS3 spec, applying +// Gitea-specific post-processing: server URL, file-schema fixups, URI +// formats, deprecated flags, and shared-enum extraction. +// +// astEnumMap is a value-set-key → Go-type-name(s) map (built by +// ScanSwaggerEnumTypes). When a value set is shared by multiple Go types, +// per-property disambiguation uses the x-go-enum-desc extension. If a shared +// enum in the spec has no matching entry, Convert returns an error — no +// fallback naming. +func Convert(swaggerJSON []byte, astEnumMap map[string][]string) (*openapi3.T, error) { + var swagger2 openapi2.T + if err := json.Unmarshal(swaggerJSON, &swagger2); err != nil { + return nil, fmt.Errorf("parsing swagger 2.0: %w", err) + } + + oas3, err := openapi2conv.ToV3(&swagger2) + if err != nil { + return nil, fmt.Errorf("converting to openapi 3.0: %w", err) + } + + oas3.Servers = openapi3.Servers{{URL: swagger2.BasePath}} + fixFileSchemas(oas3) + addURIFormats(oas3) + addDeprecatedFlags(oas3) + if err := extractSharedEnums(oas3, astEnumMap); err != nil { + return nil, err + } + return oas3, nil +} + +func fixFileSchemas(doc *openapi3.T) { + for _, pathItem := range doc.Paths.Map() { + for _, op := range []*openapi3.Operation{ + pathItem.Get, pathItem.Post, pathItem.Put, pathItem.Patch, + pathItem.Delete, pathItem.Head, pathItem.Options, pathItem.Trace, + } { + if op == nil { + continue + } + for _, resp := range op.Responses.Map() { + if resp.Value == nil { + continue + } + for _, mediaType := range resp.Value.Content { + fixSchema(mediaType.Schema) + } + } + if op.RequestBody != nil && op.RequestBody.Value != nil { + for _, mediaType := range op.RequestBody.Value.Content { + fixSchema(mediaType.Schema) + } + } + } + } +} + +// fixSchema rewrites any "type: file" schemas to the OAS3 equivalent +// (type: string, format: binary), recursing into Properties, Items, and +// AllOf/OneOf/AnyOf/Not branches. $ref nodes are skipped so shared schemas +// are rewritten exactly once when visited through their declaration. +func fixSchema(ref *openapi3.SchemaRef) { + if ref == nil || ref.Value == nil || ref.Ref != "" { + return + } + s := ref.Value + if s.Type.Is("file") { + s.Type = &openapi3.Types{"string"} + s.Format = "binary" + } + for _, p := range s.Properties { + fixSchema(p) + } + fixSchema(s.Items) + for _, sub := range s.AllOf { + fixSchema(sub) + } + for _, sub := range s.OneOf { + fixSchema(sub) + } + for _, sub := range s.AnyOf { + fixSchema(sub) + } + fixSchema(s.Not) +} + +// addURIFormats sets format: uri on string properties whose names indicate +// they hold URLs. This information is lost in Swagger 2.0 but is valuable +// for code generators. +func addURIFormats(doc *openapi3.T) { + if doc.Components == nil { + return + } + for _, schemaRef := range doc.Components.Schemas { + if schemaRef.Value == nil { + continue + } + for propName, propRef := range schemaRef.Value.Properties { + if propRef == nil || propRef.Value == nil || propRef.Ref != "" { + continue + } + prop := propRef.Value + if !prop.Type.Is("string") || prop.Format != "" { + continue + } + if isURLProperty(propName) { + prop.Format = "uri" + } + } + } +} + +func isURLProperty(name string) bool { + if strings.HasSuffix(name, "_url") { + return true + } + switch name { + case "url", "html_url", "clone_url": + return true + } + return false +} + +// addDeprecatedFlags sets deprecated: true on schema properties whose +// description starts with a "deprecated" marker (e.g. "Deprecated: true" +// or "deprecated (use X instead)"). Does not match negated phrases. +func addDeprecatedFlags(doc *openapi3.T) { + if doc.Components == nil { + return + } + for _, schemaRef := range doc.Components.Schemas { + if schemaRef.Value == nil { + continue + } + for _, propRef := range schemaRef.Value.Properties { + if propRef == nil || propRef.Value == nil || propRef.Ref != "" { + continue + } + if rxDeprecated.MatchString(propRef.Value.Description) { + propRef.Value.Deprecated = true + } + } + } +} + +type enumUsage struct { + schemaName string + propName string + propRef *openapi3.SchemaRef + inItems bool +} + +// extractSharedEnums finds identical enum arrays used by multiple schema +// properties, creates a standalone named schema for each, and replaces +// the inline enums with $ref pointers. +// +// If the derived enum name collides with an existing component schema, or +// no // swagger:enum annotation matches the value set, generation aborts +// with an actionable error — there are no silent fallbacks. +func extractSharedEnums(doc *openapi3.T, astEnumMap map[string][]string) error { + if doc.Components == nil { + return nil + } + + type groupKey struct { + valueSet string + typeName string + } + enumGroups := map[groupKey][]enumUsage{} + groupOrder := []groupKey{} // deterministic iteration + + addUsage := func(key groupKey, u enumUsage) { + if _, seen := enumGroups[key]; !seen { + groupOrder = append(groupOrder, key) + } + enumGroups[key] = append(enumGroups[key], u) + } + + for schemaName, schemaRef := range doc.Components.Schemas { + if schemaRef.Value == nil { + continue + } + for propName, propRef := range schemaRef.Value.Properties { + if propRef == nil || propRef.Value == nil || propRef.Ref != "" { + continue + } + if len(propRef.Value.Enum) > 1 && propRef.Value.Type.Is("string") { + key := groupKey{ + valueSet: EnumKey(propRef.Value.Enum), + typeName: extractEnumTypeName(propRef.Value, astEnumMap), + } + addUsage(key, enumUsage{schemaName, propName, propRef, false}) + } + if propRef.Value.Type.Is("array") && propRef.Value.Items != nil && + propRef.Value.Items.Value != nil && propRef.Value.Items.Ref == "" && + len(propRef.Value.Items.Value.Enum) > 1 && propRef.Value.Items.Value.Type.Is("string") { + key := groupKey{ + valueSet: EnumKey(propRef.Value.Items.Value.Enum), + typeName: extractEnumTypeName(propRef.Value.Items.Value, astEnumMap), + } + addUsage(key, enumUsage{schemaName, propName, propRef, true}) + } + } + } + + for _, key := range groupOrder { + usages := enumGroups[key] + if len(usages) < 2 { + continue + } + + enumName, err := deriveEnumName(key.valueSet, key.typeName, usages, astEnumMap) + if err != nil { + return err + } + if _, exists := doc.Components.Schemas[enumName]; exists { + return fmt.Errorf("enum name collision: %s already exists as a component schema", enumName) + } + + var enumValues []any + if usages[0].inItems { + enumValues = usages[0].propRef.Value.Items.Value.Enum + } else { + enumValues = usages[0].propRef.Value.Enum + } + + doc.Components.Schemas[enumName] = &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: enumValues, + }, + } + + ref := "#/components/schemas/" + enumName + + for _, usage := range usages { + if usage.inItems { + usage.propRef.Value.Items = &openapi3.SchemaRef{Ref: ref} + } else { + old := usage.propRef.Value + if old.Description == "" && !old.Deprecated && old.Format == "" { + usage.propRef.Ref = ref + usage.propRef.Value = nil + } else { + usage.propRef.Value = &openapi3.Schema{ + AllOf: openapi3.SchemaRefs{ + {Ref: ref}, + }, + Description: old.Description, + Deprecated: old.Deprecated, + Format: old.Format, + } + } + } + } + } + return nil +} + +// deriveEnumName looks up a shared enum's Go type name. If typeName is +// non-empty (because we recovered it from x-go-enum-desc), it is used +// directly. Otherwise the value-set must map to exactly one known type. On +// failure, returns an error identifying the offending properties. +func deriveEnumName(key, typeName string, usages []enumUsage, astEnumMap map[string][]string) (string, error) { + if typeName != "" { + return typeName, nil + } + names := astEnumMap[key] + if len(names) == 1 { + return names[0], nil + } + + props := map[string]bool{} + for _, u := range usages { + props[fmt.Sprintf("%s.%s", u.schemaName, u.propName)] = true + } + propList := make([]string, 0, len(props)) + for p := range props { + propList = append(propList, p) + } + if len(names) > 1 { + return "", fmt.Errorf( + "value-set %q is shared by multiple swagger:enum types %v and could not be disambiguated for properties: %v; "+ + "ensure go-swagger emits x-go-enum-desc for those properties", + key, names, propList, + ) + } + return "", fmt.Errorf( + "no swagger:enum annotation matches value-set %q used by %d properties: %v; "+ + "fix by adding a named string type with // swagger:enum to modules/structs or modules/commitstatus", + key, len(usages), propList, + ) +} + +// extractEnumTypeName recovers the Go type name a schema's enum came from by +// parsing the property's x-go-enum-desc extension. go-swagger emits one line +// per value as " [ ]"; the type is the longest +// common prefix of the const names, narrowed to the candidate set in +// astEnumMap. Returns "" if extraction is inconclusive. +func extractEnumTypeName(s *openapi3.Schema, astEnumMap map[string][]string) string { + if s == nil || s.Extensions == nil { + return "" + } + raw, ok := s.Extensions["x-go-enum-desc"] + if !ok { + return "" + } + desc, ok := raw.(string) + if !ok { + return "" + } + candidates := astEnumMap[EnumKey(s.Enum)] + if len(candidates) == 0 { + return "" + } + // Collect the const names (second whitespace-separated field per line). + var consts []string + for line := range strings.SplitSeq(desc, "\n") { + fields := strings.Fields(line) + if len(fields) >= 2 { + consts = append(consts, fields[1]) + } + } + if len(consts) == 0 { + return "" + } + // A candidate matches when it is a prefix of every const name AND the + // first character after the prefix is an uppercase ASCII letter — this + // rejects e.g. "Alpha" matching "Alphabet" (suffix "bet" starts lower) + // while still accepting both "Alpha" and "AlphaPlus" against "AlphaPlusX" + // (both prefixes valid). The most specific (longest) wins; ties return + // "" so deriveEnumName surfaces the ambiguity rather than silently + // picking a winner. + ordered := append([]string(nil), candidates...) + sort.Slice(ordered, func(i, j int) bool { return len(ordered[i]) > len(ordered[j]) }) + var matches []string + for _, name := range ordered { + ok := true + for _, c := range consts { + if !strings.HasPrefix(c, name) { + ok = false + break + } + suffix := c[len(name):] + // Empty suffix means the const name exactly equals the type name — valid exact match. + // A non-empty suffix must begin with an uppercase letter to reject incidental + // prefix matches (e.g. "Alpha" should not match "Alphabet"). + if len(suffix) > 0 && (suffix[0] < 'A' || suffix[0] > 'Z') { + ok = false + break + } + } + if ok { + matches = append(matches, name) + } + } + if len(matches) == 0 { + return "" + } + if len(matches) > 1 && len(matches[0]) == len(matches[1]) { + return "" + } + return matches[0] +} diff --git a/build/openapi3gen/convert_test.go b/build/openapi3gen/convert_test.go new file mode 100644 index 0000000..1cf73bd --- /dev/null +++ b/build/openapi3gen/convert_test.go @@ -0,0 +1,218 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openapi3gen + +import ( + "strings" + "testing" + + "github.com/getkin/kin-openapi/openapi3" +) + +func TestDeriveEnumName_hit(t *testing.T) { + key := EnumKey([]any{"red", "green", "blue"}) + astMap := map[string][]string{key: {"Color"}} + usages := []enumUsage{{schemaName: "Paint", propName: "color"}} + got, err := deriveEnumName(key, "", usages, astMap) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got != "Color" { + t.Fatalf("got %q, want %q", got, "Color") + } +} + +func TestDeriveEnumName_miss(t *testing.T) { + key := EnumKey([]any{"x", "y"}) + usages := []enumUsage{{schemaName: "Thing", propName: "kind"}} + _, err := deriveEnumName(key, "", usages, map[string][]string{}) + if err == nil { + t.Fatal("expected miss error, got nil") + } + msg := err.Error() + if !strings.Contains(msg, "Thing.kind") { + t.Fatalf("error %q should list the missing usage", msg) + } + if !strings.Contains(msg, "swagger:enum") { + t.Fatalf("error %q should hint at the fix", msg) + } +} + +func TestExtractSharedEnums_usesASTMap(t *testing.T) { + doc := &openapi3.T{ + Components: &openapi3.Components{ + Schemas: openapi3.Schemas{ + "A": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "color": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: []any{"red", "green", "blue"}, + }}, + }, + }}, + "B": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "color": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: []any{"red", "green", "blue"}, + }}, + }, + }}, + }, + }, + } + astMap := map[string][]string{EnumKey([]any{"red", "green", "blue"}): {"Color"}} + if err := extractSharedEnums(doc, astMap); err != nil { + t.Fatalf("extractSharedEnums: %v", err) + } + if _, ok := doc.Components.Schemas["Color"]; !ok { + t.Fatalf("expected Color schema to be extracted") + } +} + +func TestFixFileSchemas_recursesIntoNested(t *testing.T) { + fileType := func() *openapi3.SchemaRef { + return &openapi3.SchemaRef{Value: &openapi3.Schema{Type: &openapi3.Types{"file"}}} + } + doc := &openapi3.T{ + Paths: openapi3.NewPaths(), + } + doc.Paths.Set("/upload", &openapi3.PathItem{ + Post: &openapi3.Operation{ + RequestBody: &openapi3.RequestBodyRef{ + Value: &openapi3.RequestBody{ + Content: openapi3.Content{ + "multipart/form-data": { + Schema: &openapi3.SchemaRef{Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "attachment": fileType(), + "items": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"array"}, + Items: fileType(), + }}, + "alt": {Value: &openapi3.Schema{ + AllOf: openapi3.SchemaRefs{fileType()}, + }}, + "one": {Value: &openapi3.Schema{ + OneOf: openapi3.SchemaRefs{fileType()}, + }}, + "any": {Value: &openapi3.Schema{ + AnyOf: openapi3.SchemaRefs{fileType()}, + }}, + "not": {Value: &openapi3.Schema{ + Not: fileType(), + }}, + }, + }}, + }, + }, + }, + }, + Responses: openapi3.NewResponses(), + }, + }) + + fixFileSchemas(doc) + + props := doc.Paths.Value("/upload").Post.RequestBody.Value.Content["multipart/form-data"].Schema.Value.Properties + if !props["attachment"].Value.Type.Is("string") || props["attachment"].Value.Format != "binary" { + t.Errorf("nested property not fixed: %+v", props["attachment"].Value) + } + if !props["items"].Value.Items.Value.Type.Is("string") || props["items"].Value.Items.Value.Format != "binary" { + t.Errorf("array items not fixed: %+v", props["items"].Value.Items.Value) + } + if !props["alt"].Value.AllOf[0].Value.Type.Is("string") || props["alt"].Value.AllOf[0].Value.Format != "binary" { + t.Errorf("allOf branch not fixed: %+v", props["alt"].Value.AllOf[0].Value) + } + if !props["one"].Value.OneOf[0].Value.Type.Is("string") || props["one"].Value.OneOf[0].Value.Format != "binary" { + t.Errorf("oneOf branch not fixed: %+v", props["one"].Value.OneOf[0].Value) + } + if !props["any"].Value.AnyOf[0].Value.Type.Is("string") || props["any"].Value.AnyOf[0].Value.Format != "binary" { + t.Errorf("anyOf branch not fixed: %+v", props["any"].Value.AnyOf[0].Value) + } + if !props["not"].Value.Not.Value.Type.Is("string") || props["not"].Value.Not.Value.Format != "binary" { + t.Errorf("not branch not fixed: %+v", props["not"].Value.Not.Value) + } +} + +func TestExtractEnumTypeName_TeamVisibility(t *testing.T) { + enum := []any{"public", "limited", "private"} + key := EnumKey(enum) + astMap := map[string][]string{key: {"UserVisibility", "TeamVisibility"}} + schema := &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: enum, + Extensions: map[string]any{ + "x-go-enum-desc": "public TeamVisibilityPublic\nlimited TeamVisibilityLimited\nprivate TeamVisibilityPrivate", + }, + } + if got := extractEnumTypeName(schema, astMap); got != "TeamVisibility" { + t.Fatalf("got %q, want %q", got, "TeamVisibility") + } +} + +func TestExtractEnumTypeName_ambiguousPrefixTie(t *testing.T) { + enum := []any{"one", "two"} + key := EnumKey(enum) + astMap := map[string][]string{key: {"AB", "AC"}} + schema := &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: enum, + Extensions: map[string]any{ + "x-go-enum-desc": "one ABOne\ntwo ACTwo", + }, + } + if got := extractEnumTypeName(schema, astMap); got != "" { + t.Fatalf("got %q, want empty string for ambiguous tie", got) + } +} + +func TestExtractEnumTypeName_rejectsIncidentalPrefix(t *testing.T) { + enum := []any{"a", "b"} + key := EnumKey(enum) + astMap := map[string][]string{key: {"Alpha", "Alphabet"}} + schema := &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: enum, + Extensions: map[string]any{ + "x-go-enum-desc": "a AlphabetA\nb AlphabetB", + }, + } + if got := extractEnumTypeName(schema, astMap); got != "Alphabet" { + t.Fatalf("got %q, want %q", got, "Alphabet") + } +} + +func TestExtractSharedEnums_missReturnsError(t *testing.T) { + doc := &openapi3.T{ + Components: &openapi3.Components{ + Schemas: openapi3.Schemas{ + "A": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "color": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: []any{"red", "green"}, + }}, + }, + }}, + "B": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"object"}, + Properties: openapi3.Schemas{ + "color": {Value: &openapi3.Schema{ + Type: &openapi3.Types{"string"}, + Enum: []any{"red", "green"}, + }}, + }, + }}, + }, + }, + } + if err := extractSharedEnums(doc, map[string][]string{}); err == nil { + t.Fatal("expected miss error") + } +} diff --git a/build/openapi3gen/enumscan.go b/build/openapi3gen/enumscan.go new file mode 100644 index 0000000..73bbcb5 --- /dev/null +++ b/build/openapi3gen/enumscan.go @@ -0,0 +1,192 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// Package openapi3gen converts Gitea's Swagger 2.0 spec to an OpenAPI 3.0 +// spec. It discovers Go enum type names by scanning swagger:enum annotations +// in the source tree, then names extracted shared-enum schemas accordingly. +package openapi3gen + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" +) + +// EnumKey returns a canonical key for a set of enum values: values are +// stringified, sorted, and joined with "|". Used to match enum value sets +// across spec properties and scanned Go type declarations. +func EnumKey(values []any) string { + strs := make([]string, len(values)) + for i, v := range values { + strs[i] = fmt.Sprintf("%v", v) + } + sort.Strings(strs) + return strings.Join(strs, "|") +} + +var rxSwaggerEnum = regexp.MustCompile(`swagger:enum\s+(\w+)`) + +// ScanSwaggerEnumTypes walks .go files under each dir and returns a map from +// a canonical value-set key (see EnumKey) to the Go type names declared with +// // swagger:enum TypeName. Multiple type names per key are allowed (e.g. +// distinct enum types that happen to share a value set such as +// {public, limited, private}); callers must disambiguate per-usage (typically +// by parsing the property's x-go-enum-desc extension to recover the const +// type prefix). +// +// Returns an error on parse failure or on an annotation for a type whose +// constants can't be extracted. +func ScanSwaggerEnumTypes(dirs []string) (map[string][]string, error) { + fset := token.NewFileSet() + parsed := []*ast.File{} + + for _, dir := range dirs { + entries, err := os.ReadDir(dir) + if err != nil { + return nil, fmt.Errorf("reading %s: %w", dir, err) + } + for _, entry := range entries { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { + continue + } + if strings.HasSuffix(entry.Name(), "_test.go") { + continue + } + path := filepath.Join(dir, entry.Name()) + file, err := parser.ParseFile(fset, path, nil, parser.ParseComments) + if err != nil { + return nil, fmt.Errorf("%s: %w", path, err) + } + parsed = append(parsed, file) + } + } + + enumTypes := map[string]string{} // typeName → "" (presence marker) + enumValues := map[string][]any{} // typeName → values + + // Pass 1: collect every // swagger:enum TypeName declaration. + for _, file := range parsed { + for _, decl := range file.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + if err := collectEnumType(gd, enumTypes); err != nil { + return nil, fmt.Errorf("%s: %w", fset.Position(gd.Pos()).Filename, err) + } + } + } + + // Pass 2: collect const values; now every annotated type is visible. + for _, file := range parsed { + for _, decl := range file.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.CONST { + continue + } + collectEnumValues(gd, enumTypes, enumValues) + } + } + + result := map[string][]string{} + for typeName := range enumTypes { + values, ok := enumValues[typeName] + if !ok || len(values) == 0 { + return nil, fmt.Errorf("swagger:enum %s has no const block with typed string values", typeName) + } + key := EnumKey(values) + result[key] = append(result[key], typeName) + } + for key, names := range result { + sort.Strings(names) + result[key] = names + } + return result, nil +} + +// collectEnumType scans a `type` GenDecl for // swagger:enum annotations, +// handling both the lone form (`// swagger:enum Foo\n type Foo string`) +// where the comment group is attached to the GenDecl, and the grouped form: +// +// type ( +// // swagger:enum Foo +// Foo string +// ) +// +// where the comment group is attached to each TypeSpec. Caveat: Go's parser +// only attaches a CommentGroup when it is immediately adjacent to the decl. +// A blank line (not a `//` continuation line) between the comment and the +// declaration drops the Doc, so annotations MUST sit directly above their +// type. All current annotated files obey this — the rule is noted here so +// a future edit that inserts a blank line fails fast rather than silently. +func collectEnumType(gd *ast.GenDecl, enumTypes map[string]string) error { + if err := registerEnumAnnotation(gd.Doc, gd.Specs, enumTypes); err != nil { + return err + } + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok || ts.Doc == nil { + continue + } + if err := registerEnumAnnotation(ts.Doc, []ast.Spec{ts}, enumTypes); err != nil { + return err + } + } + return nil +} + +func registerEnumAnnotation(doc *ast.CommentGroup, specs []ast.Spec, enumTypes map[string]string) error { + if doc == nil { + return nil + } + matches := rxSwaggerEnum.FindStringSubmatch(doc.Text()) + if len(matches) < 2 { + return nil + } + annotated := matches[1] + for _, spec := range specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + if ts.Name.Name == annotated { + enumTypes[annotated] = "" + return nil + } + } + return fmt.Errorf("swagger:enum %s: no type declaration with that name in the same decl group; check for a typo", annotated) +} + +func collectEnumValues(gd *ast.GenDecl, enumTypes map[string]string, enumValues map[string][]any) { + for _, spec := range gd.Specs { + vs, ok := spec.(*ast.ValueSpec) + if !ok || vs.Type == nil { + continue + } + ident, ok := vs.Type.(*ast.Ident) + if !ok { + continue + } + if _, isEnum := enumTypes[ident.Name]; !isEnum { + continue + } + for _, val := range vs.Values { + lit, ok := val.(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + continue + } + unquoted, err := strconv.Unquote(lit.Value) + if err != nil { + continue + } + enumValues[ident.Name] = append(enumValues[ident.Name], unquoted) + } + } +} diff --git a/build/openapi3gen/enumscan_test.go b/build/openapi3gen/enumscan_test.go new file mode 100644 index 0000000..8cb16b7 --- /dev/null +++ b/build/openapi3gen/enumscan_test.go @@ -0,0 +1,249 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package openapi3gen + +import ( + "os" + "path/filepath" + "slices" + "strings" + "testing" +) + +func single(got map[string][]string, key string) string { + v := got[key] + if len(v) != 1 { + return "" + } + return v[0] +} + +func TestEnumKey_sortsAndJoins(t *testing.T) { + key := EnumKey([]any{"b", "a", "c"}) + if key != "a|b|c" { + t.Fatalf("EnumKey = %q, want %q", key, "a|b|c") + } +} + +func TestEnumKey_handlesNonStringValues(t *testing.T) { + key := EnumKey([]any{2, 1, 3}) + if key != "1|2|3" { + t.Fatalf("EnumKey = %q, want %q", key, "1|2|3") + } +} + +func TestScanSwaggerEnumTypes_basic(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +// Color is a primary color. +// swagger:enum Color +type Color string + +const ( + ColorRed Color = "red" + ColorGreen Color = "green" + ColorBlue Color = "blue" +) +` + if err := os.WriteFile(filepath.Join(dir, "color.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + got, err := ScanSwaggerEnumTypes([]string{dir}) + if err != nil { + t.Fatalf("ScanSwaggerEnumTypes: %v", err) + } + wantKey := EnumKey([]any{"red", "green", "blue"}) + if single(got, wantKey) != "Color" { + t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Color") + } +} + +func TestScanSwaggerEnumTypes_orphanAnnotation(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +// swagger:enum Sttype +type StateType string + +const ( + StateOpen StateType = "open" +) +` + if err := os.WriteFile(filepath.Join(dir, "typo.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + _, err := ScanSwaggerEnumTypes([]string{dir}) + if err == nil { + t.Fatal("expected error for annotation referencing a non-matching type name") + } + if !strings.Contains(err.Error(), "Sttype") { + t.Fatalf("error %q should mention the typo'd name Sttype", err.Error()) + } +} + +func TestScanSwaggerEnumTypes_collision(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +// swagger:enum Alpha +type Alpha string +const ( + AlphaX Alpha = "x" + AlphaY Alpha = "y" +) + +// swagger:enum Beta +type Beta string +const ( + BetaX Beta = "x" + BetaY Beta = "y" +) +` + if err := os.WriteFile(filepath.Join(dir, "dup.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + got, err := ScanSwaggerEnumTypes([]string{dir}) + if err != nil { + t.Fatalf("ScanSwaggerEnumTypes: %v", err) + } + key := EnumKey([]any{"x", "y"}) + names := got[key] + if !slices.Equal(names, []string{"Alpha", "Beta"}) { + t.Fatalf("map[%q] = %v, want [Alpha Beta]", key, names) + } +} + +func TestScanSwaggerEnumTypes_parseFailure(t *testing.T) { + dir := t.TempDir() + if err := os.WriteFile(filepath.Join(dir, "bad.go"), []byte("package fixture\nfunc Foo() {"), 0o644); err != nil { + t.Fatal(err) + } + + _, err := ScanSwaggerEnumTypes([]string{dir}) + if err == nil { + t.Fatal("expected parse error, got nil") + } +} + +func TestScanSwaggerEnumTypes_annotationWithoutConsts(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +// swagger:enum Lonely +type Lonely string +` + if err := os.WriteFile(filepath.Join(dir, "lonely.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + _, err := ScanSwaggerEnumTypes([]string{dir}) + if err == nil { + t.Fatal("expected error for annotation without consts") + } + if !strings.Contains(err.Error(), "Lonely") { + t.Fatalf("error %q should mention Lonely", err.Error()) + } +} + +func TestScanSwaggerEnumTypes_constsAndTypeInDifferentFiles(t *testing.T) { + dir := t.TempDir() + // Name ordering: `a_consts.go` < `b_type.go`, so readdir returns consts first. + // Old single-pass scanner would miss the values; two-pass must not. + constsSrc := `package fixture + +const ( + HueA Hue = "a" + HueB Hue = "b" +) +` + typeSrc := `package fixture + +// swagger:enum Hue +type Hue string +` + if err := os.WriteFile(filepath.Join(dir, "a_consts.go"), []byte(constsSrc), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "b_type.go"), []byte(typeSrc), 0o644); err != nil { + t.Fatal(err) + } + + got, err := ScanSwaggerEnumTypes([]string{dir}) + if err != nil { + t.Fatalf("ScanSwaggerEnumTypes: %v", err) + } + wantKey := EnumKey([]any{"a", "b"}) + if single(got, wantKey) != "Hue" { + t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Hue") + } +} + +func TestScanSwaggerEnumTypes_constsBeforeType(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +const ( + ShadeDark Shade = "dark" + ShadeLight Shade = "light" +) + +// swagger:enum Shade +type Shade string +` + if err := os.WriteFile(filepath.Join(dir, "shade.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + got, err := ScanSwaggerEnumTypes([]string{dir}) + if err != nil { + t.Fatalf("ScanSwaggerEnumTypes: %v", err) + } + wantKey := EnumKey([]any{"dark", "light"}) + if single(got, wantKey) != "Shade" { + t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Shade") + } +} + +func TestScanSwaggerEnumTypes_groupedTypeDecl(t *testing.T) { + dir := t.TempDir() + src := `package fixture + +type ( + // swagger:enum Color + Color string + // swagger:enum Shade + Shade string +) + +const ( + ColorRed Color = "red" + ColorBlue Color = "blue" +) + +const ( + ShadeDark Shade = "dark" + ShadeLight Shade = "light" +) +` + if err := os.WriteFile(filepath.Join(dir, "grouped.go"), []byte(src), 0o644); err != nil { + t.Fatal(err) + } + + got, err := ScanSwaggerEnumTypes([]string{dir}) + if err != nil { + t.Fatalf("ScanSwaggerEnumTypes: %v", err) + } + colorKey := EnumKey([]any{"red", "blue"}) + shadeKey := EnumKey([]any{"dark", "light"}) + if single(got, colorKey) != "Color" { + t.Fatalf("Color: map[%q] = %q, want %q", colorKey, got[colorKey], "Color") + } + if single(got, shadeKey) != "Shade" { + t.Fatalf("Shade: map[%q] = %q, want %q", shadeKey, got[shadeKey], "Shade") + } +} diff --git a/build/update-locales.sh b/build/update-locales.sh new file mode 100755 index 0000000..5316746 --- /dev/null +++ b/build/update-locales.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# this script runs in alpine image which only has `sh` shell +if [ ! -f ./options/locale/locale_en-US.json ]; then + echo "please run this script in the root directory of the project" + exit 1 +fi + +mv ./options/locale/locale_en-US.json ./options/ + +# Remove translation under 25% of en_us +baselines=$(cat "./options/locale_en-US.json" | wc -l) +baselines=$((baselines / 4)) +for filename in ./options/locale/*.json; do + lines=$(cat "$filename" | wc -l) + if [ "$lines" -lt "$baselines" ]; then + echo "Removing $filename: $lines/$baselines" + rm "$filename" + fi +done + +mv ./options/locale_en-US.json ./options/locale/ diff --git a/cmd/actions.go b/cmd/actions.go new file mode 100644 index 0000000..1a1ad28 --- /dev/null +++ b/cmd/actions.go @@ -0,0 +1,53 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + + "gitea.dev/modules/private" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +func newActionsCommand() *cli.Command { + return &cli.Command{ + Name: "actions", + Usage: "Manage Gitea Actions", + Commands: []*cli.Command{ + newActionsGenerateRunnerTokenCommand(), + }, + } +} + +func newActionsGenerateRunnerTokenCommand() *cli.Command { + return &cli.Command{ + Name: "generate-runner-token", + Usage: "Generate a new token for a runner to use to register with the server", + Action: runGenerateActionsRunnerToken, + Aliases: []string{"grt"}, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "scope", + Aliases: []string{"s"}, + Value: "", + Usage: "{owner}[/{repo}] - leave empty for a global runner", + }, + }, + } +} + +func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error { + setting.MustInstalled() + + scope := c.String("scope") + + respText, extra := private.GenerateActionsRunnerToken(ctx, scope) + if extra.HasError() { + return handleCliResponseExtra(extra) + } + cprintln(c, respText.Text) + return nil +} diff --git a/cmd/admin.go b/cmd/admin.go new file mode 100644 index 0000000..6d9a198 --- /dev/null +++ b/cmd/admin.go @@ -0,0 +1,176 @@ +// Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2016 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "fmt" + + "gitea.dev/models/db" + repo_model "gitea.dev/models/repo" + "gitea.dev/modules/git" + "gitea.dev/modules/log" + repo_module "gitea.dev/modules/repository" + + "github.com/urfave/cli/v3" +) + +func newAdminCommand() *cli.Command { + return &cli.Command{ + Name: "admin", + Usage: "Perform common administrative operations", + Commands: []*cli.Command{ + newUserCommand(), + newRepoSyncReleasesCommand(), + newRegenerateCommand(), + newAuthCommand(), + newSendMailCommand(), + }, + } +} + +func newRepoSyncReleasesCommand() *cli.Command { + return &cli.Command{ + Name: "repo-sync-releases", + Usage: "Synchronize repository releases with tags", + Action: runRepoSyncReleases, + } +} + +func newRegenerateCommand() *cli.Command { + return &cli.Command{ + Name: "regenerate", + Usage: "Regenerate specific files", + Commands: []*cli.Command{ + newRegenerateHooksCommand(), + newRegenerateKeysCommand(), + }, + } +} + +func newAuthCommand() *cli.Command { + return &cli.Command{ + Name: "auth", + Usage: "Modify external auth providers", + Before: func(ctx context.Context, _ *cli.Command) (context.Context, error) { + return cliAuditContext(ctx), nil + }, + Commands: []*cli.Command{ + microcmdAuthAddOauth(), + microcmdAuthUpdateOauth(), + microcmdAuthAddLdapBindDn(), + microcmdAuthUpdateLdapBindDn(), + microcmdAuthAddLdapSimpleAuth(), + microcmdAuthUpdateLdapSimpleAuth(), + microcmdAuthAddSMTP(), + microcmdAuthUpdateSMTP(), + newAuthListCommand(), + newAuthDeleteCommand(), + }, + } +} + +func newSendMailCommand() *cli.Command { + return &cli.Command{ + Name: "sendmail", + Usage: "Send a message to all users", + Action: runSendMail, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "title", + Usage: "a title of a message", + Required: true, + }, + &cli.StringFlag{ + Name: "content", + Usage: "a content of a message", + Value: "", + }, + &cli.BoolFlag{ + Name: "force", + Aliases: []string{"f"}, + Usage: "A flag to bypass a confirmation step", + }, + }, + } +} + +func idFlag() *cli.Int64Flag { + return &cli.Int64Flag{ + Name: "id", + Usage: "ID of authentication source", + } +} + +func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error { + if err := initDB(ctx); err != nil { + return err + } + + if err := git.InitSimple(); err != nil { + return err + } + + log.Trace("Synchronizing repository releases (this may take a while)") + for page := 1; ; page++ { + repos, count, err := repo_model.SearchRepositoryByName(ctx, repo_model.SearchRepoOptions{ + ListOptions: db.ListOptions{ + PageSize: repo_model.RepositoryListDefaultPageSize, + Page: page, + }, + Private: true, + }) + if err != nil { + return fmt.Errorf("SearchRepositoryByName: %w", err) + } + if len(repos) == 0 { + break + } + log.Trace("Processing next %d repos of %d", len(repos), count) + for _, repo := range repos { + log.Trace("Synchronizing repo %s", repo.FullName()) + gitRepo, err := git.OpenRepository(ctx, repo) + if err != nil { + log.Warn("OpenRepository: %v", err) + continue + } + + oldnum, err := getReleaseCount(ctx, repo.ID) + if err != nil { + log.Warn(" GetReleaseCountByRepoID: %v", err) + } + log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum) + + if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + log.Warn(" SyncReleasesWithTags: %v", err) + gitRepo.Close() + continue + } + + count, err = getReleaseCount(ctx, repo.ID) + if err != nil { + log.Warn(" GetReleaseCountByRepoID: %v", err) + gitRepo.Close() + continue + } + + log.Trace("repo %s releases synchronized to tags: from %d to %d", + repo.FullName(), oldnum, count) + gitRepo.Close() + } + } + + return nil +} + +func getReleaseCount(ctx context.Context, id int64) (int64, error) { + return db.Count[repo_model.Release]( + ctx, + repo_model.FindReleasesOptions{ + RepoID: id, + IncludeTags: true, + }, + ) +} diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go new file mode 100644 index 0000000..6a00e96 --- /dev/null +++ b/cmd/admin_auth.go @@ -0,0 +1,109 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "os" + "text/tabwriter" + + auth_model "gitea.dev/models/auth" + "gitea.dev/models/db" + auth_service "gitea.dev/services/auth" + + "github.com/urfave/cli/v3" +) + +func newAuthDeleteCommand() *cli.Command { + return &cli.Command{ + Name: "delete", + Usage: "Delete specific auth source", + Flags: []cli.Flag{idFlag()}, + Action: runDeleteAuth, + } +} + +func newAuthListCommand() *cli.Command { + return &cli.Command{ + Name: "list", + Usage: "List auth sources", + Action: runListAuth, + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "min-width", + Usage: "Minimal cell width including any padding for the formatted table", + Value: 0, + }, + &cli.IntFlag{ + Name: "tab-width", + Usage: "width of tab characters in formatted table (equivalent number of spaces)", + Value: 8, + }, + &cli.IntFlag{ + Name: "padding", + Usage: "padding added to a cell before computing its width", + Value: 1, + }, + &cli.StringFlag{ + Name: "pad-char", + Usage: `ASCII char used for padding if padchar == '\\t', the Writer will assume that the width of a '\\t' in the formatted output is tabwidth, and cells are left-aligned independent of align_left (for correct-looking results, tabwidth must correspond to the tab width in the viewer displaying the result)`, + Value: "\t", + }, + &cli.BoolFlag{ + Name: "vertical-bars", + Usage: "Set to true to print vertical bars between columns", + }, + }, + } +} + +func runListAuth(ctx context.Context, c *cli.Command) error { + if err := initDB(ctx); err != nil { + return err + } + + authSources, err := db.Find[auth_model.Source](ctx, auth_model.FindSourcesOptions{}) + if err != nil { + return err + } + + flags := tabwriter.AlignRight + if c.Bool("vertical-bars") { + flags |= tabwriter.Debug + } + + padChar := byte('\t') + if len(c.String("pad-char")) > 0 { + padChar = c.String("pad-char")[0] + } + + // loop through each source and print + w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags) + fmt.Fprintf(w, "ID\tName\tType\tEnabled\n") + for _, source := range authSources { + fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive) + } + w.Flush() + + return nil +} + +func runDeleteAuth(ctx context.Context, c *cli.Command) error { + if !c.IsSet("id") { + return errors.New("--id flag is missing") + } + + if err := initDB(ctx); err != nil { + return err + } + + source, err := auth_model.GetSourceByID(ctx, c.Int64("id")) + if err != nil { + return err + } + + return auth_service.DeleteSource(ctx, source) +} diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go new file mode 100644 index 0000000..b7d5071 --- /dev/null +++ b/cmd/admin_auth_ldap.go @@ -0,0 +1,473 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "fmt" + "strings" + + "gitea.dev/models/auth" + "gitea.dev/modules/util" + auth_service "gitea.dev/services/auth" + "gitea.dev/services/auth/source/ldap" + + "github.com/urfave/cli/v3" +) + +type ( + authService struct { + initDB func(ctx context.Context) error + createAuthSource func(context.Context, *auth.Source) error + updateAuthSource func(context.Context, *auth.Source) error + getAuthSourceByID func(ctx context.Context, id int64) (*auth.Source, error) + } +) + +func commonLdapCLIFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Usage: "Authentication name.", + }, + &cli.BoolFlag{ + Name: "not-active", + Usage: "Deactivate the authentication source.", + }, + &cli.BoolFlag{ + Name: "active", + Usage: "Activate the authentication source.", + }, + &cli.StringFlag{ + Name: "security-protocol", + Usage: "Security protocol name.", + }, + &cli.BoolFlag{ + Name: "skip-tls-verify", + Usage: "Disable TLS verification.", + }, + &cli.StringFlag{ + Name: "host", + Usage: "The address where the LDAP server can be reached.", + }, + &cli.IntFlag{ + Name: "port", + Usage: "The port to use when connecting to the LDAP server.", + }, + &cli.StringFlag{ + Name: "user-search-base", + Usage: "The LDAP base at which user accounts will be searched for.", + }, + &cli.StringFlag{ + Name: "user-filter", + Usage: "An LDAP filter declaring how to find the user record that is attempting to authenticate.", + }, + &cli.StringFlag{ + Name: "admin-filter", + Usage: "An LDAP filter specifying if a user should be given administrator privileges.", + }, + &cli.StringFlag{ + Name: "restricted-filter", + Usage: "An LDAP filter specifying if a user should be given restricted status.", + }, + &cli.BoolFlag{ + Name: "allow-deactivate-all", + Usage: "Allow empty search results to deactivate all users.", + }, + &cli.StringFlag{ + Name: "username-attribute", + Usage: "The attribute of the user’s LDAP record containing the user name.", + }, + &cli.StringFlag{ + Name: "firstname-attribute", + Usage: "The attribute of the user’s LDAP record containing the user’s first name.", + }, + &cli.StringFlag{ + Name: "surname-attribute", + Usage: "The attribute of the user’s LDAP record containing the user’s surname.", + }, + &cli.StringFlag{ + Name: "email-attribute", + Usage: "The attribute of the user’s LDAP record containing the user’s email address.", + }, + &cli.StringFlag{ + Name: "public-ssh-key-attribute", + Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.", + }, + &cli.BoolFlag{ + Name: "ssh-keys-are-verified", + Usage: "Set to true to automatically flag SSH keys in LDAP as verified.", + }, + &cli.BoolFlag{ + Name: "skip-local-2fa", + Usage: "Set to true to skip local 2fa for users authenticated by this source", + }, + &cli.StringFlag{ + Name: "avatar-attribute", + Usage: "The attribute of the user’s LDAP record containing the user’s avatar.", + }, + } +} + +func ldapBindDnCLIFlags() []cli.Flag { + return append(commonLdapCLIFlags(), + &cli.StringFlag{ + Name: "bind-dn", + Usage: "The DN to bind to the LDAP server with when searching for the user.", + }, + &cli.StringFlag{ + Name: "bind-password", + Usage: "The password for the Bind DN, if any.", + }, + &cli.BoolFlag{ + Name: "attributes-in-bind", + Usage: "Fetch attributes in bind DN context.", + }, + &cli.BoolFlag{ + Name: "synchronize-users", + Usage: "Enable user synchronization.", + }, + &cli.BoolFlag{ + Name: "disable-synchronize-users", + Usage: "Disable user synchronization.", + }, + &cli.UintFlag{ + Name: "page-size", + Usage: "Search page size.", + }, + &cli.BoolFlag{ + Name: "enable-groups", + Usage: "Enable LDAP groups", + }, + &cli.StringFlag{ + Name: "group-search-base-dn", + Usage: "The LDAP base DN at which group accounts will be searched for", + }, + &cli.StringFlag{ + Name: "group-member-attribute", + Usage: "Group attribute containing list of users", + }, + &cli.StringFlag{ + Name: "group-user-attribute", + Usage: "User attribute listed in group", + }, + &cli.StringFlag{ + Name: "group-filter", + Usage: "Verify group membership in LDAP", + }, + &cli.StringFlag{ + Name: "group-team-map", + Usage: "Map LDAP groups to Organization teams", + }, + &cli.BoolFlag{ + Name: "group-team-map-removal", + Usage: "Remove users from synchronized teams if user does not belong to corresponding LDAP group", + }) +} + +func ldapSimpleAuthCLIFlags() []cli.Flag { + return append(commonLdapCLIFlags(), + &cli.StringFlag{ + Name: "user-dn", + Usage: "The user's DN.", + }) +} + +func microcmdAuthAddLdapBindDn() *cli.Command { + return &cli.Command{ + Name: "add-ldap", + Usage: "Add new LDAP (via Bind DN) authentication source", + Action: func(ctx context.Context, cmd *cli.Command) error { + return newAuthService().addLdapBindDn(ctx, cmd) + }, + Flags: ldapBindDnCLIFlags(), + } +} + +func microcmdAuthUpdateLdapBindDn() *cli.Command { + return &cli.Command{ + Name: "update-ldap", + Usage: "Update existing LDAP (via Bind DN) authentication source", + Action: func(ctx context.Context, cmd *cli.Command) error { + return newAuthService().updateLdapBindDn(ctx, cmd) + }, + Flags: append([]cli.Flag{idFlag()}, ldapBindDnCLIFlags()...), + } +} + +func microcmdAuthAddLdapSimpleAuth() *cli.Command { + return &cli.Command{ + Name: "add-ldap-simple", + Usage: "Add new LDAP (simple auth) authentication source", + Action: func(ctx context.Context, cmd *cli.Command) error { + return newAuthService().addLdapSimpleAuth(ctx, cmd) + }, + Flags: ldapSimpleAuthCLIFlags(), + } +} + +func microcmdAuthUpdateLdapSimpleAuth() *cli.Command { + return &cli.Command{ + Name: "update-ldap-simple", + Usage: "Update existing LDAP (simple auth) authentication source", + Action: func(ctx context.Context, cmd *cli.Command) error { + return newAuthService().updateLdapSimpleAuth(ctx, cmd) + }, + Flags: append([]cli.Flag{idFlag()}, ldapSimpleAuthCLIFlags()...), + } +} + +// newAuthService creates a service with default functions. +func newAuthService() *authService { + return &authService{ + initDB: initDB, + createAuthSource: auth_service.CreateSource, + updateAuthSource: auth_service.UpdateSource, + getAuthSourceByID: auth.GetSourceByID, + } +} + +// parseAuthSourceLdap assigns values on authSource according to command line flags. +func parseAuthSourceLdap(c *cli.Command, authSource *auth.Source) { + if c.IsSet("name") { + authSource.Name = c.String("name") + } + if c.IsSet("not-active") { + authSource.IsActive = !c.Bool("not-active") + } + if c.IsSet("active") { + authSource.IsActive = c.Bool("active") + } + if c.IsSet("synchronize-users") { + authSource.IsSyncEnabled = c.Bool("synchronize-users") + } + if c.IsSet("disable-synchronize-users") { + authSource.IsSyncEnabled = !c.Bool("disable-synchronize-users") + } + authSource.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "") +} + +// parseLdapConfig assigns values on config according to command line flags. +func parseLdapConfig(c *cli.Command, config *ldap.Source) error { + if c.IsSet("name") { + config.Name = c.String("name") + } + if c.IsSet("host") { + config.Host = c.String("host") + } + if c.IsSet("port") { + config.Port = c.Int("port") + } + if c.IsSet("security-protocol") { + p, ok := findLdapSecurityProtocolByName(c.String("security-protocol")) + if !ok { + return fmt.Errorf("unknown security protocol name: %s", c.String("security-protocol")) + } + config.SecurityProtocol = p + } + if c.IsSet("skip-tls-verify") { + config.SkipVerify = c.Bool("skip-tls-verify") + } + if c.IsSet("bind-dn") { + config.BindDN = c.String("bind-dn") + } + if c.IsSet("user-dn") { + config.UserDN = c.String("user-dn") + } + if c.IsSet("bind-password") { + config.BindPassword = c.String("bind-password") + } + if c.IsSet("user-search-base") { + config.UserBase = c.String("user-search-base") + } + if c.IsSet("username-attribute") { + config.AttributeUsername = c.String("username-attribute") + } + if c.IsSet("firstname-attribute") { + config.AttributeName = c.String("firstname-attribute") + } + if c.IsSet("surname-attribute") { + config.AttributeSurname = c.String("surname-attribute") + } + if c.IsSet("email-attribute") { + config.AttributeMail = c.String("email-attribute") + } + if c.IsSet("attributes-in-bind") { + config.AttributesInBind = c.Bool("attributes-in-bind") + } + if c.IsSet("public-ssh-key-attribute") { + config.AttributeSSHPublicKey = c.String("public-ssh-key-attribute") + } + if c.IsSet("ssh-keys-are-verified") { + config.SSHKeysAreVerified = c.Bool("ssh-keys-are-verified") + } + if c.IsSet("avatar-attribute") { + config.AttributeAvatar = c.String("avatar-attribute") + } + if c.IsSet("page-size") { + config.SearchPageSize = uint32(c.Uint("page-size")) + } + if c.IsSet("user-filter") { + config.Filter = c.String("user-filter") + } + if c.IsSet("admin-filter") { + config.AdminFilter = c.String("admin-filter") + } + if c.IsSet("restricted-filter") { + config.RestrictedFilter = c.String("restricted-filter") + } + if c.IsSet("allow-deactivate-all") { + config.AllowDeactivateAll = c.Bool("allow-deactivate-all") + } + if c.IsSet("enable-groups") { + config.GroupsEnabled = c.Bool("enable-groups") + } + if c.IsSet("group-search-base-dn") { + config.GroupDN = c.String("group-search-base-dn") + } + if c.IsSet("group-member-attribute") { + config.GroupMemberUID = c.String("group-member-attribute") + } + if c.IsSet("group-user-attribute") { + config.UserUID = c.String("group-user-attribute") + } + if c.IsSet("group-filter") { + config.GroupFilter = c.String("group-filter") + } + if c.IsSet("group-team-map") { + config.GroupTeamMap = c.String("group-team-map") + } + if c.IsSet("group-team-map-removal") { + config.GroupTeamMapRemoval = c.Bool("group-team-map-removal") + } + return nil +} + +// findLdapSecurityProtocolByName finds security protocol by its name ignoring case. +// It returns the value of the security protocol and if it was found. +func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) { + for i, n := range ldap.SecurityProtocolNames { + if strings.EqualFold(name, n) { + return i, true + } + } + return 0, false +} + +// getAuthSourceOfType gets the login source by id. +// It returns an error if the id does not match any source or if the source is not of the expected type. +func (a *authService) getAuthSourceOfType(ctx context.Context, id int64, authType auth.Type) (*auth.Source, error) { + authSource, err := a.getAuthSourceByID(ctx, id) + if err != nil { + return nil, err + } + + if authSource.Type != authType { + return nil, fmt.Errorf("invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String()) + } + + return authSource, nil +} + +// getAuthSource gets the login source by its id defined in the command line flags. +// It returns an error if the id is not set, does not match any source or if the source is not of expected type. +func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authType auth.Type) (*auth.Source, error) { + if err := argsSet(c, "id"); err != nil { + return nil, err + } + return a.getAuthSourceOfType(ctx, c.Int64("id"), authType) +} + +// addLdapBindDn adds a new LDAP via Bind DN authentication source. +func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error { + if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil { + return err + } + if err := a.initDB(ctx); err != nil { + return err + } + + ldapConfig := &ldap.Source{ + Enabled: true, // always true + } + authSource := &auth.Source{ + Type: auth.LDAP, + IsActive: true, // active by default + Cfg: ldapConfig, + } + + parseAuthSourceLdap(c, authSource) + if err := parseLdapConfig(c, ldapConfig); err != nil { + return err + } + + return a.createAuthSource(ctx, authSource) +} + +// updateLdapBindDn updates a new LDAP via Bind DN authentication source. +func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) error { + if err := a.initDB(ctx); err != nil { + return err + } + + authSource, err := a.getAuthSource(ctx, c, auth.LDAP) + if err != nil { + return err + } + ldapConfig := auth.MustSourceCfg[*ldap.Source](authSource) + + parseAuthSourceLdap(c, authSource) + if err := parseLdapConfig(c, ldapConfig); err != nil { + return err + } + + return a.updateAuthSource(ctx, authSource) +} + +// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source. +func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) error { + if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil { + return err + } + + if err := a.initDB(ctx); err != nil { + return err + } + + ldapConfig := &ldap.Source{ + Enabled: true, // always true + } + authSource := &auth.Source{ + Type: auth.DLDAP, + IsActive: true, // active by default + Cfg: ldapConfig, + } + + parseAuthSourceLdap(c, authSource) + if err := parseLdapConfig(c, ldapConfig); err != nil { + return err + } + + return a.createAuthSource(ctx, authSource) +} + +// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source. +func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command) error { + if err := a.initDB(ctx); err != nil { + return err + } + + authSource, err := a.getAuthSource(ctx, c, auth.DLDAP) + if err != nil { + return err + } + ldapConfig := auth.MustSourceCfg[*ldap.Source](authSource) + + parseAuthSourceLdap(c, authSource) + if err := parseLdapConfig(c, ldapConfig); err != nil { + return err + } + + return a.updateAuthSource(ctx, authSource) +} diff --git a/cmd/admin_auth_ldap_test.go b/cmd/admin_auth_ldap_test.go new file mode 100644 index 0000000..f9b5338 --- /dev/null +++ b/cmd/admin_auth_ldap_test.go @@ -0,0 +1,1348 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "testing" + + "gitea.dev/models/auth" + "gitea.dev/modules/test" + "gitea.dev/services/auth/source/ldap" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestAddLdapBindDn(t *testing.T) { + // Mock cli functions to do not exit on error + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() + + // Test cases + cases := []struct { + args []string + source *auth.Source + errMsg string + }{ + // case 0 + { + args: []string{ + "ldap-test", + "--name", "ldap (via Bind DN) source full", + "--not-active", + "--security-protocol", "ldaps", + "--skip-tls-verify", + "--host", "ldap-bind-server full", + "--port", "9876", + "--user-search-base", "ou=Users,dc=full-domain-bind,dc=org", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", + "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", + "--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", + "--username-attribute", "uid-bind full", + "--firstname-attribute", "givenName-bind full", + "--surname-attribute", "sn-bind full", + "--email-attribute", "mail-bind full", + "--public-ssh-key-attribute", "publickey-bind full", + "--avatar-attribute", "avatar-bind full", + "--bind-dn", "cn=readonly,dc=full-domain-bind,dc=org", + "--bind-password", "secret-bind-full", + "--attributes-in-bind", + "--synchronize-users", + "--page-size", "99", + "--enable-groups", + "--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org", + "--group-member-attribute", "memberUid", + "--group-user-attribute", "uid", + "--group-filter", "(|(cn=gitea_users)(cn=admins))", + "--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`, + "--group-team-map-removal", + }, + source: &auth.Source{ + Type: auth.LDAP, + Name: "ldap (via Bind DN) source full", + IsActive: false, + IsSyncEnabled: true, + Cfg: &ldap.Source{ + Name: "ldap (via Bind DN) source full", + Host: "ldap-bind-server full", + Port: 9876, + SecurityProtocol: ldap.SecurityProtocol(1), + SkipVerify: true, + BindDN: "cn=readonly,dc=full-domain-bind,dc=org", + BindPassword: "secret-bind-full", + UserBase: "ou=Users,dc=full-domain-bind,dc=org", + AttributeUsername: "uid-bind full", + AttributeName: "givenName-bind full", + AttributeSurname: "sn-bind full", + AttributeMail: "mail-bind full", + AttributesInBind: true, + AttributeSSHPublicKey: "publickey-bind full", + AttributeAvatar: "avatar-bind full", + SearchPageSize: 99, + Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", + AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", + RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", + Enabled: true, + GroupsEnabled: true, + GroupDN: "ou=group,dc=full-domain-bind,dc=org", + GroupMemberUID: "memberUid", + UserUID: "uid", + GroupFilter: "(|(cn=gitea_users)(cn=admins))", + GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`, + GroupTeamMapRemoval: true, + }, + }, + }, + // case 1 + { + args: []string{ + "ldap-test", + "--name", "ldap (via Bind DN) source min", + "--security-protocol", "unencrypted", + "--host", "ldap-bind-server min", + "--port", "1234", + "--user-search-base", "ou=Users,dc=min-domain-bind,dc=org", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=min-domain-bind,dc=org)", + "--email-attribute", "mail-bind min", + }, + source: &auth.Source{ + Type: auth.LDAP, + Name: "ldap (via Bind DN) source min", + IsActive: true, + Cfg: &ldap.Source{ + Name: "ldap (via Bind DN) source min", + Host: "ldap-bind-server min", + Port: 1234, + SecurityProtocol: ldap.SecurityProtocol(0), + UserBase: "ou=Users,dc=min-domain-bind,dc=org", + AttributeMail: "mail-bind min", + Filter: "(memberOf=cn=user-group,ou=example,dc=min-domain-bind,dc=org)", + Enabled: true, + }, + }, + }, + // case 2 + { + args: []string{ + "ldap-test", + "--name", "ldap (via Bind DN) source", + "--security-protocol", "zzzzz", + "--host", "ldap-server", + "--port", "1234", + "--user-search-base", "ou=Users,dc=domain,dc=org", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", + "--email-attribute", "mail", + }, + errMsg: "unknown security protocol name: zzzzz", + }, + // case 3 + { + args: []string{ + "ldap-test", + "--security-protocol", "unencrypted", + "--host", "ldap-server", + "--port", "1234", + "--user-search-base", "ou=Users,dc=domain,dc=org", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", + "--email-attribute", "mail", + }, + errMsg: "name is not set", + }, + // case 4 + { + args: []string{ + "ldap-test", + "--name", "ldap (via Bind DN) source", + "--host", "ldap-server", + "--port", "1234", + "--user-search-base", "ou=Users,dc=domain,dc=org", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", + "--email-attribute", "mail", + }, + errMsg: "security-protocol is not set", + }, + // case 5 + { + args: []string{ + "ldap-test", + "--name", "ldap (via Bind DN) source", + "--security-protocol", "unencrypted", + "--port", "1234", + "--user-search-base", "ou=Users,dc=domain,dc=org", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", + "--email-attribute", "mail", + }, + errMsg: "host is not set", + }, + // case 6 + { + args: []string{ + "ldap-test", + "--name", "ldap (via Bind DN) source", + "--security-protocol", "unencrypted", + "--host", "ldap-server", + "--user-search-base", "ou=Users,dc=domain,dc=org", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", + "--email-attribute", "mail", + }, + errMsg: "port is not set", + }, + // case 7 + { + args: []string{ + "ldap-test", + "--name", "ldap (via Bind DN) source", + "--security-protocol", "unencrypted", + "--host", "ldap-server", + "--port", "1234", + "--user-search-base", "ou=Users,dc=domain,dc=org", + "--email-attribute", "mail", + }, + errMsg: "user-filter is not set", + }, + // case 8 + { + args: []string{ + "ldap-test", + "--name", "ldap (via Bind DN) source", + "--security-protocol", "unencrypted", + "--host", "ldap-server", + "--port", "1234", + "--user-search-base", "ou=Users,dc=domain,dc=org", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", + }, + errMsg: "email-attribute is not set", + }, + } + + for n, c := range cases { + // Mock functions. + var createdAuthSource *auth.Source + service := &authService{ + initDB: func(context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, authSource *auth.Source) error { + createdAuthSource = authSource + return nil + }, + updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { + assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n) + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { + assert.FailNow(t, "getAuthSourceByID called", "case %d: should not call getAuthSourceByID", n) + return nil, nil //nolint:nilnil // mock function covering improper behavior + }, + } + + // Create a copy of command to test + app := cli.Command{ + Flags: microcmdAuthAddLdapBindDn().Flags, + Action: service.addLdapBindDn, + } + + // Run it + err := app.Run(t.Context(), c.args) + if c.errMsg != "" { + assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) + } else { + assert.NoError(t, err, "case %d: should have no errors", n) + assert.Equal(t, c.source, createdAuthSource, "case %d: wrong authSource", n) + } + } +} + +func TestAddLdapSimpleAuth(t *testing.T) { + // Mock cli functions to do not exit on error + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() + + // Test cases + cases := []struct { + args []string + authSource *auth.Source + errMsg string + }{ + // case 0 + { + args: []string{ + "ldap-test", + "--name", "ldap (simple auth) source full", + "--not-active", + "--security-protocol", "starttls", + "--skip-tls-verify", + "--host", "ldap-simple-server full", + "--port", "987", + "--user-search-base", "ou=Users,dc=full-domain-simple,dc=org", + "--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))", + "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", + "--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)", + "--username-attribute", "uid-simple full", + "--firstname-attribute", "givenName-simple full", + "--surname-attribute", "sn-simple full", + "--email-attribute", "mail-simple full", + "--public-ssh-key-attribute", "publickey-simple full", + "--avatar-attribute", "avatar-simple full", + "--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Name: "ldap (simple auth) source full", + IsActive: false, + Cfg: &ldap.Source{ + Name: "ldap (simple auth) source full", + Host: "ldap-simple-server full", + Port: 987, + SecurityProtocol: ldap.SecurityProtocol(2), + SkipVerify: true, + UserDN: "cn=%s,ou=Users,dc=full-domain-simple,dc=org", + UserBase: "ou=Users,dc=full-domain-simple,dc=org", + AttributeUsername: "uid-simple full", + AttributeName: "givenName-simple full", + AttributeSurname: "sn-simple full", + AttributeMail: "mail-simple full", + AttributeSSHPublicKey: "publickey-simple full", + AttributeAvatar: "avatar-simple full", + Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))", + AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", + RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)", + Enabled: true, + }, + }, + }, + // case 1 + { + args: []string{ + "ldap-test", + "--name", "ldap (simple auth) source min", + "--security-protocol", "unencrypted", + "--host", "ldap-simple-server min", + "--port", "123", + "--user-filter", "(&(objectClass=posixAccount)(min-simple-cn=%s))", + "--email-attribute", "mail-simple min", + "--user-dn", "cn=%s,ou=Users,dc=min-domain-simple,dc=org", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Name: "ldap (simple auth) source min", + IsActive: true, + Cfg: &ldap.Source{ + Name: "ldap (simple auth) source min", + Host: "ldap-simple-server min", + Port: 123, + SecurityProtocol: ldap.SecurityProtocol(0), + UserDN: "cn=%s,ou=Users,dc=min-domain-simple,dc=org", + AttributeMail: "mail-simple min", + Filter: "(&(objectClass=posixAccount)(min-simple-cn=%s))", + Enabled: true, + }, + }, + }, + // case 2 + { + args: []string{ + "ldap-test", + "--name", "ldap (simple auth) source", + "--security-protocol", "zzzzz", + "--host", "ldap-server", + "--port", "1234", + "--user-filter", "(&(objectClass=posixAccount)(cn=%s))", + "--email-attribute", "mail", + "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", + }, + errMsg: "unknown security protocol name: zzzzz", + }, + // case 3 + { + args: []string{ + "ldap-test", + "--security-protocol", "unencrypted", + "--host", "ldap-server", + "--port", "123", + "--user-filter", "(&(objectClass=posixAccount)(cn=%s))", + "--email-attribute", "mail", + "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", + }, + errMsg: "name is not set", + }, + // case 4 + { + args: []string{ + "ldap-test", + "--name", "ldap (simple auth) source", + "--host", "ldap-server", + "--port", "123", + "--user-filter", "(&(objectClass=posixAccount)(cn=%s))", + "--email-attribute", "mail", + "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", + }, + errMsg: "security-protocol is not set", + }, + // case 5 + { + args: []string{ + "ldap-test", + "--name", "ldap (simple auth) source", + "--security-protocol", "unencrypted", + "--port", "123", + "--user-filter", "(&(objectClass=posixAccount)(cn=%s))", + "--email-attribute", "mail", + "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", + }, + errMsg: "host is not set", + }, + // case 6 + { + args: []string{ + "ldap-test", + "--name", "ldap (simple auth) source", + "--security-protocol", "unencrypted", + "--host", "ldap-server", + "--user-filter", "(&(objectClass=posixAccount)(cn=%s))", + "--email-attribute", "mail", + "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", + }, + errMsg: "port is not set", + }, + // case 7 + { + args: []string{ + "ldap-test", + "--name", "ldap (simple auth) source", + "--security-protocol", "unencrypted", + "--host", "ldap-server", + "--port", "123", + "--email-attribute", "mail", + "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", + }, + errMsg: "user-filter is not set", + }, + // case 8 + { + args: []string{ + "ldap-test", + "--name", "ldap (simple auth) source", + "--security-protocol", "unencrypted", + "--host", "ldap-server", + "--port", "123", + "--user-filter", "(&(objectClass=posixAccount)(cn=%s))", + "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", + }, + errMsg: "email-attribute is not set", + }, + // case 9 + { + args: []string{ + "ldap-test", + "--name", "ldap (simple auth) source", + "--security-protocol", "unencrypted", + "--host", "ldap-server", + "--port", "123", + "--user-filter", "(&(objectClass=posixAccount)(cn=%s))", + "--email-attribute", "mail", + }, + errMsg: "user-dn is not set", + }, + } + + for n, c := range cases { + // Mock functions. + var createdAuthSource *auth.Source + service := &authService{ + initDB: func(context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, authSource *auth.Source) error { + createdAuthSource = authSource + return nil + }, + updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { + assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n) + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { + assert.FailNow(t, "getAuthSourceById called", "case %d: should not call getAuthSourceByID", n) + return nil, nil //nolint:nilnil // mock function covering improper behavior + }, + } + + // Create a copy of command to test + app := &cli.Command{ + Flags: microcmdAuthAddLdapSimpleAuth().Flags, + Action: service.addLdapSimpleAuth, + } + + // Run it + err := app.Run(t.Context(), c.args) + if c.errMsg != "" { + assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) + } else { + assert.NoError(t, err, "case %d: should have no errors", n) + assert.Equal(t, c.authSource, createdAuthSource, "case %d: wrong authSource", n) + } + } +} + +func TestUpdateLdapBindDn(t *testing.T) { + // Mock cli functions to do not exit on error + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() + + // Test cases + cases := []struct { + args []string + id int64 + existingAuthSource *auth.Source + authSource *auth.Source + errMsg string + }{ + // case 0 + { + args: []string{ + "ldap-test", + "--id", "23", + "--name", "ldap (via Bind DN) source full", + "--not-active", + "--security-protocol", "LDAPS", + "--skip-tls-verify", + "--host", "ldap-bind-server full", + "--port", "9876", + "--user-search-base", "ou=Users,dc=full-domain-bind,dc=org", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", + "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", + "--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", + "--username-attribute", "uid-bind full", + "--firstname-attribute", "givenName-bind full", + "--surname-attribute", "sn-bind full", + "--email-attribute", "mail-bind full", + "--public-ssh-key-attribute", "publickey-bind full", + "--avatar-attribute", "avatar-bind full", + "--bind-dn", "cn=readonly,dc=full-domain-bind,dc=org", + "--bind-password", "secret-bind-full", + "--synchronize-users", + "--page-size", "99", + "--enable-groups", + "--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org", + "--group-member-attribute", "memberUid", + "--group-user-attribute", "uid", + "--group-filter", "(|(cn=gitea_users)(cn=admins))", + "--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`, + "--group-team-map-removal", + }, + id: 23, + existingAuthSource: &auth.Source{ + Type: auth.LDAP, + IsActive: true, + Cfg: &ldap.Source{ + Enabled: true, + }, + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Name: "ldap (via Bind DN) source full", + IsActive: false, + IsSyncEnabled: true, + Cfg: &ldap.Source{ + Name: "ldap (via Bind DN) source full", + Host: "ldap-bind-server full", + Port: 9876, + SecurityProtocol: ldap.SecurityProtocol(1), + SkipVerify: true, + BindDN: "cn=readonly,dc=full-domain-bind,dc=org", + BindPassword: "secret-bind-full", + UserBase: "ou=Users,dc=full-domain-bind,dc=org", + AttributeUsername: "uid-bind full", + AttributeName: "givenName-bind full", + AttributeSurname: "sn-bind full", + AttributeMail: "mail-bind full", + AttributesInBind: false, + AttributeSSHPublicKey: "publickey-bind full", + AttributeAvatar: "avatar-bind full", + SearchPageSize: 99, + Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)", + AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", + RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", + Enabled: true, + GroupsEnabled: true, + GroupDN: "ou=group,dc=full-domain-bind,dc=org", + GroupMemberUID: "memberUid", + UserUID: "uid", + GroupFilter: "(|(cn=gitea_users)(cn=admins))", + GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`, + GroupTeamMapRemoval: true, + }, + }, + }, + // case 1 + { + args: []string{ + "ldap-test", + "--id", "1", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{}, + }, + }, + // case 2 + { + args: []string{ + "ldap-test", + "--id", "1", + "--name", "ldap (via Bind DN) source", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Name: "ldap (via Bind DN) source", + Cfg: &ldap.Source{ + Name: "ldap (via Bind DN) source", + }, + }, + }, + // case 3 + { + args: []string{ + "ldap-test", + "--id", "1", + "--not-active", + }, + existingAuthSource: &auth.Source{ + Type: auth.LDAP, + IsActive: true, + Cfg: &ldap.Source{}, + }, + authSource: &auth.Source{ + Type: auth.LDAP, + IsActive: false, + Cfg: &ldap.Source{}, + }, + }, + // case 4 + { + args: []string{ + "ldap-test", + "--id", "1", + "--security-protocol", "LDAPS", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + SecurityProtocol: ldap.SecurityProtocol(1), + }, + }, + }, + // case 5 + { + args: []string{ + "ldap-test", + "--id", "1", + "--skip-tls-verify", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + SkipVerify: true, + }, + }, + }, + // case 6 + { + args: []string{ + "ldap-test", + "--id", "1", + "--host", "ldap-server", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + Host: "ldap-server", + }, + }, + }, + // case 7 + { + args: []string{ + "ldap-test", + "--id", "1", + "--port", "389", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + Port: 389, + }, + }, + }, + // case 8 + { + args: []string{ + "ldap-test", + "--id", "1", + "--user-search-base", "ou=Users,dc=domain,dc=org", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + UserBase: "ou=Users,dc=domain,dc=org", + }, + }, + }, + // case 9 + { + args: []string{ + "ldap-test", + "--id", "1", + "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + Filter: "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", + }, + }, + }, + // case 10 + { + args: []string{ + "ldap-test", + "--id", "1", + "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", + }, + }, + }, + // case 11 + { + args: []string{ + "ldap-test", + "--id", "1", + "--username-attribute", "uid", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + AttributeUsername: "uid", + }, + }, + }, + // case 12 + { + args: []string{ + "ldap-test", + "--id", "1", + "--firstname-attribute", "givenName", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + AttributeName: "givenName", + }, + }, + }, + // case 13 + { + args: []string{ + "ldap-test", + "--id", "1", + "--surname-attribute", "sn", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + AttributeSurname: "sn", + }, + }, + }, + // case 14 + { + args: []string{ + "ldap-test", + "--id", "1", + "--email-attribute", "mail", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + AttributeMail: "mail", + }, + }, + }, + // case 15 + { + args: []string{ + "ldap-test", + "--id", "1", + "--attributes-in-bind", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + AttributesInBind: true, + }, + }, + }, + // case 16 + { + args: []string{ + "ldap-test", + "--id", "1", + "--public-ssh-key-attribute", "publickey", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + AttributeSSHPublicKey: "publickey", + }, + }, + }, + // case 17 + { + args: []string{ + "ldap-test", + "--id", "1", + "--bind-dn", "cn=readonly,dc=domain,dc=org", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + BindDN: "cn=readonly,dc=domain,dc=org", + }, + }, + }, + // case 18 + { + args: []string{ + "ldap-test", + "--id", "1", + "--bind-password", "secret", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + BindPassword: "secret", + }, + }, + }, + // case 19 + { + args: []string{ + "ldap-test", + "--id", "1", + "--synchronize-users", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + IsSyncEnabled: true, + Cfg: &ldap.Source{}, + }, + }, + // case 20 + { + args: []string{ + "ldap-test", + "--id", "1", + "--page-size", "12", + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{ + SearchPageSize: 12, + }, + }, + }, + // case 21 + { + args: []string{ + "ldap-test", + "--id", "1", + "--security-protocol", "xxxxx", + }, + errMsg: "unknown security protocol name: xxxxx", + }, + // case 22 + { + args: []string{ + "ldap-test", + }, + errMsg: "id is not set", + }, + // case 23 + { + args: []string{ + "ldap-test", + "--id", "1", + }, + existingAuthSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &ldap.Source{}, + }, + errMsg: "invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2", + }, + // case 24 + { + args: []string{ + "ldap-test", + "--id", "24", + "--name", "ldap (via Bind DN) flip 'active' and 'user sync' attributes", + "--active", + "--disable-synchronize-users", + }, + id: 24, + existingAuthSource: &auth.Source{ + Type: auth.LDAP, + IsActive: false, + IsSyncEnabled: true, + Cfg: &ldap.Source{ + Name: "ldap (via Bind DN) flip 'active' and 'user sync' attributes", + Enabled: true, + }, + }, + authSource: &auth.Source{ + Type: auth.LDAP, + Name: "ldap (via Bind DN) flip 'active' and 'user sync' attributes", + IsActive: true, + IsSyncEnabled: false, + Cfg: &ldap.Source{ + Name: "ldap (via Bind DN) flip 'active' and 'user sync' attributes", + Enabled: true, + }, + }, + }, + } + + for n, c := range cases { + // Mock functions. + var updatedAuthSource *auth.Source + service := &authService{ + initDB: func(context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, authSource *auth.Source) error { + assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n) + return nil + }, + updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { + updatedAuthSource = authSource + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { + if c.id != 0 { + assert.Equal(t, c.id, id, "case %d: wrong id", n) + } + if c.existingAuthSource != nil { + return c.existingAuthSource, nil + } + return &auth.Source{ + Type: auth.LDAP, + Cfg: &ldap.Source{}, + }, nil + }, + } + + // Create a copy of command to test + app := cli.Command{ + Flags: microcmdAuthUpdateLdapBindDn().Flags, + Action: service.updateLdapBindDn, + } + // Run it + err := app.Run(t.Context(), c.args) + if c.errMsg != "" { + assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) + } else { + assert.NoError(t, err, "case %d: should have no errors", n) + assert.Equal(t, c.authSource, updatedAuthSource, "case %d: wrong authSource", n) + } + } +} + +func TestUpdateLdapSimpleAuth(t *testing.T) { + // Mock cli functions to do not exit on error + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() + + // Test cases + cases := []struct { + args []string + id int64 + existingAuthSource *auth.Source + authSource *auth.Source + errMsg string + }{ + // case 0 + { + args: []string{ + "ldap-test", + "--id", "7", + "--name", "ldap (simple auth) source full", + "--not-active", + "--security-protocol", "starttls", + "--skip-tls-verify", + "--host", "ldap-simple-server full", + "--port", "987", + "--user-search-base", "ou=Users,dc=full-domain-simple,dc=org", + "--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))", + "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", + "--restricted-filter", "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)", + "--username-attribute", "uid-simple full", + "--firstname-attribute", "givenName-simple full", + "--surname-attribute", "sn-simple full", + "--email-attribute", "mail-simple full", + "--public-ssh-key-attribute", "publickey-simple full", + "--avatar-attribute", "avatar-simple full", + "--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org", + }, + id: 7, + authSource: &auth.Source{ + Type: auth.DLDAP, + Name: "ldap (simple auth) source full", + IsActive: false, + Cfg: &ldap.Source{ + Name: "ldap (simple auth) source full", + Host: "ldap-simple-server full", + Port: 987, + SecurityProtocol: ldap.SecurityProtocol(2), + SkipVerify: true, + UserDN: "cn=%s,ou=Users,dc=full-domain-simple,dc=org", + UserBase: "ou=Users,dc=full-domain-simple,dc=org", + AttributeUsername: "uid-simple full", + AttributeName: "givenName-simple full", + AttributeSurname: "sn-simple full", + AttributeMail: "mail-simple full", + AttributeSSHPublicKey: "publickey-simple full", + AttributeAvatar: "avatar-simple full", + Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))", + AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)", + RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-simple,dc=org)", + }, + }, + }, + // case 1 + { + args: []string{ + "ldap-test", + "--id", "1", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{}, + }, + }, + // case 2 + { + args: []string{ + "ldap-test", + "--id", "1", + "--name", "ldap (simple auth) source", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Name: "ldap (simple auth) source", + Cfg: &ldap.Source{ + Name: "ldap (simple auth) source", + }, + }, + }, + // case 3 + { + args: []string{ + "ldap-test", + "--id", "1", + "--not-active", + }, + existingAuthSource: &auth.Source{ + Type: auth.DLDAP, + IsActive: true, + Cfg: &ldap.Source{}, + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + IsActive: false, + Cfg: &ldap.Source{}, + }, + }, + // case 4 + { + args: []string{ + "ldap-test", + "--id", "1", + "--security-protocol", "starttls", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + SecurityProtocol: ldap.SecurityProtocol(2), + }, + }, + }, + // case 5 + { + args: []string{ + "ldap-test", + "--id", "1", + "--skip-tls-verify", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + SkipVerify: true, + }, + }, + }, + // case 6 + { + args: []string{ + "ldap-test", + "--id", "1", + "--host", "ldap-server", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + Host: "ldap-server", + }, + }, + }, + // case 7 + { + args: []string{ + "ldap-test", + "--id", "1", + "--port", "987", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + Port: 987, + }, + }, + }, + // case 8 + { + args: []string{ + "ldap-test", + "--id", "1", + "--user-search-base", "ou=Users,dc=domain,dc=org", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + UserBase: "ou=Users,dc=domain,dc=org", + }, + }, + }, + // case 9 + { + args: []string{ + "ldap-test", + "--id", "1", + "--user-filter", "(&(objectClass=posixAccount)(cn=%s))", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + Filter: "(&(objectClass=posixAccount)(cn=%s))", + }, + }, + }, + // case 10 + { + args: []string{ + "ldap-test", + "--id", "1", + "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)", + }, + }, + }, + // case 11 + { + args: []string{ + "ldap-test", + "--id", "1", + "--username-attribute", "uid", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + AttributeUsername: "uid", + }, + }, + }, + // case 12 + { + args: []string{ + "ldap-test", + "--id", "1", + "--firstname-attribute", "givenName", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + AttributeName: "givenName", + }, + }, + }, + // case 13 + { + args: []string{ + "ldap-test", + "--id", "1", + "--surname-attribute", "sn", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + AttributeSurname: "sn", + }, + }, + }, + // case 14 + { + args: []string{ + "ldap-test", + "--id", "1", + "--email-attribute", "mail", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + AttributeMail: "mail", + }, + }, + }, + // case 15 + { + args: []string{ + "ldap-test", + "--id", "1", + "--public-ssh-key-attribute", "publickey", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + AttributeSSHPublicKey: "publickey", + }, + }, + }, + // case 16 + { + args: []string{ + "ldap-test", + "--id", "1", + "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{ + UserDN: "cn=%s,ou=Users,dc=domain,dc=org", + }, + }, + }, + // case 17 + { + args: []string{ + "ldap-test", + "--id", "1", + "--security-protocol", "xxxxx", + }, + errMsg: "unknown security protocol name: xxxxx", + }, + // case 18 + { + args: []string{ + "ldap-test", + }, + errMsg: "id is not set", + }, + // case 19 + { + args: []string{ + "ldap-test", + "--id", "1", + }, + existingAuthSource: &auth.Source{ + Type: auth.PAM, + Cfg: &ldap.Source{}, + }, + errMsg: "invalid authentication type. expected: LDAP (simple auth), actual: PAM", + }, + // case 20 + { + args: []string{ + "ldap-test", + "--id", "20", + "--name", "ldap (simple auth) flip 'active' attribute", + "--active", + }, + id: 20, + existingAuthSource: &auth.Source{ + Type: auth.DLDAP, + IsActive: false, + Cfg: &ldap.Source{ + Name: "ldap (simple auth) flip 'active' attribute", + Enabled: true, + }, + }, + authSource: &auth.Source{ + Type: auth.DLDAP, + Name: "ldap (simple auth) flip 'active' attribute", + IsActive: true, + Cfg: &ldap.Source{ + Name: "ldap (simple auth) flip 'active' attribute", + Enabled: true, + }, + }, + }, + } + + for n, c := range cases { + // Mock functions. + var updatedAuthSource *auth.Source + service := &authService{ + initDB: func(context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, authSource *auth.Source) error { + assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n) + return nil + }, + updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { + updatedAuthSource = authSource + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { + if c.id != 0 { + assert.Equal(t, c.id, id, "case %d: wrong id", n) + } + if c.existingAuthSource != nil { + return c.existingAuthSource, nil + } + return &auth.Source{ + Type: auth.DLDAP, + Cfg: &ldap.Source{}, + }, nil + }, + } + + // Create a copy of command to test + app := cli.Command{ + Flags: microcmdAuthUpdateLdapSimpleAuth().Flags, + Action: service.updateLdapSimpleAuth, + } + // Run it + err := app.Run(t.Context(), c.args) + if c.errMsg != "" { + assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) + } else { + assert.NoError(t, err, "case %d: should have no errors", n) + assert.Equal(t, c.authSource, updatedAuthSource, "case %d: wrong authSource", n) + } + } +} diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go new file mode 100644 index 0000000..f44327d --- /dev/null +++ b/cmd/admin_auth_oauth.go @@ -0,0 +1,321 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "net/url" + + auth_model "gitea.dev/models/auth" + "gitea.dev/modules/util" + "gitea.dev/services/auth/source/oauth2" + + "github.com/urfave/cli/v3" +) + +func oauthCLIFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Value: "", + Usage: "Application Name", + }, + &cli.StringFlag{ + Name: "provider", + Value: "", + Usage: "OAuth2 Provider", + }, + &cli.StringFlag{ + Name: "key", + Value: "", + Usage: "Client ID (Key)", + }, + &cli.StringFlag{ + Name: "secret", + Value: "", + Usage: "Client Secret", + }, + &cli.StringFlag{ + Name: "auto-discover-url", + Value: "", + Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)", + }, + &cli.StringFlag{ + Name: "use-custom-urls", + Value: "false", + Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints", + }, + &cli.StringFlag{ + Name: "custom-tenant-id", + Value: "", + Usage: "Use custom Tenant ID for OAuth endpoints", + }, + &cli.StringFlag{ + Name: "custom-auth-url", + Value: "", + Usage: "Use a custom Authorization URL (option for GitLab/GitHub)", + }, + &cli.StringFlag{ + Name: "custom-token-url", + Value: "", + Usage: "Use a custom Token URL (option for GitLab/GitHub)", + }, + &cli.StringFlag{ + Name: "custom-profile-url", + Value: "", + Usage: "Use a custom Profile URL (option for GitLab/GitHub)", + }, + &cli.StringFlag{ + Name: "custom-email-url", + Value: "", + Usage: "Use a custom Email URL (option for GitHub)", + }, + &cli.StringFlag{ + Name: "icon-url", + Value: "", + Usage: "Custom icon URL for OAuth2 login source", + }, + &cli.BoolFlag{ + Name: "skip-local-2fa", + Usage: "Set to true to skip local 2fa for users authenticated by this source", + }, + &cli.StringSliceFlag{ + Name: "scopes", + Value: nil, + Usage: "Scopes to request when to authenticate against this OAuth2 source", + }, + &cli.StringFlag{ + Name: "ssh-public-key-claim-name", + Usage: "Claim name that provides SSH public keys", + }, + &cli.StringFlag{ + Name: "full-name-claim-name", + Usage: "Claim name that provides user's full name", + }, + &cli.StringFlag{ + Name: "required-claim-name", + Value: "", + Usage: "Claim name that has to be set to allow users to login with this source", + }, + &cli.StringFlag{ + Name: "required-claim-value", + Value: "", + Usage: "Claim value that has to be set to allow users to login with this source", + }, + &cli.StringFlag{ + Name: "group-claim-name", + Value: "", + Usage: "Claim name providing group names for this source", + }, + &cli.StringFlag{ + Name: "admin-group", + Value: "", + Usage: "Group Claim value for administrator users", + }, + &cli.StringFlag{ + Name: "restricted-group", + Value: "", + Usage: "Group Claim value for restricted users", + }, + &cli.StringFlag{ + Name: "group-team-map", + Value: "", + Usage: "JSON mapping between groups and org teams", + }, + &cli.BoolFlag{ + Name: "group-team-map-removal", + Usage: "Activate automatic team membership removal depending on groups", + }, + } +} + +func microcmdAuthAddOauth() *cli.Command { + return &cli.Command{ + Name: "add-oauth", + Usage: "Add new Oauth authentication source", + Action: func(ctx context.Context, cmd *cli.Command) error { + return newAuthService().runAddOauth(ctx, cmd) + }, + Flags: oauthCLIFlags(), + } +} + +func microcmdAuthUpdateOauth() *cli.Command { + return &cli.Command{ + Name: "update-oauth", + Usage: "Update existing Oauth authentication source", + Action: func(ctx context.Context, cmd *cli.Command) error { + return newAuthService().runUpdateOauth(ctx, cmd) + }, + Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{ + Name: "id", + Usage: "ID of authentication source", + }}, oauthCLIFlags()[1:]...)...), + } +} + +func parseOAuth2Config(c *cli.Command) *oauth2.Source { + var customURLMapping *oauth2.CustomURLMapping + if c.IsSet("use-custom-urls") { + customURLMapping = &oauth2.CustomURLMapping{ + TokenURL: c.String("custom-token-url"), + AuthURL: c.String("custom-auth-url"), + ProfileURL: c.String("custom-profile-url"), + EmailURL: c.String("custom-email-url"), + Tenant: c.String("custom-tenant-id"), + } + } else { + customURLMapping = nil + } + return &oauth2.Source{ + Provider: c.String("provider"), + ClientID: c.String("key"), + ClientSecret: c.String("secret"), + OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"), + CustomURLMapping: customURLMapping, + IconURL: c.String("icon-url"), + Scopes: c.StringSlice("scopes"), + RequiredClaimName: c.String("required-claim-name"), + RequiredClaimValue: c.String("required-claim-value"), + GroupClaimName: c.String("group-claim-name"), + AdminGroup: c.String("admin-group"), + RestrictedGroup: c.String("restricted-group"), + GroupTeamMap: c.String("group-team-map"), + GroupTeamMapRemoval: c.Bool("group-team-map-removal"), + SSHPublicKeyClaimName: c.String("ssh-public-key-claim-name"), + FullNameClaimName: c.String("full-name-claim-name"), + } +} + +func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error { + if err := a.initDB(ctx); err != nil { + return err + } + + config := parseOAuth2Config(c) + if config.Provider == "openidConnect" { + discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL) + if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") { + return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL) + } + } + + return a.createAuthSource(ctx, &auth_model.Source{ + Type: auth_model.OAuth2, + Name: c.String("name"), + IsActive: true, + Cfg: config, + TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""), + }) +} + +func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error { + if !c.IsSet("id") { + return errors.New("--id flag is missing") + } + + if err := a.initDB(ctx); err != nil { + return err + } + + source, err := a.getAuthSourceOfType(ctx, c.Int64("id"), auth_model.OAuth2) + if err != nil { + return err + } + oAuth2Config := auth_model.MustSourceCfg[*oauth2.Source](source) + + if c.IsSet("name") { + source.Name = c.String("name") + } + + if c.IsSet("provider") { + oAuth2Config.Provider = c.String("provider") + } + + if c.IsSet("key") { + oAuth2Config.ClientID = c.String("key") + } + + if c.IsSet("secret") { + oAuth2Config.ClientSecret = c.String("secret") + } + + if c.IsSet("auto-discover-url") { + oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url") + } + + if c.IsSet("icon-url") { + oAuth2Config.IconURL = c.String("icon-url") + } + + if c.IsSet("scopes") { + oAuth2Config.Scopes = c.StringSlice("scopes") + } + + if c.IsSet("required-claim-name") { + oAuth2Config.RequiredClaimName = c.String("required-claim-name") + } + if c.IsSet("required-claim-value") { + oAuth2Config.RequiredClaimValue = c.String("required-claim-value") + } + + if c.IsSet("group-claim-name") { + oAuth2Config.GroupClaimName = c.String("group-claim-name") + } + if c.IsSet("admin-group") { + oAuth2Config.AdminGroup = c.String("admin-group") + } + if c.IsSet("restricted-group") { + oAuth2Config.RestrictedGroup = c.String("restricted-group") + } + if c.IsSet("group-team-map") { + oAuth2Config.GroupTeamMap = c.String("group-team-map") + } + if c.IsSet("group-team-map-removal") { + oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal") + } + if c.IsSet("ssh-public-key-claim-name") { + oAuth2Config.SSHPublicKeyClaimName = c.String("ssh-public-key-claim-name") + } + if c.IsSet("full-name-claim-name") { + oAuth2Config.FullNameClaimName = c.String("full-name-claim-name") + } + + // update custom URL mapping + customURLMapping := &oauth2.CustomURLMapping{} + + if oAuth2Config.CustomURLMapping != nil { + customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL + customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL + customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL + customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL + customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant + } + if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") { + customURLMapping.TokenURL = c.String("custom-token-url") + } + + if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") { + customURLMapping.AuthURL = c.String("custom-auth-url") + } + + if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") { + customURLMapping.ProfileURL = c.String("custom-profile-url") + } + + if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") { + customURLMapping.EmailURL = c.String("custom-email-url") + } + + if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") { + customURLMapping.Tenant = c.String("custom-tenant-id") + } + + oAuth2Config.CustomURLMapping = customURLMapping + source.Cfg = oAuth2Config + source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "") + return a.updateAuthSource(ctx, source) +} diff --git a/cmd/admin_auth_oauth_test.go b/cmd/admin_auth_oauth_test.go new file mode 100644 index 0000000..c16dd96 --- /dev/null +++ b/cmd/admin_auth_oauth_test.go @@ -0,0 +1,343 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "testing" + + auth_model "gitea.dev/models/auth" + "gitea.dev/services/auth/source/oauth2" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestAddOauth(t *testing.T) { + testCases := []struct { + name string + args []string + source *auth_model.Source + errMsg string + }{ + { + name: "valid config", + args: []string{ + "--name", "test", + "--provider", "github", + "--key", "some_key", + "--secret", "some_secret", + }, + source: &auth_model.Source{ + Type: auth_model.OAuth2, + Name: "test", + IsActive: true, + Cfg: &oauth2.Source{ + Scopes: []string{}, + Provider: "github", + ClientID: "some_key", + ClientSecret: "some_secret", + }, + TwoFactorPolicy: "", + }, + }, + { + name: "valid config with openid connect", + args: []string{ + "--name", "test", + "--provider", "openidConnect", + "--key", "some_key", + "--secret", "some_secret", + "--auto-discover-url", "https://example.com", + }, + source: &auth_model.Source{ + Type: auth_model.OAuth2, + Name: "test", + IsActive: true, + Cfg: &oauth2.Source{ + Scopes: []string{}, + Provider: "openidConnect", + ClientID: "some_key", + ClientSecret: "some_secret", + OpenIDConnectAutoDiscoveryURL: "https://example.com", + }, + TwoFactorPolicy: "", + }, + }, + { + name: "valid config with options", + args: []string{ + "--name", "test", + "--provider", "gitlab", + "--key", "some_key", + "--secret", "some_secret", + "--use-custom-urls", "true", + "--custom-token-url", "https://example.com/token", + "--custom-auth-url", "https://example.com/auth", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + "--custom-tenant-id", "some_tenant", + "--icon-url", "https://example.com/icon", + "--scopes", "scope1,scope2", + "--skip-local-2fa", "true", + "--required-claim-name", "claim_name", + "--required-claim-value", "claim_value", + "--group-claim-name", "group_name", + "--admin-group", "admin", + "--restricted-group", "restricted", + "--group-team-map", `{"group1": [1,2]}`, + "--group-team-map-removal=true", + "--ssh-public-key-claim-name", "attr_ssh_pub_key", + "--full-name-claim-name", "attr_full_name", + }, + source: &auth_model.Source{ + Type: auth_model.OAuth2, + Name: "test", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "gitlab", + ClientID: "some_key", + ClientSecret: "some_secret", + CustomURLMapping: &oauth2.CustomURLMapping{ + TokenURL: "https://example.com/token", + AuthURL: "https://example.com/auth", + ProfileURL: "https://example.com/profile", + EmailURL: "https://example.com/email", + Tenant: "some_tenant", + }, + IconURL: "https://example.com/icon", + Scopes: []string{"scope1", "scope2"}, + RequiredClaimName: "claim_name", + RequiredClaimValue: "claim_value", + GroupClaimName: "group_name", + AdminGroup: "admin", + RestrictedGroup: "restricted", + GroupTeamMap: `{"group1": [1,2]}`, + GroupTeamMapRemoval: true, + SSHPublicKeyClaimName: "attr_ssh_pub_key", + FullNameClaimName: "attr_full_name", + }, + TwoFactorPolicy: "skip", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var createdSource *auth_model.Source + a := &authService{ + initDB: func(ctx context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, source *auth_model.Source) error { + createdSource = source + return nil + }, + } + + app := &cli.Command{ + Flags: microcmdAuthAddOauth().Flags, + Action: a.runAddOauth, + } + + args := []string{"oauth-test"} + args = append(args, tc.args...) + + err := app.Run(t.Context(), args) + + if tc.errMsg != "" { + assert.EqualError(t, err, tc.errMsg) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.source, createdSource) + } + }) + } +} + +func TestUpdateOauth(t *testing.T) { + testCases := []struct { + name string + args []string + id int64 + existingAuthSource *auth_model.Source + authSource *auth_model.Source + errMsg string + }{ + { + name: "missing id", + args: []string{ + "--name", "test", + }, + errMsg: "--id flag is missing", + }, + { + name: "valid config", + id: 1, + existingAuthSource: &auth_model.Source{ + ID: 1, + Type: auth_model.OAuth2, + Name: "old name", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "github", + ClientID: "old_key", + ClientSecret: "old_secret", + }, + TwoFactorPolicy: "", + }, + args: []string{ + "--id", "1", + "--name", "test", + "--provider", "gitlab", + "--key", "new_key", + "--secret", "new_secret", + }, + authSource: &auth_model.Source{ + ID: 1, + Type: auth_model.OAuth2, + Name: "test", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "gitlab", + ClientID: "new_key", + ClientSecret: "new_secret", + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + TwoFactorPolicy: "", + }, + }, + { + name: "valid config with options", + id: 1, + existingAuthSource: &auth_model.Source{ + ID: 1, + Type: auth_model.OAuth2, + Name: "old name", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "gitlab", + ClientID: "old_key", + ClientSecret: "old_secret", + CustomURLMapping: &oauth2.CustomURLMapping{ + TokenURL: "https://old.example.com/token", + AuthURL: "https://old.example.com/auth", + ProfileURL: "https://old.example.com/profile", + EmailURL: "https://old.example.com/email", + Tenant: "old_tenant", + }, + IconURL: "https://old.example.com/icon", + Scopes: []string{"old_scope1", "old_scope2"}, + RequiredClaimName: "old_claim_name", + RequiredClaimValue: "old_claim_value", + GroupClaimName: "old_group_name", + AdminGroup: "old_admin", + RestrictedGroup: "old_restricted", + GroupTeamMap: `{"old_group1": [1,2]}`, + GroupTeamMapRemoval: true, + SSHPublicKeyClaimName: "old_ssh_pub_key", + FullNameClaimName: "old_full_name", + }, + TwoFactorPolicy: "", + }, + args: []string{ + "--id", "1", + "--name", "test", + "--provider", "github", + "--key", "new_key", + "--secret", "new_secret", + "--use-custom-urls", "true", + "--custom-token-url", "https://example.com/token", + "--custom-auth-url", "https://example.com/auth", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + "--custom-tenant-id", "new_tenant", + "--icon-url", "https://example.com/icon", + "--scopes", "scope1,scope2", + "--skip-local-2fa=true", + "--required-claim-name", "claim_name", + "--required-claim-value", "claim_value", + "--group-claim-name", "group_name", + "--admin-group", "admin", + "--restricted-group", "restricted", + "--group-team-map", `{"group1": [1,2]}`, + "--group-team-map-removal=false", + "--ssh-public-key-claim-name", "new_ssh_pub_key", + "--full-name-claim-name", "new_full_name", + }, + authSource: &auth_model.Source{ + ID: 1, + Type: auth_model.OAuth2, + Name: "test", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "github", + ClientID: "new_key", + ClientSecret: "new_secret", + CustomURLMapping: &oauth2.CustomURLMapping{ + TokenURL: "https://example.com/token", + AuthURL: "https://example.com/auth", + ProfileURL: "https://example.com/profile", + EmailURL: "https://example.com/email", + Tenant: "new_tenant", + }, + IconURL: "https://example.com/icon", + Scopes: []string{"scope1", "scope2"}, + RequiredClaimName: "claim_name", + RequiredClaimValue: "claim_value", + GroupClaimName: "group_name", + AdminGroup: "admin", + RestrictedGroup: "restricted", + GroupTeamMap: `{"group1": [1,2]}`, + GroupTeamMapRemoval: false, + SSHPublicKeyClaimName: "new_ssh_pub_key", + FullNameClaimName: "new_full_name", + }, + TwoFactorPolicy: "skip", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + a := &authService{ + initDB: func(ctx context.Context) error { + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) { + return &auth_model.Source{ + ID: 1, + Type: auth_model.OAuth2, + Name: "test", + IsActive: true, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + TwoFactorPolicy: "skip", + }, nil + }, + updateAuthSource: func(ctx context.Context, source *auth_model.Source) error { + assert.Equal(t, tc.authSource, source) + return nil + }, + } + + app := &cli.Command{ + Flags: microcmdAuthUpdateOauth().Flags, + Action: a.runUpdateOauth, + } + + args := []string{"oauth-test"} + args = append(args, tc.args...) + + err := app.Run(t.Context(), args) + + if tc.errMsg != "" { + assert.EqualError(t, err, tc.errMsg) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/cmd/admin_auth_smtp.go b/cmd/admin_auth_smtp.go new file mode 100644 index 0000000..5d99b5e --- /dev/null +++ b/cmd/admin_auth_smtp.go @@ -0,0 +1,199 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "strings" + + auth_model "gitea.dev/models/auth" + "gitea.dev/modules/util" + "gitea.dev/services/auth/source/smtp" + + "github.com/urfave/cli/v3" +) + +func smtpCLIFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Value: "", + Usage: "Application Name", + }, + &cli.StringFlag{ + Name: "auth-type", + Value: "PLAIN", + Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN", + }, + &cli.StringFlag{ + Name: "host", + Value: "", + Usage: "SMTP Host", + }, + &cli.IntFlag{ + Name: "port", + Usage: "SMTP Port", + }, + &cli.BoolFlag{ + Name: "force-smtps", + Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.", + }, + &cli.BoolFlag{ + Name: "skip-verify", + Usage: "Skip TLS verify.", + }, + &cli.StringFlag{ + Name: "helo-hostname", + Value: "", + Usage: "Hostname sent with HELO. Leave blank to send current hostname", + }, + &cli.BoolFlag{ + Name: "disable-helo", + Usage: "Disable SMTP helo.", + }, + &cli.StringFlag{ + Name: "allowed-domains", + Value: "", + Usage: "Leave empty to allow all domains. Separate multiple domains with a comma (',')", + }, + &cli.BoolFlag{ + Name: "skip-local-2fa", + Usage: "Skip 2FA to log on.", + }, + &cli.BoolFlag{ + Name: "active", + Usage: "This Authentication Source is Activated.", + Value: true, + }, + } +} + +func microcmdAuthUpdateSMTP() *cli.Command { + return &cli.Command{ + Name: "update-smtp", + Usage: "Update existing SMTP authentication source", + Action: func(ctx context.Context, cmd *cli.Command) error { + return newAuthService().runUpdateSMTP(ctx, cmd) + }, + Flags: append(smtpCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{ + Name: "id", + Usage: "ID of authentication source", + }}, smtpCLIFlags()[1:]...)...), + } +} + +func microcmdAuthAddSMTP() *cli.Command { + return &cli.Command{ + Name: "add-smtp", + Usage: "Add new SMTP authentication source", + Action: func(ctx context.Context, cmd *cli.Command) error { + return newAuthService().runAddSMTP(ctx, cmd) + }, + Flags: smtpCLIFlags(), + } +} + +func parseSMTPConfig(c *cli.Command, conf *smtp.Source) error { + if c.IsSet("auth-type") { + conf.Auth = c.String("auth-type") + validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"} + if !util.SliceContainsString(validAuthTypes, strings.ToUpper(c.String("auth-type"))) { + return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5") + } + conf.Auth = c.String("auth-type") + } + if c.IsSet("host") { + conf.Host = c.String("host") + } + if c.IsSet("port") { + conf.Port = c.Int("port") + } + if c.IsSet("allowed-domains") { + conf.AllowedDomains = c.String("allowed-domains") + } + if c.IsSet("force-smtps") { + conf.ForceSMTPS = c.Bool("force-smtps") + } + if c.IsSet("skip-verify") { + conf.SkipVerify = c.Bool("skip-verify") + } + if c.IsSet("helo-hostname") { + conf.HeloHostname = c.String("helo-hostname") + } + if c.IsSet("disable-helo") { + conf.DisableHelo = c.Bool("disable-helo") + } + return nil +} + +func (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error { + if err := a.initDB(ctx); err != nil { + return err + } + + if !c.IsSet("name") || len(c.String("name")) == 0 { + return errors.New("name must be set") + } + if !c.IsSet("host") || len(c.String("host")) == 0 { + return errors.New("host must be set") + } + if !c.IsSet("port") { + return errors.New("port must be set") + } + active := true + if c.IsSet("active") { + active = c.Bool("active") + } + + var smtpConfig smtp.Source + if err := parseSMTPConfig(c, &smtpConfig); err != nil { + return err + } + + // If not set default to PLAIN + if len(smtpConfig.Auth) == 0 { + smtpConfig.Auth = "PLAIN" + } + + return a.createAuthSource(ctx, &auth_model.Source{ + Type: auth_model.SMTP, + Name: c.String("name"), + IsActive: active, + Cfg: &smtpConfig, + TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""), + }) +} + +func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error { + if !c.IsSet("id") { + return errors.New("--id flag is missing") + } + + if err := a.initDB(ctx); err != nil { + return err + } + + source, err := a.getAuthSourceOfType(ctx, c.Int64("id"), auth_model.SMTP) + if err != nil { + return err + } + smtpConfig := auth_model.MustSourceCfg[*smtp.Source](source) + + if err := parseSMTPConfig(c, smtpConfig); err != nil { + return err + } + + if c.IsSet("name") { + source.Name = c.String("name") + } + + if c.IsSet("active") { + source.IsActive = c.Bool("active") + } + + source.Cfg = smtpConfig + source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "") + return a.updateAuthSource(ctx, source) +} diff --git a/cmd/admin_auth_smtp_test.go b/cmd/admin_auth_smtp_test.go new file mode 100644 index 0000000..2b890f1 --- /dev/null +++ b/cmd/admin_auth_smtp_test.go @@ -0,0 +1,271 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "testing" + + auth_model "gitea.dev/models/auth" + "gitea.dev/services/auth/source/smtp" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestAddSMTP(t *testing.T) { + testCases := []struct { + name string + args []string + source *auth_model.Source + errMsg string + }{ + { + name: "missing name", + args: []string{ + "--host", "localhost", + "--port", "25", + }, + errMsg: "name must be set", + }, + { + name: "missing host", + args: []string{ + "--name", "test", + "--port", "25", + }, + errMsg: "host must be set", + }, + { + name: "missing port", + args: []string{ + "--name", "test", + "--host", "localhost", + }, + errMsg: "port must be set", + }, + { + name: "valid config", + args: []string{ + "--name", "test", + "--host", "localhost", + "--port", "25", + }, + source: &auth_model.Source{ + Type: auth_model.SMTP, + Name: "test", + IsActive: true, + Cfg: &smtp.Source{ + Auth: "PLAIN", + Host: "localhost", + Port: 25, + }, + TwoFactorPolicy: "", + }, + }, + { + name: "valid config with options", + args: []string{ + "--name", "test", + "--host", "localhost", + "--port", "25", + "--auth-type", "LOGIN", + "--force-smtps", + "--skip-verify", + "--helo-hostname", "example.com", + "--disable-helo=true", + "--allowed-domains", "example.com,example.org", + "--skip-local-2fa", + "--active=false", + }, + source: &auth_model.Source{ + Type: auth_model.SMTP, + Name: "test", + IsActive: false, + Cfg: &smtp.Source{ + Auth: "LOGIN", + Host: "localhost", + Port: 25, + ForceSMTPS: true, + SkipVerify: true, + HeloHostname: "example.com", + DisableHelo: true, + AllowedDomains: "example.com,example.org", + }, + TwoFactorPolicy: "skip", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + a := &authService{ + initDB: func(ctx context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, source *auth_model.Source) error { + assert.Equal(t, tc.source, source) + return nil + }, + } + + cmd := &cli.Command{ + Flags: microcmdAuthAddSMTP().Flags, + Action: a.runAddSMTP, + } + + args := []string{"smtp-test"} + args = append(args, tc.args...) + + t.Log(args) + err := cmd.Run(t.Context(), args) + + if tc.errMsg != "" { + assert.EqualError(t, err, tc.errMsg) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestUpdateSMTP(t *testing.T) { + testCases := []struct { + name string + args []string + existingAuthSource *auth_model.Source + authSource *auth_model.Source + errMsg string + }{ + { + name: "missing id", + args: []string{ + "--name", "test", + "--host", "localhost", + "--port", "25", + }, + errMsg: "--id flag is missing", + }, + { + name: "valid config", + existingAuthSource: &auth_model.Source{ + ID: 1, + Type: auth_model.SMTP, + Name: "old name", + IsActive: true, + Cfg: &smtp.Source{ + Auth: "PLAIN", + Host: "old host", + Port: 26, + }, + }, + args: []string{ + "--id", "1", + "--name", "test", + "--host", "localhost", + "--port", "25", + }, + authSource: &auth_model.Source{ + ID: 1, + Type: auth_model.SMTP, + Name: "test", + IsActive: true, + Cfg: &smtp.Source{ + Auth: "PLAIN", + Host: "localhost", + Port: 25, + }, + }, + }, + { + name: "valid config with options", + existingAuthSource: &auth_model.Source{ + ID: 1, + Type: auth_model.SMTP, + Name: "old name", + IsActive: true, + Cfg: &smtp.Source{ + Auth: "PLAIN", + Host: "old host", + Port: 26, + HeloHostname: "old.example.com", + AllowedDomains: "old.example.com", + }, + TwoFactorPolicy: "", + }, + args: []string{ + "--id", "1", + "--name", "test", + "--host", "localhost", + "--port", "25", + "--auth-type", "LOGIN", + "--force-smtps", + "--skip-verify", + "--helo-hostname", "example.com", + "--disable-helo", + "--allowed-domains", "example.com,example.org", + "--skip-local-2fa", + "--active=false", + }, + authSource: &auth_model.Source{ + ID: 1, + Type: auth_model.SMTP, + Name: "test", + IsActive: false, + Cfg: &smtp.Source{ + Auth: "LOGIN", + Host: "localhost", + Port: 25, + ForceSMTPS: true, + SkipVerify: true, + HeloHostname: "example.com", + DisableHelo: true, + AllowedDomains: "example.com,example.org", + }, + TwoFactorPolicy: "skip", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + a := &authService{ + initDB: func(ctx context.Context) error { + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) { + return &auth_model.Source{ + ID: 1, + Type: auth_model.SMTP, + Name: "test", + IsActive: true, + Cfg: &smtp.Source{ + Auth: "PLAIN", + }, + }, nil + }, + + updateAuthSource: func(ctx context.Context, source *auth_model.Source) error { + assert.Equal(t, tc.authSource, source) + return nil + }, + } + + app := &cli.Command{ + Flags: microcmdAuthUpdateSMTP().Flags, + Action: a.runUpdateSMTP, + } + args := []string{"smtp-tests"} + args = append(args, tc.args...) + + err := app.Run(t.Context(), args) + + if tc.errMsg != "" { + assert.EqualError(t, err, tc.errMsg) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/cmd/admin_regenerate.go b/cmd/admin_regenerate.go new file mode 100644 index 0000000..89b7bf3 --- /dev/null +++ b/cmd/admin_regenerate.go @@ -0,0 +1,44 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + + "gitea.dev/modules/graceful" + asymkey_service "gitea.dev/services/asymkey" + repo_service "gitea.dev/services/repository" + + "github.com/urfave/cli/v3" +) + +func newRegenerateHooksCommand() *cli.Command { + return &cli.Command{ + Name: "hooks", + Usage: "Regenerate git-hooks", + Action: runRegenerateHooks, + } +} + +func newRegenerateKeysCommand() *cli.Command { + return &cli.Command{ + Name: "keys", + Usage: "Regenerate authorized_keys file", + Action: runRegenerateKeys, + } +} + +func runRegenerateHooks(ctx context.Context, _ *cli.Command) error { + if err := initDB(ctx); err != nil { + return err + } + return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext()) +} + +func runRegenerateKeys(ctx context.Context, _ *cli.Command) error { + if err := initDB(ctx); err != nil { + return err + } + return asymkey_service.RewriteAllPublicKeys(ctx) +} diff --git a/cmd/admin_user.go b/cmd/admin_user.go new file mode 100644 index 0000000..471154b --- /dev/null +++ b/cmd/admin_user.go @@ -0,0 +1,29 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + + "github.com/urfave/cli/v3" +) + +func newUserCommand() *cli.Command { + return &cli.Command{ + Name: "user", + Usage: "Modify users", + Before: func(ctx context.Context, _ *cli.Command) (context.Context, error) { + return cliAuditContext(ctx), nil + }, + Commands: []*cli.Command{ + microcmdUserCreate(), + newUserListCommand(), + microcmdUserChangePassword(), + microcmdUserDelete(), + newUserGenerateAccessTokenCommand(), + microcmdUserMustChangePassword(), + microcmdUserDisableTwoFactor(), + }, + } +} diff --git a/cmd/admin_user_change_password.go b/cmd/admin_user_change_password.go new file mode 100644 index 0000000..46e0588 --- /dev/null +++ b/cmd/admin_user_change_password.go @@ -0,0 +1,78 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + + user_model "gitea.dev/models/user" + "gitea.dev/modules/auth/password" + "gitea.dev/modules/optional" + "gitea.dev/modules/setting" + user_service "gitea.dev/services/user" + + "github.com/urfave/cli/v3" +) + +func microcmdUserChangePassword() *cli.Command { + return &cli.Command{ + Name: "change-password", + Usage: "Change a user's password", + Action: runChangePassword, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "username", + Aliases: []string{"u"}, + Usage: "The user to change password for", + Required: true, + }, + &cli.StringFlag{ + Name: "password", + Aliases: []string{"p"}, + Usage: "New password to set for user", + Required: true, + }, + &cli.BoolFlag{ + Name: "must-change-password", + Usage: "User must change password (can be disabled by --must-change-password=false)", + Value: true, + }, + }, + } +} + +func runChangePassword(ctx context.Context, c *cli.Command) error { + if !setting.IsInTesting { + if err := initDB(ctx); err != nil { + return err + } + } + + user, err := user_model.GetUserByName(ctx, c.String("username")) + if err != nil { + return err + } + + opts := &user_service.UpdateAuthOptions{ + Password: optional.Some(c.String("password")), + MustChangePassword: optional.Some(c.Bool("must-change-password")), + } + if err := user_service.UpdateAuth(ctx, user, opts); err != nil { + switch { + case errors.Is(err, password.ErrMinLength): + return fmt.Errorf("password is not long enough, needs to be at least %d characters", setting.MinPasswordLength) + case errors.Is(err, password.ErrComplexity): + return errors.New("password does not meet complexity requirements") + case errors.Is(err, password.ErrIsPwned): + return errors.New("the password is in a list of stolen passwords previously exposed in public data breaches, please try again with a different password, to see more details: https://haveibeenpwned.com/Passwords") + default: + return err + } + } + + cprintf(c, "%s's password has been successfully updated!\n", user.Name) + return nil +} diff --git a/cmd/admin_user_change_password_test.go b/cmd/admin_user_change_password_test.go new file mode 100644 index 0000000..50fd8d9 --- /dev/null +++ b/cmd/admin_user_change_password_test.go @@ -0,0 +1,94 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "io" + "testing" + + "gitea.dev/models/db" + "gitea.dev/models/unittest" + user_model "gitea.dev/models/user" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestChangePasswordCommand(t *testing.T) { + ctx := t.Context() + + defer func() { + require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{})) + }() + + t.Run("change password successfully", func(t *testing.T) { + // defer func() { + // require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{})) + // }() + // Prepare test user + unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) + err := microcmdUserCreate().Run(ctx, []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"}) + require.NoError(t, err) + + // load test user + userBase := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + + // Change the password + err = microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "newpassword"}) + require.NoError(t, err) + + // Verify the password has been changed + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + assert.NotEqual(t, userBase.Passwd, user.Passwd) + assert.NotEqual(t, userBase.Salt, user.Salt) + + // Additional check for must-change-password flag + require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "anotherpassword", "--must-change-password=false"})) + user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + assert.False(t, user.MustChangePassword) + + require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "yetanotherpassword", "--must-change-password"})) + user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + assert.True(t, user.MustChangePassword) + }) + + t.Run("failure cases", func(t *testing.T) { + testCases := []struct { + name string + args []string + expectedErr string + }{ + { + name: "user does not exist", + args: []string{"change-password", "--username", "nonexistentuser", "--password", "newpassword"}, + expectedErr: "user does not exist", + }, + { + name: "missing username", + args: []string{"change-password", "--password", "newpassword"}, + expectedErr: `"username" not set`, + }, + { + name: "missing password", + args: []string{"change-password", "--username", "testuser"}, + expectedErr: `"password" not set`, + }, + { + name: "too short password", + args: []string{"change-password", "--username", "testuser", "--password", "1"}, + expectedErr: "password is not long enough", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmd := microcmdUserChangePassword() + cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard + err := cmd.Run(ctx, tc.args) + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErr) + }) + } + }) +} diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go new file mode 100644 index 0000000..2db926d --- /dev/null +++ b/cmd/admin_user_create.go @@ -0,0 +1,242 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "strings" + + auth_model "gitea.dev/models/auth" + "gitea.dev/models/db" + user_model "gitea.dev/models/user" + pwd "gitea.dev/modules/auth/password" + "gitea.dev/modules/optional" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +func microcmdUserCreate() *cli.Command { + return &cli.Command{ + Name: "create", + Usage: "Create a new user in database", + Action: runCreateUser, + MutuallyExclusiveFlags: []cli.MutuallyExclusiveFlags{ + { + Flags: [][]cli.Flag{ + { + &cli.StringFlag{ + Name: "name", + Usage: "Username. DEPRECATED: use username instead", + }, + &cli.StringFlag{ + Name: "username", + Usage: "Username", + }, + }, + }, + Required: true, + }, + }, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "user-type", + Usage: "Set user's type: individual or bot", + Value: "individual", + }, + &cli.StringFlag{ + Name: "password", + Usage: "User password", + }, + &cli.StringFlag{ + Name: "email", + Usage: "User email address", + Required: true, + }, + &cli.BoolFlag{ + Name: "admin", + Usage: "User is an admin", + }, + &cli.BoolFlag{ + Name: "random-password", + Usage: "Generate a random password for the user", + }, + &cli.BoolFlag{ + Name: "must-change-password", + Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)", + HideDefault: true, + }, + &cli.IntFlag{ + Name: "random-password-length", + Usage: "Length of the random password to be generated", + Value: 12, + }, + &cli.BoolFlag{ + Name: "access-token", + Usage: "Generate access token for the user", + }, + &cli.StringFlag{ + Name: "access-token-name", + Usage: `Name of the generated access token`, + Value: "gitea-admin", + }, + &cli.StringFlag{ + Name: "access-token-scopes", + Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`, + Value: "all", + }, + &cli.BoolFlag{ + Name: "restricted", + Usage: "Make a restricted user account", + }, + &cli.StringFlag{ + Name: "fullname", + Usage: `The full, human-readable name of the user`, + }, + }, + } +} + +func runCreateUser(ctx context.Context, c *cli.Command) error { + // this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first + // duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future. + setting.LoadSettings() + + userTypes := map[string]user_model.UserType{ + "individual": user_model.UserTypeIndividual, + "bot": user_model.UserTypeBot, + } + userType, ok := userTypes[c.String("user-type")] + if !ok { + return fmt.Errorf("invalid user type: %s", c.String("user-type")) + } + if userType != user_model.UserTypeIndividual { + // Some other commands like "change-password" also only support individual users. + // It needs to clarify the "password" behavior for bot users in the future. + // At the moment, we do not allow setting password for bot users. + if c.IsSet("password") || c.IsSet("random-password") { + return errors.New("password can only be set for individual users") + } + } + + if c.IsSet("password") && c.IsSet("random-password") { + return errors.New("cannot set both -random-password and -password flags") + } + + var username string + if c.IsSet("username") { + username = c.String("username") + } else { + username = c.String("name") + _, _ = fmt.Fprintf(c.ErrWriter, "--name flag is deprecated. Use --username instead.\n") + } + + if !setting.IsInTesting { + // FIXME: need to refactor the "initDB" related code later + // it doesn't make sense to call it in (almost) every command action function + if err := initDB(ctx); err != nil { + return err + } + } + + var password string + if c.IsSet("password") { + password = c.String("password") + } else if c.IsSet("random-password") { + var err error + password, err = pwd.Generate(c.Int("random-password-length")) + if err != nil { + return err + } + // codeql[disable-next-line=go/clear-text-logging] + cprintf(c, "generated random password is '%s'\n", password) + } else if userType == user_model.UserTypeIndividual { + return errors.New("must set either password or random-password flag") + } + + isAdmin := c.Bool("admin") + // Only local, existing, regular users should be forced to update their password. Bot users for example are non-interactive + mustChangePassword := userType == user_model.UserTypeIndividual + if c.IsSet("must-change-password") { + if userType != user_model.UserTypeIndividual { + return errors.New("must-change-password flag can only be set for individual users") + } + // if the flag is set, use the value provided by the user + mustChangePassword = c.Bool("must-change-password") + } else if userType == user_model.UserTypeIndividual { + // check whether there are users in the database + hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{}) + if err != nil { + return fmt.Errorf("IsTableNotEmpty: %w", err) + } + if !hasUserRecord { + // if this is the first one being created, don't force to change password (keep the old behavior) + mustChangePassword = false + } + } + + restricted := optional.None[bool]() + + if c.IsSet("restricted") { + restricted = optional.Some(c.Bool("restricted")) + } + + // default user visibility in app.ini + visibility := setting.Service.DefaultUserVisibilityMode + + u := &user_model.User{ + Name: username, + Email: c.String("email"), + IsAdmin: isAdmin, + Type: userType, + Passwd: password, + MustChangePassword: mustChangePassword, + Visibility: visibility, + FullName: c.String("fullname"), + } + + overwriteDefault := &user_model.CreateUserOverwriteOptions{ + IsActive: optional.Some(true), + IsRestricted: restricted, + } + + var accessTokenName string + var accessTokenScope auth_model.AccessTokenScope + if c.IsSet("access-token") { + accessTokenName = strings.TrimSpace(c.String("access-token-name")) + if accessTokenName == "" { + return errors.New("access-token-name cannot be empty") + } + var err error + accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize() + if err != nil { + return fmt.Errorf("invalid access token scope provided: %w", err) + } + if !accessTokenScope.HasPermissionScope() { + return errors.New("access token does not have any permission") + } + } else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") { + return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set") + } + + // arguments should be prepared before creating the user & access token, in case there is anything wrong + + // create the user + if err := user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil { + return fmt.Errorf("CreateUser: %w", err) + } + cprintf(c, "New user '%s' has been successfully created!\n", username) + + // create the access token + if accessTokenScope != "" { + t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope} + if err := auth_model.NewAccessToken(ctx, t); err != nil { + return err + } + cprintf(c, "Access token was successfully created... %s\n", t.Token) + } + return nil +} diff --git a/cmd/admin_user_create_test.go b/cmd/admin_user_create_test.go new file mode 100644 index 0000000..ece2e88 --- /dev/null +++ b/cmd/admin_user_create_test.go @@ -0,0 +1,135 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "fmt" + "strings" + "testing" + + auth_model "gitea.dev/models/auth" + "gitea.dev/models/db" + "gitea.dev/models/unittest" + user_model "gitea.dev/models/user" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAdminUserCreate(t *testing.T) { + reset := func() { + require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{})) + require.NoError(t, db.TruncateBeans(t.Context(), &user_model.EmailAddress{})) + require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{})) + } + + t.Run("MustChangePassword", func(t *testing.T) { + type check struct { + IsAdmin bool + MustChangePassword bool + } + + createCheck := func(name, args string) check { + require.NoError(t, microcmdUserCreate().Run(t.Context(), strings.Fields(fmt.Sprintf("create --username %s --email %s@gitea.local %s --password foobar", name, name, args)))) + u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name}) + return check{IsAdmin: u.IsAdmin, MustChangePassword: u.MustChangePassword} + } + reset() + assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u", ""), "first non-admin user doesn't need to change password") + + reset() + assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u", "--admin"), "first admin user doesn't need to change password") + + reset() + assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u", "--admin --must-change-password")) + assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u2", "--admin")) + assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u3", "--admin --must-change-password=false")) + assert.Equal(t, check{IsAdmin: false, MustChangePassword: true}, createCheck("u4", "")) + assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u5", "--must-change-password=false")) + }) + + createUser := func(name string, args ...string) error { + return microcmdUserCreate().Run(t.Context(), append([]string{"create", "--username", name, "--email", name + "@gitea.local"}, args...)) + } + + t.Run("UserType", func(t *testing.T) { + reset() + assert.ErrorContains(t, createUser("u", "--user-type", "invalid"), "invalid user type") + assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--password", "123"), "can only be set for individual users") + assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--must-change-password"), "can only be set for individual users") + + assert.NoError(t, createUser("u", "--user-type", "bot")) + u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"}) + assert.Equal(t, user_model.UserTypeBot, u.Type) + assert.Empty(t, u.Passwd) + assert.False(t, u.MustChangePassword, "bot users should not be forced to change password") + }) + + t.Run("AccessToken", func(t *testing.T) { + // no generated access token + reset() + assert.NoError(t, createUser("u", "--random-password")) + assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{})) + assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{})) + + // using "--access-token" only means "all" access + reset() + assert.NoError(t, createUser("u", "--random-password", "--access-token")) + assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{})) + assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{})) + accessToken := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "gitea-admin"}) + hasScopes, err := accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository) + assert.NoError(t, err) + assert.True(t, hasScopes) + + // using "--access-token" with name & scopes + reset() + assert.NoError(t, createUser("u", "--random-password", "--access-token", "--access-token-name", "new-token-name", "--access-token-scopes", "read:issue,read:user")) + assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{})) + assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{})) + accessToken = unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "new-token-name"}) + hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopeReadUser) + assert.NoError(t, err) + assert.True(t, hasScopes) + hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository) + assert.NoError(t, err) + assert.False(t, hasScopes) + + // using "--access-token-name" without "--access-token" + reset() + err = createUser("u", "--random-password", "--access-token-name", "new-token-name") + assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{})) + assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{})) + assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set") + + // using "--access-token-scopes" without "--access-token" + reset() + err = createUser("u", "--random-password", "--access-token-scopes", "read:issue") + assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{})) + assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{})) + assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set") + + // empty permission + reset() + err = createUser("u", "--random-password", "--access-token", "--access-token-scopes", "public-only") + assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{})) + assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{})) + assert.ErrorContains(t, err, "access token does not have any permission") + }) + + t.Run("UserFields", func(t *testing.T) { + reset() + assert.NoError(t, createUser("u-FullNameWithSpace", "--random-password", "--fullname", "First O'Middle Last")) + unittest.AssertExistsAndLoadBean(t, &user_model.User{ + Name: "u-FullNameWithSpace", + LowerName: "u-fullnamewithspace", + FullName: "First O'Middle Last", + Email: "u-FullNameWithSpace@gitea.local", + }) + + assert.NoError(t, createUser("u-FullNameEmpty", "--random-password", "--fullname", "")) + u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u-fullnameempty"}) + assert.Empty(t, u.FullName) + }) +} diff --git a/cmd/admin_user_delete.go b/cmd/admin_user_delete.go new file mode 100644 index 0000000..cc70fe7 --- /dev/null +++ b/cmd/admin_user_delete.go @@ -0,0 +1,84 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "strings" + + user_model "gitea.dev/models/user" + "gitea.dev/modules/setting" + "gitea.dev/modules/storage" + user_service "gitea.dev/services/user" + + "github.com/urfave/cli/v3" +) + +func microcmdUserDelete() *cli.Command { + return &cli.Command{ + Name: "delete", + Usage: "Delete specific user by id, name or email", + Flags: []cli.Flag{ + &cli.Int64Flag{ + Name: "id", + Usage: "ID of user of the user to delete", + }, + &cli.StringFlag{ + Name: "username", + Aliases: []string{"u"}, + Usage: "Username of the user to delete", + }, + &cli.StringFlag{ + Name: "email", + Aliases: []string{"e"}, + Usage: "Email of the user to delete", + }, + &cli.BoolFlag{ + Name: "purge", + Usage: "Purge user, all their repositories, organizations and comments", + }, + }, + Action: runDeleteUser, + } +} + +func runDeleteUser(ctx context.Context, c *cli.Command) error { + if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") { + return errors.New("You must provide the id, username or email of a user to delete") + } + + if !setting.IsInTesting { + if err := initDB(ctx); err != nil { + return err + } + } + + if err := storage.Init(); err != nil { + return err + } + + var err error + var user *user_model.User + if c.IsSet("email") { + user, err = user_model.GetUserByEmail(ctx, c.String("email")) + } else if c.IsSet("username") { + user, err = user_model.GetUserByName(ctx, c.String("username")) + } else { + user, err = user_model.GetUserByID(ctx, c.Int64("id")) + } + if err != nil { + return err + } + if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) { + return fmt.Errorf("the user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username")) + } + + if c.IsSet("id") && user.ID != c.Int64("id") { + return fmt.Errorf("the user %s does not match the provided id %d", user.Name, c.Int64("id")) + } + + return user_service.DeleteUser(ctx, user, c.Bool("purge")) +} diff --git a/cmd/admin_user_delete_test.go b/cmd/admin_user_delete_test.go new file mode 100644 index 0000000..4fbd043 --- /dev/null +++ b/cmd/admin_user_delete_test.go @@ -0,0 +1,111 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "strconv" + "strings" + "testing" + + auth_model "gitea.dev/models/auth" + "gitea.dev/models/db" + "gitea.dev/models/unittest" + user_model "gitea.dev/models/user" + + "github.com/stretchr/testify/require" +) + +func TestAdminUserDelete(t *testing.T) { + ctx := t.Context() + defer func() { + require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{})) + require.NoError(t, db.TruncateBeans(t.Context(), &user_model.EmailAddress{})) + require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{})) + }() + + setupTestUser := func(t *testing.T) { + unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) + err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"}) + require.NoError(t, err) + } + + t.Run("delete user by id", func(t *testing.T) { + setupTestUser(t) + + u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--id", strconv.FormatInt(u.ID, 10)}) + require.NoError(t, err) + unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) + }) + t.Run("delete user by username", func(t *testing.T) { + setupTestUser(t) + + err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--username", "testuser"}) + require.NoError(t, err) + unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) + }) + t.Run("delete user by email", func(t *testing.T) { + setupTestUser(t) + + err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--email", "testuser@gitea.local"}) + require.NoError(t, err) + unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) + }) + t.Run("delete user by all 3 attributes", func(t *testing.T) { + setupTestUser(t) + + u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + err := microcmdUserDelete().Run(ctx, []string{"delete", "--id", strconv.FormatInt(u.ID, 10), "--username", "testuser", "--email", "testuser@gitea.local"}) + require.NoError(t, err) + unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) + }) +} + +func TestAdminUserDeleteFailure(t *testing.T) { + testCases := []struct { + name string + args []string + expectedErr string + }{ + { + name: "no user to delete", + args: []string{"delete", "--username", "nonexistentuser"}, + expectedErr: "user does not exist", + }, + { + name: "user exists but provided username does not match", + args: []string{"delete", "--email", "testuser@gitea.local", "--username", "wrongusername"}, + expectedErr: "the user testuser who has email testuser@gitea.local does not match the provided username wrongusername", + }, + { + name: "user exists but provided id does not match", + args: []string{"delete", "--username", "testuser", "--id", "999"}, + expectedErr: "the user testuser does not match the provided id 999", + }, + { + name: "no required flags are provided", + args: []string{"delete"}, + expectedErr: "You must provide the id, username or email of a user to delete", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctx := t.Context() + if strings.Contains(tc.name, "user exists") { + unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"}) + err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"}) + require.NoError(t, err) + } + + err := microcmdUserDelete().Run(ctx, tc.args) + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErr) + }) + + require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{})) + require.NoError(t, db.TruncateBeans(t.Context(), &user_model.EmailAddress{})) + require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{})) + } +} diff --git a/cmd/admin_user_disable_2fa.go b/cmd/admin_user_disable_2fa.go new file mode 100644 index 0000000..b5b59e5 --- /dev/null +++ b/cmd/admin_user_disable_2fa.go @@ -0,0 +1,72 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "strings" + + auth_model "gitea.dev/models/auth" + user_model "gitea.dev/models/user" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +func microcmdUserDisableTwoFactor() *cli.Command { + return &cli.Command{ + Name: "disable-2fa", + Usage: "Disable two-factor authentication for a user", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "username", + Aliases: []string{"u"}, + Usage: "Username of the user to disable 2FA for", + }, + &cli.Int64Flag{ + Name: "id", + Usage: "ID of the user to disable 2FA for", + }, + }, + Action: runDisableTwoFactor, + } +} + +func runDisableTwoFactor(ctx context.Context, c *cli.Command) error { + if !c.IsSet("id") && !c.IsSet("username") { + return errors.New("either --id or --username must be provided") + } + + if !setting.IsInTesting { + if err := initDB(ctx); err != nil { + return err + } + } + + var user *user_model.User + var err error + if c.IsSet("id") { + user, err = user_model.GetUserByID(ctx, c.Int64("id")) + } else { + user, err = user_model.GetUserByName(ctx, c.String("username")) + } + if err != nil { + return err + } + + // When both selectors are given, make sure they refer to the same user. + if c.IsSet("id") && c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) { + return fmt.Errorf("the user with id %d is %q, which does not match the provided username %q", user.ID, user.Name, c.String("username")) + } + + totp, webAuthn, err := auth_model.DisableTwoFactor(ctx, user.ID) + if err != nil { + return err + } + + cprintf(c, "Disabled 2FA for user %q (removed %d TOTP and %d WebAuthn credential(s))\n", user.Name, totp, webAuthn) + return nil +} diff --git a/cmd/admin_user_disable_2fa_test.go b/cmd/admin_user_disable_2fa_test.go new file mode 100644 index 0000000..4fe9497 --- /dev/null +++ b/cmd/admin_user_disable_2fa_test.go @@ -0,0 +1,119 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "io" + "strconv" + "testing" + + auth_model "gitea.dev/models/auth" + "gitea.dev/models/db" + "gitea.dev/models/unittest" + user_model "gitea.dev/models/user" + + "github.com/go-webauthn/webauthn/webauthn" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDisableTwoFactorCommand(t *testing.T) { + ctx := t.Context() + + defer func() { + require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}, &auth_model.TwoFactor{}, &auth_model.WebAuthnCredential{})) + }() + + t.Run("disable TOTP and WebAuthn", func(t *testing.T) { + require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "tfuser", "--email", "tfuser@gitea.local", "--random-password"})) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "tfuser"}) + + // Enroll TOTP. + tf := &auth_model.TwoFactor{UID: user.ID} + require.NoError(t, tf.SetSecret("test-secret")) + _, err := tf.GenerateScratchToken() + require.NoError(t, err) + require.NoError(t, auth_model.NewTwoFactor(ctx, tf)) + + // Register a WebAuthn credential. + _, err = auth_model.CreateCredential(ctx, user.ID, "test-key", &webauthn.Credential{ID: []byte("test-cred-id")}) + require.NoError(t, err) + + has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID) + require.NoError(t, err) + require.True(t, has) + + require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--username", "tfuser"})) + + // Both factors must be gone afterwards. + has, err = auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID) + require.NoError(t, err) + assert.False(t, has) + }) + + t.Run("disable by id", func(t *testing.T) { + require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "iduser", "--email", "iduser@gitea.local", "--random-password"})) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "iduser"}) + + tf := &auth_model.TwoFactor{UID: user.ID} + require.NoError(t, tf.SetSecret("test-secret")) + require.NoError(t, auth_model.NewTwoFactor(ctx, tf)) + + require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--id", strconv.FormatInt(user.ID, 10)})) + + has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID) + require.NoError(t, err) + assert.False(t, has) + }) + + t.Run("no enrollment is a no-op", func(t *testing.T) { + require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "plainuser", "--email", "plainuser@gitea.local", "--random-password"})) + require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--username", "plainuser"})) + }) + + t.Run("id and username must match when both given", func(t *testing.T) { + require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "matchuser", "--email", "matchuser@gitea.local", "--random-password"})) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "matchuser"}) + id := strconv.FormatInt(user.ID, 10) + + // Matching id + username is accepted. + require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--id", id, "--username", "matchuser"})) + + // Mismatched id + username is rejected. + cmd := microcmdUserDisableTwoFactor() + cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard + err := cmd.Run(ctx, []string{"disable-2fa", "--id", id, "--username", "someotheruser"}) + require.Error(t, err) + require.Contains(t, err.Error(), "does not match the provided username") + }) + + t.Run("failure cases", func(t *testing.T) { + testCases := []struct { + name string + args []string + expectedErr string + }{ + { + name: "user does not exist", + args: []string{"disable-2fa", "--username", "nonexistentuser"}, + expectedErr: "user does not exist", + }, + { + name: "neither id nor username", + args: []string{"disable-2fa"}, + expectedErr: "either --id or --username must be provided", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmd := microcmdUserDisableTwoFactor() + cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard + err := cmd.Run(ctx, tc.args) + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErr) + }) + } + }) +} diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go new file mode 100644 index 0000000..e1b0e98 --- /dev/null +++ b/cmd/admin_user_generate_access_token.go @@ -0,0 +1,97 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + + auth_model "gitea.dev/models/auth" + user_model "gitea.dev/models/user" + + "github.com/urfave/cli/v3" +) + +func newUserGenerateAccessTokenCommand() *cli.Command { + return &cli.Command{ + Name: "generate-access-token", + Usage: "Generate an access token for a specific user", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "username", + Aliases: []string{"u"}, + Usage: "Username", + }, + &cli.StringFlag{ + Name: "token-name", + Aliases: []string{"t"}, + Usage: "Token name", + Value: "gitea-admin", + }, + &cli.BoolFlag{ + Name: "raw", + Usage: "Display only the token value", + }, + &cli.StringFlag{ + Name: "scopes", + Value: "all", + Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`, + }, + }, + Action: runGenerateAccessToken, + } +} + +func runGenerateAccessToken(ctx context.Context, c *cli.Command) error { + if !c.IsSet("username") { + return errors.New("you must provide a username to generate a token for") + } + + if err := initDB(ctx); err != nil { + return err + } + + user, err := user_model.GetUserByName(ctx, c.String("username")) + if err != nil { + return err + } + + // construct token with name and user so we can make sure it is unique + t := &auth_model.AccessToken{ + Name: c.String("token-name"), + UID: user.ID, + } + + exist, err := auth_model.AccessTokenByNameExists(ctx, t) + if err != nil { + return err + } + if exist { + return errors.New("access token name has been used already") + } + + // make sure the scopes are valid + accessTokenScope, err := auth_model.AccessTokenScope(c.String("scopes")).Normalize() + if err != nil { + return fmt.Errorf("invalid access token scope provided: %w", err) + } + if !accessTokenScope.HasPermissionScope() { + return errors.New("access token does not have any permission") + } + t.Scope = accessTokenScope + + // create the token + if err := auth_model.NewAccessToken(ctx, t); err != nil { + return err + } + + if c.Bool("raw") { + cprintln(c, t.Token) + } else { + cprintf(c, "Access token was successfully created: %s\n", t.Token) + } + + return nil +} diff --git a/cmd/admin_user_list.go b/cmd/admin_user_list.go new file mode 100644 index 0000000..7f9ce57 --- /dev/null +++ b/cmd/admin_user_list.go @@ -0,0 +1,60 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "fmt" + "os" + "text/tabwriter" + + user_model "gitea.dev/models/user" + + "github.com/urfave/cli/v3" +) + +func newUserListCommand() *cli.Command { + return &cli.Command{ + Name: "list", + Usage: "List users", + Action: runListUsers, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "admin", + Usage: "List only admin users", + }, + }, + } +} + +func runListUsers(ctx context.Context, c *cli.Command) error { + if err := initDB(ctx); err != nil { + return err + } + + users, err := user_model.GetAllUsers(ctx) + if err != nil { + return err + } + + w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0) + + if c.IsSet("admin") { + fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n") + for _, u := range users { + if u.IsAdmin { + fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive) + } + } + } else { + twofa := user_model.UserList(users).GetTwoFaStatus(ctx) + fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n") + for _, u := range users { + fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID]) + } + } + + w.Flush() + return nil +} diff --git a/cmd/admin_user_must_change_password.go b/cmd/admin_user_must_change_password.go new file mode 100644 index 0000000..185b9e6 --- /dev/null +++ b/cmd/admin_user_must_change_password.go @@ -0,0 +1,63 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + + user_model "gitea.dev/models/user" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +func microcmdUserMustChangePassword() *cli.Command { + return &cli.Command{ + Name: "must-change-password", + Usage: "Set the must change password flag for the provided users or all users", + Action: runMustChangePassword, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "all", + Aliases: []string{"A"}, + Usage: "All users must change password, except those explicitly excluded with --exclude", + }, + &cli.StringSliceFlag{ + Name: "exclude", + Aliases: []string{"e"}, + Usage: "Do not change the must-change-password flag for these users", + }, + &cli.BoolFlag{ + Name: "unset", + Usage: "Instead of setting the must-change-password flag, unset it", + }, + }, + } +} + +func runMustChangePassword(ctx context.Context, c *cli.Command) error { + if c.NArg() == 0 && !c.IsSet("all") { + return errors.New("either usernames or --all must be provided") + } + + mustChangePassword := !c.Bool("unset") + all := c.Bool("all") + exclude := c.StringSlice("exclude") + + if !setting.IsInTesting { + if err := initDB(ctx); err != nil { + return err + } + } + + n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args().Slice(), exclude) + if err != nil { + return err + } + + // codeql[disable-next-line=go/clear-text-logging] + cprintf(c, "Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword) + return nil +} diff --git a/cmd/admin_user_must_change_password_test.go b/cmd/admin_user_must_change_password_test.go new file mode 100644 index 0000000..cf81fbe --- /dev/null +++ b/cmd/admin_user_must_change_password_test.go @@ -0,0 +1,78 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "testing" + + "gitea.dev/models/db" + "gitea.dev/models/unittest" + user_model "gitea.dev/models/user" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMustChangePassword(t *testing.T) { + defer func() { + require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{})) + }() + err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"}) + require.NoError(t, err) + err = microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuserexclude", "--email", "testuserexclude@gitea.local", "--random-password"}) + require.NoError(t, err) + // Reset password change flag + err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset"}) + require.NoError(t, err) + + testUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + assert.False(t, testUser.MustChangePassword) + testUserExclude := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) + assert.False(t, testUserExclude.MustChangePassword) + + // Make all users change password + err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all"}) + require.NoError(t, err) + + testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + assert.True(t, testUser.MustChangePassword) + testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) + assert.True(t, testUserExclude.MustChangePassword) + + // Reset password change flag but exclude all tested users + err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"}) + require.NoError(t, err) + + testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + assert.True(t, testUser.MustChangePassword) + testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) + assert.True(t, testUserExclude.MustChangePassword) + + // Reset password change flag by listing multiple users + err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser", "testuserexclude"}) + require.NoError(t, err) + + testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + assert.False(t, testUser.MustChangePassword) + testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) + assert.False(t, testUserExclude.MustChangePassword) + + // Exclude a user from all user + err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--exclude", "testuserexclude"}) + require.NoError(t, err) + + testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + assert.True(t, testUser.MustChangePassword) + testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) + assert.False(t, testUserExclude.MustChangePassword) + + // Unset a flag for single user + err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser"}) + require.NoError(t, err) + + testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}) + assert.False(t, testUser.MustChangePassword) + testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}) + assert.False(t, testUserExclude.MustChangePassword) +} diff --git a/cmd/audit.go b/cmd/audit.go new file mode 100644 index 0000000..7ccad81 --- /dev/null +++ b/cmd/audit.go @@ -0,0 +1,17 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + + audit_model "gitea.dev/models/audit" + user_model "gitea.dev/models/user" + "gitea.dev/services/audit" +) + +func cliAuditContext(ctx context.Context) context.Context { + ctx = audit.WithOrigin(ctx, audit_model.OriginCLI) + return audit.WithDoer(ctx, user_model.NewCliUser()) +} diff --git a/cmd/cert.go b/cmd/cert.go new file mode 100644 index 0000000..53b4f9d --- /dev/null +++ b/cmd/cert.go @@ -0,0 +1,206 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2016 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "log" + "math/big" + "net" + "os" + "strings" + "time" + + "github.com/urfave/cli/v3" +) + +// cmdCert represents the available cert sub-command. +func cmdCert() *cli.Command { + return &cli.Command{ + Name: "cert", + Usage: "Generate self-signed certificate", + Description: `Generate a self-signed X.509 certificate for a TLS server. +Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`, + Action: runCert, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "host", + Usage: "Comma-separated hostnames and IPs to generate a certificate for", + Required: true, + }, + &cli.StringFlag{ + Name: "ecdsa-curve", + Value: "", + Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521", + }, + &cli.IntFlag{ + Name: "rsa-bits", + Value: 3072, + Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set", + }, + &cli.StringFlag{ + Name: "start-date", + Value: "", + Usage: "Creation date formatted as Jan 1 15:04:05 2011", + }, + &cli.DurationFlag{ + Name: "duration", + Value: 365 * 24 * time.Hour, + Usage: "Duration that certificate is valid for", + }, + &cli.BoolFlag{ + Name: "ca", + Usage: "whether this cert should be its own Certificate Authority", + }, + &cli.StringFlag{ + Name: "out", + Value: "cert.pem", + Usage: "Path to the file where there certificate will be saved", + }, + &cli.StringFlag{ + Name: "keyout", + Value: "key.pem", + Usage: "Path to the file where there certificate key will be saved", + }, + }, + } +} + +func publicKey(priv any) any { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } +} + +func pemBlockForKey(priv any) *pem.Block { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} + case *ecdsa.PrivateKey: + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + log.Fatalf("Unable to marshal ECDSA private key: %v", err) + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} + default: + return nil + } +} + +func runCert(_ context.Context, c *cli.Command) error { + var priv any + var err error + switch c.String("ecdsa-curve") { + case "": + priv, err = rsa.GenerateKey(rand.Reader, c.Int("rsa-bits")) + case "P224": + priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + case "P256": + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case "P384": + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + case "P521": + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + default: + err = fmt.Errorf("unrecognized elliptic curve: %q", c.String("ecdsa-curve")) + } + if err != nil { + return fmt.Errorf("failed to generate private key: %w", err) + } + + var notBefore time.Time + if startDate := c.String("start-date"); startDate != "" { + notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate) + if err != nil { + return fmt.Errorf("failed to parse creation date %w", err) + } + } else { + notBefore = time.Now() + } + + notAfter := notBefore.Add(c.Duration("duration")) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return fmt.Errorf("failed to generate serial number: %w", err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + CommonName: "Gitea", + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.SplitSeq(c.String("host"), ",") + for h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + if c.Bool("ca") { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + if err != nil { + return fmt.Errorf("failed to create certificate: %w", err) + } + + certOut, err := os.Create(c.String("out")) + if err != nil { + return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err) + } + err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + if err != nil { + return fmt.Errorf("failed to encode certificate: %w", err) + } + err = certOut.Close() + if err != nil { + return fmt.Errorf("failed to write cert: %w", err) + } + fmt.Fprintf(c.Writer, "Written cert to %s\n", c.String("out")) + + keyOut, err := os.OpenFile(c.String("keyout"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) + if err != nil { + return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err) + } + err = pem.Encode(keyOut, pemBlockForKey(priv)) + if err != nil { + return fmt.Errorf("failed to encode key: %w", err) + } + err = keyOut.Close() + if err != nil { + return fmt.Errorf("failed to write key: %w", err) + } + fmt.Fprintf(c.Writer, "Written key to %s\n", c.String("keyout")) + return nil +} diff --git a/cmd/cert_test.go b/cmd/cert_test.go new file mode 100644 index 0000000..c5775e5 --- /dev/null +++ b/cmd/cert_test.go @@ -0,0 +1,125 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "io" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCertCommand(t *testing.T) { + cases := []struct { + name string + args []string + }{ + { + name: "RSA cert generation", + args: []string{ + "cert-test", + "--host", "localhost", + "--rsa-bits", "2048", + "--duration", "1h", + "--start-date", "Jan 1 00:00:00 2024", + }, + }, + { + name: "ECDSA cert generation", + args: []string{ + "cert-test", + "--host", "localhost", + "--ecdsa-curve", "P256", + "--duration", "1h", + "--start-date", "Jan 1 00:00:00 2024", + }, + }, + { + name: "mixed host, certificate authority", + args: []string{ + "cert-test", + "--host", "localhost,127.0.0.1", + "--duration", "1h", + "--start-date", "Jan 1 00:00:00 2024", + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + app := cmdCert() + tempDir := t.TempDir() + + certFile := filepath.Join(tempDir, "cert.pem") + keyFile := filepath.Join(tempDir, "key.pem") + + err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile)) + require.NoError(t, err) + + assert.FileExists(t, certFile) + assert.FileExists(t, keyFile) + }) + } +} + +func TestCertCommandFailures(t *testing.T) { + cases := []struct { + name string + args []string + errMsg string + }{ + { + name: "Start Date Parsing failure", + args: []string{ + "cert-test", + "--host", "localhost", + "--start-date", "invalid-date", + }, + errMsg: "parsing time", + }, + { + name: "Unknown curve", + args: []string{ + "cert-test", + "--host", "localhost", + "--ecdsa-curve", "invalid-curve", + }, + errMsg: "unrecognized elliptic curve", + }, + { + name: "Key generation failure", + args: []string{ + "cert-test", + "--host", "localhost", + "--rsa-bits", "invalid-bits", + }, + }, + { + name: "Missing parameters", + args: []string{ + "cert-test", + }, + errMsg: `"host" not set`, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + app := cmdCert() + app.Writer, app.ErrWriter = io.Discard, io.Discard + tempDir := t.TempDir() + + certFile := filepath.Join(tempDir, "cert.pem") + keyFile := filepath.Join(tempDir, "key.pem") + err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile)) + require.Error(t, err) + if c.errMsg != "" { + assert.ErrorContains(t, err, c.errMsg) + } + assert.NoFileExists(t, certFile) + assert.NoFileExists(t, keyFile) + }) + } +} diff --git a/cmd/cmdtest/cmd_test.go b/cmd/cmdtest/cmd_test.go new file mode 100644 index 0000000..f41793c --- /dev/null +++ b/cmd/cmdtest/cmd_test.go @@ -0,0 +1,248 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// Tests here reload the config system multiple times with uncontrollable details. +// So they must be in a separate package, to avoid affecting other tests + +package cmdtest + +import ( + "context" + "errors" + "fmt" + "io" + "path/filepath" + "strings" + "testing" + + "gitea.dev/cmd" + "gitea.dev/models/unittest" + "gitea.dev/modules/setting" + "gitea.dev/modules/test" + "gitea.dev/modules/util" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} + +func makePathOutput(workPath, customPath, customConf string) string { + return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf) +} + +func newTestApp(testCmd cli.Command) *cli.Command { + app := cmd.NewMainApp(cmd.AppVersion{}) + testCmd.Name = util.IfZero(testCmd.Name, "test-cmd") + cmd.PrepareSubcommandWithGlobalFlags(&testCmd) + app.Commands = append(app.Commands, &testCmd) + app.DefaultCommand = testCmd.Name + return app +} + +type runResult struct { + Stdout string + Stderr string + ExitCode int +} + +func runTestApp(app *cli.Command, args ...string) (runResult, error) { + outBuf := new(strings.Builder) + errBuf := new(strings.Builder) + app.Writer = outBuf + app.ErrWriter = errBuf + exitCode := -1 + defer test.MockVariableValue(&cli.ErrWriter, app.ErrWriter)() + defer test.MockVariableValue(&cli.OsExiter, func(code int) { + if exitCode == -1 { + exitCode = code // save the exit code once and then reset the writer (to simulate the exit) + app.Writer, app.ErrWriter, cli.ErrWriter = io.Discard, io.Discard, io.Discard + } + })() + err := cmd.RunMainApp(app, args...) + return runResult{outBuf.String(), errBuf.String(), exitCode}, err +} + +func TestCliCmd(t *testing.T) { + defaultWorkPath := filepath.FromSlash("/tmp/mocked-work-path") + defaultCustomPath := filepath.Join(defaultWorkPath, "custom") + defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini") + defer setting.MockBuiltinPaths(defaultWorkPath, "", "")() + + cli.CommandHelpTemplate = "(command help template)" + cli.RootCommandHelpTemplate = "(app help template)" + cli.SubcommandHelpTemplate = "(subcommand help template)" + + cases := []struct { + env map[string]string + cmd string + exp string + }{ + // help commands + { + cmd: "./gitea -h", + exp: "DEFAULT CONFIGURATION:", + }, + { + cmd: "./gitea help", + exp: "DEFAULT CONFIGURATION:", + }, + + { + cmd: "./gitea -c /dev/null -h", + exp: "ConfigFile: /dev/null", + }, + + { + cmd: "./gitea -c /dev/null help", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea help -c /dev/null", + exp: "ConfigFile: /dev/null", + }, + + { + cmd: "./gitea -c /dev/null test-cmd -h", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd -c /dev/null -h", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd -h -c /dev/null", + exp: "ConfigFile: /dev/null", + }, + + { + cmd: "./gitea -c /dev/null test-cmd help", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd -c /dev/null help", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd help -c /dev/null", + exp: "ConfigFile: /dev/null", + }, + + // parse paths + { + cmd: "./gitea test-cmd", + exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf), + }, + { + cmd: "./gitea -c /tmp/app.ini test-cmd", + exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"), + }, + { + cmd: "./gitea test-cmd -c /tmp/app.ini", + exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"), + }, + { + env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, + cmd: "./gitea test-cmd", + exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"), + }, + { + env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, + cmd: "./gitea test-cmd --work-path /tmp/other", + exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"), + }, + { + env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, + cmd: "./gitea test-cmd --config /tmp/app-other.ini", + exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"), + }, + } + + for _, c := range cases { + t.Run(c.cmd, func(t *testing.T) { + app := newTestApp(cli.Command{ + Action: func(ctx context.Context, cmd *cli.Command) error { + _, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf)) + return nil + }, + }) + for k, v := range c.env { + t.Setenv(k, v) + } + args := strings.Split(c.cmd, " ") // for test only, "split" is good enough + r, err := runTestApp(app, args...) + assert.NoError(t, err, c.cmd) + assert.NotEmpty(t, c.exp, c.cmd) + if !assert.Contains(t, r.Stdout, c.exp, c.cmd) { + t.Log("Full output:\n" + r.Stdout) + t.Log("Expected:\n" + c.exp) + } + }) + } +} + +func TestCliCmdError(t *testing.T) { + app := newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }}) + r, err := runTestApp(app, "./gitea", "test-cmd") + assert.Error(t, err) + assert.Equal(t, 1, r.ExitCode) + assert.Empty(t, r.Stdout) + assert.Equal(t, "Command error: normal error\n", r.Stderr) + + app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }}) + r, err = runTestApp(app, "./gitea", "test-cmd") + assert.Error(t, err) + assert.Equal(t, 2, r.ExitCode) + assert.Empty(t, r.Stdout) + assert.Equal(t, "exit error\n", r.Stderr) + + app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }}) + r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such") + assert.Error(t, err) + assert.Equal(t, 1, r.ExitCode) + assert.Empty(t, r.Stdout) + assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n", r.Stderr) + + app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }}) + r, err = runTestApp(app, "./gitea", "test-cmd") + assert.NoError(t, err) + assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called + assert.Empty(t, r.Stdout) + assert.Empty(t, r.Stderr) +} + +func TestCliCmdBefore(t *testing.T) { + ctxNew := context.WithValue(context.Background(), any("key"), "value") + configValues := map[string]string{} + setting.CustomConf = "/tmp/any.ini" + var actionCtx context.Context + app := newTestApp(cli.Command{ + Before: func(context.Context, *cli.Command) (context.Context, error) { + configValues["before"] = setting.CustomConf + return ctxNew, nil + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + configValues["action"] = setting.CustomConf + actionCtx = ctx + return nil + }, + }) + _, err := runTestApp(app, "./gitea", "--config", "/dev/null", "test-cmd") + assert.NoError(t, err) + assert.Equal(t, ctxNew, actionCtx) + assert.Equal(t, "/tmp/any.ini", configValues["before"], "BeforeFunc must be called before preparing config") + assert.Equal(t, "/dev/null", configValues["action"]) +} + +func TestCliCmdCompletion(t *testing.T) { + app := newTestApp(cli.Command{ + Action: func(ctx context.Context, cmd *cli.Command) error { return nil }, + }) + res, err := runTestApp(app, "./gitea", "completion", "bash", "--nonexist") + assert.Error(t, err) + assert.Equal(t, 1, res.ExitCode) + assert.Equal(t, "", res.Stdout) + assert.Equal(t, "Incorrect Usage: flag provided but not defined: -nonexist\n", res.Stderr) +} diff --git a/cmd/config.go b/cmd/config.go new file mode 100644 index 0000000..42880d9 --- /dev/null +++ b/cmd/config.go @@ -0,0 +1,156 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "os" + + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +func cmdConfig() *cli.Command { + subcmdConfigEditIni := &cli.Command{ + Name: "edit-ini", + Usage: "Load an existing INI file, apply environment variables, keep specified keys, and output to a new INI file.", + Description: ` +Help users to edit the Gitea configuration INI file. + +# Keep Specified Keys + +If you need to re-create the configuration file with only a subset of keys, +you can provide an INI template file for the kept keys and use the "--config-keep-keys" flag. +For example, if a helm chart needs to reset the settings and only keep SECRET_KEY, +it can use a template file (only keys take effect, values are ignored): + + [security] + SECRET_KEY= + +$ ./gitea config edit-ini --config app-old.ini --config-keep-keys app-keys.ini --out app-new.ini + +# Map Environment Variables to INI Configuration + +Environment variables of the form "GITEA__section_name__KEY_NAME" +will be mapped to the ini section "[section_name]" and the key +"KEY_NAME" with the value as provided. + +Environment variables of the form "GITEA__section_name__KEY_NAME__FILE" +will be mapped to the ini section "[section_name]" and the key +"KEY_NAME" with the value loaded from the specified file. + +Environment variable keys can only contain characters "0-9A-Z_", +if a section or key name contains dot ".", it needs to be escaped as _0x2E_. +For example, to apply this config: + + [git.config] + foo.bar=val + +$ export GITEA__git_0x2E_config__foo_0x2E_bar=val + +# Put All Together + +$ ./gitea config edit-ini --config app.ini --config-keep-keys app-keys.ini --apply-env {--in-place|--out app-new.ini} +`, + Flags: []cli.Flag{ + // "--config" flag is provided by global flags, and this flag is also used by "environment-to-ini" script wrapper + // "--in-place" is also used by "environment-to-ini" script wrapper for its old behavior: always overwrite the existing config file + &cli.BoolFlag{ + Name: "in-place", + Usage: "Output to the same config file as input. This flag will be ignored if --out is set.", + }, + &cli.StringFlag{ + Name: "config-keep-keys", + Usage: "An INI template file containing keys for keeping. Only the keys defined in the INI template will be kept from old config. If not set, all keys will be kept.", + }, + &cli.BoolFlag{ + Name: "apply-env", + Usage: "Apply all GITEA__* variables from the environment to the config.", + }, + &cli.StringFlag{ + Name: "out", + Usage: "Destination config file to write to.", + }, + }, + Action: runConfigEditIni, + } + + return &cli.Command{ + Name: "config", + Usage: "Manage Gitea configuration", + Commands: []*cli.Command{ + subcmdConfigEditIni, + }, + } +} + +func runConfigEditIni(_ context.Context, c *cli.Command) error { + // the config system may change the environment variables, so get a copy first, to be used later + env := append([]string{}, os.Environ()...) + + // don't use the guessed setting.CustomConf, instead, require the user to provide --config explicitly + if !c.IsSet("config") { + return errors.New("flag is required but not set: --config") + } + configFileIn := c.String("config") + + cfgIn, err := setting.NewConfigProviderFromFile(configFileIn) + if err != nil { + return fmt.Errorf("failed to load config file %q: %v", configFileIn, err) + } + + // determine output config file: use "--out" flag or use "--in-place" flag to overwrite input file + inPlace := c.Bool("in-place") + configFileOut := c.String("out") + if configFileOut == "" { + if !inPlace { + return errors.New("either --in-place or --out must be specified") + } + configFileOut = configFileIn // in-place edit + } + + needWriteOut := configFileOut != configFileIn + + cfgOut := cfgIn + configKeepKeys := c.String("config-keep-keys") + if configKeepKeys != "" { + needWriteOut = true + cfgOut, err = setting.NewConfigProviderFromFile(configKeepKeys) + if err != nil { + return fmt.Errorf("failed to load config-keep-keys template file %q: %v", configKeepKeys, err) + } + + for _, secOut := range cfgOut.Sections() { + for _, keyOut := range secOut.Keys() { + secIn := cfgIn.Section(secOut.Name()) + keyIn := setting.ConfigSectionKey(secIn, keyOut.Name()) + if keyIn != nil { + keyOut.SetValue(keyIn.String()) + } else { + secOut.DeleteKey(keyOut.Name()) + } + } + if len(secOut.Keys()) == 0 { + cfgOut.DeleteSection(secOut.Name()) + } + } + } + + if c.Bool("apply-env") { + if setting.EnvironmentToConfig(cfgOut, env) { + needWriteOut = true + } + } + + if needWriteOut { + err = cfgOut.SaveTo(configFileOut) + if err != nil { + return err + } + } + return nil +} diff --git a/cmd/config_test.go b/cmd/config_test.go new file mode 100644 index 0000000..d123daa --- /dev/null +++ b/cmd/config_test.go @@ -0,0 +1,85 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestConfigEdit(t *testing.T) { + tmpDir := t.TempDir() + configOld := tmpDir + "/app-old.ini" + configTemplate := tmpDir + "/app-template.ini" + _ = os.WriteFile(configOld, []byte(` +[sec] +k1=v1 +k2=v2 +`), os.ModePerm) + + _ = os.WriteFile(configTemplate, []byte(` +[sec] +k1=in-template + +[sec2] +k3=v3 +`), os.ModePerm) + + t.Setenv("GITEA__EnV__KeY", "val") + + t.Run("OutputToNewWithEnv", func(t *testing.T) { + configNew := tmpDir + "/app-new.ini" + err := NewMainApp(AppVersion{}).Run(t.Context(), []string{ + "./gitea", "--config", configOld, + "config", "edit-ini", + "--apply-env", + "--config-keep-keys", configTemplate, + "--out", configNew, + }) + require.NoError(t, err) + + // "k1" old value is kept because its key is in the template + // "k2" is removed because it isn't in the template + // "k3" isn't in new config because it isn't in the old config + // [env] is applied from environment variable + data, _ := os.ReadFile(configNew) + require.Equal(t, `[sec] +k1 = v1 + +[env] +KeY = val +`, string(data)) + }) + + t.Run("OutputToExisting(environment-to-ini)", func(t *testing.T) { + // the legacy "environment-to-ini" (now a wrapper script) behavior: + // if no "--out", then "--in-place" must be used to overwrite the existing "--config" file + err := NewMainApp(AppVersion{}).Run(t.Context(), []string{ + "./gitea", "config", "edit-ini", + "--apply-env", + "--config", configOld, + }) + require.ErrorContains(t, err, "either --in-place or --out must be specified") + + // simulate the "environment-to-ini" behavior with "--in-place" + err = NewMainApp(AppVersion{}).Run(t.Context(), []string{ + "./gitea", "config", "edit-ini", + "--in-place", + "--apply-env", + "--config", configOld, + }) + require.NoError(t, err) + + data, _ := os.ReadFile(configOld) + require.Equal(t, `[sec] +k1 = v1 +k2 = v2 + +[env] +KeY = val +`, string(data)) + }) +} diff --git a/cmd/docs.go b/cmd/docs.go new file mode 100644 index 0000000..8e0f942 --- /dev/null +++ b/cmd/docs.go @@ -0,0 +1,68 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "fmt" + "os" + "strings" + + cli_docs "github.com/urfave/cli-docs/v3" + "github.com/urfave/cli/v3" +) + +func newDocsCommand() *cli.Command { + return &cli.Command{ + Name: "docs", + Usage: "Output CLI documentation", + Description: "A command to output Gitea's CLI documentation, optionally to a file.", + Action: runDocs, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "man", + Usage: "Output man pages instead", + }, + &cli.StringFlag{ + Name: "output", + Aliases: []string{"o"}, + Usage: "Path to output to instead of stdout (will overwrite if exists)", + }, + }, + } +} + +func runDocs(_ context.Context, cmd *cli.Command) error { + docs, err := cli_docs.ToMarkdown(cmd.Root()) + if cmd.Bool("man") { + docs, err = cli_docs.ToMan(cmd.Root()) + } + if err != nil { + return err + } + + if !cmd.Bool("man") { + // Clean up markdown. The following bug was fixed in v2, but is present in v1. + // It affects markdown output (even though the issue is referring to man pages) + // https://github.com/urfave/cli/issues/1040 + firstHashtagIndex := strings.Index(docs, "#") + + if firstHashtagIndex > 0 { + docs = docs[firstHashtagIndex:] + } + } + + out := os.Stdout + if cmd.String("output") != "" { + fi, err := os.Create(cmd.String("output")) + if err != nil { + return err + } + defer fi.Close() + out = fi + } + + _, err = fmt.Fprintln(out, docs) + return err +} diff --git a/cmd/doctor.go b/cmd/doctor.go new file mode 100644 index 0000000..7c55c2d --- /dev/null +++ b/cmd/doctor.go @@ -0,0 +1,218 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "fmt" + golog "log" + "os" + "path/filepath" + "strings" + "text/tabwriter" + + "gitea.dev/modelmigration" + "gitea.dev/modelmigration/base" + "gitea.dev/models/db" + "gitea.dev/modules/container" + "gitea.dev/modules/log" + "gitea.dev/modules/setting" + "gitea.dev/services/doctor" + + "github.com/urfave/cli/v3" +) + +func newDoctorCommand() *cli.Command { + return &cli.Command{ + Name: "doctor", + Usage: "Diagnose and optionally fix problems, convert or re-create database tables", + Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.", + Commands: []*cli.Command{ + newDoctorCheckCommand(), + newRecreateTableCommand(), + newDoctorConvertCommand(), + }, + } +} + +func newDoctorCheckCommand() *cli.Command { + return &cli.Command{ + Name: "check", + Usage: "Diagnose and optionally fix problems", + Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.", + Action: runDoctorCheck, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "list", + Usage: "List the available checks", + }, + &cli.BoolFlag{ + Name: "default", + Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)", + }, + &cli.StringSliceFlag{ + Name: "run", + Usage: "Run the provided checks - (if --default is set, the default checks will also run)", + }, + &cli.BoolFlag{ + Name: "all", + Usage: "Run all the available checks", + }, + &cli.BoolFlag{ + Name: "fix", + Usage: "Automatically fix what we can", + }, + &cli.StringFlag{ + Name: "log-file", + Usage: `Name of the log file (no verbose log output by default). Set to "-" to output to stdout`, + }, + &cli.BoolFlag{ + Name: "color", + Aliases: []string{"H"}, + Usage: "Use color for outputted information", + }, + }, + } +} + +func newRecreateTableCommand() *cli.Command { + return &cli.Command{ + Name: "recreate-table", + Usage: "Recreate tables from XORM definitions and copy the data.", + ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + Usage: "Print SQL commands sent", + }, + }, + Description: `The database definitions Gitea uses change across versions, sometimes changing default values and leaving old unused columns. + +This command will cause Xorm to recreate tables, copying over the data and deleting the old table. + +You should back-up your database before doing this and ensure that your database is up-to-date first.`, + Action: runRecreateTable, + } +} + +func runRecreateTable(ctx context.Context, cmd *cli.Command) error { + // Redirect the default golog to here + golog.SetFlags(0) + golog.SetPrefix("") + golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info)) + + debug := cmd.Bool("debug") + setting.MustInstalled() + setting.LoadDBSetting() + + if debug { + setting.InitSQLLoggersForCli(log.DEBUG) + } else { + setting.InitSQLLoggersForCli(log.INFO) + } + + setting.Database.LogSQL = debug + if err := db.InitEngine(ctx); err != nil { + cprintln(cmd, err) + cprintln(cmd, "Check if you are using the right config file. You can use a --config directive to specify one.") + return nil + } + + args := cmd.Args() + names := make([]string, 0, cmd.NArg()) + for i := 0; i < cmd.NArg(); i++ { + names = append(names, args.Get(i)) + } + + beans, err := db.NamesToBean(names...) + if err != nil { + return err + } + recreateTables := base.RecreateTables(beans...) + + return db.InitEngineWithMigration(context.Background(), func(ctx context.Context, x db.EngineMigration) error { + if err := modelmigration.EnsureUpToDate(ctx, x); err != nil { + return err + } + return recreateTables(x) + }) +} + +func setupDoctorDefaultLogger(cmd *cli.Command, colorize bool) { + // Silence the default loggers + setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr) + + logFile := cmd.String("log-file") + switch logFile { + case "": + return // if no doctor log-file is set, do not show any log from default logger + case "-": + setupConsoleLogger(log.TRACE, colorize, os.Stdout) + default: + logFile, _ = filepath.Abs(logFile) + writeMode := log.WriterMode{Level: log.TRACE, WriterOption: log.WriterFileOption{FileName: logFile}} + writer, err := log.NewEventWriter("console-to-file", "file", writeMode) + if err != nil { + log.FallbackErrorf("unable to create file log writer: %v", err) + return + } + log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer) + } +} + +func runDoctorCheck(ctx context.Context, cmd *cli.Command) error { + colorize := log.CanColorStdout + if cmd.IsSet("color") { + colorize = cmd.Bool("color") + } + + setupDoctorDefaultLogger(cmd, colorize) + + // Finally redirect the default golang's log to here + golog.SetFlags(0) + golog.SetPrefix("") + golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info)) + + if cmd.IsSet("list") { + w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) + _, _ = w.Write([]byte("Default\tName\tTitle\n")) + doctor.SortChecks(doctor.Checks) + for _, check := range doctor.Checks { + if check.IsDefault { + _, _ = w.Write([]byte{'*'}) + } + _, _ = w.Write([]byte{'\t'}) + _, _ = w.Write([]byte(check.Name)) + _, _ = w.Write([]byte{'\t'}) + _, _ = w.Write([]byte(check.Title)) + _, _ = w.Write([]byte{'\n'}) + } + return w.Flush() + } + + var checks []*doctor.Check + if cmd.Bool("all") { + checks = make([]*doctor.Check, len(doctor.Checks)) + copy(checks, doctor.Checks) + } else if cmd.IsSet("run") { + addDefault := cmd.Bool("default") + runNamesSet := container.SetOf(cmd.StringSlice("run")...) + for _, check := range doctor.Checks { + if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) { + checks = append(checks, check) + runNamesSet.Remove(check.Name) + } + } + if len(runNamesSet) > 0 { + return fmt.Errorf("unknown checks: %q", strings.Join(runNamesSet.Values(), ",")) + } + } else { + for _, check := range doctor.Checks { + if check.IsDefault { + checks = append(checks, check) + } + } + } + return doctor.RunChecks(ctx, colorize, cmd.Bool("fix"), checks) +} diff --git a/cmd/doctor_convert.go b/cmd/doctor_convert.go new file mode 100644 index 0000000..e712370 --- /dev/null +++ b/cmd/doctor_convert.go @@ -0,0 +1,54 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + + "gitea.dev/models/db" + "gitea.dev/modules/log" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +func newDoctorConvertCommand() *cli.Command { + return &cli.Command{ + Name: "convert", + Usage: "Convert the database", + Description: "A command to convert an existing MySQL database from utf8 to utf8mb4 or MSSQL database from varchar to nvarchar", + Action: runDoctorConvert, + } +} + +func runDoctorConvert(ctx context.Context, cmd *cli.Command) error { + if err := initDB(ctx); err != nil { + return err + } + + log.Info("AppPath: %s", setting.AppPath) + log.Info("AppWorkPath: %s", setting.AppWorkPath) + log.Info("Custom path: %s", setting.CustomPath) + log.Info("Log path: %s", setting.Log.RootPath) + log.Info("Configuration file: %s", setting.CustomConf) + + switch { + case setting.Database.Type.IsMySQL(): + if err := db.ConvertDatabaseTable(); err != nil { + log.Fatal("Failed to convert database & table: %v", err) + return err + } + cprintln(cmd, "Converted successfully, please confirm your database's character set is now utf8mb4") + case setting.Database.Type.IsMSSQL(): + if err := db.ConvertVarcharToNVarchar(); err != nil { + log.Fatal("Failed to convert database from varchar to nvarchar: %v", err) + return err + } + cprintln(cmd, "Converted successfully, please confirm your database's all columns character is NVARCHAR now") + default: + cprintln(cmd, "This command can only be used with a MySQL or MSSQL database") + } + + return nil +} diff --git a/cmd/doctor_test.go b/cmd/doctor_test.go new file mode 100644 index 0000000..6cc22b0 --- /dev/null +++ b/cmd/doctor_test.go @@ -0,0 +1,34 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "testing" + + "gitea.dev/modules/log" + "gitea.dev/services/doctor" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestDoctorRun(t *testing.T) { + doctor.Register(&doctor.Check{ + Title: "Test Check", + Name: "test-check", + Run: func(ctx context.Context, logger log.Logger, autofix bool) error { return nil }, + + SkipDatabaseInitialization: true, + }) + app := &cli.Command{ + Commands: []*cli.Command{newDoctorCheckCommand()}, + } + err := app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check"}) + assert.NoError(t, err) + err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "no-such"}) + assert.ErrorContains(t, err, `unknown checks: "no-such"`) + err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check,no-such"}) + assert.ErrorContains(t, err, `unknown checks: "no-such"`) +} diff --git a/cmd/dump.go b/cmd/dump.go new file mode 100644 index 0000000..037e788 --- /dev/null +++ b/cmd/dump.go @@ -0,0 +1,328 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2016 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "os" + "path" + "path/filepath" + "strings" + + "gitea.dev/models/db" + "gitea.dev/modules/dump" + "gitea.dev/modules/json" + "gitea.dev/modules/log" + "gitea.dev/modules/setting" + "gitea.dev/modules/storage" + "gitea.dev/modules/util" + + "gitea.com/go-chi/session" + "github.com/urfave/cli/v3" +) + +func newDumpCommand() *cli.Command { + return &cli.Command{ + Name: "dump", + Usage: "Dump Gitea files and database", + Description: `Dump compresses all related files and database into zip file. It can be used for backup and capture Gitea server image to send to maintainer`, + Action: runDump, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "file", + Aliases: []string{"f"}, + Usage: `Name of the dump file which will be created, default to "gitea-dump-{time}.zip". Supply '-' for stdout. See type for available types.`, + }, + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"V"}, + Usage: "Show process details", + }, + &cli.BoolFlag{ + Name: "quiet", + Aliases: []string{"q"}, + Usage: "Only display warnings and errors", + }, + &cli.StringFlag{ + Name: "tempdir", + Aliases: []string{"t"}, + Value: os.TempDir(), + Usage: "Temporary dir path", + }, + &cli.StringFlag{ + Name: "database", + Aliases: []string{"d"}, + Usage: "Specify the database SQL syntax: sqlite3, mysql, mssql, postgres", + }, + &cli.BoolFlag{ + Name: "skip-repository", + Aliases: []string{"R"}, + Usage: "Skip the repository dumping", + }, + &cli.BoolFlag{ + Name: "skip-log", + Aliases: []string{"L"}, + Usage: "Skip the log dumping", + }, + &cli.BoolFlag{ + Name: "skip-custom-dir", + Usage: "Skip custom directory", + }, + &cli.BoolFlag{ + Name: "skip-lfs-data", + Usage: "Skip LFS data", + }, + &cli.BoolFlag{ + Name: "skip-attachment-data", + Usage: "Skip attachment data", + }, + &cli.BoolFlag{ + Name: "skip-package-data", + Usage: "Skip package data", + }, + &cli.BoolFlag{ + Name: "skip-index", + Usage: "Skip bleve index data", + }, + &cli.BoolFlag{ + Name: "skip-db", + Usage: "Skip database", + }, + &cli.StringFlag{ + Name: "type", + Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "), + }, + }, + } +} + +func fatal(format string, args ...any) { + log.Fatal(format, args...) +} + +func runDump(ctx context.Context, cmd *cli.Command) error { + setting.MustInstalled() + + quite := cmd.Bool("quiet") + verbose := cmd.Bool("verbose") + if verbose && quite { + fatal("Option --quiet and --verbose cannot both be set") + } + + // outFileName is either "-" or a file name (will be made absolute) + outFileName, outType := dump.PrepareFileNameAndType(cmd.String("file"), cmd.String("type")) + if outType == "" { + fatal("Invalid output type") + } + + outFile := os.Stdout + if outFileName != "-" { + var err error + if outFileName, err = filepath.Abs(outFileName); err != nil { + fatal("Unable to get absolute path of dump file: %v", err) + } + if exist, _ := util.IsExist(outFileName); exist { + fatal("Dump file %q exists", outFileName) + } + if outFile, err = os.Create(outFileName); err != nil { + fatal("Unable to create dump file %q: %v", outFileName, err) + } + defer outFile.Close() + } + + setupConsoleLogger(util.Iif(quite, log.WARN, log.INFO), log.CanColorStderr, os.Stderr) + + setting.DisableLoggerInit() + setting.LoadSettings() // cannot access session settings otherwise + + err := db.InitEngine(ctx) + if err != nil { + return err + } + + if err = storage.Init(); err != nil { + return err + } + + dumper, err := dump.NewDumper(ctx, outType, outFile) + if err != nil { + fatal("Failed to create archive %q: %v", outFile, err) + return err + } + dumper.Verbose = verbose + dumper.GlobalExcludeAbsPath(outFileName) + defer func() { + if err := dumper.Close(); err != nil { + fatal("Failed to save archive %q: %v", outFileName, err) + } + }() + + if cmd.IsSet("skip-repository") && cmd.Bool("skip-repository") { + log.Info("Skip dumping local repositories") + } else { + log.Info("Dumping local repositories... %s", setting.RepoRootPath) + if err := dumper.AddRecursiveExclude("repos", setting.RepoRootPath, nil); err != nil { + fatal("Failed to include repositories: %v", err) + } + + if cmd.IsSet("skip-lfs-data") && cmd.Bool("skip-lfs-data") { + log.Info("Skip dumping LFS data") + } else if !setting.LFS.StartServer { + log.Info("LFS isn't enabled. Skip dumping LFS data") + } else if err := storage.LFS.IterateObjects("", func(objPath string, object storage.Object) error { + info, err := object.Stat() + if err != nil { + return err + } + return dumper.AddFileByReader(object, info, path.Join("data", "lfs", objPath)) + }); err != nil { + fatal("Failed to dump LFS objects: %v", err) + } + } + + if cmd.Bool("skip-db") { + // Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere. + dumper.GlobalExcludeAbsPath(setting.Database.Path) + log.Info("Skipping database") + } else { + tmpDir := cmd.String("tempdir") + if _, err := os.Stat(tmpDir); os.IsNotExist(err) { + fatal("Path does not exist: %s", tmpDir) + } + + dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql") + if err != nil { + fatal("Failed to create tmp file: %v", err) + } + defer func() { + _ = dbDump.Close() + if err := os.Remove(dbDump.Name()); err != nil { + log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err) + } + }() + + targetDBType := setting.DatabaseType(cmd.String("database")) + if targetDBType != "" && targetDBType != setting.Database.Type { + log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType) + } else { + log.Info("Dumping database...") + } + + if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil { + fatal("Failed to dump database: %v", err) + } + + if err = dumper.AddFileByPath("gitea-db.sql", dbDump.Name()); err != nil { + fatal("Failed to include gitea-db.sql: %v", err) + } + } + + log.Info("Adding custom configuration file from %s", setting.CustomConf) + if err = dumper.AddFileByPath("app.ini", setting.CustomConf); err != nil { + fatal("Failed to include specified app.ini: %v", err) + } + + if cmd.IsSet("skip-custom-dir") && cmd.Bool("skip-custom-dir") { + log.Info("Skipping custom directory") + } else { + customDir, err := os.Stat(setting.CustomPath) + if err == nil && customDir.IsDir() { + if is, _ := dump.IsSubdir(setting.AppDataPath, setting.CustomPath); !is { + if err := dumper.AddRecursiveExclude("custom", setting.CustomPath, nil); err != nil { + fatal("Failed to include custom: %v", err) + } + } else { + log.Info("Custom dir %s is inside data dir %s, skipped", setting.CustomPath, setting.AppDataPath) + } + } else { + log.Info("Custom dir %s doesn't exist, skipped", setting.CustomPath) + } + } + + isExist, err := util.IsExist(setting.AppDataPath) + if err != nil { + log.Error("Unable to check if %s exists. Error: %v", setting.AppDataPath, err) + } + if isExist { + log.Info("Packing data directory...%s", setting.AppDataPath) + + var excludes []string + if setting.SessionConfig.OriginalProvider == "file" { + var opts session.Options + if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil { + return err + } + excludes = append(excludes, opts.ProviderConfig) + } + + if cmd.IsSet("skip-index") && cmd.Bool("skip-index") { + excludes = append(excludes, setting.Indexer.RepoPath) + excludes = append(excludes, setting.Indexer.IssuePath) + } + + excludes = append(excludes, setting.RepoRootPath) + excludes = append(excludes, setting.LFS.Storage.Path) + excludes = append(excludes, setting.Attachment.Storage.Path) + excludes = append(excludes, setting.Packages.Storage.Path) + excludes = append(excludes, setting.RepoArchive.Storage.Path) + excludes = append(excludes, setting.Log.RootPath) + if err := dumper.AddRecursiveExclude("data", setting.AppDataPath, excludes); err != nil { + fatal("Failed to include data directory: %v", err) + } + } + + if cmd.IsSet("skip-attachment-data") && cmd.Bool("skip-attachment-data") { + log.Info("Skip dumping attachment data") + } else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error { + info, err := object.Stat() + if err != nil { + return err + } + return dumper.AddFileByReader(object, info, path.Join("data", "attachments", objPath)) + }); err != nil { + fatal("Failed to dump attachments: %v", err) + } + + if cmd.IsSet("skip-package-data") && cmd.Bool("skip-package-data") { + log.Info("Skip dumping package data") + } else if !setting.Packages.Enabled { + log.Info("Packages isn't enabled. Skip dumping package data") + } else if err := storage.Packages.IterateObjects("", func(objPath string, object storage.Object) error { + info, err := object.Stat() + if err != nil { + return err + } + return dumper.AddFileByReader(object, info, path.Join("data", "packages", objPath)) + }); err != nil { + fatal("Failed to dump packages: %v", err) + } + + // Doesn't check if LogRootPath exists before processing --skip-log intentionally, + // ensuring that it's clear the dump is skipped whether the directory's initialized + // yet or not. + if cmd.IsSet("skip-log") && cmd.Bool("skip-log") { + log.Info("Skip dumping log files") + } else { + isExist, err := util.IsExist(setting.Log.RootPath) + if err != nil { + log.Error("Unable to check if %s exists. Error: %v", setting.Log.RootPath, err) + } + if isExist { + if err := dumper.AddRecursiveExclude("log", setting.Log.RootPath, nil); err != nil { + fatal("Failed to include log: %v", err) + } + } + } + + if outFileName == "-" { + log.Info("Finish dumping to stdout") + } else { + if err = os.Chmod(outFileName, 0o600); err != nil { + log.Info("Can't change file access permissions mask to 0600: %v", err) + } + log.Info("Finish dumping in file %s", outFileName) + } + return nil +} diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go new file mode 100644 index 0000000..cc84922 --- /dev/null +++ b/cmd/dump_repo.go @@ -0,0 +1,195 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "os" + "strings" + + "gitea.dev/modules/git" + "gitea.dev/modules/log" + base "gitea.dev/modules/migration" + "gitea.dev/modules/setting" + "gitea.dev/modules/structs" + "gitea.dev/modules/util" + "gitea.dev/services/convert" + "gitea.dev/services/migrations" + + "github.com/urfave/cli/v3" +) + +func newDumpRepositoryCommand() *cli.Command { + return &cli.Command{ + Name: "dump-repo", + Usage: "Dump the repository from git/github/gitea/gitlab", + Description: "This is a command for dumping the repository data.", + Action: runDumpRepository, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "git_service", + Value: "", + Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.", + }, + &cli.StringFlag{ + Name: "repo_dir", + Aliases: []string{"r"}, + Value: "./data", + Usage: "Repository dir path to store the data", + }, + &cli.StringFlag{ + Name: "clone_addr", + Value: "", + Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL", + }, + &cli.StringFlag{ + Name: "auth_username", + Value: "", + Usage: "The username to visit the clone_addr", + }, + &cli.StringFlag{ + Name: "auth_password", + Value: "", + Usage: "The password to visit the clone_addr", + }, + &cli.StringFlag{ + Name: "auth_token", + Value: "", + Usage: "The personal token to visit the clone_addr", + }, + &cli.StringFlag{ + Name: "owner_name", + Value: "", + Usage: "The data will be stored on a directory with owner name if not empty", + }, + &cli.StringFlag{ + Name: "repo_name", + Value: "", + Usage: "The data will be stored on a directory with repository name if not empty", + }, + &cli.StringFlag{ + Name: "units", + Value: "", + Usage: `Which items will be migrated, one or more units should be separated as comma. +wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`, + }, + }, + } +} + +func runDumpRepository(ctx context.Context, cmd *cli.Command) error { + setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr) + + setting.DisableLoggerInit() + setting.LoadSettings() // cannot access skip_tls_verify settings otherwise + + if err := initDB(ctx); err != nil { + return err + } + + // migrations.GiteaLocalUploader depends on git module + if err := git.InitSimple(); err != nil { + return err + } + + log.Info("AppPath: %s", setting.AppPath) + log.Info("AppWorkPath: %s", setting.AppWorkPath) + log.Info("Custom path: %s", setting.CustomPath) + log.Info("Log path: %s", setting.Log.RootPath) + log.Info("Configuration file: %s", setting.CustomConf) + + var ( + serviceType structs.GitServiceType + cloneAddr = cmd.String("clone_addr") + serviceStr = cmd.String("git_service") + ) + + if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") { + serviceStr = "github" + } else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitlab.com/") { + serviceStr = "gitlab" + } else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitea.com/") { + serviceStr = "gitea" + } + if serviceStr == "" { + return errors.New("git_service missed or clone_addr cannot be recognized") + } + serviceType = convert.ToGitServiceType(serviceStr) + + opts := base.MigrateOptions{ + GitServiceType: serviceType, + CloneAddr: cloneAddr, + AuthUsername: cmd.String("auth_username"), + AuthPassword: cmd.String("auth_password"), + AuthToken: cmd.String("auth_token"), + RepoName: cmd.String("repo_name"), + } + + if len(cmd.String("units")) == 0 { + opts.Wiki = true + opts.Issues = true + opts.Milestones = true + opts.Labels = true + opts.Releases = true + opts.Comments = true + opts.PullRequests = true + opts.ReleaseAssets = true + } else { + units := strings.SplitSeq(cmd.String("units"), ",") + for unit := range units { + switch strings.ToLower(strings.TrimSpace(unit)) { + case "": + continue + case "wiki": + opts.Wiki = true + case "issues": + opts.Issues = true + case "milestones": + opts.Milestones = true + case "labels": + opts.Labels = true + case "releases": + opts.Releases = true + case "release_assets": + opts.ReleaseAssets = true + case "comments": + opts.Comments = true + case "pull_requests": + opts.PullRequests = true + default: + return errors.New("invalid unit: " + unit) + } + } + } + + // the repo_dir will be removed if error occurs in DumpRepository + // make sure the directory doesn't exist or is empty, prevent from deleting user files + repoDir := cmd.String("repo_dir") + if exists, err := util.IsExist(repoDir); err != nil { + return fmt.Errorf("unable to stat repo_dir %q: %w", repoDir, err) + } else if exists { + if isDir, _ := util.IsDir(repoDir); !isDir { + return fmt.Errorf("repo_dir %q already exists but it's not a directory", repoDir) + } + if dir, _ := os.ReadDir(repoDir); len(dir) > 0 { + return fmt.Errorf("repo_dir %q is not empty", repoDir) + } + } + + if err := migrations.DumpRepository( + ctx, + repoDir, + cmd.String("owner_name"), + opts, + ); err != nil { + log.Fatal("Failed to dump repository: %v", err) + return err + } + + log.Trace("Dump finished!!!") + + return nil +} diff --git a/cmd/embedded.go b/cmd/embedded.go new file mode 100644 index 0000000..56d7e4c --- /dev/null +++ b/cmd/embedded.go @@ -0,0 +1,313 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "gitea.dev/modules/assetfs" + "gitea.dev/modules/glob" + "gitea.dev/modules/log" + "gitea.dev/modules/options" + "gitea.dev/modules/public" + "gitea.dev/modules/setting" + "gitea.dev/modules/templates" + + "github.com/urfave/cli/v3" +) + +var matchedAssetFiles []assetFile + +func newEmbeddedCommand() *cli.Command { + return &cli.Command{ + Name: "embedded", + Usage: "Extract embedded resources", + Description: "A command for extracting embedded resources, like templates and images", + Commands: []*cli.Command{ + newEmbeddedListCommand(), + newEmbeddedViewCommand(), + newEmbeddedExtractCommand(), + }, + } +} + +func newEmbeddedListCommand() *cli.Command { + return &cli.Command{ + Name: "list", + Usage: "List files matching the given pattern", + Action: runList, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "include-vendored", + Aliases: []string{"vendor"}, + Usage: "Include files under public/vendor as well", + }, + }, + } +} + +func newEmbeddedViewCommand() *cli.Command { + return &cli.Command{ + Name: "view", + Usage: "View a file matching the given pattern", + Action: runView, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "include-vendored", + Aliases: []string{"vendor"}, + Usage: "Include files under public/vendor as well", + }, + }, + } +} + +func newEmbeddedExtractCommand() *cli.Command { + return &cli.Command{ + Name: "extract", + Usage: "Extract resources", + Action: runExtract, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "include-vendored", + Aliases: []string{"vendor"}, + Usage: "Include files under public/vendor as well", + }, + &cli.BoolFlag{ + Name: "overwrite", + Usage: "Overwrite files if they already exist", + }, + &cli.BoolFlag{ + Name: "rename", + Usage: "Rename files as {name}.bak if they already exist (overwrites previous .bak)", + }, + &cli.BoolFlag{ + Name: "custom", + Usage: "Extract to the 'custom' directory as per app.ini", + }, + &cli.StringFlag{ + Name: "destination", + Aliases: []string{"dest-dir"}, + Usage: "Extract to the specified directory", + }, + }, + } +} + +type assetFile struct { + fs *assetfs.LayeredFS + name string + path string +} + +func initEmbeddedExtractor(c *cli.Command) error { + setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr) + + patterns, err := compileCollectPatterns(c.Args().Slice()) + if err != nil { + return err + } + + collectAssetFilesByPattern(c, patterns, "options", options.BuiltinAssets()) + collectAssetFilesByPattern(c, patterns, "public", public.BuiltinAssets()) + collectAssetFilesByPattern(c, patterns, "templates", templates.BuiltinAssets()) + + return nil +} + +func runList(_ context.Context, c *cli.Command) error { + if err := runListDo(c); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) + return err + } + return nil +} + +func runView(_ context.Context, c *cli.Command) error { + if err := runViewDo(c); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) + return err + } + return nil +} + +func runExtract(_ context.Context, c *cli.Command) error { + if err := runExtractDo(c); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) + return err + } + return nil +} + +func runListDo(c *cli.Command) error { + if err := initEmbeddedExtractor(c); err != nil { + return err + } + + for _, a := range matchedAssetFiles { + cprintln(c, a.path) + } + + return nil +} + +func runViewDo(c *cli.Command) error { + if err := initEmbeddedExtractor(c); err != nil { + return err + } + + if len(matchedAssetFiles) == 0 { + return errors.New("no files matched the given pattern") + } else if len(matchedAssetFiles) > 1 { + return errors.New("too many files matched the given pattern, try to be more specific") + } + + data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name) + if err != nil { + return fmt.Errorf("%s: %w", matchedAssetFiles[0].path, err) + } + + if _, err = os.Stdout.Write(data); err != nil { + return fmt.Errorf("%s: %w", matchedAssetFiles[0].path, err) + } + + return nil +} + +func runExtractDo(c *cli.Command) error { + if err := initEmbeddedExtractor(c); err != nil { + return err + } + + if c.NArg() == 0 { + return errors.New("a list of pattern of files to extract is mandatory (e.g. '**' for all)") + } + + destdir := "." + + if c.IsSet("destination") { + destdir = c.String("destination") + } else if c.Bool("custom") { + destdir = setting.CustomPath + cprintln(c, "Using app.ini at", setting.CustomConf) + } + + fi, err := os.Stat(destdir) + if errors.Is(err, os.ErrNotExist) { + // In case Windows users attempt to provide a forward-slash path + wdestdir := filepath.FromSlash(destdir) + if wfi, werr := os.Stat(wdestdir); werr == nil { + destdir = wdestdir + fi = wfi + err = nil + } + } + if err != nil { + return fmt.Errorf("%s: %s", destdir, err) + } else if !fi.IsDir() { + return fmt.Errorf("destination %q is not a directory", destdir) + } + + cprintf(c, "Extracting to %s:\n", destdir) + + overwrite := c.Bool("overwrite") + rename := c.Bool("rename") + + for _, a := range matchedAssetFiles { + if err := extractAsset(c, destdir, a, overwrite, rename); err != nil { + // Non-fatal error + _, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", a.path, err) + } + } + + return nil +} + +func extractAsset(c *cli.Command, d string, a assetFile, overwrite, rename bool) error { + dest := filepath.Join(d, filepath.FromSlash(a.path)) + dir := filepath.Dir(dest) + + data, err := a.fs.ReadFile(a.name) + if err != nil { + return fmt.Errorf("%s: %w", a.path, err) + } + + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return fmt.Errorf("%s: %w", dir, err) + } + + perms := os.ModePerm & 0o666 + + fi, err := os.Lstat(dest) + if err != nil { + if !errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("%s: %w", dest, err) + } + } else if !overwrite && !rename { + cprintf(c, "%s already exists; skipped.\n", dest) + return nil + } else if !fi.Mode().IsRegular() { + return fmt.Errorf("%s already exists, but it's not a regular file", dest) + } else if rename { + if err := os.Rename(dest, dest+".bak"); err != nil { + return fmt.Errorf("error creating backup for %s: %w", dest, err) + } + // Attempt to respect file permissions mask (even if user:group will be set anew) + perms = fi.Mode() + } + + file, err := os.OpenFile(dest, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, perms) + if err != nil { + return fmt.Errorf("%s: %w", dest, err) + } + defer file.Close() + + if _, err = file.Write(data); err != nil { + return fmt.Errorf("%s: %w", dest, err) + } + + cprintln(c, dest) + + return nil +} + +func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string, layer *assetfs.Layer) { + fs := assetfs.Layered(layer) + files, err := fs.ListAllFiles(".", true) + if err != nil { + log.Error("Error listing files in %q: %v", path, err) + return + } + for _, name := range files { + if path == "public" && + strings.HasPrefix(name, "vendor/") && + !c.Bool("include-vendored") { + continue + } + matchName := path + "/" + name + for _, g := range globs { + if g.Match(matchName) { + matchedAssetFiles = append(matchedAssetFiles, assetFile{fs: fs, name: name, path: path + "/" + name}) + break + } + } + } +} + +func compileCollectPatterns(args []string) (_ []glob.Glob, err error) { + if len(args) == 0 { + args = []string{"**"} + } + pat := make([]glob.Glob, len(args)) + for i := range args { + if pat[i], err = glob.Compile(args[i], '/'); err != nil { + return nil, fmt.Errorf("invalid glob pattern %q: %w", args[i], err) + } + } + return pat, nil +} diff --git a/cmd/generate.go b/cmd/generate.go new file mode 100644 index 0000000..122dbbc --- /dev/null +++ b/cmd/generate.go @@ -0,0 +1,181 @@ +// Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2016 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "os" + + "gitea.dev/modules/generate" + "gitea.dev/modules/ssh" + + "github.com/mattn/go-isatty" + "github.com/urfave/cli/v3" +) + +func newGenerateCommand() *cli.Command { + return &cli.Command{ + Name: "generate", + Usage: "Generate Gitea's secrets/keys/tokens", + Commands: []*cli.Command{ + newGenerateSecretCommand(), + newGenerateSSHCommand(), + }, + } +} + +func newGenerateSecretCommand() *cli.Command { + return &cli.Command{ + Name: "secret", + Usage: "Generate a secret token", + Commands: []*cli.Command{ + newGenerateInternalTokenCommand(), + newGenerateLfsJWTSecretCommand(), + newGenerateSecretKeyCommand(), + }, + } +} + +func newGenerateSSHCommand() *cli.Command { + return &cli.Command{ + Name: "ssh", + Usage: "Generate ssh keys", + Commands: []*cli.Command{ + newGenerateSSHKeyCommand(), + newGenerateSSHHostKeysCommand(), + }, + } +} + +func newGenerateInternalTokenCommand() *cli.Command { + return &cli.Command{ + Name: "INTERNAL_TOKEN", + Usage: "Generate a new INTERNAL_TOKEN", + Action: runGenerateInternalToken, + } +} + +func newGenerateLfsJWTSecretCommand() *cli.Command { + return &cli.Command{ + Name: "JWT_SECRET", + Aliases: []string{"LFS_JWT_SECRET"}, + Usage: "Generate a new JWT_SECRET", + Action: runGenerateLfsJwtSecret, + } +} + +func newGenerateSecretKeyCommand() *cli.Command { + return &cli.Command{ + Name: "SECRET_KEY", + Usage: "Generate a new SECRET_KEY", + Action: runGenerateSecretKey, + } +} + +func newGenerateSSHKeyCommand() *cli.Command { + return &cli.Command{ + Name: "key", + Usage: "Generate a new ssh key", + Flags: []cli.Flag{ + &cli.IntFlag{Name: "bits", Aliases: []string{"b"}, Usage: "Number of bits in the key, ignored when key is ed25519"}, + &cli.StringFlag{Name: "type", Aliases: []string{"t"}, Value: "ed25519", Usage: "Specifies the type of key to create."}, + &cli.StringFlag{Name: "file", Aliases: []string{"f"}, Usage: "Specifies the path or base directory for the key file", Required: true}, + }, + Action: runGenerateKeyPair, + } +} + +func newGenerateSSHHostKeysCommand() *cli.Command { + return &cli.Command{ + Name: "host-keys", + Usage: "Generate host keys of all default key types (rsa, ecdsa, and ed25519) if they do not already exist.", + Flags: []cli.Flag{ + &cli.StringFlag{Name: "dir", Aliases: []string{"d"}, Usage: "Specifies the base directory for the key files", Required: true}, + }, + Action: runGenerateHostKey, + } +} + +func runGenerateInternalToken(_ context.Context, c *cli.Command) error { + internalToken, err := generate.NewInternalToken() + if err != nil { + return err + } + + cprintf(c, "%s", internalToken) + + if isatty.IsTerminal(os.Stdout.Fd()) { + cprintf(c, "\n") + } + + return nil +} + +func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error { + _, jwtSecretBase64 := generate.NewJwtSecretWithBase64() + cprintf(c, "%s", jwtSecretBase64) + + if isatty.IsTerminal(os.Stdout.Fd()) { + cprintf(c, "\n") + } + + return nil +} + +func runGenerateSecretKey(_ context.Context, c *cli.Command) error { + secretKey, err := generate.NewSecretKey() + if err != nil { + return err + } + + // codeql[disable-next-line=go/clear-text-logging] + cprintf(c, "%s", secretKey) + + if isatty.IsTerminal(os.Stdout.Fd()) { + cprintf(c, "\n") + } + + return nil +} + +func runGenerateHostKey(_ context.Context, c *cli.Command) error { + file := c.String("dir") + info, err := os.Stat(file) + if errors.Is(err, os.ErrNotExist) { + if err = os.MkdirAll(file, 0o644); err != nil { + return err + } + } else if err != nil { + return err + } else if !info.IsDir() { + return errors.New("file already exists and is not a directory") + } + fmt.Fprintf(c.Writer, "Generating host keys in %s\n", file) + _, err = ssh.InitDefaultHostKeys(file) + return err +} + +func runGenerateKeyPair(_ context.Context, c *cli.Command) error { + file := c.String("file") + keyType := c.String("type") + + fmt.Fprintf(c.Writer, "Generating public/private %s key pair.\n", keyType) + + // Check if file exists to prevent overwriting + if _, err := os.Stat(file); err == nil { + if !confirm(c.Reader, c.Writer, "%s already exists.\nOverwrite (y/n)? ", file) { + cprintln(c, "Aborting") + return nil + } + } + bits := c.Int("bits") + err := ssh.GenKeyPair(file, generate.SSHKeyType(keyType), bits) + if err == nil { + cprintf(c, "Your SSH key has been saved in %s\n", file) + } + return err +} diff --git a/cmd/helper.go b/cmd/helper.go new file mode 100644 index 0000000..592eda5 --- /dev/null +++ b/cmd/helper.go @@ -0,0 +1,149 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "os/signal" + "strings" + "syscall" + + "gitea.dev/models/db" + "gitea.dev/modules/log" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +// argsSet checks that all the required arguments are set. +// args is a list of arguments that must be set in the command context. +func argsSet(c *cli.Command, args ...string) error { + for _, a := range args { + if !c.IsSet(a) { + return errors.New(a + " is not set") + } + + if c.Value(a) == nil { + return errors.New(a + " is required") + } + } + return nil +} + +// confirm waits for user input which confirms an action +func confirm(stdin io.Reader, stdout io.Writer, msg string, args ...any) bool { + var response string + _, _ = fmt.Fprintf(stdout, msg, args...) + _, _ = fmt.Fscanln(stdin, &response) + switch strings.ToLower(response) { + case "y", "yes": + return true + } + return false +} + +func initDB(ctx context.Context) error { + setting.MustInstalled() + setting.LoadDBSetting() + setting.InitSQLLoggersForCli(log.INFO) + + if setting.Database.Type == "" { + log.Fatal(`Database settings are missing from the configuration file: %q. +Ensure you are running in the correct environment or set the correct configuration file with -c. +If this is the intended configuration file complete the [database] section.`, setting.CustomConf) + } + if err := db.InitEngine(ctx); err != nil { + return fmt.Errorf("unable to initialize the database using the configuration in %q. Error: %w", setting.CustomConf, err) + } + return nil +} + +func installSignals() (context.Context, context.CancelFunc) { + ctx, cancel := context.WithCancel(context.Background()) + go func() { + // install notify + signalChannel := make(chan os.Signal, 1) + + signal.Notify( + signalChannel, + syscall.SIGINT, + syscall.SIGTERM, + ) + select { + case <-signalChannel: + case <-ctx.Done(): + } + cancel() + signal.Reset() + }() + + return ctx, cancel +} + +func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) { + if out != os.Stdout && out != os.Stderr { + panic("setupConsoleLogger can only be used with os.Stdout or os.Stderr") + } + + writeMode := log.WriterMode{ + Level: level, + Colorize: colorize, + WriterOption: log.WriterConsoleOption{Stderr: out == os.Stderr}, + } + writer := log.NewEventWriterConsole("console-default", writeMode) + log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer) +} + +func globalBool(c *cli.Command, name string) bool { + for _, ctx := range c.Lineage() { + if ctx.Bool(name) { + return true + } + } + return false +} + +// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout. +// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever. +func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cli.Command) (context.Context, error) { + return func(ctx context.Context, c *cli.Command) (context.Context, error) { + if setting.InstallLock { + // During config loading, there might also be logs (for example: deprecation warnings). + // It must make sure that console logger is set up before config is loaded. + log.Error("Config is loaded before console logger is setup, it will cause bugs. Please fix it. CustomConf=%s", setting.CustomConf) + return nil, errors.New("console logger must be setup before config is loaded") + } + level := defaultLevel + if globalBool(c, "quiet") { + level = log.FATAL + } + if globalBool(c, "debug") || globalBool(c, "verbose") { + level = log.TRACE + } + log.SetupStderrLogger(log.DEFAULT, "console-stderr", level) + return ctx, nil + } +} + +func isValidDefaultSubCommand(cmd *cli.Command) (string, bool) { + // Dirty patch for urfave/cli's strange design. + // "./gitea bad-cmd" should not start the web server. + args := cmd.Args().Slice() + if len(args) != 0 { + return args[0], false + } + return "", true +} + +func cprintf(c *cli.Command, format string, args ...any) { + _, _ = fmt.Fprintf(c.Writer, format, args...) +} + +func cprintln(c *cli.Command, args ...any) { + _, _ = fmt.Fprintln(c.Writer, args...) +} diff --git a/cmd/hook.go b/cmd/hook.go new file mode 100644 index 0000000..b0065d2 --- /dev/null +++ b/cmd/hook.go @@ -0,0 +1,782 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "bufio" + "bytes" + "context" + "fmt" + "io" + "os" + "strconv" + "strings" + "time" + + "gitea.dev/modules/git" + "gitea.dev/modules/git/gitcmd" + "gitea.dev/modules/log" + "gitea.dev/modules/private" + repo_module "gitea.dev/modules/repository" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +const ( + hookBatchSize = 500 +) + +func newHookCommand() *cli.Command { + return &cli.Command{ + Name: "hook", + Usage: "(internal) Should only be called by Git", + Hidden: true, // internal commands shouldn't be visible + Description: "Delegate commands to corresponding Git hooks", + Before: PrepareConsoleLoggerLevel(log.FATAL), + Commands: []*cli.Command{ + newHookPreReceiveCommand(), + newHookUpdateCommand(), + newHookPostReceiveCommand(), + newHookProcReceiveCommand(), + }, + } +} + +func newHookPreReceiveCommand() *cli.Command { + return &cli.Command{ + Name: "pre-receive", + Usage: "Delegate pre-receive Git hook", + Description: "This command should only be called by Git", + Action: runHookPreReceive, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + } +} + +func newHookUpdateCommand() *cli.Command { + return &cli.Command{ + Name: "update", + Usage: "Delegate update Git hook", + Description: "This command should only be called by Git", + Action: runHookUpdate, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + } +} + +func newHookPostReceiveCommand() *cli.Command { + return &cli.Command{ + Name: "post-receive", + Usage: "Delegate post-receive Git hook", + Description: "This command should only be called by Git", + Action: runHookPostReceive, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + } +} + +// Note: new hook since git 2.29 +func newHookProcReceiveCommand() *cli.Command { + return &cli.Command{ + Name: "proc-receive", + Usage: "Delegate proc-receive Git hook", + Description: "This command should only be called by Git", + Action: runHookProcReceive, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + } +} + +type delayWriter struct { + internal io.Writer + buf *bytes.Buffer + timer *time.Timer +} + +func newDelayWriter(internal io.Writer, delay time.Duration) *delayWriter { + timer := time.NewTimer(delay) + return &delayWriter{ + internal: internal, + buf: &bytes.Buffer{}, + timer: timer, + } +} + +func (d *delayWriter) Write(p []byte) (n int, err error) { + if d.buf != nil { + select { + case <-d.timer.C: + _, err := d.internal.Write(d.buf.Bytes()) + if err != nil { + return 0, err + } + d.buf = nil + return d.internal.Write(p) + default: + return d.buf.Write(p) + } + } + return d.internal.Write(p) +} + +func (d *delayWriter) WriteString(s string) (n int, err error) { + if d.buf != nil { + select { + case <-d.timer.C: + _, err := d.internal.Write(d.buf.Bytes()) + if err != nil { + return 0, err + } + d.buf = nil + return d.internal.Write([]byte(s)) + default: + return d.buf.WriteString(s) + } + } + return d.internal.Write([]byte(s)) +} + +func (d *delayWriter) Close() error { + stopped := d.timer.Stop() + if stopped || d.buf == nil { + return nil + } + _, err := d.internal.Write(d.buf.Bytes()) + d.buf = nil + return err +} + +func parseGitHookCommitRefLine(line string) (oldCommitID, newCommitID string, refFullName git.RefName, ok bool) { + fields := strings.Split(line, " ") + if len(fields) != 3 { + return "", "", "", false + } + return fields[0], fields[1], git.RefName(fields[2]), true +} + +func runHookPreReceive(ctx context.Context, c *cli.Command) error { + if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal { + return nil + } + + setup(ctx, c.Bool("debug")) + + if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { + if setting.OnlyAllowPushIfGiteaEnvironmentSet { + return fail(ctx, `Rejecting changes as Gitea environment not set. +If you are pushing over SSH you must push with a key managed by +Gitea or set your environment appropriately.`, "") + } + return nil + } + + // the environment is set by serv command + isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki)) + ownerName := os.Getenv(repo_module.EnvRepoUsername) + repoName := os.Getenv(repo_module.EnvRepoName) + userID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64) + prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64) + + hookOptions := private.HookOptions{ + IsWiki: isWiki, + + GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories), + GitObjectDirectory: os.Getenv(private.GitObjectDirectory), + GitQuarantinePath: os.Getenv(private.GitQuarantinePath), + GitPushOptions: pushOptions(), + + PullRequestID: prID, + + UserID: userID, + UserName: os.Getenv(repo_module.EnvPusherName), + UserExtDoerData: os.Getenv(repo_module.EnvPusherExtDoerData), + } + + scanner := bufio.NewScanner(os.Stdin) + + oldCommitIDs := make([]string, hookBatchSize) + newCommitIDs := make([]string, hookBatchSize) + refFullNames := make([]git.RefName, hookBatchSize) + count := 0 + total := 0 + lastline := 0 + + out := io.Discard + if setting.Git.VerbosePush { + if setting.Git.VerbosePushDelay > 0 { + dWriter := newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay) + defer dWriter.Close() + out = dWriter + } else { + out = os.Stdout + } + } + + supportProcReceive := git.DefaultFeatures().SupportProcReceive + + for scanner.Scan() { + // TODO: support news feeds for wiki + if isWiki { + continue + } + + oldCommitID, newCommitID, refFullName, ok := parseGitHookCommitRefLine(scanner.Text()) + if !ok { + continue + } + + total++ + lastline++ + + // If the ref is a branch or tag, check if it's protected + // if supportProcReceive all ref should be checked because + // permission check was delayed + if supportProcReceive || refFullName.IsBranch() || refFullName.IsTag() { + oldCommitIDs[count] = oldCommitID + newCommitIDs[count] = newCommitID + refFullNames[count] = refFullName + count++ + fmt.Fprintf(out, "*") + + if count >= hookBatchSize { + fmt.Fprintf(out, " Checking %d references\n", count) + + hookOptions.OldCommitIDs = oldCommitIDs + hookOptions.NewCommitIDs = newCommitIDs + hookOptions.RefFullNames = refFullNames + extra := private.HookPreReceive(ctx, ownerName, repoName, hookOptions) + if extra.HasError() { + return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error) + } + count = 0 + lastline = 0 + } + } else { + fmt.Fprintf(out, ".") + } + if lastline >= hookBatchSize { + fmt.Fprintf(out, "\n") + lastline = 0 + } + } + if err := scanner.Err(); err != nil { + return fail(ctx, "Hook failed: stdin read error", "scanner error: %v", err) + } + + if count > 0 { + hookOptions.OldCommitIDs = oldCommitIDs[:count] + hookOptions.NewCommitIDs = newCommitIDs[:count] + hookOptions.RefFullNames = refFullNames[:count] + + fmt.Fprintf(out, " Checking %d references\n", count) + + extra := private.HookPreReceive(ctx, ownerName, repoName, hookOptions) + if extra.HasError() { + return fail(ctx, extra.UserMsg, "HookPreReceive(last) failed: %v", extra.Error) + } + } else if lastline > 0 { + fmt.Fprintf(out, "\n") + } + + fmt.Fprintf(out, "Checked %d references in total\n", total) + return nil +} + +// runHookUpdate avoid to do heavy operations on update hook because it will be +// invoked for every ref update which does not like pre-receive and post-receive +func runHookUpdate(_ context.Context, c *cli.Command) error { + if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal { + return nil + } + + // Update is empty and is kept only for backwards compatibility + if len(os.Args) < 3 { + return nil + } + refName := git.RefName(os.Args[len(os.Args)-3]) + if refName.IsPull() { + // ignore update to refs/pull/xxx/head, so we don't need to output any information + os.Exit(1) + } + return nil +} + +func runHookPostReceive(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + + // First of all run update-server-info no matter what + if err := gitcmd.NewCommand("update-server-info").RunWithStderr(ctx); err != nil { + return fmt.Errorf("failed to call 'git update-server-info': %w", err) + } + + // Now if we're an internal don't do anything else + if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal { + return nil + } + + if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { + if setting.OnlyAllowPushIfGiteaEnvironmentSet { + return fail(ctx, `Rejecting changes as Gitea environment not set. +If you are pushing over SSH you must push with a key managed by +Gitea or set your environment appropriately.`, "") + } + return nil + } + + out := io.Discard + if setting.Git.VerbosePush { + if setting.Git.VerbosePushDelay > 0 { + dWriter := newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay) + defer dWriter.Close() + out = dWriter + } else { + out = os.Stdout + } + } + + // the environment is set by serv command + repoUser := os.Getenv(repo_module.EnvRepoUsername) + isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki)) + repoName := os.Getenv(repo_module.EnvRepoName) + pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64) + prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64) + + hookOptions := private.HookOptions{ + IsWiki: isWiki, + + GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories), + GitObjectDirectory: os.Getenv(private.GitObjectDirectory), + GitQuarantinePath: os.Getenv(private.GitQuarantinePath), + GitPushOptions: pushOptions(), + + PullRequestID: prID, + PushTrigger: repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)), + + UserID: pusherID, + UserName: os.Getenv(repo_module.EnvPusherName), + UserExtDoerData: os.Getenv(repo_module.EnvPusherExtDoerData), + } + + oldCommitIDs := make([]string, 0, hookBatchSize) + newCommitIDs := make([]string, 0, hookBatchSize) + refFullNames := make([]git.RefName, 0, hookBatchSize) + results := make([]private.HookPostReceiveBranchResult, 0) + + defer func() { + hookPrintResults(results) + }() + + processBatch := func() error { + if len(refFullNames) == 0 { + return nil + } + _, _ = fmt.Fprintf(out, " Processing %d references\n", len(refFullNames)) + hookOptions.OldCommitIDs = oldCommitIDs + hookOptions.NewCommitIDs = newCommitIDs + hookOptions.RefFullNames = refFullNames + resp, extra := private.HookPostReceive(ctx, repoUser, repoName, hookOptions) + if extra.HasError() { + return fail(ctx, extra.UserMsg, "HookPostReceive failed: %v", extra.Error) + } + results = append(results, resp.Results...) + oldCommitIDs = oldCommitIDs[:0] + newCommitIDs = newCommitIDs[:0] + refFullNames = refFullNames[:0] + return nil + } + + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + // wiki doesn't need "post-receive" at the moment + if isWiki { + continue + } + + oldCommitID, newCommitID, refFullName, ok := parseGitHookCommitRefLine(scanner.Text()) + if !ok { + continue + } + _, _ = fmt.Fprintf(out, ".") + + oldCommitIDs = append(oldCommitIDs, oldCommitID) + newCommitIDs = append(newCommitIDs, newCommitID) + refFullNames = append(refFullNames, refFullName) + if len(refFullNames) >= hookBatchSize { + // process and start a new batch + if err := processBatch(); err != nil { + return err + } + } + } + if err := scanner.Err(); err != nil { + return fail(ctx, "Hook failed: stdin read error", "scanner error: %v", err) + } + return processBatch() +} + +func hookPrintResults(results []private.HookPostReceiveBranchResult) { + for _, res := range results { + hookPrintResult(res.Message, res.Create, res.Branch, res.URL) + } +} + +func hookPrintResult(output, isCreate bool, branch, url string) { + if !output { + return + } + fmt.Fprintln(os.Stderr, "") + if isCreate { + fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", branch) + fmt.Fprintf(os.Stderr, " %s\n", url) + } else { + fmt.Fprint(os.Stderr, "Visit the existing pull request:\n") + fmt.Fprintf(os.Stderr, " %s\n", url) + } + fmt.Fprintln(os.Stderr, "") + _ = os.Stderr.Sync() +} + +func pushOptions() map[string]string { + opts := make(map[string]string) + if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil { + for idx := range pushCount { + opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx)) + kv := strings.SplitN(opt, "=", 2) + if len(kv) == 2 { + opts[kv[0]] = kv[1] + } + } + } + return opts +} + +func runHookProcReceive(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + + if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { + if setting.OnlyAllowPushIfGiteaEnvironmentSet { + return fail(ctx, `Rejecting changes as Gitea environment not set. +If you are pushing over SSH you must push with a key managed by +Gitea or set your environment appropriately.`, "") + } + return nil + } + + if !git.DefaultFeatures().SupportProcReceive { + return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.") + } + + reader := bufio.NewReader(os.Stdin) + repoUser := os.Getenv(repo_module.EnvRepoUsername) + isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki)) + repoName := os.Getenv(repo_module.EnvRepoName) + pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64) + + // 1. Version and features negotiation. + // S: PKT-LINE(version=1\0push-options atomic...) / PKT-LINE(version=1\n) + // S: flush-pkt + // H: PKT-LINE(version=1\0push-options...) + // H: flush-pkt + + rs, err := readPktLine(ctx, reader, pktLineTypeData) + if err != nil { + return err + } + + const VersionHead string = "version=1" + + var ( + hasPushOptions bool + response = []byte(VersionHead) + requestOptions []string + ) + + index := bytes.IndexByte(rs.Data, byte(0)) + if index >= len(rs.Data) { + return fail(ctx, "Protocol: format error", "pkt-line: format error %s", rs.Data) + } + + if index < 0 { + if len(rs.Data) == 10 && rs.Data[9] == '\n' { + index = 9 + } else { + return fail(ctx, "Protocol: format error", "pkt-line: format error %s", rs.Data) + } + } + + if string(rs.Data[0:index]) != VersionHead { + return fail(ctx, "Protocol: version error", "Received unsupported version: %s", string(rs.Data[0:index])) + } + requestOptions = strings.Split(string(rs.Data[index+1:]), " ") + + for _, option := range requestOptions { + if strings.HasPrefix(option, "push-options") { + response = append(response, byte(0)) + response = append(response, []byte("push-options")...) + hasPushOptions = true + } + } + response = append(response, '\n') + + _, err = readPktLine(ctx, reader, pktLineTypeFlush) + if err != nil { + return err + } + + err = writeDataPktLine(ctx, os.Stdout, response) + if err != nil { + return err + } + + err = writeFlushPktLine(ctx, os.Stdout) + if err != nil { + return err + } + + // 2. receive commands from server. + // S: PKT-LINE( ) + // S: ... ... + // S: flush-pkt + // # [receive push-options] + // S: PKT-LINE(push-option) + // S: ... ... + // S: flush-pkt + hookOptions := private.HookOptions{ + IsWiki: isWiki, + + GitPushOptions: make(map[string]string), + + UserID: pusherID, + UserName: os.Getenv(repo_module.EnvPusherName), + UserExtDoerData: os.Getenv(repo_module.EnvPusherExtDoerData), + } + hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize) + hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize) + hookOptions.RefFullNames = make([]git.RefName, 0, hookBatchSize) + + for { + // note: pktLineTypeUnknown means pktLineTypeFlush and pktLineTypeData all allowed + rs, err = readPktLine(ctx, reader, pktLineTypeUnknown) + if err != nil { + return err + } + + if rs.Type == pktLineTypeFlush { + break + } + t := strings.SplitN(string(rs.Data), " ", 3) + if len(t) != 3 { + continue + } + hookOptions.OldCommitIDs = append(hookOptions.OldCommitIDs, t[0]) + hookOptions.NewCommitIDs = append(hookOptions.NewCommitIDs, t[1]) + hookOptions.RefFullNames = append(hookOptions.RefFullNames, git.RefName(t[2])) + } + + if hasPushOptions { + for { + rs, err = readPktLine(ctx, reader, pktLineTypeUnknown) + if err != nil { + return err + } + + if rs.Type == pktLineTypeFlush { + break + } + hookOptions.GitPushOptions.AddFromKeyValue(string(rs.Data)) + } + } + + // 3. run hook + resp, extra := private.HookProcReceive(ctx, repoUser, repoName, hookOptions) + if extra.HasError() { + return fail(ctx, extra.UserMsg, "HookProcReceive failed: %v", extra.Error) + } + + // 4. response result to service + // # a. OK, but has an alternate reference. The alternate reference name + // # and other status can be given in option directives. + // H: PKT-LINE(ok ) + // H: PKT-LINE(option refname ) + // H: PKT-LINE(option old-oid ) + // H: PKT-LINE(option new-oid ) + // H: PKT-LINE(option forced-update) + // H: ... ... + // H: flush-pkt + // # b. NO, I reject it. + // H: PKT-LINE(ng ) + // # c. Fall through, let 'receive-pack' to execute it. + // H: PKT-LINE(ok ) + // H: PKT-LINE(option fall-through) + + for _, rs := range resp.Results { + if len(rs.Err) > 0 { + err = writeDataPktLine(ctx, os.Stdout, []byte("ng "+rs.OriginalRef.String()+" "+rs.Err)) + if err != nil { + return err + } + continue + } + + if rs.IsNotMatched { + err = writeDataPktLine(ctx, os.Stdout, []byte("ok "+rs.OriginalRef.String())) + if err != nil { + return err + } + err = writeDataPktLine(ctx, os.Stdout, []byte("option fall-through")) + if err != nil { + return err + } + continue + } + + err = writeDataPktLine(ctx, os.Stdout, []byte("ok "+rs.OriginalRef)) + if err != nil { + return err + } + err = writeDataPktLine(ctx, os.Stdout, []byte("option refname "+rs.Ref)) + if err != nil { + return err + } + commitID, _ := git.NewIDFromString(rs.OldOID) + if !commitID.IsZero() { + err = writeDataPktLine(ctx, os.Stdout, []byte("option old-oid "+rs.OldOID)) + if err != nil { + return err + } + } + err = writeDataPktLine(ctx, os.Stdout, []byte("option new-oid "+rs.NewOID)) + if err != nil { + return err + } + if rs.IsForcePush { + err = writeDataPktLine(ctx, os.Stdout, []byte("option forced-update")) + if err != nil { + return err + } + } + } + err = writeFlushPktLine(ctx, os.Stdout) + + if err == nil { + for _, res := range resp.Results { + hookPrintResult(res.ShouldShowMessage, res.IsCreatePR, res.HeadBranch, res.URL) + } + } + + return err +} + +// git PKT-Line api +// pktLineType message type of pkt-line +type pktLineType int64 + +const ( + // Unknown type + pktLineTypeUnknown pktLineType = 0 + // flush-pkt "0000" + pktLineTypeFlush pktLineType = iota + // data line + pktLineTypeData +) + +// gitPktLine pkt-line api +type gitPktLine struct { + Type pktLineType + Length uint64 + Data []byte +} + +func readPktLine(ctx context.Context, in *bufio.Reader, requestType pktLineType) (*gitPktLine, error) { + var ( + err error + r *gitPktLine + ) + + // read prefix + lengthBytes := make([]byte, 4) + for i := range 4 { + lengthBytes[i], err = in.ReadByte() + if err != nil { + return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err) + } + } + + r = new(gitPktLine) + r.Length, err = strconv.ParseUint(string(lengthBytes), 16, 32) + if err != nil { + return nil, fail(ctx, "Protocol: format parse error", "Pkt-Line format is wrong :%v", err) + } + + if r.Length == 0 { + if requestType == pktLineTypeData { + return nil, fail(ctx, "Protocol: format data error", "Pkt-Line format is wrong") + } + r.Type = pktLineTypeFlush + return r, nil + } + + if r.Length <= 4 || r.Length > 65520 || requestType == pktLineTypeFlush { + return nil, fail(ctx, "Protocol: format length error", "Pkt-Line format is wrong") + } + + r.Data = make([]byte, r.Length-4) + for i := range r.Data { + r.Data[i], err = in.ReadByte() + if err != nil { + return nil, fail(ctx, "Protocol: data error", "Pkt-Line: read stdin failed : %v", err) + } + } + + r.Type = pktLineTypeData + + return r, nil +} + +func writeFlushPktLine(ctx context.Context, out io.Writer) error { + l, err := out.Write([]byte("0000")) + if err != nil || l != 4 { + return fail(ctx, "Protocol: write error", "Pkt-Line response failed: %v", err) + } + return nil +} + +func writeDataPktLine(ctx context.Context, out io.Writer, data []byte) error { + hexchar := []byte("0123456789abcdef") + hex := func(n uint64) byte { + return hexchar[n&15] + } + + length := uint64(len(data) + 4) + tmp := make([]byte, 4) + tmp[0] = hex(length >> 12) + tmp[1] = hex(length >> 8) + tmp[2] = hex(length >> 4) + tmp[3] = hex(length) + + lr, err := out.Write(tmp) + if err != nil || lr != 4 { + return fail(ctx, "Protocol: write error", "Pkt-Line response failed: %v", err) + } + + lr, err = out.Write(data) + if err != nil || int(length-4) != lr { + return fail(ctx, "Protocol: write error", "Pkt-Line response failed: %v", err) + } + + return nil +} diff --git a/cmd/hook_test.go b/cmd/hook_test.go new file mode 100644 index 0000000..fefc33c --- /dev/null +++ b/cmd/hook_test.go @@ -0,0 +1,55 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "bufio" + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPktLine(t *testing.T) { + // test read + ctx := t.Context() + s := strings.NewReader("0000") + r := bufio.NewReader(s) + result, err := readPktLine(ctx, r, pktLineTypeFlush) + assert.NoError(t, err) + assert.Equal(t, pktLineTypeFlush, result.Type) + + s = strings.NewReader("0006a\n") + r = bufio.NewReader(s) + result, err = readPktLine(ctx, r, pktLineTypeData) + assert.NoError(t, err) + assert.Equal(t, pktLineTypeData, result.Type) + assert.Equal(t, []byte("a\n"), result.Data) + + // test write + w := bytes.NewBuffer([]byte{}) + err = writeFlushPktLine(ctx, w) + assert.NoError(t, err) + assert.Equal(t, []byte("0000"), w.Bytes()) + + w.Reset() + err = writeDataPktLine(ctx, w, []byte("a\nb")) + assert.NoError(t, err) + assert.Equal(t, []byte("0007a\nb"), w.Bytes()) +} + +func TestParseGitHookCommitRefLine(t *testing.T) { + oldCommitID, newCommitID, refName, ok := parseGitHookCommitRefLine("a b c") + assert.True(t, ok) + assert.Equal(t, "a", oldCommitID) + assert.Equal(t, "b", newCommitID) + assert.Equal(t, "c", string(refName)) + + _, _, _, ok = parseGitHookCommitRefLine("a\tb\tc") + assert.False(t, ok) + + _, _, _, ok = parseGitHookCommitRefLine("a b") + assert.False(t, ok) +} diff --git a/cmd/keys.go b/cmd/keys.go new file mode 100644 index 0000000..2f9f52e --- /dev/null +++ b/cmd/keys.go @@ -0,0 +1,84 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "strings" + + "gitea.dev/modules/log" + "gitea.dev/modules/private" + + "github.com/urfave/cli/v3" +) + +// NewKeysCommand returns the internal SSH key lookup sub-command. +func NewKeysCommand() *cli.Command { + return &cli.Command{ + Name: "keys", + Usage: "(internal) Should only be called by SSH server", + Hidden: true, // internal commands shouldn't be visible + Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint", + Before: PrepareConsoleLoggerLevel(log.FATAL), + Action: runKeys, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "expected", + Aliases: []string{"e"}, + Value: "git", + Usage: "Expected user for whom provide key commands", + }, + &cli.StringFlag{ + Name: "username", + Aliases: []string{"u"}, + Value: "", + Usage: "Username trying to log in by SSH", + }, + &cli.StringFlag{ + Name: "type", + Aliases: []string{"t"}, + Value: "", + Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)", + }, + &cli.StringFlag{ + Name: "content", + Aliases: []string{"k"}, + Value: "", + Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)", + }, + }, + } +} + +func runKeys(ctx context.Context, c *cli.Command) error { + if !c.IsSet("username") { + return errors.New("No username provided") + } + // Check username matches the expected username + if strings.TrimSpace(c.String("username")) != strings.TrimSpace(c.String("expected")) { + return nil + } + + content := "" + + if c.IsSet("type") && c.IsSet("content") { + content = fmt.Sprintf("%s %s", strings.TrimSpace(c.String("type")), strings.TrimSpace(c.String("content"))) + } + + if content == "" { + return errors.New("No key type and content provided") + } + + setup(ctx, c.Bool("debug")) + + authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content) + // do not use handleCliResponseExtra or cli.NewExitError, if it exists immediately, it breaks some tests like Test_CmdKeys + if extra.Error != nil { + return extra.Error + } + _, _ = fmt.Fprintln(c.Root().Writer, strings.TrimSpace(authorizedString.Text)) + return nil +} diff --git a/cmd/mailer.go b/cmd/mailer.go new file mode 100644 index 0000000..d4c94f1 --- /dev/null +++ b/cmd/mailer.go @@ -0,0 +1,39 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + + "gitea.dev/modules/private" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +func runSendMail(ctx context.Context, c *cli.Command) error { + setting.MustInstalled() + + subject := c.String("title") + confirmSkipped := c.Bool("force") + body := c.String("content") + + if !confirmSkipped { + if len(body) == 0 { + cprintln(c, "warning: Content is empty") + } + + if !confirm(c.Reader, c.Writer, "Proceed with sending email? [Y/n] ") { + cprintln(c, "The mail was not sent") + return nil + } + } + + respText, extra := private.SendEmail(ctx, subject, body, nil) + if extra.HasError() { + return handleCliResponseExtra(extra) + } + cprintf(c, "Sent %s email(s) to all users\n", respText.Text) + return nil +} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..66b2fba --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,193 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "io" + "os" + + "gitea.dev/modules/log" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +var cliHelpPrinterOld = cli.HelpPrinter + +func init() { + cli.HelpPrinter = cliHelpPrinterNew +} + +// cliHelpPrinterNew helps to print "DEFAULT CONFIGURATION" for the following cases ( "-c" can apper in any position): +// * ./gitea -c /dev/null -h +// * ./gitea -c help /dev/null help +// * ./gitea help -c /dev/null +// * ./gitea help -c /dev/null web +// * ./gitea help web -c /dev/null +// * ./gitea web help -c /dev/null +// * ./gitea web -h -c /dev/null +func cliHelpPrinterNew(out io.Writer, templ string, data any) { + cmd, _ := data.(*cli.Command) + if cmd != nil { + prepareWorkPathAndCustomConf(cmd) + } + cliHelpPrinterOld(out, templ, data) + if setting.CustomConf != "" { + _, _ = fmt.Fprintf(out, ` +DEFAULT CONFIGURATION: + AppPath: %s + WorkPath: %s + CustomPath: %s + ConfigFile: %s + +`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf) + } +} + +func PrepareSubcommandWithGlobalFlags(originCmd *cli.Command) { + originBefore := originCmd.Before + originCmd.Before = func(ctxOrig context.Context, cmd *cli.Command) (ctx context.Context, err error) { + ctx = ctxOrig + if originBefore != nil { + ctx, err = originBefore(ctx, cmd) + if err != nil { + return ctx, err + } + } + prepareWorkPathAndCustomConf(cmd) + return ctx, nil + } +} + +// prepareWorkPathAndCustomConf tries to prepare the work path, custom path and custom config from various inputs: +// command line flags, environment variables, config file +func prepareWorkPathAndCustomConf(cmd *cli.Command) { + var args setting.ArgWorkPathAndCustomConf + if cmd.IsSet("work-path") { + args.WorkPath = cmd.String("work-path") + } + if cmd.IsSet("custom-path") { + args.CustomPath = cmd.String("custom-path") + } + if cmd.IsSet("config") { + args.CustomConf = cmd.String("config") + } + setting.InitWorkPathAndCommonConfig(os.Getenv, args) +} + +type AppVersion struct { + Version string + Extra string +} + +func NewMainApp(appVer AppVersion) *cli.Command { + app := &cli.Command{} + app.Name = "gitea" // must be lower-cased because it appears in the "USAGE" section like "gitea doctor [command [command options]]" + app.Usage = "A painless self-hosted Git service" + app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.` + app.Version = appVer.Version + appVer.Extra + app.EnableShellCompletion = true + app.Flags = []cli.Flag{ + &cli.StringFlag{ + Name: "work-path", + Aliases: []string{"w"}, + TakesFile: true, + Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)", + }, + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + TakesFile: true, + Value: setting.CustomConf, + Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')", + }, + &cli.StringFlag{ + Name: "custom-path", + Aliases: []string{"C"}, + TakesFile: true, + Usage: "Set custom path (defaults to '{WorkPath}/custom')", + }, + } + webCmd := newWebCommand() + // these sub-commands need to use a config file + subCmdWithConfig := []*cli.Command{ + webCmd, + newServCommand(), + newHookCommand(), + NewKeysCommand(), + newDumpCommand(), + newAdminCommand(), + newMigrateCommand(), + newDoctorCommand(), + newManagerCommand(), + newEmbeddedCommand(), + newMigrateStorageCommand(), + newDumpRepositoryCommand(), + newRestoreRepositoryCommand(), + newActionsCommand(), + } + + // these sub-commands do not need the config file, and they do not depend on any path or environment variable. + subCmdStandalone := []*cli.Command{ + cmdConfig(), + cmdCert(), + newGenerateCommand(), + newDocsCommand(), + } + + // TODO: we should eventually drop the default command, + // but not sure whether it would break Windows users who used to double-click the EXE to run. + app.DefaultCommand = webCmd.Name + + app.Before = PrepareConsoleLoggerLevel(log.INFO) + for i := range subCmdWithConfig { + PrepareSubcommandWithGlobalFlags(subCmdWithConfig[i]) + } + app.Commands = append(app.Commands, subCmdWithConfig...) + app.Commands = append(app.Commands, subCmdStandalone...) + + setting.UnsetUnnecessaryEnvVars() + return app +} + +// usageErr marks a usage error already reported by cliOnUsageError, so RunMainApp does not print it again. +type usageErr struct{ err error } + +func (e usageErr) Error() string { return e.err.Error() } +func (e usageErr) Unwrap() error { return e.err } + +// cliOnUsageError reports usage errors itself instead of letting urfave/cli dump the full help to stdout (since urfave/cli v3.10). +func cliOnUsageError(_ context.Context, cmd *cli.Command, err error, _ bool) error { + _, _ = fmt.Fprintf(cmd.Root().ErrWriter, "Incorrect Usage: %s\n", err.Error()) + return usageErr{err} +} + +func setCLIOnUsageError(cmd *cli.Command) { + _ = cmd.Walk(func(c *cli.Command) error { + c.OnUsageError = cliOnUsageError + return nil + }) +} + +func RunMainApp(app *cli.Command, args ...string) error { + ctx, cancel := installSignals() + defer cancel() + setCLIOnUsageError(app) + // the completion subcommands are built during app.Run, after the Walk above, so cover them via this hook + app.ConfigureShellCompletionCommand = setCLIOnUsageError + err := app.Run(ctx, args) + if err == nil { + return nil + } + if _, ok := errors.AsType[usageErr](err); ok { + cli.OsExiter(1) // cliOnUsageError already reported it + return err + } + _, _ = fmt.Fprintf(app.ErrWriter, "Command error: %v\n", err) + cli.OsExiter(1) + return err +} diff --git a/cmd/main_test.go b/cmd/main_test.go new file mode 100644 index 0000000..0476980 --- /dev/null +++ b/cmd/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "testing" + + "gitea.dev/models/unittest" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} + +func TestDefaultCommand(t *testing.T) { + test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) { + called := false + cmd := &cli.Command{ + DefaultCommand: "test", + Commands: []*cli.Command{ + { + Name: "test", + Action: func(ctx context.Context, command *cli.Command) error { + retName, retValid := isValidDefaultSubCommand(command) + assert.Equal(t, expectedRetName, retName) + assert.Equal(t, expectedRetValid, retValid) + called = true + return nil + }, + }, + }, + } + assert.NoError(t, cmd.Run(t.Context(), args)) + assert.True(t, called) + } + test(t, []string{"./gitea"}, "", true) + test(t, []string{"./gitea", "test"}, "", true) + test(t, []string{"./gitea", "other"}, "other", false) + test(t, []string{"./gitea", "test", "extra"}, "extra", false) +} diff --git a/cmd/manager.go b/cmd/manager.go new file mode 100644 index 0000000..85fccc9 --- /dev/null +++ b/cmd/manager.go @@ -0,0 +1,154 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "os" + "time" + + "gitea.dev/modules/private" + + "github.com/urfave/cli/v3" +) + +func newManagerCommand() *cli.Command { + return &cli.Command{ + Name: "manager", + Usage: "Manage the running gitea process", + Description: "This is a command for managing the running gitea process", + Commands: []*cli.Command{ + newShutdownCommand(), + newRestartCommand(), + newReloadTemplatesCommand(), + newFlushQueuesCommand(), + newLoggingCommand(), + newProcessesCommand(), + }, + } +} + +func newShutdownCommand() *cli.Command { + return &cli.Command{ + Name: "shutdown", + Usage: "Gracefully shutdown the running process", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + Action: runShutdown, + } +} + +func newRestartCommand() *cli.Command { + return &cli.Command{ + Name: "restart", + Usage: "Gracefully restart the running process - (not implemented for windows servers)", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + Action: runRestart, + } +} + +func newReloadTemplatesCommand() *cli.Command { + return &cli.Command{ + Name: "reload-templates", + Usage: "Reload template files in the running process", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + Action: runReloadTemplates, + } +} + +func newFlushQueuesCommand() *cli.Command { + return &cli.Command{ + Name: "flush-queues", + Usage: "Flush queues in the running process", + Action: runFlushQueues, + Flags: []cli.Flag{ + &cli.DurationFlag{ + Name: "timeout", + Value: 60 * time.Second, + Usage: "Timeout for the flushing process", + }, + &cli.BoolFlag{ + Name: "non-blocking", + Usage: "Set to true to not wait for flush to complete before returning", + }, + &cli.BoolFlag{ + Name: "debug", + }, + }, + } +} + +func newProcessesCommand() *cli.Command { + return &cli.Command{ + Name: "processes", + Usage: "Display running processes within the current process", + Action: runProcesses, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + &cli.BoolFlag{ + Name: "flat", + Usage: "Show processes as flat table rather than as tree", + }, + &cli.BoolFlag{ + Name: "no-system", + Usage: "Do not show system processes", + }, + &cli.BoolFlag{ + Name: "stacktraces", + Usage: "Show stacktraces", + }, + &cli.BoolFlag{ + Name: "json", + Usage: "Output as json", + }, + &cli.StringFlag{ + Name: "cancel", + Usage: "Process PID to cancel. (Only available for non-system processes.)", + }, + }, + } +} + +func runShutdown(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + extra := private.Shutdown(ctx) + return handleCliResponseExtra(extra) +} + +func runRestart(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + extra := private.Restart(ctx) + return handleCliResponseExtra(extra) +} + +func runReloadTemplates(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + extra := private.ReloadTemplates(ctx) + return handleCliResponseExtra(extra) +} + +func runFlushQueues(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking")) + return handleCliResponseExtra(extra) +} + +func runProcesses(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + extra := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel")) + return handleCliResponseExtra(extra) +} diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go new file mode 100644 index 0000000..a54041f --- /dev/null +++ b/cmd/manager_logging.go @@ -0,0 +1,92 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "fmt" + "os" + + "gitea.dev/modules/private" + + "github.com/urfave/cli/v3" +) + +func newLoggingCommand() *cli.Command { + return &cli.Command{ + Name: "logging", + Usage: "Adjust logging commands", + Commands: []*cli.Command{ + { + Name: "pause", + Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + Action: runPauseLogging, + }, { + Name: "resume", + Usage: "Resume logging", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + Action: runResumeLogging, + }, { + Name: "release-and-reopen", + Usage: "Cause Gitea to release and re-open files used for logging", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + }, + Action: runReleaseReopenLogging, + }, { + Name: "log-sql", + Usage: "Set LogSQL", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + }, + &cli.BoolFlag{ + Name: "off", + Usage: "Switch off SQL logging", + }, + }, + Action: runSetLogSQL, + }, + }, + } +} + +func runPauseLogging(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + userMsg := private.PauseLogging(ctx) + _, _ = fmt.Fprintln(os.Stdout, userMsg) + return nil +} + +func runResumeLogging(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + userMsg := private.ResumeLogging(ctx) + _, _ = fmt.Fprintln(os.Stdout, userMsg) + return nil +} + +func runReleaseReopenLogging(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + userMsg := private.ReleaseReopenLogging(ctx) + _, _ = fmt.Fprintln(os.Stdout, userMsg) + return nil +} + +func runSetLogSQL(ctx context.Context, c *cli.Command) error { + setup(ctx, c.Bool("debug")) + + extra := private.SetLogSQL(ctx, !c.Bool("off")) + return handleCliResponseExtra(extra) +} diff --git a/cmd/migrate.go b/cmd/migrate.go new file mode 100644 index 0000000..cc4fee3 --- /dev/null +++ b/cmd/migrate.go @@ -0,0 +1,43 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + + "gitea.dev/models/db" + "gitea.dev/modules/log" + "gitea.dev/modules/setting" + "gitea.dev/services/versioned_migration" + + "github.com/urfave/cli/v3" +) + +func newMigrateCommand() *cli.Command { + return &cli.Command{ + Name: "migrate", + Usage: "Migrate the database", + Description: `This is a command for migrating the database, so that you can run "gitea admin create user" before starting the server.`, + Action: runMigrate, + } +} + +func runMigrate(ctx context.Context, c *cli.Command) error { + if err := initDB(ctx); err != nil { + return err + } + + log.Info("AppPath: %s", setting.AppPath) + log.Info("AppWorkPath: %s", setting.AppWorkPath) + log.Info("Custom path: %s", setting.CustomPath) + log.Info("Log path: %s", setting.Log.RootPath) + log.Info("Configuration file: %s", setting.CustomConf) + + if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil { + log.Fatal("Failed to initialize ORM engine: %v", err) + return err + } + + return nil +} diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go new file mode 100644 index 0000000..36b224f --- /dev/null +++ b/cmd/migrate_storage.go @@ -0,0 +1,310 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "errors" + "fmt" + "io/fs" + "strings" + + actions_model "gitea.dev/models/actions" + "gitea.dev/models/db" + git_model "gitea.dev/models/git" + packages_model "gitea.dev/models/packages" + repo_model "gitea.dev/models/repo" + user_model "gitea.dev/models/user" + "gitea.dev/modules/log" + packages_module "gitea.dev/modules/packages" + "gitea.dev/modules/setting" + "gitea.dev/modules/storage" + "gitea.dev/services/versioned_migration" + + "github.com/urfave/cli/v3" +) + +func newMigrateStorageCommand() *cli.Command { + return &cli.Command{ + Name: "migrate-storage", + Usage: "Migrate the storage", + Description: "Copies stored files from storage configured in app.ini to parameter-configured storage", + Action: runMigrateStorage, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "type", + Aliases: []string{"t"}, + Value: "", + Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts'", + }, + &cli.StringFlag{ + Name: "storage", + Aliases: []string{"s"}, + Value: "", + Usage: "New storage type: local (default), minio or azureblob", + }, + &cli.StringFlag{ + Name: "path", + Aliases: []string{"p"}, + Value: "", + Usage: "New storage placement if store is local (leave blank for default)", + }, + // Minio Storage special configurations + &cli.StringFlag{ + Name: "minio-endpoint", + Value: "", + Usage: "Minio storage endpoint", + }, + &cli.StringFlag{ + Name: "minio-access-key-id", + Value: "", + Usage: "Minio storage accessKeyID", + }, + &cli.StringFlag{ + Name: "minio-secret-access-key", + Value: "", + Usage: "Minio storage secretAccessKey", + }, + &cli.StringFlag{ + Name: "minio-bucket", + Value: "", + Usage: "Minio storage bucket", + }, + &cli.StringFlag{ + Name: "minio-location", + Value: "", + Usage: "Minio storage location to create bucket", + }, + &cli.StringFlag{ + Name: "minio-base-path", + Value: "", + Usage: "Minio storage base path on the bucket", + }, + &cli.BoolFlag{ + Name: "minio-use-ssl", + Usage: "Enable SSL for minio", + }, + &cli.BoolFlag{ + Name: "minio-insecure-skip-verify", + Usage: "Skip SSL verification", + }, + &cli.StringFlag{ + Name: "minio-checksum-algorithm", + Value: "", + Usage: "Minio checksum algorithm (default/md5)", + }, + &cli.StringFlag{ + Name: "minio-bucket-lookup-type", + Value: "", + Usage: "Minio bucket lookup type", + }, + // Azure Blob Storage special configurations + &cli.StringFlag{ + Name: "azureblob-endpoint", + Value: "", + Usage: "Azure Blob storage endpoint", + }, + &cli.StringFlag{ + Name: "azureblob-account-name", + Value: "", + Usage: "Azure Blob storage account name", + }, + &cli.StringFlag{ + Name: "azureblob-account-key", + Value: "", + Usage: "Azure Blob storage account key", + }, + &cli.StringFlag{ + Name: "azureblob-container", + Value: "", + Usage: "Azure Blob storage container", + }, + &cli.StringFlag{ + Name: "azureblob-base-path", + Value: "", + Usage: "Azure Blob storage base path", + }, + }, + } +} + +func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error { + return db.Iterate(ctx, nil, func(ctx context.Context, attach *repo_model.Attachment) error { + _, err := storage.Copy(dstStorage, attach.RelativePath(), storage.Attachments, attach.RelativePath()) + return err + }) +} + +func migrateLFS(ctx context.Context, dstStorage storage.ObjectStorage) error { + return db.Iterate(ctx, nil, func(ctx context.Context, mo *git_model.LFSMetaObject) error { + _, err := storage.Copy(dstStorage, mo.RelativePath(), storage.LFS, mo.RelativePath()) + return err + }) +} + +func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error { + return db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error { + if user.CustomAvatarRelativePath() == "" { + return nil + } + _, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath()) + return err + }) +} + +func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error { + return db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error { + if repo.CustomAvatarRelativePath() == "" { + return nil + } + _, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath()) + return err + }) +} + +func migrateRepoArchivers(ctx context.Context, dstStorage storage.ObjectStorage) error { + return db.Iterate(ctx, nil, func(ctx context.Context, archiver *repo_model.RepoArchiver) error { + p := archiver.RelativePath() + _, err := storage.Copy(dstStorage, p, storage.RepoArchives, p) + return err + }) +} + +func migratePackages(ctx context.Context, dstStorage storage.ObjectStorage) error { + return db.Iterate(ctx, nil, func(ctx context.Context, pb *packages_model.PackageBlob) error { + p := packages_module.KeyToRelativePath(packages_module.BlobHash256Key(pb.HashSHA256)) + _, err := storage.Copy(dstStorage, p, storage.Packages, p) + return err + }) +} + +func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) error { + return db.Iterate(ctx, nil, func(ctx context.Context, task *actions_model.ActionTask) error { + if task.LogExpired { + // the log has been cleared + return nil + } + if !task.LogInStorage { + // running tasks store logs in DBFS + return nil + } + p := task.LogFilename + _, err := storage.Copy(dstStorage, p, storage.Actions, p) + return err + }) +} + +func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error { + return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error { + if artifact.Status == actions_model.ArtifactStatusExpired { + return nil + } + + _, err := storage.Copy(dstStorage, artifact.StoragePath, storage.ActionsArtifacts, artifact.StoragePath) + if err != nil { + // ignore files that do not exist + if errors.Is(err, fs.ErrNotExist) { + return nil + } + return err + } + + return nil + }) +} + +func runMigrateStorage(ctx context.Context, cmd *cli.Command) error { + if err := initDB(ctx); err != nil { + return err + } + + log.Info("AppPath: %s", setting.AppPath) + log.Info("AppWorkPath: %s", setting.AppWorkPath) + log.Info("Custom path: %s", setting.CustomPath) + log.Info("Log path: %s", setting.Log.RootPath) + log.Info("Configuration file: %s", setting.CustomConf) + + if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil { + log.Fatal("Failed to initialize ORM engine: %v", err) + return err + } + + if err := storage.Init(); err != nil { + return err + } + + var dstStorage storage.ObjectStorage + var err error + switch strings.ToLower(cmd.String("storage")) { + case "": + fallthrough + case string(setting.LocalStorageType): + p := cmd.String("path") + if p == "" { + log.Fatal("Path must be given when storage is local") + return nil + } + dstStorage, err = storage.NewLocalStorage( + ctx, + &setting.Storage{ + Path: p, + }) + case string(setting.MinioStorageType): + dstStorage, err = storage.NewMinioStorage( + ctx, + &setting.Storage{ + MinioConfig: setting.MinioStorageConfig{ + Endpoint: cmd.String("minio-endpoint"), + AccessKeyID: cmd.String("minio-access-key-id"), + SecretAccessKey: cmd.String("minio-secret-access-key"), + Bucket: cmd.String("minio-bucket"), + Location: cmd.String("minio-location"), + BasePath: cmd.String("minio-base-path"), + UseSSL: cmd.Bool("minio-use-ssl"), + InsecureSkipVerify: cmd.Bool("minio-insecure-skip-verify"), + ChecksumAlgorithm: cmd.String("minio-checksum-algorithm"), + BucketLookUpType: cmd.String("minio-bucket-lookup-type"), + }, + }) + case string(setting.AzureBlobStorageType): + dstStorage, err = storage.NewAzureBlobStorage( + ctx, + &setting.Storage{ + AzureBlobConfig: setting.AzureBlobStorageConfig{ + Endpoint: cmd.String("azureblob-endpoint"), + AccountName: cmd.String("azureblob-account-name"), + AccountKey: cmd.String("azureblob-account-key"), + Container: cmd.String("azureblob-container"), + BasePath: cmd.String("azureblob-base-path"), + }, + }) + default: + return fmt.Errorf("unsupported storage type: %s", cmd.String("storage")) + } + if err != nil { + return err + } + + migratedMethods := map[string]func(context.Context, storage.ObjectStorage) error{ + "attachments": migrateAttachments, + "lfs": migrateLFS, + "avatars": migrateAvatars, + "repo-avatars": migrateRepoAvatars, + "repo-archivers": migrateRepoArchivers, + "packages": migratePackages, + "actions-log": migrateActionsLog, + "actions-artifacts": migrateActionsArtifacts, + } + + tp := strings.ToLower(cmd.String("type")) + if m, ok := migratedMethods[tp]; ok { + if err := m(ctx, dstStorage); err != nil { + return err + } + log.Info("%s files have successfully been copied to the new storage.", tp) + return nil + } + + return fmt.Errorf("unsupported storage: %s", cmd.String("type")) +} diff --git a/cmd/migrate_storage_test.go b/cmd/migrate_storage_test.go new file mode 100644 index 0000000..67a6b9e --- /dev/null +++ b/cmd/migrate_storage_test.go @@ -0,0 +1,73 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "os" + "strings" + "testing" + + "gitea.dev/models/packages" + "gitea.dev/models/unittest" + user_model "gitea.dev/models/user" + packages_module "gitea.dev/modules/packages" + "gitea.dev/modules/setting" + "gitea.dev/modules/storage" + packages_service "gitea.dev/services/packages" + + "github.com/stretchr/testify/assert" +) + +func TestMigratePackages(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + creator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + + content := "package main\n\nfunc main() {\nfmt.Println(\"hi\")\n}\n" + buf, err := packages_module.CreateHashedBufferFromReaderWithSize(strings.NewReader(content), 1024) + assert.NoError(t, err) + defer buf.Close() + + v, f, err := packages_service.CreatePackageAndAddFile(t.Context(), &packages_service.PackageCreationInfo{ + PackageInfo: packages_service.PackageInfo{ + Owner: creator, + PackageType: packages.TypeGeneric, + Name: "test", + Version: "1.0.0", + }, + Creator: creator, + SemverCompatible: true, + VersionProperties: map[string]string{}, + }, &packages_service.PackageFileCreationInfo{ + PackageFileInfo: packages_service.PackageFileInfo{ + Filename: "a.go", + }, + Creator: creator, + Data: buf, + IsLead: true, + }) + assert.NoError(t, err) + assert.NotNil(t, v) + assert.NotNil(t, f) + + ctx := t.Context() + + p := t.TempDir() + + dstStorage, err := storage.NewLocalStorage( + ctx, + &setting.Storage{ + Path: p, + }) + assert.NoError(t, err) + + err = migratePackages(ctx, dstStorage) + assert.NoError(t, err) + + entries, err := os.ReadDir(p) + assert.NoError(t, err) + assert.Len(t, entries, 2) + assert.Equal(t, "01", entries[0].Name()) + assert.Equal(t, "tmp", entries[1].Name()) +} diff --git a/cmd/restore_repo.go b/cmd/restore_repo.go new file mode 100644 index 0000000..c7bbddd --- /dev/null +++ b/cmd/restore_repo.go @@ -0,0 +1,68 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "strings" + + "gitea.dev/modules/private" + "gitea.dev/modules/setting" + + "github.com/urfave/cli/v3" +) + +func newRestoreRepositoryCommand() *cli.Command { + return &cli.Command{ + Name: "restore-repo", + Usage: "Restore the repository from disk", + Description: "This is a command for restoring the repository data.", + Action: runRestoreRepository, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo_dir", + Aliases: []string{"r"}, + Value: "./data", + Usage: "Repository dir path to restore from", + }, + &cli.StringFlag{ + Name: "owner_name", + Value: "", + Usage: "Restore destination owner name", + }, + &cli.StringFlag{ + Name: "repo_name", + Value: "", + Usage: "Restore destination repository name", + }, + &cli.StringFlag{ + Name: "units", + Value: "", + Usage: `Which items will be restored, one or more units should be separated as comma. +wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`, + }, + &cli.BoolFlag{ + Name: "validation", + Usage: "Sanity check the content of the files before trying to load them", + }, + }, + } +} + +func runRestoreRepository(ctx context.Context, c *cli.Command) error { + setting.MustInstalled() + var units []string + if s := c.String("units"); s != "" { + units = strings.Split(s, ",") + } + extra := private.RestoreRepo( + ctx, + c.String("repo_dir"), + c.String("owner_name"), + c.String("repo_name"), + units, + c.Bool("validation"), + ) + return handleCliResponseExtra(extra) +} diff --git a/cmd/serv.go b/cmd/serv.go new file mode 100644 index 0000000..583d534 --- /dev/null +++ b/cmd/serv.go @@ -0,0 +1,352 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2016 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "fmt" + "net/url" + "os" + "path/filepath" + "strconv" + "strings" + "unicode" + + asymkey_model "gitea.dev/models/asymkey" + git_model "gitea.dev/models/git" + "gitea.dev/models/perm" + repo_model "gitea.dev/models/repo" + "gitea.dev/modules/git" + "gitea.dev/modules/git/gitcmd" + "gitea.dev/modules/json" + "gitea.dev/modules/lfstransfer" + "gitea.dev/modules/log" + "gitea.dev/modules/pprof" + "gitea.dev/modules/private" + "gitea.dev/modules/process" + repo_module "gitea.dev/modules/repository" + "gitea.dev/modules/setting" + "gitea.dev/services/agit" + "gitea.dev/services/lfs" + + "github.com/kballard/go-shellquote" + "github.com/urfave/cli/v3" +) + +func newServCommand() *cli.Command { + return &cli.Command{ + Name: "serv", + Usage: "(internal) Should only be called by SSH shell", + Description: "Serv provides access auth for repositories", + Hidden: true, // Internal commands shouldn't be visible in help + Before: PrepareConsoleLoggerLevel(log.FATAL), + Action: runServ, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "enable-pprof", + }, + &cli.BoolFlag{ + Name: "debug", + }, + }, + } +} + +func setup(ctx context.Context, debug bool) { + if debug { + setupConsoleLogger(log.TRACE, false, os.Stderr) + } else { + setupConsoleLogger(log.FATAL, false, os.Stderr) + } + setting.MustInstalled() + if _, err := os.Stat(setting.RepoRootPath); err != nil { + _ = fail(ctx, "Unable to access repository path", "Unable to access repository path %q, err: %v", setting.RepoRootPath, err) + return + } + if err := git.InitSimple(); err != nil { + _ = fail(ctx, "Failed to init git", "Failed to init git, err: %v", err) + } +} + +// fail prints message to stdout, it's mainly used for git serv and git hook commands. +// The output will be passed to git client and shown to user. +func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error { + if userMessage == "" { + userMessage = "Internal Server Error (no specific error)" + } + + // There appears to be a chance to cause a zombie process and failure to read the Exit status + // if nothing is outputted on stdout. + _, _ = fmt.Fprintln(os.Stdout, "") + // add extra empty lines to separate our message from other git errors to get more attention + _, _ = fmt.Fprintln(os.Stderr, "error:") + _, _ = fmt.Fprintln(os.Stderr, "error:", userMessage) + _, _ = fmt.Fprintln(os.Stderr, "error:") + + if logMsgFmt != "" { + logMsg := fmt.Sprintf(logMsgFmt, args...) + if !setting.IsProd { + _, _ = fmt.Fprintln(os.Stderr, "Gitea:", logMsg) + } + if unicode.IsPunct(rune(userMessage[len(userMessage)-1])) { + logMsg = userMessage + " " + logMsg + } else { + logMsg = userMessage + ". " + logMsg + } + _ = private.SSHLog(ctx, true, logMsg) + } + return cli.Exit("", 1) +} + +// handleCliResponseExtra handles the extra response from the cli sub-commands +// If there is a user message it will be printed to stdout +// If the command failed it will return an error (the error will be printed by cli framework) +func handleCliResponseExtra(extra private.ResponseExtra) error { + if extra.UserMsg != "" { + _, _ = fmt.Fprintln(os.Stdout, extra.UserMsg) + } + if extra.HasError() { + return cli.Exit(extra.Error, 1) + } + return nil +} + +// getAccessMode maps an SSH git/LFS verb to the access mode it requires, with +// ok=false for an unrecognised verb. Callers MUST reject the request when ok is +// false: AccessModeNone would otherwise pass the `userMode < mode` permission +// check in routers/private/serv.go and grant access. +func getAccessMode(verb, lfsVerb string) (mode perm.AccessMode, ok bool) { + switch verb { + case git.CmdVerbUploadPack, git.CmdVerbUploadArchive: + return perm.AccessModeRead, true + case git.CmdVerbReceivePack: + return perm.AccessModeWrite, true + case git.CmdVerbLfsAuthenticate, git.CmdVerbLfsTransfer: + switch lfsVerb { + case git.CmdSubVerbLfsUpload: + return perm.AccessModeWrite, true + case git.CmdSubVerbLfsDownload: + return perm.AccessModeRead, true + } + } + return perm.AccessModeNone, false +} + +func runServ(ctx context.Context, c *cli.Command) error { + // FIXME: This needs to internationalised + setup(ctx, c.Bool("debug")) + + if setting.SSH.Disabled { + cprintln(c, "Gitea: SSH has been disabled") + return nil + } + + if c.NArg() < 1 { + if err := cli.ShowSubcommandHelp(c); err != nil { + cprintf(c, "error showing subcommand help: %v\n", err) + } + return nil + } + + defer func() { + if err := recover(); err != nil { + _ = fail(ctx, "Internal Server Error", "Panic: %v\n%s", err, log.Stack(2)) + } + }() + + keys := strings.Split(c.Args().First(), "-") + if len(keys) != 2 || keys[0] != "key" { + return fail(ctx, "Key ID format error", "Invalid key argument: %s", c.Args().First()) + } + keyID, err := strconv.ParseInt(keys[1], 10, 64) + if err != nil { + return fail(ctx, "Key ID parsing error", "Invalid key argument: %s", c.Args().Get(1)) + } + + cmd := os.Getenv("SSH_ORIGINAL_COMMAND") + if len(cmd) == 0 { + key, user, err := private.ServNoCommand(ctx, keyID) + if err != nil { + return fail(ctx, "Key check failed", "Failed to check provided key: %v", err) + } + var authSuccessMsg string + switch key.Type { + case asymkey_model.KeyTypeDeploy: + authSuccessMsg = "Hi there! You've successfully authenticated with an SSH deploy key." + case asymkey_model.KeyTypePrincipal: + authSuccessMsg = "Hi there! You've successfully authenticated with the SSH principal " + key.Content + "." + default: + authSuccessMsg = "Hi there, " + user.Name + "! You've successfully authenticated with the SSH key named " + key.Name + "." + } + _, _ = fmt.Fprintf(c.ErrWriter, "%s\n%s", + authSuccessMsg, + "Gitea does not provide shell access. If this is unexpected, please setup Gitea under another SSH user or use container to deploy.", + ) + return nil + } else if c.Bool("debug") { + log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND")) + } + + sshCmdArgs, err := shellquote.Split(cmd) + if err != nil { + return fail(ctx, "Error parsing arguments", "Failed to parse arguments: %v", err) + } + + if len(sshCmdArgs) < 2 { + if git.DefaultFeatures().SupportProcReceive { + // for AGit Flow + if cmd == "ssh_info" { + cprintf(c, "%s", agit.SshInfoJson) + return nil + } + } + return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd) + } + + var reqOwnerName, reqRepoName string + { + var ok bool + reqRepoPath := strings.TrimPrefix(sshCmdArgs[1], "/") + reqOwnerName, reqRepoName, ok = strings.Cut(reqRepoPath, "/") + if !ok { + return fail(ctx, "Invalid repository path", "Invalid repository path: %v", reqRepoPath) + } + reqRepoName = strings.TrimSuffix(reqRepoName, ".git") // "the-repo-name" or "the-repo-name.wiki" + } + + if !repo_model.IsValidSSHAccessRepoName(reqRepoName) { + return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reqRepoName) + } + + if c.Bool("enable-pprof") { + stopProfiler, err := pprof.DumpPprofForUsername(setting.PprofDataPath, reqOwnerName) + if err != nil { + return fail(ctx, "Unable to start pprof profiler", "Unable to start pprof profile: %v", err) + } + defer stopProfiler() + } + + verb, lfsVerb := sshCmdArgs[0], "" + if !git.IsAllowedVerbForServe(verb) { + return fail(ctx, "Unknown git command", "Unknown git command %s", verb) + } + + if git.IsAllowedVerbForServeLfs(verb) { + if !setting.LFS.StartServer { + return fail(ctx, "LFS Server is not enabled", "") + } + if verb == git.CmdVerbLfsTransfer && !setting.LFS.AllowPureSSH { + return fail(ctx, "LFS SSH transfer is not enabled", "") + } + if len(sshCmdArgs) > 2 { + lfsVerb = sshCmdArgs[2] + } + } + + requestedMode, ok := getAccessMode(verb, lfsVerb) + if !ok { + return fail(ctx, "Unknown git command", "Unknown git command %s %s", verb, lfsVerb) + } + + results, extra := private.ServCommand(ctx, keyID, reqOwnerName, reqRepoName, requestedMode, verb, lfsVerb) + if extra.HasError() { + return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error) + } + + // LFS SSH protocol + if verb == git.CmdVerbLfsTransfer { + if results.IsWiki { + return fail(ctx, "LFS Transfer is not supported for wikis", "") + } + token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, UserExtDoerData: results.UserExtDoerData, RepoID: results.RepoID}) + if err != nil { + return err + } + return lfstransfer.Main(ctx, results.OwnerName, results.RepoName, lfsVerb, token) + } + + // LFS token authentication + if verb == git.CmdVerbLfsAuthenticate { + if results.IsWiki { + return fail(ctx, "LFS Authenticate is not supported for wikis", "") + } + lfsTokenHref := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName)) + + token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, UserExtDoerData: results.UserExtDoerData, RepoID: results.RepoID}) + if err != nil { + return err + } + + tokenAuthentication := &git_model.LFSTokenResponse{ + Header: make(map[string]string), + Href: lfsTokenHref, + } + tokenAuthentication.Header["Authorization"] = token + + enc := json.NewEncoder(os.Stdout) + err = enc.Encode(tokenAuthentication) + if err != nil { + return fail(ctx, "Failed to encode LFS json response", "Failed to encode LFS json response: %v", err) + } + return nil + } + + var command *process.Cmd + gitBinPath := filepath.Dir(gitcmd.GitExecutable) // e.g. /usr/bin + gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack + if _, err := os.Stat(gitBinVerb); err != nil { + // if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git + // ps: Windows only has "git.exe" in the bin path, so Windows always uses this way + verbFields := strings.SplitN(verb, "-", 2) + if len(verbFields) == 2 { + // use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ... + command = process.CommandContext(ctx, gitcmd.GitExecutable, verbFields[1], results.RepoStoragePath) + } + } + if command == nil { + // by default, use the verb (it has been checked above by allowedCommands) + command = process.CommandContext(ctx, gitBinVerb, results.RepoStoragePath) + } + + command.Dir = setting.RepoRootPath + command.Stdout = os.Stdout + command.Stdin = os.Stdin + command.Stderr = os.Stderr + command.Env = append(command.Env, os.Environ()...) + command.Env = append(command.Env, + repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki), + + repo_module.EnvRepoUsername+"="+results.OwnerName, + repo_module.EnvRepoName+"="+results.RepoName, + repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10), + + repo_module.EnvKeyID+"="+strconv.FormatInt(results.PublicKeyID, 10), + + repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10), + repo_module.EnvPusherName+"="+results.UserName, + repo_module.EnvPusherEmail+"="+results.UserEmail, + repo_module.EnvPusherExtDoerData+"="+results.UserExtDoerData, + + repo_module.EnvPRID+"="+strconv.Itoa(0), + + repo_module.EnvAppURL+"="+setting.AppURL, + ) + // to avoid breaking, here only use the minimal environment variables for the "gitea serv" command. + // it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later. + command.Env = append(command.Env, gitcmd.CommonCmdServEnvs()...) + + if err = command.Run(); err != nil { + return fail(ctx, "Failed to execute git command", "Failed to execute git command: %v", err) + } + + // Update user key activity. + if results.PublicKeyID > 0 { + if err = private.UpdatePublicKeyInRepo(ctx, results.PublicKeyID, results.RepoID); err != nil { + return fail(ctx, "Failed to update public key", "UpdatePublicKeyInRepo: %v", err) + } + } + + return nil +} diff --git a/cmd/serv_test.go b/cmd/serv_test.go new file mode 100644 index 0000000..3bdcba1 --- /dev/null +++ b/cmd/serv_test.go @@ -0,0 +1,56 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "testing" + + "gitea.dev/models/perm" + "gitea.dev/modules/git" + + "github.com/stretchr/testify/assert" +) + +func TestGetAccessMode(t *testing.T) { + cases := []struct { + verb, lfsVerb string + expected perm.AccessMode + }{ + {git.CmdVerbUploadPack, "", perm.AccessModeRead}, + {git.CmdVerbUploadArchive, "", perm.AccessModeRead}, + {git.CmdVerbReceivePack, "", perm.AccessModeWrite}, + {git.CmdVerbLfsAuthenticate, git.CmdSubVerbLfsUpload, perm.AccessModeWrite}, + {git.CmdVerbLfsAuthenticate, git.CmdSubVerbLfsDownload, perm.AccessModeRead}, + {git.CmdVerbLfsTransfer, git.CmdSubVerbLfsUpload, perm.AccessModeWrite}, + {git.CmdVerbLfsTransfer, git.CmdSubVerbLfsDownload, perm.AccessModeRead}, + } + for _, tc := range cases { + t.Run(tc.verb+"/"+tc.lfsVerb, func(t *testing.T) { + mode, ok := getAccessMode(tc.verb, tc.lfsVerb) + assert.True(t, ok) + assert.Equal(t, tc.expected, mode) + }) + } +} + +// TestGetAccessModeUnknownVerb locks in the invariant that getAccessMode reports +// ok=false for unrecognised verbs and LFS sub-verbs, so runServ rejects them. An +// unknown verb has no valid access mode; if it were treated as AccessModeNone (0) +// it would pass the `userMode < mode` permission check in routers/private/serv.go +// and hand out valid LFS JWTs for any private repository. +func TestGetAccessModeUnknownVerb(t *testing.T) { + cases := []struct{ verb, lfsVerb string }{ + {git.CmdVerbLfsAuthenticate, ""}, + {git.CmdVerbLfsAuthenticate, "badverb"}, + {git.CmdVerbLfsTransfer, "badverb"}, + {"git-unknown-verb", ""}, + } + for _, tc := range cases { + t.Run(tc.verb+"/"+tc.lfsVerb, func(t *testing.T) { + mode, ok := getAccessMode(tc.verb, tc.lfsVerb) + assert.False(t, ok) + assert.Equal(t, perm.AccessModeNone, mode) + }) + } +} diff --git a/cmd/web.go b/cmd/web.go new file mode 100644 index 0000000..11c5d83 --- /dev/null +++ b/cmd/web.go @@ -0,0 +1,392 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "fmt" + "net" + "net/http" + "net/http/pprof" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + audit_model "gitea.dev/models/audit" + "gitea.dev/modules/container" + "gitea.dev/modules/graceful" + "gitea.dev/modules/gtprof" + "gitea.dev/modules/log" + "gitea.dev/modules/process" + "gitea.dev/modules/public" + "gitea.dev/modules/setting" + "gitea.dev/modules/templates" + "gitea.dev/modules/util" + "gitea.dev/routers" + "gitea.dev/routers/install" + "gitea.dev/services/audit" + + "github.com/felixge/fgprof" + "github.com/urfave/cli/v3" +) + +// PIDFile could be set from build tag +var PIDFile = "/run/gitea.pid" + +func newWebCommand() *cli.Command { + return &cli.Command{ + Name: "web", + Usage: "Start Gitea web server", + Description: `Gitea web server is the only thing you need to run, +and it takes care of all the other things for you`, + Before: PrepareConsoleLoggerLevel(log.INFO), + Action: runWeb, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "port", + Aliases: []string{"p"}, + Value: "3000", + Usage: "Temporary port number to prevent conflict", + }, + &cli.StringFlag{ + Name: "install-port", + Value: "3000", + Usage: "Temporary port number to run the install page on to prevent conflict", + }, + &cli.StringFlag{ + Name: "pid", + Aliases: []string{"P"}, + Value: PIDFile, + Usage: "Custom pid file path", + }, + &cli.BoolFlag{ + Name: "quiet", + Aliases: []string{"q"}, + Usage: "Only display Fatal logging errors until logging is set-up", + }, + &cli.BoolFlag{ + Name: "verbose", + Usage: "Set initial logging to TRACE level until logging is properly set-up", + }, + }, + } +} + +func runHTTPRedirector() { + _, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: HTTP Redirector", process.SystemProcessType, true) + defer finished() + + source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect) + dest := strings.TrimSuffix(setting.AppURL, "/") + log.Info("Redirecting: %s to %s", source, dest) + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + target := dest + r.URL.Path + if len(r.URL.RawQuery) > 0 { + target += "?" + r.URL.RawQuery + } + http.Redirect(w, r, target, http.StatusTemporaryRedirect) + }) + + err := runHTTP("tcp", source, "HTTP Redirector", handler, setting.RedirectorUseProxyProtocol) + if err != nil { + log.Fatal("Failed to start port redirection: %v", err) + } +} + +func createPIDFile(pidPath string) { + currentPid := os.Getpid() + if err := os.MkdirAll(filepath.Dir(pidPath), os.ModePerm); err != nil { + log.Fatal("Failed to create PID folder: %v", err) + } + + file, err := os.Create(pidPath) + if err != nil { + log.Fatal("Failed to create PID file: %v", err) + } + defer file.Close() + if _, err := file.WriteString(strconv.FormatInt(int64(currentPid), 10)); err != nil { + log.Fatal("Failed to write PID information: %v", err) + } +} + +func showWebStartupMessage(msg string) { + log.Info("Gitea version: %s%s", setting.AppVer, setting.AppBuiltWith) + log.Info("* RunMode: %s", setting.RunMode) + log.Info("* AppPath: %s", setting.AppPath) + log.Info("* WorkPath: %s", setting.AppWorkPath) + log.Info("* CustomPath: %s", setting.CustomPath) + log.Info("* ConfigFile: %s", setting.CustomConf) + log.Info("%s", msg) // show startup message + + if setting.CORSConfig.Enabled { + log.Info("CORS Service Enabled") + } + if setting.DefaultUILocation != time.Local { + log.Info("Default UI Location is %v", setting.DefaultUILocation.String()) + } + if setting.MailService != nil { + log.Info("Mail Service Enabled: RegisterEmailConfirm=%v, Service.EnableNotifyMail=%v", setting.Service.RegisterEmailConfirm, setting.Service.EnableNotifyMail) + } +} + +func serveInstall(cmd *cli.Command) error { + showWebStartupMessage("Prepare to run install page") + + routers.InitWebInstallPage(graceful.GetManager().HammerContext()) + + // Flag for port number in case first time run conflict + if cmd.IsSet("port") { + if err := setPort(cmd.String("port")); err != nil { + return err + } + } + if cmd.IsSet("install-port") { + if err := setPort(cmd.String("install-port")); err != nil { + return err + } + } + c := install.Routes() + err := listen(c, false) + if err != nil { + log.Error("Unable to open listener for installer. Is Gitea already running?") + graceful.GetManager().DoGracefulShutdown() + } + select { + case <-graceful.GetManager().IsShutdown(): + <-graceful.GetManager().Done() + log.Info("PID: %d Gitea Web Finished", os.Getpid()) + return err + default: + } + return nil +} + +func serveInstalled(c *cli.Command) error { + setting.MustInstalled() + + showWebStartupMessage("Prepare to run web server") + + if setting.AppWorkPathMismatch { + log.Error("WORK_PATH from config %q doesn't match other paths from environment variables or command arguments. "+ + "Only WORK_PATH in config should be set and used. Please make sure the path in config file is correct, "+ + "remove the other outdated work paths from environment variables and command arguments", setting.CustomConf) + } + + rootCfg := setting.CfgProvider + if rootCfg.Section("").Key("WORK_PATH").String() == "" { + saveCfg, err := rootCfg.PrepareSaving() + if err != nil { + log.Error("Unable to prepare saving WORK_PATH=%s to config %q: %v\nYou should set it manually, otherwise there might be bugs when accessing the git repositories.", setting.AppWorkPath, setting.CustomConf, err) + } else { + rootCfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath) + saveCfg.Section("").Key("WORK_PATH").SetValue(setting.AppWorkPath) + if err = saveCfg.Save(); err != nil { + log.Error("Unable to update WORK_PATH=%s to config %q: %v\nYou should set it manually, otherwise there might be bugs when accessing the git repositories.", setting.AppWorkPath, setting.CustomConf, err) + } + } + } + + // in old versions, user's custom web files are placed in "custom/public", and they were served as "http://domain.com/assets/xxx" + // now, Gitea only serves pre-defined files in the "custom/public" folder basing on the web root, the user should move their custom files to "custom/public/assets" + publicFiles, _ := public.AssetFS().ListFiles(".") + publicFilesSet := container.SetOf(publicFiles...) + publicFilesSet.Remove(".well-known") + publicFilesSet.Remove("assets") + publicFilesSet.Remove("robots.txt") + for _, fn := range publicFilesSet.Values() { + log.Error("Found legacy public asset %q in CustomPath. Please move it to %s/public/assets/%s", fn, setting.CustomPath, fn) + } + if _, err := os.Stat(filepath.Join(setting.CustomPath, "robots.txt")); err == nil { + log.Error(`Found legacy public asset "robots.txt" in CustomPath. Please move it to %s/public/robots.txt`, setting.CustomPath) + } + + routers.InitWebInstalled(graceful.GetManager().HammerContext()) + + // We check that AppDataPath exists here (it should have been created during installation) + // We can't check it in `InitWebInstalled`, because some integration tests + // use cmd -> InitWebInstalled, but the AppDataPath doesn't exist during those tests. + if _, err := os.Stat(setting.AppDataPath); err != nil { + log.Fatal("Can not find APP_DATA_PATH %q", setting.AppDataPath) + } + + // the AppDataTempDir is fully managed by us with a safe sub-path + // so it's safe to automatically remove the outdated files + setting.AppDataTempDir("").RemoveOutdated(3 * 24 * time.Hour) + + // Override the provided port number within the configuration + if c.IsSet("port") { + if err := setPort(c.String("port")); err != nil { + return err + } + } + + gtprof.EnableBuiltinTracer(util.Iif(setting.IsProd, 2000*time.Millisecond, 100*time.Millisecond)) + + // Set up Chi routes + webRoutes := routers.NormalRoutes() + + auditCtx := cliAuditContext(context.Background()) + log.Info("Audit record output: %s", setting.Audit.RecordOutput) + audit.Record(auditCtx, audit_model.SystemStartup, nil, "version", setting.AppVer) + + err := listen(webRoutes, true) + + audit.Record(auditCtx, audit_model.SystemShutdown, nil) + <-graceful.GetManager().Done() + log.Info("PID: %d Gitea Web Finished", os.Getpid()) + return err +} + +func servePprof() { + mux := http.NewServeMux() + mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + mux.Handle("/debug/fgprof", fgprof.Handler()) + // FIXME: it should use a proper context + _, _, finished := process.GetManager().AddTypedContext(context.TODO(), "Web: PProf Server", process.SystemProcessType, true) + // The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment, it's not worth introducing a configurable option for it. + log.Info("Starting pprof server on localhost:6060") + server := &http.Server{Addr: "localhost:6060", Handler: mux, ReadHeaderTimeout: 10 * time.Second} + log.Info("Stopped pprof server: %v", server.ListenAndServe()) + finished() +} + +func runWeb(ctx context.Context, cmd *cli.Command) error { + if subCmdName, valid := isValidDefaultSubCommand(cmd); !valid { + return fmt.Errorf("unknown command: %s", subCmdName) + } + + managerCtx, cancel := context.WithCancel(ctx) + graceful.InitManager(managerCtx) + defer cancel() + + if os.Getppid() > 1 && len(os.Getenv("LISTEN_FDS")) > 0 { + log.Info("Restarting Gitea on PID: %d from parent PID: %d", os.Getpid(), os.Getppid()) + } else { + log.Info("Starting Gitea on PID: %d", os.Getpid()) + } + + // Set pid file setting + if cmd.IsSet("pid") { + createPIDFile(cmd.String("pid")) + } + + // init the HTML renderer and load templates, if error happens, it will report the error immediately and exit with error log + // in dev mode, it won't exit, but watch the template files for changes + _ = templates.PageRenderer() + + if !setting.InstallLock { + if err := serveInstall(cmd); err != nil { + return err + } + } else { + NoInstallListener() + } + + if setting.EnablePprof { + go servePprof() + } + + return serveInstalled(cmd) +} + +func setPort(port string) error { + setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1) + setting.HTTPPort = port + + switch setting.Protocol { + case setting.HTTPUnix: + case setting.FCGI: + case setting.FCGIUnix: + default: + defaultLocalURL := string(setting.Protocol) + "://" + if setting.HTTPAddr == "0.0.0.0" { + defaultLocalURL += "localhost" + } else { + defaultLocalURL += setting.HTTPAddr + } + defaultLocalURL += ":" + setting.HTTPPort + "/" + + // Save LOCAL_ROOT_URL if port changed + rootCfg := setting.CfgProvider + saveCfg, err := rootCfg.PrepareSaving() + if err != nil { + return fmt.Errorf("failed to save config file: %v", err) + } + rootCfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL) + saveCfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL) + if err = saveCfg.Save(); err != nil { + return fmt.Errorf("failed to save config file: %v", err) + } + } + return nil +} + +func listen(m http.Handler, handleRedirector bool) error { + listenAddr := setting.HTTPAddr + if setting.Protocol != setting.HTTPUnix && setting.Protocol != setting.FCGIUnix { + listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort) + } + _, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: Gitea Server", process.SystemProcessType, true) + defer finished() + log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL) + // This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy. + // A user may fix the configuration mistake when he sees this log. + // And this is also very helpful to maintainers to provide help to users to resolve their configuration problems. + log.Info("AppURL(ROOT_URL): %s", setting.AppURL) + + if setting.LFS.StartServer { + log.Info("LFS server enabled") + } + + var err error + switch setting.Protocol { + case setting.HTTP: + if handleRedirector { + NoHTTPRedirector() + } + err = runHTTP("tcp", listenAddr, "Web", m, setting.UseProxyProtocol) + case setting.HTTPS: + if setting.EnableAcme { + err = runACME(listenAddr, m) + break + } + if handleRedirector { + if setting.RedirectOtherPort { + go runHTTPRedirector() + } else { + NoHTTPRedirector() + } + } + err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging) + case setting.FCGI: + if handleRedirector { + NoHTTPRedirector() + } + err = runFCGI("tcp", listenAddr, "FCGI Web", m, setting.UseProxyProtocol) + case setting.HTTPUnix: + if handleRedirector { + NoHTTPRedirector() + } + err = runHTTP("unix", listenAddr, "Web", m, setting.UseProxyProtocol) + case setting.FCGIUnix: + if handleRedirector { + NoHTTPRedirector() + } + err = runFCGI("unix", listenAddr, "Web", m, setting.UseProxyProtocol) + default: + log.Fatal("Invalid protocol: %s", setting.Protocol) + } + if err != nil { + log.Error("Failed to start server: %v", err) + } + log.Info("HTTP Listener: %s Closed", listenAddr) + return err +} diff --git a/cmd/web_acme.go b/cmd/web_acme.go new file mode 100644 index 0000000..10a00cc --- /dev/null +++ b/cmd/web_acme.go @@ -0,0 +1,144 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "net" + "net/http" + "os" + "strconv" + "strings" + + "gitea.dev/modules/graceful" + "gitea.dev/modules/log" + "gitea.dev/modules/process" + "gitea.dev/modules/setting" + "gitea.dev/modules/util" + + "github.com/caddyserver/certmagic" +) + +func getCARoot(path string) (*x509.CertPool, error) { + r, err := os.ReadFile(path) + if err != nil { + return nil, err + } + block, _ := pem.Decode(r) + if block == nil { + return nil, fmt.Errorf("no PEM found in the file %s", path) + } + caRoot, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, err + } + certPool := x509.NewCertPool() + certPool.AddCert(caRoot) + return certPool, nil +} + +func runACME(listenAddr string, m http.Handler) error { + // If HTTP Challenge enabled, needs to be serving on port 80. For TLSALPN needs 443. + // Due to docker port mapping this can't be checked programmatically + // TODO: these are placeholders until we add options for each in settings with appropriate warning + enableHTTPChallenge := true + enableTLSALPNChallenge := true + altHTTPPort := 0 + altTLSALPNPort := 0 + + if p, err := strconv.Atoi(setting.PortToRedirect); err == nil { + altHTTPPort = p + } + if p, err := strconv.Atoi(setting.HTTPPort); err == nil { + altTLSALPNPort = p + } + + // Try to use private CA root if provided, otherwise defaults to system's trust + var certPool *x509.CertPool + if setting.AcmeCARoot != "" { + var err error + certPool, err = getCARoot(setting.AcmeCARoot) + if err != nil { + log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err) + } + } + // FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https" + // Ideally it should migrate to AppDataPath write to "AppDataPath/https" + // And one more thing, no idea why we should set the global default variables here + // But it seems that the current ACME code needs these global variables to make renew work. + // Otherwise, "renew" will use incorrect storage path + oldDefaultACME := certmagic.DefaultACME + certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory} + certmagic.DefaultACME = certmagic.ACMEIssuer{ + // try to use the default values provided by DefaultACME + CA: util.IfZero(setting.AcmeURL, oldDefaultACME.CA), + TestCA: oldDefaultACME.TestCA, + Logger: oldDefaultACME.Logger, + HTTPProxy: oldDefaultACME.HTTPProxy, + + TrustedRoots: certPool, + Email: setting.AcmeEmail, + Agreed: setting.AcmeTOS, + DisableHTTPChallenge: !enableHTTPChallenge, + DisableTLSALPNChallenge: !enableTLSALPNChallenge, + ListenHost: setting.HTTPAddr, + AltTLSALPNPort: altTLSALPNPort, + AltHTTPPort: altHTTPPort, + } + + magic := certmagic.NewDefault() + myACME := certmagic.NewACMEIssuer(magic, certmagic.DefaultACME) + magic.Issuers = []certmagic.Issuer{myACME} + + // Obtain certificates or renew them if necessary. ManageSync fails closed on + // renewal errors even when a still-valid certificate is already on disk, which + // takes HTTPS down on restart (https://github.com/go-gitea/gitea/issues/38519). + // Prefer keeping the existing cert and retrying renewals asynchronously. + ctx := graceful.GetManager().ShutdownContext() + err := magic.ManageSync(ctx, []string{setting.Domain}) + if err != nil { + cert, cacheErr := magic.CacheManagedCertificate(ctx, setting.Domain) + if cacheErr != nil || cert.Expired() { + return errors.Join(err, cacheErr) + } + log.Error("ACME certificate manage failed; continuing with existing certificate: %v", err) + if err := magic.ManageAsync(ctx, []string{setting.Domain}); err != nil { + log.Error("Failed to start async ACME management: %v", err) + } + } + + // certmagic only advertises its own ACME challenge protocol, applyTLSSettings appends ours + tlsConfig := applyTLSSettings(magic.TLSConfig()) + + if enableHTTPChallenge { + go func() { + _, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: ACME HTTP challenge server", process.SystemProcessType, true) + defer finished() + + log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect) + // all traffic coming into HTTP will be redirected to HTTPS automatically (LE HTTP-01 validation happens here) + err := runHTTP("tcp", net.JoinHostPort(setting.HTTPAddr, setting.PortToRedirect), "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)), setting.RedirectorUseProxyProtocol) + if err != nil { + log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err) + } + }() + } + + return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging) +} + +func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet && r.Method != http.MethodHead { + http.Error(w, "Use HTTPS", http.StatusBadRequest) + return + } + // Remove the trailing slash at the end of setting.AppURL, the request + // URI always contains a leading slash, which would result in a double + // slash + target := strings.TrimSuffix(setting.AppURL, "/") + r.URL.RequestURI() + http.Redirect(w, r, target, http.StatusTemporaryRedirect) +} diff --git a/cmd/web_graceful.go b/cmd/web_graceful.go new file mode 100644 index 0000000..13eb9dd --- /dev/null +++ b/cmd/web_graceful.go @@ -0,0 +1,49 @@ +// Copyright 2016 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "net" + "net/http" + "net/http/fcgi" + "strings" + + "gitea.dev/modules/graceful" + "gitea.dev/modules/log" + "gitea.dev/modules/setting" +) + +func runHTTP(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error { + return graceful.HTTPListenAndServe(network, listenAddr, name, m, useProxyProtocol) +} + +// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector +func NoHTTPRedirector() { + graceful.GetManager().InformCleanup() +} + +// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener +// for our install HTTP/HTTPS service +func NoInstallListener() { + graceful.GetManager().InformCleanup() +} + +func runFCGI(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error { + // This needs to handle stdin as fcgi point + fcgiServer := graceful.NewServer(network, listenAddr, name) + + err := fcgiServer.ListenAndServe(func(listener net.Listener) error { + return fcgi.Serve(listener, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + if setting.AppSubURL != "" { + req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL) + } + m.ServeHTTP(resp, req) + })) + }, useProxyProtocol) + if err != nil { + log.Fatal("Failed to start FCGI main server: %v", err) + } + log.Info("FCGI Listener: %s Closed", listenAddr) + return err +} diff --git a/cmd/web_https.go b/cmd/web_https.go new file mode 100644 index 0000000..907c0ea --- /dev/null +++ b/cmd/web_https.go @@ -0,0 +1,125 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "crypto/tls" + "net/http" + "os" + "strings" + + "gitea.dev/modules/graceful" + "gitea.dev/modules/log" + "gitea.dev/modules/setting" +) + +var tlsVersionStringMap = map[string]uint16{ + "tlsv1.0": tls.VersionTLS10, + "tlsv1.1": tls.VersionTLS11, + "tlsv1.2": tls.VersionTLS12, + "tlsv1.3": tls.VersionTLS13, +} + +// toTLSVersion returns 0 when unset or unknown, which keeps the crypto/tls default +func toTLSVersion(version string) uint16 { + version = strings.TrimSpace(strings.ToLower(version)) + tlsVersion, ok := tlsVersionStringMap[version] + if !ok && version != "" { + log.Warn("Unknown tls version: %s", version) + } + return tlsVersion +} + +var curveStringMap = map[string]tls.CurveID{ + "x25519": tls.X25519, + "p256": tls.CurveP256, + "p384": tls.CurveP384, + "p521": tls.CurveP521, + "mlkem1024": tls.MLKEM1024, + "x25519mlkem768": tls.X25519MLKEM768, + "secp256r1mlkem768": tls.SecP256r1MLKEM768, + "secp384r1mlkem1024": tls.SecP384r1MLKEM1024, +} + +// lookupAll resolves configured names, warning about and skipping the ones crypto/tls does not know +func lookupAll[T comparable](names []string, table map[string]T, kind string) []T { + values := make([]T, 0, len(names)) + for _, name := range names { + value, ok := table[strings.TrimSpace(strings.ToLower(name))] + if !ok { + log.Warn("Unknown %s: %s", kind, name) + continue + } + values = append(values, value) + } + return values +} + +// cipherStringMap is derived from crypto/tls so that suites Go adds or removes are picked up automatically +var cipherStringMap = buildCipherStringMap() + +func buildCipherStringMap() map[string]uint16 { + ciphers := map[string]uint16{ + // aliases for the two suites Go later renamed with a _SHA256 suffix + "ecdhe_rsa_with_chacha20_poly1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + "ecdhe_ecdsa_with_chacha20_poly1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + } + for _, cipher := range append(tls.CipherSuites(), tls.InsecureCipherSuites()...) { + ciphers[strings.ToLower(strings.TrimPrefix(cipher.Name, "TLS_"))] = cipher.ID + } + return ciphers +} + +// applyTLSSettings applies the configured [server] TLS options. Every option +// left unset keeps the crypto/tls default, which Go keeps current. +func applyTLSSettings(tlsConfig *tls.Config) *tls.Config { + // the listener is already TLS wrapped when it reaches http.Server, so it never sets ALPN for us + tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2", "http/1.1") + tlsConfig.MinVersion = toTLSVersion(setting.SSLMinimumVersion) + tlsConfig.MaxVersion = toTLSVersion(setting.SSLMaximumVersion) + + if curves := lookupAll(setting.SSLCurvePreferences, curveStringMap, "curve"); len(curves) > 0 { + tlsConfig.CurvePreferences = curves + } + if ciphers := lookupAll(setting.SSLCipherSuites, cipherStringMap, "cipher suite"); len(ciphers) > 0 { + tlsConfig.CipherSuites = ciphers + } + return tlsConfig +} + +// runHTTPS listens on the provided network address and then calls +// Serve to handle requests on incoming TLS connections. +// +// Filenames containing a certificate and matching private key for the server must +// be provided. If the certificate is signed by a certificate authority, the +// certFile should be the concatenation of the server's certificate followed by the +// CA's certificate. +func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error { + tlsConfig := applyTLSSettings(&tls.Config{}) + tlsConfig.Certificates = make([]tls.Certificate, 1) + + certPEMBlock, err := os.ReadFile(certFile) + if err != nil { + log.Error("Failed to load https cert file %s for %s:%s: %v", certFile, network, listenAddr, err) + return err + } + + keyPEMBlock, err := os.ReadFile(keyFile) + if err != nil { + log.Error("Failed to load https key file %s for %s:%s: %v", keyFile, network, listenAddr, err) + return err + } + + tlsConfig.Certificates[0], err = tls.X509KeyPair(certPEMBlock, keyPEMBlock) + if err != nil { + log.Error("Failed to create certificate from cert file %s and key file %s for %s:%s: %v", certFile, keyFile, network, listenAddr, err) + return err + } + + return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging) +} + +func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error { + return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging) +} diff --git a/cmd/web_https_test.go b/cmd/web_https_test.go new file mode 100644 index 0000000..a6bc4dc --- /dev/null +++ b/cmd/web_https_test.go @@ -0,0 +1,109 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "crypto/tls" + "math" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "gitea.dev/modules/setting" + "gitea.dev/modules/test" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestToTLSVersion(t *testing.T) { + assert.Zero(t, toTLSVersion(""), "unset must keep the crypto/tls default") + assert.Zero(t, toTLSVersion("unknown")) + assert.Equal(t, uint16(tls.VersionTLS12), toTLSVersion(" TLSV1.2 ")) +} + +func unsetTLSSettings(t *testing.T) { + t.Helper() + t.Cleanup(test.MockVariableValue(&setting.SSLMinimumVersion, "")) + t.Cleanup(test.MockVariableValue(&setting.SSLMaximumVersion, "")) + t.Cleanup(test.MockVariableValue(&setting.SSLCurvePreferences, nil)) + t.Cleanup(test.MockVariableValue(&setting.SSLCipherSuites, nil)) +} + +// TestApplyTLSSettingsUnsetKeepsGoDefaults guards against reintroducing hardcoded defaults, +// which is how the server ended up capped at TLS 1.2 and without post-quantum key exchange. +// It asserts the fields are left alone rather than which algorithms Go then picks, so a new +// Go release changing its preferences does not fail this test. +func TestApplyTLSSettingsUnsetKeepsGoDefaults(t *testing.T) { + unsetTLSSettings(t) + + tlsConfig := applyTLSSettings(&tls.Config{}) + assert.Zero(t, tlsConfig.MinVersion) + assert.Zero(t, tlsConfig.MaxVersion) + assert.Nil(t, tlsConfig.CurvePreferences) + assert.Nil(t, tlsConfig.CipherSuites) + assert.Equal(t, []string{"h2", "http/1.1"}, tlsConfig.NextProtos) +} + +func TestApplyTLSSettingsConfigured(t *testing.T) { + t.Cleanup(test.MockVariableValue(&setting.SSLMinimumVersion, "tlsv1.2")) + t.Cleanup(test.MockVariableValue(&setting.SSLMaximumVersion, "tlsv1.3")) + t.Cleanup(test.MockVariableValue(&setting.SSLCurvePreferences, []string{"p384"})) + t.Cleanup(test.MockVariableValue(&setting.SSLCipherSuites, []string{"ecdhe_rsa_with_aes_256_gcm_sha384"})) + + tlsConfig := applyTLSSettings(&tls.Config{}) + assert.Equal(t, uint16(tls.VersionTLS12), tlsConfig.MinVersion) + assert.Equal(t, uint16(tls.VersionTLS13), tlsConfig.MaxVersion) + assert.Equal(t, []tls.CurveID{tls.CurveP384}, tlsConfig.CurvePreferences) + assert.Equal(t, []uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, tlsConfig.CipherSuites) +} + +func TestHTTPSServesTLS13(t *testing.T) { + unsetTLSSettings(t) + + server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + })) + server.TLS = applyTLSSettings(&tls.Config{}) + server.EnableHTTP2 = true + server.StartTLS() + defer server.Close() + + conn, err := tls.Dial("tcp", strings.TrimPrefix(server.URL, "https://"), &tls.Config{InsecureSkipVerify: true}) + require.NoError(t, err) + defer conn.Close() + assert.Equal(t, uint16(tls.VersionTLS13), conn.ConnectionState().Version) +} + +// TestCurveStringMapCoversGoCurves fails when Go gains a curve the map does not name, so the +// config vocabulary cannot silently go stale the way it did for the post-quantum key exchanges +func TestCurveStringMapCoversGoCurves(t *testing.T) { + for id := range math.MaxUint16 + 1 { + name := tls.CurveID(id).String() + if strings.HasPrefix(name, "CurveID(") { + continue // crypto/tls has no name for this ID, so it supports no such curve + } + assert.Contains(t, curveStringMap, strings.TrimPrefix(strings.ToLower(name), "curve"), "curve %s", name) + } +} + +// TestCipherStringMapCoversLegacyNames pins the names accepted before the map was derived +// from crypto/tls, so no existing SSL_CIPHER_SUITES config silently stops resolving +func TestCipherStringMapCoversLegacyNames(t *testing.T) { + for _, name := range []string{ + "rsa_with_rc4_128_sha", "rsa_with_3des_ede_cbc_sha", "rsa_with_aes_128_cbc_sha", + "rsa_with_aes_256_cbc_sha", "rsa_with_aes_128_cbc_sha256", "rsa_with_aes_128_gcm_sha256", + "rsa_with_aes_256_gcm_sha384", "ecdhe_ecdsa_with_rc4_128_sha", "ecdhe_ecdsa_with_aes_128_cbc_sha", + "ecdhe_ecdsa_with_aes_256_cbc_sha", "ecdhe_rsa_with_rc4_128_sha", "ecdhe_rsa_with_3des_ede_cbc_sha", + "ecdhe_rsa_with_aes_128_cbc_sha", "ecdhe_rsa_with_aes_256_cbc_sha", "ecdhe_ecdsa_with_aes_128_cbc_sha256", + "ecdhe_rsa_with_aes_128_cbc_sha256", "ecdhe_rsa_with_aes_128_gcm_sha256", "ecdhe_ecdsa_with_aes_128_gcm_sha256", + "ecdhe_rsa_with_aes_256_gcm_sha384", "ecdhe_ecdsa_with_aes_256_gcm_sha384", + "ecdhe_rsa_with_chacha20_poly1305_sha256", "ecdhe_ecdsa_with_chacha20_poly1305_sha256", + "ecdhe_rsa_with_chacha20_poly1305", "ecdhe_ecdsa_with_chacha20_poly1305", + "aes_128_gcm_sha256", "aes_256_gcm_sha384", "chacha20_poly1305_sha256", + } { + assert.NotZero(t, cipherStringMap[name], "cipher %q", name) + } +} diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..fddd5ac --- /dev/null +++ b/contrib/README @@ -0,0 +1 @@ +All files in subdirectories are templates, do modifications based on your environment first. \ No newline at end of file diff --git a/contrib/development/README.md b/contrib/development/README.md new file mode 100644 index 0000000..5fc5dd3 --- /dev/null +++ b/contrib/development/README.md @@ -0,0 +1,14 @@ +# IDE and code editor configuration + +## Table of Contents + +- [IDE and code editor configuration](#ide-and-code-editor-configuration) + - [Microsoft Visual Studio Code](#microsoft-visual-studio-code) + +## Microsoft Visual Studio Code + +Download Microsoft Visual Studio Code at https://code.visualstudio.com/ and follow instructions at https://code.visualstudio.com/docs/languages/go to setup Go extension for it. + +Create new directory `.vscode` in Gitea root folder and copy contents of folder [contrib/development/vscode](vscode/) to it. You can now use `Ctrl`+`Shift`+`B` to build gitea executable and `F5` to run it in debug mode. + +Supported on Debian, Ubuntu, Red Hat, Fedora, SUSE Linux, MacOS and Microsoft Windows. diff --git a/contrib/development/vscode/launch.json b/contrib/development/vscode/launch.json new file mode 100644 index 0000000..048428f --- /dev/null +++ b/contrib/development/vscode/launch.json @@ -0,0 +1,18 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "type": "go", + "request": "launch", + "mode": "debug", + "buildFlags": "", + "program": "${workspaceRoot}/main.go", + "env": { + "GITEA_WORK_DIR": "${workspaceRoot}", + }, + "args": ["web"], + "showLog": true + } + ] +} diff --git a/contrib/development/vscode/settings.json b/contrib/development/vscode/settings.json new file mode 100644 index 0000000..d968e4d --- /dev/null +++ b/contrib/development/vscode/settings.json @@ -0,0 +1,4 @@ +{ + "go.buildTags": "", + "go.testFlags": ["-v"] +} diff --git a/contrib/development/vscode/tasks.json b/contrib/development/vscode/tasks.json new file mode 100644 index 0000000..490e303 --- /dev/null +++ b/contrib/development/vscode/tasks.json @@ -0,0 +1,27 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build", + "type": "shell", + "command": "go", + "group": "build", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared" + }, + "linux": { + "args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ] + }, + "osx": { + "args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ] + }, + "windows": { + "args": ["build", "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""] + }, + "problemMatcher": ["$go"] + } + ] +} diff --git a/contrib/fhs-compliant-script/gitea b/contrib/fhs-compliant-script/gitea new file mode 100755 index 0000000..ea033b0 --- /dev/null +++ b/contrib/fhs-compliant-script/gitea @@ -0,0 +1,40 @@ +#!/bin/bash + +############################################################################# +# This script sets some defaults for gitea to run in a FHS compliant manner # +############################################################################# + +# It assumes that you place this script as gitea in /usr/bin +# +# And place the original in /usr/lib/gitea with working files in /var/lib/gitea +# and main configuration in /etc/gitea/app.ini +GITEA="/usr/lib/gitea/gitea" +WORK_DIR="/var/lib/gitea" +APP_INI="/etc/gitea/app.ini" + +APP_INI_SET="" +for i in "$@"; do + case "$i" in + "-c") + APP_INI_SET=1 + ;; + "-c="*) + APP_INI_SET=1 + ;; + "--config") + APP_INI_SET=1 + ;; + "--config="*) + APP_INI_SET=1 + ;; + *) + ;; + esac +done + +if [ -z "$APP_INI_SET" ]; then + CONF_ARG=("-c" "${GITEA_APP_INI:-$APP_INI}") +fi + +# Provide FHS compliant defaults +GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" exec -a "$0" "$GITEA" "${CONF_ARG[@]}" "$@" diff --git a/contrib/grafana-monitoring-mixin/.gitignore b/contrib/grafana-monitoring-mixin/.gitignore new file mode 100644 index 0000000..f8472b0 --- /dev/null +++ b/contrib/grafana-monitoring-mixin/.gitignore @@ -0,0 +1,2 @@ +dashboards_out +vendor diff --git a/contrib/grafana-monitoring-mixin/Makefile b/contrib/grafana-monitoring-mixin/Makefile new file mode 100644 index 0000000..429dfc4 --- /dev/null +++ b/contrib/grafana-monitoring-mixin/Makefile @@ -0,0 +1,31 @@ +JSONNET_FMT := jsonnetfmt -n 2 --max-blank-lines 1 --string-style s --comment-style s + +.PHONY: all +all: build dashboards_out + +vendor: jsonnetfile.json + jb install + +.PHONY: build +build: vendor + +.PHONY: fmt +fmt: + find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \ + xargs -n 1 -- $(JSONNET_FMT) -i + +.PHONY: lint +lint: build + find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \ + while read f; do \ + $(JSONNET_FMT) "$$f" | diff -u "$$f" -; \ + done + mixtool lint mixin.libsonnet + +dashboards_out: mixin.libsonnet config.libsonnet $(wildcard dashboards/*) + @mkdir -p dashboards_out + jsonnet -J vendor -m dashboards_out lib/dashboards.jsonnet + +.PHONY: clean +clean: + rm -rf dashboards_out diff --git a/contrib/grafana-monitoring-mixin/README.md b/contrib/grafana-monitoring-mixin/README.md new file mode 100644 index 0000000..2e11706 --- /dev/null +++ b/contrib/grafana-monitoring-mixin/README.md @@ -0,0 +1,33 @@ +# Gitea Mixin + +Gitea Mixin is a set of configurable Grafana dashboards based on the metrics exported by the Gitea built-in metrics endpoint. + +## Generate config files + +You can manually generate dashboards, but first you should install some tools: + +```bash +go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest +go install github.com/google/go-jsonnet/cmd/jsonnet@latest +# or in brew: brew install go-jsonnet +``` + +For linting and formatting, you would also need `mixtool` and `jsonnetfmt` installed. If you +have a working Go development environment, it's easiest to run the following: + +```bash +go install github.com/monitoring-mixins/mixtool/cmd/mixtool@latest +go install github.com/google/go-jsonnet/cmd/jsonnetfmt@latest +``` + +The files in `dashboards_out` need to be imported +into your Grafana server. The exact details will be depending on your environment. + +Edit `config.libsonnet` if required and then build JSON dashboard files for Grafana: + +```bash +make +``` + +For more advanced uses of mixins, see +https://github.com/monitoring-mixins/docs. diff --git a/contrib/grafana-monitoring-mixin/config.libsonnet b/contrib/grafana-monitoring-mixin/config.libsonnet new file mode 100644 index 0000000..446fc09 --- /dev/null +++ b/contrib/grafana-monitoring-mixin/config.libsonnet @@ -0,0 +1,99 @@ +{ + _config+:: { + local c = self, + dashboardNamePrefix: 'Gitea', + dashboardTags: ['gitea'], + dashboardPeriod: 'now-1h', + dashboardTimezone: 'default', + dashboardRefresh: '1m', + + // please see https://docs.gitea.com/administration/config-cheat-sheet#metrics-metrics + // Show issue by repository metrics with format gitea_issues_by_repository{repository="org/repo"} 5. + // Requires Gitea 1.16.0 with ENABLED_ISSUE_BY_REPOSITORY set to true. + showIssuesByRepository: true, + // Show graphs for issue by label metrics with format gitea_issues_by_label{label="bug"} 2. + // Requires Gitea 1.16.0 with ENABLED_ISSUE_BY_LABEL set to true. + showIssuesByLabel: true, + + // Requires Gitea 1.16.0. + showIssuesOpenClose: true, + + // add or remove metrics from dashboard + giteaStatMetrics: + [ + { + name: 'gitea_organizations', + description: 'Organizations', + }, + { + name: 'gitea_teams', + description: 'Teams', + }, + { + name: 'gitea_users', + description: 'Users', + }, + { + name: 'gitea_repositories', + description: 'Repositories', + }, + { + name: 'gitea_milestones', + description: 'Milestones', + }, + { + name: 'gitea_stars', + description: 'Stars', + }, + { + name: 'gitea_releases', + description: 'Releases', + }, + ] + + + if c.showIssuesOpenClose then + [ + { + name: 'gitea_issues_open', + description: 'Issues opened', + }, + { + name: 'gitea_issues_closed', + description: 'Issues closed', + }, + ] else + [ + { + name: 'gitea_issues', + description: 'Issues', + }, + ], + //set this for using label colors on graphs + issueLabels: [ + { + label: 'bug', + color: '#ee0701', + }, + { + label: 'duplicate', + color: '#cccccc', + }, + { + label: 'invalid', + color: '#e6e6e6', + }, + { + label: 'enhancement', + color: '#84b6eb', + }, + { + label: 'help wanted', + color: '#128a0c', + }, + { + label: 'question', + color: '#cc317c', + }, + ], + }, +} diff --git a/contrib/grafana-monitoring-mixin/dashboards/dashboards.libsonnet b/contrib/grafana-monitoring-mixin/dashboards/dashboards.libsonnet new file mode 100644 index 0000000..800feec --- /dev/null +++ b/contrib/grafana-monitoring-mixin/dashboards/dashboards.libsonnet @@ -0,0 +1 @@ +(import 'overview.libsonnet') diff --git a/contrib/grafana-monitoring-mixin/dashboards/overview.libsonnet b/contrib/grafana-monitoring-mixin/dashboards/overview.libsonnet new file mode 100644 index 0000000..31b7d4f --- /dev/null +++ b/contrib/grafana-monitoring-mixin/dashboards/overview.libsonnet @@ -0,0 +1,467 @@ +local grafana = import 'github.com/grafana/grafonnet-lib/grafonnet/grafana.libsonnet'; +local prometheus = grafana.prometheus; + +local addIssueLabelsOverrides(labels) = + { + fieldConfig+: { + overrides+: [ + { + matcher: { + id: 'byRegexp', + options: label.label, + }, + properties: [ + { + id: 'color', + value: { + fixedColor: label.color, + mode: 'fixed', + }, + }, + ], + } + for label in labels + ], + }, + }; + +{ + + grafanaDashboards+:: { + + local giteaSelector = 'job=~"$job", instance=~"$instance"', + local giteaStatsPanel = + grafana.statPanel.new( + 'Gitea stats', + datasource='$datasource', + reducerFunction='lastNotNull', + graphMode='none', + colorMode='value', + ) + .addTargets( + [ + prometheus.target(expr='%s{%s}' % [metric.name, giteaSelector], legendFormat=metric.description, intervalFactor=10) + for metric in $._config.giteaStatMetrics + ] + ) + + { + fieldConfig+: { + defaults+: { + color: { + fixedColor: 'blue', + mode: 'fixed', + }, + }, + }, + }, + + local giteaUptimePanel = + grafana.statPanel.new( + 'Uptime', + datasource='$datasource', + reducerFunction='last', + graphMode='area', + colorMode='value', + ) + .addTarget(prometheus.target(expr='time()-process_start_time_seconds{%s}' % giteaSelector, intervalFactor=1)) + + { + fieldConfig+: { + defaults+: { + color: { + fixedColor: 'blue', + mode: 'fixed', + }, + unit: 's', + }, + }, + }, + + local giteaMemoryPanel = + grafana.graphPanel.new( + 'Memory usage', + datasource='$datasource' + ) + .addTarget(prometheus.target(expr='process_resident_memory_bytes{%s}' % giteaSelector, intervalFactor=2)) + + { + type: 'timeseries', + options+: { + tooltip: { + mode: 'multi', + }, + legend+: { + displayMode: 'hidden', + }, + }, + fieldConfig+: { + defaults+: { + custom+: { + lineInterpolation: 'smooth', + fillOpacity: 15, + }, + color: { + fixedColor: 'green', + mode: 'fixed', + }, + unit: 'decbytes', + }, + }, + }, + + local giteaCpuPanel = + grafana.graphPanel.new( + 'CPU usage', + datasource='$datasource' + ) + .addTarget(prometheus.target(expr='rate(process_cpu_seconds_total{%s}[$__rate_interval])*100' % giteaSelector, intervalFactor=2)) + + { + type: 'timeseries', + options+: { + tooltip: { + mode: 'multi', + }, + legend+: { + displayMode: 'hidden', + }, + }, + fieldConfig+: { + defaults+: { + custom+: { + lineInterpolation: 'smooth', + gradientMode: 'scheme', + fillOpacity: 15, + axisSoftMin: 0, + axisSoftMax: 0, + }, + color: { + mode: 'continuous-GrYlRd', // from green to red (100%) + }, + unit: 'percent', + }, + overrides: [ + { + matcher: { + id: 'byRegexp', + options: '.+', + }, + properties: [ + { + id: 'max', + value: 100, + }, + { + id: 'min', + value: 0, + }, + ], + }, + ], + }, + }, + + local giteaFileDescriptorsPanel = + grafana.graphPanel.new( + 'File descriptors usage', + datasource='$datasource', + ) + .addTarget(prometheus.target(expr='process_open_fds{%s}' % giteaSelector, intervalFactor=2)) + .addTarget(prometheus.target(expr='process_max_fds{%s}' % giteaSelector, intervalFactor=2)) + .addSeriesOverride( + { + alias: '/process_max_fds.+/', + color: '#F2495C', // red + dashes: true, + fill: 0, + }, + ) + + { + type: 'timeseries', + options+: { + tooltip: { + mode: 'multi', + }, + legend+: { + displayMode: 'hidden', + }, + }, + fieldConfig+: { + defaults+: { + custom+: { + lineInterpolation: 'smooth', + gradientMode: 'scheme', + fillOpacity: 0, + }, + color: { + fixedColor: 'green', + mode: 'fixed', + }, + unit: '', + }, + overrides: [ + { + matcher: { + id: 'byFrameRefID', + options: 'B', + }, + properties: [ + { + id: 'custom.lineStyle', + value: { + fill: 'dash', + dash: [ + 10, + 10, + ], + }, + }, + { + id: 'color', + value: { + mode: 'fixed', + fixedColor: 'red', + }, + }, + ], + }, + ], + }, + }, + + local giteaChangesPanelPrototype = + grafana.graphPanel.new( + '', + datasource='$datasource', + interval='$agg_interval', + maxDataPoints=10000, + ) + + { + type: 'timeseries', + options+: { + tooltip: { + mode: 'multi', + }, + legend+: { + calcs+: [ + 'sum', + ], + }, + }, + fieldConfig+: { + defaults+: { + noValue: '0', + custom+: { + drawStyle: 'bars', + barAlignment: -1, + fillOpacity: 50, + gradientMode: 'hue', + pointSize: 1, + lineWidth: 0, + stacking: { + group: 'A', + mode: 'normal', + }, + }, + }, + }, + }, + + local giteaChangesPanelAll = + giteaChangesPanelPrototype + .addTarget(prometheus.target(expr='changes(process_start_time_seconds{%s}[$__interval]) > 0' % [giteaSelector], legendFormat='Restarts', intervalFactor=1)) + .addTargets( + [ + prometheus.target(expr='floor(delta(%s{%s}[$__interval])) > 0' % [metric.name, giteaSelector], legendFormat=metric.description, intervalFactor=1) + for metric in $._config.giteaStatMetrics + ] + ) + { id: 200 }, // some unique number, beyond the maximum number of panels in the dashboard, + + local giteaChangesPanelTotal = + grafana.statPanel.new( + 'Changes', + datasource='-- Dashboard --', + reducerFunction='sum', + graphMode='none', + textMode='value_and_name', + colorMode='value', + ) + + { + targets+: [ + { + panelId: giteaChangesPanelAll.id, + refId: 'A', + }, + ], + } + + { + fieldConfig+: { + defaults+: { + color: { + mode: 'palette-classic', + }, + }, + }, + }, + + local giteaChangesByRepositories = + giteaChangesPanelPrototype + .addTarget(prometheus.target(expr='floor(increase(gitea_issues_by_repository{%s}[$__interval])) > 0' % [giteaSelector], legendFormat='{{ repository }}', intervalFactor=1)) + + { id: 210 }, // some unique number, beyond the maximum number of panels in the dashboard, + + local giteaChangesByRepositoriesTotal = + grafana.statPanel.new( + 'Issues by repository', + datasource='-- Dashboard --', + reducerFunction='sum', + graphMode='none', + textMode='value_and_name', + colorMode='value', + ) + + { + id: 211, + targets+: [ + { + panelId: giteaChangesByRepositories.id, + refId: 'A', + }, + ], + } + + { + fieldConfig+: { + defaults+: { + color: { + mode: 'palette-classic', + }, + }, + }, + }, + + local giteaChangesByLabel = + giteaChangesPanelPrototype + .addTarget(prometheus.target(expr='floor(increase(gitea_issues_by_label{%s}[$__interval])) > 0' % [giteaSelector], legendFormat='{{ label }}', intervalFactor=1)) + + addIssueLabelsOverrides($._config.issueLabels) + + { id: 220 }, // some unique number, beyond the maximum number of panels in the dashboard, + + local giteaChangesByLabelTotal = + grafana.statPanel.new( + 'Issues by labels', + datasource='-- Dashboard --', + reducerFunction='sum', + graphMode='none', + textMode='value_and_name', + colorMode='value', + ) + + addIssueLabelsOverrides($._config.issueLabels) + + { + id: 221, + targets+: [ + { + panelId: giteaChangesByLabel.id, + refId: 'A', + }, + ], + } + + { + fieldConfig+: { + defaults+: { + color: { + mode: 'palette-classic', + }, + }, + }, + }, + + 'gitea-overview.json': + grafana.dashboard.new( + '%s Overview' % $._config.dashboardNamePrefix, + time_from='%s' % $._config.dashboardPeriod, + editable=false, + tags=($._config.dashboardTags), + timezone='%s' % $._config.dashboardTimezone, + refresh='%s' % $._config.dashboardRefresh, + graphTooltip='shared_crosshair', + uid='gitea-overview' + ) + .addTemplate( + { + current: { + text: 'Prometheus', + value: 'Prometheus', + }, + hide: 0, + label: 'Data Source', + name: 'datasource', + options: [], + query: 'prometheus', + refresh: 1, + regex: '', + type: 'datasource', + }, + ) + .addTemplate( + { + hide: 0, + label: 'job', + name: 'job', + options: [], + datasource: '$datasource', + query: 'label_values(gitea_organizations, job)', + refresh: 1, + regex: '', + type: 'query', + multi: true, + allValue: '.+' + }, + ) + .addTemplate( + { + hide: 0, + label: 'instance', + name: 'instance', + options: [], + datasource: '$datasource', + query: 'label_values(gitea_organizations{job="$job"}, instance)', + refresh: 1, + regex: '', + type: 'query', + multi: true, + allValue: '.+' + }, + ) + .addTemplate( + { + hide: 0, + label: 'aggregation interval', + name: 'agg_interval', + auto_min: '1m', + auto: true, + query: '1m,10m,1h,1d,7d', + type: 'interval', + }, + ) + .addPanel(grafana.row.new(title='General'), gridPos={ x: 0, y: 0, w: 0, h: 0 },) + .addPanel(giteaStatsPanel, gridPos={ x: 0, y: 0, w: 16, h: 4 }) + .addPanel(giteaUptimePanel, gridPos={ x: 16, y: 0, w: 8, h: 4 }) + .addPanel(giteaMemoryPanel, gridPos={ x: 0, y: 4, w: 8, h: 6 }) + .addPanel(giteaCpuPanel, gridPos={ x: 8, y: 4, w: 8, h: 6 }) + .addPanel(giteaFileDescriptorsPanel, gridPos={ x: 16, y: 4, w: 8, h: 6 }) + .addPanel(grafana.row.new(title='Changes', collapse=false), gridPos={ x: 0, y: 10, w: 24, h: 8 }) + .addPanel(giteaChangesPanelTotal, gridPos={ x: 0, y: 12, w: 6, h: 8 }) + + // use patching instead of .addPanel() to keep static ids + { + panels+: std.flattenArrays([ + [ + giteaChangesPanelAll { gridPos: { x: 6, y: 12, w: 18, h: 8 } }, + ], + if $._config.showIssuesByRepository then + [ + giteaChangesByRepositoriesTotal { gridPos: { x: 0, y: 20, w: 6, h: 8 } }, + giteaChangesByRepositories { gridPos: { x: 6, y: 20, w: 18, h: 8 } }, + ] else [], + if $._config.showIssuesByLabel then + [ + giteaChangesByLabelTotal { gridPos: { x: 0, y: 28, w: 6, h: 8 } }, + giteaChangesByLabel { gridPos: { x: 6, y: 28, w: 18, h: 8 } }, + ] else [], + ]), + }, + }, +} diff --git a/contrib/grafana-monitoring-mixin/jsonnetfile.json b/contrib/grafana-monitoring-mixin/jsonnetfile.json new file mode 100644 index 0000000..5e9bae2 --- /dev/null +++ b/contrib/grafana-monitoring-mixin/jsonnetfile.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "dependencies": [ + { + "source": { + "git": { + "remote": "https://github.com/grafana/grafonnet-lib.git", + "subdir": "grafonnet" + } + }, + "version": "master" + } + ], + "legacyImports": false + } \ No newline at end of file diff --git a/contrib/grafana-monitoring-mixin/jsonnetfile.lock.json b/contrib/grafana-monitoring-mixin/jsonnetfile.lock.json new file mode 100644 index 0000000..0430b39 --- /dev/null +++ b/contrib/grafana-monitoring-mixin/jsonnetfile.lock.json @@ -0,0 +1,16 @@ +{ + "version": 1, + "dependencies": [ + { + "source": { + "git": { + "remote": "https://github.com/grafana/grafonnet-lib.git", + "subdir": "grafonnet" + } + }, + "version": "3626fc4dc2326931c530861ac5bebe39444f6cbf", + "sum": "gF8foHByYcB25jcUOBqP6jxk0OPifQMjPvKY0HaCk6w=" + } + ], + "legacyImports": false +} diff --git a/contrib/grafana-monitoring-mixin/lib/alerts.jsonnet b/contrib/grafana-monitoring-mixin/lib/alerts.jsonnet new file mode 100644 index 0000000..d396a38 --- /dev/null +++ b/contrib/grafana-monitoring-mixin/lib/alerts.jsonnet @@ -0,0 +1 @@ +std.manifestYamlDoc((import '../mixin.libsonnet').prometheusAlerts) diff --git a/contrib/grafana-monitoring-mixin/lib/dashboards.jsonnet b/contrib/grafana-monitoring-mixin/lib/dashboards.jsonnet new file mode 100644 index 0000000..dadaebe --- /dev/null +++ b/contrib/grafana-monitoring-mixin/lib/dashboards.jsonnet @@ -0,0 +1,6 @@ +local dashboards = (import '../mixin.libsonnet').grafanaDashboards; + +{ + [name]: dashboards[name] + for name in std.objectFields(dashboards) +} diff --git a/contrib/grafana-monitoring-mixin/lib/rules.jsonnet b/contrib/grafana-monitoring-mixin/lib/rules.jsonnet new file mode 100644 index 0000000..2d7fa91 --- /dev/null +++ b/contrib/grafana-monitoring-mixin/lib/rules.jsonnet @@ -0,0 +1 @@ +std.manifestYamlDoc((import '../mixin.libsonnet').prometheusRules) diff --git a/contrib/grafana-monitoring-mixin/mixin.libsonnet b/contrib/grafana-monitoring-mixin/mixin.libsonnet new file mode 100644 index 0000000..bb56a6c --- /dev/null +++ b/contrib/grafana-monitoring-mixin/mixin.libsonnet @@ -0,0 +1,2 @@ +(import 'dashboards/dashboards.libsonnet') + +(import 'config.libsonnet') diff --git a/contrib/options/label/Advanced b/contrib/options/label/Advanced new file mode 100644 index 0000000..e413236 --- /dev/null +++ b/contrib/options/label/Advanced @@ -0,0 +1,26 @@ +#424242 Status: Needs feedback ; Feedback is needed +#fbc02d Status: In progress ; Work is in progress +#43a047 Status: Completed ; Work is complete +#00796b Status: Help wanted ; +#880e4f Status: Blocked ; +#b0bec5 Status: Stale ; + +#d32f2f Kind: Bug ; +#607d8b Kind: Question ; +#9c27b0 Kind: Security ; +#795548 Kind: Testing ; +#c62828 Kind: Breaking ; +#37474f Kind: Documentation ; +#0288d1 Kind: Feature ; +#2e7d32 Kind: Enhancement ; +#f4511e Kind: Maintenance ; + +#546e7a Reviewed: Invalid ; Something was marked as invalid +#616161 Reviewed: Duplicate; Something exists already +#795548 Reviewed: Confirmed ; Something has been confirmed +#eeeeee Reviewed: Wontfix ; Something won't be fixed + +#d32f2f Priority: High ; The priority is high +#e64a19 Priority: Medium ; The priority is medium +#4caf50 Priority: Low ; The priority is low +#b71c1c Priority: Critical ; The priority is critical diff --git a/contrib/sample-page/privacy.html.sample b/contrib/sample-page/privacy.html.sample new file mode 100644 index 0000000..adb3ea7 --- /dev/null +++ b/contrib/sample-page/privacy.html.sample @@ -0,0 +1,196 @@ + + + + Privacy Policy + + + +

Privacy Policy

+ +

Last updated: January 29, 2020

+ +

Who We Are?

+ +

Your Gitea Instance

+ +

What Personal Data We Collect?

+ +

We collect following personal data (collectively called User Personal Information):

+ +
    +
  1. Registration information (username, email, password, etc.)
  2. +
  3. Profile information for your Account (such as your full name, biography, website, gpg key, and location.)
  4. +
  5. Usage information (pages you view, your IP address, referring site, session information, and request date and time.)
  6. +
  7. Device information (its IP address, client application information, language preference, operating system and application version, device type, ID, model and manufacturer.)
  8. +
  9. Git data that you upload to a repository
  10. +
  11. Cookies and Similar Technologies
  12. +
+ +

We may also collect User Personal Information from third-parties (vendors, partners, or affiliates). We don't purchase them from third-party data brokers, though.

+ +

However, we don't intentionally collect sensitive information (such as racial or ethnic origin, political affiliations, religious/philosophical beliefs, biometric data, etc.)

+ + +

How We Share Information We Collect?

+ +

We may share your User Personal Information with third-parties under following circumstances:

+ +

With your Consent

+ +

We share your User Personal Information, if you consent, after letting you know what information will be shared, with whom, and why. For example, if you allow third party applications to access your Account using OAuth2 providers, we share all information associated with your Account, including private repos and organizations. You may also direct us through your action on Your Gitea Instance to share your User Personal Information, such as when joining an Organization.

+ +

With Service Providers

+ +

We share User Personal Information with a limited number of service providers who process it on our behalf to provide or improve our Service, and who have agreed to privacy restrictions similar to the ones in our Privacy Statement by signing data protection agreements or making similar commitments. Our service providers perform payment processing, customer support ticketing, network data transmission, security, and other similar services. While Your Gitea Instance processes all User Personal Information in the (country/state where Gitea is deployed), our service providers may process data outside of (country/state where Gitea is deployed), the United States or the European Union.

+ +

For Security Purposes

+ +

If you are a member of an Organization, Your Gitea Instance may share your username, Usage Information, and Device Information associated with that Organization with an owner and/or administrator of the Organization who has agreed to the Corporate Terms of Service or applicable customer agreements, to the extent that such information is provided only to investigate or respond to a security incident that affects or compromises the security of that particular Organization.

+ +

For Legal Disclosure

+ +

Your Gitea Instance strives for transparency in complying with legal process and legal obligations. Unless prevented from doing so by law or court order, or in rare, exigent circumstances, we make a reasonable effort to notify users of any legally compelled or required disclosure of their information. Your Gitea Instance may disclose User Personal Information or other information we collect about you to law enforcement if required in response to a valid subpoena, court order, search warrant, a similar government order, or when we believe in good faith that disclosure is necessary to comply with our legal obligations, to protect our property or rights, or those of third parties or the public at large.

+ +

Change in Control or Sale

+ +

We may share User Personal Information if we are involved in a merger, sale, or acquisition of corporate entities or business units. If any such change of ownership happens, we will ensure that it is under terms that preserve the confidentiality of User Personal Information, and we will notify you on our Website or by email before any transfer of your User Personal Information. The organization receiving any User Personal Information will have to honor any promises we made in our Privacy Statement or Terms of Service.

+ +

Aggregate, Non-Personally Identifying Information

+ +

We share certain aggregated, non-personally identifying information with others about how our users, collectively, use Your Gitea Instance, or how our users respond to our other offerings, such as our conferences or events. For example, we may compile statistics on the open source activity across Your Gitea Instance.

+ +

We don't sell your User Personal Information for monetary or other consideration.

+ +

How We Use Your Information?

+ +

We may use your information for following purposes:

+ +
    +
  1. We use your Registration Information to create your account, and to provide you the Service.
  2. +
  3. We use your User Personal Information, specifically your username, to identify you on Your Gitea Instance.
  4. +
  5. We use your Profile Information to fill out your Account profile and to share that profile with other users if you ask us to.
  6. +
  7. We use your email address to communicate with you, if you've said that's okay, and only for the reasons you’ve said that’s okay.
  8. +
  9. We use User Personal Information and other data to make recommendations for you, such as to suggest projects you may want to follow or contribute to. We learn from your public behavior on Your Gitea Instance—such as the projects you star—to determine your coding interests, and we recommend similar projects. These recommendations are automated decisions, but they have no legal impact on your rights.
  10. +
  11. We use Usage Information and Device Information to better understand how our Users use Your Gitea Instance and to improve our Website and Service.
  12. +
  13. We may use your User Personal Information if it is necessary for security purposes or to investigate possible fraud or attempts to harm Your Gitea Instance or our Users.
  14. +
  15. We may use your User Personal Information to comply with our legal obligations, protect our intellectual property, and enforce our Terms of Service.
  16. + +
  17. We limit our use of your User Personal Information to the purposes listed in this Privacy Statement. If we need to use your User Personal Information for other purposes, we will ask your permission first. You can always see what information we have, how we're using it, and what permissions you have given us in your user profile.
  18. +
+ +

How Your Gitea Instance Secures Your Information?

+ +

Your Gitea Instance takes all measures reasonably necessary to protect User Personal Information from unauthorized access, alteration, or destruction; maintain data accuracy; and help ensure the appropriate use of User Personal Information.

+ +

To the extent above, we enforce a written security information program, which:

+ +
    +
  • aligns with industry recognized frameworks;
  • +
  • includes security safeguards reasonably designed to protect the confidentiality, integrity, availability, and resilience of our Users' data;
  • +
  • is appropriate to the nature, size, and complexity of Your Gitea Instance’s business operations;
  • +
  • includes incident response and data breach notification processes; and
  • +
  • complies with applicable information security-related laws and regulations in the geographic regions where Your Gitea Instance does business.
  • +
+ +

In the event of a data breach that affects your User Personal Information, we will act promptly to mitigate the impact of a breach and notify any affected Users without undue delay.

+ +

Transmission of data on Your Gitea Instance is encrypted using SSH, HTTPS (TLS), and git repository content is encrypted at rest. We host Your Gitea Instance at our hosting partner, which they provide data centers with high level of physical and network security.

+ +

Disclaimer: No method of transmission, or method of electronic storage, is 100% secure, therefore, we cannot guarantee absolute security.

+ +

Cookies and Tracking Usage

+ +

Cookies

+ +

We uses cookies to make interactions with our service easy and meaningful. Cookies are small text files that websites often store on computer hard drives or mobile devices of visitors. We use cookies (and similar technologies, like HTML5 localStorage) to keep you logged in, remember your preferences, and provide information for future development of Your Gitea Instance. For security purposes, we use cookies to identify a device. By using our Website, you agree that we can place these types of cookies on your computer or device. If you disable your browser or device’s ability to accept these cookies, you will not be able to log in or use our services.

+ +

Tracking and Analytics

+ +

Out of the box, Gitea doesn't use third-party analytics. In case when we opt in to their usage, we do that to help us evaluate our Users' use of Your Gitea Instance, compile statistical reports on activity, and improve our content and Website performance. We only use these third-party analytics providers on certain areas of our Website, and all of them have signed data protection agreements with us that limit the type of User Personal Information they can collect and the purpose for which they can process the information. In addition, we may also deploy internal analytics software to provide similar functionality.

+ +

Some browsers have incorporated "Do Not Track" (DNT) features that can send a signal to the websites you visit indicating you do not wish to be tracked. Your Gitea Instance responds to browser DNT signals and follows the W3C standard for responding to DNT signals. If you have not enabled DNT on a browser that supports it, cookies on some parts of our Website will track your online browsing activity on other online services over time, though we do not permit third parties other than our analytics and service providers to track Your Gitea Instance Users' activity over time on Your Gitea Instance.

+ +

Repository Contents

+ +

Our employees do not access private repositories unless required to for security purposes, for support, to maintain integrity of the Service, or to comply with our legal obligations. While we don't generally search for content in your repositories, we may scan our servers and your content to detect tokens or security signatures, known malwares, or child exploitation imagery.

+ +

If your repository is public, anyone may view its contents. If you include private, confidential or Sensitive Personal Information, such as email addresses or passwords, in your public repository, that information may be indexed by search engines or used by third parties.

+ +

Public Information

+ +

Many of our services and feature are public-facing. If your content is public-facing, third parties may access and use it in compliance with our Terms of Service, such as by viewing your profile or repositories or pulling data via our API. We do not sell that content; it is yours. However, we do allow third parties, such as research organizations or archives, to compile public-facing Your Gitea Instance information. Other third parties, such as data brokers, have been known to scrape Your Gitea Instance and compile data as well.

+ +

Your User Personal Information associated with your content could be gathered by third parties in these compilations of Your Gitea Instance data. If you do not want your User Personal Information to appear in third parties’ compilations of Your Gitea Instance data, please do not make your User Personal Information publicly available and be sure to configure your email address to be private in your user profile and in your git commit settings.

+ +

If you would like to compile Your Gitea Instance data, you must comply with our Terms of Service regarding scraping and privacy, and you may only use any public-facing User Personal Information you gather for the purpose for which our user authorized it. For example, where a Your Gitea Instance user has made an email address public-facing for the purpose of identification and attribution, do not use that email address for commercial advertising. We expect you to reasonably secure any User Personal Information you have gathered from Your Gitea Instance, and to respond promptly to complaints, removal requests, and "do not contact" requests from Your Gitea Instance or Your Gitea Instance users.

+ +

In similar fashion, projects on Your Gitea Instance may include publicly available User Personal Information collected as part of the collaborative events.

+ +

Organizations

+ +

If you collaborate on or become a member of an Organization, then its Account owners may receive your User Personal Information. When you accept an invitation to an Organization, you will be notified of the types of information owners may be able to see. If you accept an invitation to an Organization with a verified domain, then the owners of that Organization will be able to see your full email address(es) within that Organization's verified domain(s).

+ +

Please note, Your Gitea Instance may share your username, Usage Information, and Device Information with the owner of the Organization you are a member of, to the extent that your User Personal Information is provided only to investigate or respond to a security incident that affects or compromises the security of that particular Organization.

+ +

If you collaborate with or become a member of an Account that has agreed to a Data Protection Addendum (DPA) to this Privacy Policy, then that DPA governs in the event of conflicts between this Privacy Policy and DPA with respect to your activity in the Account.

+ +

Please contact the Account owners for more information about how they might process your User Personal Information in their Organization and the ways for you to access, update, alter, or delete the User Personal Information stored in the Account.

+ +

How You Can Access and Control the Information We Collect?

+ +

If you're already a Your Gitea Instance user, you may access, update, alter, or delete your basic user information by editing your user profile. You can control the information we collect about you by limiting what information is in your profile, or by keeping your information current.

+ +

If Your Gitea Instance processes information about you, such as information receives from third parties, and you do not have an account, then you may, subject to applicable law, access, update, alter, delete, or object to the processing of your personal information by contacting our support.

+ +

Data Portability

+ +

As a Your Gitea Instance User, you can always take your data with you. You can clone your repositories to your computer, or you can perform migrations using the provided interfaces, for example.

+ +

Data Retention and Deletion of Data

+ +

In general, Your Gitea Instance retains User Personal Information for as long as your account is active, or as needed to provide you service.

+ +

If you would like to cancel your account or delete your User Personal Information, you may do so in your user profile. We retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile (within reason) within 90 days of your request. Feel free to contact our support to request erasure of the data we process on the basis of consent within 30 days.

+ +

After an account has been deleted, certain data, such as contributions to other Users' repositories and comments in others' issues, will remain. However, we will delete or de-identify your User Personal Information, including your username and email address, from the author field of issues, pull requests, and comments by associating them with a ghost user.

+ +

That said, the email address you have supplied via your Git commit settings will always be associated with your commits in the Git system. If you choose to make your email address private, you should also update your Git commit settings. We are unable to change or delete data in the Git commit history — the Git software is designed to maintain a record — but we do enable you to control what information you put in that record.

+ +

Our Global Privacy Practices

+ +

We store and process the information that we collect in the (country/state where Gitea is deployed) in accordance with this Privacy Statement though our service providers may store and process data outside the (country/state where Gitea is deployed). However, we understand that we have Users from different countries and regions with different privacy expectations, and we try to meet those needs even when the (country/state where Gitea is deployed) does not have the same privacy framework as other countries.

+ +

We provide a high standard of privacy protection—as described in this Privacy Statement—to all our users around the world, regardless of their country of origin or location, and we are proud of the levels of notice, choice, accountability, security, data integrity, access, and recourse we provide. We work hard to comply with the applicable data privacy laws wherever we do business, working with our Data Protection Officer as part of a cross-functional team that oversees our privacy compliance efforts. Additionally, if our vendors or affiliates have access to User Personal Information, they must sign agreements that require them to comply with our privacy policies and with applicable data privacy laws.

+ +

In particular:

+ +
    +
  • Your Gitea Instance provides clear methods of unambiguous, informed, specific, and freely given consent at the time of data collection, when we collect your User Personal Information using consent as a basis.
  • +
  • We collect only the minimum amount of User Personal Information necessary for our purposes, unless you choose to provide more. We encourage you to only give us the amount of data you are comfortable sharing.
  • +
  • We offer you simple methods of accessing, altering, or deleting the User Personal Information we have collected, where legally permitted.
  • +
  • We provide our Users notice, choice, accountability, security, and access regarding their User Personal Information, and we limit the purpose for processing it. We also provide our Users a method of recourse and enforcement. These are the Privacy Shield Principles, but they are also just good practices.
  • +
+ +

How We Communicate with You?

+ +

We use your email address to communicate with you, if you've said that's okay, and only for the reasons you’ve said that’s okay. For example, if you contact our support with a request, we respond to you via email. You have a lot of control over how your email address is used and shared on and through Your Gitea instance. You may manage your communication preferences in your user profile.

+ +

By design, the Git version control system associates many actions with a User's email address, such as commit messages. We are not able to change many aspects of the Git system. If you would like your email address to remain private, even when you’re commenting on public repositories, you can create a private email address in your user profile. You should also update your local Git configuration to use your private email address. This will not change how we contact you, but it will affect how others see you.

+ +

Depending on your email settings, Your Gitea instance may occasionally send notification emails about changes in a repository you’re watching, new features, requests for feedback, important policy changes, or to offer customer support. We also send marketing emails, based on your choices and in accordance with applicable laws and regulations. There's an “unsubscribe” link located at the bottom of each of the marketing emails we send you. Note that you can opt out of any communications with us, except the important ones (like from our support and system emails).

+ +

Our emails may contain a pixel tag, which is a small, clear image that can tell us whether or not you have opened an email and what your IP address is. We use this pixel tag to make our email more effective for you and to make sure we’re not sending you unwanted email.

+ +

Changes to this Privacy Policy

+ +

Although most changes are likely to be minor, Your Gitea Instance may change our Privacy Statement from time to time. We will provide notification to Users of material changes to this Privacy Statement through our Website at least 30 days prior to the change taking effect by posting a notice on our home page or sending email to the primary email address specified in your account.

+ +

Contact

+ +

If you have any concerns about privacy, please contact us at privacy@your-gitea-instance. We will respond promptly, within 45 days.

+ +

COPYING

+ +

This document is licensed under CC0 Public Domain License. See full legal code here.

+ + diff --git a/contrib/sample-page/tos.html.sample b/contrib/sample-page/tos.html.sample new file mode 100644 index 0000000..d390829 --- /dev/null +++ b/contrib/sample-page/tos.html.sample @@ -0,0 +1,245 @@ + + + + Terms of Service + + + +

Terms of Service

+ +

Last updated: January 29, 2020

+ +

Thank you for choosing Your Gitea Instance! Before you use it, please read this Terms of Service agreement carefully, which contains important contract between us and our users.

+ +

Definitions

+ +
    +
  1. An "Account" represents your legal relationship with Your Gitea Instance. A “User Account” represents an individual User’s authorization to log in to and use the Service and serves as a User’s identity on Your Gitea Instance. “Organizations” are shared workspaces that may be associated with a single entity or with one or more Users where multiple Users can collaborate across many projects at once. A User Account can be a member of any number of Organizations.
  2. + +
  3. The "Agreement" collectively refers to all terms, conditions, and notices referenced or contained in this document and other operating rules, policies (including Privacy Policy) and procedures that we may publish from time to time on this Website.
  4. + +
  5. “Content” refers to content featured or displayed through the Website, including without limitation code, text, data, articles, images, photographs, graphics, software, applications, packages, designs, features, and other materials that are available on the Website or otherwise available through the Service. "Content" also includes Services. “User-Generated Content” is Content, written or otherwise, created or uploaded by our Users. "Your Content" is Content that you create or own.
  6. + +
  7. "Your Gitea Instance", "We", and "Us" refers to Your Gitea Instance, as well as our affiliates, directors, subsidiaries, contractors, licensors, officers, agents, and employees.
  8. + +
  9. The "Service" refers to applications/software, products, and services provided by Your Gitea Instance.
  10. + +
  11. The "User", "You", and "Your" refers to individual person or institution (organizations or company) that has visited or using the Service; that have access or use any part of the Account; or that directs to use the Account to perform its function. Please note that additional terms may apply for Accounts related to business or government.
  12. + +
  13. The "Website" refers to Your Gitea Instance's website at your-gitea-instance, including its subdomains and other websites owned by Your Gitea Instance.
  14. +
+ +

Account Terms

+ +

Account Controls

+ +
    +
  • Users: Subject to these Terms, you retain ultimate administrative control over your User Account and the Content within it.
  • + +
  • Organizations. The "owner" of an Organization that was created under these Terms has ultimate administrative control over that Organization and the Content within it. Within the Service, an owner can manage User access to the Organization’s data and projects. An Organization may have multiple owners, but there must be at least one User Account designated as an owner of an Organization. If you are the owner of an Organization under these Terms, we consider you responsible for the actions that are performed on or through that Organization.
  • +
+ +

Required Information

+ +

You must provide a valid email address in order to complete the signup process. Any other information requested, such as your real name, is optional, unless you are accepting these terms on behalf of a legal entity (in which case we need more information about the legal entity).

+ +

Account Requirements

+ +
    +
  • You must be a human to create an Account. Accounts registered by "bots" or other automated methods are not permitted. We do permit machine accounts:
  • +
  • A machine account is an Account set up by an individual human who accepts the Terms on behalf of the Account, provides a valid email address, and is responsible for its actions. A machine account is used exclusively for performing automated tasks. Multiple users may direct the actions of a machine account, but the owner of the Account is ultimately responsible for the machine's actions.
  • +
  • You must be age 13 or older. If we learn of any User under that age, we will immediately terminate that User's Account. Different countries may have different minimum age; in such cases you are responsible for complying with your country's regulation. By using Your Gitea Instance, you agree to comply with COPPA and/or similar law in your country.
  • +
+ +

User Account Security

+ +

You are responsible for keeping your Account secure while you use our Service. We offer tools such as two-factor authentication to help you maintain your Account's security, but the content of your Account and its security are up to you.

+ +

Additional Terms

+ +

In some situations, third parties' terms may apply to your use of Your Gitea Instance. For example, you may be a member of an organization on Your Gitea Instance with its own terms or license agreements; you may download an application that integrates with Your Gitea Instance; or you may use Your Gitea Instance to authenticate to another service. Please be aware that while these Terms are our full agreement with you, other parties' terms govern their relationships with you.

+ +

Acceptable Use

+ +

Your use of the Website and Service must not violate any applicable laws, including copyright or trademark laws, export control or sanctions laws, or other laws in your jurisdiction. You are responsible for making sure that your use of the Service is in compliance with laws and any applicable regulations.

+ +

User-Generated Content

+ +
    +
  1. You may create or upload User-Generated Content while using the Service. You are solely responsible for the content of, and for any harm resulting from, any User-Generated Content that you post, upload, link to or otherwise make available via the Service, regardless of the form of that Content. We are not responsible for any public display or misuse of your User-Generated Content.
  2. + +
  3. We do not pre-screen User-Generated Content, but we have the right (though not the obligation) to refuse or remove any User-Generated Content that, in our sole discretion, violates any our terms or policies.
  4. + +
  5. +

    You retain ownership of and responsibility for Your Content. If you're posting anything you did not create yourself or do not own the rights to, you agree that you are responsible for any Content you post; that you will only submit Content that you have the right to post; and that you will fully comply with any third party licenses relating to Content you post.

    + +

    Because of above, we need you to grant us -- and other Your Gitea Instance users -- certain legal permissions, listed below in this section. If you upload Content that already comes with a license granting Your Gitea Instance the permissions we need to run our Service, no additional license is required. You understand that you will not receive any payment for any of the rights granted below. The licenses you grant to us will end when you remove Your Content from our servers, unless other Users have forked it.

    +
  6. + +
  7. +

    We need the legal right to do things like host Your Content, publish it, and share it. You grant us and our legal successors the right to store, parse, and display Your Content, and make incidental copies as necessary to render the Website and provide the Service. This includes the right to do things like copy it to our database and make backups; show it to you and other users; parse it into a search index or otherwise analyze it on our servers; share it with other users; and perform it, in case Your Content is something like music or video.

    + +

    This license, however, doesn't grant Your Gitea Instance the right to sell Your Content or otherwise distribute or use it outside of our provision of the Service.

    +
  8. + +
  9. +

    Any User-Generated Content you post publicly, including issues, comments, and contributions to other Users' repositories, may be viewed by others. By setting your repositories to be viewed publicly, you agree to allow others to view and "fork" your repositories (this means that others may make their own copies of Content from your repositories in repositories they control).

    + +

    If you set your pages and repositories to be viewed publicly, you grant each User of Your Gitea Instance a nonexclusive, worldwide license to use, display, and perform Your Content through the Your Gitea Instance Service and to reproduce Your Content solely on Your Gitea Instance as permitted through Your Gitea Instance's functionality (for example, through forking). You may grant further rights if you adopt a license. If you are uploading Content you did not create or own, you are responsible for ensuring that the Content you upload is licensed under terms that grant these permissions to other Your Gitea Instance Users.

    +
  10. + +
  11. +

    Whenever you make a contribution to a repository containing notice of a license, you license your contribution under the same terms, and you agree that you have the right to license your contribution under those terms. If you have a separate agreement to license your contributions under different terms, such as a contributor license agreement, that agreement will supersede.

    + +

    Isn't this just how it works already? Yep. This is widely accepted as the norm in the open-source community; it's commonly referred to by the shorthand "inbound=outbound". We're just making it explicit.

    +
  12. + +
  13. +

    You retain all moral rights to Your Content that you upload, publish, or submit to any part of the Service, including the rights of integrity and attribution. However, you waive these rights and agree not to assert them against us, to enable us to reasonably exercise the rights granted above, but not otherwise.

    + +

    To the extent this agreement is not enforceable by applicable law, you grant Your Gitea Instance the rights we need to use Your Content without attribution and to make reasonable adaptations of Your Content as necessary to render the Website and provide the Service.

    +
  14. +
+ +

Private Repositories

+ +
    +
  1. Some Accounts may have private repositories, which allow the User to control access to Content.
  2. + +
  3. Your Gitea Instance considers the contents of private repositories to be confidential to you. Your Gitea Instance will protect the contents of private repositories from unauthorized use, access, or disclosure in the same manner that we would use to protect our own confidential information of a similar nature and in no event with less than a reasonable degree of care.
  4. + +
  5. +

    Your Gitea Instance employees may only access the content of your private repositories in the following situations:

    + +
      +
    • With your consent and knowledge, for support reasons. If Your Gitea Instance accesses a private repository for support reasons, we will only do so with the owner’s consent and knowledge.
    • +
    • When access is required for security reasons, including when access is required to maintain ongoing confidentiality, integrity, availability and resilience of Your Gitea Instance's systems and Service.
    • +
    +
  6. + +
  7. You may choose to enable additional access to your private repositories. For example: You may enable various Your Gitea Instance services or features that require additional rights to Your Content in private repositories. These rights may vary depending on the service or feature, but Your Gitea Instance will continue to treat your private repository Content as confidential. If those services or features require rights in addition to those we need to provide the Your Gitea Instance Service, we will provide an explanation of those rights.
  8. +
+ +

Copyright Infringement and DMCA Policy

+ +

If you are copyright owner and believe that content on our website violates your copyright, please contact us at copyright@your-gitea-instance. Please note that before sending a takedown notice, consider legal uses (such as fair use and licensed use); and legal consequences for sending false notices.

+ +

Intellectual Properties and COPYING

+ +

Your Gitea Instance and our licensors, vendors, agents, and/or our content providers retain ownership of all intellectual property rights of any kind related to the Website and Service. We reserve all rights that are not expressly granted to you under this Agreement or by law. The look and feel of the Website and Service is copyright © Your Gitea Instance. All rights reserved.

+ +

If you'd like to use our trademarks, you must follow all of our trademark guidelines.

+ +

This Agreement is licensed under CCO Public Domain License.

+ +

API Terms

+ +

Abuse or excessively frequent requests to Your Gitea Instance via the API may result in the temporary or permanent suspension of your Account's access to the API. Your Gitea Instance, in our sole discretion, will determine abuse or excessive usage of the API. We will make a reasonable attempt to warn you via email prior to suspension.

+ +

You may not share API tokens to exceed Your Gitea Instance's rate limitations.

+ +

You may not use the API to download data or Content from Your Gitea Instance for spamming purposes, including for the purposes of selling Your Gitea Instance users' personal information, such as to recruiters, headhunters, and job boards.

+ +

All use of the Your Gitea Instance API is subject to these Terms of Service and the Your Gitea Instance Privacy Statement.

+ +

However, we may provide subscription-based access to our API for Users who need high-throughput access or reselling our Service.

+ + +

Cancellation and Termination

+ +

Account Cancellation

+ +

It is your responsibility to properly cancel your Account with Your Gitea Instance. You can cancel your Account at any time by going into your Settings in the global navigation bar at the top of the screen. The Account screen provides a simple, no questions asked cancellation link. We are not able to cancel Accounts in response to an email or phone request.

+ +

Upon Cancellation

+ +

We will retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile and the Content of your repositories within 90 days of cancellation or termination (though some information may remain in encrypted backups). This information can not be recovered once your Account is cancelled.

+ +

We will not delete Content that you have contributed to other Users' repositories or that other Users have forked.

+ +

Upon request, we will make a reasonable effort to provide an Account owner with a copy of your lawful, non-infringing Account contents after Account cancellation or termination. You must make this request within 90 days of cancellation or termination.

+ +

We May Terminate

+ +

Your Gitea Instance has the right to suspend or terminate your access to all or any part of the Website at any time, with or without cause, with or without notice, effective immediately. Your Gitea Instance reserves the right to refuse service to anyone for any reason at any time.

+ +

Survival

+ +

All provisions of this Agreement which, by their nature, should survive termination will survive termination — including, without limitation: ownership provisions, warranty disclaimers, indemnity, and limitations of liability.

+ +

Communications with Us

+ +

Electronic Communications Required

+ +

For contractual purposes, you (1) consent to receive communications from us in an electronic form via the email address you have submitted or via the Service; and (2) agree that all Terms of Service, agreements, notices, disclosures, and other communications that we provide to you electronically satisfy any legal requirement that those communications would satisfy if they were on paper. This section does not affect your non-waivable rights.

+ +

Legal Notices to Us Must Be in Writing

+ +

Communications made through email or Your Gitea Instance Support's messaging system will not constitute legal notice to Your Gitea Instance or any of its officers, employees, agents or representatives in any situation where notice to Your Gitea Instance is required by contract or any law or regulation. Legal notice to Your Gitea Instance must be in writing and served on Your Gitea Instance's legal agent.

+ +

No Phone Support

+ +

We only offer support via email, in-Service communications, and electronic messages. We do not offer telephone support.

+ +

Disclaimer of Warranties

+ +

Your Gitea Instance provides the Website and the Service “as is” and “as available,” without warranty of any kind. Without limiting this, we expressly disclaim all warranties, whether express, implied or statutory, regarding the Website and the Service including without limitation any warranty of merchantability, fitness for a particular purpose, title, security, accuracy and non-infringement.

+ +

Your Gitea Instance does not warrant that the Service will meet your requirements; that the Service will be uninterrupted, timely, secure, or error-free; that the information provided through the Service is accurate, reliable or correct; that any defects or errors will be corrected; that the Service will be available at any particular time or location; or that the Service is free of viruses or other harmful components. You assume full responsibility and risk of loss resulting from your downloading and/or use of files, information, content or other material obtained from the Service.

+ +

Limitation of Liability

+ +

You understand and agree that we will not be liable to you or any third party for any loss of profits, use, goodwill, or data, or for any incidental, indirect, special, consequential or exemplary damages, however arising, that result from:

+ +
    +
  • the use, disclosure, or display of your User-Generated Content;
  • + +
  • your use or inability to use the Service;
  • + +
  • any modification, price change, suspension or discontinuance of the Service;
  • + +
  • the Service generally or the software or systems that make the Service available;
  • + +
  • unauthorized access to or alterations of your transmissions or data;
  • + +
  • statements or conduct of any third party on the Service;
  • + +
  • any other user interactions that you input or receive through your use of the Service; or + any other matter relating to the Service.
  • +
+ +

Our liability is limited whether or not we have been informed of the possibility of such damages, and even if a remedy set forth in this Agreement is found to have failed of its essential purpose. We will have no liability for any failure or delay due to matters beyond our reasonable control.

+ +

Release and Indemnification

+ +

If you have a dispute with one or more Users, you agree to release Your Gitea Instance from any and all claims, demands and damages (actual and consequential) of every kind and nature, known and unknown, arising out of or in any way connected with such disputes.

+ +

You agree to indemnify us, defend us, and hold us harmless from and against any and all claims, liabilities, and expenses, including attorneys’ fees, arising out of your use of the Website and the Service, including but not limited to your violation of this Agreement, provided that Your Gitea Instance (1) promptly gives you written notice of the claim, demand, suit or proceeding; (2) gives you sole control of the defense and settlement of the claim, demand, suit or proceeding (provided that you may not settle any claim, demand, suit or proceeding unless the settlement unconditionally releases Your Gitea Instance of all liability); and (3) provides to you all reasonable assistance, at your expense.

+ +

Changes to These Terms

+ +

We reserve the right, at our sole discretion, to amend these Terms of Service at any time and will update these Terms of Service in the event of any such amendments. We will notify our Users of material changes to this Agreement, such as price changes, at least 30 days prior to the change taking effect by posting a notice on our Website. For non-material modifications, your continued use of the Website constitutes agreement to our revisions of these Terms of Service.

+ +

Miscellaneous

+ +

Governing Law

+ +

Except to the extent applicable law provides otherwise, this Agreement between you and us and any access to or use of the Website or the Service are governed by (national laws of country/state where Gitea is deployed) and (regional laws of locality where Gitea is deployed), without regard to conflict of law provisions. You and Your Gitea Instance agree to submit to the exclusive jurisdiction and venue of the courts located in (locality where Gitea is deployed).

+ +

Non-Assignability

+ +

Your Gitea Instance may assign or delegate these Terms of Service and/or our Privacy Policy in whole or in part, to any person or entity at any time with or without your consent, including the license granted in User-Generated Content. You may not assign or delegate any rights or obligations under the Terms of Service or Privacy Statement without our prior written consent, and any unauthorized assignment and delegation by you is void.

+ +

Severablity, No Waiver, and Survival

+ +

If any part of this Agreement is held invalid or unenforceable, that portion of the Agreement will be construed to reflect the parties’ original intent. The remaining portions will remain in full force and effect. Any failure on the part of Your Gitea Instance to enforce any provision of this Agreement will not be considered a waiver of our right to enforce such provision. Our rights under this Agreement will survive any termination of this Agreement.

+ +

Amendments and Complete Agreement

+ +

This Agreement may only be modified by a written amendment signed by an authorized representative of Your Gitea Instance, or by the posting by Your Gitea Instance of a revised version in accordance with Changes to These Terms. These Terms of Service, together with the Your Gitea Instance Privacy Policy, represent the complete and exclusive statement of the agreement between you and us. This Agreement supersedes any proposal or prior agreement oral or written, and any other communications between you and Your Gitea Instance relating to the subject matter of these terms including any confidentiality or nondisclosure agreements.

+ +

Contact

+ +

If you have questions about these Terms of Service, you can contact our support.

+ + diff --git a/contrib/service/freebsd/gitea b/contrib/service/freebsd/gitea new file mode 100644 index 0000000..2c034ce --- /dev/null +++ b/contrib/service/freebsd/gitea @@ -0,0 +1,48 @@ +#!/bin/sh +# +# $FreeBSD$ +# +# PROVIDE: gitea +# REQUIRE: NETWORKING SYSLOG +# KEYWORD: shutdown +# +# Add the following lines to /etc/rc.conf to enable gitea: +# +#gitea_enable="YES" + +. /etc/rc.subr + +name="gitea" +rcvar="gitea_enable" + +load_rc_config $name + +: ${gitea_user:="git"} +: ${gitea_enable:="NO"} +: ${gitea_directory:="/var/lib/gitea"} + +command="/usr/local/bin/gitea web -c /etc/gitea/app.ini" +procname="$(echo $command |cut -d' ' -f1)" + +pidfile="${gitea_directory}/${name}.pid" + +start_cmd="${name}_start" +stop_cmd="${name}_stop" + +gitea_start() { + cd ${gitea_directory} + export USER=${gitea_user} + export HOME=/usr/home/${gitea_user} + export GITEA_WORK_DIR=${gitea_directory} + /usr/sbin/daemon -f -u ${gitea_user} -p ${pidfile} $command +} + +gitea_stop() { + if [ ! -f $pidfile ]; then + echo "GITEA PID File not found. Maybe GITEA is not running?" + else + kill $(cat $pidfile) + fi +} + +run_rc_command "$1" diff --git a/contrib/service/gentoo/gitea b/contrib/service/gentoo/gitea new file mode 100644 index 0000000..db904e7 --- /dev/null +++ b/contrib/service/gentoo/gitea @@ -0,0 +1,44 @@ +#!/sbin/openrc-run + +DIR=/var/lib/gitea +USER=git +HOME=/home/${USER} +GITEA_WORK_DIR=${DIR} +EXECUTABLE=/usr/local/bin/gitea + +export USER +export HOME +export GITEA_WORK_DIR + +name=$RC_SVCNAME +cfgfile="/etc/$RC_SVCNAME/app.ini" +command="${EXECUTABLE}" +command_user="${USER}" +command_args="web -c /etc/$RC_SVCNAME/app.ini" +command_background="yes" +pidfile="/run/$RC_SVCNAME/$RC_SVCNAME.pid" +start_stop_daemon_args="--user ${USER} --chdir ${DIR}" + +depend() +{ + need net + ### + # Don't forget to add the database service requirements + ### + #after postgresql + #after mysql + #after mariadb + #after memcached + #after redis +} + +start_pre() +{ + checkpath --directory --owner $command_user:$command_user --mode 0750 \ + /run/$RC_SVCNAME /var/log/$RC_SVCNAME + ## + # If you want to bind Gitea to a port below 1024, uncomment + # the value below + ## + #setcap cap_net_bind_service=+ep "${EXECUTABLE}" +} diff --git a/contrib/service/launchd/io.gitea.web.plist b/contrib/service/launchd/io.gitea.web.plist new file mode 100644 index 0000000..43ec612 --- /dev/null +++ b/contrib/service/launchd/io.gitea.web.plist @@ -0,0 +1,39 @@ + + + + + Label + io.gitea.web + + + UserName + git + GroupName + git + ProgramArguments + + + + /Users/git/gitea/gitea + web + + RunAtLoad + + KeepAlive + + + + WorkingDirectory + /Users/git/gitea/ + StandardOutPath + /Users/git/gitea/log/stdout.log + StandardErrorPath + /Users/git/gitea/log/stderr.log + + SoftResourceLimits + + NumberOfFiles + 8192 + + + diff --git a/contrib/service/openbsd/gitea b/contrib/service/openbsd/gitea new file mode 100755 index 0000000..89d4f68 --- /dev/null +++ b/contrib/service/openbsd/gitea @@ -0,0 +1,19 @@ +#!/bin/sh +# +# $OpenBSD$ + +daemon="/usr/local/bin/gitea" +daemon_user="git" +daemon_flags="web -c /etc/gitea/app.ini" + +gitea_directory="/var/lib/gitea" + +rc_bg=YES + +. /etc/rc.d/rc.subr + +rc_start() { + ${rcexec} "cd ${gitea_directory}; ${daemon} ${daemon_flags} ${_bg}" +} + +rc_cmd $1 diff --git a/contrib/service/openwrt/gitea b/contrib/service/openwrt/gitea new file mode 100644 index 0000000..67b4495 --- /dev/null +++ b/contrib/service/openwrt/gitea @@ -0,0 +1,35 @@ +#!/bin/sh /etc/rc.common + +USE_PROCD=1 + +# PROCD_DEBUG=1 + +START=90 +STOP=10 + +PROG=/opt/gitea/gitea +GITEA_WORK_DIR=/opt/gitea +CONF_FILE=$GITEA_WORK_DIR/app.ini + +start_service(){ + procd_open_instance gitea + procd_set_param env GITEA_WORK_DIR=$GITEA_WORK_DIR + procd_set_param env HOME=$GITEA_WORK_DIR + procd_set_param command $PROG web -c $CONF_FILE + procd_set_param file $CONF_FILE + procd_set_param user git + procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5} # respawn automatically if something died, be careful if you have an alternative process supervisor + procd_close_instance +} + +start(){ + service_start $PROG +} + +stop(){ + service_stop $PROG +} + +reload(){ + service_reload $PROG +} diff --git a/contrib/service/sunos/gitea.xml b/contrib/service/sunos/gitea.xml new file mode 100644 index 0000000..4b8cc3a --- /dev/null +++ b/contrib/service/sunos/gitea.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/service/supervisor/gitea b/contrib/service/supervisor/gitea new file mode 100644 index 0000000..a8a908d --- /dev/null +++ b/contrib/service/supervisor/gitea @@ -0,0 +1,16 @@ +[program:gitea] +directory=/home/git/go/src/github.com/go-gitea/gitea/ +command=/home/git/go/src/github.com/go-gitea/gitea/gitea web +autostart=true +autorestart=true +startsecs=10 +stdout_logfile=/var/log/gitea/stdout.log +stdout_logfile_maxbytes=1MB +stdout_logfile_backups=10 +stdout_capture_maxbytes=1MB +stderr_logfile=/var/log/gitea/stderr.log +stderr_logfile_maxbytes=1MB +stderr_logfile_backups=10 +stderr_capture_maxbytes=1MB +user = git +environment = HOME="/home/git", USER="git" \ No newline at end of file diff --git a/contrib/service/systemd/gitea.service b/contrib/service/systemd/gitea.service new file mode 100644 index 0000000..c091722 --- /dev/null +++ b/contrib/service/systemd/gitea.service @@ -0,0 +1,85 @@ +[Unit] +Description=Gitea (Git with a cup of tea) +After=network.target +### +# Don't forget to add the database service dependencies +### +# +#Wants=mysql.service +#After=mysql.service +# +#Wants=mariadb.service +#After=mariadb.service +# +#Wants=postgresql.service +#After=postgresql.service +# +#Wants=memcached.service +#After=memcached.service +# +#Wants=redis.service +#After=redis.service +# +### +# If using socket activation for main http/s +### +# +#After=gitea.main.socket +#Requires=gitea.main.socket +# +### +# (You can also provide gitea an http fallback and/or ssh socket too) +# +# An example of /etc/systemd/system/gitea.main.socket +### +## +## [Unit] +## Description=Gitea Web Socket +## PartOf=gitea.service +## +## [Socket] +## Service=gitea.service +## ListenStream= +## NoDelay=true +## +## [Install] +## WantedBy=sockets.target +## +### + +[Service] +# Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that +# LimitNOFILE=524288:524288 +RestartSec=2s +Type=simple +User=git +Group=git +WorkingDirectory=/var/lib/gitea/ +# If using Unix socket: tells systemd to create the /run/gitea folder, which will contain the gitea.sock file +# (manually creating /run/gitea doesn't work, because it would not persist across reboots) +#RuntimeDirectory=gitea +ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini +Restart=always +Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea +# If you install Git to directory prefix other than default PATH (which happens +# for example if you install other versions of Git side-to-side with +# distribution version), uncomment below line and add that prefix to PATH +# Don't forget to place git-lfs binary on the PATH below if you want to enable +# Git LFS support +#Environment=PATH=/path/to/git/bin:/bin:/sbin:/usr/bin:/usr/sbin +# If you want to bind Gitea to a port below 1024, uncomment +# the two values below, or use socket activation to pass Gitea its ports as above +### +#CapabilityBoundingSet=CAP_NET_BIND_SERVICE +#AmbientCapabilities=CAP_NET_BIND_SERVICE +### +# In some cases, when using CapabilityBoundingSet and AmbientCapabilities option, you may want to +# set the following value to false to allow capabilities to be applied on gitea process. The following +# value if set to true sandboxes gitea service and prevent any processes from running with privileges +# in the host user namespace. +### +#PrivateUsers=false +### + +[Install] +WantedBy=multi-user.target diff --git a/contrib/service/sysvinit/gitea b/contrib/service/sysvinit/gitea new file mode 100644 index 0000000..1508da7 --- /dev/null +++ b/contrib/service/sysvinit/gitea @@ -0,0 +1,89 @@ +#!/bin/bash +### BEGIN INIT INFO +# Provides: gitea +# Required-Start: $syslog $network +# Required-Stop: $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: A self-hosted Git service written in Go. +# Description: A self-hosted Git service written in Go. +### END INIT INFO + +# This is a System V Init (init.d) startup script for legacy Linux distributions + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin +DESC="Gitea - Git with a cup of tea" +NAME=gitea +SERVICEVERBOSE=yes +PIDFILE=/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME +WORKINGDIR=/var/lib/$NAME +DAEMON=/usr/local/bin/$NAME +DAEMON_ARGS="web -c /etc/$NAME/app.ini" +USER=git +STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/1/KILL/5}" + +# If you want to bind Gitea to a port below 1024, apply "setcap" to the gitea binary +#setcap cap_net_bind_service=+ep $DAEMON" + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +do_start() +{ + GITEA_ENVS="USER=$USER GITEA_WORK_DIR=$WORKINGDIR HOME=/home/$USER" + GITEA_EXEC="$DAEMON -- $DAEMON_ARGS" + sh -c "start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\ + --background --chdir $WORKINGDIR --chuid $USER \\ + --exec /bin/bash -- -c '/usr/bin/env $GITEA_ENVS $GITEA_EXEC'" +} + +do_stop() +{ + start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PIDFILE --name $NAME --oknodo + rm -f $PIDFILE +} + +do_status() +{ + if [ -f $PIDFILE ]; then + if kill -0 $(cat "$PIDFILE"); then + echo "$NAME is running, PID is $(cat $PIDFILE)" + else + echo "$NAME process is dead, but pidfile exists" + fi + else + echo "$NAME is not running" + fi +} + +case "$1" in + start) + echo "Starting $DESC" "$NAME" + do_start + ;; + stop) + echo "Stopping $DESC" "$NAME" + do_stop + ;; + status) + do_status + ;; + restart) + echo "Restarting $DESC" "$NAME" + do_stop + do_start + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2 + exit 2 + ;; +esac + +exit 0 diff --git a/contrib/upgrade.sh b/contrib/upgrade.sh new file mode 100755 index 0000000..71d0435 --- /dev/null +++ b/contrib/upgrade.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +# This is an update script for gitea installed via the binary distribution +# from dl.gitea.com on linux as systemd service. It performs a backup and updates +# Gitea in place. +# NOTE: This adds the GPG Signing Key of the Gitea maintainers to the keyring. +# Depends on: bash, curl, xz, sha256sum. optionally jq, gpg +# See section below for available environment vars. +# When no version is specified, updates to the latest release. +# Examples: +# upgrade.sh 1.15.10 +# giteahome=/opt/gitea giteaconf=$giteahome/app.ini upgrade.sh + +# Check if gitea service is running +if ! pidof gitea &> /dev/null; then + echo "Error: gitea is not running." + exit 1 +fi + +# Continue with rest of the script if gitea is running +echo "Gitea is running. Continuing with rest of script..." + +# apply variables from environment +: "${giteabin:="/usr/local/bin/gitea"}" +: "${giteahome:="/var/lib/gitea"}" +: "${giteaconf:="/etc/gitea/app.ini"}" +: "${giteauser:="git"}" +: "${sudocmd:="sudo"}" +: "${arch:="linux-amd64"}" +: "${service_start:="$sudocmd systemctl start gitea"}" +: "${service_stop:="$sudocmd systemctl stop gitea"}" +: "${service_status:="$sudocmd systemctl status gitea"}" +: "${backupopts:=""}" # see `gitea dump --help` for available options + +function giteacmd { + if [[ $sudocmd = "su" ]]; then + # `-c` only accept one string as argument. + "$sudocmd" - "$giteauser" -c "$(printf "%q " "$giteabin" "--config" "$giteaconf" "--work-path" "$giteahome" "$@")" + else + "$sudocmd" --user "$giteauser" "$giteabin" --config "$giteaconf" --work-path "$giteahome" "$@" + fi +} + +function require { + for exe in "$@"; do + command -v "$exe" &>/dev/null || (echo "missing dependency '$exe'"; exit 1) + done +} + +# parse command line arguments +while true; do + case "$1" in + -v | --version ) giteaversion="$2"; shift 2 ;; + -y | --yes ) no_confirm="yes"; shift ;; + --ignore-gpg) ignore_gpg="yes"; shift ;; + "" | -- ) shift; break ;; + * ) echo "Usage: [] upgrade.sh [-v ] [-y] [--ignore-gpg]"; exit 1;; + esac +done + +# exit once any command fails. this means that each step should be idempotent! +set -euo pipefail + +if [[ -f /etc/os-release ]]; then + os_release=$(cat /etc/os-release) + + if [[ "$os_release" =~ "OpenWrt" ]]; then + sudocmd="su" + service_start="/etc/init.d/gitea start" + service_stop="/etc/init.d/gitea stop" + service_status="/etc/init.d/gitea status" + else + require systemctl + fi +fi + +require curl xz sha256sum "$sudocmd" + +# select version to install +if [[ -z "${giteaversion:-}" ]]; then + require jq + giteaversion=$(curl --connect-timeout 10 -sL https://dl.gitea.com/gitea/version.json | jq -r .latest.version) + echo "Latest available version is $giteaversion" +fi + +# confirm update +echo "Checking currently installed version..." +current=$(giteacmd --version | cut -d ' ' -f 3) +[[ "$current" == "$giteaversion" ]] && echo "$current is already installed, stopping." && exit 0 +if [[ -z "${no_confirm:-}" ]]; then + echo "Make sure to read the changelog first: https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md" + echo "Are you ready to update Gitea from ${current} to ${giteaversion}? (y/N)" + read -r confirm + [[ "$confirm" == "y" ]] || [[ "$confirm" == "Y" ]] || exit 1 +fi + +echo "Upgrading gitea from $current to $giteaversion ..." + +pushd "$(pwd)" &>/dev/null +cd "$giteahome" # needed for gitea dump later + +# download new binary +binname="gitea-${giteaversion}-${arch}" +binurl="https://dl.gitea.com/gitea/${giteaversion}/${binname}.xz" +echo "Downloading $binurl..." +curl --connect-timeout 10 --silent --show-error --fail --location -O "$binurl{,.sha256,.asc}" + +# validate checksum & gpg signature +sha256sum -c "${binname}.xz.sha256" +if [[ -z "${ignore_gpg:-}" ]]; then + require gpg + # try to use curl first, it uses standard tcp 443 port and works better behind strict firewall rules + curl -fsSL --connect-timeout 10 "https://keys.openpgp.org/vks/v1/by-fingerprint/7C9E68152594688862D62AF62D9AE806EC1592E2" | gpg --import \ + || gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2 + gpg --verify "${binname}.xz.asc" "${binname}.xz" || { echo 'Signature does not match'; exit 1; } +fi +rm "${binname}".xz.{sha256,asc} + +# unpack binary + make executable +xz --decompress --force "${binname}.xz" +chown "$giteauser" "$binname" +chmod +x "$binname" + +# stop gitea, create backup, replace binary, restart gitea +echo "Flushing gitea queues at $(date)" +giteacmd manager flush-queues +echo "Stopping gitea at $(date)" +$service_stop +echo "Creating backup in $giteahome" +# shellcheck disable=SC2086 # flag string +giteacmd dump $backupopts +echo "Updating binary at $giteabin" +cp -f "$giteabin" "$giteabin.bak" && mv -f "$binname" "$giteabin" +# Restore SELinux context if applicable (e.g. RHEL/Fedora) +command -v restorecon &>/dev/null && restorecon -v "$giteabin" || true +$service_start +$service_status + +echo "Upgrade to $giteaversion successful!" + +popd diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 0000000..d14d484 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,12 @@ +project_id_env: CROWDIN_PROJECT_ID +api_token_env: CROWDIN_KEY +base_path: "." +base_url: "https://api.crowdin.com" +preserve_hierarchy: true +files: + - source: "/options/locale/locale_en-US.json" + translation: "/options/locale/locale_%locale%.json" + type: "json" + skip_untranslated_strings: true + export_only_approved: true + update_option: "update_as_unapproved" diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..b014f42 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,7 @@ +# Gitea - Docker + +Dockerfile is found in the root of the repository. + +Docker image can be found on [docker hub](https://hub.docker.com/r/gitea/gitea). + +Documentation on using docker image can be found on [Gitea Docs site](https://docs.gitea.com/installation/install-with-docker-rootless). diff --git a/docker/manifest.rootless.tmpl b/docker/manifest.rootless.tmpl new file mode 100644 index 0000000..3fa94ab --- /dev/null +++ b/docker/manifest.rootless.tmpl @@ -0,0 +1,29 @@ +image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-rootless +{{#if build.tags}} +{{#unless (contains "-rc" build.tag)}} +{{#unless (contains "-dev" build.tag)}} +tags: +{{#each build.tags}} + - {{this}}-rootless +{{/each}} + - "latest-rootless" +{{/unless}} +{{/unless}} +{{/if}} +manifests: + - + image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-amd64-rootless + platform: + architecture: amd64 + os: linux + - + image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-arm64-rootless + platform: + architecture: arm64 + os: linux + variant: v8 + - + image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-riscv64-rootless + platform: + architecture: riscv64 + os: linux diff --git a/docker/manifest.tmpl b/docker/manifest.tmpl new file mode 100644 index 0000000..c68ca46 --- /dev/null +++ b/docker/manifest.tmpl @@ -0,0 +1,29 @@ +image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}} +{{#if build.tags}} +{{#unless (contains "-rc" build.tag)}} +{{#unless (contains "-dev" build.tag)}} +tags: +{{#each build.tags}} + - {{this}} +{{/each}} + - "latest" +{{/unless}} +{{/unless}} +{{/if}} +manifests: + - + image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-amd64 + platform: + architecture: amd64 + os: linux + - + image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-arm64 + platform: + architecture: arm64 + os: linux + variant: v8 + - + image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}nightly{{/if}}-linux-riscv64 + platform: + architecture: riscv64 + os: linux diff --git a/docker/root/etc/nsswitch.conf b/docker/root/etc/nsswitch.conf new file mode 100644 index 0000000..25fad99 --- /dev/null +++ b/docker/root/etc/nsswitch.conf @@ -0,0 +1,15 @@ +# /etc/nsswitch.conf + +passwd: compat +group: compat +shadow: compat + +hosts: files dns +networks: files + +protocols: db files +services: db files +ethers: db files +rpc: db files + +netgroup: nis diff --git a/docker/root/etc/s6/.s6-svscan/finish b/docker/root/etc/s6/.s6-svscan/finish new file mode 100755 index 0000000..06bd986 --- /dev/null +++ b/docker/root/etc/s6/.s6-svscan/finish @@ -0,0 +1,2 @@ +#!/bin/bash +exit 0 diff --git a/docker/root/etc/s6/gitea/finish b/docker/root/etc/s6/gitea/finish new file mode 100755 index 0000000..5d44f41 --- /dev/null +++ b/docker/root/etc/s6/gitea/finish @@ -0,0 +1,2 @@ +#!/bin/bash +s6-svscanctl -t /etc/s6/ diff --git a/docker/root/etc/s6/gitea/run b/docker/root/etc/s6/gitea/run new file mode 100755 index 0000000..7b85835 --- /dev/null +++ b/docker/root/etc/s6/gitea/run @@ -0,0 +1,6 @@ +#!/bin/bash +[[ -f ./setup ]] && source ./setup + +pushd /app/gitea >/dev/null +exec su-exec $USER /usr/local/bin/gitea web +popd diff --git a/docker/root/etc/s6/gitea/setup b/docker/root/etc/s6/gitea/setup new file mode 100755 index 0000000..b801ef4 --- /dev/null +++ b/docker/root/etc/s6/gitea/setup @@ -0,0 +1,66 @@ +#!/bin/bash + +if [ ! -d /data/git/.ssh ]; then + mkdir -p /data/git/.ssh +fi + +# Set the correct permissions on the .ssh directory and authorized_keys file, +# or sshd will refuse to use them and lead to clone/push/pull failures. +# It could happen when users have copied their data to a new volume and changed the file permission by accident, +# and it would be very hard to troubleshoot unless users know how to check the logs of sshd which is started by s6. +chmod 700 /data/git/.ssh +if [ -f /data/git/.ssh/authorized_keys ]; then + chmod 600 /data/git/.ssh/authorized_keys +fi + +if [ ! -f /data/git/.ssh/environment ]; then + echo "GITEA_CUSTOM=$GITEA_CUSTOM" >| /data/git/.ssh/environment + chmod 600 /data/git/.ssh/environment + +elif ! grep -q "^GITEA_CUSTOM=$GITEA_CUSTOM$" /data/git/.ssh/environment; then + sed -i /^GITEA_CUSTOM=/d /data/git/.ssh/environment + echo "GITEA_CUSTOM=$GITEA_CUSTOM" >> /data/git/.ssh/environment +fi + +if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then + mkdir -p ${GITEA_CUSTOM}/conf + + # Set INSTALL_LOCK to true only if SECRET_KEY is not empty and + # INSTALL_LOCK is empty + if [ -n "$SECRET_KEY" ] && [ -z "$INSTALL_LOCK" ]; then + INSTALL_LOCK=true + fi + + # Substitute the environment variables in the template + APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \ + RUN_MODE=${RUN_MODE:-"prod"} \ + DOMAIN=${DOMAIN:-"localhost"} \ + SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \ + HTTP_PORT=${HTTP_PORT:-"3000"} \ + ROOT_URL=${ROOT_URL:-""} \ + DISABLE_SSH=${DISABLE_SSH:-"false"} \ + SSH_PORT=${SSH_PORT:-"22"} \ + SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \ + LFS_START_SERVER=${LFS_START_SERVER:-"false"} \ + DB_TYPE=${DB_TYPE:-"sqlite3"} \ + DB_HOST=${DB_HOST:-"localhost:3306"} \ + DB_NAME=${DB_NAME:-"gitea"} \ + DB_USER=${DB_USER:-"root"} \ + DB_PASSWD=${DB_PASSWD:-""} \ + INSTALL_LOCK=${INSTALL_LOCK:-"false"} \ + DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-"false"} \ + REQUIRE_SIGNIN_VIEW=${REQUIRE_SIGNIN_VIEW:-"false"} \ + SECRET_KEY=${SECRET_KEY:-""} \ + envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini + + chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini +fi + +# Replace app.ini settings with env variables in the form GITEA__SECTION_NAME__KEY_NAME +environment-to-ini --config ${GITEA_CUSTOM}/conf/app.ini + +# only chown if current owner is not already the gitea ${USER}. No recursive check to save time +if ! [[ $(ls -ld /data/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /data/gitea; fi +if ! [[ $(ls -ld /app/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /app/gitea; fi +if ! [[ $(ls -ld /data/git | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /data/git; fi +chmod 0755 /data/gitea /app/gitea /data/git diff --git a/docker/root/etc/s6/openssh/finish b/docker/root/etc/s6/openssh/finish new file mode 100755 index 0000000..8e89b35 --- /dev/null +++ b/docker/root/etc/s6/openssh/finish @@ -0,0 +1,8 @@ +#!/bin/bash +# $1 = exit code of the run script, $2 = signal +if [ "$1" -ne 0 ]; then + # avoid immediately restarting the sshd service, which may cause CPU 100% if the error (permission, configuration) is not fixed + echo "openssh failed with exit code $1 - waiting a short delay before attempting a restart" + sleep 3 +fi +exit 0 diff --git a/docker/root/etc/s6/openssh/run b/docker/root/etc/s6/openssh/run new file mode 100755 index 0000000..a40b5b1 --- /dev/null +++ b/docker/root/etc/s6/openssh/run @@ -0,0 +1,6 @@ +#!/bin/bash +[[ -f ./setup ]] && source ./setup + +pushd /root >/dev/null +exec su-exec root /usr/sbin/sshd -D -e 2>&1 +popd diff --git a/docker/root/etc/s6/openssh/setup b/docker/root/etc/s6/openssh/setup new file mode 100755 index 0000000..48e7d4b --- /dev/null +++ b/docker/root/etc/s6/openssh/setup @@ -0,0 +1,66 @@ +#!/bin/bash + +if [ ! -d /data/ssh ]; then + mkdir -p /data/ssh +fi + +if [ ! -f /data/ssh/ssh_host_ed25519_key ]; then + echo "Generating /data/ssh/ssh_host_ed25519_key..." + ssh-keygen -t ed25519 -f /data/ssh/ssh_host_ed25519_key -N "" > /dev/null +fi + +if [ ! -f /data/ssh/ssh_host_rsa_key ]; then + echo "Generating /data/ssh/ssh_host_rsa_key..." + ssh-keygen -t rsa -b 3072 -f /data/ssh/ssh_host_rsa_key -N "" > /dev/null +fi + +if [ ! -f /data/ssh/ssh_host_ecdsa_key ]; then + echo "Generating /data/ssh/ssh_host_ecdsa_key..." + ssh-keygen -t ecdsa -b 256 -f /data/ssh/ssh_host_ecdsa_key -N "" > /dev/null +fi + +if [ -e /data/ssh/ssh_host_ed25519_cert ]; then + SSH_ED25519_CERT=${SSH_ED25519_CERT:-"/data/ssh/ssh_host_ed25519_cert"} +fi + +if [ -e /data/ssh/ssh_host_rsa_cert ]; then + SSH_RSA_CERT=${SSH_RSA_CERT:-"/data/ssh/ssh_host_rsa_cert"} +fi + +if [ -e /data/ssh/ssh_host_ecdsa_cert ]; then + SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_cert"} +fi + +# In case someone wants to sign the `{keyname}.pub` key by `ssh-keygen -s ca -I identity ...` to +# make use of the ssh-key certificate authority feature (see ssh-keygen CERTIFICATES section), +# the generated key file name is `{keyname}-cert.pub` +if [ -e /data/ssh/ssh_host_ed25519_key-cert.pub ]; then + SSH_ED25519_CERT=${SSH_ED25519_CERT:-"/data/ssh/ssh_host_ed25519_key-cert.pub"} +fi + +if [ -e /data/ssh/ssh_host_rsa_key-cert.pub ]; then + SSH_RSA_CERT=${SSH_RSA_CERT:-"/data/ssh/ssh_host_rsa_key-cert.pub"} +fi + +if [ -e /data/ssh/ssh_host_ecdsa_key-cert.pub ]; then + SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_key-cert.pub"} +fi + +if [ -d /etc/ssh ]; then + SSH_PORT=${SSH_PORT:-"22"} \ + SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \ + SSH_ED25519_CERT="${SSH_ED25519_CERT:+"HostCertificate "}${SSH_ED25519_CERT}" \ + SSH_RSA_CERT="${SSH_RSA_CERT:+"HostCertificate "}${SSH_RSA_CERT}" \ + SSH_ECDSA_CERT="${SSH_ECDSA_CERT:+"HostCertificate "}${SSH_ECDSA_CERT}" \ + SSH_MAX_STARTUPS="${SSH_MAX_STARTUPS:+"MaxStartups "}${SSH_MAX_STARTUPS}" \ + SSH_MAX_SESSIONS="${SSH_MAX_SESSIONS:+"MaxSessions "}${SSH_MAX_SESSIONS}" \ + SSH_INCLUDE_FILE="${SSH_INCLUDE_FILE:+"Include "}${SSH_INCLUDE_FILE}" \ + SSH_LOG_LEVEL=${SSH_LOG_LEVEL:-"INFO"} \ + envsubst < /etc/templates/sshd_config > /etc/ssh/sshd_config + + chmod 0644 /etc/ssh/sshd_config +fi + +chown root:root /data/ssh/* +chmod 0700 /data/ssh +chmod 0600 /data/ssh/* diff --git a/docker/root/etc/templates/app.ini b/docker/root/etc/templates/app.ini new file mode 100644 index 0000000..1794d1f --- /dev/null +++ b/docker/root/etc/templates/app.ini @@ -0,0 +1,60 @@ +APP_NAME = $APP_NAME +RUN_MODE = $RUN_MODE + +[repository] +ROOT = /data/git/repositories + +[repository.local] +LOCAL_COPY_PATH = /data/gitea/tmp/local-repo + +[repository.upload] +TEMP_PATH = /data/gitea/uploads + +[server] +APP_DATA_PATH = /data/gitea +DOMAIN = $DOMAIN +SSH_DOMAIN = $SSH_DOMAIN +HTTP_PORT = $HTTP_PORT +ROOT_URL = $ROOT_URL +DISABLE_SSH = $DISABLE_SSH +SSH_PORT = $SSH_PORT +SSH_LISTEN_PORT = $SSH_LISTEN_PORT +LFS_START_SERVER = $LFS_START_SERVER + +[database] +PATH = /data/gitea/gitea.db +DB_TYPE = $DB_TYPE +HOST = $DB_HOST +NAME = $DB_NAME +USER = $DB_USER +PASSWD = $DB_PASSWD +LOG_SQL = false + +[indexer] +ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve + +[session] +PROVIDER_CONFIG = /data/gitea/sessions + +[picture] +AVATAR_UPLOAD_PATH = /data/gitea/avatars +REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars + +[attachment] +PATH = /data/gitea/attachments + +[log] +MODE = console +LEVEL = info +ROOT_PATH = /data/gitea/log + +[security] +INSTALL_LOCK = $INSTALL_LOCK +SECRET_KEY = $SECRET_KEY + +[service] +DISABLE_REGISTRATION = $DISABLE_REGISTRATION +REQUIRE_SIGNIN_VIEW = $REQUIRE_SIGNIN_VIEW + +[lfs] +PATH = /data/git/lfs diff --git a/docker/root/etc/templates/sshd_config b/docker/root/etc/templates/sshd_config new file mode 100644 index 0000000..033c434 --- /dev/null +++ b/docker/root/etc/templates/sshd_config @@ -0,0 +1,43 @@ +Port ${SSH_LISTEN_PORT} +Protocol 2 + +AddressFamily any +ListenAddress 0.0.0.0 +ListenAddress :: + +${SSH_MAX_STARTUPS} +${SSH_MAX_SESSIONS} + +LogLevel ${SSH_LOG_LEVEL} + +HostKey /data/ssh/ssh_host_ed25519_key +${SSH_ED25519_CERT} +HostKey /data/ssh/ssh_host_rsa_key +${SSH_RSA_CERT} +HostKey /data/ssh/ssh_host_ecdsa_key +${SSH_ECDSA_CERT} + +AuthorizedKeysFile .ssh/authorized_keys +AuthorizedPrincipalsFile .ssh/authorized_principals +TrustedUserCAKeys /data/git/.ssh/gitea-trusted-user-ca-keys.pem +CASignatureAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ecdsa-sha2-nistp256@openssh.com,ssh-ed25519,sk-ssh-ed25519@openssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa + +UseDNS no +AllowAgentForwarding no +AllowTcpForwarding no +PrintMotd no + +PermitUserEnvironment yes +PermitRootLogin no +ChallengeResponseAuthentication no +PasswordAuthentication no +PermitEmptyPasswords no + +AllowUsers ${USER} + +Banner none +Subsystem sftp /usr/lib/ssh/sftp-server + +AcceptEnv GIT_PROTOCOL + +${SSH_INCLUDE_FILE} diff --git a/docker/root/usr/bin/entrypoint b/docker/root/usr/bin/entrypoint new file mode 100755 index 0000000..08587fc --- /dev/null +++ b/docker/root/usr/bin/entrypoint @@ -0,0 +1,41 @@ +#!/bin/sh + +# Protect against buggy runc in docker <20.10.6 causing problems in with Alpine >= 3.14 +if [ ! -x /bin/sh ]; then + echo "Executable test for /bin/sh failed. Your Docker version is too old to run Alpine 3.14+ and Gitea. You must upgrade Docker."; + exit 1; +fi + +if [ "${USER}" != "git" ]; then + # Rename user + sed -i -e "s/^git\:/${USER}\:/g" /etc/passwd +fi + +if [ -z "${USER_GID}" ]; then + USER_GID="`id -g ${USER}`" +fi + +if [ -z "${USER_UID}" ]; then + USER_UID="`id -u ${USER}`" +fi + +# Change GID for USER? +if [ -n "${USER_GID}" ] && [ "${USER_GID}" != "`id -g ${USER}`" ]; then + sed -i -e "s/^${USER}:\([^:]*\):[0-9]*/${USER}:\1:${USER_GID}/" /etc/group + sed -i -e "s/^${USER}:\([^:]*\):\([0-9]*\):[0-9]*/${USER}:\1:\2:${USER_GID}/" /etc/passwd +fi + +# Change UID for USER? +if [ -n "${USER_UID}" ] && [ "${USER_UID}" != "`id -u ${USER}`" ]; then + sed -i -e "s/^${USER}:\([^:]*\):[0-9]*:\([0-9]*\)/${USER}:\1:${USER_UID}:\2/" /etc/passwd +fi + +for FOLDER in /data/gitea/conf /data/gitea/log /data/git /data/ssh; do + mkdir -p ${FOLDER} +done + +if [ $# -gt 0 ]; then + exec "$@" +else + exec /usr/bin/s6-svscan /etc/s6 +fi diff --git a/docker/root/usr/local/bin/environment-to-ini b/docker/root/usr/local/bin/environment-to-ini new file mode 100644 index 0000000..bb0c540 --- /dev/null +++ b/docker/root/usr/local/bin/environment-to-ini @@ -0,0 +1,2 @@ +#!/bin/bash +exec /app/gitea/gitea config edit-ini --in-place --apply-env "$@" diff --git a/docker/root/usr/local/bin/gitea b/docker/root/usr/local/bin/gitea new file mode 100644 index 0000000..3f78d65 --- /dev/null +++ b/docker/root/usr/local/bin/gitea @@ -0,0 +1,15 @@ +#!/bin/bash + +############################################################### +# This script sets defaults for gitea to run in the container # +############################################################### + +# It assumes that you place this script as gitea in /usr/local/bin +# +# And place the original in /usr/lib/gitea with working files in /data/gitea +GITEA="/app/gitea/gitea" +WORK_DIR="/data/gitea" +CUSTOM_PATH="/data/gitea" + +# Provide docker defaults +GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" GITEA_CUSTOM="${GITEA_CUSTOM:-$CUSTOM_PATH}" exec -a "$0" "$GITEA" $CONF_ARG "$@" diff --git a/docker/rootless/etc/templates/app.ini b/docker/rootless/etc/templates/app.ini new file mode 100644 index 0000000..0357e6a --- /dev/null +++ b/docker/rootless/etc/templates/app.ini @@ -0,0 +1,57 @@ +APP_NAME = $APP_NAME +RUN_USER = $RUN_USER +RUN_MODE = $RUN_MODE + +[repository] +ROOT = $GITEA_WORK_DIR/git/repositories + +[repository.local] +LOCAL_COPY_PATH = $GITEA_TEMP/local-repo + +[repository.upload] +TEMP_PATH = $GITEA_TEMP/uploads + +[server] +APP_DATA_PATH = $GITEA_WORK_DIR +SSH_DOMAIN = $SSH_DOMAIN +HTTP_PORT = $HTTP_PORT +ROOT_URL = $ROOT_URL +DISABLE_SSH = $DISABLE_SSH +; In rootless gitea container only internal ssh server is supported +START_SSH_SERVER = true +SSH_PORT = $SSH_PORT +SSH_LISTEN_PORT = $SSH_LISTEN_PORT +BUILTIN_SSH_SERVER_USER = $RUN_USER +LFS_START_SERVER = $LFS_START_SERVER + +[database] +PATH = $GITEA_WORK_DIR/data/gitea.db +DB_TYPE = $DB_TYPE +HOST = $DB_HOST +NAME = $DB_NAME +USER = $DB_USER +PASSWD = $DB_PASSWD + +[session] +PROVIDER_CONFIG = $GITEA_WORK_DIR/data/sessions + +[picture] +AVATAR_UPLOAD_PATH = $GITEA_WORK_DIR/data/avatars +REPOSITORY_AVATAR_UPLOAD_PATH = $GITEA_WORK_DIR/data/repo-avatars + +[attachment] +PATH = $GITEA_WORK_DIR/data/attachments + +[log] +ROOT_PATH = $GITEA_WORK_DIR/data/log + +[security] +INSTALL_LOCK = $INSTALL_LOCK +SECRET_KEY = $SECRET_KEY + +[service] +DISABLE_REGISTRATION = $DISABLE_REGISTRATION +REQUIRE_SIGNIN_VIEW = $REQUIRE_SIGNIN_VIEW + +[lfs] +PATH = $GITEA_WORK_DIR/git/lfs diff --git a/docker/rootless/usr/local/bin/docker-entrypoint.sh b/docker/rootless/usr/local/bin/docker-entrypoint.sh new file mode 100755 index 0000000..4ffc4ff --- /dev/null +++ b/docker/rootless/usr/local/bin/docker-entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# Protect against buggy runc in docker <20.10.6 causing problems in with Alpine >= 3.14 +if [ ! -x /bin/sh ]; then + echo "Executable test for /bin/sh failed. Your Docker version is too old to run Alpine 3.14+ and Gitea. You must upgrade Docker."; + exit 1; +fi + +if [ -x /usr/local/bin/docker-setup.sh ]; then + /usr/local/bin/docker-setup.sh || { echo 'docker setup failed' ; exit 1; } +fi + +if [ $# -gt 0 ]; then + exec "$@" +else + exec /usr/local/bin/gitea -c "${GITEA_APP_INI}" web +fi diff --git a/docker/rootless/usr/local/bin/docker-setup.sh b/docker/rootless/usr/local/bin/docker-setup.sh new file mode 100755 index 0000000..3320d69 --- /dev/null +++ b/docker/rootless/usr/local/bin/docker-setup.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Prepare git folder +mkdir -p "${HOME}" && chmod 0700 "${HOME}" +if [ ! -w "${HOME}" ]; then echo "${HOME} is not writable"; exit 1; fi + +# Prepare custom folder +mkdir -p "${GITEA_CUSTOM}" && chmod 0700 "${GITEA_CUSTOM}" + +# Prepare temp folder +mkdir -p "${GITEA_TEMP}" && chmod 0700 "${GITEA_TEMP}" +if [ ! -w "${GITEA_TEMP}" ]; then echo "${GITEA_TEMP} is not writable"; exit 1; fi + +#Prepare config file +if [ ! -f "${GITEA_APP_INI}" ]; then + + #Prepare config file folder + GITEA_APP_INI_DIR=$(dirname "${GITEA_APP_INI}") + mkdir -p "${GITEA_APP_INI_DIR}" && chmod 0700 "${GITEA_APP_INI_DIR}" + if [ ! -w "${GITEA_APP_INI_DIR}" ]; then echo "${GITEA_APP_INI_DIR} is not writable"; exit 1; fi + + # Set INSTALL_LOCK to true only if SECRET_KEY is not empty and + # INSTALL_LOCK is empty + if [ -n "$SECRET_KEY" ] && [ -z "$INSTALL_LOCK" ]; then + INSTALL_LOCK=true + fi + + # Substitute the environment variables in the template + APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \ + RUN_MODE=${RUN_MODE:-"prod"} \ + RUN_USER=${USER:-"git"} \ + SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \ + HTTP_PORT=${HTTP_PORT:-"3000"} \ + ROOT_URL=${ROOT_URL:-""} \ + DISABLE_SSH=${DISABLE_SSH:-"false"} \ + SSH_PORT=${SSH_PORT:-"2222"} \ + SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-} \ + DB_TYPE=${DB_TYPE:-"sqlite3"} \ + DB_HOST=${DB_HOST:-"localhost:3306"} \ + DB_NAME=${DB_NAME:-"gitea"} \ + DB_USER=${DB_USER:-"root"} \ + DB_PASSWD=${DB_PASSWD:-""} \ + INSTALL_LOCK=${INSTALL_LOCK:-"false"} \ + DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-"false"} \ + REQUIRE_SIGNIN_VIEW=${REQUIRE_SIGNIN_VIEW:-"false"} \ + SECRET_KEY=${SECRET_KEY:-""} \ + envsubst < /etc/templates/app.ini > "${GITEA_APP_INI}" +fi + +# Replace app.ini settings with env variables in the form GITEA__SECTION_NAME__KEY_NAME +environment-to-ini --config "${GITEA_APP_INI}" diff --git a/docker/rootless/usr/local/bin/environment-to-ini b/docker/rootless/usr/local/bin/environment-to-ini new file mode 100644 index 0000000..bb0c540 --- /dev/null +++ b/docker/rootless/usr/local/bin/environment-to-ini @@ -0,0 +1,2 @@ +#!/bin/bash +exec /app/gitea/gitea config edit-ini --in-place --apply-env "$@" diff --git a/docker/rootless/usr/local/bin/gitea b/docker/rootless/usr/local/bin/gitea new file mode 100644 index 0000000..9a9a569 --- /dev/null +++ b/docker/rootless/usr/local/bin/gitea @@ -0,0 +1,40 @@ +#!/bin/bash + +############################################################### +# This script sets defaults for gitea to run in the container # +############################################################### + +# It assumes that you place this script as gitea in /usr/local/bin +# +# And place the original in /usr/lib/gitea with working files in /data/gitea +GITEA="/app/gitea/gitea" +WORK_DIR="/var/lib/gitea" +APP_INI="/etc/gitea/app.ini" + +APP_INI_SET="" +for i in "$@"; do + case "$i" in + "-c") + APP_INI_SET=1 + ;; + "-c="*) + APP_INI_SET=1 + ;; + "--config") + APP_INI_SET=1 + ;; + "--config="*) + APP_INI_SET=1 + ;; + *) + ;; + esac +done + +if [ -z "$APP_INI_SET" ]; then + CONF_ARG=("-c" "${GITEA_APP_INI:-$APP_INI}") +fi + + +# Provide docker defaults +GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" exec -a "$0" "$GITEA" "${CONF_ARG[@]}" "$@" diff --git a/docs/build-setup.md b/docs/build-setup.md new file mode 100644 index 0000000..d1cd465 --- /dev/null +++ b/docs/build-setup.md @@ -0,0 +1,67 @@ +# Setup and requirements + +This document lists the tools you need to build Gitea from source and how to get +the code. Once your environment is ready, see [development.md](development.md) for +the build and development workflow, and [testing.md](testing.md) for running tests. + +For the contribution workflow and review process, see [CONTRIBUTING.md](../CONTRIBUTING.md). + +## Requirements + +### Go + +[Install Go](https://go.dev/doc/install) and set up your Go environment. The +required version is the one declared in [`go.mod`](../go.mod); installing the same +version your continuous integration uses avoids `gofmt` differences between Go +releases. + +> [!NOTE] +> Some `make` tasks build external Go tools on demand (for example `make +> watch-backend`). To use them, the `"$GOPATH"/bin` directory must be on your +> executable `PATH`; otherwise you have to manage those tools yourself. + +### Node.js and pnpm + +[Install Node.js](https://nodejs.org/en/download/) to build the JavaScript and CSS +files. The minimum supported version is the one declared in +[`package.json`](../package.json) (`engines.node`); the latest LTS is recommended. + +Gitea manages frontend dependencies with [pnpm](https://pnpm.io/). The `make` +targets invoke it for you, so installing pnpm manually is only needed if you want +to run `pnpm` commands directly. + +### Make + +Gitea uses [Make](https://www.gnu.org/software/make/) to drive builds, linting, and +tests. On Windows it can be installed via [MSYS2](https://www.msys2.org/) or +[Chocolatey](https://chocolatey.org/packages/make). + +### Python with uv (optional) + +Linting the templates, workflow files, and YAML requires Python tooling that Gitea +runs through [uv](https://docs.astral.sh/uv/). After installing uv, `make` creates +the environment automatically (`uv sync`); you only need this if you run +`make lint-templates`, `make lint-yaml`, or `make lint-actions` locally. + +### Git LFS + +The integration tests require [Git LFS](https://git-lfs.com/) to be installed. + +## Getting the source code + +Clone the repository: + +```bash +git clone https://github.com/go-gitea/gitea +``` + +To contribute changes, [fork the repository](https://github.com/go-gitea/gitea) on +GitHub and add your fork as a git remote so you can push branches and open pull +requests. See GitHub's [working with forks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks) +documentation for the details. + +## Installing dependencies + +Most build and test targets install the dependencies they need on their own. To +fetch everything up front, run `make deps` (or the per-group `make deps-frontend`, +`make deps-backend`, `make deps-tools`, `make deps-py`). diff --git a/docs/build-source.md b/docs/build-source.md new file mode 100644 index 0000000..4355857 --- /dev/null +++ b/docs/build-source.md @@ -0,0 +1,92 @@ +# Prepare build environment + +Complete the steps in [build-setup.md](build-setup.md) to prepare your environment for building Gitea from source. + +## Choose a branch + +By default, the cloned repository is on main branch (the current development branch for next major release, aka: main nightly). + +You can switch to a versioned branch (the branch for the next minor stable release, aka: stable nightly ) +or a versioned tag (matches the official releases with version numbers) + +To test a Pull Request, you can fetch its code by its Pull Request number (take `PR #123456` as example): + +```bash +git fetch origin pull/123456/head:pr-123456 +``` + +# Build + +Various [make tasks](https://github.com/go-gitea/gitea/blob/main/Makefile) +are provided to keep the build process as simple as possible. + +Depending on requirements, the following build tags can be included. + +- `bindata`: Build a single monolithic binary, with all assets included. Required for distribution and production build. +- `pam`: Enable support for PAM (Linux Pluggable Authentication Modules). + Can be used to authenticate local users or extend authentication to methods available to PAM. +- `gogit`: (EXPERIMENTAL) Use go-git variants of Git commands. + +To include all assets, use the `bindata` tag: + +```bash +TAGS="bindata" make build +``` + +Tag `gogit` is used to try to resolve some Windows-specific performance problems, POSIX systems don't need it. +You can build a Windows binary by: + +```bash +GOOS=windows TAGS="bindata gogit" make build +``` + +## Changing default paths + +Gitea will search for a number of things from the _`CustomPath`_. +By default, this is the `custom/` directory in the current working directory when running Gitea. +It will also look for its configuration file _`CustomConf`_ in `$(CustomPath)/conf/app.ini`, +and will use the current working directory as the relative base path _`AppWorkPath`_. + +These values, although useful when developing, may conflict with downstream users preferences. + +For packagers who need to use paths like `/etc/gitea/app.ini`, +they should define these values at build time for `make build` by environment variable like +`LDFLAGS='-X "module.Var1=Value1" -X "module.Var2=Value2"' TAGS="bindata" make build`. + +- _`CustomConf`_: `-X "gitea.dev/modules/setting.CustomConf=/etc/gitea/app.ini"` +- _`AppWorkPath`_: `-X "gitea.dev/modules/setting.AppWorkPath=/var/lib/gitea"` +- _`CustomPath`_: `-X "gitea.dev/modules/setting.CustomPath=/var/lib/gitea/custom"` +- Default PID file location: `-X "gitea.dev/cmd.PIDFile=/run/gitea.pid"` + +Add as many of the strings with their preceding `-X` to the `LDFLAGS` variable and run `make build` +with the appropriate `TAGS` as above. + +Running `gitea help` will allow you to review what the computed settings will be for your `gitea`. + +## Cross Build + +Gitea use's Golang's toolchain variables for cross-building. + +For example, to cross build for Linux ARM64: + +``` +GOOS=linux GOARCH=arm64 TAGS="bindata" make build +``` + +### Adding shell autocompletion + +Shell completion can be generated directly from binary with: +```sh +gitea completion +``` + +Supported values for `` are `bash`, `fish`, `pwsh` and `zsh`. +Details on how to load the completion for your shell can be found in the completion command help. + +## Source Maps + +By default, gitea generates reduced source maps for frontend files to conserve space. This can be controlled with the `ENABLE_SOURCEMAP` environment variable: + +- `ENABLE_SOURCEMAP=true` generates all source maps, the default for development builds +- `ENABLE_SOURCEMAP=reduced` generates limited source maps, the default for production builds +- `ENABLE_SOURCEMAP=false` generates no source maps diff --git a/docs/community-governance.md b/docs/community-governance.md new file mode 100644 index 0000000..da252a8 --- /dev/null +++ b/docs/community-governance.md @@ -0,0 +1,263 @@ +# Community governance and review process + +This document describes maintainer expectations, project governance, and the detailed pull request review workflow (labels, merge queue, commit message format for mergers). For what contributors should do when opening and updating a PR, see [CONTRIBUTING.md](../CONTRIBUTING.md). + +## Table of contents + +- [Community governance and review process](#community-governance-and-review-process) + - [Table of contents](#table-of-contents) + - [Code review](#code-review) + - [Milestone](#milestone) + - [Labels](#labels) + - [Reviewing PRs](#reviewing-prs) + - [For reviewers](#for-reviewers) + - [Getting PRs merged](#getting-prs-merged) + - [Final call](#final-call) + - [Commit messages](#commit-messages) + - [PR Co-authors](#pr-co-authors) + - [PRs targeting `main`](#prs-targeting-main) + - [Backport PRs](#backport-prs) + - [Contribution Roles](#contribution-roles) + - [Maintainers](#maintainers) + - [Review expectations](#review-expectations) + - [Becoming a maintainer](#becoming-a-maintainer) + - [Stepping down, advisors, and inactivity](#stepping-down-advisors-and-inactivity) + - [Account security](#account-security) + - [Mergers](#mergers) + - [Becoming a merger](#becoming-a-merger) + - [Technical Oversight Committee (TOC)](#technical-oversight-committee-toc) + - [TOC election process](#toc-election-process) + - [Current TOC members](#current-toc-members) + - [Previous TOC/owners members](#previous-tocowners-members) + - [Governance Compensation](#governance-compensation) + - [Roadmap](#roadmap) + +## Code review + +### Milestone + +A PR should only be assigned to a milestone if it will likely be merged into the given version. \ +PRs without a milestone may not be merged. + +### Labels + +Almost all labels used inside Gitea can be classified as one of the following: + +- `modifies/…`: Determines which parts of the codebase are affected. These labels will be set through the CI. +- `topic/…`: Determines the conceptual component of Gitea that is affected, i.e. issues, projects, or authentication. At best, PRs should only target one component but there might be overlap. Must be set manually. +- `type/…`: Determines the type of an issue or PR (feature, refactoring, docs, bug, …). If GitHub supported scoped labels, these labels would be exclusive, so you should set **exactly** one, not more or less (every PR should fall into one of the provided categories, and only one). +- `issue/…` / `lgtm/…`: Labels that are specific to issues or PRs respectively and that are only necessary in a given context, i.e. `issue/not-a-bug` or `lgtm/need 2` + +Every PR should be labeled correctly with every label that applies. + +There are also some labels that will be managed automatically.\ +In particular, these are + +- the amount of pending required approvals +- has all `backport`s or needs a manual backport + +### Reviewing PRs + +Maintainers are encouraged to review pull requests in areas where they have expertise or particular interest. + +#### For reviewers + +- **Verification**: Verify that the PR accurately reflects the changes, and verify that the tests and documentation are complete and aligned with the implementation. +- **Actionable feedback**: Say what should change and why, and distinguish required changes from optional suggestions. +- **Feedback**: Focus feedback on the issue itself and avoid comments about the contributor's abilities. +- **Request changes**: If you request changes (i.e., block a PR), give a clear rationale and, whenever possible, a concrete path to resolution. +- **Approval**: Only approve a PR when you are fully satisfied with its current state - "rubber-stamp" approvals need to be highlighted as such. + +### Getting PRs merged + +Changes to Gitea must be reviewed before they are accepted, including changes from owners and maintainers. The exception is critical bugs that prevent Gitea from compiling or starting. + +We require two maintainer approvals for every PR. When that is satisfied, your PR gets the `lgtm/done` label. After that, you mainly fix merge conflicts and respond to or implement maintainer requests; maintainers drive getting the PR merged. + +If a PR has `lgtm/done`, no open discussions, and no merge conflicts, any maintainer may add `reviewed/wait-merge`. That puts the PR in the merge queue. PRs are merged from the queue in the order of this list: + + + +Gitea uses its own tool, , to automate parts of the review process. The backporter: + +- Creates a backport PR when needed after the initial PR merges. +- Removes the PR from the merge queue after it merges. +- Keeps the oldest branch in the merge queue up to date with merges. + +### Final call + +If a PR has been ignored for more than 7 days with no comments or reviews, and the author or any maintainer believes it will not survive a long wait (such as a refactoring PR), they can send "final call" to the TOC by mentioning them in a comment. + +After another 7 days, if there is still zero approval, this is considered a polite refusal, and the PR will be closed to avoid wasting further time. Therefore, the "final call" has a cost, and should be used cautiously. + +However, if there are no objections from maintainers, the PR can be merged with only one approval from the TOC (not the author). + +### Commit messages + +Mergers are required to rewrite the PR title and the first comment (the summary) when necessary so the squash commit message is clear. +Usually the Pull Request description and commit message body should not be empty, unless the title is already clear enough or the description would be a copy of the comments in code. + +The final commit message: + +- should match the code changes. +- should only keep true co-authors, false-positive co-authors should be removed. +- should not hedge: replace phrases like `hopefully, won't happen anymore` with definite wording. +- should not contain hidden information like `` or extra information after the description's divider `----`. +- should not contain unrelated contents (e.g.: Release Notes, Configuration, etc.) from a Renovate update PR. + +#### PR Co-authors + +A person counts as a PR co-author once they (co-)authored a commit that is not simply a `Merge base branch into branch` commit. Mergers must remove such false-positive co-authors when writing the squash message. Every true co-author must remain in the commit message. + +#### PRs targeting `main` + +The commit message of PRs targeting `main` is always + +```bash +$PR_TITLE ($PR_INDEX) + +$REWRITTEN_PR_SUMMARY +``` + +#### Backport PRs + +The commit message of backport PRs is always + +```bash +$PR_TITLE ($INITIAL_PR_INDEX) ($BACKPORT_PR_INDEX) + +$REWRITTEN_PR_SUMMARY +``` + +## Contribution Roles + +### Maintainers + +We list [maintainers](../MAINTAINERS) so every PR gets proper review. + +#### Review expectations + +Every PR **must** be reviewed by at least two maintainers (or owners) before merge. **Exception:** after one week, refactoring PRs and documentation-only PRs need only one maintainer approval. + +Maintainers are expected to spend time on code reviews. + +#### Becoming a maintainer + +A maintainer should already be a Gitea contributor with at least four merged PRs. To apply, use the [Discord](https://discord.gg/Gitea) `#develop` channel. Maintainer teams may also invite contributors. + +#### Stepping down, advisors, and inactivity + +If you cannot keep reviewing, apply to leave the maintainers team. You can join the [advisors team](https://github.com/orgs/go-gitea/teams/advisors); advisors who want to review again are welcome back as maintainers. + +If a maintainer is inactive for more than three months and has not left the team, owners may move them to the advisors team. + +#### Account security + +For security, maintainers should enable 2FA and sign commits with GPG when possible: + +- [Two-factor authentication](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication) +- [Signing commits with GPG](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) + +Any account with write access (including bots and TOC members) **must** use [2FA](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication). + +### Mergers + +Mergers are the maintainers who carry out the final merge of approved PRs. Their responsibilities, described throughout this guide, are: + +- Merging PRs from the [merge queue](#getting-prs-merged) in order, once a PR has `lgtm/done`, no open discussions, and no merge conflicts. +- Rewriting the PR title and description prior to the merge, making the [commit message](#commit-messages) clear.\ + In particular, mergers should edit the PR description.\ + Mergers should **not** edit the actual commit message except to remove unnecessary information. Because of that, even if users are looking at the PR, they can understand what changed. +- Assigning the correct labels (including `type/…`) needed for changelog and backport decisions. +- Agreeing, together with the owners, on when a release is ready (see [release management](release-management.md)). +- Merging a PR also means the PR looks good to the merger and is approved by the merger. + +If a merger violates these merge guides more than 3 times in the past 365 days +(e.g.: merge with unresolved reviews without TOC decision to ignore the review, merge with garbage commit messages), +they may lose their merging privileges for at least three months. + +#### Becoming a merger + +A merger must already be a Gitea maintainer. +To apply, use the [Discord](https://discord.gg/Gitea) `#maintainers` channel. +The minimum requirement for applications to become a merger is to have participated actively in the community for at least four months before applying. +Ultimately, regardless of previous participation, you can only become a merger if the TOC votes in your favor. + +You may also be invited by the TOC to become a merger. + +### Technical Oversight Committee (TOC) + +At the start of 2023, the `Owners` team was dissolved. Instead, the governance charter proposed a technical oversight committee (TOC) which expands the ownership team of the Gitea project from three elected positions to six positions. Three positions are elected as it has been over the past years, and the other three consist of appointed members from the Gitea company. +https://blog.gitea.com/quarterly-23q1/ + +#### TOC election process + +Any maintainer is eligible to be part of the community TOC if they are not associated with the Gitea company. +A maintainer can either nominate themselves, or can be nominated by other maintainers to be a candidate for the TOC election. +If you are nominated by someone else, you must first accept your nomination before the vote starts to be a candidate. + +The TOC is elected for one year, the TOC election happens yearly. +After the announcement of the results of the TOC election, elected members have two weeks time to confirm or refuse the seat. +If an elected member does not answer within this timeframe, they are automatically assumed to refuse the seat. +Refusals result in the person with the next highest vote getting the same choice. +As long as seats are empty in the TOC, members of the previous TOC can fill them until an elected member accepts the seat. + +If an elected member that accepts the seat does not have 2FA configured yet, they will be temporarily counted as `answer pending` until they manage to configure 2FA, thus leaving their seat empty for this duration. + +If multiple persons have the same amount of votes, a random draw will be used to determine the order of the candidates with the same amount of votes, and thus who gets the seat first. +The candidates will be placed in the list in an alphabetical insensitive order by their username. +We use this script to determine the order of candidates with the same amount of votes: + +```python +import random +random.seed("Gitea TOC Election") +random.choice([, , ...]) +``` + +The result of this script needs then to be published in the TOC election issue to ensure transparency of the process. + +### Current TOC members + +- 2026-06-14 ~ 2026-12-31 + - Company + - [Yu Tian](https://gitea.com/Zettat123) + - [Lunny Xiao](https://gitea.com/lunny) + - [Matti Ranta](https://gitea.com/techknowlogick) + - Community + - [bircni](https://gitea.com/bircni) + - [delvh](https://gitea.com/delvh) + - [TheFox0x7](https://gitea.com/TheFox0x7) + + +### Previous TOC/owners members + +Here's the history of the owners and the time they served: + +- [Lunny Xiao](https://gitea.com/lunny) - 2016, 2017, [2018](https://github.com/go-gitea/gitea/issues/3255), [2019](https://github.com/go-gitea/gitea/issues/5572), [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872), 2023 +- [Kim Carlbäcker](https://github.com/bkcsoft) - 2016, 2017 +- [Thomas Boerger](https://gitea.com/tboerger) - 2016, 2017 +- [Lauris Bukšis](https://gitea.com/lafriks) - [2018](https://github.com/go-gitea/gitea/issues/3255), [2019](https://github.com/go-gitea/gitea/issues/5572), [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), 2025 +- [Matti Ranta](https://gitea.com/techknowlogick) - [2019](https://github.com/go-gitea/gitea/issues/5572), [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872), 2023 +- [Andrew Thornton](https://gitea.com/zeripath) - [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872), 2023 +- [6543](https://gitea.com/6543) - 2023, 2025 +- [John Olheiser](https://gitea.com/jolheiser) - 2023, 2024 +- [Jason Song](https://gitea.com/wolfogre) - 2023, 2025 + +## Governance Compensation + +Each member of the community elected TOC will be granted $500 each month as compensation for their work. + +Furthermore, any community release manager for a specific release or LTS will be compensated $500 for the delivery of said release. + +These funds will come from community sources like the OpenCollective rather than directly from the company. +Only non-company members are eligible for this compensation, and if a member of the community TOC takes the responsibility of release manager, they would only be compensated for their TOC duties. +Gitea Ltd employees are not eligible to receive any funds from the OpenCollective unless it is reimbursement for a purchase made for the Gitea project itself. + +## Roadmap + +Each year a roadmap will be discussed with the entire Gitea maintainers team, and feedback will be solicited from various stakeholders. +TOC members need to review the roadmap every year and work together on the direction of the project. + +When a vote is required for a proposal or other change, the vote of community elected TOC members count slightly more than the vote of company elected TOC members. With this approach, we both avoid ties and ensure that changes align with the mission statement and community opinion. + +You can visit our roadmap on the wiki. diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..88a0380 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,141 @@ +# Development + +This document describes how to build Gitea from source and the day-to-day +development workflow. For prerequisites and how to obtain the code, see +[build-setup.md](build-setup.md). For running tests, see [testing.md](testing.md). For the +contribution workflow and review process, see [CONTRIBUTING.md](../CONTRIBUTING.md). + +Area-specific guidelines: + +- [Backend development guidelines](guidelines-backend.md) +- [Frontend development guidelines](guidelines-frontend.md) +- [Refactoring guidelines](guidelines-refactoring.md) + +## Building + +To build Gitea for development, run: + +```bash +make build +``` + +No build tags are required: SQLite support is compiled in by default, which is +enough for local development. The `build` target runs two sub-targets, `frontend` +and `backend`. The `bindata` tag embeds the frontend assets into the binary and is +only needed when packaging a self-contained build, so leave it out during +development. + +See `make help` for all available targets, and the workflows in +[`.github/workflows`](https://github.com/go-gitea/gitea/tree/main/.github/workflows) +to see how continuous integration builds and checks Gitea. + +## Building continuously + +To rebuild automatically when source files change: + +```bash +# watch both frontend and backend +make watch + +# or watch only the frontend (starts the Vite dev server) +make watch-frontend + +# or watch only the backend (Go) +make watch-backend +``` + +Watching all backend source files may hit the default open-files limit on macOS or +Linux; raise it with `ulimit -n 12288` for the current shell, or in your shell +startup file to make it permanent. + +## Formatting, linting and checks + +Continuous integration rejects pull requests that fail formatting, linting, or +consistency checks. Format your code first: + +```bash +make fmt +``` + +Then lint: + +```bash +# lint everything +make lint +# or only one side +make lint-backend +make lint-frontend +``` + +Many linters can fix issues automatically with `make lint-fix` (or the scoped +`make lint-backend-fix` / `make lint-frontend-fix`). The combined consistency +checks that CI runs are available as `make checks`. + +## Building and adding SVGs + +SVG icons are built with `make svg`, which compiles the icon sources into +`public/assets/img/svg`. Custom icons can be added under `web_src/svg`. + +## Updating the API + +When you create or change API routes, you **must** update the +[Swagger](https://swagger.io/docs/specification/2-0/what-is-swagger/) documentation +using [go-swagger](https://goswagger.io/) comments. See the +[backend development guidelines](guidelines-backend.md) for how API routes, +request/response structs, and swagger definitions fit together. + +Regenerate and validate the spec after changing an endpoint, then commit the +updated JSON: + +```bash +make generate-swagger +make swagger-validate +``` + +CI verifies the committed spec is up to date with: + +```bash +make swagger-check +``` + +## Creating new configuration options + +When adding configuration options it is not enough to add them to the +`modules/setting` files. Also update +[`custom/conf/app.example.ini`](../custom/conf/app.example.ini), and document them in +the [configuration cheat sheet](https://docs.gitea.com/administration/config-cheat-sheet), +which lives in the [documentation repository](https://gitea.com/gitea/docs). + +## Database migrations + +If you make breaking changes to a database-persisted struct under `models/`, add a +new migration in `modelmigration/`. See [testing.md](testing.md#migration-tests) +for running the migration tests. + +## Testing + +For unit, integration, end-to-end, and migration tests, see [testing.md](testing.md). + +## IDE configuration + +### Visual Studio Code + +A `launch.json` and `tasks.json` are provided in +[`contrib/development/vscode`](../contrib/development/vscode). See +[`contrib/development/README.md`](../contrib/development/README.md) for details. + +### GoLand + +Clicking the `Run Application` arrow on `func main()` in `/main.go` starts a +debuggable Gitea instance. + +The `Output Directory` in `Run/Debug Configuration` **must** be set to the Gitea +project directory (the one containing `main.go` and `go.mod`). Otherwise the working +directory is a GoLand temporary directory, which prevents Gitea from loading dynamic +resources (such as templates) in development. + +## Submitting your changes + +Push your branch and open a pull request. See [CONTRIBUTING.md](../CONTRIBUTING.md) +for the review process and PR requirements. For help, join the `#Develop` channel on +[Discord](https://discord.gg/gitea). diff --git a/docs/guidelines-backend.md b/docs/guidelines-backend.md new file mode 100644 index 0000000..87c67b2 --- /dev/null +++ b/docs/guidelines-backend.md @@ -0,0 +1,150 @@ +# Backend development guidelines + +This document covers backend-specific architecture and contribution expectations. +For the general workflow see [CONTRIBUTING.md](../CONTRIBUTING.md), and for building +and testing see [development.md](development.md) and [testing.md](testing.md). + +## Background + +The backend is written in Go. Web routing is handled by +[chi](https://github.com/go-chi/chi) and database access goes through the +[XORM](https://xorm.io/) ORM. Understanding how the packages depend on each other is +essential before contributing backend code. + +## Package design + +### Package layout + +The backend is split into top-level packages, each with a focused responsibility: + +- `build`: helper scripts used at compile time +- `cmd`: subcommands such as `web`, `serv`, `hooks`, `doctor`, and admin utilities +- `models`: data structures and database operations (XORM); keeps external + dependencies to a minimum + - `models/db`: core database operations + - `models/fixtures`: sample data used by tests +- `modelmigration`: schema migration scripts +- `modules`: standalone functionality with few dependencies + - `modules/setting`: configuration handling + - `modules/git`: interaction with the Git command line +- `routers`: request handlers, split into `api`, `web`, `install`, and `private` +- `services`: business logic that ties routers and models together +- `templates`: Go HTML templates +- `public`: compiled frontend assets +- `tests`: integration and end-to-end test helpers + +### Dependency direction + +Dependencies only flow in one direction: + +```text +cmd → routers → services → models → modules +``` + +A package on the left may import a package on its right, but never the reverse. + +### Naming conventions + +- Top-level packages use the plural form: `services`, `models`, `routers`. +- Subpackages use the singular form: `services/user`, `models/repository`. + +When packages from different layers share a name, use a snake_case import alias to +disambiguate: + +```go +import user_service "gitea.dev/services/user" +``` + +### Database transactions + +Operations that must roll back together should run inside `db.WithTx()` (or +`db.WithTx2()` when a value must be returned), defined in `models/db/context.go`. +Functions that participate in a transaction take a `context.Context` as their first +parameter so the transaction can be propagated. + +### XORM gotchas + +- Never call `x.Update(exemplar)` without an explicit `WHERE` clause — it updates + every row in the table. +- Partial table migrations must use `SyncWithOptions(IgnoreDrop...)` rather than a + plain `Sync`. +- When inserting rows with preset IDs, MSSQL requires `SET IDENTITY_INSERT` to be + enabled and PostgreSQL requires the sequence to be updated afterwards. + +## Dependencies + +Go dependencies are managed with [Go Modules](https://go.dev/ref/mod). + +Pull requests should only modify `go.mod` and `go.sum` where it relates to the +change at hand, be it a bug fix or a new feature. Otherwise, these files should only +be touched by pull requests whose sole purpose is updating dependencies. Run +`make tidy` after any change to `go.mod`. + +Any `go.mod` / `go.sum` update must be justified in the PR description and must be +verified by reviewers and the merger to reference an existing upstream commit. + +## Golang HTML template + +Gitea uses Go's built-in HTML template engine which is dynamically & weakly typed. +To make template code maintainable: + +- Go code should take over complex logic and prepare template data as much as possible, templates only render the data. +- Prefer struct types provided by Go code instead of map types for template data. +- Avoid using single word names for non-local variables. +- Avoid passing `"root" $` or `"." .` to sub-templates, instead pass the specific data needed by the sub-template. +- Use explicit variable names instead of `.` to access data: ``{{range $item := $.TargetItems}}{{ $item.Name }}{{end}}`` +- Use Go code to implement render helpers if the render logic is too complex. + +Using a modern and statically & strongly typed template engine to replace Golang HTML template might be good, +but the challenge is huge, the requisites are: + +- A working and maintainable proof-of-concept for the most complex templates (like "PR View" and "PR Diff"). +- A firm commitment of sufficient engineering resources. + +## API v1 + +The API is documented with [Swagger](https://gitea.com/api/swagger) and is modelled +on [the GitHub API](https://docs.github.com/en/rest). + +### GitHub API compatibility + +Gitea's API should use the same endpoints and fields as the GitHub API where +possible, unless there is a good reason to deviate. + +- If Gitea offers functionality GitHub does not, a new endpoint may be added. +- If Gitea exposes information the GitHub API does not, a new field may be added as + long as it does not collide with a GitHub field. +- Existing fields should not be removed unless there is a strong reason; the same + applies to status responses. + +If you notice a problem that would require a breaking change, leave a comment in the +code for a future refactor to API v2 (which is currently not planned) rather than +breaking v1. + +### Adding and maintaining API routes + +- All possible results (errors, success, and failure messages) must be documented in + the swagger comments on the route. +- Every JSON request body must be defined as a struct in `modules/structs/` and + registered in [`routers/api/v1/swagger/options.go`](../routers/api/v1/swagger/options.go). +- Every JSON response must be defined as a struct in `modules/structs/` and + registered with its category under [`routers/api/v1/swagger/`](../routers/api/v1/swagger). + +### HTTP methods and status codes + +In general, choose HTTP methods as follows: + +- **GET** returns the requested object(s) with status **200 OK**. +- **POST** creates a new object (e.g. a user) and returns **201 Created** with the + created object. +- **PUT** adds or assigns an existing object (e.g. a user to a team) and returns + **204 No Content** with no response body. +- **PATCH** edits an existing object and returns the changed object with **200 OK**. +- **DELETE** removes an object and returns **204 No Content** with no body. + +### Requirements for API routes + +- All parameters of endpoints that edit an object must be optional, except those + needed to identify the object, which are required. +- Endpoints returning lists must support pagination (`page` and `limit` query + options) and set the `X-Total-Count` header via `ctx.SetTotalCountHeader(...)`. diff --git a/docs/guidelines-frontend.md b/docs/guidelines-frontend.md new file mode 100644 index 0000000..68b3348 --- /dev/null +++ b/docs/guidelines-frontend.md @@ -0,0 +1,101 @@ +# Frontend development guidelines + +This document covers frontend-specific architecture and contribution expectations. +For the general workflow see [CONTRIBUTING.md](../CONTRIBUTING.md), and for building +and testing see [development.md](development.md) and [testing.md](testing.md). + +## Background + +The frontend uses [Vue 3](https://vuejs.org/), hard-forked Fomantic-UI (built on jQuery) +and [Tailwind CSS](https://tailwindcss.com/). Pages are rendered with Go HTML templates. +Source files live in: + +- `web_src/css/`: CSS styles +- `web_src/js/`: JavaScript and TypeScript +- `web_src/js/components/`: Vue components +- `web_src/js/features/`: feature modules wired up at page load +- `templates/`: Go HTML templates + +## Dependencies + +Frontend dependencies are managed with [pnpm](https://pnpm.io/). The same rules as +for [backend dependencies](guidelines-backend.md#dependencies) apply, except the +relevant files are `package.json` and `pnpm-lock.yaml`, and new versions must always +reference an existing published version. + +## Framework usage + +Mixing frameworks arbitrarily makes code hard to maintain. Recommended combinations: + +- Vue3 +- Vanilla JavaScript +- Fomantic-UI (jQuery), deprecated, we vendored a specific version with a lot of changes. + +Avoid combinations such as Vue with Fomantic-UI. +Vue components may reuse Fomantic-UI CSS classes for visual consistency. +Use Go templates for simple or SEO-relevant pages and Vue for complex, interactive pages. +Gitea uses Vue 3 **without** JSX to keep HTML and JavaScript separate. + +> [!NOTE] +> Fomantic-UI is not an accessibility-friendly framework. Gitea patches some ARIA +> behavior, but accessibility work is ongoing — prefer semantic HTML and test +> keyboard/screen-reader behavior where you can. + +## Gitea-specific conventions + +- Keep features in their own files or directories. +- Use kebab-case for HTML `id`s and classes with 2-3 feature keywords. +- Prefix classes to avoid short-name conflicts between different frameworks. +- Our framework can automatically link "input" and "label" if they are the children of a `.field` element, + no need to write `id`/`for` attributes for them unless there are reasons to do so. +- Create a new class name when overriding framework styles instead of editing the framework's own classes, + or fix the framework's source to fix all cases. +- Prefer semantic elements such as `