From 1dac4d13f333d00505d568bb3a6f606c02689936 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Sun, 2 Nov 2025 00:39:27 +0000 Subject: [PATCH 1/8] [skip ci] Updated translations via Crowdin --- options/locale/locale_ja-JP.ini | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index a0d4f6cb75..0d7df0647b 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -3914,6 +3914,14 @@ variables.update.success=変数を更新しました。 logs.always_auto_scroll=常にログを自動スクロール logs.always_expand_running=常に実行中のログを展開 +general=一般 +general.enable_actions=Actionsの有効化 +general.collaborative_owners_management=協力オーナーの管理 +general.collaborative_owners_management_help=協力オーナーとは、このリポジトリのActionやワークフローに、自身のプライベートリポジトリからアクセスできるユーザーまたは組織のことです。 +general.add_collaborative_owner=協力オーナーを追加 +general.collaborative_owner_not_exist=協力オーナーが存在しません。 +general.remove_collaborative_owner=協力オーナーの削除 +general.remove_collaborative_owner_desc=協力オーナーを削除すると、そのオーナーのリポジトリはこのリポジトリのActionにアクセスできなくなります。 続行しますか? [projects] deleted.display_name=削除されたプロジェクト From b3e5b9611100d8f40488bb680513bd0e91425b74 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 2 Nov 2025 00:52:59 -0700 Subject: [PATCH 2/8] Fix clone mixed bug (#35810) Fix #35807 --- cmd/serv.go | 14 ++++++----- tests/integration/wiki_test.go | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/cmd/serv.go b/cmd/serv.go index 72ca7c4a00..4110fda0d5 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -18,7 +18,7 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/perm" - "code.gitea.io/gitea/models/repo" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/json" @@ -207,7 +207,7 @@ func runServ(ctx context.Context, c *cli.Command) error { username := repoPathFields[0] reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki" - if !repo.IsValidSSHAccessRepoName(reponame) { + if !repo_model.IsValidSSHAccessRepoName(reponame) { return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame) } @@ -253,10 +253,12 @@ func runServ(ctx context.Context, c *cli.Command) error { return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error) } - // LowerCase and trim the repoPath as that's how they are stored. - // This should be done after splitting the repoPath into username and reponame - // so that username and reponame are not affected. - repoPath = strings.ToLower(results.OwnerName + "/" + results.RepoName + ".git") + // because the original repoPath maybe redirected, we need to use the returned actual repository information + if results.IsWiki { + repoPath = repo_model.RelativeWikiPath(results.OwnerName, results.RepoName) + } else { + repoPath = repo_model.RelativePath(results.OwnerName, results.RepoName) + } // LFS SSH protocol if verb == git.CmdVerbLfsTransfer { diff --git a/tests/integration/wiki_test.go b/tests/integration/wiki_test.go index c31f73eabf..5718156ffa 100644 --- a/tests/integration/wiki_test.go +++ b/tests/integration/wiki_test.go @@ -11,7 +11,9 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/git" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/tests" @@ -71,3 +73,46 @@ func Test_RepoWikiPages(t *testing.T) { assert.Equal(t, expectedPagePaths[i], pagePath) }) } + +func Test_WikiClone(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + username := "user2" + reponame := "repo1" + wikiPath := username + "/" + reponame + ".wiki.git" + keyname := "my-testing-key" + baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + + u.Path = wikiPath + + t.Run("Clone HTTP", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + dstLocalPath := t.TempDir() + assert.NoError(t, git.Clone(t.Context(), u.String(), dstLocalPath, git.CloneRepoOptions{})) + content, err := os.ReadFile(filepath.Join(dstLocalPath, "Home.md")) + assert.NoError(t, err) + assert.Equal(t, "# Home page\n\nThis is the home page!\n", string(content)) + }) + + t.Run("Clone SSH", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + dstLocalPath := t.TempDir() + sshURL := createSSHUrl(wikiPath, u) + + withKeyFile(t, keyname, func(keyFile string) { + var keyID int64 + t.Run("CreateUserKey", doAPICreateUserKey(baseAPITestContext, "test-key", keyFile, func(t *testing.T, key api.PublicKey) { + keyID = key.ID + })) + assert.NotZero(t, keyID) + + // Setup clone folder + assert.NoError(t, git.Clone(t.Context(), sshURL.String(), dstLocalPath, git.CloneRepoOptions{})) + content, err := os.ReadFile(filepath.Join(dstLocalPath, "Home.md")) + assert.NoError(t, err) + assert.Equal(t, "# Home page\n\nThis is the home page!\n", string(content)) + }) + }) + }) +} From f2d7931b702b59a35be042266e0c12271a7456f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=81=E6=B1=80?= <131967983+lutinglt@users.noreply.github.com> Date: Sun, 2 Nov 2025 16:30:13 +0800 Subject: [PATCH 3/8] Fix circular spin animation direction (#35785) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wait for the status icon to rotate clockwise instead of counterclockwise before: ![GIF 2025-10-30 10-50-07](https://github.com/user-attachments/assets/3771b0bf-44e4-45a0-bde5-1b2b3dd8ba2a) after: ![GIF 2025-10-30 10-50-43](https://github.com/user-attachments/assets/c45307fe-39a4-4e60-b48e-9d922c407565) --------- Signed-off-by: 鲁汀 <131967983+lutinglt@users.noreply.github.com> Signed-off-by: wxiaoguang Co-authored-by: lutinglt Co-authored-by: wxiaoguang --- templates/repo/actions/status.tmpl | 2 +- templates/repo/issue/view_content/pull_merge_box.tmpl | 2 +- web_src/css/modules/animations.css | 10 ++++------ web_src/js/components/ActionRunStatus.vue | 2 +- web_src/js/components/RepoActionView.vue | 2 +- web_src/js/components/RepoCodeFrequency.vue | 2 +- web_src/js/components/RepoContributors.vue | 2 +- web_src/js/components/RepoRecentCommits.vue | 2 +- web_src/js/components/ViewFileTreeItem.vue | 2 +- 9 files changed, 12 insertions(+), 14 deletions(-) diff --git a/templates/repo/actions/status.tmpl b/templates/repo/actions/status.tmpl index 055bc714c9..1e5465a97f 100644 --- a/templates/repo/actions/status.tmpl +++ b/templates/repo/actions/status.tmpl @@ -16,7 +16,7 @@ {{else if eq .status "blocked"}} {{svg "octicon-blocked" $size (printf "text yellow %s" $className)}} {{else if eq .status "running"}} - {{svg "gitea-running" $size (printf "text yellow circular-spin %s" $className)}} + {{svg "gitea-running" $size (printf "text yellow rotate-clockwise %s" $className)}} {{else}}{{/*failure, unknown*/}} {{svg "octicon-x-circle-fill" $size (printf "text red %s" $className)}} {{end}} diff --git a/templates/repo/issue/view_content/pull_merge_box.tmpl b/templates/repo/issue/view_content/pull_merge_box.tmpl index 2b943d2069..159910e36e 100644 --- a/templates/repo/issue/view_content/pull_merge_box.tmpl +++ b/templates/repo/issue/view_content/pull_merge_box.tmpl @@ -102,7 +102,7 @@ {{template "repo/issue/view_content/update_branch_by_merge" $}} {{else if .Issue.PullRequest.IsChecking}}
- {{svg "octicon-sync" 16 "circular-spin"}} + {{svg "gitea-running" 16 "rotate-clockwise"}} {{ctx.Locale.Tr "repo.pulls.is_checking"}}
{{else if .Issue.PullRequest.IsAncestor}} diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css index deaaf83680..779339c46b 100644 --- a/web_src/css/modules/animations.css +++ b/web_src/css/modules/animations.css @@ -116,14 +116,12 @@ code.language-math.is-loading::after { animation-timing-function: ease-in-out; } -/* FIXME: `octicon-sync` is counterclockwise, so this animation is also counterclockwise, it looks somewhat strange. -Ideally in the future we should use a better image for clockwise animation. */ -.circular-spin { - animation: circular-spin-keyframes 1s linear infinite; +.rotate-clockwise { + animation: rotate-clockwise-keyframes 1s linear infinite; } -@keyframes circular-spin-keyframes { +@keyframes rotate-clockwise-keyframes { 100% { - transform: rotate(-360deg); + transform: rotate(360deg); } } diff --git a/web_src/js/components/ActionRunStatus.vue b/web_src/js/components/ActionRunStatus.vue index 22f79384e3..24fae920a8 100644 --- a/web_src/js/components/ActionRunStatus.vue +++ b/web_src/js/components/ActionRunStatus.vue @@ -24,7 +24,7 @@ withDefaults(defineProps<{ - + diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 24c0f5300c..300f635793 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -608,7 +608,7 @@ export default defineComponent({ - + diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue index 9afacbbf24..efdf2bae71 100644 --- a/web_src/js/components/RepoCodeFrequency.vue +++ b/web_src/js/components/RepoCodeFrequency.vue @@ -150,7 +150,7 @@ const options: ChartOptions<'line'> = {
- + {{ locale.loadingInfo }}
diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue index c3a32fe3fc..08d63540e2 100644 --- a/web_src/js/components/RepoContributors.vue +++ b/web_src/js/components/RepoContributors.vue @@ -381,7 +381,7 @@ export default defineComponent({
- + {{ locale.loadingInfo }}
diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue index 8ca825993b..39e86781d1 100644 --- a/web_src/js/components/RepoRecentCommits.vue +++ b/web_src/js/components/RepoRecentCommits.vue @@ -128,7 +128,7 @@ const options: ChartOptions<'bar'> = {
- + {{ locale.loadingInfo }}
diff --git a/web_src/js/components/ViewFileTreeItem.vue b/web_src/js/components/ViewFileTreeItem.vue index 5173c7eb46..06d761dd65 100644 --- a/web_src/js/components/ViewFileTreeItem.vue +++ b/web_src/js/components/ViewFileTreeItem.vue @@ -62,7 +62,7 @@ const onItemClick = (e: MouseEvent) => { @click.stop="onItemClick" >
- +
From 5cb453c01b2baf8a0e9825de2316ddee8541782b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 2 Nov 2025 01:07:32 -0800 Subject: [PATCH 4/8] Revert gomail to v0.7.0 to fix sending mail failed (#35816) Revert gomail to the last work version to fix #35794 There is a problem between go mail v0.7.1 to prevent sending email work. https://github.com/wneessen/go-mail/compare/v0.7.0...v0.7.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 81187804a3..832d829f7a 100644 --- a/go.mod +++ b/go.mod @@ -109,7 +109,7 @@ require ( github.com/ulikunitz/xz v0.5.15 github.com/urfave/cli-docs/v3 v3.0.0-alpha6 github.com/urfave/cli/v3 v3.4.1 - github.com/wneessen/go-mail v0.7.2 + github.com/wneessen/go-mail v0.7.0 github.com/xeipuuv/gojsonschema v1.2.0 github.com/yohcop/openid-go v1.0.1 github.com/yuin/goldmark v1.7.13 diff --git a/go.sum b/go.sum index 02a710e7f0..054816fe9a 100644 --- a/go.sum +++ b/go.sum @@ -768,8 +768,8 @@ github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZ github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= -github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= +github.com/wneessen/go-mail v0.7.0 h1:/Wmgd5AVjp5PA+Ken5EFfr+QR83gmqHli9HcAhh0vnU= +github.com/wneessen/go-mail v0.7.0/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= From 685c8c314fe685f6b27dfab90e5d65c4752c02ce Mon Sep 17 00:00:00 2001 From: TheFox0x7 Date: Sun, 2 Nov 2025 10:42:25 +0100 Subject: [PATCH 5/8] Add cache to container build (#35697) add mount cache directives to container builds, which speeds up local builds bypassing node and go package download entirely on second build and caching go compilation. drop job level split on regular/rootless, which allows to reuse the previously made stage for rootless, skipping duplicate builds in CI. --------- Co-authored-by: Lunny Xiao --- .dockerignore | 3 + .github/workflows/pull-docker-dryrun.yml | 18 +++--- .github/workflows/release-nightly.yml | 78 +++++++++-------------- .github/workflows/release-tag-rc.yml | 52 +++++---------- .github/workflows/release-tag-version.yml | 50 +++++---------- Dockerfile | 38 +++++------ Dockerfile.rootless | 32 +++++----- 7 files changed, 109 insertions(+), 162 deletions(-) diff --git a/.dockerignore b/.dockerignore index 8e0d6b3666..c88fb144fe 100644 --- a/.dockerignore +++ b/.dockerignore @@ -74,6 +74,9 @@ cpu.out /VERSION /.air /.go-licenses +/Dockerfile +/Dockerfile.rootless +/.venv # Files and folders that were previously generated /public/assets/img/webpack diff --git a/.github/workflows/pull-docker-dryrun.yml b/.github/workflows/pull-docker-dryrun.yml index f74277de67..9c9dd2ffe6 100644 --- a/.github/workflows/pull-docker-dryrun.yml +++ b/.github/workflows/pull-docker-dryrun.yml @@ -11,25 +11,23 @@ jobs: files-changed: uses: ./.github/workflows/files-changed.yml - regular: + container: if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true' needs: files-changed runs-on: ubuntu-latest steps: + - uses: actions/checkout@v5 - uses: docker/setup-buildx-action@v3 - - uses: docker/build-push-action@v5 + - name: Build regular container image + uses: docker/build-push-action@v5 with: + context: . push: false tags: gitea/gitea:linux-amd64 - - rootless: - if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true' - needs: files-changed - runs-on: ubuntu-latest - steps: - - uses: docker/setup-buildx-action@v3 - - uses: docker/build-push-action@v5 + - name: Build rootless container image + uses: docker/build-push-action@v5 with: + context: . push: false file: Dockerfile.rootless tags: gitea/gitea:linux-amd64 diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index 16ce0fd643..ada4c18d33 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -56,7 +56,7 @@ jobs: - name: upload binaries to s3 run: | aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress - nightly-docker-rootful: + nightly-container: runs-on: namespace-profile-gitea-release-docker permissions: packages: write # to publish to ghcr.io @@ -65,10 +65,6 @@ jobs: # 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@v6 - with: - go-version-file: go.mod - check-latest: true - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - name: Get cleaned branch name @@ -76,6 +72,29 @@ jobs: run: | REF_NAME=$(echo "${{ github.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@v5 + 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@v5 + 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@v3 with: @@ -87,57 +106,20 @@ jobs: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: fetch go modules - run: make vendor - - name: build rootful docker image + - name: build regular docker image uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64,linux/riscv64 push: true - tags: |- - gitea/gitea:${{ steps.clean_name.outputs.branch }} - ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }} - nightly-docker-rootless: - runs-on: namespace-profile-gitea-release-docker - permissions: - packages: write # to publish to ghcr.io - steps: - - uses: actions/checkout@v5 - # 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@v6 - with: - go-version-file: go.mod - check-latest: true - - uses: docker/setup-qemu-action@v3 - - uses: docker/setup-buildx-action@v3 - - name: Get cleaned branch name - id: clean_name - run: | - REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//') - echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT" - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Login to GHCR using PAT - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: fetch go modules - run: make vendor + tags: ${{ steps.meta.outputs.tags }} + annotations: ${{ steps.meta.outputs.annotations }} - name: build rootless docker image uses: docker/build-push-action@v5 with: context: . - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64,linux/arm64,linux/riscv64 push: true file: Dockerfile.rootless - tags: |- - gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless - ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless + tags: ${{ steps.meta_rootless.outputs.tags }} + annotations: ${{ steps.meta_rootless.outputs.annotations }} diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml index c239ff392b..35558933e0 100644 --- a/.github/workflows/release-tag-rc.yml +++ b/.github/workflows/release-tag-rc.yml @@ -66,7 +66,7 @@ jobs: gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/* env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} - docker-rootful: + container: runs-on: namespace-profile-gitea-release-docker permissions: packages: write # to publish to ghcr.io @@ -88,38 +88,10 @@ jobs: # 1.2.3-rc0 tags: | type=semver,pattern={{version}} - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Login to GHCR using PAT - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: build rootful docker image - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64,linux/riscv64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - docker-rootless: - runs-on: namespace-profile-gitea-release-docker - permissions: - packages: write # to publish to ghcr.io - steps: - - uses: actions/checkout@v5 - # 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@v3 - - uses: docker/setup-buildx-action@v3 + annotations: | + org.opencontainers.image.authors="maintainers@gitea.io" - uses: docker/metadata-action@v5 - id: meta + id: meta_rootless with: images: |- gitea/gitea @@ -131,6 +103,8 @@ jobs: # 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@v3 with: @@ -142,12 +116,20 @@ jobs: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: build rootless docker image + - name: build regular container image + uses: docker/build-push-action@v5 + 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@v5 with: context: . platforms: linux/amd64,linux/arm64,linux/riscv64 push: true file: Dockerfile.rootless - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + 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 index 289b0e9d9c..56426d3bc3 100644 --- a/.github/workflows/release-tag-version.yml +++ b/.github/workflows/release-tag-version.yml @@ -70,7 +70,7 @@ jobs: gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/* env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} - docker-rootful: + container: runs-on: namespace-profile-gitea-release-docker permissions: packages: write # to publish to ghcr.io @@ -96,36 +96,10 @@ jobs: type=semver,pattern={{version}} type=semver,pattern={{major}} type=semver,pattern={{major}}.{{minor}} - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Login to GHCR using PAT - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: build rootful docker image - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64,linux/riscv64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - docker-rootless: - runs-on: namespace-profile-gitea-release-docker - steps: - - uses: actions/checkout@v5 - # 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@v3 - - uses: docker/setup-buildx-action@v3 + annotations: | + org.opencontainers.image.authors="maintainers@gitea.io" - uses: docker/metadata-action@v5 - id: meta + id: meta_rootless with: images: |- gitea/gitea @@ -142,6 +116,8 @@ jobs: 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@v3 with: @@ -153,12 +129,20 @@ jobs: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: build rootless docker image + - name: build regular container image + uses: docker/build-push-action@v5 + 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@v5 with: context: . platforms: linux/amd64,linux/arm64,linux/riscv64 push: true file: Dockerfile.rootless - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.meta_rootless.outputs.tags }} + annotations: ${{ steps.meta_rootless.outputs.annotations }} diff --git a/Dockerfile b/Dockerfile index b60d94cc47..7cee0f32d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ +# syntax=docker/dockerfile:1 # Build stage FROM docker.io/library/golang:1.25-alpine3.22 AS build-env -ARG GOPROXY -ENV GOPROXY=${GOPROXY:-direct} +ARG GOPROXY=direct ARG GITEA_VERSION ARG TAGS="sqlite sqlite_unlock_notify" @@ -14,22 +14,24 @@ RUN apk --no-cache add \ build-base \ git \ nodejs \ - npm \ - && npm install -g pnpm@10 \ - && rm -rf /var/cache/apk/* + pnpm -# Setup repo -COPY . ${GOPATH}/src/code.gitea.io/gitea WORKDIR ${GOPATH}/src/code.gitea.io/gitea +# Use COPY but not "mount" because some directories like "node_modules" contain platform-depended contents and these directories need to be ignored. +# ".git" directory will be mounted later separately for getting version data. +# TODO: in the future, maybe we can pre-build the frontend assets on one platform and share them for different platforms, the benefit is that it won't be affected by webpack plugin compatibility problems, then the working directory can be fully mounted and the COPY is not needed. +COPY --exclude=.git/ . . -# Checkout version if set -RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \ - && make clean-all build +# Build gitea, .git mount is required for version data +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target="/root/.cache/go-build" \ + --mount=type=cache,target=/root/.local/share/pnpm/store \ + --mount=type=bind,source=".git/",target=".git/" \ + make -# Copy local files COPY docker/root /tmp/local -# Set permissions +# 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/* \ @@ -37,8 +39,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \ /tmp/local/etc/s6/.s6-svscan/* \ /go/src/code.gitea.io/gitea/gitea -FROM docker.io/library/alpine:3.22 -LABEL maintainer="maintainers@gitea.io" +FROM docker.io/library/alpine:3.22 AS gitea EXPOSE 22 3000 @@ -53,8 +54,7 @@ RUN apk --no-cache add \ s6 \ sqlite \ su-exec \ - gnupg \ - && rm -rf /var/cache/apk/* + gnupg RUN addgroup \ -S -g 1000 \ @@ -68,6 +68,9 @@ RUN addgroup \ git && \ echo "git:*" | chpasswd -e +COPY --from=build-env /tmp/local / +COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea + ENV USER=git ENV GITEA_CUSTOM=/data/gitea @@ -75,6 +78,3 @@ VOLUME ["/data"] ENTRYPOINT ["/usr/bin/entrypoint"] CMD ["/usr/bin/s6-svscan", "/etc/s6"] - -COPY --from=build-env /tmp/local / -COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea diff --git a/Dockerfile.rootless b/Dockerfile.rootless index f7a0412be2..8a6fa587e9 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -1,40 +1,39 @@ +# syntax=docker/dockerfile:1 # Build stage FROM docker.io/library/golang:1.25-alpine3.22 AS build-env -ARG GOPROXY -ENV GOPROXY=${GOPROXY:-direct} +ARG GOPROXY=direct ARG GITEA_VERSION ARG TAGS="sqlite sqlite_unlock_notify" ENV TAGS="bindata timetzdata $TAGS" ARG CGO_EXTRA_CFLAGS -#Build deps +# Build deps RUN apk --no-cache add \ build-base \ git \ nodejs \ - npm \ - && npm install -g pnpm@10 \ - && rm -rf /var/cache/apk/* + pnpm -# Setup repo -COPY . ${GOPATH}/src/code.gitea.io/gitea WORKDIR ${GOPATH}/src/code.gitea.io/gitea +# See the comments in Dockerfile +COPY --exclude=.git/ . . -# Checkout version if set -RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \ - && make clean-all build +# Build gitea, .git mount is required for version data +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target="/root/.cache/go-build" \ + --mount=type=cache,target=/root/.local/share/pnpm/store \ + --mount=type=bind,source=".git/",target=".git/" \ + make -# Copy local files COPY docker/rootless /tmp/local -# Set permissions +# 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/code.gitea.io/gitea/gitea -FROM docker.io/library/alpine:3.22 -LABEL maintainer="maintainers@gitea.io" +FROM docker.io/library/alpine:3.22 AS gitea-rootless EXPOSE 2222 3000 @@ -46,8 +45,7 @@ RUN apk --no-cache add \ git \ curl \ gnupg \ - openssh-keygen \ - && rm -rf /var/cache/apk/* + openssh-keygen RUN addgroup \ -S -g 1000 \ From 17a6a2bab1ef1a4033ec80ee7d633a95bda7cef2 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 3 Nov 2025 11:32:45 -0800 Subject: [PATCH 6/8] upgrade go mail to 0.7.2 and fix the bug (#35833) patch from https://github.com/wneessen/go-mail/issues/504#issuecomment-3477890515. Thanks to @wneessen --- go.mod | 2 +- go.sum | 4 ++-- services/mailer/sender/smtp.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 832d829f7a..81187804a3 100644 --- a/go.mod +++ b/go.mod @@ -109,7 +109,7 @@ require ( github.com/ulikunitz/xz v0.5.15 github.com/urfave/cli-docs/v3 v3.0.0-alpha6 github.com/urfave/cli/v3 v3.4.1 - github.com/wneessen/go-mail v0.7.0 + github.com/wneessen/go-mail v0.7.2 github.com/xeipuuv/gojsonschema v1.2.0 github.com/yohcop/openid-go v1.0.1 github.com/yuin/goldmark v1.7.13 diff --git a/go.sum b/go.sum index 054816fe9a..02a710e7f0 100644 --- a/go.sum +++ b/go.sum @@ -768,8 +768,8 @@ github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZ github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/wneessen/go-mail v0.7.0 h1:/Wmgd5AVjp5PA+Ken5EFfr+QR83gmqHli9HcAhh0vnU= -github.com/wneessen/go-mail v0.7.0/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= +github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= +github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= diff --git a/services/mailer/sender/smtp.go b/services/mailer/sender/smtp.go index 8dc1b40b74..3207eee32f 100644 --- a/services/mailer/sender/smtp.go +++ b/services/mailer/sender/smtp.go @@ -128,7 +128,7 @@ func (s *SMTPSender) Send(from string, to []string, msg io.WriterTo) error { return fmt.Errorf("failed to issue MAIL command: %w", err) } } else { - if err = client.Mail(from); err != nil { + if err = client.Mail(fmt.Sprintf("<%s>", from)); err != nil { return fmt.Errorf("failed to issue MAIL command: %w", err) } } From aa7ec64a54f46cdcd89c291e5a2212626b8c8940 Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Mon, 3 Nov 2025 14:57:30 -0500 Subject: [PATCH 7/8] ignore .worktrees as a "special folder" (#35835) following the approach from nixpkgs that ignore the .worktrees folder, we could also do the same, this would allow worktrees to be worked on in the same folder as the primary branch. ref: https://github.com/NixOS/nixpkgs/commit/b6420c7bca86997ad66218dcf4fb902efc7ac4f6 --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 821b1b8c67..7e8e5f84a7 100644 --- a/.gitignore +++ b/.gitignore @@ -124,3 +124,6 @@ prime/ /AGENT.md /CLAUDE.md /llms.txt + +# Ignore worktrees when working on multiple branches +.worktrees/ From 37208fef7ef09edec7e7692b1ffc829a23cf6598 Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 3 Nov 2025 21:17:06 +0100 Subject: [PATCH 8/8] Fix a number of `strictNullChecks`-related issues (#35795) In preparation to work on enabling https://www.typescriptlang.org/tsconfig/#strictNullChecks, I fixed all the issues outside of `web_src` that came up when the option was enabled. There was also one lint issue in web_src that apparently only came up with the option enabled, so I fixed that as well. `isTruthy` is introduced because Typescript has a bug regarding `filter(Boolean)` which they are seemingly unwilling to fix. --------- Signed-off-by: silverwind --- tailwind.config.ts | 2 +- tests/e2e/utils_e2e.ts | 6 +++--- tools/generate-svg.ts | 26 +++++++++++++++++++++----- tsconfig.json | 1 + web_src/js/features/repo-issue.ts | 2 +- web_src/js/features/repo-settings.ts | 2 +- web_src/js/svg.ts | 2 +- webpack.config.ts | 6 +++--- 8 files changed, 32 insertions(+), 15 deletions(-) diff --git a/tailwind.config.ts b/tailwind.config.ts index 624ae47a5c..8693208e13 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -37,7 +37,7 @@ export default { './{build,models,modules,routers,services}/**/*.go', './templates/**/*.tmpl', './web_src/js/**/*.{ts,js,vue}', - ].filter(Boolean), + ].filter(Boolean as unknown as (x: T | boolean) => x is T), blocklist: [ // classes that don't work without CSS variables from "@tailwind base" which we don't use 'transform', 'shadow', 'ring', 'blur', 'grayscale', 'invert', '!invert', 'filter', '!filter', diff --git a/tests/e2e/utils_e2e.ts b/tests/e2e/utils_e2e.ts index 3e92e0d3c2..0973f0838c 100644 --- a/tests/e2e/utils_e2e.ts +++ b/tests/e2e/utils_e2e.ts @@ -33,15 +33,15 @@ export async function login_user(browser: Browser, workerInfo: WorkerInfo, user: } export async function load_logged_in_context(browser: Browser, workerInfo: WorkerInfo, user: string) { - let context; try { - context = await browser.newContext({storageState: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`}); + return await browser.newContext({storageState: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`}); } catch (err) { if (err.code === 'ENOENT') { throw new Error(`Could not find state for '${user}'. Did you call login_user(browser, workerInfo, '${user}') in test.beforeAll()?`); + } else { + throw err; } } - return context; } export async function save_visual(page: Page) { diff --git a/tools/generate-svg.ts b/tools/generate-svg.ts index b1dc46d451..c18eacc86f 100755 --- a/tools/generate-svg.ts +++ b/tools/generate-svg.ts @@ -47,6 +47,10 @@ function processAssetsSvgFiles(pattern: string, opts: Opts = {}) { return glob(pattern).map((path) => processAssetsSvgFile(path, opts)); } +function lowercaseKeys(obj: Record) { + return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key.toLowerCase(), value])); +} + async function processMaterialFileIcons() { const paths = glob('node_modules/material-icon-theme/icons/*.svg'); const svgSymbols: Record = {}; @@ -76,18 +80,30 @@ async function processMaterialFileIcons() { // * https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers // * https://github.com/microsoft/vscode/tree/1.98.0/extensions delete iconRules.iconDefinitions; - for (const [k, v] of Object.entries(iconRules.fileNames)) iconRules.fileNames[k.toLowerCase()] = v; - for (const [k, v] of Object.entries(iconRules.folderNames)) iconRules.folderNames[k.toLowerCase()] = v; - for (const [k, v] of Object.entries(iconRules.fileExtensions)) iconRules.fileExtensions[k.toLowerCase()] = v; + + if (iconRules.fileNames) { + iconRules.fileNames = lowercaseKeys(iconRules.fileNames); + } + if (iconRules.folderNames) { + iconRules.folderNames = lowercaseKeys(iconRules.folderNames); + } + if (iconRules.fileExtensions) { + iconRules.fileExtensions = lowercaseKeys(iconRules.fileExtensions); + } + // Use VSCode's "Language ID" mapping from its extensions for (const [_, langIdExtMap] of Object.entries(vscodeExtensions)) { for (const [langId, names] of Object.entries(langIdExtMap)) { for (const name of names) { const nameLower = name.toLowerCase(); if (nameLower[0] === '.') { - iconRules.fileExtensions[nameLower.substring(1)] ??= langId; + if (iconRules.fileExtensions) { + iconRules.fileExtensions[nameLower.substring(1)] ??= langId; + } } else { - iconRules.fileNames[nameLower] ??= langId; + if (iconRules.fileNames) { + iconRules.fileNames[nameLower] ??= langId; + } } } } diff --git a/tsconfig.json b/tsconfig.json index 3bc6065647..1daf4b7233 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -40,6 +40,7 @@ "strictBindCallApply": true, "strictBuiltinIteratorReturn": true, "strictFunctionTypes": true, + "strictNullChecks": false, "stripInternal": true, "verbatimModuleSyntax": true, "types": [ diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index 58274b95c0..bacdae5350 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -261,7 +261,7 @@ export function initRepoPullRequestReview() { if (commentDiv) { // get the name of the parent id const groupID = commentDiv.closest('div[id^="code-comments-"]')?.getAttribute('id'); - if (groupID && groupID.startsWith('code-comments-')) { + if (groupID?.startsWith('code-comments-')) { const id = groupID.slice(14); const ancestorDiffBox = commentDiv.closest('.diff-file-box'); diff --git a/web_src/js/features/repo-settings.ts b/web_src/js/features/repo-settings.ts index 43a236b0c6..7a8df8af0f 100644 --- a/web_src/js/features/repo-settings.ts +++ b/web_src/js/features/repo-settings.ts @@ -101,7 +101,7 @@ function initRepoSettingsBranches() { // show the `Matched` mark for the status checks that match the pattern const markMatchedStatusChecks = () => { const patterns = (document.querySelector('#status_check_contexts').value || '').split(/[\r\n]+/); - const validPatterns = patterns.map((item) => item.trim()).filter(Boolean); + const validPatterns = patterns.map((item) => item.trim()).filter(Boolean as unknown as (x: T | boolean) => x is T); const marks = document.querySelectorAll('.status-check-matched-mark'); for (const el of marks) { diff --git a/web_src/js/svg.ts b/web_src/js/svg.ts index 2ad9bffd51..3d3e653072 100644 --- a/web_src/js/svg.ts +++ b/web_src/js/svg.ts @@ -181,7 +181,7 @@ export function svg(name: SvgName, size = 16, classNames?: string | string[]): s svgNode.setAttribute('width', String(size)); svgNode.setAttribute('height', String(size)); } - if (className) svgNode.classList.add(...className.split(/\s+/).filter(Boolean)); + if (className) svgNode.classList.add(...className.split(/\s+/).filter(Boolean as unknown as (x: T | boolean) => x is T)); return serializeXml(svgNode); } diff --git a/webpack.config.ts b/webpack.config.ts index 9f31c01e88..f0528a7331 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -30,7 +30,7 @@ const isProduction = env.NODE_ENV !== 'development'; // false - all disabled let sourceMaps; if ('ENABLE_SOURCEMAP' in env) { - sourceMaps = ['true', 'false'].includes(env.ENABLE_SOURCEMAP) ? env.ENABLE_SOURCEMAP : 'reduced'; + sourceMaps = ['true', 'false'].includes(env.ENABLE_SOURCEMAP || '') ? env.ENABLE_SOURCEMAP : 'reduced'; } else { sourceMaps = isProduction ? 'reduced' : 'true'; } @@ -95,7 +95,7 @@ export default { path: fileURLToPath(new URL('public/assets', import.meta.url)), filename: () => 'js/[name].js', chunkFilename: ({chunk}) => { - const language = (/monaco.*languages?_.+?_(.+?)_/.exec(String(chunk.id)) || [])[1]; + const language = (/monaco.*languages?_.+?_(.+?)_/.exec(String(chunk?.id)) || [])[1]; return `js/${language ? `monaco-language-${language.toLowerCase()}` : `[name]`}.[contenthash:8].js`; }, }, @@ -270,7 +270,7 @@ export default { excludeAssets: [ /^js\/monaco-language-.+\.js$/, !isProduction && /^licenses.txt$/, - ].filter(Boolean), + ].filter(Boolean as unknown as (x: T | boolean) => x is T), groupAssetsByChunk: false, groupAssetsByEmitStatus: false, groupAssetsByInfo: false,