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/.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/ 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 \ 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/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=削除されたプロジェクト 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) } } 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/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/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/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)) + }) + }) + }) +} 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/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" >
- +
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,