diff --git a/.deadcode-out b/.deadcode-out index 338d22e258..61c5bcb055 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -1,7 +1,7 @@ -code.gitea.io/gitea/cmd +forgejo.org/cmd NoMainListener -code.gitea.io/gitea/cmd/forgejo +forgejo.org/cmd/forgejo ContextSetNoInit ContextSetNoExit ContextSetStderr @@ -9,94 +9,121 @@ code.gitea.io/gitea/cmd/forgejo ContextSetStdout ContextSetStdin -code.gitea.io/gitea/models +forgejo.org/models IsErrSHANotFound IsErrMergeDivergingFastForwardOnly -code.gitea.io/gitea/models/auth +forgejo.org/models/activities + GetActivityByID + NewFederatedUserActivity + CreateUserActivity + GetFollowingFeeds + FederatedUserActivity.loadActor + +forgejo.org/models/auth WebAuthnCredentials -code.gitea.io/gitea/models/db +forgejo.org/models/db TruncateBeans InTransaction DumpTables -code.gitea.io/gitea/models/dbfs +forgejo.org/models/dbfs file.renameTo Create Rename -code.gitea.io/gitea/models/forgefed +forgejo.org/models/forgefed GetFederationHost -code.gitea.io/gitea/models/forgejo/semver +forgejo.org/models/forgejo/semver GetVersion SetVersionString SetVersion -code.gitea.io/gitea/models/git +forgejo.org/models/git RemoveDeletedBranchByID -code.gitea.io/gitea/models/issues +forgejo.org/models/issues IsErrUnknownDependencyType IsErrIssueWasClosed -code.gitea.io/gitea/models/organization +forgejo.org/models/organization SearchMembersOptions.ToConds -code.gitea.io/gitea/models/perm/access +forgejo.org/models/perm/access GetRepoWriters -code.gitea.io/gitea/models/repo +forgejo.org/models/repo WatchRepoMode -code.gitea.io/gitea/models/user +forgejo.org/models/user IsErrExternalLoginUserAlreadyExist IsErrExternalLoginUserNotExist NewFederatedUser + NewFederatedUserFollower IsErrUserSettingIsNotExist GetUserAllSettings DeleteUserSetting + GetFederatedUser + GetFederatedUserByUserID + UpdateFederatedUser + GetFollowersForUser + AddFollower + RemoveFollower + IsFollowingAp -code.gitea.io/gitea/modules/activitypub +forgejo.org/modules/activitypub NewContext Context.APClientFactory -code.gitea.io/gitea/modules/assetfs +forgejo.org/modules/assetfs Bindata -code.gitea.io/gitea/modules/auth/password/hash +forgejo.org/modules/auth/password/hash DummyHasher.HashWithSaltBytes NewDummyHasher -code.gitea.io/gitea/modules/auth/password/pwn +forgejo.org/modules/auth/password/pwn WithHTTP -code.gitea.io/gitea/modules/base +forgejo.org/modules/base SetupGiteaRoot -code.gitea.io/gitea/modules/cache +forgejo.org/modules/cache GetInt WithNoCacheContext RemoveContextData -code.gitea.io/gitea/modules/emoji +forgejo.org/modules/emoji ReplaceCodes -code.gitea.io/gitea/modules/eventsource +forgejo.org/modules/eventsource Event.String -code.gitea.io/gitea/modules/forgefed +forgejo.org/modules/forgefed + NewForgeFollowFromAp + NewForgeFollow + ForgeFollow.MarshalJSON + ForgeFollow.UnmarshalJSON + ForgeFollow.Validate NewForgeUndoLike ForgeUndoLike.UnmarshalJSON ForgeUndoLike.Validate + NewForgeUserActivityFromAp + NewForgeUserActivity + ForgeUserActivity.Validate + NewPersonIDFromModel GetItemByType JSONUnmarshalerFn NotEmpty + NewForgeUserActivityNoteFromAp + newNote + ForgeUserActivityNote.Validate ToRepository OnRepository -code.gitea.io/gitea/modules/git +forgejo.org/modules/git AllowLFSFiltersArgs AddChanges AddChangesWithArgs @@ -106,55 +133,55 @@ code.gitea.io/gitea/modules/git openRepositoryWithDefaultContext ToEntryMode -code.gitea.io/gitea/modules/gitrepo +forgejo.org/modules/gitrepo GetBranchCommitID GetWikiDefaultBranch -code.gitea.io/gitea/modules/graceful +forgejo.org/modules/graceful Manager.TerminateContext Manager.Err Manager.Value Manager.Deadline -code.gitea.io/gitea/modules/hcaptcha +forgejo.org/modules/hcaptcha WithHTTP -code.gitea.io/gitea/modules/hostmatcher +forgejo.org/modules/hostmatcher HostMatchList.AppendPattern -code.gitea.io/gitea/modules/json +forgejo.org/modules/json StdJSON.Marshal StdJSON.Unmarshal StdJSON.NewEncoder StdJSON.NewDecoder StdJSON.Indent -code.gitea.io/gitea/modules/log +forgejo.org/modules/log NewEventWriterBuffer -code.gitea.io/gitea/modules/markup +forgejo.org/modules/markup GetRendererByType RenderString IsMarkupFile -code.gitea.io/gitea/modules/markup/console +forgejo.org/modules/markup/console Render RenderString -code.gitea.io/gitea/modules/markup/markdown +forgejo.org/modules/markup/markdown RenderRawString -code.gitea.io/gitea/modules/markup/mdstripper +forgejo.org/modules/markup/mdstripper stripRenderer.AddOptions StripMarkdown -code.gitea.io/gitea/modules/markup/orgmode +forgejo.org/modules/markup/orgmode RenderString -code.gitea.io/gitea/modules/process +forgejo.org/modules/process Manager.ExecTimeout -code.gitea.io/gitea/modules/queue +forgejo.org/modules/queue newBaseChannelSimple newBaseChannelUnique newBaseRedisSimple @@ -163,71 +190,73 @@ code.gitea.io/gitea/modules/queue testStateRecorder.Reset newWorkerPoolQueueForTest -code.gitea.io/gitea/modules/queue/lqinternal +forgejo.org/modules/queue/lqinternal QueueItemIDBytes QueueItemKeyBytes ListLevelQueueKeys -code.gitea.io/gitea/modules/setting +forgejo.org/modules/setting NewConfigProviderFromData GitConfigType.GetOption InitLoggersForTest -code.gitea.io/gitea/modules/sync +forgejo.org/modules/sync StatusTable.Start StatusTable.IsRunning -code.gitea.io/gitea/modules/timeutil +forgejo.org/modules/timeutil GetExecutableModTime MockSet MockUnset -code.gitea.io/gitea/modules/translation +forgejo.org/modules/translation MockLocale.Language MockLocale.TrString MockLocale.Tr MockLocale.TrN MockLocale.TrPluralString + MockLocale.TrPluralStringAllForms MockLocale.TrSize MockLocale.HasKey MockLocale.PrettyNumber -code.gitea.io/gitea/modules/util +forgejo.org/modules/translation/localeiter + IterateMessagesContent + +forgejo.org/modules/util OptionalArg -code.gitea.io/gitea/modules/util/filebuffer +forgejo.org/modules/util/filebuffer CreateFromReader -code.gitea.io/gitea/modules/validation +forgejo.org/modules/validation IsErrNotValid + ValidateIDExists -code.gitea.io/gitea/modules/web +forgejo.org/modules/web RouteMock RouteMockReset -code.gitea.io/gitea/modules/zstd +forgejo.org/modules/zstd NewWriter Writer.Write Writer.Close -code.gitea.io/gitea/routers/web - NotFound - -code.gitea.io/gitea/routers/web/org +forgejo.org/routers/web/org MustEnableProjects -code.gitea.io/gitea/services/context +forgejo.org/services/context GetPrivateContext -code.gitea.io/gitea/services/repository +forgejo.org/services/repository IsErrForkAlreadyExist -code.gitea.io/gitea/services/repository/files +forgejo.org/services/repository/files ContentType.String -code.gitea.io/gitea/services/repository/gitgraph +forgejo.org/services/repository/gitgraph Parser.Reset -code.gitea.io/gitea/services/webhook +forgejo.org/services/webhook NewNotifier diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f3d30963c7..28fa9e4555 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,13 +4,9 @@ "features": { // installs nodejs into container "ghcr.io/devcontainers/features/node:1": { - "version": "20" - }, - "ghcr.io/devcontainers/features/git-lfs:1.2.3": {}, - "ghcr.io/devcontainers-contrib/features/poetry:2": {}, - "ghcr.io/devcontainers/features/python:1": { - "version": "3.12" + "version": "22" }, + "ghcr.io/devcontainers/features/git-lfs:1.2.4": {}, "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} }, "customizations": { diff --git a/.dockerignore b/.dockerignore index 5e7a893014..807c70b000 100644 --- a/.dockerignore +++ b/.dockerignore @@ -37,13 +37,9 @@ coverage.all coverage/ cpu.out -/modules/migration/bindata.go /modules/migration/bindata.go.hash -/modules/options/bindata.go /modules/options/bindata.go.hash -/modules/public/bindata.go /modules/public/bindata.go.hash -/modules/templates/bindata.go /modules/templates/bindata.go.hash *.db diff --git a/.editorconfig b/.editorconfig index a547e8a585..5476eb02fb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,9 @@ insert_final_newline = true [{*.{go,tmpl,html},Makefile,go.mod}] indent_style = tab +[go.*] +indent_style = tab + [templates/custom/*.tmpl] insert_final_newline = false diff --git a/.forgejo/issue_template/bug-report-ui.yaml b/.forgejo/issue_template/bug-report-ui.yaml index 57d578b232..8bb7bf1d49 100644 --- a/.forgejo/issue_template/bug-report-ui.yaml +++ b/.forgejo/issue_template/bug-report-ui.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - **NOTE: If your issue is a security concern, please email (GPG: `A4676E79`) instead of opening a public issue.** + **NOTE: If your issue is a security concern, please email ([security.txt](https://forgejo.org/.well-known/security.txt)) instead of opening a public issue.** - type: markdown attributes: value: | diff --git a/.forgejo/issue_template/bug-report.yaml b/.forgejo/issue_template/bug-report.yaml index 6e9b116e60..a2b50dbca2 100644 --- a/.forgejo/issue_template/bug-report.yaml +++ b/.forgejo/issue_template/bug-report.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - **NOTE: If your issue is a security concern, please email (GPG: `A4676E79`) instead of opening a public issue.** + **NOTE: If your issue is a security concern, please email ([security.txt](https://forgejo.org/.well-known/security.txt)) instead of opening a public issue.** - type: markdown attributes: value: | diff --git a/.forgejo/testdata/build-release/Dockerfile b/.forgejo/testdata/build-release/Dockerfile index d10564359e..09cce06c47 100644 --- a/.forgejo/testdata/build-release/Dockerfile +++ b/.forgejo/testdata/build-release/Dockerfile @@ -1,4 +1,4 @@ -FROM data.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.22 ARG RELEASE_VERSION=unkown LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.version="${RELEASE_VERSION}" diff --git a/.forgejo/testdata/build-release/go.mod b/.forgejo/testdata/build-release/go.mod index 729cb6f831..585dcc4f3d 100644 --- a/.forgejo/testdata/build-release/go.mod +++ b/.forgejo/testdata/build-release/go.mod @@ -1,3 +1,3 @@ -module code.gitea.io/gitea +module forgejo.org go 1.23.3 diff --git a/.forgejo/workflows-composite/apt-install-from/action.yaml b/.forgejo/workflows-composite/apt-install-from/action.yaml index 615e7cb184..4ce4805cd7 100644 --- a/.forgejo/workflows-composite/apt-install-from/action.yaml +++ b/.forgejo/workflows-composite/apt-install-from/action.yaml @@ -13,17 +13,20 @@ runs: run: | export DEBIAN_FRONTEND=noninteractive echo "deb http://deb.debian.org/debian/ ${RELEASE} main" > "/etc/apt/sources.list.d/${RELEASE}.list" + wget -O- http://neuro.debian.net/lists/bookworm.de-fzj.libre | tee /etc/apt/sources.list.d/neurodebian.sources.list + apt-key adv --recv-keys --keyserver hkps://keyserver.ubuntu.com 0xA5D32F012649A5A9 env: RELEASE: ${{inputs.release}} - name: install packages run: | apt-get update -qq - apt-get -q install -qq -y ${PACKAGES} + apt-get -q install --allow-downgrades -qq -y ${PACKAGES} env: PACKAGES: ${{inputs.packages}} - name: remove temporary package list to prevent using it in other steps run: | rm "/etc/apt/sources.list.d/${RELEASE}.list" + rm "/etc/apt/sources.list.d/neurodebian.sources.list" apt-get update -qq env: RELEASE: ${{inputs.release}} diff --git a/.forgejo/workflows/backport.yml b/.forgejo/workflows/backport.yml index 31c5c0cc3a..573c33f93b 100644 --- a/.forgejo/workflows/backport.yml +++ b/.forgejo/workflows/backport.yml @@ -47,7 +47,7 @@ jobs: cat <<'EOF' ${{ toJSON(github) }} EOF - - uses: https://data.forgejo.org/actions/git-backporting@v4.8.4 + - uses: https://data.forgejo.org/actions/git-backporting@v4.8.5 with: target-branch-pattern: "^backport/(?(v.*))$" strategy: ort diff --git a/.forgejo/workflows/build-oci-image.yml b/.forgejo/workflows/build-oci-image.yml new file mode 100644 index 0000000000..8e843b41ee --- /dev/null +++ b/.forgejo/workflows/build-oci-image.yml @@ -0,0 +1,41 @@ +on: + push: + branches: + - 'forgejo' + tags: + - '*-git-annex*' + +jobs: + build-oci-image: + runs-on: docker + strategy: + matrix: + type: ["rootful", "rootless"] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # fetch the full history so that the Forgejo version is determined properly + - name: Determine registry and username + id: determine-registry-and-username + run: | + echo "registry=${GITHUB_SERVER_URL#https://}" >> "$GITHUB_OUTPUT" + echo "username=${GITHUB_REPOSITORY%/*}" >> "$GITHUB_OUTPUT" + - name: Install Docker + run: curl -fsSL https://get.docker.com | sh + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: ${{ steps.determine-registry-and-username.outputs.registry }} + username: ${{ steps.determine-registry-and-username.outputs.username }} + password: ${{ secrets.REGISTRY_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ (matrix.type == 'rootful' && 'Dockerfile') || (matrix.type == 'rootless' && 'Dockerfile.rootless') }} + push: true + tags: ${{ steps.determine-registry-and-username.outputs.registry }}/${{ github.repository }}:${{ github.ref_name }}${{ (matrix.type == 'rootful' && ' ') || (matrix.type == 'rootless' && '-rootless') }} diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index a34f3533fd..3ab63b0589 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -164,7 +164,7 @@ jobs: - name: build container & release if: ${{ secrets.TOKEN != '' }} - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" @@ -183,7 +183,7 @@ jobs: - name: build rootless container if: ${{ secrets.TOKEN != '' }} - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.4 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" diff --git a/.forgejo/workflows/merge-requirements.yml b/.forgejo/workflows/merge-requirements.yml index b052f18c06..9aaf2af68d 100644 --- a/.forgejo/workflows/merge-requirements.yml +++ b/.forgejo/workflows/merge-requirements.yml @@ -31,7 +31,7 @@ jobs: || contains(toJSON(github.event.pull_request.labels), 'test/manual') ) run: | - echo "Test label must be set to either 'present', 'not-needed' or 'manual'." + echo "A team member must set the label to either 'present', 'not-needed' or 'manual'." exit 1 - name: Missing manual test instructions if: > diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 0863a1597c..3aec46fb03 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -2,6 +2,8 @@ # # See also https://forgejo.org/docs/next/contributor/release/#stable-release-process # +# TOKEN_NEXT_DIGEST is a token with write repository access to https://invisible.forgejo.org/infrastructure/next-digest issued by https://invisible.forgejo.org/forgejo-next-digest +# # https://codeberg.org/forgejo-experimental/forgejo # # Copies a release from codeberg.org/forgejo-integration to codeberg.org/forgejo-experimental @@ -14,7 +16,7 @@ # vars.DOER: forgejo-experimental-ci # secrets.TOKEN: # -# http://private.forgejo.org/forgejo/forgejo +# http://invisible.forgejo.org/forgejo/forgejo # # Copies & sign a release from codeberg.org/forgejo-integration to codeberg.org/forgejo # @@ -42,7 +44,7 @@ jobs: - uses: https://data.forgejo.org/actions/checkout@v4 - name: copy & sign - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.4 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.5 with: from-forgejo: ${{ vars.FORGEJO }} to-forgejo: ${{ vars.FORGEJO }} @@ -80,7 +82,7 @@ jobs: - name: upgrade v*.next.forgejo.org uses: https://data.forgejo.org/infrastructure/next-digest@v1.1.0 with: - url: https://placeholder:${{ secrets.TOKEN_NEXT_DIGEST }}@code.forgejo.org/infrastructure/next-digest + url: https://placeholder:${{ secrets.TOKEN_NEXT_DIGEST }}@invisible.forgejo.org/infrastructure/next-digest ref_name: '${{ github.ref_name }}' image: 'codeberg.org/forgejo-experimental/forgejo' tag_suffix: '-rootless' diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index db33d30afb..7f77098357 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -4,6 +4,9 @@ on: schedule: - cron: '@daily' +env: + RNA_VERSION: v1.2.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + jobs: release-notes: if: vars.ROLE == 'forgejo-coding' @@ -29,5 +32,5 @@ jobs: set -x curl -sS $GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/milestones?state=open | jq -r '.[] | .title' | while read forgejo version ; do milestone="$forgejo $version" - go run code.forgejo.org/forgejo/release-notes-assistant@v1.1.1 --config .release-notes-assistant.yaml --storage milestone --storage-location "$milestone" --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} release $version + go run code.forgejo.org/forgejo/release-notes-assistant@$RNA_VERSION --config .release-notes-assistant.yaml --storage milestone --storage-location "$milestone" --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} release $version done diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index 92edd912ec..cdcd2e6fe4 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -7,6 +7,9 @@ on: - synchronize - labeled +env: + RNA_VERSION: v1.2.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + jobs: release-notes: if: ( vars.ROLE == 'forgejo-coding' ) && contains(github.event.pull_request.labels.*.name, 'worth a release-note') @@ -38,4 +41,4 @@ jobs: - name: release-notes-assistant preview run: | - go run code.forgejo.org/forgejo/release-notes-assistant@v1.1.1 --config .release-notes-assistant.yaml --storage pr --storage-location ${{ github.event.pull_request.number }} --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} preview ${{ github.event.pull_request.number }} + go run code.forgejo.org/forgejo/release-notes-assistant@$RNA_VERSION --config .release-notes-assistant.yaml --storage pr --storage-location ${{ github.event.pull_request.number }} --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} preview ${{ github.event.pull_request.number }} diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index dbba9a82bb..5aa6c8cd98 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -20,7 +20,7 @@ env: RENOVATE_REPOSITORIES: ${{ github.repository }} # fix because 10.0.0-58-7e1df53+gitea-1.22.0 < 10.0.0 for semver # and codeberg api returns such versions from `git describe --tags` - RENOVATE_X_PLATFORM_VERSION: 10.0.0+gitea-1.22.0 + # RENOVATE_X_PLATFORM_VERSION: 10.0.0+gitea-1.22.0 currently not needed jobs: renovate: @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:39.212.0 + image: data.forgejo.org/renovate/renovate:41.1.4 steps: - name: Load renovate repo cache diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml new file mode 100644 index 0000000000..9e5cfb92ed --- /dev/null +++ b/.forgejo/workflows/testing-integration.yml @@ -0,0 +1,71 @@ +# +# Additional integration tests designed to run once a day when +# `mirror.yml` pushes to https://codeberg.org/forgejo-integration/forgejo +# and send a notification via email should they fail. +# +# For debug purposes: +# +# - uncomment [on].pull_request +# - swap 'forgejo-integration' and 'forgejo-coding' +# - open a pull request at https://codeberg.org/forgejo/forgejo and fix things +# - swap 'forgejo-integration' and 'forgejo-coding' +# - comment [on].pull_request +# + +name: testing-integration + +on: +# pull_request: + push: + tags: 'v[0-9]+.[0-9]+.*' + branches: + - 'forgejo' + - 'v*/forgejo' + +jobs: + test-unit: +# if: vars.ROLE == 'forgejo-coding' + if: vars.ROLE == 'forgejo-integration' + runs-on: docker + container: + image: 'data.forgejo.org/oci/node:22-bookworm' + options: --tmpfs /tmp:exec,noatime + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/workflows-composite/setup-env + - name: install git 2.30 + uses: ./.forgejo/workflows-composite/apt-install-from + with: + packages: git/bullseye git-lfs/bullseye + release: bullseye + - uses: ./.forgejo/workflows-composite/build-backend + - run: | + su forgejo -c 'make test-backend test-check' + timeout-minutes: 120 + env: + RACE_ENABLED: 'true' + TAGS: bindata + test-sqlite: +# if: vars.ROLE == 'forgejo-coding' + if: vars.ROLE == 'forgejo-integration' + runs-on: docker + container: + image: 'data.forgejo.org/oci/node:22-bookworm' + options: --tmpfs /tmp:exec,noatime + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/workflows-composite/setup-env + - name: install git 2.30 + uses: ./.forgejo/workflows-composite/apt-install-from + with: + packages: git/bullseye git-lfs/bullseye + release: bullseye + - uses: ./.forgejo/workflows-composite/build-backend + - run: | + su forgejo -c 'make test-sqlite-migration test-sqlite' + timeout-minutes: 120 + env: + TAGS: sqlite sqlite_unlock_notify + RACE_ENABLED: true + TEST_TAGS: sqlite sqlite_unlock_notify + USE_REPO_TEST_DIR: 1 diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index 62136b1b28..3897b7ed51 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -10,7 +10,6 @@ on: jobs: backend-checks: - if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker container: image: 'data.forgejo.org/oci/node:22-bookworm' @@ -27,7 +26,6 @@ jobs: - run: su forgejo -c 'make --always-make -j$(nproc) lint-backend tidy-check swagger-check lint-swagger fmt-check swagger-validate' # ensure the "go-licenses" make target runs - uses: ./.forgejo/workflows-composite/build-backend frontend-checks: - if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker container: image: 'data.forgejo.org/oci/node:22-bookworm' @@ -91,6 +89,7 @@ jobs: RACE_ENABLED: 'true' TAGS: bindata TEST_ELASTICSEARCH_URL: http://elasticsearch:9200 + TEST_MINIO_ENDPOINT: minio:9000 test-e2e: if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker @@ -114,9 +113,14 @@ jobs: run: | su forgejo -c 'make deps-frontend frontend' - uses: ./.forgejo/workflows-composite/build-backend + - name: Decide to run all tests + id: run-all + if: contains(github.event.pull_request.labels.*.name, 'run-all-playwright-tests') || contains(github.event.pull_request.title, 'playwright') + run: | + echo "all=1" >> "$GITHUB_OUTPUT" - name: Get changed files id: changed-files - uses: https://data.forgejo.org/tj-actions/changed-files@v45 + uses: https://data.forgejo.org/tj-actions/changed-files@v46 with: separator: '\n' - run: | @@ -126,6 +130,7 @@ jobs: USE_REPO_TEST_DIR: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 CHANGED_FILES: ${{steps.changed-files.outputs.all_changed_files}} + RUN_ALL: ${{steps.run-all.all}} - name: Upload test artifacts on failure if: failure() uses: https://data.forgejo.org/forgejo/upload-artifact@v4 @@ -176,7 +181,6 @@ jobs: TAGS: bindata TEST_REDIS_SERVER: cacher:${{ matrix.cacher.port }} test-mysql: - if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker needs: [backend-checks, frontend-checks] container: @@ -199,15 +203,13 @@ jobs: - name: install dependencies & git >= 2.42 uses: ./.forgejo/workflows-composite/apt-install-from with: - packages: git git-lfs + packages: git git-annex-standalone git-lfs - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-mysql-migration test-mysql' - timeout-minutes: 120 env: USE_REPO_TEST_DIR: 1 test-pgsql: - if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker needs: [backend-checks, frontend-checks] container: @@ -236,17 +238,15 @@ jobs: - name: install dependencies & git >= 2.42 uses: ./.forgejo/workflows-composite/apt-install-from with: - packages: git git-lfs + packages: git git-annex-standalone git-lfs - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-pgsql-migration test-pgsql' - timeout-minutes: 120 env: RACE_ENABLED: true USE_REPO_TEST_DIR: 1 TEST_LDAP: 1 test-sqlite: - if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker needs: [backend-checks, frontend-checks] container: @@ -258,25 +258,21 @@ jobs: - name: install dependencies & git >= 2.42 uses: ./.forgejo/workflows-composite/apt-install-from with: - packages: git git-lfs + packages: git git-annex-standalone git-lfs - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-sqlite-migration test-sqlite' - timeout-minutes: 120 env: TAGS: sqlite sqlite_unlock_notify RACE_ENABLED: true TEST_TAGS: sqlite sqlite_unlock_notify USE_REPO_TEST_DIR: 1 security-check: - if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' runs-on: docker needs: - test-sqlite - test-pgsql - test-mysql - - test-remote-cacher - - test-unit container: image: 'data.forgejo.org/oci/node:22-bookworm' options: --tmpfs /tmp:exec,noatime diff --git a/.gitignore b/.gitignore index 79a4108dab..744e24a09a 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ cpu.out /gitea-vet /debug /integrations.test +/forgejo /bin /dist diff --git a/.golangci.yml b/.golangci.yml index cceb7070e7..532132838d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,7 +1,9 @@ +version: "2" +output: + sort-order: + - file linters: - enable-all: false - disable-all: true - fast: false + default: none enable: - bidichk - depguard @@ -9,142 +11,155 @@ linters: - errcheck - forbidigo - gocritic - - gofmt - - gofumpt - - gosimple - govet - ineffassign - nakedret - nolintlint - revive - staticcheck - - stylecheck - testifylint - - typecheck - unconvert - - unused - unparam + - unused - usetesting - wastedassign - -run: - timeout: 10m - -output: - sort-results: true - sort-order: [file] - show-stats: true - -linters-settings: - stylecheck: - checks: ["all", "-ST1005", "-ST1003"] - nakedret: - max-func-lines: 0 - gocritic: - disabled-checks: - - ifElseChain - revive: - severity: error + 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: forgejo.org/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: github.com/minio/sha256-simd + desc: use crypto/sha256 instead, see https://codeberg.org/forgejo/forgejo/pulls/1528 + gocritic: + disabled-checks: + - ifElseChain + revive: + severity: error + rules: + - name: atomic + - name: bare-return + - name: blank-imports + - name: constant-logical-expr + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: duplicated-imports + - name: empty-lines + - name: error-naming + - name: error-return + - name: error-strings + - name: errorf + - name: exported + - name: identical-branches + - name: if-return + - name: increment-decrement + - name: indent-error-flow + - name: modifies-value-receiver + - name: package-comments + - name: range + - name: receiver-naming + - name: redefines-builtin-id + - name: string-of-int + - name: superfluous-else + - name: time-naming + - name: unconditional-recursion + - name: unexported-return + - name: unreachable-code + - name: var-declaration + - name: var-naming + - name: redefines-builtin-id + disabled: true + staticcheck: + checks: + - all + testifylint: + disable: + - go-require + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling rules: - - name: atomic - - name: bare-return - - name: blank-imports - - name: constant-logical-expr - - name: context-as-argument - - name: context-keys-type - - name: dot-imports - - name: duplicated-imports - - name: empty-lines - - name: error-naming - - name: error-return - - name: error-strings - - name: errorf - - name: exported - - name: identical-branches - - name: if-return - - name: increment-decrement - - name: indent-error-flow - - name: modifies-value-receiver - - name: package-comments - - name: range - - name: receiver-naming - - name: redefines-builtin-id - - name: string-of-int - - name: superfluous-else - - name: time-naming - - name: unconditional-recursion - - name: unexported-return - - name: unreachable-code - - name: var-declaration - - name: var-naming - - name: redefines-builtin-id - disabled: true - gofumpt: - extra-rules: true - 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: code.gitea.io/gitea/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: github.com/minio/sha256-simd - desc: use crypto/sha256 instead, see https://codeberg.org/forgejo/forgejo/pulls/1528 - testifylint: - disable: - - go-require - + - linters: + - nolintlint + path: models/db/sql_postgres_with_schema.go + - linters: + - dupl + - errcheck + - gocyclo + - gosec + - staticcheck + - unparam + path: _test\.go + - linters: + - dupl + - errcheck + - gocyclo + - gosec + path: models/migrations/v + - linters: + - forbidigo + path: cmd + - linters: + - dupl + text: (?i)webhook + - linters: + - gocritic + text: (?i)`ID' should not be capitalized + - linters: + - deadcode + - unused + text: (?i)swagger + - linters: + - staticcheck + text: (?i)argument x is overwritten before first use + - linters: + - gocritic + text: '(?i)commentFormatting: put a space between `//` and comment text' + - linters: + - gocritic + text: '(?i)exitAfterDefer:' + - linters: + - staticcheck + text: "(ST1005|ST1003|QF1001):" + paths: + - node_modules + - public + - web_src + - third_party$ + - builtin$ + - examples$ issues: max-issues-per-linter: 0 max-same-issues: 0 - exclude-dirs: [node_modules, public, web_src] - exclude-case-sensitive: true - exclude-rules: - - path: models/db/sql_postgres_with_schema.go - linters: - - nolintlint - - path: _test\.go - linters: - - gocyclo - - errcheck - - dupl - - gosec - - unparam - - staticcheck - - path: models/migrations/v - linters: - - gocyclo - - errcheck - - dupl - - gosec - - path: cmd - linters: - - forbidigo - - text: "webhook" - linters: - - dupl - - text: "`ID' should not be capitalized" - linters: - - gocritic - - text: "swagger" - linters: - - unused - - deadcode - - text: "argument x is overwritten before first use" - linters: - - staticcheck - - text: "commentFormatting: put a space between `//` and comment text" - linters: - - gocritic - - text: "exitAfterDefer:" - linters: - - gocritic +formatters: + enable: + - gofmt + - gofumpt + settings: + gofumpt: + extra-rules: true + exclusions: + generated: lax + paths: + - node_modules + - public + - web_src + - third_party$ + - builtin$ + - examples$ diff --git a/BSDmakefile b/BSDmakefile index 79696eadcf..f4a819ff93 100644 --- a/BSDmakefile +++ b/BSDmakefile @@ -36,10 +36,6 @@ GARGS = "--no-print-directory" 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 = @@ -48,11 +44,12 @@ GPREFIX = .endif .BEGIN: .SILENT - which $(GMAKE) || (printf "Error: GNU Make is required!\n\n" 1>&2 && false) + which $(GMAKE) >/dev/null || (printf "Error: GNU Make is required!\n\n" 1>&2 && false) -.PHONY: FRC -$(.TARGETS): FRC - $(GMAKE) $(GPREFIX) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG) +.PHONY: EMPTY +EMPTY: .SILENT + $(GMAKE) $(GPREFIX) $(GARGS) $(JARG) -.DONE .DEFAULT: .SILENT - $(GMAKE) $(GPREFIX) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG) +.PHONY: $(.TARGETS) +$(.TARGETS): .SILENT + $(GMAKE) $(GPREFIX) $(GARGS) $(JARG) $@ diff --git a/CODEOWNERS b/CODEOWNERS index ff2a4b9fdd..34cdceca09 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -9,10 +9,11 @@ # Files related to frontend development. # Javascript and CSS code. -web_src/.* @caesar @crystal @gusted +web_src/.* @beowulf @gusted +web_src/css/.* @0ko # HTML templates used by the backend. -templates/.* @caesar @crystal @gusted +templates/.* @beowulf @gusted ## the issue sidebar was touched by fnetx templates/repo/issue/view_content/sidebar.* @fnetx diff --git a/Dockerfile b/Dockerfile index ebe41ed5c8..ebd4e4f5e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.22 AS build-env ARG GOPROXY -ENV GOPROXY=${GOPROXY:-direct} +ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct} ARG RELEASE_VERSION ARG TAGS="sqlite sqlite_unlock_notify" @@ -30,13 +30,13 @@ RUN cp /*-alpine-linux-musl*/lib/ld-musl-*.so.1 /lib || true RUN apk --no-cache add build-base git nodejs npm -COPY . ${GOPATH}/src/code.gitea.io/gitea -WORKDIR ${GOPATH}/src/code.gitea.io/gitea +COPY . ${GOPATH}/src/forgejo.org +WORKDIR ${GOPATH}/src/forgejo.org -RUN make clean +RUN make clean-no-bindata RUN make frontend RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini -RUN LDFLAGS="-buildid=" make RELEASE_VERSION=$RELEASE_VERSION GOFLAGS="-trimpath" go-check generate-backend static-executable && xx-verify gitea +RUN LDFLAGS="-buildid=" make FORGEJO_GENERATE_SKIP_HASH=true RELEASE_VERSION=$RELEASE_VERSION GOFLAGS="-trimpath" go-check generate-backend static-executable && xx-verify gitea # Copy local files COPY docker/root /tmp/local @@ -47,11 +47,11 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \ /tmp/local/etc/s6/gitea/* \ /tmp/local/etc/s6/openssh/* \ /tmp/local/etc/s6/.s6-svscan/* \ - /go/src/code.gitea.io/gitea/gitea \ - /go/src/code.gitea.io/gitea/environment-to-ini -RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete + /go/src/forgejo.org/gitea \ + /go/src/forgejo.org/environment-to-ini +RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete -FROM data.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.22 ARG RELEASE_VERSION LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.authors="Forgejo" \ @@ -78,6 +78,7 @@ RUN apk --no-cache add \ sqlite \ su-exec \ gnupg \ + git-annex \ && rm -rf /var/cache/apk/* RUN addgroup \ @@ -102,7 +103,7 @@ CMD ["/usr/bin/s6-svscan", "/etc/s6"] COPY --from=build-env /tmp/local / RUN cd /usr/local/bin ; ln -s gitea forgejo -COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea +COPY --from=build-env /go/src/forgejo.org/gitea /app/gitea/gitea RUN ln -s /app/gitea/gitea /app/gitea/forgejo-cli -COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini -COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh +COPY --from=build-env /go/src/forgejo.org/environment-to-ini /usr/local/bin/environment-to-ini +COPY --from=build-env /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh diff --git a/Dockerfile.rootless b/Dockerfile.rootless index 93004610a7..b347ab6f7e 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -1,9 +1,9 @@ FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.21 AS build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.24-alpine3.22 AS build-env ARG GOPROXY -ENV GOPROXY=${GOPROXY:-direct} +ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct} ARG RELEASE_VERSION ARG TAGS="sqlite sqlite_unlock_notify" @@ -30,13 +30,13 @@ RUN cp /*-alpine-linux-musl*/lib/ld-musl-*.so.1 /lib || true RUN apk --no-cache add build-base git nodejs npm -COPY . ${GOPATH}/src/code.gitea.io/gitea -WORKDIR ${GOPATH}/src/code.gitea.io/gitea +COPY . ${GOPATH}/src/forgejo.org +WORKDIR ${GOPATH}/src/forgejo.org -RUN make clean +RUN make clean-no-bindata RUN make frontend RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini -RUN make RELEASE_VERSION=$RELEASE_VERSION go-check generate-backend static-executable && xx-verify gitea +RUN make FORGEJO_GENERATE_SKIP_HASH=true RELEASE_VERSION=$RELEASE_VERSION go-check generate-backend static-executable && xx-verify gitea # Copy local files COPY docker/rootless /tmp/local @@ -45,11 +45,11 @@ COPY docker/rootless /tmp/local RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \ /tmp/local/usr/local/bin/docker-setup.sh \ /tmp/local/usr/local/bin/gitea \ - /go/src/code.gitea.io/gitea/gitea \ - /go/src/code.gitea.io/gitea/environment-to-ini -RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete + /go/src/forgejo.org/gitea \ + /go/src/forgejo.org/environment-to-ini +RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete -FROM data.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.22 ARG RELEASE_VERSION LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.authors="Forgejo" \ @@ -73,6 +73,7 @@ RUN apk --no-cache add \ curl \ gnupg \ openssh-client \ + git-annex \ && rm -rf /var/cache/apk/* RUN addgroup \ @@ -91,10 +92,10 @@ RUN chown git:git /var/lib/gitea /etc/gitea COPY --from=build-env /tmp/local / RUN cd /usr/local/bin ; ln -s gitea forgejo -COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea +COPY --from=build-env --chown=root:root /go/src/forgejo.org/gitea /app/gitea/gitea RUN ln -s /app/gitea/gitea /app/gitea/forgejo-cli -COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini -COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh +COPY --from=build-env --chown=root:root /go/src/forgejo.org/environment-to-ini /usr/local/bin/environment-to-ini +COPY --from=build-env /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh #git:git USER 1000:1000 diff --git a/Makefile b/Makefile index ff9de004c9..82a1fe4d56 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ self := $(location) @tmpdir=`mktemp --tmpdir -d` ; \ echo Using temporary directory $$tmpdir for test repositories ; \ USE_REPO_TEST_DIR= $(MAKE) -f $(self) --no-print-directory REPO_TEST_DIR=$$tmpdir/ $@ ; \ - STATUS=$$? ; rm -r "$$tmpdir" ; exit $$STATUS + STATUS=$$? ; chmod -R +w "$$tmpdir" && rm -r "$$tmpdir" ; exit $$STATUS else @@ -16,7 +16,7 @@ else DIST := dist DIST_DIRS := $(DIST)/binaries $(DIST)/release -IMPORT := code.gitea.io/gitea +IMPORT := forgejo.org GO ?= $(shell go env GOROOT)/bin/go SHASUM ?= shasum -a 256 @@ -37,19 +37,17 @@ endif XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go -EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.1 # renovate: datasource=go -GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0 # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.7 # renovate: datasource=go +EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.3.0 # renovate: datasource=go +GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go -MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0 # renovate: datasource=go SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasource=go GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go -DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.31.0 # renovate: datasource=go -GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go -GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@39.212.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go +GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go +RENOVATE_NPM_PACKAGE ?= renovate@41.1.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... @@ -92,29 +90,22 @@ else FORGEJO_VERSION_API ?= $(GITEA_VERSION)+${GITEA_COMPATIBILITY} else # drop the "g" prefix prepended by git describe to the commit hash - FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/')+${GITEA_COMPATIBILITY} + FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always 2>/dev/null | sed 's/^v//' | sed 's/\-g/-/2') + ifneq ($(FORGEJO_VERSION),) + ifeq ($(findstring $(GITEA_COMPATIBILITY),$(FORGEJO_VERSION)),) + FORGEJO_VERSION := $(FORGEJO_VERSION)+$(GITEA_COMPATIBILITY) + endif + endif endif endif FORGEJO_VERSION_MAJOR=$(shell echo $(FORGEJO_VERSION) | sed -e 's/\..*//') FORGEJO_VERSION_MINOR=$(shell echo $(FORGEJO_VERSION) | sed -E -e 's/^([0-9]+\.[0-9]+).*/\1/') -show-version-full: - @echo ${FORGEJO_VERSION} - -show-version-major: - @echo ${FORGEJO_VERSION_MAJOR} - -show-version-minor: - @echo ${FORGEJO_VERSION_MINOR} - RELEASE_VERSION ?= ${FORGEJO_VERSION} VERSION ?= ${RELEASE_VERSION} FORGEJO_VERSION_API ?= ${FORGEJO_VERSION} -show-version-api: - @echo ${FORGEJO_VERSION_API} - # Strip binaries by default to reduce size, allow overriding for debugging STRIP ?= 1 ifeq ($(STRIP),1) @@ -125,10 +116,10 @@ LDFLAGS := $(LDFLAGS) -X "main.ReleaseVersion=$(RELEASE_VERSION)" -X "main.MakeV LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64 ifeq ($(HAS_GO), yes) - GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) $(shell $(GO) list code.gitea.io/gitea/models/forgejo_migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./...)) + GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list forgejo.org/models/migrations/...) $(shell $(GO) list forgejo.org/models/forgejo_migrations/...) forgejo.org/tests/integration/migration-test forgejo.org/tests forgejo.org/tests/integration forgejo.org/tests/e2e,$(shell $(GO) list ./...)) endif REMOTE_CACHER_MODULES ?= cache nosql session queue -GO_TEST_REMOTE_CACHER_PACKAGES ?= $(addprefix code.gitea.io/gitea/modules/,$(REMOTE_CACHER_MODULES)) +GO_TEST_REMOTE_CACHER_PACKAGES ?= $(addprefix forgejo.org/modules/,$(REMOTE_CACHER_MODULES)) FOMANTIC_WORK_DIR := web_src/fomantic @@ -137,7 +128,7 @@ WEBPACK_CONFIGS := webpack.config.js tailwind.config.js WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts -BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go +BINDATA_DEST := modules/migration/bindata.go modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST)) GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go @@ -169,7 +160,7 @@ GO_SOURCES += $(GENERATED_GO_DEST) GO_SOURCES_NO_BINDATA := $(GO_SOURCES) ifeq ($(HAS_GO), yes) - MIGRATION_PACKAGES := $(shell $(GO) list code.gitea.io/gitea/models/migrations/... code.gitea.io/gitea/models/forgejo_migrations/...) + MIGRATION_PACKAGES := $(shell $(GO) list forgejo.org/models/migrations/... forgejo.org/models/forgejo_migrations/...) endif ifeq ($(filter $(TAGS_SPLIT),bindata),bindata) @@ -208,7 +199,7 @@ all: build .PHONY: help help: @echo "Make Routines:" - @echo " - \"\" equivalent to \"build\"" + @echo " - \"\" equivalent to \"build\"" @echo " - build build everything" @echo " - frontend build frontend files" @echo " - backend build backend files" @@ -221,31 +212,22 @@ help: @echo " - deps-frontend install frontend dependencies" @echo " - deps-backend install backend dependencies" @echo " - deps-tools install tool dependencies" - @echo " - deps-py install python dependencies" @echo " - lint lint everything" @echo " - lint-fix lint everything and fix issues" @echo " - lint-frontend lint frontend files" @echo " - lint-frontend-fix lint frontend files and fix issues" @echo " - lint-backend lint backend files" @echo " - lint-backend-fix lint backend files and fix issues" - @echo " - lint-codespell lint typos" - @echo " - lint-codespell-fix lint typos and fix them automatically" - @echo " - lint-codespell-fix-i lint typos and fix them interactively" @echo " - lint-go lint go files" @echo " - lint-go-fix lint go files and fix issues" @echo " - lint-go-vet lint go files with vet" - @echo " - lint-go-gopls lint go files with gopls" @echo " - lint-js lint js files" @echo " - lint-js-fix lint js files and fix issues" @echo " - lint-css lint css files" @echo " - lint-css-fix lint css files and fix issues" @echo " - lint-md lint markdown files" @echo " - lint-swagger lint swagger files" - @echo " - lint-templates lint template files" @echo " - lint-renovate lint renovate files" - @echo " - lint-yaml lint yaml files" - @echo " - lint-spell lint spelling" - @echo " - lint-spell-fix lint spelling and fix issues" @echo " - checks run various consistency checks" @echo " - checks-frontend check frontend files" @echo " - checks-backend check backend files" @@ -276,6 +258,30 @@ help: @echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite" @echo " - reproduce-build\#version build a reproducible binary for the specified release version" +.PHONY: verify-version +verify-version: +ifeq ($(FORGEJO_VERSION),) + @echo "Error: Could not determine FORGEJO_VERSION; version file $(STORED_VERSION_FILE) not present and no suitable git tag found" + @echo 'In most cases this likely means you forgot to fetch git tags, you can fix this by executing `git fetch --tags`. If this is not possible and this is part of a custom build process, then you can set a specific version by writing it to $(STORED_VERSION_FILE) (This must be a semver compatible version).' + @false +endif + +.PHONY: show-version-full +show-version-full: verify-version + @echo ${FORGEJO_VERSION} + +.PHONY: show-version-major +show-version-major: verify-version + @echo ${FORGEJO_VERSION_MAJOR} + +.PHONY: show-version-minor +show-version-minor: verify-version + @echo ${FORGEJO_VERSION_MINOR} + +.PHONY: show-version-api +show-version-api: verify-version + @echo ${FORGEJO_VERSION_API} + ### # Check system and environment requirements ### @@ -317,8 +323,12 @@ clean-all: clean rm -rf $(WEBPACK_DEST_ENTRIES) node_modules .PHONY: clean -clean: - rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \ +clean: clean-no-bindata + rm -rf $(BINDATA_DEST) $(BINDATA_HASH) + +.PHONY: clean-no-bindata +clean-no-bindata: + rm -rf $(EXECUTABLE) $(DIST) \ integrations*.test \ e2e*.test \ tests/integration/gitea-integration-* \ @@ -401,10 +411,10 @@ checks-frontend: lockfile-check svg-check checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check .PHONY: lint -lint: lint-frontend lint-backend lint-spell +lint: lint-frontend lint-backend .PHONY: lint-fix -lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix +lint-fix: lint-frontend-fix lint-backend-fix .PHONY: lint-frontend lint-frontend: lint-js lint-css @@ -418,18 +428,6 @@ lint-backend: lint-go lint-go-vet lint-editorconfig lint-renovate lint-locale li .PHONY: lint-backend-fix lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig lint-disposable-emails-fix -.PHONY: lint-codespell -lint-codespell: deps-py - @poetry run codespell - -.PHONY: lint-codespell-fix -lint-codespell-fix: deps-py - @poetry run codespell -w - -.PHONY: lint-codespell-fix-i -lint-codespell-fix-i: deps-py - @poetry run codespell -w -i 3 -C 2 - .PHONY: lint-js lint-js: node_modules npx eslint --color --max-warnings=0 @@ -452,8 +450,8 @@ lint-swagger: node_modules .PHONY: lint-renovate lint-renovate: node_modules - npx --yes --package $(RENOVATE_NPM_PACKAGE) -- renovate-config-validator --strict > .lint-renovate 2>&1 || true - @if grep --quiet --extended-regexp -e '^( WARN:|ERROR:)' .lint-renovate ; then cat .lint-renovate ; rm .lint-renovate ; exit 1 ; fi + npx --yes --package $(RENOVATE_NPM_PACKAGE) -- renovate-config-validator > .lint-renovate 2>&1 || true + @if grep --quiet --extended-regexp -e '^( ERROR:)' .lint-renovate ; then cat .lint-renovate ; rm .lint-renovate ; exit 1 ; fi @rm .lint-renovate .PHONY: lint-locale @@ -462,21 +460,13 @@ lint-locale: .PHONY: lint-locale-usage lint-locale-usage: - $(GO) run build/lint-locale-usage/lint-locale-usage.go --allow-missing-msgids + $(GO) run build/lint-locale-usage/lint-locale-usage.go .PHONY: lint-md lint-md: node_modules npx markdownlint docs *.md -.PHONY: lint-spell -lint-spell: lint-codespell - @go run $(MISSPELL_PACKAGE) -error $(SPELLCHECK_FILES) - -.PHONY: lint-spell-fix -lint-spell-fix: lint-codespell-fix - @go run $(MISSPELL_PACKAGE) -w $(SPELLCHECK_FILES) - -RUN_DEADCODE = $(GO) run $(DEADCODE_PACKAGE) -generated=false -f='{{println .Path}}{{range .Funcs}}{{printf "\t%s\n" .Name}}{{end}}{{println}}' -test code.gitea.io/gitea +RUN_DEADCODE = $(GO) run $(DEADCODE_PACKAGE) -generated=false -f='{{println .Path}}{{range .Funcs}}{{printf "\t%s\n" .Name}}{{end}}{{println}}' -test forgejo.org .PHONY: lint-go lint-go: @@ -495,11 +485,6 @@ lint-go-vet: @echo "Running go vet..." @$(GO) vet ./... -.PHONY: lint-go-gopls -lint-go-gopls: - @echo "Running gopls check..." - @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA) - .PHONY: lint-editorconfig lint-editorconfig: $(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .forgejo/workflows @@ -512,18 +497,9 @@ lint-disposable-emails: lint-disposable-emails-fix: $(GO) run build/generate-disposable-email.go -r $(DISPOSABLE_EMAILS_SHA) -.PHONY: lint-templates -lint-templates: .venv node_modules - @node tools/lint-templates-svg.js - @poetry run djlint $(shell find templates -type f -iname '*.tmpl') - -.PHONY: lint-yaml -lint-yaml: .venv - @poetry run yamllint -s . - .PHONY: security-check security-check: - go run $(GOVULNCHECK_PACKAGE) ./... + go run $(GOVULNCHECK_PACKAGE) -show color ./... ### # Development and testing targets @@ -577,7 +553,7 @@ test-check: .PHONY: test\#% test\#%: - @echo "Running go test with -tags '$(TEST_TAGS)'..." + @echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." @$(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES) .PHONY: coverage @@ -610,7 +586,7 @@ tidy-check: tidy go-licenses: $(GO_LICENSE_FILE) $(GO_LICENSE_FILE): go.mod go.sum - -$(GO) run $(GO_LICENSES_PACKAGE) save . --force --ignore code.gitea.io/gitea --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null + -$(GO) run $(GO_LICENSES_PACKAGE) save . --force --ignore forgejo.org --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null $(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE) @rm -rf $(GO_LICENSE_TMP_DIR) @@ -740,33 +716,33 @@ integration-test-coverage-sqlite: integrations.cover.sqlite.test generate-ini-sq GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.cover.sqlite.test -test.coverprofile=integration.coverage.out integrations.mysql.test: git-check $(GO_SOURCES) - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mysql.test + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration -o integrations.mysql.test integrations.pgsql.test: git-check $(GO_SOURCES) - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.pgsql.test + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration -o integrations.pgsql.test integrations.sqlite.test: git-check $(GO_SOURCES) - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.sqlite.test -tags '$(TEST_TAGS)' + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration -o integrations.sqlite.test -tags '$(TEST_TAGS)' integrations.cover.test: git-check $(GO_SOURCES) - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.test + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.test integrations.cover.sqlite.test: git-check $(GO_SOURCES) - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.sqlite.test -tags '$(TEST_TAGS)' + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.sqlite.test -tags '$(TEST_TAGS)' .PHONY: migrations.mysql.test migrations.mysql.test: $(GO_SOURCES) generate-ini-mysql - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mysql.test + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration/migration-test -o migrations.mysql.test GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini $(GOTESTCOMPILEDRUNPREFIX) ./migrations.mysql.test $(GOTESTCOMPILEDRUNSUFFIX) .PHONY: migrations.pgsql.test migrations.pgsql.test: $(GO_SOURCES) generate-ini-pgsql - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.pgsql.test + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration/migration-test -o migrations.pgsql.test GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GOTESTCOMPILEDRUNPREFIX) ./migrations.pgsql.test $(GOTESTCOMPILEDRUNSUFFIX) .PHONY: migrations.sqlite.test migrations.sqlite.test: $(GO_SOURCES) generate-ini-sqlite - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)' + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)' GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTESTCOMPILEDRUNPREFIX) ./migrations.sqlite.test $(GOTESTCOMPILEDRUNSUFFIX) .PHONY: migrations.individual.mysql.test @@ -777,7 +753,7 @@ migrations.individual.mysql.test: $(GO_SOURCES) .PHONY: migrations.individual.sqlite.test\#% migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite - GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$* + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/migrations/$* .PHONY: migrations.individual.pgsql.test migrations.individual.pgsql.test: $(GO_SOURCES) @@ -787,7 +763,7 @@ migrations.individual.pgsql.test: $(GO_SOURCES) .PHONY: migrations.individual.pgsql.test\#% migrations.individual.pgsql.test\#%: $(GO_SOURCES) generate-ini-pgsql - GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$* + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/migrations/$* .PHONY: migrations.individual.sqlite.test migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite @@ -797,16 +773,16 @@ migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite .PHONY: migrations.individual.sqlite.test\#% migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite - GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$* + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/migrations/$* e2e.mysql.test: $(GO_SOURCES) - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mysql.test + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/e2e -o e2e.mysql.test e2e.pgsql.test: $(GO_SOURCES) - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.pgsql.test + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/e2e -o e2e.pgsql.test e2e.sqlite.test: $(GO_SOURCES) - $(GOTEST) $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.sqlite.test -tags '$(TEST_TAGS)' + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/e2e -o e2e.sqlite.test -tags '$(TEST_TAGS)' .PHONY: check check: test @@ -816,7 +792,7 @@ check: test ### .PHONY: install $(TAGS_PREREQ) -install: $(wildcard *.go) +install: $(wildcard *.go) | verify-version CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '$(LDFLAGS)' .PHONY: build @@ -844,13 +820,13 @@ generate-go: $(TAGS_PREREQ) merge-locales: @echo "NOT NEEDED: THIS IS A NOOP AS OF Forgejo 7.0 BUT KEPT FOR BACKWARD COMPATIBILITY" -$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ) +$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ) | verify-version CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $@ forgejo: $(EXECUTABLE) ln -f $(EXECUTABLE) forgejo -static-executable: $(GO_SOURCES) $(TAGS_PREREQ) +static-executable: $(GO_SOURCES) $(TAGS_PREREQ) | verify-version CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -o $(EXECUTABLE) .PHONY: release @@ -863,18 +839,18 @@ $(DIST_DIRS): mkdir -p $(DIST_DIRS) .PHONY: release-linux -release-linux: | $(DIST_DIRS) +release-linux: | $(DIST_DIRS) verify-version CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out forgejo-$(VERSION) . ifeq ($(CI),true) cp /build/* $(DIST)/binaries endif .PHONY: release-darwin -release-darwin: | $(DIST_DIRS) +release-darwin: | $(DIST_DIRS) verify-version CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) . .PHONY: release-freebsd -release-freebsd: | $(DIST_DIRS) +release-freebsd: | $(DIST_DIRS) verify-version CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) . .PHONY: release-copy @@ -928,10 +904,7 @@ reproduce-build\#%: ### .PHONY: deps -deps: deps-frontend deps-backend deps-tools deps-py - -.PHONY: deps-py -deps-py: .venv +deps: deps-frontend deps-backend deps-tools .PHONY: deps-frontend deps-frontend: node_modules @@ -947,13 +920,11 @@ deps-tools: $(GO) install $(GOFUMPT_PACKAGE) $(GO) install $(GOLANGCI_LINT_PACKAGE) $(GO) install $(GXZ_PACKAGE) - $(GO) install $(MISSPELL_PACKAGE) $(GO) install $(SWAGGER_PACKAGE) $(GO) install $(XGO_PACKAGE) $(GO) install $(GO_LICENSES_PACKAGE) $(GO) install $(GOVULNCHECK_PACKAGE) $(GO) install $(GOMOCK_PACKAGE) - $(GO) install $(GOPLS_PACKAGE) node_modules: package-lock.json npm install --no-save @@ -1013,12 +984,11 @@ generate-gitignore: .PHONY: generate-gomock generate-gomock: - $(GO) run $(GOMOCK_PACKAGE) -package mock -destination ./modules/queue/mock/redisuniversalclient.go code.gitea.io/gitea/modules/nosql RedisClient + $(GO) run $(GOMOCK_PACKAGE) -package mock -destination ./modules/queue/mock/redisuniversalclient.go forgejo.org/modules/nosql RedisClient .PHONY: generate-images generate-images: | node_modules - npm install --no-save fabric@6 imagemin-zopfli@7 - node tools/generate-images.js $(TAGS) + node tools/generate-images.js .PHONY: generate-manpage generate-manpage: diff --git a/README.md b/README.md index 0c4becacc4..f95aebadeb 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,6 @@ Our promise: **Independent Free/Libre Software forever!** ## What does Forgejo offer? - - If you like any of the following, Forgejo is literally meant for you: - Lightweight: Forgejo can easily be hosted on nearly **every machine**. diff --git a/assets/favicon.svg b/assets/favicon.svg index bcacdc0200..bb0031b93d 100644 --- a/assets/favicon.svg +++ b/assets/favicon.svg @@ -1,27 +1,33 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/assets/go-licenses.json b/assets/go-licenses.json index af9c0ce8e0..fb6c201a5e 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -19,6 +19,11 @@ "path": "code.forgejo.org/forgejo-contrib/go-libravatar/LICENSE", "licenseText": "Copyright (c) 2016 Sandro Santilli \u003cstrk@kbt.io\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" }, + { + "name": "code.forgejo.org/forgejo/go-rpmutils", + "path": "code.forgejo.org/forgejo/go-rpmutils/LICENSE", + "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" + }, { "name": "code.forgejo.org/forgejo/levelqueue", "path": "code.forgejo.org/forgejo/levelqueue/LICENSE", @@ -99,19 +104,14 @@ "path": "github.com/Azure/go-ntlmssp/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Microsoft\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, - { - "name": "github.com/DataDog/zstd", - "path": "github.com/DataDog/zstd/LICENSE", - "licenseText": "Simplified BSD License\n\nCopyright (c) 2016, Datadog \u003cinfo@datadoghq.com\u003e\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright notice,\n this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n * Neither the name of the copyright holder nor the names of its contributors\n may be used to endorse or promote products derived from this software\n without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" - }, { "name": "github.com/ProtonMail/go-crypto", "path": "github.com/ProtonMail/go-crypto/LICENSE", "licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, { - "name": "github.com/RoaringBitmap/roaring", - "path": "github.com/RoaringBitmap/roaring/LICENSE", + "name": "github.com/RoaringBitmap/roaring/v2", + "path": "github.com/RoaringBitmap/roaring/v2/LICENSE", "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2016 by the authors\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n================================================================================\n\nPortions of runcontainer.go are from the Go standard library, which is licensed\nunder:\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the following disclaimer\n in the documentation and/or other materials provided with the\n distribution.\n * Neither the name of Google Inc. nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, { @@ -289,11 +289,6 @@ "path": "github.com/cloudflare/circl/LICENSE", "licenseText": "Copyright (c) 2019 Cloudflare. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Cloudflare nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n========================================================================\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, - { - "name": "github.com/cpuguy83/go-md2man/v2/md2man", - "path": "github.com/cpuguy83/go-md2man/v2/md2man/LICENSE.md", - "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Brian Goff\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" - }, { "name": "github.com/cyphar/filepath-securejoin", "path": "github.com/cyphar/filepath-securejoin/LICENSE", @@ -494,11 +489,6 @@ "path": "github.com/golang-jwt/jwt/v5/LICENSE", "licenseText": "Copyright (c) 2012 Dave Grijalva\nCopyright (c) 2021 golang-jwt maintainers\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE 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.\n\n" }, - { - "name": "github.com/golang/geo", - "path": "github.com/golang/geo/LICENSE", - "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" - }, { "name": "github.com/golang/groupcache/lru", "path": "github.com/golang/groupcache/lru/LICENSE", @@ -814,6 +804,11 @@ "path": "github.com/opencontainers/image-spec/specs-go/LICENSE", "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n Copyright 2016 The Linux Foundation.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" }, + { + "name": "github.com/philhofer/fwd", + "path": "github.com/philhofer/fwd/LICENSE.md", + "licenseText": "Copyright (c) 2014-2015, Philip Hofer\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE 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." + }, { "name": "github.com/pierrec/lz4/v4", "path": "github.com/pierrec/lz4/v4/LICENSE", @@ -889,21 +884,11 @@ "path": "github.com/rs/xid/LICENSE", "licenseText": "Copyright (c) 2015 Olivier Poitrey \u003crs@dailymotion.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is furnished\nto do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" }, - { - "name": "github.com/russross/blackfriday/v2", - "path": "github.com/russross/blackfriday/v2/LICENSE.txt", - "licenseText": "Blackfriday is distributed under the Simplified BSD License:\n\n\u003e Copyright © 2011 Russ Ross\n\u003e All rights reserved.\n\u003e\n\u003e Redistribution and use in source and binary forms, with or without\n\u003e modification, are permitted provided that the following conditions\n\u003e are met:\n\u003e\n\u003e 1. Redistributions of source code must retain the above copyright\n\u003e notice, this list of conditions and the following disclaimer.\n\u003e\n\u003e 2. Redistributions in binary form must reproduce the above\n\u003e copyright notice, this list of conditions and the following\n\u003e disclaimer in the documentation and/or other materials provided with\n\u003e the distribution.\n\u003e\n\u003e THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\u003e \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n\u003e LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n\u003e FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n\u003e COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n\u003e INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n\u003e BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n\u003e LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n\u003e CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n\u003e LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\n\u003e ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n\u003e POSSIBILITY OF SUCH DAMAGE.\n" - }, { "name": "github.com/santhosh-tekuri/jsonschema/v6", "path": "github.com/santhosh-tekuri/jsonschema/v6/LICENSE", "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability." }, - { - "name": "github.com/sassoftware/go-rpmutils", - "path": "github.com/sassoftware/go-rpmutils/LICENSE", - "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" - }, { "name": "github.com/sergi/go-diff/diffmatchpatch", "path": "github.com/sergi/go-diff/diffmatchpatch/LICENSE", @@ -934,15 +919,20 @@ "path": "github.com/syndtr/goleveldb/leveldb/LICENSE", "licenseText": "Copyright 2012 Suryandaru Triandana \u003csyndtr@gmail.com\u003e\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, + { + "name": "github.com/tinylib/msgp/msgp", + "path": "github.com/tinylib/msgp/msgp/LICENSE", + "licenseText": "Copyright (c) 2014 Philip Hofer\nPortions Copyright (c) 2009 The Go Authors (license at http://golang.org) where indicated\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE 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." + }, { "name": "github.com/ulikunitz/xz", "path": "github.com/ulikunitz/xz/LICENSE", "licenseText": "Copyright (c) 2014-2022 Ulrich Kunitz\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* My name, Ulrich Kunitz, may not be used to endorse or promote products\n derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, { - "name": "github.com/urfave/cli/v2", - "path": "github.com/urfave/cli/v2/LICENSE", - "licenseText": "MIT License\n\nCopyright (c) 2022 urfave/cli maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + "name": "github.com/urfave/cli/v3", + "path": "github.com/urfave/cli/v3/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2023 urfave/cli maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, { "name": "github.com/valyala/fastjson", @@ -959,11 +949,6 @@ "path": "github.com/xanzy/ssh-agent/LICENSE", "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n" }, - { - "name": "github.com/xrash/smetrics", - "path": "github.com/xrash/smetrics/LICENSE", - "licenseText": "Copyright (C) 2016 Felipe da Cunha Gonçalves\nAll Rights Reserved.\n\nMIT LICENSE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, { "name": "github.com/yohcop/openid-go", "path": "github.com/yohcop/openid-go/LICENSE", diff --git a/assets/logo.svg b/assets/logo.svg index bcacdc0200..bb0031b93d 100644 --- a/assets/logo.svg +++ b/assets/logo.svg @@ -1,27 +1,33 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/build.go b/build.go deleted file mode 100644 index d410e171c7..0000000000 --- a/build.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//go:build vendor - -package main - -// Libraries that are included to vendor utilities used during build. -// These libraries will not be included in a normal compilation. - -import ( - // for embed - _ "github.com/shurcooL/vfsgen" -) diff --git a/build/backport-locales.go b/build/backport-locales.go index 3df83ea6d9..3125f19014 100644 --- a/build/backport-locales.go +++ b/build/backport-locales.go @@ -12,8 +12,8 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/container" + "forgejo.org/modules/setting" ) func main() { diff --git a/build/code-batch-process.go b/build/code-batch-process.go index cc2ab68026..516736b65c 100644 --- a/build/code-batch-process.go +++ b/build/code-batch-process.go @@ -15,7 +15,7 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/build/codeformat" + "forgejo.org/build/codeformat" ) // Windows has a limitation for command line arguments, the size can not exceed 32KB. diff --git a/build/codeformat/formatimports.go b/build/codeformat/formatimports.go index c9fc2a27b4..acedd13234 100644 --- a/build/codeformat/formatimports.go +++ b/build/codeformat/formatimports.go @@ -13,8 +13,8 @@ import ( ) var importPackageGroupOrders = map[string]int{ - "": 1, // internal - "code.gitea.io/gitea/": 2, + "": 1, // internal + "forgejo.org/": 2, } var errInvalidCommentBetweenImports = errors.New("comments between imported packages are invalid, please move comments to the end of the package line") diff --git a/build/codeformat/formatimports_test.go b/build/codeformat/formatimports_test.go index 1abc9f8ab7..03c780911f 100644 --- a/build/codeformat/formatimports_test.go +++ b/build/codeformat/formatimports_test.go @@ -58,8 +58,8 @@ import ( "code.gitea.io/other/package" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "xorm.io/the/package" @@ -82,8 +82,8 @@ import ( _ "image/jpeg" // for processing jpeg images _ "image/png" // for processing png images - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "code.gitea.io/other/package" "github.com/issue9/identicon" diff --git a/build/generate-bindata.go b/build/generate-bindata.go index 2fcb7c2f2a..67d3776847 100644 --- a/build/generate-bindata.go +++ b/build/generate-bindata.go @@ -1,5 +1,6 @@ // Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later //go:build ignore @@ -7,30 +8,40 @@ package main import ( "bytes" - "crypto/sha1" + "crypto/sha256" "fmt" + "io" + "io/fs" "log" - "net/http" "os" + "path" "path/filepath" "strconv" + "text/template" - "github.com/shurcooL/vfsgen" + "github.com/klauspost/compress/zstd" ) -func needsUpdate(dir, filename string) (bool, []byte) { - needRegen := false +func fileExists(filename string) bool { _, err := os.Stat(filename) - if err != nil { - needRegen = true + if err == nil { + return true } + if os.IsNotExist(err) { + return false + } + panic(err) +} + +func needsUpdate(dir, filename string) (bool, []byte) { + needRegen := !fileExists(filename) oldHash, err := os.ReadFile(filename + ".hash") if err != nil { oldHash = []byte{} } - hasher := sha1.New() + hasher := sha256.New() err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error { if err != nil { @@ -51,7 +62,7 @@ func needsUpdate(dir, filename string) (bool, []byte) { newHash := hasher.Sum([]byte{}) - if bytes.Compare(oldHash, newHash) != 0 { + if !bytes.Equal(oldHash, newHash) { return true, newHash } @@ -69,24 +80,280 @@ func main() { useGlobalModTime, _ = strconv.ParseBool(os.Args[4]) } - update, newHash := needsUpdate(dir, filename) + if os.Getenv("FORGEJO_GENERATE_SKIP_HASH") == "true" && fileExists(filename) { + fmt.Printf("bindata %s already exists and FORGEJO_GENERATE_SKIP_HASH=true\n", packageName) + return + } + update, newHash := needsUpdate(dir, filename) if !update { - fmt.Printf("bindata for %s already up-to-date\n", packageName) + fmt.Printf("bindata %s already exists and the checksum is a match\n", packageName) return } fmt.Printf("generating bindata for %s\n", packageName) - var fsTemplates http.FileSystem = http.Dir(dir) - err := vfsgen.Generate(fsTemplates, vfsgen.Options{ - PackageName: packageName, - BuildTags: "bindata", - VariableName: "Assets", - Filename: filename, - UseGlobalModTime: useGlobalModTime, - }) + + root, err := os.OpenRoot(dir) if err != nil { - log.Fatalf("%v\n", err) + log.Fatal(err) + } + + out, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644) + if err != nil { + log.Fatal(err) + } + defer out.Close() + + if err := generate(root.FS(), packageName, useGlobalModTime, out); err != nil { + log.Fatal(err) } _ = os.WriteFile(filename+".hash", newHash, 0o666) } + +type file struct { + Path string + Name string + UncompressedSize int + CompressedData []byte + UncompressedData []byte +} + +type direntry struct { + Name string + IsDir bool +} + +func generate(fsRoot fs.FS, packageName string, globalTime bool, output io.Writer) error { + enc, err := zstd.NewWriter(nil, zstd.WithEncoderLevel(zstd.SpeedBestCompression)) + if err != nil { + return err + } + + files := []file{} + + dirs := map[string][]direntry{} + + if err := fs.WalkDir(fsRoot, ".", func(filePath string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + entries, err := fs.ReadDir(fsRoot, filePath) + if err != nil { + return err + } + dirEntries := make([]direntry, 0, len(entries)) + for _, entry := range entries { + dirEntries = append(dirEntries, direntry{Name: entry.Name(), IsDir: entry.IsDir()}) + } + dirs[filePath] = dirEntries + return nil + } + + src, err := fs.ReadFile(fsRoot, filePath) + if err != nil { + return err + } + + dst := enc.EncodeAll(src, nil) + if len(dst) < len(src) { + files = append(files, file{ + Path: filePath, + Name: path.Base(filePath), + UncompressedSize: len(src), + CompressedData: dst, + }) + } else { + files = append(files, file{ + Path: filePath, + Name: path.Base(filePath), + UncompressedData: src, + }) + } + return nil + }); err != nil { + return err + } + + return generatedTmpl.Execute(output, map[string]any{ + "Packagename": packageName, + "GlobalTime": globalTime, + "Files": files, + "Dirs": dirs, + }) +} + +var generatedTmpl = template.Must(template.New("").Parse(`// Code generated by efs-gen. DO NOT EDIT. + +//go:build bindata + +package {{.Packagename}} + +import ( + "bytes" + "time" + "io" + "io/fs" + + "github.com/klauspost/compress/zstd" +) + +type normalFile struct { + name string + content []byte +} + +type compressedFile struct { + name string + uncompressedSize int64 + data []byte +} + +var files = map[string]any{ +{{- range .Files}} + "{{.Path}}": {{if .CompressedData}}compressedFile{"{{.Name}}", {{.UncompressedSize}}, []byte({{printf "%+q" .CompressedData}})}{{else}}normalFile{"{{.Name}}", []byte({{printf "%+q" .UncompressedData}})}{{end}}, +{{- end}} +} + +var dirs = map[string][]fs.DirEntry{ +{{- range $key, $entry := .Dirs}} + "{{$key}}": { +{{- range $entry}} + direntry{"{{.Name}}", {{.IsDir}}}, +{{- end}} + }, +{{- end}} +} + +type assets struct{} + +var Assets = assets{} + +func (a assets) Open(name string) (fs.File, error) { + f, ok := files[name] + if !ok { + return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} + } + + switch f := f.(type) { + case normalFile: + return file{name: f.name, size: int64(len(f.content)), data: bytes.NewReader(f.content)}, nil + case compressedFile: + r, _ := zstd.NewReader(bytes.NewReader(f.data)) + return &compressFile{name: f.name, size: f.uncompressedSize, data: r, content: f.data}, nil + default: + panic("unknown file type") + } +} + +func (a assets) ReadDir(name string) ([]fs.DirEntry, error) { + d, ok := dirs[name] + if !ok { + return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} + } + return d, nil +} + +type file struct { + name string + size int64 + data io.ReadSeeker +} + +var _ io.ReadSeeker = (*file)(nil) + +func (f file) Stat() (fs.FileInfo, error) { + return fileinfo{name: f.name, size: f.size}, nil +} + +func (f file) Read(p []byte) (int, error) { + return f.data.Read(p) +} + +func (f file) Seek(offset int64, whence int) (int64, error) { + return f.data.Seek(offset, whence) +} + +func (f file) Close() error { return nil } + +type compressFile struct { + name string + size int64 + data *zstd.Decoder + content []byte + zstdPos int64 + seekPos int64 +} + +var _ io.ReadSeeker = (*compressFile)(nil) + +func (f *compressFile) Stat() (fs.FileInfo, error) { + return fileinfo{name: f.name, size: f.size}, nil +} + +func (f *compressFile) Read(p []byte) (int, error) { + if f.zstdPos > f.seekPos { + if err := f.data.Reset(bytes.NewReader(f.content)); err != nil { + return 0, err + } + f.zstdPos = 0 + } + if f.zstdPos < f.seekPos { + if _, err := io.CopyN(io.Discard, f.data, f.seekPos - f.zstdPos); err != nil { + return 0, err + } + f.zstdPos = f.seekPos + } + n, err := f.data.Read(p) + f.zstdPos += int64(n) + f.seekPos = f.zstdPos + return n, err +} + +func (f *compressFile) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + f.seekPos = 0 + offset + case io.SeekCurrent: + f.seekPos += offset + case io.SeekEnd: + f.seekPos = f.size + offset + } + return f.seekPos, nil +} + +func (f *compressFile) Close() error { + f.data.Close() + return nil +} + +func (f *compressFile) ZstdBytes() []byte { return f.content } + +type fileinfo struct { + name string + size int64 +} + +func (f fileinfo) Name() string { return f.name } +func (f fileinfo) Size() int64 { return f.size } +func (f fileinfo) Mode() fs.FileMode { return 0o444 } +func (f fileinfo) ModTime() time.Time { return {{if .GlobalTime}}GlobalModTime(f.name){{else}}time.Unix(0, 0){{end}} } +func (f fileinfo) IsDir() bool { return false } +func (f fileinfo) Sys() any { return nil } + +type direntry struct { + name string + isDir bool +} + +func (d direntry) Name() string { return d.name } +func (d direntry) IsDir() bool { return d.isDir } +func (d direntry) Type() fs.FileMode { + if d.isDir { + return 0o755 | fs.ModeDir + } + return 0o444 +} +func (direntry) Info() (fs.FileInfo, error) { return nil, fs.ErrNotExist } +`)) diff --git a/build/generate-emoji.go b/build/generate-emoji.go index 98c2f15d75..0ad49a6541 100644 --- a/build/generate-emoji.go +++ b/build/generate-emoji.go @@ -20,7 +20,7 @@ import ( "strings" "unicode/utf8" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" ) const ( diff --git a/build/generate-gitignores.go b/build/generate-gitignores.go index 1e09c83a6a..7acfd6cbe4 100644 --- a/build/generate-gitignores.go +++ b/build/generate-gitignores.go @@ -15,7 +15,7 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) func main() { diff --git a/build/generate-go-licenses.go b/build/generate-go-licenses.go index 22ef817ebc..3f4d62a2cc 100644 --- a/build/generate-go-licenses.go +++ b/build/generate-go-licenses.go @@ -16,7 +16,7 @@ import ( "sort" "strings" - "code.gitea.io/gitea/modules/container" + "forgejo.org/modules/container" ) // regexp is based on go-license, excluding README and NOTICE @@ -102,9 +102,9 @@ func main() { pkgName := path.Dir(pkgPath) // There might be a bug somewhere in go-licenses that sometimes interprets the - // root package as "." and sometimes as "code.gitea.io/gitea". Workaround by + // root package as "." and sometimes as "forgejo.org". Workaround by // removing both of them for the sake of stable output. - if pkgName == "." || pkgName == "code.gitea.io/gitea" { + if pkgName == "." || pkgName == "forgejo.org" { continue } diff --git a/build/generate-licenses.go b/build/generate-licenses.go index 9a111bc811..e925d8af02 100644 --- a/build/generate-licenses.go +++ b/build/generate-licenses.go @@ -15,7 +15,7 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) func main() { diff --git a/build/lint-locale-usage/lint-locale-usage.go b/build/lint-locale-usage/lint-locale-usage.go index f42bc59cbb..88375c1c36 100644 --- a/build/lint-locale-usage/lint-locale-usage.go +++ b/build/lint-locale-usage/lint-locale-usage.go @@ -17,17 +17,15 @@ import ( "text/template" tmplParser "text/template/parse" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/locale" - fjTemplates "code.gitea.io/gitea/modules/templates" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/container" + fjTemplates "forgejo.org/modules/templates" + "forgejo.org/modules/translation/localeiter" + "forgejo.org/modules/util" ) // this works by first gathering all valid source string IDs from `en-US` reference files // and then checking if all used source strings are actually defined -type OnMsgidHandler func(fset *token.FileSet, pos token.Pos, msgid string) - type LocatedError struct { Location string Kind string @@ -49,8 +47,24 @@ func (e LocatedError) Error() string { return sb.String() } -func isLocaleTrFunction(funcname string) bool { - return funcname == "Tr" || funcname == "TrN" +func InitLocaleTrFunctions() map[string][]uint { + ret := make(map[string][]uint) + + f0 := []uint{0} + ret["Tr"] = f0 + ret["TrString"] = f0 + ret["TrHTML"] = f0 + + ret["TrPluralString"] = []uint{1} + ret["TrN"] = []uint{1, 2} + + return ret +} + +type Handler struct { + OnMsgid func(fset *token.FileSet, pos token.Pos, msgid string) + OnUnexpectedInvoke func(fset *token.FileSet, pos token.Pos, funcname string, argc int) + LocaleTrFunctions map[string][]uint } // the `Handle*File` functions follow the following calling convention: @@ -58,7 +72,7 @@ func isLocaleTrFunction(funcname string) bool { // * `src` is either `nil` (then the function invokes `ReadFile` to read the file) // or the contents of the file as {`[]byte`, or a `string`} -func (omh OnMsgidHandler) HandleGoFile(fname string, src any) error { +func (handler Handler) HandleGoFile(fname string, src any) error { fset := token.NewFileSet() node, err := goParser.ParseFile(fset, fname, src, goParser.SkipObjectResolution) if err != nil { @@ -70,31 +84,47 @@ func (omh OnMsgidHandler) HandleGoFile(fname string, src any) error { } ast.Inspect(node, func(n ast.Node) bool { - // search for function calls of the form `anything.Tr(any-string-lit)` + // search for function calls of the form `anything.Tr(any-string-lit, ...)` call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) != 1 { + if !ok || len(call.Args) < 1 { return true } funSel, ok := call.Fun.(*ast.SelectorExpr) - if (!ok) || !isLocaleTrFunction(funSel.Sel.Name) { + if !ok { return true } - argLit, ok := call.Args[0].(*ast.BasicLit) - if (!ok) || argLit.Kind != token.STRING { + ltf, ok := handler.LocaleTrFunctions[funSel.Sel.Name] + if !ok { return true } - // extract string content - arg, err := strconv.Unquote(argLit.Value) - if err != nil { - return true + var gotUnexpectedInvoke *int + + for _, argNum := range ltf { + if len(call.Args) >= int(argNum+1) { + argLit, ok := call.Args[int(argNum)].(*ast.BasicLit) + if !ok || argLit.Kind != token.STRING { + continue + } + + // extract string content + arg, err := strconv.Unquote(argLit.Value) + if err == nil { + // found interesting strings + handler.OnMsgid(fset, argLit.ValuePos, arg) + } + } else { + argc := len(call.Args) + gotUnexpectedInvoke = &argc + } } - // found interesting string - omh(fset, argLit.ValuePos, arg) + if gotUnexpectedInvoke != nil { + handler.OnUnexpectedInvoke(fset, funSel.Sel.NamePos, funSel.Sel.Name, *gotUnexpectedInvoke) + } return true }) @@ -103,33 +133,33 @@ func (omh OnMsgidHandler) HandleGoFile(fname string, src any) error { } // derived from source: modules/templates/scopedtmpl/scopedtmpl.go, L169-L213 -func (omh OnMsgidHandler) handleTemplateNode(fset *token.FileSet, node tmplParser.Node) { +func (handler Handler) handleTemplateNode(fset *token.FileSet, node tmplParser.Node) { switch node.Type() { case tmplParser.NodeAction: - omh.handleTemplatePipeNode(fset, node.(*tmplParser.ActionNode).Pipe) + handler.handleTemplatePipeNode(fset, node.(*tmplParser.ActionNode).Pipe) case tmplParser.NodeList: nodeList := node.(*tmplParser.ListNode) - omh.handleTemplateFileNodes(fset, nodeList.Nodes) + handler.handleTemplateFileNodes(fset, nodeList.Nodes) case tmplParser.NodePipe: - omh.handleTemplatePipeNode(fset, node.(*tmplParser.PipeNode)) + handler.handleTemplatePipeNode(fset, node.(*tmplParser.PipeNode)) case tmplParser.NodeTemplate: - omh.handleTemplatePipeNode(fset, node.(*tmplParser.TemplateNode).Pipe) + handler.handleTemplatePipeNode(fset, node.(*tmplParser.TemplateNode).Pipe) case tmplParser.NodeIf: nodeIf := node.(*tmplParser.IfNode) - omh.handleTemplateBranchNode(fset, nodeIf.BranchNode) + handler.handleTemplateBranchNode(fset, nodeIf.BranchNode) case tmplParser.NodeRange: nodeRange := node.(*tmplParser.RangeNode) - omh.handleTemplateBranchNode(fset, nodeRange.BranchNode) + handler.handleTemplateBranchNode(fset, nodeRange.BranchNode) case tmplParser.NodeWith: nodeWith := node.(*tmplParser.WithNode) - omh.handleTemplateBranchNode(fset, nodeWith.BranchNode) + handler.handleTemplateBranchNode(fset, nodeWith.BranchNode) case tmplParser.NodeCommand: nodeCommand := node.(*tmplParser.CommandNode) - omh.handleTemplateFileNodes(fset, nodeCommand.Args) + handler.handleTemplateFileNodes(fset, nodeCommand.Args) - if len(nodeCommand.Args) != 2 { + if len(nodeCommand.Args) < 2 { return } @@ -138,54 +168,66 @@ func (omh OnMsgidHandler) handleTemplateNode(fset *token.FileSet, node tmplParse return } - nodeString, ok := nodeCommand.Args[1].(*tmplParser.StringNode) + nodeIdent, ok := nodeChain.Node.(*tmplParser.IdentifierNode) + if !ok || nodeIdent.Ident != "ctx" || len(nodeChain.Field) != 2 || nodeChain.Field[0] != "Locale" { + return + } + + ltf, ok := handler.LocaleTrFunctions[nodeChain.Field[1]] if !ok { return } - nodeIdent, ok := nodeChain.Node.(*tmplParser.IdentifierNode) - if !ok || nodeIdent.Ident != "ctx" { - return + var gotUnexpectedInvoke *int + + for _, argNum := range ltf { + if len(nodeCommand.Args) >= int(argNum+2) { + nodeString, ok := nodeCommand.Args[int(argNum+1)].(*tmplParser.StringNode) + if ok { + // found interesting strings + // the column numbers are a bit "off", but much better than nothing + handler.OnMsgid(fset, token.Pos(nodeString.Pos), nodeString.Text) + } + } else { + argc := len(nodeCommand.Args) - 1 + gotUnexpectedInvoke = &argc + } } - if len(nodeChain.Field) != 2 || nodeChain.Field[0] != "Locale" || !isLocaleTrFunction(nodeChain.Field[1]) { - return + if gotUnexpectedInvoke != nil { + handler.OnUnexpectedInvoke(fset, token.Pos(nodeChain.Pos), nodeChain.Field[1], *gotUnexpectedInvoke) } - // found interesting string - // the column numbers are a bit "off", but much better than nothing - omh(fset, token.Pos(nodeString.Pos), nodeString.Text) - default: } } -func (omh OnMsgidHandler) handleTemplatePipeNode(fset *token.FileSet, pipeNode *tmplParser.PipeNode) { +func (handler Handler) handleTemplatePipeNode(fset *token.FileSet, pipeNode *tmplParser.PipeNode) { if pipeNode == nil { return } // NOTE: we can't pass `pipeNode.Cmds` to handleTemplateFileNodes due to incompatible argument types for _, node := range pipeNode.Cmds { - omh.handleTemplateNode(fset, node) + handler.handleTemplateNode(fset, node) } } -func (omh OnMsgidHandler) handleTemplateBranchNode(fset *token.FileSet, branchNode tmplParser.BranchNode) { - omh.handleTemplatePipeNode(fset, branchNode.Pipe) - omh.handleTemplateFileNodes(fset, branchNode.List.Nodes) +func (handler Handler) handleTemplateBranchNode(fset *token.FileSet, branchNode tmplParser.BranchNode) { + handler.handleTemplatePipeNode(fset, branchNode.Pipe) + handler.handleTemplateFileNodes(fset, branchNode.List.Nodes) if branchNode.ElseList != nil { - omh.handleTemplateFileNodes(fset, branchNode.ElseList.Nodes) + handler.handleTemplateFileNodes(fset, branchNode.ElseList.Nodes) } } -func (omh OnMsgidHandler) handleTemplateFileNodes(fset *token.FileSet, nodes []tmplParser.Node) { +func (handler Handler) handleTemplateFileNodes(fset *token.FileSet, nodes []tmplParser.Node) { for _, node := range nodes { - omh.handleTemplateNode(fset, node) + handler.handleTemplateNode(fset, node) } } -func (omh OnMsgidHandler) HandleTemplateFile(fname string, src any) error { +func (handler Handler) HandleTemplateFile(fname string, src any) error { var tmplContent []byte switch src2 := src.(type) { case nil: @@ -222,7 +264,7 @@ func (omh OnMsgidHandler) HandleTemplateFile(fname string, src any) error { Err: err, } } - omh.handleTemplateFileNodes(fset, tmplParsed.Tree.Root.Nodes) + handler.handleTemplateFileNodes(fset, tmplParsed.Root.Nodes) return nil } @@ -258,10 +300,6 @@ func main() { } msgids := make(container.Set[string]) - onMsgid := func(trKey, trValue string) error { - msgids[trKey] = struct{}{} - return nil - } localeFile := filepath.Join(filepath.Join("options", "locale"), "locale_en-US.ini") localeContent, err := os.ReadFile(localeFile) @@ -270,7 +308,10 @@ func main() { os.Exit(2) } - if err = locale.IterateMessagesContent(localeContent, onMsgid); err != nil { + if err = localeiter.IterateMessagesContent(localeContent, func(trKey, trValue string) error { + msgids[trKey] = struct{}{} + return nil + }); err != nil { fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) os.Exit(2) } @@ -282,19 +323,30 @@ func main() { os.Exit(2) } - if err := locale.IterateMessagesNextContent(localeContent, onMsgid); err != nil { + if err := localeiter.IterateMessagesNextContent(localeContent, func(trKey, pluralForm, trValue string) error { + // ignore plural form + msgids[trKey] = struct{}{} + return nil + }); err != nil { fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) os.Exit(2) } gotAnyMsgidError := false - omh := OnMsgidHandler(func(fset *token.FileSet, pos token.Pos, msgid string) { - if !msgids.Contains(msgid) { + handler := Handler{ + OnMsgid: func(fset *token.FileSet, pos token.Pos, msgid string) { + if !msgids.Contains(msgid) { + gotAnyMsgidError = true + fmt.Printf("%s:\tmissing msgid: %s\n", fset.Position(pos).String(), msgid) + } + }, + OnUnexpectedInvoke: func(fset *token.FileSet, pos token.Pos, funcname string, argc int) { gotAnyMsgidError = true - fmt.Printf("%s:\tmissing msgid: %s\n", fset.Position(pos).String(), msgid) - } - }) + fmt.Printf("%s:\tunexpected invocation of %s with %d arguments\n", fset.Position(pos).String(), funcname, argc) + }, + LocaleTrFunctions: InitLocaleTrFunctions(), + } if err := filepath.WalkDir(".", func(fpath string, d fs.DirEntry, err error) error { if err != nil { @@ -308,15 +360,15 @@ func main() { if name == "docker" || name == ".git" || name == "node_modules" { return fs.SkipDir } - } else if name == "bindata.go" { + } else if name == "bindata.go" || fpath == "modules/translation/i18n/i18n_test.go" { // skip false positives } else if strings.HasSuffix(name, ".go") { - onError(omh.HandleGoFile(fpath, nil)) + onError(handler.HandleGoFile(fpath, nil)) } else if strings.HasSuffix(name, ".tmpl") { if strings.HasPrefix(fpath, "tests") && strings.HasSuffix(name, ".ini.tmpl") { // skip false positives } else { - onError(omh.HandleTemplateFile(fpath, nil)) + onError(handler.HandleTemplateFile(fpath, nil)) } } return nil diff --git a/build/lint-locale-usage/lint-locale-usage_test.go b/build/lint-locale-usage/lint-locale-usage_test.go index 3b3b746053..81ca12c6db 100644 --- a/build/lint-locale-usage/lint-locale-usage_test.go +++ b/build/lint-locale-usage/lint-locale-usage_test.go @@ -11,33 +11,39 @@ import ( "github.com/stretchr/testify/require" ) +func buildHandler(ret *[]string) Handler { + return Handler{ + OnMsgid: func(fset *token.FileSet, pos token.Pos, msgid string) { + *ret = append(*ret, msgid) + }, + OnUnexpectedInvoke: func(fset *token.FileSet, pos token.Pos, funcname string, argc int) {}, + LocaleTrFunctions: InitLocaleTrFunctions(), + } +} + func HandleGoFileWrapped(t *testing.T, fname, src string) []string { var ret []string - omh := OnMsgidHandler(func(fset *token.FileSet, pos token.Pos, msgid string) { - ret = append(ret, msgid) - }) - require.NoError(t, omh.HandleGoFile(fname, src)) + handler := buildHandler(&ret) + require.NoError(t, handler.HandleGoFile(fname, src)) return ret } func HandleTemplateFileWrapped(t *testing.T, fname, src string) []string { var ret []string - omh := OnMsgidHandler(func(fset *token.FileSet, pos token.Pos, msgid string) { - ret = append(ret, msgid) - }) - require.NoError(t, omh.HandleTemplateFile(fname, src)) + handler := buildHandler(&ret) + require.NoError(t, handler.HandleTemplateFile(fname, src)) return ret } func TestUsagesParser(t *testing.T) { t.Run("go, simple", func(t *testing.T) { - assert.EqualValues(t, + assert.Equal(t, []string{"what.an.example"}, HandleGoFileWrapped(t, "", "package main\nfunc Render(ctx *context.Context) string { return ctx.Tr(\"what.an.example\"); }\n")) }) t.Run("template, simple", func(t *testing.T) { - assert.EqualValues(t, + assert.Equal(t, []string{"what.an.example"}, HandleTemplateFileWrapped(t, "", "{{ ctx.Locale.Tr \"what.an.example\" }}\n")) }) diff --git a/build/lint-locale/lint-locale.go b/build/lint-locale/lint-locale.go index a738fbd684..dc4088c73c 100644 --- a/build/lint-locale/lint-locale.go +++ b/build/lint-locale/lint-locale.go @@ -14,7 +14,7 @@ import ( "slices" "strings" - "code.gitea.io/gitea/modules/locale" + "forgejo.org/modules/translation/localeiter" "github.com/microcosm-cc/bluemonday" "github.com/sergi/go-diff/diffmatchpatch" @@ -52,7 +52,7 @@ func initBlueMondayPolicy() { policy.AllowAttrs("id").Matching(positionalPlaceholderRe).OnElements("code") // Allowed elements with no attributes. Must be a recognized tagname. - policy.AllowElements("strong", "br", "b", "strike", "code", "i") + policy.AllowElements("strong", "br", "b", "strike", "code", "i", "kbd") // TODO: Remove in `actions.workflow.dispatch.trigger_found`. policy.AllowNoAttrs().OnElements("c") @@ -100,7 +100,7 @@ func checkValue(trKey, value string) []string { func checkLocaleContent(localeContent []byte) []string { errors := []string{} - if err := locale.IterateMessagesContent(localeContent, func(trKey, trValue string) error { + if err := localeiter.IterateMessagesContent(localeContent, func(trKey, trValue string) error { errors = append(errors, checkValue(trKey, trValue)...) return nil }); err != nil { @@ -113,8 +113,12 @@ func checkLocaleContent(localeContent []byte) []string { func checkLocaleNextContent(localeContent []byte) []string { errors := []string{} - if err := locale.IterateMessagesNextContent(localeContent, func(trKey, trValue string) error { - errors = append(errors, checkValue(trKey, trValue)...) + if err := localeiter.IterateMessagesNextContent(localeContent, func(trKey, pluralForm, trValue string) error { + fullKey := trKey + if pluralForm != "" { + fullKey = trKey + "." + pluralForm + } + errors = append(errors, checkValue(fullKey, trValue)...) return nil }); err != nil { panic(err) diff --git a/build/lint-locale/lint-locale_test.go b/build/lint-locale/lint-locale_test.go index 791f5278bc..dd146c0d70 100644 --- a/build/lint-locale/lint-locale_test.go +++ b/build/lint-locale/lint-locale_test.go @@ -15,9 +15,9 @@ func TestLocalizationPolicy(t *testing.T) { t.Run("Remove tags", func(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte(`hidden_comment_types_description = Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all " added/removed %[2]s into %[3]s`))) assert.Empty(t, checkLocaleContent([]byte(`editor.commit_directly_to_this_branch = Commit directly to the %[1]s branch.`))) - assert.EqualValues(t, []string{"workflow.dispatch.trigger_found: This workflow has a \x1b[31m\x1b[0mworkflow_dispatch\x1b[31m\x1b[0m event trigger."}, checkLocaleContent([]byte(`workflow.dispatch.trigger_found = This workflow has a workflow_dispatch event trigger.`))) - assert.EqualValues(t, []string{"key: %[3]s"}, checkLocaleContent([]byte(`key = %[3]s`))) - assert.EqualValues(t, []string{"key: "}, checkLocaleContent([]byte(`key = `))) - assert.EqualValues(t, []string{"key: "}, checkLocaleContent([]byte(`key = `))) - assert.EqualValues(t, []string{"key: %[1]s"}, checkLocaleContent([]byte(`key = %[1]s`))) + assert.Equal(t, []string{"workflow.dispatch.trigger_found: This workflow has a \x1b[31m\x1b[0mworkflow_dispatch\x1b[31m\x1b[0m event trigger."}, checkLocaleContent([]byte(`workflow.dispatch.trigger_found = This workflow has a workflow_dispatch event trigger.`))) + assert.Equal(t, []string{"key: %[3]s"}, checkLocaleContent([]byte(`key = %[3]s`))) + assert.Equal(t, []string{"key: "}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: "}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: %[1]s"}, checkLocaleContent([]byte(`key = %[1]s`))) }) t.Run("General safe tags", func(t *testing.T) { @@ -37,8 +37,9 @@ func TestLocalizationPolicy(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte("teams.specific_repositories_helper = Members will only have access to repositories explicitly added to the team. Selecting this will not automatically remove repositories already added with All repositories."))) assert.Empty(t, checkLocaleContent([]byte("sqlite_helper = File path for the SQLite3 database.
Enter an absolute path if you run Forgejo as a service."))) assert.Empty(t, checkLocaleContent([]byte("hi_user_x = Hi %s,"))) + assert.Empty(t, checkLocaleContent([]byte("key = Press Shift"))) - assert.EqualValues(t, []string{"error404: The page you are trying to reach either does not exist or you are not authorized to view it."}, checkLocaleContent([]byte("error404 = The page you are trying to reach either does not exist or you are not authorized to view it."))) + assert.Equal(t, []string{"error404: The page you are trying to reach either does not exist or you are not authorized to view it."}, checkLocaleContent([]byte("error404 = The page you are trying to reach either does not exist or you are not authorized to view it."))) }) t.Run("
", func(t *testing.T) { @@ -47,20 +48,20 @@ func TestLocalizationPolicy(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte(`webauthn_desc = Security keys are hardware devices containing cryptographic keys. They can be used for two-factor authentication. Security keys must support the WebAuthn Authenticator standard.`))) assert.Empty(t, checkLocaleContent([]byte("issues.closed_at = `closed this issue %[2]s`"))) - assert.EqualValues(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) - assert.EqualValues(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) - assert.EqualValues(t, []string{"key: "}, checkLocaleContent([]byte(`key = `))) - assert.EqualValues(t, []string{"key: "}, checkLocaleContent([]byte(`key = `))) - assert.EqualValues(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) - assert.EqualValues(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) - assert.EqualValues(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) - assert.EqualValues(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: "}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: "}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) + assert.Equal(t, []string{"key: \x1b[31m\x1b[0m"}, checkLocaleContent([]byte(`key = `))) }) t.Run("Escaped HTML characters", func(t *testing.T) { assert.Empty(t, checkLocaleContent([]byte("activity.git_stats_push_to_branch = `إلى %s و\"`"))) - assert.EqualValues(t, []string{"key: و\x1b[31m \x1b[0m\x1b[32m\u00a0\x1b[0m"}, checkLocaleContent([]byte(`key = و `))) + assert.Equal(t, []string{"key: و\x1b[31m \x1b[0m\x1b[32m\u00a0\x1b[0m"}, checkLocaleContent([]byte(`key = و `))) }) } @@ -75,7 +76,7 @@ func TestNextLocalizationPolicy(t *testing.T) { } }`))) - assert.EqualValues(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent([]byte(`{ + assert.Equal(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent([]byte(`{ "settings": { "hidden_comment_types_description": "\" \x1b[0m"}, checkLocaleNextContent([]byte(`{"repo.pulls.title_desc": { + "few": "key = ", + "other": "key = " + }}`))) + + assert.Equal(t, []string{"repo.pulls.title_desc.few: key = \x1b[31m\x1b[0m"}, checkLocaleNextContent([]byte(`{"repo.pulls.title_desc": { + "few": "key = ", + "other": "key = " + }}`))) + }) } diff --git a/build/update-locales.sh b/build/update-locales.sh deleted file mode 100755 index 6f9ee334be..0000000000 --- a/build/update-locales.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh - -# this script runs in alpine image which only has `sh` shell - -set +e -if sed --version 2>/dev/null | grep -q GNU; then - SED_INPLACE="sed -i" -else - SED_INPLACE="sed -i ''" -fi -set -e - -if [ ! -f ./options/locale/locale_en-US.ini ]; then - echo "please run this script in the root directory of the project" - exit 1 -fi - -mv ./options/locale/locale_en-US.ini ./options/ - -# the "ini" library for locale has many quirks, its behavior is different from Crowdin. -# see i18n_test.go for more details - -# this script helps to unquote the Crowdin outputs for the quirky ini library -# * find all `key="...\"..."` lines -# * remove the leading quote -# * remove the trailing quote -# * unescape the quotes -# * eg: key="...\"..." => key=..."... -$SED_INPLACE -r -e '/^[-.A-Za-z0-9_]+[ ]*=[ ]*".*"$/ { - s/^([-.A-Za-z0-9_]+)[ ]*=[ ]*"/\1=/ - s/"$// - s/\\"/"/g - }' ./options/locale/*.ini - -# * if the escaped line is incomplete like `key="...` or `key=..."`, quote it with backticks -# * eg: key="... => key=`"...` -# * eg: key=..." => key=`..."` -$SED_INPLACE -r -e 's/^([-.A-Za-z0-9_]+)[ ]*=[ ]*(".*[^"])$/\1=`\2`/' ./options/locale/*.ini -$SED_INPLACE -r -e 's/^([-.A-Za-z0-9_]+)[ ]*=[ ]*([^"].*")$/\1=`\2`/' ./options/locale/*.ini - -# Remove translation under 25% of en_us -baselines=$(wc -l "./options/locale_en-US.ini" | cut -d" " -f1) -baselines=$((baselines / 4)) -for filename in ./options/locale/*.ini; do - lines=$(wc -l "$filename" | cut -d" " -f1) - if [ $lines -lt $baselines ]; then - echo "Removing $filename: $lines/$baselines" - rm "$filename" - fi -done - -mv ./options/locale_en-US.ini ./options/locale/ diff --git a/cmd/actions.go b/cmd/actions.go index 10ae6243c3..12af2c8e86 100644 --- a/cmd/actions.go +++ b/cmd/actions.go @@ -4,25 +4,28 @@ package cmd import ( + "context" "fmt" - "code.gitea.io/gitea/modules/private" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/private" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var ( - // CmdActions represents the available actions sub-commands. - CmdActions = &cli.Command{ +// CmdActions represents the available actions sub-commands. +func cmdActions() *cli.Command { + return &cli.Command{ Name: "actions", Usage: "Manage Forgejo Actions", - Subcommands: []*cli.Command{ - subcmdActionsGenRunnerToken, + Commands: []*cli.Command{ + subcmdActionsGenRunnerToken(), }, } +} - subcmdActionsGenRunnerToken = &cli.Command{ +func subcmdActionsGenRunnerToken() *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, @@ -36,10 +39,10 @@ var ( }, }, } -) +} -func runGenerateActionsRunnerToken(c *cli.Context) error { - ctx, cancel := installSignals() +func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() setting.MustInstalled() diff --git a/cmd/admin.go b/cmd/admin.go index 6c9480e76e..7e06a99cda 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -8,63 +8,71 @@ import ( "context" "fmt" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/gitrepo" - "code.gitea.io/gitea/modules/log" - repo_module "code.gitea.io/gitea/modules/repository" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + "forgejo.org/modules/git" + "forgejo.org/modules/gitrepo" + "forgejo.org/modules/log" + repo_module "forgejo.org/modules/repository" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var ( - // CmdAdmin represents the available admin sub-command. - CmdAdmin = &cli.Command{ +// CmdAdmin represents the available admin sub-command. +func cmdAdmin() *cli.Command { + return &cli.Command{ Name: "admin", Usage: "Perform common administrative operations", - Subcommands: []*cli.Command{ - subcmdUser, - subcmdRepoSyncReleases, - subcmdRegenerate, - subcmdAuth, - subcmdSendMail, + Commands: []*cli.Command{ + subcmdUser(), + subcmdRepoSyncReleases(), + subcmdRegenerate(), + subcmdAuth(), + subcmdSendMail(), }, } +} - subcmdRepoSyncReleases = &cli.Command{ +func subcmdRepoSyncReleases() *cli.Command { + return &cli.Command{ Name: "repo-sync-releases", Usage: "Synchronize repository releases with tags", Action: runRepoSyncReleases, } +} - subcmdRegenerate = &cli.Command{ +func subcmdRegenerate() *cli.Command { + return &cli.Command{ Name: "regenerate", Usage: "Regenerate specific files", - Subcommands: []*cli.Command{ + Commands: []*cli.Command{ microcmdRegenHooks, microcmdRegenKeys, }, } +} - subcmdAuth = &cli.Command{ +func subcmdAuth() *cli.Command { + return &cli.Command{ Name: "auth", Usage: "Modify external auth providers", - Subcommands: []*cli.Command{ - microcmdAuthAddOauth, - microcmdAuthUpdateOauth, - microcmdAuthAddLdapBindDn, - microcmdAuthUpdateLdapBindDn, - microcmdAuthAddLdapSimpleAuth, - microcmdAuthUpdateLdapSimpleAuth, - microcmdAuthAddSMTP, - microcmdAuthUpdateSMTP, - microcmdAuthList, - microcmdAuthDelete, + Commands: []*cli.Command{ + microcmdAuthAddOauth(), + microcmdAuthUpdateOauth(), + microcmdAuthAddLdapBindDn(), + microcmdAuthUpdateLdapBindDn(), + microcmdAuthAddLdapSimpleAuth(), + microcmdAuthUpdateLdapSimpleAuth(), + microcmdAuthAddSMTP(), + microcmdAuthUpdateSMTP(), + microcmdAuthList(), + microcmdAuthDelete(), }, } +} - subcmdSendMail = &cli.Command{ +func subcmdSendMail() *cli.Command { + return &cli.Command{ Name: "sendmail", Usage: "Send a message to all users", Action: runSendMail, @@ -86,15 +94,17 @@ var ( }, }, } +} - idFlag = &cli.Int64Flag{ +func idFlag() *cli.Int64Flag { + return &cli.Int64Flag{ Name: "id", Usage: "ID of authentication source", } -) +} -func runRepoSyncReleases(_ *cli.Context) error { - ctx, cancel := installSignals() +func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index 4777a92908..cb95b3b3c8 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -4,26 +4,30 @@ package cmd import ( + "context" "errors" "fmt" "os" "text/tabwriter" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - auth_service "code.gitea.io/gitea/services/auth" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + auth_service "forgejo.org/services/auth" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var ( - microcmdAuthDelete = &cli.Command{ +func microcmdAuthDelete() *cli.Command { + return &cli.Command{ Name: "delete", Usage: "Delete specific auth source", - Flags: []cli.Flag{idFlag}, + Flags: []cli.Flag{idFlag()}, Action: runDeleteAuth, } - microcmdAuthList = &cli.Command{ +} + +func microcmdAuthList() *cli.Command { + return &cli.Command{ Name: "list", Usage: "List auth sources", Action: runListAuth, @@ -54,10 +58,10 @@ var ( }, }, } -) +} -func runListAuth(c *cli.Context) error { - ctx, cancel := installSignals() +func runListAuth(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { @@ -81,7 +85,7 @@ func runListAuth(c *cli.Context) error { // 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") + fmt.Fprint(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) } @@ -90,12 +94,12 @@ func runListAuth(c *cli.Context) error { return nil } -func runDeleteAuth(c *cli.Context) error { +func runDeleteAuth(ctx context.Context, c *cli.Command) error { if !c.IsSet("id") { return errors.New("--id flag is missing") } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go index aff2a12855..997d6b3a16 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -8,10 +8,10 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/services/auth/source/ldap" + "forgejo.org/models/auth" + "forgejo.org/services/auth/source/ldap" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) type ( @@ -23,8 +23,8 @@ type ( } ) -var ( - commonLdapCLIFlags = []cli.Flag{ +func commonLdapCLIFlags() []cli.Flag { + return []cli.Flag{ &cli.StringFlag{ Name: "name", Usage: "Authentication name.", @@ -102,8 +102,10 @@ var ( Usage: "The attribute of the user’s LDAP record containing the user’s avatar.", }, } +} - ldapBindDnCLIFlags = append(commonLdapCLIFlags, +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.", @@ -128,49 +130,59 @@ var ( Name: "page-size", Usage: "Search page size.", }) +} - ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags, +func ldapSimpleAuthCLIFlags() []cli.Flag { + return append(commonLdapCLIFlags(), &cli.StringFlag{ Name: "user-dn", Usage: "The user's DN.", }) +} - microcmdAuthAddLdapBindDn = &cli.Command{ +func microcmdAuthAddLdapBindDn() *cli.Command { + return &cli.Command{ Name: "add-ldap", Usage: "Add new LDAP (via Bind DN) authentication source", - Action: func(c *cli.Context) error { - return newAuthService().addLdapBindDn(c) + Action: func(ctx context.Context, cli *cli.Command) error { + return newAuthService().addLdapBindDn(ctx, cli) }, - Flags: ldapBindDnCLIFlags, + Flags: ldapBindDnCLIFlags(), } +} - microcmdAuthUpdateLdapBindDn = &cli.Command{ +func microcmdAuthUpdateLdapBindDn() *cli.Command { + return &cli.Command{ Name: "update-ldap", Usage: "Update existing LDAP (via Bind DN) authentication source", - Action: func(c *cli.Context) error { - return newAuthService().updateLdapBindDn(c) + Action: func(ctx context.Context, cli *cli.Command) error { + return newAuthService().updateLdapBindDn(ctx, cli) }, - Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...), + Flags: append([]cli.Flag{idFlag()}, ldapBindDnCLIFlags()...), } +} - microcmdAuthAddLdapSimpleAuth = &cli.Command{ +func microcmdAuthAddLdapSimpleAuth() *cli.Command { + return &cli.Command{ Name: "add-ldap-simple", Usage: "Add new LDAP (simple auth) authentication source", - Action: func(c *cli.Context) error { - return newAuthService().addLdapSimpleAuth(c) + Action: func(ctx context.Context, cli *cli.Command) error { + return newAuthService().addLdapSimpleAuth(ctx, cli) }, - Flags: ldapSimpleAuthCLIFlags, + Flags: ldapSimpleAuthCLIFlags(), } +} - microcmdAuthUpdateLdapSimpleAuth = &cli.Command{ +func microcmdAuthUpdateLdapSimpleAuth() *cli.Command { + return &cli.Command{ Name: "update-ldap-simple", Usage: "Update existing LDAP (simple auth) authentication source", - Action: func(c *cli.Context) error { - return newAuthService().updateLdapSimpleAuth(c) + Action: func(ctx context.Context, cli *cli.Command) error { + return newAuthService().updateLdapSimpleAuth(ctx, cli) }, - Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...), + Flags: append([]cli.Flag{idFlag()}, ldapSimpleAuthCLIFlags()...), } -) +} // newAuthService creates a service with default functions. func newAuthService() *authService { @@ -183,7 +195,7 @@ func newAuthService() *authService { } // parseAuthSource assigns values on authSource according to command line flags. -func parseAuthSource(c *cli.Context, authSource *auth.Source) { +func parseAuthSource(c *cli.Command, authSource *auth.Source) { if c.IsSet("name") { authSource.Name = c.String("name") } @@ -202,7 +214,7 @@ func parseAuthSource(c *cli.Context, authSource *auth.Source) { } // parseLdapConfig assigns values on config according to command line flags. -func parseLdapConfig(c *cli.Context, config *ldap.Source) error { +func parseLdapConfig(c *cli.Command, config *ldap.Source) error { if c.IsSet("name") { config.Name = c.String("name") } @@ -289,7 +301,7 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) { // 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.Context, authType auth.Type) (*auth.Source, error) { +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 } @@ -307,12 +319,12 @@ func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, authTyp } // addLdapBindDn adds a new LDAP via Bind DN authentication source. -func (a *authService) addLdapBindDn(c *cli.Context) error { +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 } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if err := a.initDB(ctx); err != nil { @@ -336,8 +348,8 @@ func (a *authService) addLdapBindDn(c *cli.Context) error { } // updateLdapBindDn updates a new LDAP via Bind DN authentication source. -func (a *authService) updateLdapBindDn(c *cli.Context) error { - ctx, cancel := installSignals() +func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if err := a.initDB(ctx); err != nil { @@ -358,12 +370,12 @@ func (a *authService) updateLdapBindDn(c *cli.Context) error { } // addLdapSimpleAuth adds a new LDAP (simple auth) authentication source. -func (a *authService) addLdapSimpleAuth(c *cli.Context) error { +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 } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if err := a.initDB(ctx); err != nil { @@ -387,8 +399,8 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error { } // updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source. -func (a *authService) updateLdapSimpleAuth(c *cli.Context) error { - ctx, cancel := installSignals() +func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if err := a.initDB(ctx); err != nil { diff --git a/cmd/admin_auth_ldap_test.go b/cmd/admin_auth_ldap_test.go index d5385d09e8..89ce5f4f08 100644 --- a/cmd/admin_auth_ldap_test.go +++ b/cmd/admin_auth_ldap_test.go @@ -7,19 +7,18 @@ import ( "context" "testing" - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/services/auth/source/ldap" + "forgejo.org/models/auth" + "forgejo.org/modules/test" + "forgejo.org/services/auth/source/ldap" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) func TestAddLdapBindDn(t *testing.T) { // Mock cli functions to do not exit on error - osExiter := cli.OsExiter - defer func() { cli.OsExiter = osExiter }() - cli.OsExiter = func(code int) {} + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() // Test cases cases := []struct { @@ -216,22 +215,22 @@ func TestAddLdapBindDn(t *testing.T) { return nil }, updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { - assert.FailNow(t, "case %d: should not call updateAuthSource", n) + assert.FailNow(t, "should not call updateAuthSource", "case: %d", n) return nil }, getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { - assert.FailNow(t, "case %d: should not call getAuthSourceByID", n) + assert.FailNow(t, "should not call getAuthSourceByID", "case: %d", n) return nil, nil }, } // Create a copy of command to test - app := cli.NewApp() - app.Flags = microcmdAuthAddLdapBindDn.Flags + app := cli.Command{} + app.Flags = microcmdAuthAddLdapBindDn().Flags app.Action = service.addLdapBindDn // Run it - err := app.Run(c.args) + err := app.Run(t.Context(), c.args) if c.errMsg != "" { assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) } else { @@ -243,9 +242,7 @@ func TestAddLdapBindDn(t *testing.T) { func TestAddLdapSimpleAuth(t *testing.T) { // Mock cli functions to do not exit on error - osExiter := cli.OsExiter - defer func() { cli.OsExiter = osExiter }() - cli.OsExiter = func(code int) {} + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() // Test cases cases := []struct { @@ -447,22 +444,22 @@ func TestAddLdapSimpleAuth(t *testing.T) { return nil }, updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { - assert.FailNow(t, "case %d: should not call updateAuthSource", n) + assert.FailNow(t, "should not call updateAuthSource", "case: %d", n) return nil }, getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { - assert.FailNow(t, "case %d: should not call getAuthSourceByID", n) + assert.FailNow(t, "should not call getAuthSourceByID", "case: %d", n) return nil, nil }, } // Create a copy of command to test - app := cli.NewApp() - app.Flags = microcmdAuthAddLdapSimpleAuth.Flags + app := cli.Command{} + app.Flags = microcmdAuthAddLdapSimpleAuth().Flags app.Action = service.addLdapSimpleAuth // Run it - err := app.Run(c.args) + err := app.Run(t.Context(), c.args) if c.errMsg != "" { assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) } else { @@ -474,9 +471,7 @@ func TestAddLdapSimpleAuth(t *testing.T) { func TestUpdateLdapBindDn(t *testing.T) { // Mock cli functions to do not exit on error - osExiter := cli.OsExiter - defer func() { cli.OsExiter = osExiter }() - cli.OsExiter = func(code int) {} + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() // Test cases cases := []struct { @@ -898,7 +893,7 @@ func TestUpdateLdapBindDn(t *testing.T) { return nil }, createAuthSource: func(ctx context.Context, authSource *auth.Source) error { - assert.FailNow(t, "case %d: should not call createAuthSource", n) + assert.FailNow(t, "should not call createAuthSource", "case: %d", n) return nil }, updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { @@ -920,12 +915,12 @@ func TestUpdateLdapBindDn(t *testing.T) { } // Create a copy of command to test - app := cli.NewApp() - app.Flags = microcmdAuthUpdateLdapBindDn.Flags + app := cli.Command{} + app.Flags = microcmdAuthUpdateLdapBindDn().Flags app.Action = service.updateLdapBindDn // Run it - err := app.Run(c.args) + err := app.Run(t.Context(), c.args) if c.errMsg != "" { assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) } else { @@ -937,9 +932,7 @@ func TestUpdateLdapBindDn(t *testing.T) { func TestUpdateLdapSimpleAuth(t *testing.T) { // Mock cli functions to do not exit on error - osExiter := cli.OsExiter - defer func() { cli.OsExiter = osExiter }() - cli.OsExiter = func(code int) {} + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() // Test cases cases := []struct { @@ -1288,7 +1281,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { return nil }, createAuthSource: func(ctx context.Context, authSource *auth.Source) error { - assert.FailNow(t, "case %d: should not call createAuthSource", n) + assert.FailNow(t, "should not call createAuthSource", "case: %d", n) return nil }, updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { @@ -1310,12 +1303,12 @@ func TestUpdateLdapSimpleAuth(t *testing.T) { } // Create a copy of command to test - app := cli.NewApp() - app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags + app := cli.Command{} + app.Flags = microcmdAuthUpdateLdapSimpleAuth().Flags app.Action = service.updateLdapSimpleAuth // Run it - err := app.Run(c.args) + err := app.Run(t.Context(), c.args) if c.errMsg != "" { assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) } else { diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go index 8e6239ac33..abdcd5d48a 100644 --- a/cmd/admin_auth_oauth.go +++ b/cmd/admin_auth_oauth.go @@ -4,18 +4,19 @@ package cmd import ( + "context" "errors" "fmt" "net/url" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/services/auth/source/oauth2" + auth_model "forgejo.org/models/auth" + "forgejo.org/services/auth/source/oauth2" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var ( - oauthCLIFlags = []cli.Flag{ +func oauthCLIFlags() []cli.Flag { + return []cli.Flag{ &cli.StringFlag{ Name: "name", Value: "", @@ -120,23 +121,27 @@ var ( Usage: "Activate automatic team membership removal depending on groups", }, } +} - microcmdAuthAddOauth = &cli.Command{ +func microcmdAuthAddOauth() *cli.Command { + return &cli.Command{ Name: "add-oauth", Usage: "Add new Oauth authentication source", Action: runAddOauth, - Flags: oauthCLIFlags, + Flags: oauthCLIFlags(), } +} - microcmdAuthUpdateOauth = &cli.Command{ +func microcmdAuthUpdateOauth() *cli.Command { + return &cli.Command{ Name: "update-oauth", Usage: "Update existing Oauth authentication source", Action: runUpdateOauth, - Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...), + Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{idFlag()}, oauthCLIFlags()[1:]...)...), } -) +} -func parseOAuth2Config(c *cli.Context) *oauth2.Source { +func parseOAuth2Config(_ context.Context, c *cli.Command) *oauth2.Source { var customURLMapping *oauth2.CustomURLMapping if c.IsSet("use-custom-urls") { customURLMapping = &oauth2.CustomURLMapping{ @@ -168,15 +173,15 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source { } } -func runAddOauth(c *cli.Context) error { - ctx, cancel := installSignals() +func runAddOauth(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { return err } - config := parseOAuth2Config(c) + config := parseOAuth2Config(ctx, c) if config.Provider == "openidConnect" { discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL) if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") { @@ -192,12 +197,12 @@ func runAddOauth(c *cli.Context) error { }) } -func runUpdateOauth(c *cli.Context) error { +func runUpdateOauth(ctx context.Context, c *cli.Command) error { if !c.IsSet("id") { return errors.New("--id flag is missing") } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { diff --git a/cmd/admin_auth_stmp.go b/cmd/admin_auth_stmp.go index d724746905..48b3adaac3 100644 --- a/cmd/admin_auth_stmp.go +++ b/cmd/admin_auth_stmp.go @@ -4,18 +4,19 @@ package cmd import ( + "context" "errors" "strings" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/services/auth/source/smtp" + auth_model "forgejo.org/models/auth" + "forgejo.org/modules/util" + "forgejo.org/services/auth/source/smtp" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var ( - smtpCLIFlags = []cli.Flag{ +func smtpCLIFlags() []cli.Flag { + return []cli.Flag{ &cli.StringFlag{ Name: "name", Value: "", @@ -71,23 +72,27 @@ var ( Value: true, }, } +} - microcmdAuthAddSMTP = &cli.Command{ +func microcmdAuthAddSMTP() *cli.Command { + return &cli.Command{ Name: "add-smtp", Usage: "Add new SMTP authentication source", Action: runAddSMTP, - Flags: smtpCLIFlags, + Flags: smtpCLIFlags(), } +} - microcmdAuthUpdateSMTP = &cli.Command{ +func microcmdAuthUpdateSMTP() *cli.Command { + return &cli.Command{ Name: "update-smtp", Usage: "Update existing SMTP authentication source", Action: runUpdateSMTP, - Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...), + Flags: append(smtpCLIFlags()[:1], append([]cli.Flag{idFlag()}, smtpCLIFlags()[1:]...)...), } -) +} -func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error { +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"} @@ -123,8 +128,8 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error { return nil } -func runAddSMTP(c *cli.Context) error { - ctx, cancel := installSignals() +func runAddSMTP(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { @@ -163,12 +168,12 @@ func runAddSMTP(c *cli.Context) error { }) } -func runUpdateSMTP(c *cli.Context) error { +func runUpdateSMTP(ctx context.Context, c *cli.Command) error { if !c.IsSet("id") { return errors.New("--id flag is missing") } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { diff --git a/cmd/admin_regenerate.go b/cmd/admin_regenerate.go index 0db505ff9c..7bfd12f8f4 100644 --- a/cmd/admin_regenerate.go +++ b/cmd/admin_regenerate.go @@ -4,11 +4,13 @@ package cmd import ( - asymkey_model "code.gitea.io/gitea/models/asymkey" - "code.gitea.io/gitea/modules/graceful" - repo_service "code.gitea.io/gitea/services/repository" + "context" - "github.com/urfave/cli/v2" + asymkey_model "forgejo.org/models/asymkey" + "forgejo.org/modules/graceful" + repo_service "forgejo.org/services/repository" + + "github.com/urfave/cli/v3" ) var ( @@ -25,8 +27,8 @@ var ( } ) -func runRegenerateHooks(_ *cli.Context) error { - ctx, cancel := installSignals() +func runRegenerateHooks(ctx context.Context, _ *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { @@ -35,8 +37,8 @@ func runRegenerateHooks(_ *cli.Context) error { return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext()) } -func runRegenerateKeys(_ *cli.Context) error { - ctx, cancel := installSignals() +func runRegenerateKeys(ctx context.Context, _ *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { diff --git a/cmd/admin_user.go b/cmd/admin_user.go index 967a6ed88a..f4f6fb49af 100644 --- a/cmd/admin_user.go +++ b/cmd/admin_user.go @@ -4,18 +4,21 @@ package cmd import ( - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var subcmdUser = &cli.Command{ - Name: "user", - Usage: "Modify users", - Subcommands: []*cli.Command{ - microcmdUserCreate, - microcmdUserList, - microcmdUserChangePassword, - microcmdUserDelete, - microcmdUserGenerateAccessToken, - microcmdUserMustChangePassword, - }, +func subcmdUser() *cli.Command { + return &cli.Command{ + Name: "user", + Usage: "Modify users", + Commands: []*cli.Command{ + microcmdUserCreate(), + microcmdUserList(), + microcmdUserChangePassword(), + microcmdUserDelete(), + microcmdUserGenerateAccessToken(), + microcmdUserMustChangePassword(), + microcmdUserResetMFA(), + }, + } } diff --git a/cmd/admin_user_change_password.go b/cmd/admin_user_change_password.go index bd9063a8e4..dd8c9d378a 100644 --- a/cmd/admin_user_change_password.go +++ b/cmd/admin_user_change_password.go @@ -4,49 +4,52 @@ package cmd import ( + "context" "errors" "fmt" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/auth/password" - "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/setting" - user_service "code.gitea.io/gitea/services/user" + user_model "forgejo.org/models/user" + "forgejo.org/modules/auth/password" + "forgejo.org/modules/optional" + "forgejo.org/modules/setting" + user_service "forgejo.org/services/user" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var microcmdUserChangePassword = &cli.Command{ - Name: "change-password", - Usage: "Change a user's password", - Action: runChangePassword, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "username", - Aliases: []string{"u"}, - Value: "", - Usage: "The user to change password for", +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"}, + Value: "", + Usage: "The user to change password for", + }, + &cli.StringFlag{ + Name: "password", + Aliases: []string{"p"}, + Value: "", + Usage: "New password to set for user", + }, + &cli.BoolFlag{ + Name: "must-change-password", + Usage: "User must change password", + Value: true, + }, }, - &cli.StringFlag{ - Name: "password", - Aliases: []string{"p"}, - Value: "", - Usage: "New password to set for user", - }, - &cli.BoolFlag{ - Name: "must-change-password", - Usage: "User must change password", - Value: true, - }, - }, + } } -func runChangePassword(c *cli.Context) error { +func runChangePassword(ctx context.Context, c *cli.Command) error { if err := argsSet(c, "username", "password"); err != nil { return err } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index 781148e734..96431412f6 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -4,71 +4,88 @@ package cmd import ( + "context" "errors" "fmt" + "strings" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" - pwd "code.gitea.io/gitea/modules/auth/password" - "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/setting" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + user_model "forgejo.org/models/user" + pwd "forgejo.org/modules/auth/password" + "forgejo.org/modules/optional" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var microcmdUserCreate = &cli.Command{ - Name: "create", - Usage: "Create a new user in database", - Action: runCreateUser, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Usage: "Username. DEPRECATED: use username instead", +func microcmdUserCreate() *cli.Command { + return &cli.Command{ + Name: "create", + Usage: "Create a new user in database", + Action: runCreateUser, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Usage: "Username. DEPRECATED: use username instead", + }, + &cli.StringFlag{ + Name: "username", + Usage: "Username", + }, + &cli.StringFlag{ + Name: "password", + Usage: "User password", + }, + &cli.StringFlag{ + Name: "email", + Usage: "User email address", + }, + &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: "Set this option to false to prevent forcing the user to change their password after initial login", + Value: 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`, + }, }, - &cli.StringFlag{ - Name: "username", - Usage: "Username", - }, - &cli.StringFlag{ - Name: "password", - Usage: "User password", - }, - &cli.StringFlag{ - Name: "email", - Usage: "User email address", - }, - &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: "Set this option to false to prevent forcing the user to change their password after initial login", - Value: true, - DisableDefaultText: 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.BoolFlag{ - Name: "restricted", - Usage: "Make a restricted user account", - }, - }, + } } -func runCreateUser(c *cli.Context) error { +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() @@ -93,10 +110,10 @@ func runCreateUser(c *cli.Context) error { username = c.String("username") } else { username = c.String("name") - _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n") + _, _ = fmt.Fprint(c.Root().ErrWriter, "--name flag is deprecated. Use --username instead.\n") } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { @@ -150,6 +167,7 @@ func runCreateUser(c *cli.Context) error { IsAdmin: isAdmin, MustChangePassword: mustChangePassword, Visibility: visibility, + FullName: c.String("fullname"), } overwriteDefault := &user_model.CreateUserOverwriteOptions{ @@ -157,23 +175,40 @@ func runCreateUser(c *cli.Context) error { 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, overwriteDefault); err != nil { return fmt.Errorf("CreateUser: %w", err) } + fmt.Printf("New user '%s' has been successfully created!\n", username) - if c.Bool("access-token") { - t := &auth_model.AccessToken{ - Name: "gitea-admin", - UID: u.ID, - } - + // 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 } - fmt.Printf("Access token was successfully created... %s\n", t.Token) } - - fmt.Printf("New user '%s' has been successfully created!\n", username) return nil } diff --git a/cmd/admin_user_delete.go b/cmd/admin_user_delete.go index 520557554a..3382c53e5f 100644 --- a/cmd/admin_user_delete.go +++ b/cmd/admin_user_delete.go @@ -4,49 +4,52 @@ package cmd import ( + "context" "errors" "fmt" "strings" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/storage" - user_service "code.gitea.io/gitea/services/user" + user_model "forgejo.org/models/user" + "forgejo.org/modules/storage" + user_service "forgejo.org/services/user" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var microcmdUserDelete = &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", +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", + }, }, - &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, + Action: runDeleteUser, + } } -func runDeleteUser(c *cli.Context) error { +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") } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go index 6c2c10494e..d0f2878297 100644 --- a/cmd/admin_user_generate_access_token.go +++ b/cmd/admin_user_generate_access_token.go @@ -4,49 +4,52 @@ package cmd import ( + "context" "errors" "fmt" - auth_model "code.gitea.io/gitea/models/auth" - user_model "code.gitea.io/gitea/models/user" + auth_model "forgejo.org/models/auth" + user_model "forgejo.org/models/user" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var microcmdUserGenerateAccessToken = &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", +func microcmdUserGenerateAccessToken() *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"`, + }, }, - &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: "", - Usage: "Comma separated list of scopes to apply to access token", - }, - }, - Action: runGenerateAccessToken, + Action: runGenerateAccessToken, + } } -func runGenerateAccessToken(c *cli.Context) error { +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") + return errors.New("you must provide a username to generate a token for") } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { @@ -77,6 +80,9 @@ func runGenerateAccessToken(c *cli.Context) error { 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 diff --git a/cmd/admin_user_list.go b/cmd/admin_user_list.go index 4c2b26d1df..ccc4b8c917 100644 --- a/cmd/admin_user_list.go +++ b/cmd/admin_user_list.go @@ -4,29 +4,32 @@ package cmd import ( + "context" "fmt" "os" "text/tabwriter" - user_model "code.gitea.io/gitea/models/user" + user_model "forgejo.org/models/user" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var microcmdUserList = &cli.Command{ - Name: "list", - Usage: "List users", - Action: runListUsers, - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "admin", - Usage: "List only admin users", +func microcmdUserList() *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(c *cli.Context) error { - ctx, cancel := installSignals() +func runListUsers(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if err := initDB(ctx); err != nil { @@ -41,7 +44,7 @@ func runListUsers(c *cli.Context) error { w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0) if c.IsSet("admin") { - fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n") + fmt.Fprint(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) @@ -49,7 +52,7 @@ func runListUsers(c *cli.Context) error { } } else { twofa := user_model.UserList(users).GetTwoFaStatus(ctx) - fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n") + fmt.Fprint(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]) } diff --git a/cmd/admin_user_must_change_password.go b/cmd/admin_user_must_change_password.go index 2794414259..2ccad56eb9 100644 --- a/cmd/admin_user_must_change_password.go +++ b/cmd/admin_user_must_change_password.go @@ -4,38 +4,41 @@ package cmd import ( + "context" "errors" "fmt" - user_model "code.gitea.io/gitea/models/user" + user_model "forgejo.org/models/user" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var microcmdUserMustChangePassword = &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", +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", + }, }, - &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(c *cli.Context) error { - ctx, cancel := installSignals() +func runMustChangePassword(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() if c.NArg() == 0 && !c.IsSet("all") { diff --git a/cmd/admin_user_reset_mfa.go b/cmd/admin_user_reset_mfa.go new file mode 100644 index 0000000000..8107fd97bf --- /dev/null +++ b/cmd/admin_user_reset_mfa.go @@ -0,0 +1,73 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package cmd + +import ( + "context" + "fmt" + + auth_model "forgejo.org/models/auth" + user_model "forgejo.org/models/user" + + "github.com/urfave/cli/v3" +) + +func microcmdUserResetMFA() *cli.Command { + return &cli.Command{ + Name: "reset-mfa", + Usage: "Remove all two-factor authentication configurations for a user", + Action: runResetMFA, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "username", + Aliases: []string{"u"}, + Value: "", + Usage: "The user to update", + }, + }, + } +} + +func runResetMFA(ctx context.Context, c *cli.Command) error { + if err := argsSet(c, "username"); err != nil { + return err + } + + ctx, cancel := installSignals(ctx) + defer cancel() + + if err := initDB(ctx); err != nil { + return err + } + + user, err := user_model.GetUserByName(ctx, c.String("username")) + if err != nil { + return err + } + + webAuthnList, err := auth_model.GetWebAuthnCredentialsByUID(ctx, user.ID) + if err != nil { + return err + } + + for _, credential := range webAuthnList { + if _, err := auth_model.DeleteCredential(ctx, credential.ID, user.ID); err != nil { + return err + } + } + + tfaModes, err := auth_model.GetTwoFactorByUID(ctx, user.ID) + if err == nil && tfaModes != nil { + if err := auth_model.DeleteTwoFactorByID(ctx, tfaModes.ID, user.ID); err != nil { + return err + } + } else { + if _, is := err.(auth_model.ErrTwoFactorNotEnrolled); !is { + return err + } + } + + fmt.Printf("%s's two-factor authentication settings have been removed!\n", user.Name) + return nil +} diff --git a/cmd/cert.go b/cmd/cert.go index bf83af389f..f9e3a16f3e 100644 --- a/cmd/cert.go +++ b/cmd/cert.go @@ -6,6 +6,7 @@ package cmd import ( + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -20,47 +21,49 @@ import ( "strings" "time" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // CmdCert represents the available cert sub-command. -var CmdCert = &cli.Command{ - Name: "cert", - Usage: "Generate self-signed certificate", - Description: `Generate a self-signed X.509 certificate for a TLS server. +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", - Value: "", - Usage: "Comma-separated hostnames and IPs to generate a certificate for", + Action: runCert, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "host", + Value: "", + Usage: "Comma-separated hostnames and IPs to generate a certificate for", + }, + &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: "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", - }, - }, + } } func publicKey(priv any) any { @@ -89,7 +92,7 @@ func pemBlockForKey(priv any) *pem.Block { } } -func runCert(c *cli.Context) error { +func runCert(ctx context.Context, c *cli.Command) error { if err := argsSet(c, "host"); err != nil { return err } diff --git a/cmd/cmd.go b/cmd/cmd.go index 423dce2674..85a482b78c 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -15,24 +15,26 @@ import ( "strings" "syscall" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" - "github.com/urfave/cli/v2" + "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 passed Context. -func argsSet(c *cli.Context, args ...string) error { +func argsSet(c *cli.Command, args ...string) error { for _, a := range args { if !c.IsSet(a) { return errors.New(a + " is not set") } - if util.IsEmptyString(c.String(a)) { - return errors.New(a + " is required") + if s, ok := c.Value(a).(string); ok { + if util.IsEmptyString(s) { + return errors.New(a + " is required") + } } } return nil @@ -73,8 +75,8 @@ If this is the intended configuration file complete the [database] section.`, se return nil } -func installSignals() (context.Context, context.CancelFunc) { - ctx, cancel := context.WithCancel(context.Background()) +func installSignals(ctx context.Context) (context.Context, context.CancelFunc) { + ctx, cancel := context.WithCancel(ctx) go func() { // install notify signalChannel := make(chan os.Signal, 1) @@ -109,7 +111,7 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) { log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer) } -func globalBool(c *cli.Context, name string) bool { +func globalBool(c *cli.Command, name string) bool { for _, ctx := range c.Lineage() { if ctx.Bool(name) { return true @@ -120,16 +122,16 @@ func globalBool(c *cli.Context, name string) bool { // 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(*cli.Context) error { - return func(c *cli.Context) error { +func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(ctx context.Context, cli *cli.Command) (context.Context, error) { + return func(ctx context.Context, cli *cli.Command) (context.Context, error) { level := defaultLevel - if globalBool(c, "quiet") { + if globalBool(cli, "quiet") { level = log.FATAL } - if globalBool(c, "debug") || globalBool(c, "verbose") { + if globalBool(cli, "debug") || globalBool(cli, "verbose") { level = log.TRACE } log.SetConsoleLogger(log.DEFAULT, "console-default", level) - return nil + return ctx, nil } } diff --git a/cmd/docs.go b/cmd/docs.go deleted file mode 100644 index 1dc0980c00..0000000000 --- a/cmd/docs.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package cmd - -import ( - "fmt" - "os" - "strings" - - "github.com/urfave/cli/v2" -) - -// CmdDocs represents the available docs sub-command. -var CmdDocs = &cli.Command{ - Name: "docs", - Usage: "Output CLI documentation", - Description: "A command to output Forgejo'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(ctx *cli.Context) error { - docs, err := ctx.App.ToMarkdown() - if ctx.Bool("man") { - docs, err = ctx.App.ToMan() - } - if err != nil { - return err - } - - if !ctx.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 ctx.String("output") != "" { - fi, err := os.Create(ctx.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 index 9957053365..681794f094 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -4,6 +4,7 @@ package cmd import ( + "context" "fmt" golog "log" "os" @@ -11,89 +12,94 @@ import ( "strings" "text/tabwriter" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/migrations" - migrate_base "code.gitea.io/gitea/models/migrations/base" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/services/doctor" + "forgejo.org/models/db" + "forgejo.org/models/migrations" + migrate_base "forgejo.org/models/migrations/base" + "forgejo.org/modules/container" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/services/doctor" - "github.com/urfave/cli/v2" - "xorm.io/xorm" + "github.com/urfave/cli/v3" ) // CmdDoctor represents the available doctor sub-command. -var CmdDoctor = &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 Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.", +func cmdDoctor() *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 Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.", - Subcommands: []*cli.Command{ - cmdDoctorCheck, - cmdRecreateTable, - cmdDoctorConvert, - }, + Commands: []*cli.Command{ + cmdDoctorCheck(), + cmdRecreateTable(), + cmdDoctorConvert(), + }, + } } -var cmdDoctorCheck = &cli.Command{ - Name: "check", - Usage: "Diagnose and optionally fix problems", - Description: "A command to diagnose problems with the current Forgejo 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", +func cmdDoctorCheck() *cli.Command { + return &cli.Command{ + Name: "check", + Usage: "Diagnose and optionally fix problems", + Description: "A command to diagnose problems with the current Forgejo 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", + }, }, - &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", - }, - }, + } } -var cmdRecreateTable = &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", +func cmdRecreateTable() *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 Forgejo uses change across versions, sometimes changing default values and leaving old unused columns. + Description: `The database definitions Forgejo 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, + Action: runRecreateTable, + } } -func runRecreateTable(ctx *cli.Context) error { - stdCtx, cancel := installSignals() +func runRecreateTable(stdCtx context.Context, ctx *cli.Command) error { + stdCtx, cancel := installSignals(stdCtx) defer cancel() // Redirect the default golog to here @@ -120,7 +126,7 @@ func runRecreateTable(ctx *cli.Context) error { args := ctx.Args() names := make([]string, 0, ctx.NArg()) - for i := 0; i < ctx.NArg(); i++ { + for i := range ctx.NArg() { names = append(names, args.Get(i)) } @@ -130,24 +136,31 @@ func runRecreateTable(ctx *cli.Context) error { } recreateTables := migrate_base.RecreateTables(beans...) - return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error { - if err := migrations.EnsureUpToDate(x); err != nil { + return db.InitEngineWithMigration(stdCtx, func(x db.Engine) error { + engine, err := db.GetMasterEngine(x) + if err != nil { return err } - return recreateTables(x) + + if err := migrations.EnsureUpToDate(engine); err != nil { + return err + } + + return recreateTables(engine) }) } -func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) { +func setupDoctorDefaultLogger(ctx *cli.Command, colorize bool) { // Silence the default loggers setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr) logFile := ctx.String("log-file") - if logFile == "" { + switch logFile { + case "": return // if no doctor log-file is set, do not show any log from default logger - } else if logFile == "-" { + case "-": setupConsoleLogger(log.TRACE, colorize, os.Stdout) - } else { + 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) @@ -159,8 +172,8 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) { } } -func runDoctorCheck(ctx *cli.Context) error { - stdCtx, cancel := installSignals() +func runDoctorCheck(stdCtx context.Context, ctx *cli.Command) error { + stdCtx, cancel := installSignals(stdCtx) defer cancel() colorize := log.CanColorStdout diff --git a/cmd/doctor_convert.go b/cmd/doctor_convert.go index 190b2fc2ef..44bebae154 100644 --- a/cmd/doctor_convert.go +++ b/cmd/doctor_convert.go @@ -4,25 +4,28 @@ package cmd import ( + "context" "fmt" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // cmdDoctorConvert represents the available convert sub-command. -var cmdDoctorConvert = &cli.Command{ - Name: "convert", - Usage: "Convert the database", - Description: "A command to convert an existing MySQL database from utf8 to utf8mb4", - Action: runDoctorConvert, +func cmdDoctorConvert() *cli.Command { + return &cli.Command{ + Name: "convert", + Usage: "Convert the database", + Description: "A command to convert an existing MySQL database from utf8 to utf8mb4", + Action: runDoctorConvert, + } } -func runDoctorConvert(ctx *cli.Context) error { - stdCtx, cancel := installSignals() +func runDoctorConvert(stdCtx context.Context, ctx *cli.Command) error { + stdCtx, cancel := installSignals(stdCtx) defer cancel() if err := initDB(stdCtx); err != nil { diff --git a/cmd/doctor_test.go b/cmd/doctor_test.go index e6daae18b9..c3eda8315b 100644 --- a/cmd/doctor_test.go +++ b/cmd/doctor_test.go @@ -7,11 +7,11 @@ import ( "context" "testing" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/services/doctor" + "forgejo.org/modules/log" + "forgejo.org/services/doctor" "github.com/stretchr/testify/require" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) func TestDoctorRun(t *testing.T) { @@ -22,12 +22,12 @@ func TestDoctorRun(t *testing.T) { SkipDatabaseInitialization: true, }) - app := cli.NewApp() - app.Commands = []*cli.Command{cmdDoctorCheck} - err := app.Run([]string{"./gitea", "check", "--run", "test-check"}) + app := cli.Command{} + app.Commands = []*cli.Command{cmdDoctorCheck()} + err := app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check"}) require.NoError(t, err) - err = app.Run([]string{"./gitea", "check", "--run", "no-such"}) + err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "no-such"}) require.ErrorContains(t, err, `unknown checks: "no-such"`) - err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"}) + err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check,no-such"}) require.ErrorContains(t, err, `unknown checks: "no-such"`) } diff --git a/cmd/dump.go b/cmd/dump.go index 14f9a00b58..cb01e74196 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -5,6 +5,8 @@ package cmd import ( + "context" + "errors" "fmt" "io" "os" @@ -13,16 +15,16 @@ import ( "strings" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/storage" + "forgejo.org/modules/util" "code.forgejo.org/go-chi/session" "github.com/mholt/archiver/v3" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error { @@ -83,6 +85,10 @@ func (o *outputType) Set(value string) error { return fmt.Errorf("allowed values are %s", o.Join()) } +func (o *outputType) Get() any { + return o.String() +} + func (o outputType) String() string { if o.selected == "" { return o.Default @@ -96,80 +102,81 @@ var outputTypeEnum = &outputType{ } // CmdDump represents the available dump sub-command. -var CmdDump = &cli.Command{ - Name: "dump", - Usage: "Dump Forgejo files and database", - Description: `Dump compresses all related files and database into zip file. +func cmdDump() *cli.Command { + return &cli.Command{ + Name: "dump", + Usage: "Dump Forgejo files and database", + Description: `Dump compresses all related files and database into zip file. It can be used for backup and capture Forgejo server image to send to maintainer`, - Action: runDump, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "file", - Aliases: []string{"f"}, - Value: fmt.Sprintf("forgejo-dump-%d.zip", time.Now().Unix()), - Usage: "Name of the dump file which will be created. Supply '-' for stdout. See type for available types.", + Action: runDump, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "file", + Aliases: []string{"f"}, + Value: fmt.Sprintf("forgejo-dump-%d.zip", time.Now().Unix()), + Usage: "Name of the dump file which will be created. 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"}, + Usage: "Temporary dir path", + }, + &cli.StringFlag{ + Name: "database", + Aliases: []string{"d"}, + Usage: "Specify the database SQL syntax: sqlite3, mysql, postgres", + }, + &cli.BoolFlag{ + Name: "skip-repository", + Aliases: []string{"R"}, + Usage: "Skip repositories", + }, + &cli.BoolFlag{ + Name: "skip-log", + Aliases: []string{"L"}, + Usage: "Skip logs", + }, + &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-repo-archives", + Usage: "Skip repository archives", + }, + &cli.GenericFlag{ + Name: "type", + Value: outputTypeEnum, + Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()), + }, }, - &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, postgres", - }, - &cli.BoolFlag{ - Name: "skip-repository", - Aliases: []string{"R"}, - Usage: "Skip repositories", - }, - &cli.BoolFlag{ - Name: "skip-log", - Aliases: []string{"L"}, - Usage: "Skip logs", - }, - &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-repo-archives", - Usage: "Skip repository archives", - }, - &cli.GenericFlag{ - Name: "type", - Value: outputTypeEnum, - Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()), - }, - }, + } } func fatal(format string, args ...any) { @@ -177,7 +184,7 @@ func fatal(format string, args ...any) { log.Fatal(format, args...) } -func runDump(ctx *cli.Context) error { +func runDump(stdCtx context.Context, ctx *cli.Command) error { var file *os.File fileName := ctx.String("file") outType := ctx.String("type") @@ -213,16 +220,16 @@ func runDump(ctx *cli.Context) error { if !setting.InstallLock { log.Error("Is '%s' really the right config path?\n", setting.CustomConf) - return fmt.Errorf("forgejo is not initialized") + return errors.New("forgejo is not initialized") } setting.LoadSettings() // cannot access session settings otherwise verbose := ctx.Bool("verbose") if verbose && ctx.Bool("quiet") { - return fmt.Errorf("--quiet and --verbose cannot both be set") + return errors.New("--quiet and --verbose cannot both be set") } - stdCtx, cancel := installSignals() + stdCtx, cancel := installSignals(stdCtx) defer cancel() err := db.InitEngine(stdCtx) @@ -288,18 +295,31 @@ func runDump(ctx *cli.Context) error { } tmpDir := ctx.String("tempdir") + if tmpDir == "" { + tmpDir, err = os.MkdirTemp("", "forgejo-dump-*") + if err != nil { + fatal("Failed to create temporary directory: %v", err) + } + + defer func() { + if err := util.Remove(tmpDir); err != nil { + log.Warn("Failed to remove temporary directory: %s: Error: %v", tmpDir, err) + } + }() + } + if _, err := os.Stat(tmpDir); os.IsNotExist(err) { fatal("Path does not exist: %s", tmpDir) } dbDump, err := os.CreateTemp(tmpDir, "forgejo-db.sql") if err != nil { - fatal("Failed to create tmp file: %v", err) + fatal("Failed to create temporary file: %v", err) } defer func() { _ = dbDump.Close() if err := util.Remove(dbDump.Name()); err != nil { - log.Warn("Failed to remove temporary file: %s: Error: %v", dbDump.Name(), err) + log.Warn("Failed to remove temporary database file: %s: Error: %v", dbDump.Name(), err) } }() diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go index 3a24cf6c5f..eb89273e7f 100644 --- a/cmd/dump_repo.go +++ b/cmd/dump_repo.go @@ -10,77 +10,79 @@ import ( "os" "strings" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - base "code.gitea.io/gitea/modules/migration" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/services/convert" - "code.gitea.io/gitea/services/migrations" + "forgejo.org/modules/git" + "forgejo.org/modules/log" + base "forgejo.org/modules/migration" + "forgejo.org/modules/setting" + "forgejo.org/modules/structs" + "forgejo.org/modules/util" + "forgejo.org/services/convert" + "forgejo.org/services/migrations" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // CmdDumpRepository represents the available dump repository sub-command. -var CmdDumpRepository = &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. +func cmdDumpRepository() *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 *cli.Context) error { - stdCtx, cancel := installSignals() +func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error { + stdCtx, cancel := installSignals(stdCtx) defer cancel() if err := initDB(stdCtx); err != nil { diff --git a/cmd/embedded.go b/cmd/embedded.go index 9f03f7be7c..8fa76ccef1 100644 --- a/cmd/embedded.go +++ b/cmd/embedded.go @@ -4,38 +4,41 @@ package cmd import ( + "context" "errors" "fmt" "os" "path/filepath" "strings" - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/options" - "code.gitea.io/gitea/modules/public" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/templates" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/log" + "forgejo.org/modules/options" + "forgejo.org/modules/public" + "forgejo.org/modules/setting" + "forgejo.org/modules/templates" + "forgejo.org/modules/util" "github.com/gobwas/glob" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // CmdEmbedded represents the available extract sub-command. -var ( - CmdEmbedded = &cli.Command{ +func cmdEmbedded() *cli.Command { + return &cli.Command{ Name: "embedded", Usage: "Extract embedded resources", Description: "A command for extracting embedded resources, like templates and images", - Subcommands: []*cli.Command{ - subcmdList, - subcmdView, - subcmdExtract, + Commands: []*cli.Command{ + subcmdList(), + subcmdView(), + subcmdExtract(), }, } +} - subcmdList = &cli.Command{ +func subcmdList() *cli.Command { + return &cli.Command{ Name: "list", Usage: "List files matching the given pattern", Action: runList, @@ -47,8 +50,10 @@ var ( }, }, } +} - subcmdView = &cli.Command{ +func subcmdView() *cli.Command { + return &cli.Command{ Name: "view", Usage: "View a file matching the given pattern", Action: runView, @@ -60,8 +65,10 @@ var ( }, }, } +} - subcmdExtract = &cli.Command{ +func subcmdExtract() *cli.Command { + return &cli.Command{ Name: "extract", Usage: "Extract resources", Action: runExtract, @@ -90,9 +97,9 @@ var ( }, }, } +} - matchedAssetFiles []assetFile -) +var matchedAssetFiles []assetFile type assetFile struct { fs *assetfs.LayeredFS @@ -100,7 +107,7 @@ type assetFile struct { path string } -func initEmbeddedExtractor(c *cli.Context) error { +func initEmbeddedExtractor(_ context.Context, c *cli.Command) error { setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr) patterns, err := compileCollectPatterns(c.Args().Slice()) @@ -115,32 +122,32 @@ func initEmbeddedExtractor(c *cli.Context) error { return nil } -func runList(c *cli.Context) error { - if err := runListDo(c); err != nil { +func runList(ctx context.Context, c *cli.Command) error { + if err := runListDo(ctx, c); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return err } return nil } -func runView(c *cli.Context) error { - if err := runViewDo(c); err != nil { +func runView(ctx context.Context, c *cli.Command) error { + if err := runViewDo(ctx, c); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return err } return nil } -func runExtract(c *cli.Context) error { - if err := runExtractDo(c); err != nil { +func runExtract(ctx context.Context, c *cli.Command) error { + if err := runExtractDo(ctx, c); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return err } return nil } -func runListDo(c *cli.Context) error { - if err := initEmbeddedExtractor(c); err != nil { +func runListDo(ctx context.Context, c *cli.Command) error { + if err := initEmbeddedExtractor(ctx, c); err != nil { return err } @@ -151,8 +158,8 @@ func runListDo(c *cli.Context) error { return nil } -func runViewDo(c *cli.Context) error { - if err := initEmbeddedExtractor(c); err != nil { +func runViewDo(ctx context.Context, c *cli.Command) error { + if err := initEmbeddedExtractor(ctx, c); err != nil { return err } @@ -174,8 +181,8 @@ func runViewDo(c *cli.Context) error { return nil } -func runExtractDo(c *cli.Context) error { - if err := initEmbeddedExtractor(c); err != nil { +func runExtractDo(ctx context.Context, c *cli.Command) error { + if err := initEmbeddedExtractor(ctx, c); err != nil { return err } @@ -271,7 +278,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error { return nil } -func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string, layer *assetfs.Layer) { +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 { diff --git a/cmd/forgejo/actions.go b/cmd/forgejo/actions.go index 1560b10fac..c445d1aa38 100644 --- a/cmd/forgejo/actions.go +++ b/cmd/forgejo/actions.go @@ -6,24 +6,25 @@ package forgejo import ( "context" "encoding/hex" + "errors" "fmt" "io" "os" "strings" - actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/modules/private" - "code.gitea.io/gitea/modules/setting" - private_routers "code.gitea.io/gitea/routers/private" + actions_model "forgejo.org/models/actions" + "forgejo.org/modules/private" + "forgejo.org/modules/setting" + private_routers "forgejo.org/routers/private" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) func CmdActions(ctx context.Context) *cli.Command { return &cli.Command{ Name: "actions", Usage: "Commands for managing Forgejo Actions", - Subcommands: []*cli.Command{ + Commands: []*cli.Command{ SubcmdActionsGenerateRunnerToken(ctx), SubcmdActionsGenerateRunnerSecret(ctx), SubcmdActionsRegister(ctx), @@ -36,7 +37,7 @@ func SubcmdActionsGenerateRunnerToken(ctx context.Context) *cli.Command { Name: "generate-runner-token", Usage: "Generate a new token for a runner to use to register with the server", Before: prepareWorkPathAndCustomConf(ctx), - Action: func(cliCtx *cli.Context) error { return RunGenerateActionsRunnerToken(ctx, cliCtx) }, + Action: RunGenerateActionsRunnerToken, Flags: []cli.Flag{ &cli.StringFlag{ Name: "scope", @@ -52,7 +53,7 @@ func SubcmdActionsGenerateRunnerSecret(ctx context.Context) *cli.Command { return &cli.Command{ Name: "generate-secret", Usage: "Generate a secret suitable for input to the register subcommand", - Action: func(cliCtx *cli.Context) error { return RunGenerateSecret(ctx, cliCtx) }, + Action: RunGenerateSecret, } } @@ -61,7 +62,7 @@ func SubcmdActionsRegister(ctx context.Context) *cli.Command { Name: "register", Usage: "Idempotent registration of a runner using a shared secret", Before: prepareWorkPathAndCustomConf(ctx), - Action: func(cliCtx *cli.Context) error { return RunRegister(ctx, cliCtx) }, + Action: RunRegister, Flags: []cli.Flag{ &cli.StringFlag{ Name: "secret", @@ -105,26 +106,26 @@ func SubcmdActionsRegister(ctx context.Context) *cli.Command { } } -func readSecret(ctx context.Context, cliCtx *cli.Context) (string, error) { - if cliCtx.IsSet("secret") { - return cliCtx.String("secret"), nil +func readSecret(ctx context.Context, cli *cli.Command) (string, error) { + if cli.IsSet("secret") { + return cli.String("secret"), nil } - if cliCtx.IsSet("secret-stdin") { + if cli.IsSet("secret-stdin") { buf, err := io.ReadAll(ContextGetStdin(ctx)) if err != nil { return "", err } return string(buf), nil } - if cliCtx.IsSet("secret-file") { - path := cliCtx.String("secret-file") + if cli.IsSet("secret-file") { + path := cli.String("secret-file") buf, err := os.ReadFile(path) if err != nil { return "", err } return string(buf), nil } - return "", fmt.Errorf("at least one of the --secret, --secret-stdin, --secret-file options is required") + return "", errors.New("at least one of the --secret, --secret-stdin, --secret-file options is required") } func validateSecret(secret string) error { @@ -138,18 +139,18 @@ func validateSecret(secret string) error { return nil } -func getLabels(cliCtx *cli.Context) (*[]string, error) { - if !cliCtx.Bool("keep-labels") { - lblValue := strings.Split(cliCtx.String("labels"), ",") +func getLabels(cli *cli.Command) (*[]string, error) { + if !cli.Bool("keep-labels") { + lblValue := strings.Split(cli.String("labels"), ",") return &lblValue, nil } - if cliCtx.String("labels") != "" { - return nil, fmt.Errorf("--labels and --keep-labels should not be used together") + if cli.String("labels") != "" { + return nil, errors.New("--labels and --keep-labels should not be used together") } return nil, nil } -func RunRegister(ctx context.Context, cliCtx *cli.Context) error { +func RunRegister(ctx context.Context, cli *cli.Command) error { var cancel context.CancelFunc if !ContextGetNoInit(ctx) { ctx, cancel = installSignals(ctx) @@ -161,17 +162,17 @@ func RunRegister(ctx context.Context, cliCtx *cli.Context) error { } setting.MustInstalled() - secret, err := readSecret(ctx, cliCtx) + secret, err := readSecret(ctx, cli) if err != nil { return err } if err := validateSecret(secret); err != nil { return err } - scope := cliCtx.String("scope") - name := cliCtx.String("name") - version := cliCtx.String("version") - labels, err := getLabels(cliCtx) + scope := cli.String("scope") + name := cli.String("name") + version := cli.String("version") + labels, err := getLabels(cli) if err != nil { return err } @@ -209,7 +210,7 @@ func RunRegister(ctx context.Context, cliCtx *cli.Context) error { return nil } -func RunGenerateSecret(ctx context.Context, cliCtx *cli.Context) error { +func RunGenerateSecret(ctx context.Context, cli *cli.Command) error { runner := actions_model.ActionRunner{} if err := runner.GenerateToken(); err != nil { return err @@ -220,7 +221,7 @@ func RunGenerateSecret(ctx context.Context, cliCtx *cli.Context) error { return nil } -func RunGenerateActionsRunnerToken(ctx context.Context, cliCtx *cli.Context) error { +func RunGenerateActionsRunnerToken(ctx context.Context, cli *cli.Command) error { if !ContextGetNoInit(ctx) { var cancel context.CancelFunc ctx, cancel = installSignals(ctx) @@ -229,7 +230,7 @@ func RunGenerateActionsRunnerToken(ctx context.Context, cliCtx *cli.Context) err setting.MustInstalled() - scope := cliCtx.String("scope") + scope := cli.String("scope") respText, extra := private.GenerateActionsRunnerToken(ctx, scope) if extra.HasError() { diff --git a/cmd/forgejo/actions_test.go b/cmd/forgejo/actions_test.go index 897af98315..11315239f7 100644 --- a/cmd/forgejo/actions_test.go +++ b/cmd/forgejo/actions_test.go @@ -4,14 +4,13 @@ package forgejo import ( + "context" "fmt" "testing" - "code.gitea.io/gitea/services/context" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) func TestActions_getLabels(t *testing.T) { @@ -54,21 +53,21 @@ func TestActions_getLabels(t *testing.T) { }, } - flags := SubcmdActionsRegister(context.Context{}).Flags + flags := SubcmdActionsRegister(t.Context()).Flags for _, c := range cases { t.Run(fmt.Sprintf("args: %v", c.args), func(t *testing.T) { // Create a copy of command to test var result *resultType - app := cli.NewApp() + app := cli.Command{} app.Flags = flags - app.Action = func(ctx *cli.Context) error { + app.Action = func(_ context.Context, ctx *cli.Command) error { labels, err := getLabels(ctx) result = &resultType{labels, err} return nil } // Run it - _ = app.Run(c.args) + _ = app.Run(t.Context(), c.args) // Test the results require.NotNil(t, result) diff --git a/cmd/forgejo/f3.go b/cmd/forgejo/f3.go index 5a0d0ac036..c4aafeac58 100644 --- a/cmd/forgejo/f3.go +++ b/cmd/forgejo/f3.go @@ -8,19 +8,19 @@ import ( "context" "errors" - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/services/f3/util" + "forgejo.org/models" + "forgejo.org/modules/git" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/storage" + "forgejo.org/services/f3/util" - _ "code.gitea.io/gitea/services/f3/driver" // register the driver + _ "forgejo.org/services/f3/driver" // register the driver f3_cmd "code.forgejo.org/f3/gof3/v3/cmd" f3_logger "code.forgejo.org/f3/gof3/v3/logger" f3_util "code.forgejo.org/f3/gof3/v3/util" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) func CmdF3(ctx context.Context) *cli.Command { @@ -28,21 +28,21 @@ func CmdF3(ctx context.Context) *cli.Command { return &cli.Command{ Name: "f3", Usage: "F3", - Subcommands: []*cli.Command{ + Commands: []*cli.Command{ SubcmdF3Mirror(ctx), }, } } func SubcmdF3Mirror(ctx context.Context) *cli.Command { - mirrorCmd := f3_cmd.CreateCmdMirror(ctx) + mirrorCmd := f3_cmd.CreateCmdMirror() mirrorCmd.Before = prepareWorkPathAndCustomConf(ctx) f3Action := mirrorCmd.Action - mirrorCmd.Action = func(c *cli.Context) error { return runMirror(ctx, c, f3Action) } + mirrorCmd.Action = func(ctx context.Context, cli *cli.Command) error { return runMirror(ctx, cli, f3Action) } return mirrorCmd } -func runMirror(ctx context.Context, c *cli.Context, action cli.ActionFunc) error { +func runMirror(ctx context.Context, c *cli.Command, action cli.ActionFunc) error { setting.LoadF3Setting() if !setting.F3.Enabled { return errors.New("F3 is disabled, it is not ready to be used and is only present for development purposes") @@ -69,7 +69,7 @@ func runMirror(ctx context.Context, c *cli.Context, action cli.ActionFunc) error } } - err := action(c) + err := action(ctx, c) if panicError, ok := err.(f3_util.PanicError); ok { log.Debug("F3 Stack trace\n%s", panicError.Stack()) } diff --git a/cmd/forgejo/forgejo.go b/cmd/forgejo/forgejo.go index 1b7e16ca8f..171ef1a71d 100644 --- a/cmd/forgejo/forgejo.go +++ b/cmd/forgejo/forgejo.go @@ -11,12 +11,12 @@ import ( "os/signal" "syscall" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/private" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/private" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) type key int @@ -34,7 +34,7 @@ func CmdForgejo(ctx context.Context) *cli.Command { Name: "forgejo-cli", Usage: "Forgejo CLI", Flags: []cli.Flag{}, - Subcommands: []*cli.Command{ + Commands: []*cli.Command{ CmdActions(ctx), CmdF3(ctx), }, @@ -147,12 +147,12 @@ func handleCliResponseExtra(ctx context.Context, extra private.ResponseExtra) er return cli.Exit(extra.Error, 1) } -func prepareWorkPathAndCustomConf(ctx context.Context) func(c *cli.Context) error { - return func(c *cli.Context) error { +func prepareWorkPathAndCustomConf(ctx context.Context) func(ctx context.Context, cli *cli.Command) (context.Context, error) { + return func(ctx context.Context, cli *cli.Command) (context.Context, error) { if !ContextGetNoInit(ctx) { var args setting.ArgWorkPathAndCustomConf // from children to parent, check the global flags - for _, curCtx := range c.Lineage() { + for _, curCtx := range cli.Lineage() { if curCtx.IsSet("work-path") && args.WorkPath == "" { args.WorkPath = curCtx.String("work-path") } @@ -165,6 +165,6 @@ func prepareWorkPathAndCustomConf(ctx context.Context) func(c *cli.Context) erro } setting.InitWorkPathAndCommonConfig(os.Getenv, args) } - return nil + return ctx, nil } } diff --git a/cmd/generate.go b/cmd/generate.go index 806946244b..7076ae541f 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -5,56 +5,65 @@ package cmd import ( + "context" "fmt" "os" - "code.gitea.io/gitea/modules/generate" + "forgejo.org/modules/generate" "github.com/mattn/go-isatty" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var ( - // CmdGenerate represents the available generate sub-command. - CmdGenerate = &cli.Command{ +// CmdGenerate represents the available generate sub-command. +func cmdGenerate() *cli.Command { + return &cli.Command{ Name: "generate", Usage: "Generate Gitea's secrets/keys/tokens", - Subcommands: []*cli.Command{ - subcmdSecret, + Commands: []*cli.Command{ + subcmdSecret(), }, } +} - subcmdSecret = &cli.Command{ +func subcmdSecret() *cli.Command { + return &cli.Command{ Name: "secret", Usage: "Generate a secret token", - Subcommands: []*cli.Command{ - microcmdGenerateInternalToken, - microcmdGenerateLfsJwtSecret, - microcmdGenerateSecretKey, + Commands: []*cli.Command{ + microcmdGenerateInternalToken(), + microcmdGenerateLfsJwtSecret(), + microcmdGenerateSecretKey(), }, } +} - microcmdGenerateInternalToken = &cli.Command{ +func microcmdGenerateInternalToken() *cli.Command { + return &cli.Command{ Name: "INTERNAL_TOKEN", Usage: "Generate a new INTERNAL_TOKEN", Action: runGenerateInternalToken, } +} - microcmdGenerateLfsJwtSecret = &cli.Command{ +func microcmdGenerateLfsJwtSecret() *cli.Command { + return &cli.Command{ Name: "JWT_SECRET", Aliases: []string{"LFS_JWT_SECRET"}, Usage: "Generate a new JWT_SECRET", Action: runGenerateLfsJwtSecret, } +} - microcmdGenerateSecretKey = &cli.Command{ +func microcmdGenerateSecretKey() *cli.Command { + return &cli.Command{ Name: "SECRET_KEY", Usage: "Generate a new SECRET_KEY", Action: runGenerateSecretKey, } -) +} -func runGenerateInternalToken(c *cli.Context) error { +func runGenerateInternalToken(ctx context.Context, c *cli.Command) error { internalToken, err := generate.NewInternalToken() if err != nil { return err @@ -63,28 +72,25 @@ func runGenerateInternalToken(c *cli.Context) error { fmt.Printf("%s", internalToken) if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Printf("\n") + fmt.Println() } return nil } -func runGenerateLfsJwtSecret(c *cli.Context) error { - _, jwtSecretBase64, err := generate.NewJwtSecret() - if err != nil { - return err - } +func runGenerateLfsJwtSecret(ctx context.Context, c *cli.Command) error { + _, jwtSecretBase64 := generate.NewJwtSecret() fmt.Printf("%s", jwtSecretBase64) if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Printf("\n") + fmt.Print("\n") } return nil } -func runGenerateSecretKey(c *cli.Context) error { +func runGenerateSecretKey(ctx context.Context, c *cli.Command) error { secretKey, err := generate.NewSecretKey() if err != nil { return err @@ -93,7 +99,7 @@ func runGenerateSecretKey(c *cli.Context) error { fmt.Printf("%s", secretKey) if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Printf("\n") + fmt.Print("\n") } return nil diff --git a/cmd/hook.go b/cmd/hook.go index 93dfd9d648..909cdfdf84 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -14,36 +14,38 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/git/pushoptions" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/private" - repo_module "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/git" + "forgejo.org/modules/git/pushoptions" + "forgejo.org/modules/log" + "forgejo.org/modules/private" + repo_module "forgejo.org/modules/repository" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) const ( hookBatchSize = 30 ) -var ( - // CmdHook represents the available hooks sub-command. - CmdHook = &cli.Command{ +// CmdHook represents the available hooks sub-command. +func cmdHook() *cli.Command { + return &cli.Command{ Name: "hook", Usage: "(internal) Should only be called by Git", Description: "Delegate commands to corresponding Git hooks", Before: PrepareConsoleLoggerLevel(log.FATAL), - Subcommands: []*cli.Command{ - subcmdHookPreReceive, - subcmdHookUpdate, - subcmdHookPostReceive, - subcmdHookProcReceive, + Commands: []*cli.Command{ + subcmdHookPreReceive(), + subcmdHookUpdate(), + subcmdHookPostReceive(), + subcmdHookProcReceive(), }, } +} - subcmdHookPreReceive = &cli.Command{ +func subcmdHookPreReceive() *cli.Command { + return &cli.Command{ Name: "pre-receive", Usage: "Delegate pre-receive Git hook", Description: "This command should only be called by Git", @@ -54,7 +56,10 @@ var ( }, }, } - subcmdHookUpdate = &cli.Command{ +} + +func subcmdHookUpdate() *cli.Command { + return &cli.Command{ Name: "update", Usage: "Delegate update Git hook", Description: "This command should only be called by Git", @@ -65,7 +70,10 @@ var ( }, }, } - subcmdHookPostReceive = &cli.Command{ +} + +func subcmdHookPostReceive() *cli.Command { + return &cli.Command{ Name: "post-receive", Usage: "Delegate post-receive Git hook", Description: "This command should only be called by Git", @@ -76,8 +84,11 @@ var ( }, }, } - // Note: new hook since git 2.29 - subcmdHookProcReceive = &cli.Command{ +} + +// Note: new hook since git 2.29 +func subcmdHookProcReceive() *cli.Command { + return &cli.Command{ Name: "proc-receive", Usage: "Delegate proc-receive Git hook", Description: "This command should only be called by Git", @@ -88,7 +99,7 @@ var ( }, }, } -) +} type delayWriter struct { internal io.Writer @@ -161,14 +172,14 @@ func (n *nilWriter) WriteString(s string) (int, error) { return len(s), nil } -func runHookPreReceive(c *cli.Context) error { +func runHookPreReceive(ctx context.Context, c *cli.Command) error { if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal { return nil } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { if setting.OnlyAllowPushIfGiteaEnvironmentSet { @@ -220,10 +231,7 @@ Forgejo or set your environment appropriately.`, "") } } - supportProcReceive := false - if git.CheckGitVersionAtLeast("2.29") == nil { - supportProcReceive = true - } + supportProcReceive := git.CheckGitVersionAtLeast("2.29") == nil for scanner.Scan() { // TODO: support news feeds for wiki @@ -250,7 +258,7 @@ Forgejo or set your environment appropriately.`, "") newCommitIDs[count] = newCommitID refFullNames[count] = refFullName count++ - fmt.Fprintf(out, "*") + fmt.Fprint(out, "*") if count >= hookBatchSize { fmt.Fprintf(out, " Checking %d references\n", count) @@ -266,10 +274,10 @@ Forgejo or set your environment appropriately.`, "") lastline = 0 } } else { - fmt.Fprintf(out, ".") + fmt.Fprint(out, ".") } if lastline >= hookBatchSize { - fmt.Fprintf(out, "\n") + fmt.Fprint(out, "\n") lastline = 0 } } @@ -286,7 +294,7 @@ Forgejo or set your environment appropriately.`, "") return fail(ctx, extra.UserMsg, "HookPreReceive(last) failed: %v", extra.Error) } } else if lastline > 0 { - fmt.Fprintf(out, "\n") + fmt.Fprint(out, "\n") } fmt.Fprintf(out, "Checked %d references in total\n", total) @@ -294,13 +302,13 @@ Forgejo or set your environment appropriately.`, "") } // runHookUpdate process the update hook: https://git-scm.com/docs/githooks#update -func runHookUpdate(c *cli.Context) error { +func runHookUpdate(ctx context.Context, c *cli.Command) error { // Now if we're an internal don't do anything else if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal { return nil } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() if c.NArg() != 3 { @@ -326,11 +334,11 @@ func runHookUpdate(c *cli.Context) error { return fail(ctx, fmt.Sprintf("The modification of %s is skipped as it's an internal reference.", refFullName), "") } -func runHookPostReceive(c *cli.Context) error { - ctx, cancel := installSignals() +func runHookPostReceive(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) // First of all run update-server-info no matter what if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil { @@ -402,7 +410,7 @@ Forgejo or set your environment appropriately.`, "") continue } - fmt.Fprintf(out, ".") + fmt.Fprint(out, ".") oldCommitIDs[count] = string(fields[0]) newCommitIDs[count] = string(fields[1]) refFullNames[count] = git.RefName(fields[2]) @@ -490,11 +498,11 @@ func hookPrintResults(results []private.HookPostReceiveBranchResult) { } } -func runHookProcReceive(c *cli.Context) error { - ctx, cancel := installSignals() +func runHookProcReceive(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { if setting.OnlyAllowPushIfGiteaEnvironmentSet { diff --git a/cmd/hook_test.go b/cmd/hook_test.go index 4fa249a316..82ed392fb8 100644 --- a/cmd/hook_test.go +++ b/cmd/hook_test.go @@ -14,12 +14,12 @@ import ( "testing" "time" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // Capture what's being written into a standard file descriptor. @@ -134,14 +134,14 @@ func TestDelayWriter(t *testing.T) { defer ts.Close() defer test.MockVariableValue(&setting.LocalURL, ts.URL+"/")() - app := cli.NewApp() - app.Commands = []*cli.Command{subcmdHookPreReceive} + app := cli.Command{} + app.Commands = []*cli.Command{subcmdHookPreReceive()} t.Run("Should delay", func(t *testing.T) { defer test.MockVariableValue(&setting.Git.VerbosePushDelay, time.Millisecond*500)() finish := captureOutput(t, os.Stdout) - err = app.Run([]string{"./forgejo", "pre-receive"}) + err = app.Run(t.Context(), []string{"./forgejo", "pre-receive"}) require.NoError(t, err) out := finish() @@ -153,7 +153,7 @@ func TestDelayWriter(t *testing.T) { defer test.MockVariableValue(&setting.Git.VerbosePushDelay, time.Second*5)() finish := captureOutput(t, os.Stdout) - err = app.Run([]string{"./forgejo", "pre-receive"}) + err = app.Run(t.Context(), []string{"./forgejo", "pre-receive"}) require.NoError(t, err) out := finish() @@ -163,15 +163,15 @@ func TestDelayWriter(t *testing.T) { } func TestRunHookUpdate(t *testing.T) { - app := cli.NewApp() - app.Commands = []*cli.Command{subcmdHookUpdate} + app := cli.Command{} + app.Commands = []*cli.Command{subcmdHookUpdate()} t.Run("Removal of internal reference", func(t *testing.T) { defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() defer test.MockVariableValue(&setting.IsProd, false)() finish := captureOutput(t, os.Stderr) - err := app.Run([]string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"}) + err := app.Run(t.Context(), []string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"}) out := finish() require.Error(t, err) @@ -183,7 +183,7 @@ func TestRunHookUpdate(t *testing.T) { defer test.MockVariableValue(&setting.IsProd, false)() finish := captureOutput(t, os.Stderr) - err := app.Run([]string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000001"}) + err := app.Run(t.Context(), []string{"./forgejo", "update", "refs/pull/1/head", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000001"}) out := finish() require.Error(t, err) @@ -191,12 +191,12 @@ func TestRunHookUpdate(t *testing.T) { }) t.Run("Removal of branch", func(t *testing.T) { - err := app.Run([]string{"./forgejo", "update", "refs/head/main", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"}) + err := app.Run(t.Context(), []string{"./forgejo", "update", "refs/head/main", "0a51ae26bc73c47e2f754560c40904cf14ed51a9", "0000000000000000000000000000000000000000"}) require.NoError(t, err) }) t.Run("Not enough arguments", func(t *testing.T) { - err := app.Run([]string{"./forgejo", "update"}) + err := app.Run(t.Context(), []string{"./forgejo", "update"}) require.NoError(t, err) }) } diff --git a/cmd/keys.go b/cmd/keys.go index 81425a5722..00901903f4 100644 --- a/cmd/keys.go +++ b/cmd/keys.go @@ -4,52 +4,55 @@ package cmd import ( + "context" "errors" "fmt" "strings" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/private" + "forgejo.org/modules/log" + "forgejo.org/modules/private" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // CmdKeys represents the available keys sub-command -var CmdKeys = &cli.Command{ - Name: "keys", - Usage: "(internal) Should only be called by SSH server", - Description: "Queries the Forgejo 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", +func cmdKeys() *cli.Command { + return &cli.Command{ + Name: "keys", + Usage: "(internal) Should only be called by SSH server", + Description: "Queries the Forgejo 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)", + }, }, - &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(c *cli.Context) error { +func runKeys(ctx context.Context, c *cli.Command) error { if !c.IsSet("username") { return errors.New("No username provided") } @@ -68,16 +71,16 @@ func runKeys(c *cli.Context) error { return errors.New("No key type and content provided") } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) 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.App.Writer, strings.TrimSpace(authorizedString.Text)) + _, _ = fmt.Fprintln(c.Root().Writer, strings.TrimSpace(authorizedString.Text)) return nil } diff --git a/cmd/mailer.go b/cmd/mailer.go index 0c5f2c8c8d..d05d6c849b 100644 --- a/cmd/mailer.go +++ b/cmd/mailer.go @@ -4,16 +4,17 @@ package cmd import ( + "context" "fmt" - "code.gitea.io/gitea/modules/private" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/private" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -func runSendMail(c *cli.Context) error { - ctx, cancel := installSignals() +func runSendMail(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() setting.MustInstalled() diff --git a/cmd/main.go b/cmd/main.go index 9a28722b4b..65cde47884 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -10,11 +10,11 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/cmd/forgejo" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/cmd/forgejo" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // cmdHelp is our own help subcommand with more information @@ -25,18 +25,18 @@ func cmdHelp() *cli.Command { Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", - Action: func(c *cli.Context) (err error) { - lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil} + Action: func(ctx context.Context, c *cli.Command) (err error) { + lineage := c.Lineage() // The order is from child to parent: help, doctor, Forgejo targetCmdIdx := 0 - if c.Command.Name == "help" { + if c.Name == "help" { targetCmdIdx = 1 } - if lineage[targetCmdIdx+1].Command != nil { - err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name) + if targetCmdIdx+1 < len(lineage) { + err = cli.ShowCommandHelp(ctx, lineage[targetCmdIdx+1], lineage[targetCmdIdx].Name) } else { err = cli.ShowAppHelp(c) } - _, _ = fmt.Fprintf(c.App.Writer, ` + _, _ = fmt.Fprintf(c.Root().Writer, ` DEFAULT CONFIGURATION: AppPath: %s WorkPath: %s @@ -77,25 +77,25 @@ func appGlobalFlags() []cli.Flag { } } -func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) { - command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...) +func prepareSubcommandWithConfig(command *cli.Command, globalFlags func() []cli.Flag) { + command.Flags = append(globalFlags(), command.Flags...) command.Action = prepareWorkPathAndCustomConf(command.Action) command.HideHelp = true if command.Name != "help" { - command.Subcommands = append(command.Subcommands, cmdHelp()) + command.Commands = append(command.Commands, cmdHelp()) } - for i := range command.Subcommands { - prepareSubcommandWithConfig(command.Subcommands[i], globalFlags) + for i := range command.Commands { + prepareSubcommandWithConfig(command.Commands[i], globalFlags) } } // prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config // It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times -func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error { - return func(ctx *cli.Context) error { +func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(_ context.Context, _ *cli.Command) error { + return func(ctx context.Context, cli *cli.Command) error { var args setting.ArgWorkPathAndCustomConf // from children to parent, check the global flags - for _, curCtx := range ctx.Lineage() { + for _, curCtx := range cli.Lineage() { if curCtx.IsSet("work-path") && args.WorkPath == "" { args.WorkPath = curCtx.String("work-path") } @@ -107,24 +107,24 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) } } setting.InitWorkPathAndCommonConfig(os.Getenv, args) - if ctx.Bool("help") || action == nil { + if cli.Bool("help") || action == nil { // the default behavior of "urfave/cli": "nil action" means "show help" - return cmdHelp().Action(ctx) + return cmdHelp().Action(ctx, cli) } - return action(ctx) + return action(ctx, cli) } } -func NewMainApp(version, versionExtra string) *cli.App { +func NewMainApp(version, versionExtra string) *cli.Command { path, err := os.Executable() if err != nil { panic(err) } executable := filepath.Base(path) - var subCmdsStandalone []*cli.Command = make([]*cli.Command, 0, 10) - var subCmdWithConfig []*cli.Command = make([]*cli.Command, 0, 10) - var globalFlags []cli.Flag = make([]cli.Flag, 0, 10) + subCmdsStandalone := make([]*cli.Command, 0, 10) + subCmdWithConfig := make([]*cli.Command, 0, 10) + globalFlags := func() []cli.Flag { return []cli.Flag{} } // // If the executable is forgejo-cli, provide a Forgejo specific CLI @@ -133,14 +133,16 @@ func NewMainApp(version, versionExtra string) *cli.App { if executable == "forgejo-cli" { subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdActions(context.Background())) subCmdWithConfig = append(subCmdWithConfig, forgejo.CmdF3(context.Background())) - globalFlags = append(globalFlags, []cli.Flag{ - &cli.BoolFlag{ - Name: "quiet", - }, - &cli.BoolFlag{ - Name: "verbose", - }, - }...) + globalFlags = func() []cli.Flag { + return []cli.Flag{ + &cli.BoolFlag{ + Name: "quiet", + }, + &cli.BoolFlag{ + Name: "verbose", + }, + } + } } else { // // Otherwise provide a Gitea compatible CLI which includes Forgejo @@ -149,55 +151,54 @@ func NewMainApp(version, versionExtra string) *cli.App { // binary and rename it to forgejo if they want. // subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdForgejo(context.Background())) - subCmdWithConfig = append(subCmdWithConfig, CmdActions) + subCmdWithConfig = append(subCmdWithConfig, cmdActions()) } return innerNewMainApp(version, versionExtra, subCmdsStandalone, subCmdWithConfig, globalFlags) } -func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmdWithConfigArgs []*cli.Command, globalFlagsArgs []cli.Flag) *cli.App { - app := cli.NewApp() - app.HelpName = "forgejo" - app.Name = "Forgejo" +func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmdWithConfigArgs []*cli.Command, globalFlagsArgs func() []cli.Flag) *cli.Command { + app := &cli.Command{} + app.Name = "forgejo" app.Usage = "Beyond coding. We forge." app.Description = `By default, forgejo will start serving using the web-server with no argument, which can alternatively be run by running the subcommand "web".` app.Version = version + versionExtra - app.EnableBashCompletion = true + app.EnableShellCompletion = true // these sub-commands need to use config file subCmdWithConfig := []*cli.Command{ cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config" - CmdWeb, - CmdServ, - CmdHook, - CmdKeys, - CmdDump, - CmdAdmin, - CmdMigrate, - CmdDoctor, - CmdManager, - CmdEmbedded, - CmdMigrateStorage, - CmdDumpRepository, - CmdRestoreRepository, + cmdWeb(), + cmdServ(), + cmdHook(), + cmdKeys(), + cmdDump(), + cmdAdmin(), + cmdMigrate(), + cmdDoctor(), + cmdManager(), + cmdEmbedded(), + cmdMigrateStorage(), + cmdDumpRepository(), + cmdRestoreRepository(), } subCmdWithConfig = append(subCmdWithConfig, subCmdWithConfigArgs...) // these sub-commands do not need the config file, and they do not depend on any path or environment variable. subCmdStandalone := []*cli.Command{ - CmdCert, - CmdGenerate, - CmdDocs, + cmdCert(), + cmdGenerate(), } subCmdStandalone = append(subCmdStandalone, subCmdsStandaloneArgs...) - app.DefaultCommand = CmdWeb.Name + app.DefaultCommand = cmdWeb().Name - globalFlags := appGlobalFlags() - globalFlags = append(globalFlags, globalFlagsArgs...) + globalFlags := func() []cli.Flag { + return append(appGlobalFlags(), globalFlagsArgs()...) + } app.Flags = append(app.Flags, cli.VersionFlag) - app.Flags = append(app.Flags, globalFlags...) + app.Flags = append(app.Flags, globalFlags()...) app.HideHelp = true // use our own help action to show helps (with more information like default config) app.Before = PrepareConsoleLoggerLevel(log.INFO) for i := range subCmdWithConfig { @@ -210,8 +211,8 @@ func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmd return app } -func RunMainApp(app *cli.App, args ...string) error { - err := app.Run(args) +func RunMainApp(app *cli.Command, args ...string) error { + err := app.Run(context.Background(), args) if err == nil { return nil } @@ -220,7 +221,7 @@ func RunMainApp(app *cli.App, args ...string) error { cli.OsExiter(1) return err } - _, _ = fmt.Fprintf(app.ErrWriter, "Command error: %v\n", err) + _, _ = fmt.Fprintf(app.Root().ErrWriter, "Command error: %v\n", err) cli.OsExiter(1) return err } diff --git a/cmd/main_test.go b/cmd/main_test.go index 8a9ec14b2e..737150c62f 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -4,19 +4,21 @@ package cmd import ( + "context" + "errors" "fmt" "io" "path/filepath" "strings" "testing" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" + "forgejo.org/models/unittest" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) func TestMain(m *testing.M) { @@ -27,10 +29,10 @@ func makePathOutput(workPath, customPath, customConf string) string { return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf) } -func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App { +func newTestApp(testCmdAction func(_ context.Context, ctx *cli.Command) error) *cli.Command { app := NewMainApp("version", "version-extra") testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction} - prepareSubcommandWithConfig(testCmd, appGlobalFlags()) + prepareSubcommandWithConfig(testCmd, appGlobalFlags) app.Commands = append(app.Commands, testCmd) app.DefaultCommand = testCmd.Name return app @@ -42,7 +44,7 @@ type runResult struct { ExitCode int } -func runTestApp(app *cli.App, args ...string) (runResult, error) { +func runTestApp(app *cli.Command, args ...string) (runResult, error) { outBuf := new(strings.Builder) errBuf := new(strings.Builder) app.Writer = outBuf @@ -65,7 +67,6 @@ func TestCliCmd(t *testing.T) { defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini") cli.CommandHelpTemplate = "(command help template)" - cli.AppHelpTemplate = "(app help template)" cli.SubcommandHelpTemplate = "(subcommand help template)" cases := []struct { @@ -109,12 +110,17 @@ func TestCliCmd(t *testing.T) { }, } - app := newTestApp(func(ctx *cli.Context) error { - _, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf)) - return nil - }) for _, c := range cases { t.Run(c.cmd, func(t *testing.T) { + defer test.MockProtect(&setting.AppWorkPath)() + defer test.MockProtect(&setting.CustomPath)() + defer test.MockProtect(&setting.CustomConf)() + + app := newTestApp(func(_ context.Context, ctx *cli.Command) error { + _, _ = fmt.Fprint(ctx.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf)) + return nil + }) + for k, v := range c.env { t.Setenv(k, v) } @@ -122,37 +128,37 @@ func TestCliCmd(t *testing.T) { r, err := runTestApp(app, args...) require.NoError(t, err, c.cmd) assert.NotEmpty(t, c.exp, c.cmd) - assert.Contains(t, r.Stdout, c.exp, c.cmd) + assert.Contains(t, r.Stdout, c.exp, c.cmd+"\n"+r.Stdout) }) } } func TestCliCmdError(t *testing.T) { - app := newTestApp(func(ctx *cli.Context) error { return fmt.Errorf("normal error") }) + app := newTestApp(func(_ context.Context, ctx *cli.Command) error { return errors.New("normal error") }) r, err := runTestApp(app, "./gitea", "test-cmd") require.Error(t, err) assert.Equal(t, 1, r.ExitCode) - assert.Equal(t, "", r.Stdout) + assert.Empty(t, r.Stdout) assert.Equal(t, "Command error: normal error\n", r.Stderr) - app = newTestApp(func(ctx *cli.Context) error { return cli.Exit("exit error", 2) }) + app = newTestApp(func(_ context.Context, ctx *cli.Command) error { return cli.Exit("exit error", 2) }) r, err = runTestApp(app, "./gitea", "test-cmd") require.Error(t, err) assert.Equal(t, 2, r.ExitCode) - assert.Equal(t, "", r.Stdout) + assert.Empty(t, r.Stdout) assert.Equal(t, "exit error\n", r.Stderr) - app = newTestApp(func(ctx *cli.Context) error { return nil }) + app = newTestApp(func(_ context.Context, ctx *cli.Command) error { return nil }) r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such") require.Error(t, err) assert.Equal(t, 1, r.ExitCode) - assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stdout) - assert.Equal(t, "", r.Stderr) // the cli package's strange behavior, the error message is not in stderr .... + assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr) + assert.Empty(t, r.Stdout) - app = newTestApp(func(ctx *cli.Context) error { return nil }) + app = newTestApp(func(_ context.Context, ctx *cli.Command) error { return nil }) r, err = runTestApp(app, "./gitea", "test-cmd") require.NoError(t, err) assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called - assert.Equal(t, "", r.Stdout) - assert.Equal(t, "", r.Stderr) + assert.Empty(t, r.Stdout) + assert.Empty(t, r.Stderr) } diff --git a/cmd/manager.go b/cmd/manager.go index b74771e53d..029329b44e 100644 --- a/cmd/manager.go +++ b/cmd/manager.go @@ -4,30 +4,34 @@ package cmd import ( + "context" "os" "time" - "code.gitea.io/gitea/modules/private" + "forgejo.org/modules/private" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var ( - // CmdManager represents the manager command - CmdManager = &cli.Command{ +// CmdManager represents the manager command +func cmdManager() *cli.Command { + return &cli.Command{ Name: "manager", Usage: "Manage the running forgejo process", Description: "This is a command for managing the running forgejo process", - Subcommands: []*cli.Command{ - subcmdShutdown, - subcmdRestart, - subcmdReloadTemplates, - subcmdFlushQueues, - subcmdLogging, - subCmdProcesses, + Commands: []*cli.Command{ + subcmdShutdown(), + subcmdRestart(), + subcmdReloadTemplates(), + subcmdFlushQueues(), + subcmdLogging(), + subCmdProcesses(), }, } - subcmdShutdown = &cli.Command{ +} + +func subcmdShutdown() *cli.Command { + return &cli.Command{ Name: "shutdown", Usage: "Gracefully shutdown the running process", Flags: []cli.Flag{ @@ -37,7 +41,10 @@ var ( }, Action: runShutdown, } - subcmdRestart = &cli.Command{ +} + +func subcmdRestart() *cli.Command { + return &cli.Command{ Name: "restart", Usage: "Gracefully restart the running process - (not implemented for windows servers)", Flags: []cli.Flag{ @@ -47,7 +54,10 @@ var ( }, Action: runRestart, } - subcmdReloadTemplates = &cli.Command{ +} + +func subcmdReloadTemplates() *cli.Command { + return &cli.Command{ Name: "reload-templates", Usage: "Reload template files in the running process", Flags: []cli.Flag{ @@ -57,7 +67,10 @@ var ( }, Action: runReloadTemplates, } - subcmdFlushQueues = &cli.Command{ +} + +func subcmdFlushQueues() *cli.Command { + return &cli.Command{ Name: "flush-queues", Usage: "Flush queues in the running process", Action: runFlushQueues, @@ -76,7 +89,10 @@ var ( }, }, } - subCmdProcesses = &cli.Command{ +} + +func subCmdProcesses() *cli.Command { + return &cli.Command{ Name: "processes", Usage: "Display running processes within the current process", Action: runProcesses, @@ -106,49 +122,49 @@ var ( }, }, } -) +} -func runShutdown(c *cli.Context) error { - ctx, cancel := installSignals() +func runShutdown(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.Shutdown(ctx) return handleCliResponseExtra(extra) } -func runRestart(c *cli.Context) error { - ctx, cancel := installSignals() +func runRestart(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.Restart(ctx) return handleCliResponseExtra(extra) } -func runReloadTemplates(c *cli.Context) error { - ctx, cancel := installSignals() +func runReloadTemplates(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.ReloadTemplates(ctx) return handleCliResponseExtra(extra) } -func runFlushQueues(c *cli.Context) error { - ctx, cancel := installSignals() +func runFlushQueues(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking")) return handleCliResponseExtra(extra) } -func runProcesses(c *cli.Context) error { - ctx, cancel := installSignals() +func runProcesses(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) 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 index 6049b00d5e..c543afe872 100644 --- a/cmd/manager_logging.go +++ b/cmd/manager_logging.go @@ -4,18 +4,19 @@ package cmd import ( + "context" "errors" "fmt" "os" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/private" + "forgejo.org/modules/log" + "forgejo.org/modules/private" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var ( - defaultLoggingFlags = []cli.Flag{ +func defaultLoggingFlags() []cli.Flag { + return []cli.Flag{ &cli.StringFlag{ Name: "logger", Usage: `Logger name - will default to "default"`, @@ -56,11 +57,13 @@ var ( Name: "debug", }, } +} - subcmdLogging = &cli.Command{ +func subcmdLogging() *cli.Command { + return &cli.Command{ Name: "logging", Usage: "Adjust logging commands", - Subcommands: []*cli.Command{ + Commands: []*cli.Command{ { Name: "pause", Usage: "Pause logging (Forgejo will buffer logs up to a certain point and will drop them after that point)", @@ -104,11 +107,11 @@ var ( }, { Name: "add", Usage: "Add a logger", - Subcommands: []*cli.Command{ + Commands: []*cli.Command{ { Name: "file", Usage: "Add a file logger", - Flags: append(defaultLoggingFlags, []cli.Flag{ + Flags: append(defaultLoggingFlags(), []cli.Flag{ &cli.StringFlag{ Name: "filename", Aliases: []string{"f"}, @@ -152,7 +155,7 @@ var ( }, { Name: "conn", Usage: "Add a net conn logger", - Flags: append(defaultLoggingFlags, []cli.Flag{ + Flags: append(defaultLoggingFlags(), []cli.Flag{ &cli.BoolFlag{ Name: "reconnect-on-message", Aliases: []string{"R"}, @@ -193,13 +196,13 @@ var ( }, }, } -) +} -func runRemoveLogger(c *cli.Context) error { - ctx, cancel := installSignals() +func runRemoveLogger(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) logger := c.String("logger") if len(logger) == 0 { logger = log.DEFAULT @@ -210,11 +213,11 @@ func runRemoveLogger(c *cli.Context) error { return handleCliResponseExtra(extra) } -func runAddConnLogger(c *cli.Context) error { - ctx, cancel := installSignals() +func runAddConnLogger(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) vals := map[string]any{} mode := "conn" vals["net"] = "tcp" @@ -237,14 +240,14 @@ func runAddConnLogger(c *cli.Context) error { if c.IsSet("reconnect-on-message") { vals["reconnectOnMsg"] = c.Bool("reconnect-on-message") } - return commonAddLogger(c, mode, vals) + return commonAddLogger(ctx, c, mode, vals) } -func runAddFileLogger(c *cli.Context) error { - ctx, cancel := installSignals() +func runAddFileLogger(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) vals := map[string]any{} mode := "file" if c.IsSet("filename") { @@ -270,10 +273,10 @@ func runAddFileLogger(c *cli.Context) error { if c.IsSet("compression-level") { vals["compressionLevel"] = c.Int("compression-level") } - return commonAddLogger(c, mode, vals) + return commonAddLogger(ctx, c, mode, vals) } -func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error { +func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error { if len(c.String("level")) > 0 { vals["level"] = log.LevelFromString(c.String("level")).String() } @@ -300,47 +303,47 @@ func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error { if c.IsSet("writer") { writer = c.String("writer") } - ctx, cancel := installSignals() + ctx, cancel := installSignals(ctx) defer cancel() extra := private.AddLogger(ctx, logger, writer, mode, vals) return handleCliResponseExtra(extra) } -func runPauseLogging(c *cli.Context) error { - ctx, cancel := installSignals() +func runPauseLogging(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) userMsg := private.PauseLogging(ctx) _, _ = fmt.Fprintln(os.Stdout, userMsg) return nil } -func runResumeLogging(c *cli.Context) error { - ctx, cancel := installSignals() +func runResumeLogging(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) userMsg := private.ResumeLogging(ctx) _, _ = fmt.Fprintln(os.Stdout, userMsg) return nil } -func runReleaseReopenLogging(c *cli.Context) error { - ctx, cancel := installSignals() +func runReleaseReopenLogging(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) userMsg := private.ReleaseReopenLogging(ctx) _, _ = fmt.Fprintln(os.Stdout, userMsg) return nil } -func runSetLogSQL(c *cli.Context) error { - ctx, cancel := installSignals() +func runSetLogSQL(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), false) extra := private.SetLogSQL(ctx, !c.Bool("off")) return handleCliResponseExtra(extra) diff --git a/cmd/migrate.go b/cmd/migrate.go index 53c496a36c..5a485d17f9 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -6,24 +6,26 @@ package cmd import ( "context" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/migrations" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/db" + "forgejo.org/models/migrations" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // CmdMigrate represents the available migrate sub-command. -var CmdMigrate = &cli.Command{ - Name: "migrate", - Usage: "Migrate the database", - Description: "This is a command for migrating the database, so that you can run 'forgejo admin user create' before starting the server.", - Action: runMigrate, +func cmdMigrate() *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 'forgejo admin user create' before starting the server.", + Action: runMigrate, + } } -func runMigrate(ctx *cli.Context) error { - stdCtx, cancel := installSignals() +func runMigrate(stdCtx context.Context, ctx *cli.Command) error { + stdCtx, cancel := installSignals(stdCtx) defer cancel() if err := initDB(stdCtx); err != nil { @@ -36,7 +38,13 @@ func runMigrate(ctx *cli.Context) error { log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) - if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { + if err := db.InitEngineWithMigration(context.Background(), func(dbEngine db.Engine) error { + masterEngine, err := db.GetMasterEngine(dbEngine) + if err != nil { + return err + } + return migrations.Migrate(masterEngine) + }); err != nil { log.Fatal("Failed to initialize ORM engine: %v", err) return err } diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go index 3a69b555e0..d741a883e3 100644 --- a/cmd/migrate_storage.go +++ b/cmd/migrate_storage.go @@ -10,90 +10,93 @@ import ( "io/fs" "strings" - actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/models/db" - git_model "code.gitea.io/gitea/models/git" - "code.gitea.io/gitea/models/migrations" - packages_model "code.gitea.io/gitea/models/packages" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" - packages_module "code.gitea.io/gitea/modules/packages" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/storage" + actions_model "forgejo.org/models/actions" + "forgejo.org/models/db" + git_model "forgejo.org/models/git" + "forgejo.org/models/migrations" + packages_model "forgejo.org/models/packages" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/log" + packages_module "forgejo.org/modules/packages" + "forgejo.org/modules/setting" + "forgejo.org/modules/storage" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" + "xorm.io/xorm" ) // CmdMigrateStorage represents the available migrate storage sub-command. -var CmdMigrateStorage = &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'", +func cmdMigrateStorage() *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) or minio", + }, + &cli.StringFlag{ + Name: "path", + Aliases: []string{"p"}, + Value: "", + Usage: "New storage placement if store is local (leave blank for default)", + }, + &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: "storage", - Aliases: []string{"s"}, - Value: "", - Usage: "New storage type: local (default) or minio", - }, - &cli.StringFlag{ - Name: "path", - Aliases: []string{"p"}, - Value: "", - Usage: "New storage placement if store is local (leave blank for default)", - }, - &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)", - }, - }, + } } func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error { @@ -181,8 +184,8 @@ func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStora }) } -func runMigrateStorage(ctx *cli.Context) error { - stdCtx, cancel := installSignals() +func runMigrateStorage(stdCtx context.Context, ctx *cli.Command) error { + stdCtx, cancel := installSignals(stdCtx) defer cancel() if err := initDB(stdCtx); err != nil { @@ -195,7 +198,9 @@ func runMigrateStorage(ctx *cli.Context) error { log.Info("Log path: %s", setting.Log.RootPath) log.Info("Configuration file: %s", setting.CustomConf) - if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { + if err := db.InitEngineWithMigration(context.Background(), func(e db.Engine) error { + return migrations.Migrate(e.(*xorm.Engine)) + }); err != nil { log.Fatal("Failed to initialize ORM engine: %v", err) return err } diff --git a/cmd/migrate_storage_test.go b/cmd/migrate_storage_test.go index 245f0cacff..b75eb3cfa0 100644 --- a/cmd/migrate_storage_test.go +++ b/cmd/migrate_storage_test.go @@ -9,16 +9,16 @@ import ( "strings" "testing" - "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" - packages_module "code.gitea.io/gitea/modules/packages" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/modules/test" - packages_service "code.gitea.io/gitea/services/packages" + "forgejo.org/models/actions" + "forgejo.org/models/db" + "forgejo.org/models/packages" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + packages_module "forgejo.org/modules/packages" + "forgejo.org/modules/setting" + "forgejo.org/modules/storage" + "forgejo.org/modules/test" + packages_service "forgejo.org/services/packages" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -81,8 +81,8 @@ func TestMigratePackages(t *testing.T) { entries, err := os.ReadDir(p) require.NoError(t, err) assert.Len(t, entries, 2) - assert.EqualValues(t, "01", entries[0].Name()) - assert.EqualValues(t, "tmp", entries[1].Name()) + assert.Equal(t, "01", entries[0].Name()) + assert.Equal(t, "tmp", entries[1].Name()) } func TestMigrateActionsArtifacts(t *testing.T) { diff --git a/cmd/restore_repo.go b/cmd/restore_repo.go index 37b32aa304..0e9f0bb50a 100644 --- a/cmd/restore_repo.go +++ b/cmd/restore_repo.go @@ -4,52 +4,55 @@ package cmd import ( + "context" "strings" - "code.gitea.io/gitea/modules/private" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/private" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // CmdRestoreRepository represents the available restore a repository sub-command. -var CmdRestoreRepository = &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. +func cmdRestoreRepository() *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", + }, }, - &cli.BoolFlag{ - Name: "validation", - Usage: "Sanity check the content of the files before trying to load them", - }, - }, + } } -func runRestoreRepository(c *cli.Context) error { - ctx, cancel := installSignals() +func runRestoreRepository(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() setting.MustInstalled() diff --git a/cmd/serv.go b/cmd/serv.go index db67e36fa3..2e34f2e24c 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -18,58 +18,64 @@ import ( "time" "unicode" - 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/modules/git" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/pprof" - "code.gitea.io/gitea/modules/private" - "code.gitea.io/gitea/modules/process" - repo_module "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/services/lfs" + asymkey_model "forgejo.org/models/asymkey" + git_model "forgejo.org/models/git" + "forgejo.org/models/perm" + "forgejo.org/modules/git" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/pprof" + "forgejo.org/modules/private" + "forgejo.org/modules/process" + repo_module "forgejo.org/modules/repository" + "forgejo.org/modules/setting" + "forgejo.org/services/lfs" "github.com/golang-jwt/jwt/v5" "github.com/kballard/go-shellquote" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) const ( lfsAuthenticateVerb = "git-lfs-authenticate" + gitAnnexShellVerb = "git-annex-shell" ) // CmdServ represents the available serv sub-command. -var CmdServ = &cli.Command{ - Name: "serv", - Usage: "(internal) Should only be called by SSH shell", - Description: "Serv provides access auth for repositories", - Before: PrepareConsoleLoggerLevel(log.FATAL), - Action: runServ, - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "enable-pprof", +func cmdServ() *cli.Command { + return &cli.Command{ + Name: "serv", + Usage: "(internal) Should only be called by SSH shell", + Description: "Serv provides access auth for repositories", + Before: PrepareConsoleLoggerLevel(log.FATAL), + Action: runServ, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "enable-pprof", + }, + &cli.BoolFlag{ + Name: "debug", + }, }, - &cli.BoolFlag{ - Name: "debug", - }, - }, + } } -func setup(ctx context.Context, debug bool) { +func setup(ctx context.Context, debug, gitNeeded bool) { if debug { setupConsoleLogger(log.TRACE, false, os.Stderr) } else { setupConsoleLogger(log.FATAL, false, os.Stderr) } setting.MustInstalled() + // Sanity check to ensure path is not relative, see: https://github.com/go-gitea/gitea/pull/19317 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(context.Background()); err != nil { - _ = fail(ctx, "Failed to init git", "Failed to init git, err: %v", err) + if gitNeeded { + if err := git.InitSimple(context.Background()); err != nil { + _ = fail(ctx, "Failed to init git", "Failed to init git, err: %v", err) + } } } @@ -79,6 +85,7 @@ var ( "git-upload-archive": perm.AccessModeRead, "git-receive-pack": perm.AccessModeWrite, lfsAuthenticateVerb: perm.AccessModeNone, + gitAnnexShellVerb: perm.AccessModeRead, // annex permissions are enforced by GIT_ANNEX_SHELL_READONLY, rather than the Gitea API, but read permissions are required at a minimum } alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`) ) @@ -128,12 +135,12 @@ func handleCliResponseExtra(extra private.ResponseExtra) error { return nil } -func runServ(c *cli.Context) error { - ctx, cancel := installSignals() +func runServ(ctx context.Context, c *cli.Command) error { + ctx, cancel := installSignals(ctx) defer cancel() // FIXME: This needs to internationalised - setup(ctx, c.Bool("debug")) + setup(ctx, c.Bool("debug"), true) if setting.SSH.Disabled { fmt.Println("Forgejo: SSH has been disabled") @@ -191,7 +198,7 @@ func runServ(c *cli.Context) error { if git.CheckGitVersionAtLeast("2.29") == nil { // for AGit Flow if cmd == "ssh_info" { - fmt.Print(`{"type":"gitea","version":1}`) + fmt.Print(`{"type":"agit","version":1}`) return nil } } @@ -212,6 +219,28 @@ func runServ(c *cli.Context) error { } } + if verb == gitAnnexShellVerb { + if !setting.Annex.Enabled { + return fail(ctx, "Unknown git command", "git-annex request over SSH denied, git-annex support is disabled") + } + + if len(words) < 3 { + return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd) + } + + // git-annex always puts the repo in words[2], unlike most other + // git subcommands; and it sometimes names repos like /~/, as if + // $HOME should get expanded while also being rooted. e.g.: + // git-annex-shell 'configlist' '/~/user/repo' + // git-annex-shell 'sendkey' '/user/repo 'key' + repoPath = words[2] + repoPath = strings.TrimPrefix(repoPath, "/") + repoPath = strings.TrimPrefix(repoPath, "~/") + } + + // prevent directory traversal attacks + repoPath = filepath.Clean("/" + repoPath)[1:] + rr := strings.SplitN(repoPath, "/", 2) if len(rr) != 2 { return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath) @@ -225,6 +254,18 @@ func runServ(c *cli.Context) error { // so that username and reponame are not affected. repoPath = strings.ToLower(strings.TrimSpace(repoPath)) + // put the sanitized repoPath back into the argument list for later + if verb == gitAnnexShellVerb { + // git-annex-shell demands an absolute path + absRepoPath, err := filepath.Abs(filepath.Join(setting.RepoRootPath, repoPath)) + if err != nil { + return fail(ctx, "Error locating repoPath", "%v", err) + } + words[2] = absRepoPath + } else { + words[1] = repoPath + } + if alphaDashDotPattern.MatchString(reponame) { return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame) } @@ -253,11 +294,12 @@ func runServ(c *cli.Context) error { } if verb == lfsAuthenticateVerb { - if lfsVerb == "upload" { + switch lfsVerb { + case "upload": requestedMode = perm.AccessModeWrite - } else if lfsVerb == "download" { + case "download": requestedMode = perm.AccessModeRead - } else { + default: return fail(ctx, "Unknown LFS verb", "Unknown lfs verb %s", lfsVerb) } } @@ -303,21 +345,45 @@ func runServ(c *cli.Context) error { return nil } - var gitcmd *exec.Cmd - gitBinPath := filepath.Dir(git.GitExecutable) // e.g. /usr/bin - gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack - if _, err := os.Stat(gitBinVerb); err != nil { + gitBinVerb, err := exec.LookPath(verb) + if 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 + // ps: git-annex-shell and other extensions may not necessarily be in gitBinPath, + // but '{gitBinPath}/git annex-shell' should be able to find them on $PATH. verbFields := strings.SplitN(verb, "-", 2) if len(verbFields) == 2 { // use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ... - gitcmd = exec.CommandContext(ctx, git.GitExecutable, verbFields[1], repoPath) + gitBinVerb = git.GitExecutable + words = append([]string{verbFields[1]}, words...) } } - if gitcmd == nil { - // by default, use the verb (it has been checked above by allowedCommands) - gitcmd = exec.CommandContext(ctx, gitBinVerb, repoPath) + + // by default, use the verb (it has been checked above by allowedCommands) + gitcmd := exec.CommandContext(ctx, gitBinVerb, words[1:]...) + + if verb == gitAnnexShellVerb { + // This doesn't get its own isolated section like LFS does, because LFS + // is handled by internal Gitea routines, but git-annex has to be shelled out + // to like other git subcommands, so we need to build up gitcmd. + + // TODO: does this work on Windows? + gitcmd.Env = append(gitcmd.Env, + // "If set, disallows running git-shell to handle unknown commands." + // - git-annex-shell(1) + "GIT_ANNEX_SHELL_LIMITED=True", + // "If set, git-annex-shell will refuse to run commands + // that do not operate on the specified directory." + // - git-annex-shell(1) + fmt.Sprintf("GIT_ANNEX_SHELL_DIRECTORY=%s", words[2]), + ) + if results.UserMode < perm.AccessModeWrite { + // "If set, disallows any action that could modify the git-annex repository." + // - git-annex-shell(1) + // We set this when the backend API has told us that we don't have write permission to this repo. + log.Debug("Setting GIT_ANNEX_SHELL_READONLY=True") + gitcmd.Env = append(gitcmd.Env, "GIT_ANNEX_SHELL_READONLY=True") + } } process.SetSysProcAttribute(gitcmd) diff --git a/cmd/web.go b/cmd/web.go index 787411939c..27a70008f0 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -9,6 +9,7 @@ import ( "net" "net/http" "os" + "os/exec" "path/filepath" "strconv" "strings" @@ -16,58 +17,60 @@ import ( _ "net/http/pprof" // Used for debugging if enabled and a web server is running - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/process" - "code.gitea.io/gitea/modules/public" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/routers" - "code.gitea.io/gitea/routers/install" + "forgejo.org/modules/container" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/process" + "forgejo.org/modules/public" + "forgejo.org/modules/setting" + "forgejo.org/routers" + "forgejo.org/routers/install" "github.com/felixge/fgprof" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // PIDFile could be set from build tag var PIDFile = "/run/gitea.pid" // CmdWeb represents the available web sub-command. -var CmdWeb = &cli.Command{ - Name: "web", - Usage: "Start the Forgejo web server", - Description: `The Forgejo web server is the only thing you need to run, +func cmdWeb() *cli.Command { + return &cli.Command{ + Name: "web", + Usage: "Start the Forgejo web server", + Description: `The Forgejo 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", + 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", + }, }, - &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() { @@ -128,7 +131,7 @@ func showWebStartupMessage(msg string) { } } -func serveInstall(ctx *cli.Context) error { +func serveInstall(_ context.Context, ctx *cli.Command) error { showWebStartupMessage("Prepare to run install page") routers.InitWebInstallPage(graceful.GetManager().HammerContext()) @@ -161,7 +164,7 @@ func serveInstall(ctx *cli.Context) error { return nil } -func serveInstalled(ctx *cli.Context) error { +func serveInstalled(_ context.Context, ctx *cli.Command) error { setting.InitCfgProvider(setting.CustomConf) setting.LoadCommonSettings() setting.MustInstalled() @@ -198,9 +201,6 @@ func serveInstalled(ctx *cli.Context) error { for fn := range publicFilesSet.Seq() { 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()) @@ -236,7 +236,7 @@ func servePprof() { finished() } -func runWeb(ctx *cli.Context) error { +func runWeb(ctx context.Context, cli *cli.Command) error { defer func() { if panicked := recover(); panicked != nil { log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2)) @@ -254,12 +254,18 @@ func runWeb(ctx *cli.Context) error { } // Set pid file setting - if ctx.IsSet("pid") { - createPIDFile(ctx.String("pid")) + if cli.IsSet("pid") { + createPIDFile(cli.String("pid")) + } + + if setting.Annex.Enabled { + if _, err := exec.LookPath("git-annex"); err != nil { + log.Fatal("You have enabled git-annex support but git-annex is not installed. Please make sure that Forgejo's PATH contains the git-annex executable.") + } } if !setting.InstallLock { - if err := serveInstall(ctx); err != nil { + if err := serveInstall(ctx, cli); err != nil { return err } } else { @@ -270,7 +276,7 @@ func runWeb(ctx *cli.Context) error { go servePprof() } - return serveInstalled(ctx) + return serveInstalled(ctx, cli) } func setPort(port string) error { @@ -322,6 +328,10 @@ func listen(m http.Handler, handleRedirector bool) error { log.Info("LFS server enabled") } + if setting.Annex.Enabled { + log.Info("git-annex enabled") + } + var err error switch setting.Protocol { case setting.HTTP: diff --git a/cmd/web_acme.go b/cmd/web_acme.go index 90e4a02764..be6314addb 100644 --- a/cmd/web_acme.go +++ b/cmd/web_acme.go @@ -12,10 +12,11 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/process" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/process" + "forgejo.org/modules/proxy" + "forgejo.org/modules/setting" "github.com/caddyserver/certmagic" ) @@ -54,8 +55,8 @@ func runACME(listenAddr string, m http.Handler) error { altTLSALPNPort = p } - magic := certmagic.NewDefault() - magic.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory} + certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory} + // Try to use private CA root if provided, otherwise defaults to system's trust var certPool *x509.CertPool if setting.AcmeCARoot != "" { @@ -65,7 +66,8 @@ func runACME(listenAddr string, m http.Handler) error { log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err) } } - myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{ + + certmagic.DefaultACME = certmagic.ACMEIssuer{ CA: setting.AcmeURL, TrustedRoots: certPool, Email: setting.AcmeEmail, @@ -75,7 +77,17 @@ func runACME(listenAddr string, m http.Handler) error { ListenHost: setting.HTTPAddr, AltTLSALPNPort: altTLSALPNPort, AltHTTPPort: altHTTPPort, - }) + HTTPProxy: proxy.Proxy(), + } + + // Preserve behavior to use Let's encrypt test CA when Let's encrypt is CA. + if certmagic.DefaultACME.CA == certmagic.LetsEncryptProductionCA { + certmagic.DefaultACME.TestCA = certmagic.LetsEncryptStagingCA + } + + magic := certmagic.NewDefault() + + myACME := certmagic.NewACMEIssuer(magic, certmagic.DefaultACME) magic.Issuers = []certmagic.Issuer{myACME} diff --git a/cmd/web_graceful.go b/cmd/web_graceful.go index 996537be3b..b0145d4fc7 100644 --- a/cmd/web_graceful.go +++ b/cmd/web_graceful.go @@ -9,9 +9,9 @@ import ( "net/http/fcgi" "strings" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" ) func runHTTP(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error { diff --git a/cmd/web_https.go b/cmd/web_https.go index 70d35cd40d..bebc5f7c8f 100644 --- a/cmd/web_https.go +++ b/cmd/web_https.go @@ -9,9 +9,9 @@ import ( "os" "strings" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "github.com/klauspost/cpuid/v2" ) diff --git a/contrib/autocompletion/bash_autocomplete b/contrib/autocompletion/bash_autocomplete index 5cb62f26a7..58844938a6 100755 --- a/contrib/autocompletion/bash_autocomplete +++ b/contrib/autocompletion/bash_autocomplete @@ -1,4 +1,3 @@ -#! /bin/bash # Heavily inspired by https://github.com/urfave/cli _cli_bash_autocomplete() { @@ -7,9 +6,9 @@ _cli_bash_autocomplete() { COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" if [[ "$cur" == "-"* ]]; then - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) + opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-shell-completion ) else - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) + opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-shell-completion ) fi COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/contrib/autocompletion/zsh_autocomplete b/contrib/autocompletion/zsh_autocomplete index b3b40df503..0fd1a0b175 100644 --- a/contrib/autocompletion/zsh_autocomplete +++ b/contrib/autocompletion/zsh_autocomplete @@ -9,9 +9,9 @@ _cli_zsh_autocomplete() { local cur cur=${words[-1]} if [[ "$cur" == "-"* ]]; then - opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}") + opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-shell-completion)}") else - opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}") + opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-shell-completion)}") fi if [[ "${opts[1]}" != "" ]]; then diff --git a/contrib/backport/README b/contrib/backport/README deleted file mode 100644 index 466b79c6d4..0000000000 --- a/contrib/backport/README +++ /dev/null @@ -1,41 +0,0 @@ -`backport` -========== - -`backport` is a command to help create backports of PRs. It backports a -provided PR from main on to a released version. - -It will create a backport branch, cherry-pick the PR's merge commit, adjust -the commit message and then push this back up to your fork's remote. - -The default version will read from `docs/config.yml`. You can override this -using the option `--version`. - -The upstream branches will be fetched, using the remote `origin`. This can -be overridden using `--upstream`, and fetching can be avoided using -`--no-fetch`. - -By default the branch created will be called `backport-$PR-$VERSION`. You -can override this using the option `--backport-branch`. This branch will -be created from `--release-branch` which is `release/$(VERSION)` -by default and will be pulled from `$(UPSTREAM)`. - -The merge-commit as determined by the github API will be used as the SHA to -cherry-pick. You can override this using `--cherry-pick`. - -The commit message will be amended to add the `Backport` header. -`--no-amend-message` can be set to stop this from happening. - -If cherry-pick is successful the backported branch will be pushed up to your -fork using your remote. These will be determined using `git remote -v`. You -can set your fork name using `--fork-user` and your remote name using -`--remote`. You can avoid pushing using `--no-push`. - -If the push is successful, `xdg-open` will be called to open a backport url. -You can stop this using `--no-xdg-open`. - -Installation -============ - -```bash -go install contrib/backport/backport.go -``` diff --git a/contrib/backport/backport.go b/contrib/backport/backport.go deleted file mode 100644 index dd6b4129df..0000000000 --- a/contrib/backport/backport.go +++ /dev/null @@ -1,474 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//nolint:forbidigo -package main - -import ( - "context" - "fmt" - "log" - "net/http" - "os" - "os/exec" - "os/signal" - "path" - "strconv" - "strings" - "syscall" - - "github.com/google/go-github/v64/github" - "github.com/urfave/cli/v2" - "gopkg.in/yaml.v3" -) - -const defaultVersion = "v1.18" // to backport to - -func main() { - app := cli.NewApp() - app.Name = "backport" - app.Usage = "Backport provided PR-number on to the current or previous released version" - app.Description = `Backport will look-up the PR in Gitea's git log and attempt to cherry-pick it on the current version` - app.ArgsUsage = "" - - app.Flags = []cli.Flag{ - &cli.StringFlag{ - Name: "version", - Usage: "Version branch to backport on to", - }, - &cli.StringFlag{ - Name: "upstream", - Value: "origin", - Usage: "Upstream remote for the Gitea upstream", - }, - &cli.StringFlag{ - Name: "release-branch", - Value: "", - Usage: "Release branch to backport on. Will default to release/", - }, - &cli.StringFlag{ - Name: "cherry-pick", - Usage: "SHA to cherry-pick as backport", - }, - &cli.StringFlag{ - Name: "backport-branch", - Usage: "Backport branch to backport on to (default: backport--", - }, - &cli.StringFlag{ - Name: "remote", - Value: "", - Usage: "Remote for your fork of the Gitea upstream", - }, - &cli.StringFlag{ - Name: "fork-user", - Value: "", - Usage: "Forked user name on Github", - }, - &cli.BoolFlag{ - Name: "no-fetch", - Usage: "Set this flag to prevent fetch of remote branches", - }, - &cli.BoolFlag{ - Name: "no-amend-message", - Usage: "Set this flag to prevent automatic amendment of the commit message", - }, - &cli.BoolFlag{ - Name: "no-push", - Usage: "Set this flag to prevent pushing the backport up to your fork", - }, - &cli.BoolFlag{ - Name: "no-xdg-open", - Usage: "Set this flag to not use xdg-open to open the PR URL", - }, - &cli.BoolFlag{ - Name: "continue", - Usage: "Set this flag to continue from a git cherry-pick that has broken", - }, - } - cli.AppHelpTemplate = `NAME: - {{.Name}} - {{.Usage}} -USAGE: - {{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} - {{if len .Authors}} -AUTHOR: - {{range .Authors}}{{ . }}{{end}} - {{end}}{{if .Commands}} -OPTIONS: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}} -` - - app.Action = runBackport - - if err := app.Run(os.Args); err != nil { - fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err) - } -} - -func runBackport(c *cli.Context) error { - ctx, cancel := installSignals() - defer cancel() - - continuing := c.Bool("continue") - - var pr string - - version := c.String("version") - if version == "" && continuing { - // determine version from current branch name - var err error - pr, version, err = readCurrentBranch(ctx) - if err != nil { - return err - } - } - if version == "" { - version = readVersion() - } - if version == "" { - version = defaultVersion - } - - upstream := c.String("upstream") - if upstream == "" { - upstream = "origin" - } - - forkUser := c.String("fork-user") - remote := c.String("remote") - if remote == "" && !c.Bool("--no-push") { - var err error - remote, forkUser, err = determineRemote(ctx, forkUser) - if err != nil { - return err - } - } - - upstreamReleaseBranch := c.String("release-branch") - if upstreamReleaseBranch == "" { - upstreamReleaseBranch = path.Join("release", version) - } - - localReleaseBranch := path.Join(upstream, upstreamReleaseBranch) - - args := c.Args().Slice() - if len(args) == 0 && pr == "" { - return fmt.Errorf("no PR number provided\nProvide a PR number to backport") - } else if len(args) != 1 && pr == "" { - return fmt.Errorf("multiple PRs provided %v\nOnly a single PR can be backported at a time", args) - } - if pr == "" { - pr = args[0] - } - - backportBranch := c.String("backport-branch") - if backportBranch == "" { - backportBranch = "backport-" + pr + "-" + version - } - - fmt.Printf("* Backporting %s to %s as %s\n", pr, localReleaseBranch, backportBranch) - - sha := c.String("cherry-pick") - if sha == "" { - var err error - sha, err = determineSHAforPR(ctx, pr) - if err != nil { - return err - } - } - if sha == "" { - return fmt.Errorf("unable to determine sha for cherry-pick of %s", pr) - } - - if !c.Bool("no-fetch") { - if err := fetchRemoteAndMain(ctx, upstream, upstreamReleaseBranch); err != nil { - return err - } - } - - if !continuing { - if err := checkoutBackportBranch(ctx, backportBranch, localReleaseBranch); err != nil { - return err - } - } - - if err := cherrypick(ctx, sha); err != nil { - return err - } - - if !c.Bool("no-amend-message") { - if err := amendCommit(ctx, pr); err != nil { - return err - } - } - - if !c.Bool("no-push") { - url := "https://github.com/go-gitea/gitea/compare/" + upstreamReleaseBranch + "..." + forkUser + ":" + backportBranch - - if err := gitPushUp(ctx, remote, backportBranch); err != nil { - return err - } - - if !c.Bool("no-xdg-open") { - if err := xdgOpen(ctx, url); err != nil { - return err - } - } else { - fmt.Printf("* Navigate to %s to open PR\n", url) - } - } - return nil -} - -func xdgOpen(ctx context.Context, url string) error { - fmt.Printf("* `xdg-open %s`\n", url) - out, err := exec.CommandContext(ctx, "xdg-open", url).Output() - if err != nil { - fmt.Fprintf(os.Stderr, "%s", string(out)) - return fmt.Errorf("unable to xdg-open to %s: %w", url, err) - } - return nil -} - -func gitPushUp(ctx context.Context, remote, backportBranch string) error { - fmt.Printf("* `git push -u %s %s`\n", remote, backportBranch) - out, err := exec.CommandContext(ctx, "git", "push", "-u", remote, backportBranch).Output() - if err != nil { - fmt.Fprintf(os.Stderr, "%s", string(out)) - return fmt.Errorf("unable to push up to %s: %w", remote, err) - } - return nil -} - -func amendCommit(ctx context.Context, pr string) error { - fmt.Printf("* Amending commit to prepend `Backport #%s` to body\n", pr) - out, err := exec.CommandContext(ctx, "git", "log", "-1", "--pretty=format:%B").Output() - if err != nil { - fmt.Fprintf(os.Stderr, "%s", string(out)) - return fmt.Errorf("unable to get last log message: %w", err) - } - - parts := strings.SplitN(string(out), "\n", 2) - - if len(parts) != 2 { - return fmt.Errorf("unable to interpret log message:\n%s", string(out)) - } - subject, body := parts[0], parts[1] - if !strings.HasSuffix(subject, " (#"+pr+")") { - subject = subject + " (#" + pr + ")" - } - - out, err = exec.CommandContext(ctx, "git", "commit", "--amend", "-m", subject+"\n\nBackport #"+pr+"\n"+body).Output() - if err != nil { - fmt.Fprintf(os.Stderr, "%s", string(out)) - return fmt.Errorf("unable to amend last log message: %w", err) - } - return nil -} - -func cherrypick(ctx context.Context, sha string) error { - // Check if a CHERRY_PICK_HEAD exists - if _, err := os.Stat(".git/CHERRY_PICK_HEAD"); err == nil { - // Assume that we are in the middle of cherry-pick - continue it - fmt.Println("* Attempting git cherry-pick --continue") - out, err := exec.CommandContext(ctx, "git", "cherry-pick", "--continue").Output() - if err != nil { - fmt.Fprintf(os.Stderr, "git cherry-pick --continue failed:\n%s\n", string(out)) - return fmt.Errorf("unable to continue cherry-pick: %w", err) - } - return nil - } - - fmt.Printf("* Attempting git cherry-pick %s\n", sha) - out, err := exec.CommandContext(ctx, "git", "cherry-pick", sha).Output() - if err != nil { - fmt.Fprintf(os.Stderr, "git cherry-pick %s failed:\n%s\n", sha, string(out)) - return fmt.Errorf("git cherry-pick %s failed: %w", sha, err) - } - return nil -} - -func checkoutBackportBranch(ctx context.Context, backportBranch, releaseBranch string) error { - out, err := exec.CommandContext(ctx, "git", "branch", "--show-current").Output() - if err != nil { - return fmt.Errorf("unable to check current branch %w", err) - } - - currentBranch := strings.TrimSpace(string(out)) - fmt.Printf("* Current branch is %s\n", currentBranch) - if currentBranch == backportBranch { - fmt.Printf("* Current branch is %s - not checking out\n", currentBranch) - return nil - } - - if _, err := exec.CommandContext(ctx, "git", "rev-list", "-1", backportBranch).Output(); err == nil { - fmt.Printf("* Branch %s already exists. Checking it out...\n", backportBranch) - return exec.CommandContext(ctx, "git", "checkout", "-f", backportBranch).Run() - } - - fmt.Printf("* `git checkout -b %s %s`\n", backportBranch, releaseBranch) - return exec.CommandContext(ctx, "git", "checkout", "-b", backportBranch, releaseBranch).Run() -} - -func fetchRemoteAndMain(ctx context.Context, remote, releaseBranch string) error { - fmt.Printf("* `git fetch %s main`\n", remote) - out, err := exec.CommandContext(ctx, "git", "fetch", remote, "main").Output() - if err != nil { - fmt.Println(string(out)) - return fmt.Errorf("unable to fetch %s from %s: %w", "main", remote, err) - } - fmt.Println(string(out)) - - fmt.Printf("* `git fetch %s %s`\n", remote, releaseBranch) - out, err = exec.CommandContext(ctx, "git", "fetch", remote, releaseBranch).Output() - if err != nil { - fmt.Println(string(out)) - return fmt.Errorf("unable to fetch %s from %s: %w", releaseBranch, remote, err) - } - fmt.Println(string(out)) - - return nil -} - -func determineRemote(ctx context.Context, forkUser string) (string, string, error) { - out, err := exec.CommandContext(ctx, "git", "remote", "-v").Output() - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out)) - return "", "", fmt.Errorf("unable to determine forked remote: %w", err) - } - lines := strings.Split(string(out), "\n") - for _, line := range lines { - fields := strings.Split(line, "\t") - name, remote := fields[0], fields[1] - // only look at pushers - if !strings.HasSuffix(remote, " (push)") { - continue - } - // only look at github.com pushes - if !strings.Contains(remote, "github.com") { - continue - } - // ignore go-gitea/gitea - if strings.Contains(remote, "go-gitea/gitea") { - continue - } - if !strings.Contains(remote, forkUser) { - continue - } - if strings.HasPrefix(remote, "git@github.com:") { - forkUser = strings.TrimPrefix(remote, "git@github.com:") - } else if strings.HasPrefix(remote, "https://github.com/") { - forkUser = strings.TrimPrefix(remote, "https://github.com/") - } else if strings.HasPrefix(remote, "https://www.github.com/") { - forkUser = strings.TrimPrefix(remote, "https://www.github.com/") - } else if forkUser == "" { - return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote) - } - idx := strings.Index(forkUser, "/") - if idx >= 0 { - forkUser = forkUser[:idx] - } - return name, forkUser, nil - } - return "", "", fmt.Errorf("unable to find appropriate remote in:\n%s", string(out)) -} - -func readCurrentBranch(ctx context.Context) (pr, version string, err error) { - out, err := exec.CommandContext(ctx, "git", "branch", "--show-current").Output() - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to read current git branch:\n%s\n", string(out)) - return "", "", fmt.Errorf("unable to read current git branch: %w", err) - } - parts := strings.Split(strings.TrimSpace(string(out)), "-") - - if len(parts) != 3 || parts[0] != "backport" { - fmt.Fprintf(os.Stderr, "Unable to continue from git branch:\n%s\n", string(out)) - return "", "", fmt.Errorf("unable to continue from git branch:\n%s", string(out)) - } - - return parts[1], parts[2], nil -} - -func readVersion() string { - bs, err := os.ReadFile("docs/config.yaml") - if err != nil { - if err == os.ErrNotExist { - log.Println("`docs/config.yaml` not present") - return "" - } - fmt.Fprintf(os.Stderr, "Unable to read `docs/config.yaml`: %v\n", err) - return "" - } - - type params struct { - Version string - } - type docConfig struct { - Params params - } - dc := &docConfig{} - if err := yaml.Unmarshal(bs, dc); err != nil { - fmt.Fprintf(os.Stderr, "Unable to read `docs/config.yaml`: %v\n", err) - return "" - } - - if dc.Params.Version == "" { - fmt.Fprintf(os.Stderr, "No version in `docs/config.yaml`") - return "" - } - - version := dc.Params.Version - if version[0] != 'v' { - version = "v" + version - } - - split := strings.SplitN(version, ".", 3) - - return strings.Join(split[:2], ".") -} - -func determineSHAforPR(ctx context.Context, prStr string) (string, error) { - prNum, err := strconv.Atoi(prStr) - if err != nil { - return "", err - } - - client := github.NewClient(http.DefaultClient) - - pr, _, err := client.PullRequests.Get(ctx, "go-gitea", "gitea", prNum) - if err != nil { - return "", err - } - - if pr.Merged == nil || !*pr.Merged { - return "", fmt.Errorf("PR #%d is not yet merged - cannot determine sha to backport", prNum) - } - - if pr.MergeCommitSHA != nil { - return *pr.MergeCommitSHA, nil - } - - 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 -} diff --git a/contrib/environment-to-ini/README b/contrib/environment-to-ini/README index f1d3f2ae83..e4caf25666 100644 --- a/contrib/environment-to-ini/README +++ b/contrib/environment-to-ini/README @@ -1,45 +1,46 @@ Environment To Ini ================== -Multiple docker users have requested that the Gitea docker is changed -to permit arbitrary configuration via environment variables. +This tool allows defining Forgejo's entire configuration via environment +variables, mostly geared towards usage in Docker. -Gitea needs to use an ini file for configuration because the running -environment that starts the docker may not be the same as that used -by the hooks. An ini file also gives a good default and means that -users do not have to completely provide a full environment. +Forgejo needs to use an INI file for configuration because the running +environment that starts the container may not be the same as the one used +by the hooks. An INI file also gives a good default and means that +users do not have to provide the entire set of environment variables. With those caveats above, this command provides a generic way of converting suitably structured environment variables into any ini value. -To use the command is very simple just run it and the default gitea -app.ini will be rewritten to take account of the variables provided, -however there are various options to give slightly different -behavior and these can be interrogated with the `-h` option. +When run, `environment-to-ini` will write the config files based on the +environment variables provided. +Check with the `-h` flag for several options to alter this behaviour. -The environment variables should be of the form: +Environment variables of the form "FORGEJO__SECTION_NAME__KEY_NAME" +will be mapped to the ini section "[section_name]" and the key +"KEY_NAME" with the value as provided. - GITEA__SECTION_NAME__KEY_NAME - -Note, SECTION_NAME in the notation above is case-insensitive. +Environment variables of the form "FORGEJO__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 variables are usually restricted to a reduced character set "0-9A-Z_" - in order to allow the setting of sections with characters outside of that set, they should be escaped as following: -"_0X2E_" for "." and "_0X2D_" for "-". The entire section and key names -can be escaped as a UTF8 byte string if necessary. E.g. to configure: +"_0X2E_" for ".". The entire section and key names can be escaped as +a UTF8 byte string if necessary. E.g. to configure: - """ - ... - [log.console] - COLORIZE=false - STDERR=true - ... - """ + """ + ... + [log.console] + COLORIZE=false + STDERR=true + ... + """ -You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false" -and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found +You would set the environment variables: "FORGEJO__LOG_0x2E_CONSOLE__COLORIZE=false" +and "FORGEJO__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found on the configuration cheat sheet. To build locally, run: diff --git a/contrib/environment-to-ini/environment-to-ini.go b/contrib/environment-to-ini/environment-to-ini.go index f8593e49c3..e8e799b5f3 100644 --- a/contrib/environment-to-ini/environment-to-ini.go +++ b/contrib/environment-to-ini/environment-to-ini.go @@ -4,16 +4,17 @@ package main import ( + "context" "os" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) func main() { - app := cli.NewApp() + app := cli.Command{} app.Name = "environment-to-ini" app.Usage = "Use provided environment to update configuration ini" app.Description = `As a helper to allow docker users to update the forgejo configuration @@ -72,13 +73,13 @@ func main() { }, } app.Action = runEnvironmentToIni - err := app.Run(os.Args) + err := app.Run(context.Background(), os.Args) if err != nil { log.Fatal("Failed to run app with %s: %v", os.Args, err) } } -func runEnvironmentToIni(c *cli.Context) error { +func runEnvironmentToIni(ctx 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()...) setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{ diff --git a/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet b/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet index 31b7d4f9b2..108cab0eb1 100644 --- a/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet +++ b/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet @@ -408,7 +408,7 @@ local addIssueLabelsOverrides(labels) = regex: '', type: 'query', multi: true, - allValue: '.+' + allValue: '.+', }, ) .addTemplate( @@ -423,7 +423,7 @@ local addIssueLabelsOverrides(labels) = regex: '', type: 'query', multi: true, - allValue: '.+' + allValue: '.+', }, ) .addTemplate( diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 79793ad47f..37724610a3 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -183,7 +183,7 @@ RUN_USER = ; git ;; ;; For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, ;; for system SSH this setting has no effect -;SSH_SERVER_KEY_EXCHANGES = curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1 +;SSH_SERVER_KEY_EXCHANGES = mlkem768x25519-sha256, curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1 ;; ;; For the built-in SSH server, choose the MACs to support for SSH connections, ;; for system SSH this setting has no effect @@ -1025,6 +1025,10 @@ LEVEL = Info ;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS. ;DEFAULT_FORK_REPO_UNITS = repo.code,repo.pulls ;; +;; Comma separated list of default mirror repo units. +;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS. +;DEFAULT_MIRROR_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.wiki,repo.projects,repo.packages +;; ;; Prefix archive files by placing them in a directory named after the repository ;PREFIX_ARCHIVE_FILES = true ;; @@ -1131,9 +1135,6 @@ LEVEL = Info ;; Add co-authored-by and co-committed-by trailers if committer does not match author ;ADD_CO_COMMITTER_TRAILERS = true ;; -;; In addition to testing patches using the three-way merge method, re-test conflicting patches with git apply -;TEST_CONFLICTING_PATCHES_WITH_GIT_APPLY = false -;; ;; Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo. ;RETARGET_CHILDREN_ON_MERGE = true @@ -1163,9 +1164,13 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; +;; Signing format that Forgejo should use, openpgp uses GPG and ssh uses OpenSSH. +;FORMAT = openpgp +;; ;; GPG key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey ;; run in the context of the RUN_USER -;; Switch to none to stop signing completely +;; Switch to none to stop signing completely. +;; If `FORMAT` is set to **ssh** this should be set to an absolute path to an public OpenSSH key. ;SIGNING_KEY = default ;; ;; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer. @@ -1571,6 +1576,15 @@ LEVEL = Info ;; - manage_gpg_keys: a user cannot configure gpg keys ;;EXTERNAL_USER_DISABLE_FEATURES = +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;[moderation] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; When true enables moderation capabilities; default is false. +;; If enabled it will be possible for users to report abusive content (new actions are added in the UI and /report_abuse route will be enabled) and a new Moderation section will be added to Admin settings where the reports can be reviewed. +;ENABLED = false + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[openid] @@ -2408,7 +2422,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The first locale will be used as the default if user browser's language doesn't match any locale in the list. ;LANGS = en-US,zh-CN,zh-HK,zh-TW,da,de-DE,nds,fr-FR,nl-NL,lv-LV,ru-RU,uk-UA,ja-JP,es-ES,pt-BR,pt-PT,pl-PL,bg,it-IT,fi-FI,fil,eo,tr-TR,cs-CZ,sl,sv-SE,ko-KR,el-GR,fa-IR,hu-HU,id-ID -;NAMES = English,简体中文,繁體中文(香港),繁體中文(台灣),Danish,Deutsch,Plattdüütsch,Français,Nederlands,Latviešu,Русский,Українська,日本語,Español,Português do Brasil,Português de Portugal,Polski,Български,Italiano,Suomi,Filipino,Esperanto,Türkçe,Čeština,Slovenščina,Svenska,한국어,Ελληνικά,فارسی,Magyar nyelv,Bahasa Indonesia +;NAMES = English,简体中文,繁體中文(香港),繁體中文(台灣),Dansk,Deutsch,Plattdüütsch,Français,Nederlands,Latviešu,Русский,Українська,日本語,Español,Português do Brasil,Português de Portugal,Polski,Български,Italiano,Suomi,Filipino,Esperanto,Türkçe,Čeština,Slovenščina,Svenska,한국어,Ελληνικά,فارسی,Magyar nyelv,Bahasa Indonesia ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2440,7 +2454,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Set the maximum number of characters in a mermaid source. (Set to -1 to disable limits) -;MERMAID_MAX_SOURCE_CHARACTERS = 5000 +;MERMAID_MAX_SOURCE_CHARACTERS = 50000 ;; Set the maximum number of lines allowed for a filepreview. (Set to -1 to disable limits; set to 0 to disable the feature) ;FILEPREVIEW_MAX_LINES = 50 @@ -2678,6 +2692,17 @@ LEVEL = Info ;; Limit the number of concurrent upload/download operations within a batch ;BATCH_OPERATION_CONCURRENCY = 8 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;[annex] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Whether git-annex is enabled; defaults to false +;ENABLED = false +;; Whether to disable p2phttp support; default is the same as repository.DISABLE_HTTP_GIT +;DISABLE_P2PHTTP = false + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; settings for packages, will override storage setting diff --git a/docker/root/etc/s6/openssh/setup b/docker/root/etc/s6/openssh/setup index dbb3bafd35..48e7d4b211 100755 --- a/docker/root/etc/s6/openssh/setup +++ b/docker/root/etc/s6/openssh/setup @@ -31,6 +31,21 @@ 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}"} \ diff --git a/eslint.config.mjs b/eslint.config.mjs index 17f461a8f4..28cfa80089 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,5 +1,5 @@ import eslintCommunityEslintPluginEslintComments from '@eslint-community/eslint-plugin-eslint-comments'; -import stylisticEslintPluginJs from '@stylistic/eslint-plugin-js'; +import stylisticEslintPlugin from '@stylistic/eslint-plugin'; import vitest from '@vitest/eslint-plugin'; import arrayFunc from 'eslint-plugin-array-func'; import eslintPluginImportX from 'eslint-plugin-import-x'; @@ -26,7 +26,7 @@ export default tseslint.config( { plugins: { '@eslint-community/eslint-comments': eslintCommunityEslintPluginEslintComments, - '@stylistic/js': stylisticEslintPluginJs, + '@stylistic': stylisticEslintPlugin, '@vitest': vitest, 'array-func': arrayFunc, 'no-jquery': noJquery, @@ -69,62 +69,62 @@ export default tseslint.config( '@eslint-community/eslint-comments/no-unused-enable': [2], '@eslint-community/eslint-comments/no-use': [0], '@eslint-community/eslint-comments/require-description': [0], - '@stylistic/js/array-bracket-newline': [0], - '@stylistic/js/array-bracket-spacing': [2, 'never'], - '@stylistic/js/array-element-newline': [0], - '@stylistic/js/arrow-parens': [2, 'always'], + '@stylistic/array-bracket-newline': [0], + '@stylistic/array-bracket-spacing': [2, 'never'], + '@stylistic/array-element-newline': [0], + '@stylistic/arrow-parens': [2, 'always'], - '@stylistic/js/arrow-spacing': [2, { + '@stylistic/arrow-spacing': [2, { before: true, after: true, }], - '@stylistic/js/block-spacing': [0], + '@stylistic/block-spacing': [0], - '@stylistic/js/brace-style': [2, '1tbs', { + '@stylistic/brace-style': [2, '1tbs', { allowSingleLine: true, }], - '@stylistic/js/comma-dangle': [2, 'always-multiline'], + '@stylistic/comma-dangle': [2, 'always-multiline'], - '@stylistic/js/comma-spacing': [2, { + '@stylistic/comma-spacing': [2, { before: false, after: true, }], - '@stylistic/js/comma-style': [2, 'last'], - '@stylistic/js/computed-property-spacing': [2, 'never'], - '@stylistic/js/dot-location': [2, 'property'], - '@stylistic/js/eol-last': [2], - '@stylistic/js/function-call-spacing': [2, 'never'], - '@stylistic/js/function-call-argument-newline': [0], - '@stylistic/js/function-paren-newline': [0], - '@stylistic/js/generator-star-spacing': [0], - '@stylistic/js/implicit-arrow-linebreak': [0], + '@stylistic/comma-style': [2, 'last'], + '@stylistic/computed-property-spacing': [2, 'never'], + '@stylistic/dot-location': [2, 'property'], + '@stylistic/eol-last': [2], + '@stylistic/function-call-spacing': [2, 'never'], + '@stylistic/function-call-argument-newline': [0], + '@stylistic/function-paren-newline': [0], + '@stylistic/generator-star-spacing': [0], + '@stylistic/implicit-arrow-linebreak': [0], - '@stylistic/js/indent': [2, 2, { + '@stylistic/indent': [2, 2, { ignoreComments: true, SwitchCase: 1, }], - '@stylistic/js/key-spacing': [2], - '@stylistic/js/keyword-spacing': [2], - '@stylistic/js/linebreak-style': [2, 'unix'], - '@stylistic/js/lines-around-comment': [0], - '@stylistic/js/lines-between-class-members': [0], - '@stylistic/js/max-len': [0], - '@stylistic/js/max-statements-per-line': [0], - '@stylistic/js/multiline-ternary': [0], - '@stylistic/js/new-parens': [2], - '@stylistic/js/newline-per-chained-call': [0], - '@stylistic/js/no-confusing-arrow': [0], - '@stylistic/js/no-extra-parens': [0], - '@stylistic/js/no-extra-semi': [2], - '@stylistic/js/no-floating-decimal': [0], - '@stylistic/js/no-mixed-operators': [0], - '@stylistic/js/no-mixed-spaces-and-tabs': [2], + '@stylistic/key-spacing': [2], + '@stylistic/keyword-spacing': [2], + '@stylistic/linebreak-style': [2, 'unix'], + '@stylistic/lines-around-comment': [0], + '@stylistic/lines-between-class-members': [0], + '@stylistic/max-len': [0], + '@stylistic/max-statements-per-line': [0], + '@stylistic/multiline-ternary': [0], + '@stylistic/new-parens': [2], + '@stylistic/newline-per-chained-call': [0], + '@stylistic/no-confusing-arrow': [0], + '@stylistic/no-extra-parens': [0], + '@stylistic/no-extra-semi': [2], + '@stylistic/no-floating-decimal': [0], + '@stylistic/no-mixed-operators': [0], + '@stylistic/no-mixed-spaces-and-tabs': [2], - '@stylistic/js/no-multi-spaces': [2, { + '@stylistic/no-multi-spaces': [2, { ignoreEOLComments: true, exceptions: { @@ -132,60 +132,60 @@ export default tseslint.config( }, }], - '@stylistic/js/no-multiple-empty-lines': [2, { + '@stylistic/no-multiple-empty-lines': [2, { max: 1, maxEOF: 0, maxBOF: 0, }], - '@stylistic/js/no-tabs': [2], - '@stylistic/js/no-trailing-spaces': [2], - '@stylistic/js/no-whitespace-before-property': [2], - '@stylistic/js/nonblock-statement-body-position': [2], - '@stylistic/js/object-curly-newline': [0], - '@stylistic/js/object-curly-spacing': [2, 'never'], - '@stylistic/js/object-property-newline': [0], - '@stylistic/js/one-var-declaration-per-line': [0], - '@stylistic/js/operator-linebreak': [2, 'after'], - '@stylistic/js/padded-blocks': [2, 'never'], - '@stylistic/js/padding-line-between-statements': [0], - '@stylistic/js/quote-props': [0], + '@stylistic/no-tabs': [2], + '@stylistic/no-trailing-spaces': [2], + '@stylistic/no-whitespace-before-property': [2], + '@stylistic/nonblock-statement-body-position': [2], + '@stylistic/object-curly-newline': [0], + '@stylistic/object-curly-spacing': [2, 'never'], + '@stylistic/object-property-newline': [0], + '@stylistic/one-var-declaration-per-line': [0], + '@stylistic/operator-linebreak': [2, 'after'], + '@stylistic/padded-blocks': [2, 'never'], + '@stylistic/padding-line-between-statements': [0], + '@stylistic/quote-props': [0], - '@stylistic/js/quotes': [2, 'single', { + '@stylistic/quotes': [2, 'single', { avoidEscape: true, allowTemplateLiterals: true, }], - '@stylistic/js/rest-spread-spacing': [2, 'never'], + '@stylistic/rest-spread-spacing': [2, 'never'], - '@stylistic/js/semi': [2, 'always', { + '@stylistic/semi': [2, 'always', { omitLastInOneLineBlock: true, }], - '@stylistic/js/semi-spacing': [2, { + '@stylistic/semi-spacing': [2, { before: false, after: true, }], - '@stylistic/js/semi-style': [2, 'last'], - '@stylistic/js/space-before-blocks': [2, 'always'], + '@stylistic/semi-style': [2, 'last'], + '@stylistic/space-before-blocks': [2, 'always'], - '@stylistic/js/space-before-function-paren': [2, { + '@stylistic/space-before-function-paren': [2, { anonymous: 'ignore', named: 'never', asyncArrow: 'always', }], - '@stylistic/js/space-in-parens': [2, 'never'], - '@stylistic/js/space-infix-ops': [2], - '@stylistic/js/space-unary-ops': [2], - '@stylistic/js/spaced-comment': [2, 'always'], - '@stylistic/js/switch-colon-spacing': [2], - '@stylistic/js/template-curly-spacing': [2, 'never'], - '@stylistic/js/template-tag-spacing': [2, 'never'], - '@stylistic/js/wrap-iife': [2, 'inside'], - '@stylistic/js/wrap-regex': [0], - '@stylistic/js/yield-star-spacing': [2, 'after'], + '@stylistic/space-in-parens': [2, 'never'], + '@stylistic/space-infix-ops': [2], + '@stylistic/space-unary-ops': [2], + '@stylistic/spaced-comment': [2, 'always'], + '@stylistic/switch-colon-spacing': [2], + '@stylistic/template-curly-spacing': [2, 'never'], + '@stylistic/template-tag-spacing': [2, 'never'], + '@stylistic/wrap-iife': [2, 'inside'], + '@stylistic/wrap-regex': [0], + '@stylistic/yield-star-spacing': [2, 'after'], 'accessor-pairs': [2], 'array-callback-return': [2, { @@ -318,8 +318,9 @@ export default tseslint.config( 'no-jquery/no-data': [0], 'no-jquery/no-deferred': [2], 'no-jquery/no-delegate': [2], + 'no-jquery/no-done-fail': [2], 'no-jquery/no-each-collection': [0], - 'no-jquery/no-each-util': [0], + 'no-jquery/no-each-util': [2], 'no-jquery/no-each': [0], 'no-jquery/no-error-shorthand': [2], 'no-jquery/no-error': [2], @@ -331,6 +332,7 @@ export default tseslint.config( 'no-jquery/no-find-collection': [0], 'no-jquery/no-find-util': [2], 'no-jquery/no-find': [0], + 'no-jquery/no-fx': [2], 'no-jquery/no-fx-interval': [2], 'no-jquery/no-global-eval': [2], 'no-jquery/no-global-selector': [0], @@ -350,7 +352,7 @@ export default tseslint.config( 'no-jquery/no-live': [2], 'no-jquery/no-load-shorthand': [2], 'no-jquery/no-load': [2], - 'no-jquery/no-map-collection': [0], + 'no-jquery/no-map-collection': [2], 'no-jquery/no-map-util': [2], 'no-jquery/no-map': [2], 'no-jquery/no-merge': [2], @@ -374,12 +376,12 @@ export default tseslint.config( 'no-jquery/no-selector-prop': [2], 'no-jquery/no-serialize': [2], 'no-jquery/no-size': [2], - 'no-jquery/no-sizzle': [0], + 'no-jquery/no-sizzle': [2], 'no-jquery/no-slide': [2], 'no-jquery/no-sub': [2], 'no-jquery/no-support': [2], 'no-jquery/no-text': [0], - 'no-jquery/no-trigger': [0], + 'no-jquery/no-trigger': [2], 'no-jquery/no-trim': [2], 'no-jquery/no-type': [2], 'no-jquery/no-unique': [2], @@ -744,7 +746,6 @@ export default tseslint.config( 'unicorn/no-array-callback-reference': [0], 'unicorn/no-array-for-each': [2], 'unicorn/no-array-method-this-argument': [2], - 'unicorn/no-array-push-push': [2], 'unicorn/no-array-reduce': [2], 'unicorn/no-await-expression-member': [0], 'unicorn/no-await-in-promise-methods': [2], @@ -757,7 +758,6 @@ export default tseslint.config( 'unicorn/no-invalid-fetch-options': [2], 'unicorn/no-invalid-remove-event-listener': [2], 'unicorn/no-keyword-prefix': [0], - 'unicorn/no-length-as-slice-end': [2], 'unicorn/no-lonely-if': [2], 'unicorn/no-magic-array-flat-depth': [0], 'unicorn/no-named-default': [2], @@ -774,8 +774,11 @@ export default tseslint.config( 'unicorn/no-thenable': [2], 'unicorn/no-this-assignment': [2], 'unicorn/no-typeof-undefined': [2], + 'unicorn/no-unnecessary-array-flat-depth': [2], + 'unicorn/no-unnecessary-array-splice-count': [2], 'unicorn/no-unnecessary-await': [2], 'unicorn/no-unnecessary-polyfills': [2], + 'unicorn/no-unnecessary-slice-end': [2], 'unicorn/no-unreadable-array-destructuring': [0], 'unicorn/no-unreadable-iife': [2], 'unicorn/no-unused-properties': [2], @@ -806,6 +809,7 @@ export default tseslint.config( 'unicorn/prefer-event-target': [2], 'unicorn/prefer-export-from': [0], 'unicorn/prefer-global-this': [0], + 'unicorn/prefer-import-meta-properties': [2], 'unicorn/prefer-includes': [2], 'unicorn/prefer-json-parse-buffer': [0], 'unicorn/prefer-keyboard-event-key': [2], @@ -828,6 +832,7 @@ export default tseslint.config( 'unicorn/prefer-regexp-test': [2], 'unicorn/prefer-set-has': [0], 'unicorn/prefer-set-size': [2], + 'unicorn/prefer-single-call': [2], 'unicorn/prefer-spread': [0], 'unicorn/prefer-string-raw': [0], 'unicorn/prefer-string-replace-all': [0], diff --git a/flake.lock b/flake.lock index 90672733d5..dcf7755013 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1733392399, - "narHash": "sha256-kEsTJTUQfQFIJOcLYFt/RvNxIK653ZkTBIs4DG+cBns=", + "lastModified": 1749285348, + "narHash": "sha256-frdhQvPbmDYaScPFiCnfdh3B/Vh81Uuoo0w5TkWmmjU=", "owner": "nixos", "repo": "nixpkgs", - "rev": "d0797a04b81caeae77bcff10a9dde78bc17f5661", + "rev": "3e3afe5174c561dee0df6f2c2b2236990146329f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 9f858541df..01b23258b9 100644 --- a/flake.nix +++ b/flake.nix @@ -3,39 +3,20 @@ nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { - nixpkgs, - flake-utils, - ... - }: + outputs = + { + nixpkgs, + flake-utils, + ... + }: flake-utils.lib.eachDefaultSystem ( - system: let + system: + let pkgs = nixpkgs.legacyPackages.${system}; - in { - devShells.default = pkgs.mkShell { - buildInputs = with pkgs; [ - # generic - git - git-lfs - gnumake - gnused - gnutar - gzip - - # frontend - nodejs_20 - - # linting - python312 - poetry - - # backend - gofumpt - sqlite - go - gopls - ]; - }; + in + { + devShells.default = import ./shell.nix { inherit pkgs; }; + formatter = pkgs.nixfmt-rfc-style; } ); } diff --git a/go.mod b/go.mod index b49ef7436f..bb2be827eb 100644 --- a/go.mod +++ b/go.mod @@ -1,33 +1,34 @@ -module code.gitea.io/gitea +module forgejo.org go 1.24 -toolchain go1.24.1 +toolchain go1.24.4 require ( - code.forgejo.org/f3/gof3/v3 v3.10.6 + code.forgejo.org/f3/gof3/v3 v3.11.0 code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 + code.forgejo.org/forgejo/go-rpmutils v1.0.0 code.forgejo.org/forgejo/levelqueue v1.0.0 code.forgejo.org/forgejo/reply v1.0.2 - code.forgejo.org/go-chi/binding v1.0.0 - code.forgejo.org/go-chi/cache v1.0.0 - code.forgejo.org/go-chi/captcha v1.0.1 - code.forgejo.org/go-chi/session v1.0.1 + code.forgejo.org/go-chi/binding v1.0.1 + code.forgejo.org/go-chi/cache v1.0.1 + code.forgejo.org/go-chi/captcha v1.0.2 + code.forgejo.org/go-chi/session v1.0.2 code.gitea.io/actions-proto-go v0.4.0 - code.gitea.io/sdk/gitea v0.20.0 + code.gitea.io/sdk/gitea v0.21.0 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 - connectrpc.com/connect v1.17.0 - github.com/42wim/httpsig v1.2.2 - github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 + connectrpc.com/connect v1.18.1 + github.com/42wim/httpsig v1.2.3 + github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 - github.com/ProtonMail/go-crypto v1.1.6 - github.com/PuerkitoBio/goquery v1.10.2 + github.com/ProtonMail/go-crypto v1.3.0 + github.com/PuerkitoBio/goquery v1.10.3 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 - github.com/alecthomas/chroma/v2 v2.15.0 + github.com/alecthomas/chroma/v2 v2.18.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb - github.com/blevesearch/bleve/v2 v2.4.4 + github.com/blevesearch/bleve/v2 v2.5.2 github.com/buildkite/terminal-to-html/v3 v3.16.8 - github.com/caddyserver/certmagic v0.22.2 + github.com/caddyserver/certmagic v0.23.0 github.com/chi-middleware/proxy v1.1.1 github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 @@ -36,27 +37,26 @@ require ( github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 github.com/emersion/go-imap v1.2.1 github.com/felixge/fgprof v0.9.5 - github.com/fsnotify/fsnotify v1.8.0 + github.com/fsnotify/fsnotify v1.9.0 github.com/gliderlabs/ssh v0.3.8 github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 - github.com/go-chi/chi/v5 v5.2.0 + github.com/go-chi/chi/v5 v5.2.2 github.com/go-chi/cors v1.2.1 github.com/go-co-op/gocron v1.37.0 github.com/go-enry/go-enry/v2 v2.9.2 github.com/go-git/go-git/v5 v5.13.2 github.com/go-ldap/ldap/v3 v3.4.6 - github.com/go-openapi/spec v0.20.14 - github.com/go-sql-driver/mysql v1.9.1 - github.com/go-testfixtures/testfixtures/v3 v3.14.0 - github.com/go-webauthn/webauthn v0.12.2 + github.com/go-openapi/spec v0.21.0 + github.com/go-sql-driver/mysql v1.9.3 + github.com/go-webauthn/webauthn v0.13.0 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/google/go-github/v64 v64.0.0 - github.com/google/pprof v0.0.0-20241017200806-017d972448fc + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.2.0 github.com/gorilla/sessions v1.4.0 @@ -67,47 +67,45 @@ require ( github.com/jhillyerd/enmime/v2 v2.1.0 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/klauspost/compress v1.17.11 + github.com/klauspost/compress v1.18.0 github.com/klauspost/cpuid/v2 v2.2.10 github.com/lib/pq v1.10.9 github.com/markbates/goth v1.80.0 github.com/mattn/go-isatty v0.0.20 - github.com/mattn/go-sqlite3 v1.14.24 + github.com/mattn/go-sqlite3 v1.14.28 github.com/meilisearch/meilisearch-go v0.31.0 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/minio/minio-go/v7 v7.0.88 - github.com/msteinert/pam/v2 v2.0.0 + github.com/minio/minio-go/v7 v7.0.94 + github.com/msteinert/pam/v2 v2.1.0 github.com/nektos/act v0.2.52 - github.com/niklasfasching/go-org v1.7.0 + github.com/niklasfasching/go-org v1.8.0 github.com/olivere/elastic/v7 v7.0.32 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 github.com/pquerna/otp v1.4.0 github.com/prometheus/client_golang v1.21.1 - github.com/redis/go-redis/v9 v9.7.3 + github.com/redis/go-redis/v9 v9.8.0 github.com/robfig/cron/v3 v3.0.1 - github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 - github.com/sassoftware/go-rpmutils v0.4.0 - github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 - github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 + github.com/sergi/go-diff v1.4.0 github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.0 github.com/ulikunitz/xz v0.5.12 - github.com/urfave/cli/v2 v2.27.6 + github.com/urfave/cli/v3 v3.3.3 github.com/valyala/fastjson v1.6.4 github.com/yohcop/openid-go v1.0.1 - github.com/yuin/goldmark v1.7.8 + github.com/yuin/goldmark v1.7.12 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc - gitlab.com/gitlab-org/api/client-go v0.126.0 - go.uber.org/mock v0.5.0 - golang.org/x/crypto v0.36.0 - golang.org/x/image v0.25.0 - golang.org/x/net v0.37.0 - golang.org/x/oauth2 v0.28.0 - golang.org/x/sync v0.12.0 - golang.org/x/sys v0.31.0 - golang.org/x/text v0.23.0 + gitlab.com/gitlab-org/api/client-go v0.130.1 + go.uber.org/mock v0.5.2 + golang.org/x/crypto v0.39.0 + golang.org/x/image v0.27.0 + golang.org/x/net v0.41.0 + golang.org/x/oauth2 v0.30.0 + golang.org/x/sync v0.15.0 + golang.org/x/sys v0.33.0 + golang.org/x/text v0.26.0 google.golang.org/protobuf v1.36.4 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 @@ -118,66 +116,50 @@ require ( ) require ( - cel.dev/expr v0.19.1 // indirect - cloud.google.com/go v0.116.0 // indirect - cloud.google.com/go/auth v0.9.9 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/iam v1.2.1 // indirect - cloud.google.com/go/longrunning v0.6.1 // indirect - cloud.google.com/go/monitoring v1.21.1 // indirect - cloud.google.com/go/spanner v1.73.0 // indirect dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect - github.com/DataDog/zstd v1.5.5 // indirect - github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/RoaringBitmap/roaring v1.9.3 // indirect + github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.13.0 // indirect - github.com/blevesearch/bleve_index_api v1.1.12 // indirect - github.com/blevesearch/geo v0.1.20 // indirect - github.com/blevesearch/go-faiss v1.0.24 // indirect + github.com/bits-and-blooms/bitset v1.22.0 // indirect + github.com/blevesearch/bleve_index_api v1.2.8 // indirect + github.com/blevesearch/geo v0.2.3 // indirect + github.com/blevesearch/go-faiss v1.0.25 // indirect github.com/blevesearch/go-porterstemmer v1.0.3 // indirect github.com/blevesearch/gtreap v0.1.1 // indirect github.com/blevesearch/mmap-go v1.0.4 // indirect - github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect + github.com/blevesearch/scorch_segment_api/v2 v2.3.10 // indirect github.com/blevesearch/segment v0.9.1 // indirect github.com/blevesearch/snowballstem v0.9.0 // indirect github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect - github.com/blevesearch/vellum v1.0.10 // indirect - github.com/blevesearch/zapx/v11 v11.3.10 // indirect - github.com/blevesearch/zapx/v12 v12.3.10 // indirect - github.com/blevesearch/zapx/v13 v13.3.10 // indirect - github.com/blevesearch/zapx/v14 v14.3.10 // indirect - github.com/blevesearch/zapx/v15 v15.3.16 // indirect - github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b // indirect + github.com/blevesearch/vellum v1.1.0 // indirect + github.com/blevesearch/zapx/v11 v11.4.2 // indirect + github.com/blevesearch/zapx/v12 v12.4.2 // indirect + github.com/blevesearch/zapx/v13 v13.4.2 // indirect + github.com/blevesearch/zapx/v14 v14.4.2 // indirect + github.com/blevesearch/zapx/v15 v15.4.2 // indirect + github.com/blevesearch/zapx/v16 v16.2.4 // indirect github.com/boombuler/barcode v1.0.1 // indirect - github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect + github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect github.com/caddyserver/zerossl v0.1.3 // indirect github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cloudflare/circl v1.3.8 // indirect - github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/cloudflare/circl v1.6.1 // indirect github.com/cyphar/filepath-securejoin v0.3.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dlclark/regexp2 v1.11.4 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect - github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-enry/go-oniguruma v1.2.1 // indirect @@ -185,26 +167,19 @@ require ( github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect - github.com/go-openapi/swag v0.22.7 // indirect - github.com/go-webauthn/x v0.1.19 // indirect + github.com/go-openapi/jsonpointer v0.21.1 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect + github.com/go-webauthn/x v0.1.21 // indirect github.com/goccy/go-json v0.10.5 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect - github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/go-tpm v0.9.3 // indirect - github.com/google/s2a-go v0.1.8 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect - github.com/googleapis/gax-go/v2 v2.13.0 // indirect - github.com/googleapis/go-sql-spanner v1.7.4 // indirect + github.com/google/go-tpm v0.9.5 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -214,12 +189,12 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect - github.com/libdns/libdns v0.2.3 // indirect + github.com/libdns/libdns v1.0.0-beta.1 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/markbates/going v1.0.3 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/mholt/acmez/v3 v3.1.1 // indirect + github.com/mholt/acmez/v3 v3.1.2 // indirect github.com/miekg/dns v1.1.63 // indirect github.com/minio/crc64nvme v1.0.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect @@ -232,59 +207,42 @@ require ( github.com/nwaples/rardecode v1.1.3 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect + github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rhysd/actionlint v1.6.27 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rs/xid v1.6.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect + github.com/tinylib/msgp v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + github.com/zeebo/assert v1.3.0 // indirect github.com/zeebo/blake3 v0.2.4 // indirect - go.etcd.io/bbolt v1.3.9 // indirect - go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect - go.opentelemetry.io/otel/sdk v1.34.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.etcd.io/bbolt v1.4.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/time v0.10.0 // indirect - golang.org/x/tools v0.31.0 // indirect - google.golang.org/api v0.203.0 // indirect - google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect - google.golang.org/grpc v1.71.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/time v0.11.0 // indirect + golang.org/x/tools v0.34.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 -replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 - -replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.25.1 +replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.28.0 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 diff --git a/go.sum b/go.sum index 67a1642186..639880e2ce 100644 --- a/go.sum +++ b/go.sum @@ -1,623 +1,15 @@ -cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= -cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= -cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/auth v0.9.9 h1:BmtbpNQozo8ZwW2t7QJjnrQtdganSdmqeIBxHxNkEZQ= -cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= -cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= -cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.2.1 h1:QFct02HRb7H12J/3utj0qf5tobFh9V4vR6h9eX5EBRU= -cloud.google.com/go/iam v1.2.1/go.mod h1:3VUIJDPpwT6p/amXRC5GY8fCCh70lxPygguVtI0Z4/g= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.6.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc= -cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.21.1 h1:zWtbIoBMnU5LP9A/fz8LmWMGHpk4skdfeiaa66QdFGc= -cloud.google.com/go/monitoring v1.21.1/go.mod h1:Rj++LKrlht9uBi8+Eb530dIrzG/cU/lB8mt+lbeFK1c= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.73.0 h1:0bab8QDn6MNj9lNK6XyGAVFhMlhMU2waePPa6GZNoi8= -cloud.google.com/go/spanner v1.73.0/go.mod h1:mw98ua5ggQXVWwp83yjwggqEmW9t8rjs9Po1ohcUGW4= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -code.forgejo.org/f3/gof3/v3 v3.10.6 h1:Ru/Iz+pqM8IPi7atUHE7+q7v3O3DRbYgMFqrFTsO1m8= -code.forgejo.org/f3/gof3/v3 v3.10.6/go.mod h1:K6lQCWQIyN/5rjP/OJL9fMA6fd++satndE20w/I6Kss= +code.forgejo.org/f3/gof3/v3 v3.11.0 h1:f/xToKwqTgxG6PYxvewywjDQyCcyHEEJ6sZqUitFsAE= +code.forgejo.org/f3/gof3/v3 v3.11.0/go.mod h1:4FaRUNSQGBiD1M0DuB0yNv+Z2wMtlOeckgygHSSq4KQ= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= -code.forgejo.org/forgejo/act v1.25.1 h1:T0CsN9iEWIyJzIbmMHMM9pl1KHzmI41q8mtepqVqdCc= -code.forgejo.org/forgejo/act v1.25.1/go.mod h1:tSg5CAHnXp4WLNkMa2e9AEDSujMxKzNM4bF2pvvRCYQ= +code.forgejo.org/forgejo/act v1.28.0 h1:96njNC7C1YNyjWq5OWvLZMF/nw0PMthzIA8Nwbnn7jo= +code.forgejo.org/forgejo/act v1.28.0/go.mod h1:dFuiwAmD5vyrzecysHB2kL/GM3wRpoVPl+WdbCTC8Bs= code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE= code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= +code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M= +code.forgejo.org/forgejo/go-rpmutils v1.0.0/go.mod h1:cg+VbgLXfrDPza9T+kBsMb3TVmmzPN4XseT6gDGLSUk= code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:RArF5AsF9LH4nEoJxqRxcP5r8hhRfWcId84G82YbqzA= code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/UfU/XgPpLuE= @@ -626,90 +18,64 @@ code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCd code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= -code.forgejo.org/go-chi/binding v1.0.0 h1:EIDJtk9brK7WsT7rvS/D4cxX8XlnhY3LMy8ex1jeHu0= -code.forgejo.org/go-chi/binding v1.0.0/go.mod h1:fWwqaHj0H1/KeCpBqdvKunflq8pYfciEHI5v3UUeE2E= -code.forgejo.org/go-chi/cache v1.0.0 h1:akLfGxNlHcacmtutovNtYFSTMsbdcp5MGjAEsP4pxnE= -code.forgejo.org/go-chi/cache v1.0.0/go.mod h1:OVlZ/TqDYJ+RUJ+R+J+OLxtlyjo3pbjBeK7LAWAB+Vk= -code.forgejo.org/go-chi/captcha v1.0.1 h1:/oe1fvGOpdyyeGijg3oMYNOYLvEovNvp79Y3gLe3qbk= -code.forgejo.org/go-chi/captcha v1.0.1/go.mod h1:6EbjSVVa7WoZFENgwK/hLAJZq+HBXtgRsjnIngILC8Y= -code.forgejo.org/go-chi/session v1.0.1 h1:RNkcJQZJBqlvJoIFXSth87b3kMFZLDBA18VcitD+Z0Y= -code.forgejo.org/go-chi/session v1.0.1/go.mod h1:y69sjS984wc7k4xyu77yNE5HKeSlBoQW8VSGdsK7RAs= +code.forgejo.org/go-chi/binding v1.0.1 h1:coKNI+X1NzRN7X85LlrpvBRqk0TXpJ+ja28vusQWEuY= +code.forgejo.org/go-chi/binding v1.0.1/go.mod h1:oTFFDg/dkwFbmVuusiULB1OlrIJM95cOGK7Nc3GYcoo= +code.forgejo.org/go-chi/cache v1.0.1 h1:w6IsDcPbeEnEYZn7M2HJe3/3/Ehtcw/72VjcVK7+lBw= +code.forgejo.org/go-chi/cache v1.0.1/go.mod h1:K3aQSyRIN4xiuqV1kanfQ6O4ToDpzDpY3bNOyGjFe3U= +code.forgejo.org/go-chi/captcha v1.0.2 h1:vyHDPXkpjDv8bLO9NqtWzZayzstD/WpJ5xwEkAaqZGQ= +code.forgejo.org/go-chi/captcha v1.0.2/go.mod h1:lxiPLcJ76UCZHoH31/Wbum4GUi2NgjfFZLrJkKv1lLE= +code.forgejo.org/go-chi/session v1.0.2 h1:pG+AXre9L9VXJmTaADXkmeEPuRalhmBXyv6tG2Rvjcc= +code.forgejo.org/go-chi/session v1.0.2/go.mod h1:HnEGyBny7WPzCiVLP2vzL5ssma+3gCSl/vLpuVNYrqc= code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU= code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= -code.gitea.io/sdk/gitea v0.20.0 h1:Zm/QDwwZK1awoM4AxdjeAQbxolzx2rIP8dDfmKu+KoU= -code.gitea.io/sdk/gitea v0.20.0/go.mod h1:faouBHC/zyx5wLgjmRKR62ydyvMzwWf3QnU0bH7Cw6U= +code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4= +code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= -connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk= -connectrpc.com/connect v1.17.0/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= -github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA= -github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY= -github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 h1:r3qt8PCHnfjOv9PN3H+XXKmDA1dfFMIN1AislhlA/ps= -github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121/go.mod h1:Ock8XgA7pvULhIaHGAk/cDnRfNrF9Jey81nPcc403iU= +github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= +github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM= +github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920 h1:mWAVGlovzUfREJBhm0GwJnDNu21yRrL9QH9NIzAU3rg= +github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920/go.mod h1:zWxcT7BIWOe05xVJL0VMvO/PJ6RpoCux10heb77H6Q8= github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U= github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= -github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg= -github.com/ClickHouse/clickhouse-go/v2 v2.30.0 h1:AG4D/hW39qa58+JHQIFOSnxyL46H6h2lrmGGk17dhFo= -github.com/ClickHouse/clickhouse-go/v2 v2.30.0/go.mod h1:i9ZQAojcayW3RsdCb3YR+n+wC2h65eJsZCscZ1Z1wyo= -github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= -github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 h1:oVLqHXhnYtUwM89y9T1fXGaK9wTkXHgNp8/ZNMQzUxE= -github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= -github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8= -github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU= -github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM= -github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= +github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= +github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= +github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= +github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg= +github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 h1:cSXom2MoKJ9KPPw29RoZtHvUETY4F4n/kXl8m9btnQ0= github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2/go.mod h1:JitQWJ8JuV4Y87l8VsHiiwhb3cgdyn68mX40s7NT6PA= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc= -github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio= +github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4= +github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= @@ -717,70 +83,63 @@ github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd3 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= -github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4= +github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= -github.com/blevesearch/bleve/v2 v2.4.4 h1:RwwLGjUm54SwyyykbrZs4vc1qjzYic4ZnAnY9TwNl60= -github.com/blevesearch/bleve/v2 v2.4.4/go.mod h1:fa2Eo6DP7JR+dMFpQe+WiZXINKSunh7WBtlDGbolKXk= -github.com/blevesearch/bleve_index_api v1.1.12 h1:P4bw9/G/5rulOF7SJ9l4FsDoo7UFJ+5kexNy1RXfegY= -github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8= -github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM= -github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w= -github.com/blevesearch/go-faiss v1.0.24 h1:K79IvKjoKHdi7FdiXEsAhxpMuns0x4fM0BO93bW5jLI= -github.com/blevesearch/go-faiss v1.0.24/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= +github.com/blevesearch/bleve/v2 v2.5.2 h1:Ab0r0MODV2C5A6BEL87GqLBySqp/s9xFgceCju6BQk8= +github.com/blevesearch/bleve/v2 v2.5.2/go.mod h1:5Dj6dUQxZM6aqYT3eutTD/GpWKGFSsV8f7LDidFbwXo= +github.com/blevesearch/bleve_index_api v1.2.8 h1:Y98Pu5/MdlkRyLM0qDHostYo7i+Vv1cDNhqTeR4Sy6Y= +github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= +github.com/blevesearch/geo v0.2.3 h1:K9/vbGI9ehlXdxjxDRJtoAMt7zGAsMIzc6n8zWcwnhg= +github.com/blevesearch/geo v0.2.3/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8= +github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U= +github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= -github.com/blevesearch/scorch_segment_api/v2 v2.2.16 h1:uGvKVvG7zvSxCwcm4/ehBa9cCEuZVE+/zvrSl57QUVY= -github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0= +github.com/blevesearch/scorch_segment_api/v2 v2.3.10 h1:Yqk0XD1mE0fDZAJXTjawJ8If/85JxnLd8v5vG/jWE/s= +github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8= github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= -github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI= -github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k= -github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk= -github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ= -github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s= -github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs= -github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8= -github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk= -github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU= -github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns= -github.com/blevesearch/zapx/v15 v15.3.16 h1:Ct3rv7FUJPfPk99TI/OofdC+Kpb4IdyfdMH48sb+FmE= -github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg= -github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b h1:ju9Az5YgrzCeK3M1QwvZIpxYhChkXp7/L0RhDYsxXoE= -github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b/go.mod h1:BlrYNpOu4BvVRslmIG+rLtKhmjIaRhIbG8sb9scGTwI= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= +github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= +github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs= +github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc= +github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE= +github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58= +github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks= +github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk= +github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0= +github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8= +github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k= +github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= +github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww= +github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= -github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= +github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf h1:TqhNAT4zKbTdLa62d2HDBFdvgSbIGB3eJE8HqhgiL9I= +github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/buildkite/terminal-to-html/v3 v3.16.8 h1:QN/daUob6cmK8GcdKnwn9+YTlPr1vNj+oeAIiJK6fPc= github.com/buildkite/terminal-to-html/v3 v3.16.8/go.mod h1:+k1KVKROZocrTLsEQ9PEf9A+8+X8uaVV5iO1ZIOwKYM= -github.com/caddyserver/certmagic v0.22.2 h1:qzZURXlrxwR5m25/jpvVeEyJHeJJMvAwe5zlMufOTQk= -github.com/caddyserver/certmagic v0.22.2/go.mod h1:hbqE7BnkjhX5IJiFslPmrSeobSeZvI6ux8tyxhsd6qs= +github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= +github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chi-middleware/proxy v1.1.1 h1:4HaXUp8o2+bhHr1OhVy+VjN0+L7/07JDcn6v7YrTjrQ= @@ -788,32 +147,11 @@ github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdi github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= -github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 h1:boJj011Hh+874zpIySeApCX4GeOjPl9qhRF3QuIZq+Q= -github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= @@ -823,8 +161,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= -github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw= -github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/djherbis/buffer v1.1.0/go.mod h1:VwN8VdFkMY0DCALdY8o00d3IZ6Amz/UNVMWcSaJT44o= @@ -835,14 +171,12 @@ github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmW github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= -github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs= @@ -858,46 +192,18 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTe github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= -github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= -github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= -github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= -github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= -github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= -github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= +github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 h1:j2TrkUG/NATGi/EQS+MvEoF79CxiRUmT16ErFroNcKI= github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9/go.mod h1:cJ9Ye0ZNSMN7RzZDBRY3E+8M3Bpf/R1JX22Ir9yX6WI= github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 h1:I2nuhyVI/48VXoRCCZR2hYBgnSXa+EuDJf/VyX06TC0= @@ -907,8 +213,8 @@ github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5La github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= -github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= +github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= @@ -917,17 +223,8 @@ github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6n github.com/go-enry/go-enry/v2 v2.9.2/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8= github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4= -github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= -github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= -github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= -github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= @@ -936,128 +233,67 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= -github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= -github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= -github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do= -github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw= -github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8= -github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI= -github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= +github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-testfixtures/testfixtures/v3 v3.14.0 h1:aRt5qyH2XjzFgCC5NizNs6QrzjO7rC4pQZ1oJpPIdo8= -github.com/go-testfixtures/testfixtures/v3 v3.14.0/go.mod h1:HHb6Yd8spzm6aFZU6jwBj9qFvVUNNkx5nGbjG4UHeOE= -github.com/go-webauthn/webauthn v0.12.2 h1:yLaNPgBUEXDQtWnOjhsGhMMCEWbXwjg/aNkC8riJQI8= -github.com/go-webauthn/webauthn v0.12.2/go.mod h1:Q8SZPPj4sZ469fNTcQXxRpzJOdb30jQrn/36FX8jilA= -github.com/go-webauthn/x v0.1.19 h1:IUfdHiBRoTdujpBA/14qbrMXQ3LGzYe/PRGWdZcmudg= -github.com/go-webauthn/x v0.1.19/go.mod h1:C5arLuTQ3pVHKPw89v7CDGnqAZSZJj+4Jnr40dsn7tk= +github.com/go-webauthn/webauthn v0.13.0 h1:cJIL1/1l+22UekVhipziAaSgESJxokYkowUqAIsWs0Y= +github.com/go-webauthn/webauthn v0.13.0/go.mod h1:Oy9o2o79dbLKRPZWWgRIOdtBGAhKnDIaBp2PFkICRHs= +github.com/go-webauthn/x v0.1.21 h1:nFbckQxudvHEJn2uy1VEi713MeSpApoAv9eRqsb9AdQ= +github.com/go-webauthn/x v0.1.21/go.mod h1:sEYohtg1zL4An1TXIUIQ5csdmoO+WO0R4R2pGKaHYKA= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= -github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= -github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I= -github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -1065,67 +301,18 @@ github.com/google/go-github/v64 v64.0.0 h1:4G61sozmY3eiPAjjoOHponXDBONm+utovTKby github.com/google/go-github/v64 v64.0.0/go.mod h1:xB3vqMQNdHzilXBiO2I+M7iEFtHf+DP/omBOv6tQzVo= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc= -github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= +github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU= +github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs= -github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= -github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= -github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= -github.com/googleapis/go-sql-spanner v1.7.4 h1:pwndJlqgIMOewkORveYQQocaSyOGqaQg8e2Os8hYh00= -github.com/googleapis/go-sql-spanner v1.7.4/go.mod h1:DfuJMbqpcDQwtbol+TnfO+AUyeoW5H+w8Gm216dTPys= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= @@ -1140,17 +327,12 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -1158,62 +340,32 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= -github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= -github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= -github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= -github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= -github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA= github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jhillyerd/enmime/v2 v2.1.0 h1:c8Qwi5Xq5EdtMN6byQWoZ/8I2RMTo6OJ7Xay+s1oPO0= github.com/jhillyerd/enmime/v2 v2.1.0/go.mod h1:EJ74dcRbBcqHSP2TBu08XRoy6y3Yx0cevwb1YkGMEmQ= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -1228,13 +380,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8= -github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= -github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI= -github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0/go.mod h1:JEfTc3+2DF9Z4PXhLLvXL42zexJyh8rIq3OzUj/0rAk= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ= +github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= @@ -1244,32 +391,28 @@ github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8 github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= -github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY= github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g= -github.com/mholt/acmez/v3 v3.1.1 h1:Jh+9uKHkPxUJdxM16q5mOr+G2V0aqkuFtNA28ihCxhQ= -github.com/mholt/acmez/v3 v3.1.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= +github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= +github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY= github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.88 h1:v8MoIJjwYxOkehp+eiLIuvXk87P2raUtoU5klrAAshs= -github.com/minio/minio-go/v7 v7.0.88/go.mod h1:33+O8h0tO7pCeCWwBVa07RhVVfB/3vS4kEX7rwYKmIg= +github.com/minio/minio-go/v7 v7.0.94 h1:1ZoksIKPyaSt64AVOyaQvhDOgVC3MfZsWM6mZXRUGtM= +github.com/minio/minio-go/v7 v7.0.94/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1281,12 +424,12 @@ github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUll github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= -github.com/msteinert/pam/v2 v2.0.0 h1:jnObb8MT6jvMbmrUQO5J/puTUjxy7Av+55zVJRJsCyE= -github.com/msteinert/pam/v2 v2.0.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc= +github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidGY= +github.com/msteinert/pam/v2 v2.1.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek= -github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o= +github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY= +github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= @@ -1312,25 +455,16 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= -github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= -github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -1338,17 +472,14 @@ github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rhysd/actionlint v1.6.27 h1:xxwe8YmveBcC8lydW6GoHMGmB6H/MTqUU60F2p10wjw= @@ -1358,41 +489,22 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= -github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= -github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI= -github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= -github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs= -github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1402,23 +514,23 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= +github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= -github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I= +github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -1427,22 +539,15 @@ github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js= github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= -github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= +github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= @@ -1451,48 +556,17 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -gitlab.com/gitlab-org/api/client-go v0.126.0 h1:VV5TdkF6pMbEdFGvbR2CwEgJwg6qdg1u3bj5eD2tiWk= -gitlab.com/gitlab-org/api/client-go v0.126.0/go.mod h1:bYC6fPORKSmtuPRyD9Z2rtbAjE7UeNatu2VWHRf4/LE= -go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= -go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= -go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +gitlab.com/gitlab-org/api/client-go v0.130.1 h1:1xF5C5Zq3sFeNg3PzS2z63oqrxifne3n/OnbI7nptRc= +gitlab.com/gitlab-org/api/client-go v0.130.1/go.mod h1:ZhSxLAWadqP6J9lMh40IAZOlOxBLPRh7yFOXR/bMJWM= +go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= @@ -1500,694 +574,131 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= -golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= +golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= -golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.203.0 h1:SrEeuwU3S11Wlscsn+LA1kb/Y5xT8uggJSkIhD08NAU= -google.golang.org/api v0.203.0/go.mod h1:BuOVyCSYEPwJb3npWvDnNmFI92f3GeRnHNkETneT3SI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 h1:Df6WuGvthPzc+JiQ/G+m+sNX24kc0aTBqoDN/0yyykE= -google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53/go.mod h1:fheguH3Am2dGp1LfXkrvwqC/KlFq8F0nLq3LryOMrrE= -google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= -google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= -google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= @@ -2209,78 +720,36 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE= modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8= mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo= xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/xorm v1.3.9 h1:TUovzS0ko+IQ1XnNLfs5dqK1cJl1H5uHpWbWqAQ04nU= diff --git a/main.go b/main.go index b8cc5668e1..ade43881cf 100644 --- a/main.go +++ b/main.go @@ -10,18 +10,18 @@ import ( "strings" "time" - "code.gitea.io/gitea/cmd" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/cmd" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" // register supported doc types - _ "code.gitea.io/gitea/modules/markup/asciicast" - _ "code.gitea.io/gitea/modules/markup/console" - _ "code.gitea.io/gitea/modules/markup/csv" - _ "code.gitea.io/gitea/modules/markup/markdown" - _ "code.gitea.io/gitea/modules/markup/orgmode" + _ "forgejo.org/modules/markup/asciicast" + _ "forgejo.org/modules/markup/console" + _ "forgejo.org/modules/markup/csv" + _ "forgejo.org/modules/markup/markdown" + _ "forgejo.org/modules/markup/orgmode" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) // these flags will be set by the build flags diff --git a/manifest.scm b/manifest.scm new file mode 100644 index 0000000000..f9605bc2d9 --- /dev/null +++ b/manifest.scm @@ -0,0 +1,38 @@ +;;; Copyright 2025 The Forgejo Authors. All rights reserved. +;;; SPDX-License-Identifier: MIT +;;; +;;; Commentary: +;;; +;;; This is a GNU Guix manifest that can be used to create a +;;; development environment to build and test Forgejo. +;;; +;;; The following is a usage example to create a containerized +;;; environment, with HOME shared for the Go cache and the network +;;; made available to fetch required Go and Node dependencies. +;;; +#| +guix shell -CNF --share=$HOME -m manifest.scm +export GOTOOLCHAIN=local # to use the Go binary from Guix +export CC=gcc CGO_ENABLED=1 +export TAGS="timetzdata sqlite sqlite_unlock_notify" +make clean +make -j$(nproc) +make test -j$(nproc) # run unit tests +make test-sqlite -j$(nproc) # run integration tests +make watch # run an instance/rebuild on changes +|# +(specifications->manifest + (list "bash-minimal" + "coreutils" + "findutils" + "gcc-toolchain" + "git" ;libpcre support is required + "git-lfs" + "gnupg" + "go" + "grep" + "make" + "node" + "nss-certs" + "openssh" + "sed")) diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 0bc66ba24e..10cd3868a1 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -11,9 +11,9 @@ import ( "errors" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "xorm.io/builder" ) diff --git a/models/actions/forgejo.go b/models/actions/forgejo.go index 5ea77f4473..ce3f8b0c8b 100644 --- a/models/actions/forgejo.go +++ b/models/actions/forgejo.go @@ -7,9 +7,9 @@ import ( "crypto/subtle" "fmt" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/util" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/modules/util" gouuid "github.com/google/uuid" ) diff --git a/models/actions/forgejo_test.go b/models/actions/forgejo_test.go index 9295fc698e..5702068c1b 100644 --- a/models/actions/forgejo_test.go +++ b/models/actions/forgejo_test.go @@ -6,9 +6,9 @@ import ( "crypto/subtle" "testing" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -24,9 +24,9 @@ func TestActions_RegisterRunner_Token(t *testing.T) { version := "v1.2.3" runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version) require.NoError(t, err) - assert.EqualValues(t, name, runner.Name) + assert.Equal(t, name, runner.Name) - assert.EqualValues(t, 1, subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))), "the token cannot be verified with the same method as routers/api/actions/runner/interceptor.go as of 8228751c55d6a4263f0fec2932ca16181c09c97d") + assert.Equal(t, 1, subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))), "the token cannot be verified with the same method as routers/api/actions/runner/interceptor.go as of 8228751c55d6a4263f0fec2932ca16181c09c97d") } // TestActions_RegisterRunner_TokenUpdate tests that a token's secret is updated @@ -73,19 +73,19 @@ func TestActions_RegisterRunner_CreateWithLabels(t *testing.T) { require.NoError(t, err) // Check that the returned record has been updated, except for the labels - assert.EqualValues(t, ownerID, runner.OwnerID) - assert.EqualValues(t, repoID, runner.RepoID) - assert.EqualValues(t, name, runner.Name) - assert.EqualValues(t, version, runner.Version) - assert.EqualValues(t, labelsCopy, runner.AgentLabels) + assert.Equal(t, ownerID, runner.OwnerID) + assert.Equal(t, repoID, runner.RepoID) + assert.Equal(t, name, runner.Name) + assert.Equal(t, version, runner.Version) + assert.Equal(t, labelsCopy, runner.AgentLabels) // Check that whatever is in the DB has been updated, except for the labels after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID}) - assert.EqualValues(t, ownerID, after.OwnerID) - assert.EqualValues(t, repoID, after.RepoID) - assert.EqualValues(t, name, after.Name) - assert.EqualValues(t, version, after.Version) - assert.EqualValues(t, labelsCopy, after.AgentLabels) + assert.Equal(t, ownerID, after.OwnerID) + assert.Equal(t, repoID, after.RepoID) + assert.Equal(t, name, after.Name) + assert.Equal(t, version, after.Version) + assert.Equal(t, labelsCopy, after.AgentLabels) } func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) { @@ -100,19 +100,19 @@ func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) { require.NoError(t, err) // Check that the returned record has been updated, except for the labels - assert.EqualValues(t, ownerID, runner.OwnerID) - assert.EqualValues(t, repoID, runner.RepoID) - assert.EqualValues(t, name, runner.Name) - assert.EqualValues(t, version, runner.Version) - assert.EqualValues(t, []string{}, runner.AgentLabels) + assert.Equal(t, ownerID, runner.OwnerID) + assert.Equal(t, repoID, runner.RepoID) + assert.Equal(t, name, runner.Name) + assert.Equal(t, version, runner.Version) + assert.Equal(t, []string{}, runner.AgentLabels) // Check that whatever is in the DB has been updated, except for the labels after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID}) - assert.EqualValues(t, ownerID, after.OwnerID) - assert.EqualValues(t, repoID, after.RepoID) - assert.EqualValues(t, name, after.Name) - assert.EqualValues(t, version, after.Version) - assert.EqualValues(t, []string{}, after.AgentLabels) + assert.Equal(t, ownerID, after.OwnerID) + assert.Equal(t, repoID, after.RepoID) + assert.Equal(t, name, after.Name) + assert.Equal(t, version, after.Version) + assert.Equal(t, []string{}, after.AgentLabels) } func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) { @@ -132,19 +132,19 @@ func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) { require.NoError(t, err) // Check that the returned record has been updated - assert.EqualValues(t, newOwnerID, runner.OwnerID) - assert.EqualValues(t, newRepoID, runner.RepoID) - assert.EqualValues(t, newName, runner.Name) - assert.EqualValues(t, newVersion, runner.Version) - assert.EqualValues(t, labelsCopy, runner.AgentLabels) + assert.Equal(t, newOwnerID, runner.OwnerID) + assert.Equal(t, newRepoID, runner.RepoID) + assert.Equal(t, newName, runner.Name) + assert.Equal(t, newVersion, runner.Version) + assert.Equal(t, labelsCopy, runner.AgentLabels) // Check that whatever is in the DB has been updated after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) - assert.EqualValues(t, newOwnerID, after.OwnerID) - assert.EqualValues(t, newRepoID, after.RepoID) - assert.EqualValues(t, newName, after.Name) - assert.EqualValues(t, newVersion, after.Version) - assert.EqualValues(t, labelsCopy, after.AgentLabels) + assert.Equal(t, newOwnerID, after.OwnerID) + assert.Equal(t, newRepoID, after.RepoID) + assert.Equal(t, newName, after.Name) + assert.Equal(t, newVersion, after.Version) + assert.Equal(t, labelsCopy, after.AgentLabels) } func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) { @@ -162,17 +162,17 @@ func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) { require.NoError(t, err) // Check that the returned record has been updated, except for the labels - assert.EqualValues(t, newOwnerID, runner.OwnerID) - assert.EqualValues(t, newRepoID, runner.RepoID) - assert.EqualValues(t, newName, runner.Name) - assert.EqualValues(t, newVersion, runner.Version) - assert.EqualValues(t, before.AgentLabels, runner.AgentLabels) + assert.Equal(t, newOwnerID, runner.OwnerID) + assert.Equal(t, newRepoID, runner.RepoID) + assert.Equal(t, newName, runner.Name) + assert.Equal(t, newVersion, runner.Version) + assert.Equal(t, before.AgentLabels, runner.AgentLabels) // Check that whatever is in the DB has been updated, except for the labels after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) - assert.EqualValues(t, newOwnerID, after.OwnerID) - assert.EqualValues(t, newRepoID, after.RepoID) - assert.EqualValues(t, newName, after.Name) - assert.EqualValues(t, newVersion, after.Version) - assert.EqualValues(t, before.AgentLabels, after.AgentLabels) + assert.Equal(t, newOwnerID, after.OwnerID) + assert.Equal(t, newRepoID, after.RepoID) + assert.Equal(t, newName, after.Name) + assert.Equal(t, newVersion, after.Version) + assert.Equal(t, before.AgentLabels, after.AgentLabels) } diff --git a/models/actions/main_test.go b/models/actions/main_test.go index 3cfb395e62..2eb923d9d0 100644 --- a/models/actions/main_test.go +++ b/models/actions/main_test.go @@ -6,13 +6,14 @@ package actions import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" ) func TestMain(m *testing.M) { unittest.MainTest(m, &unittest.TestOptions{ FixtureFiles: []string{ "action_runner.yml", + "repository.yml", "action_runner_token.yml", }, }) diff --git a/models/actions/run.go b/models/actions/run.go index c5512106b9..69592120e9 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -5,20 +5,21 @@ package actions import ( "context" + "errors" "fmt" "slices" "strings" "time" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/json" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" - webhook_module "code.gitea.io/gitea/modules/webhook" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/git" + "forgejo.org/modules/json" + api "forgejo.org/modules/structs" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" + webhook_module "forgejo.org/modules/webhook" "github.com/nektos/act/pkg/jobparser" "xorm.io/builder" @@ -54,6 +55,7 @@ type ActionRun struct { PreviousDuration time.Duration Created timeutil.TimeStamp `xorm:"created"` Updated timeutil.TimeStamp `xorm:"updated"` + NotifyEmail bool } func init() { @@ -185,77 +187,9 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err return err } -// CancelPreviousJobs cancels all previous jobs of the same repository, reference, workflow, and event. -// It's useful when a new run is triggered, and all previous runs needn't be continued anymore. -func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error { - // Find all runs in the specified repository, reference, and workflow with non-final status - runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{ - RepoID: repoID, - Ref: ref, - WorkflowID: workflowID, - TriggerEvent: event, - Status: []Status{StatusRunning, StatusWaiting, StatusBlocked}, - }) - if err != nil { - return err - } - - // If there are no runs found, there's no need to proceed with cancellation, so return nil. - if total == 0 { - return nil - } - - // Iterate over each found run and cancel its associated jobs. - for _, run := range runs { - // Find all jobs associated with the current run. - jobs, err := db.Find[ActionRunJob](ctx, FindRunJobOptions{ - RunID: run.ID, - }) - if err != nil { - return err - } - - // Iterate over each job and attempt to cancel it. - for _, job := range jobs { - // Skip jobs that are already in a terminal state (completed, cancelled, etc.). - status := job.Status - if status.IsDone() { - continue - } - - // If the job has no associated task (probably an error), set its status to 'Cancelled' and stop it. - if job.TaskID == 0 { - job.Status = StatusCancelled - job.Stopped = timeutil.TimeStampNow() - - // Update the job's status and stopped time in the database. - n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped") - if err != nil { - return err - } - - // If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again. - if n == 0 { - return fmt.Errorf("job has changed, try again") - } - - // Continue with the next job. - continue - } - - // If the job has an associated task, try to stop the task, effectively cancelling the job. - if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil { - return err - } - } - } - - // Return nil to indicate successful cancellation of all running and waiting jobs. - return nil -} - // InsertRun inserts a run // The title will be cut off at 255 characters if it's longer than 255 characters. +// We don't have to send the ActionRunNowDone notification here because there are no runs that start in a not done status. func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error { ctx, commiter, err := db.TxContext(ctx) if err != nil { @@ -290,29 +224,38 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork var hasWaiting bool for _, v := range jobs { id, job := v.Job() - needs := job.Needs() - if err := v.SetJob(id, job.EraseNeeds()); err != nil { - return err + status := StatusFailure + payload := []byte{} + needs := []string{} + name := run.Title + runsOn := []string{} + if job != nil { + needs = job.Needs() + if err := v.SetJob(id, job.EraseNeeds()); err != nil { + return err + } + payload, _ = v.Marshal() + + if len(needs) > 0 || run.NeedApproval { + status = StatusBlocked + } else { + status = StatusWaiting + hasWaiting = true + } + name, _ = util.SplitStringAtByteN(job.Name, 255) + runsOn = job.RunsOn() } - payload, _ := v.Marshal() - status := StatusWaiting - if len(needs) > 0 || run.NeedApproval { - status = StatusBlocked - } else { - hasWaiting = true - } - job.Name, _ = util.SplitStringAtByteN(job.Name, 255) runJobs = append(runJobs, &ActionRunJob{ RunID: run.ID, RepoID: run.RepoID, OwnerID: run.OwnerID, CommitSHA: run.CommitSHA, IsForkPullRequest: run.IsForkPullRequest, - Name: job.Name, + Name: name, WorkflowPayload: payload, JobID: id, Needs: needs, - RunsOn: job.RunsOn(), + RunsOn: runsOn, Status: status, }) } @@ -341,6 +284,12 @@ func GetLatestRun(ctx context.Context, repoID int64) (*ActionRun, error) { return &run, nil } +func GetRunBefore(ctx context.Context, _ *ActionRun) (*ActionRun, error) { + // TODO return the most recent run related to the run given in argument + // see https://codeberg.org/forgejo/user-research/issues/63 for context + return nil, nil +} + func GetLatestRunForBranchAndWorkflow(ctx context.Context, repoID int64, branch, workflowFile, event string) (*ActionRun, error) { var run ActionRun q := db.GetEngine(ctx).Where("repo_id=?", repoID).And("workflow_id=?", workflowFile) @@ -389,7 +338,9 @@ func GetRunByIndex(ctx context.Context, repoID, index int64) (*ActionRun, error) // UpdateRun updates a run. // It requires the inputted run has Version set. // It will return error if the version is not matched (it means the run has been changed after loaded). -func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error { +// All calls to UpdateRunWithoutNotification that change run.Status from a not done status to a done status must call the ActionRunNowDone notification channel. +// Use the wrapper function UpdateRun instead. +func UpdateRunWithoutNotification(ctx context.Context, run *ActionRun, cols ...string) error { sess := db.GetEngine(ctx).ID(run.ID) if len(cols) > 0 { sess.Cols(cols...) @@ -400,7 +351,7 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error { return err } if affected == 0 { - return fmt.Errorf("run has changed") + return errors.New("run has changed") // It's impossible that the run is not found, since Gitea never deletes runs. } diff --git a/models/actions/run_job.go b/models/actions/run_job.go index 9f8edfe4fc..1fadb4b7c7 100644 --- a/models/actions/run_job.go +++ b/models/actions/run_job.go @@ -9,10 +9,10 @@ import ( "slices" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/container" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "xorm.io/builder" ) @@ -101,7 +101,9 @@ func GetRunJobsByRunID(ctx context.Context, runID int64) ([]*ActionRunJob, error return jobs, nil } -func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, cols ...string) (int64, error) { +// All calls to UpdateRunJobWithoutNotification that change run.Status for any run from a not done status to a done status must call the ActionRunNowDone notification channel. +// Use the wrapper function UpdateRunJob instead. +func UpdateRunJobWithoutNotification(ctx context.Context, job *ActionRunJob, cond builder.Cond, cols ...string) (int64, error) { e := db.GetEngine(ctx) sess := e.ID(job.ID) @@ -154,7 +156,8 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col if run.Stopped.IsZero() && run.Status.IsDone() { run.Stopped = timeutil.TimeStampNow() } - if err := UpdateRun(ctx, run, "status", "started", "stopped"); err != nil { + // As the caller has to ensure the ActionRunNowDone notification is sent we can ignore doing so here. + if err := UpdateRunWithoutNotification(ctx, run, "status", "started", "stopped"); err != nil { return 0, fmt.Errorf("update run %d: %w", run.ID, err) } } diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go index 6c5d3b3252..afc754f26a 100644 --- a/models/actions/run_job_list.go +++ b/models/actions/run_job_list.go @@ -6,9 +6,9 @@ package actions import ( "context" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + "forgejo.org/modules/container" + "forgejo.org/modules/timeutil" "xorm.io/builder" ) @@ -54,6 +54,8 @@ type FindRunJobOptions struct { CommitSHA string Statuses []Status UpdatedBefore timeutil.TimeStamp + Events []string // []webhook_module.HookEventType + RunNumber int64 } func (opts FindRunJobOptions) ToConds() builder.Cond { @@ -76,5 +78,11 @@ func (opts FindRunJobOptions) ToConds() builder.Cond { if opts.UpdatedBefore > 0 { cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore}) } + if len(opts.Events) > 0 { + cond = cond.And(builder.In("event", opts.Events)) + } + if opts.RunNumber > 0 { + cond = cond.And(builder.Eq{"`index`": opts.RunNumber}) + } return cond } diff --git a/models/actions/run_list.go b/models/actions/run_list.go index b9b9324e07..92be510569 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -6,12 +6,12 @@ package actions import ( "context" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/translation" - webhook_module "code.gitea.io/gitea/modules/webhook" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/container" + "forgejo.org/modules/translation" + webhook_module "forgejo.org/modules/webhook" "xorm.io/builder" ) diff --git a/models/actions/run_test.go b/models/actions/run_test.go new file mode 100644 index 0000000000..c9a552a2b2 --- /dev/null +++ b/models/actions/run_test.go @@ -0,0 +1,11 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package actions + +import ( + "testing" +) + +func TestGetRunBefore(t *testing.T) { +} diff --git a/models/actions/runner.go b/models/actions/runner.go index b24950d014..bece1ae301 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -11,15 +11,16 @@ import ( "strings" "time" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/shared/types" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/translation" - "code.gitea.io/gitea/modules/util" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/shared/types" + user_model "forgejo.org/models/user" + "forgejo.org/modules/log" + "forgejo.org/modules/optional" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/translation" + "forgejo.org/modules/util" runnerv1 "code.gitea.io/actions-proto-go/runner/v1" "xorm.io/builder" @@ -87,9 +88,10 @@ func (r *ActionRunner) BelongsToOwnerType() types.OwnerType { return types.OwnerTypeRepository } if r.OwnerID != 0 { - if r.Owner.Type == user_model.UserTypeOrganization { + switch r.Owner.Type { + case user_model.UserTypeOrganization: return types.OwnerTypeOrganization - } else if r.Owner.Type == user_model.UserTypeIndividual { + case user_model.UserTypeIndividual: return types.OwnerTypeIndividual } } @@ -167,12 +169,7 @@ func (r *ActionRunner) GenerateToken() (err error) { // UpdateSecret updates the hash based on the specified token. It does not // ensure that the runner's UUID matches the first 16 bytes of the token. func (r *ActionRunner) UpdateSecret(token string) error { - saltBytes, err := util.CryptoRandomBytes(16) - if err != nil { - return fmt.Errorf("CryptoRandomBytes %v", err) - } - - salt := hex.EncodeToString(saltBytes) + salt := hex.EncodeToString(util.CryptoRandomBytes(16)) r.Token = token r.TokenSalt = salt @@ -357,3 +354,53 @@ func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) { } return res.RowsAffected() } + +func DeleteOfflineRunners(ctx context.Context, olderThan timeutil.TimeStamp, globalOnly bool) error { + log.Info("Doing: DeleteOfflineRunners") + + if olderThan.AsTime().After(timeutil.TimeStampNow().AddDuration(-RunnerOfflineTime).AsTime()) { + return fmt.Errorf("invalid `cron.cleanup_offline_runners.older_than`value: must be at least %q", RunnerOfflineTime) + } + + cond := builder.Or( + // never online + builder.And(builder.Eq{"last_online": 0}, builder.Lt{"created": olderThan}), + // was online but offline + builder.And(builder.Gt{"last_online": 0}, builder.Lt{"last_online": olderThan}), + ) + + if globalOnly { + cond = builder.And(cond, builder.Eq{"owner_id": 0}, builder.Eq{"repo_id": 0}) + } + + if err := db.Iterate( + ctx, + cond, + func(ctx context.Context, r *ActionRunner) error { + if err := DeleteRunner(ctx, r); err != nil { + return fmt.Errorf("DeleteOfflineRunners: %w", err) + } + lastOnline := r.LastOnline.AsTime() + olderThanTime := olderThan.AsTime() + if !lastOnline.IsZero() && lastOnline.Before(olderThanTime) { + log.Info( + "Deleted runner [ID: %d, Name: %s], last online %s ago", + r.ID, r.Name, olderThanTime.Sub(lastOnline).String(), + ) + } else { + log.Info( + "Deleted runner [ID: %d, Name: %s], unused since %s ago", + r.ID, r.Name, olderThanTime.Sub(r.Created.AsTime()).String(), + ) + } + + return nil + }, + ); err != nil { + return err + } + + log.Info("Finished: DeleteOfflineRunners") + + return nil +} diff --git a/models/actions/runner_list.go b/models/actions/runner_list.go index 3ef8ebb254..6a64c46596 100644 --- a/models/actions/runner_list.go +++ b/models/actions/runner_list.go @@ -6,10 +6,10 @@ package actions import ( "context" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/container" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/container" ) type RunnerList []*ActionRunner diff --git a/models/actions/runner_test.go b/models/actions/runner_test.go index 2c8d430f94..1916c35a76 100644 --- a/models/actions/runner_test.go +++ b/models/actions/runner_test.go @@ -6,10 +6,12 @@ import ( "encoding/binary" "fmt" "testing" + "time" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/models/unittest" + "forgejo.org/modules/timeutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -73,3 +75,68 @@ func TestDeleteRunner(t *testing.T) { idAsBinary[6], idAsBinary[7]) assert.Equal(t, idAsHexadecimal, after.UUID[19:]) } + +func TestDeleteOfflineRunnersRunnerGlobalOnly(t *testing.T) { + baseTime := time.Date(2024, 5, 19, 7, 40, 32, 0, time.UTC) + timeutil.MockSet(baseTime) + defer timeutil.MockUnset() + + require.NoError(t, unittest.PrepareTestDatabase()) + + olderThan := timeutil.TimeStampNow().Add(-timeutil.Hour) + + require.NoError(t, DeleteOfflineRunners(db.DefaultContext, olderThan, true)) + + // create at test base time + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 12345678}) + // last_online test base time + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000001}) + // created one month ago but a repo + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000002}) + // last online one hour ago + unittest.AssertNotExistsBean(t, &ActionRunner{ID: 10000003}) + // last online 10 seconds ago + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000004}) + // created 1 month ago + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000005}) + // created 1 hour ago + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000006}) + // last online 1 hour ago + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000007}) +} + +func TestDeleteOfflineRunnersAll(t *testing.T) { + baseTime := time.Date(2024, 5, 19, 7, 40, 32, 0, time.UTC) + timeutil.MockSet(baseTime) + defer timeutil.MockUnset() + + require.NoError(t, unittest.PrepareTestDatabase()) + + olderThan := timeutil.TimeStampNow().Add(-timeutil.Hour) + + require.NoError(t, DeleteOfflineRunners(db.DefaultContext, olderThan, false)) + + // create at test base time + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 12345678}) + // last_online test base time + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000001}) + // created one month ago + unittest.AssertNotExistsBean(t, &ActionRunner{ID: 10000002}) + // last online one hour ago + unittest.AssertNotExistsBean(t, &ActionRunner{ID: 10000003}) + // last online 10 seconds ago + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000004}) + // created 1 month ago + unittest.AssertNotExistsBean(t, &ActionRunner{ID: 10000005}) + // created 1 hour ago + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000006}) + // last online 1 hour ago + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000007}) +} + +func TestDeleteOfflineRunnersErrorOnInvalidOlderThanValue(t *testing.T) { + baseTime := time.Date(2024, 5, 19, 7, 40, 32, 0, time.UTC) + timeutil.MockSet(baseTime) + defer timeutil.MockUnset() + require.Error(t, DeleteOfflineRunners(db.DefaultContext, timeutil.TimeStampNow(), false)) +} diff --git a/models/actions/runner_token.go b/models/actions/runner_token.go index fd6ba7ecad..a59304d8e8 100644 --- a/models/actions/runner_token.go +++ b/models/actions/runner_token.go @@ -7,11 +7,11 @@ import ( "context" "fmt" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" ) // ActionRunnerToken represents runner tokens diff --git a/models/actions/runner_token_test.go b/models/actions/runner_token_test.go index 35c9a9d3c3..0de9ca5648 100644 --- a/models/actions/runner_token_test.go +++ b/models/actions/runner_token_test.go @@ -6,8 +6,8 @@ package actions import ( "testing" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/db" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -18,7 +18,7 @@ func TestGetLatestRunnerToken(t *testing.T) { token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3}) expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) - assert.EqualValues(t, expectedToken, token) + assert.Equal(t, expectedToken, token) } func TestNewRunnerToken(t *testing.T) { @@ -27,7 +27,7 @@ func TestNewRunnerToken(t *testing.T) { require.NoError(t, err) expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) - assert.EqualValues(t, expectedToken, token) + assert.Equal(t, expectedToken, token) } func TestUpdateRunnerToken(t *testing.T) { @@ -37,5 +37,5 @@ func TestUpdateRunnerToken(t *testing.T) { require.NoError(t, UpdateRunnerToken(db.DefaultContext, token)) expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) - assert.EqualValues(t, expectedToken, token) + assert.Equal(t, expectedToken, token) } diff --git a/models/actions/schedule.go b/models/actions/schedule.go index 1ef9978185..cc68e9eafc 100644 --- a/models/actions/schedule.go +++ b/models/actions/schedule.go @@ -5,15 +5,14 @@ package actions import ( "context" - "fmt" "time" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" - webhook_module "code.gitea.io/gitea/modules/webhook" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" + webhook_module "forgejo.org/modules/webhook" "xorm.io/builder" ) @@ -119,27 +118,6 @@ func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error { return committer.Commit() } -func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository, cancelPreviousJobs bool) error { - // If actions disabled when there is schedule task, this will remove the outdated schedule tasks - // There is no other place we can do this because the app.ini will be changed manually - if err := DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil { - return fmt.Errorf("DeleteCronTaskByRepo: %v", err) - } - if cancelPreviousJobs { - // cancel running cron jobs of this repository and delete old schedules - if err := CancelPreviousJobs( - ctx, - repo.ID, - repo.DefaultBranch, - "", - webhook_module.HookEventSchedule, - ); err != nil { - return fmt.Errorf("CancelPreviousJobs: %v", err) - } - } - return nil -} - type FindScheduleOptions struct { db.ListOptions RepoID int64 diff --git a/models/actions/schedule_spec.go b/models/actions/schedule_spec.go index 923e5f7807..83bdceb850 100644 --- a/models/actions/schedule_spec.go +++ b/models/actions/schedule_spec.go @@ -8,9 +8,9 @@ import ( "strings" "time" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + "forgejo.org/modules/timeutil" "github.com/robfig/cron/v3" ) diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go index 78ba622dc3..0a09a60acb 100644 --- a/models/actions/schedule_spec_list.go +++ b/models/actions/schedule_spec_list.go @@ -6,9 +6,9 @@ package actions import ( "context" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/container" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + "forgejo.org/modules/container" "xorm.io/builder" ) diff --git a/models/actions/status.go b/models/actions/status.go index eda2234137..e42c221121 100644 --- a/models/actions/status.go +++ b/models/actions/status.go @@ -4,7 +4,7 @@ package actions import ( - "code.gitea.io/gitea/modules/translation" + "forgejo.org/modules/translation" runnerv1 "code.gitea.io/actions-proto-go/runner/v1" ) @@ -34,6 +34,15 @@ var statusNames = map[Status]string{ StatusBlocked: "blocked", } +var nameToStatus = make(map[string]Status, len(statusNames)) + +func init() { + // Populate name to status lookup map + for status, name := range statusNames { + nameToStatus[name] = status + } +} + // String returns the string name of the Status func (s Status) String() string { return statusNames[s] @@ -102,3 +111,8 @@ func (s Status) AsResult() runnerv1.Result { } return runnerv1.Result_RESULT_UNSPECIFIED } + +func StatusFromString(name string) (Status, bool) { + status, exists := nameToStatus[name] + return status, exists +} diff --git a/models/actions/task.go b/models/actions/task.go index 31655d2f1d..93369db7e8 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -9,18 +9,16 @@ import ( "fmt" "time" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/models/unit" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" - runnerv1 "code.gitea.io/actions-proto-go/runner/v1" lru "github.com/hashicorp/golang-lru/v2" "github.com/nektos/act/pkg/jobparser" - "google.golang.org/protobuf/types/known/timestamppb" "xorm.io/builder" ) @@ -313,7 +311,8 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask } job.TaskID = task.ID - if n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}); err != nil { + // We never have to send a notification here because the job is started with a not done status. + if n, err := UpdateRunJobWithoutNotification(ctx, job, builder.Eq{"task_id": 0}); err != nil { return nil, false, err } else if n != 1 { return nil, false, nil @@ -337,140 +336,6 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error { return err } -// UpdateTaskByState updates the task by the state. -// It will always update the task if the state is not final, even there is no change. -// So it will update ActionTask.Updated to avoid the task being judged as a zombie task. -func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.TaskState) (*ActionTask, error) { - stepStates := map[int64]*runnerv1.StepState{} - for _, v := range state.Steps { - stepStates[v.Id] = v - } - - ctx, commiter, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer commiter.Close() - - e := db.GetEngine(ctx) - - task := &ActionTask{} - if has, err := e.ID(state.Id).Get(task); err != nil { - return nil, err - } else if !has { - return nil, util.ErrNotExist - } else if runnerID != task.RunnerID { - return nil, fmt.Errorf("invalid runner for task") - } - - if task.Status.IsDone() { - // the state is final, do nothing - return task, nil - } - - // state.Result is not unspecified means the task is finished - if state.Result != runnerv1.Result_RESULT_UNSPECIFIED { - task.Status = Status(state.Result) - task.Stopped = timeutil.TimeStamp(state.StoppedAt.AsTime().Unix()) - if err := UpdateTask(ctx, task, "status", "stopped"); err != nil { - return nil, err - } - if _, err := UpdateRunJob(ctx, &ActionRunJob{ - ID: task.JobID, - Status: task.Status, - Stopped: task.Stopped, - }, nil); err != nil { - return nil, err - } - } else { - // Force update ActionTask.Updated to avoid the task being judged as a zombie task - task.Updated = timeutil.TimeStampNow() - if err := UpdateTask(ctx, task, "updated"); err != nil { - return nil, err - } - } - - if err := task.LoadAttributes(ctx); err != nil { - return nil, err - } - - for _, step := range task.Steps { - var result runnerv1.Result - if v, ok := stepStates[step.Index]; ok { - result = v.Result - step.LogIndex = v.LogIndex - step.LogLength = v.LogLength - step.Started = convertTimestamp(v.StartedAt) - step.Stopped = convertTimestamp(v.StoppedAt) - } - if result != runnerv1.Result_RESULT_UNSPECIFIED { - step.Status = Status(result) - } else if step.Started != 0 { - step.Status = StatusRunning - } - if _, err := e.ID(step.ID).Update(step); err != nil { - return nil, err - } - } - - if err := commiter.Commit(); err != nil { - return nil, err - } - - return task, nil -} - -func StopTask(ctx context.Context, taskID int64, status Status) error { - if !status.IsDone() { - return fmt.Errorf("cannot stop task with status %v", status) - } - e := db.GetEngine(ctx) - - task := &ActionTask{} - if has, err := e.ID(taskID).Get(task); err != nil { - return err - } else if !has { - return util.ErrNotExist - } - if task.Status.IsDone() { - return nil - } - - now := timeutil.TimeStampNow() - task.Status = status - task.Stopped = now - if _, err := UpdateRunJob(ctx, &ActionRunJob{ - ID: task.JobID, - Status: task.Status, - Stopped: task.Stopped, - }, nil); err != nil { - return err - } - - if err := UpdateTask(ctx, task, "status", "stopped"); err != nil { - return err - } - - if err := task.LoadAttributes(ctx); err != nil { - return err - } - - for _, step := range task.Steps { - if !step.Status.IsDone() { - step.Status = status - if step.Started == 0 { - step.Started = now - } - step.Stopped = now - } - if _, err := e.ID(step.ID).Update(step); err != nil { - return err - } - } - - return nil -} - func FindOldTasksToExpire(ctx context.Context, olderThan timeutil.TimeStamp, limit int) ([]*ActionTask, error) { e := db.GetEngine(ctx) @@ -481,13 +346,6 @@ func FindOldTasksToExpire(ctx context.Context, olderThan timeutil.TimeStamp, lim Find(&tasks) } -func convertTimestamp(timestamp *timestamppb.Timestamp) timeutil.TimeStamp { - if timestamp.GetSeconds() == 0 && timestamp.GetNanos() == 0 { - return timeutil.TimeStamp(0) - } - return timeutil.TimeStamp(timestamp.AsTime().Unix()) -} - func logFileName(repoFullName string, taskID int64) string { ret := fmt.Sprintf("%s/%02x/%d.log", repoFullName, taskID%256, taskID) diff --git a/models/actions/task_list.go b/models/actions/task_list.go index 502d29e1a3..fe4c028c2c 100644 --- a/models/actions/task_list.go +++ b/models/actions/task_list.go @@ -6,9 +6,9 @@ package actions import ( "context" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + "forgejo.org/modules/container" + "forgejo.org/modules/timeutil" "xorm.io/builder" ) diff --git a/models/actions/task_output.go b/models/actions/task_output.go index eab5b93118..fa13cadd53 100644 --- a/models/actions/task_output.go +++ b/models/actions/task_output.go @@ -6,7 +6,7 @@ package actions import ( "context" - "code.gitea.io/gitea/models/db" + "forgejo.org/models/db" ) // ActionTaskOutput represents an output of ActionTask. diff --git a/models/actions/task_step.go b/models/actions/task_step.go index 3af1fe3f5a..1f20157271 100644 --- a/models/actions/task_step.go +++ b/models/actions/task_step.go @@ -7,8 +7,8 @@ import ( "context" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + "forgejo.org/modules/timeutil" ) // ActionTaskStep represents a step of ActionTask diff --git a/models/actions/tasks_version.go b/models/actions/tasks_version.go index d8df353593..a5c357888f 100644 --- a/models/actions/tasks_version.go +++ b/models/actions/tasks_version.go @@ -6,9 +6,9 @@ package actions import ( "context" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" ) // ActionTasksVersion diff --git a/models/actions/utils.go b/models/actions/utils.go index 12657942fc..d8e053b0ba 100644 --- a/models/actions/utils.go +++ b/models/actions/utils.go @@ -12,9 +12,9 @@ import ( "io" "time" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + auth_model "forgejo.org/models/auth" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" ) func generateSaltedToken() (string, string, string, string, error) { @@ -22,11 +22,7 @@ func generateSaltedToken() (string, string, string, string, error) { if err != nil { return "", "", "", "", err } - buf, err := util.CryptoRandomBytes(20) - if err != nil { - return "", "", "", "", err - } - token := hex.EncodeToString(buf) + token := hex.EncodeToString(util.CryptoRandomBytes(20)) hash := auth_model.HashToken(token, salt) return token, salt, hash, token[len(token)-8:], nil } diff --git a/models/actions/utils_test.go b/models/actions/utils_test.go index 98c048d4ef..af6fd04a6a 100644 --- a/models/actions/utils_test.go +++ b/models/actions/utils_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/timeutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/actions/variable.go b/models/actions/variable.go index 39cea95c4b..203065487c 100644 --- a/models/actions/variable.go +++ b/models/actions/variable.go @@ -7,9 +7,9 @@ import ( "context" "strings" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" "xorm.io/builder" ) diff --git a/models/activities/action.go b/models/activities/action.go index dd67b98242..f928ad6784 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -1,11 +1,13 @@ // Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package activities import ( "context" + "errors" "fmt" "net/url" "path" @@ -14,20 +16,20 @@ import ( "strings" "time" - "code.gitea.io/gitea/models/db" - issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/models/organization" - access_model "code.gitea.io/gitea/models/perm/access" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unit" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + "forgejo.org/models/organization" + access_model "forgejo.org/models/perm/access" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unit" + user_model "forgejo.org/models/user" + "forgejo.org/modules/base" + "forgejo.org/modules/container" + "forgejo.org/modules/git" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/structs" + "forgejo.org/modules/timeutil" "xorm.io/builder" "xorm.io/xorm/schemas" @@ -247,6 +249,12 @@ func (a *Action) GetActDisplayNameTitle(ctx context.Context) string { return a.GetActFullName(ctx) } +// GetRepo returns the repository of the action. +func (a *Action) GetRepo(ctx context.Context) *repo_model.Repository { + a.loadRepo(ctx) + return a.Repo +} + // GetRepoUserName returns the name of the action repository owner. func (a *Action) GetRepoUserName(ctx context.Context) string { a.loadRepo(ctx) @@ -434,6 +442,12 @@ func (a *Action) GetIssueContent(ctx context.Context) string { return a.Issue.Content } +func GetActivityByID(ctx context.Context, id int64) (*Action, error) { + var act Action + _, err := db.GetEngine(ctx).ID(id).Get(&act) + return &act, err +} + // GetFeedsOptions options for retrieving feeds type GetFeedsOptions struct { db.ListOptions @@ -451,7 +465,7 @@ type GetFeedsOptions struct { // GetFeeds returns actions according to the provided options func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) { if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil { - return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo") + return nil, 0, errors.New("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo") } cond, err := activityQueryCondition(ctx, opts) @@ -590,13 +604,14 @@ func DeleteOldActions(ctx context.Context, olderThan time.Duration) (err error) } // NotifyWatchers creates batch of actions for every watcher. -func NotifyWatchers(ctx context.Context, actions ...*Action) error { +func NotifyWatchers(ctx context.Context, actions ...*Action) ([]Action, error) { var watchers []*repo_model.Watch var repo *repo_model.Repository var err error var permCode []bool var permIssue []bool var permPR []bool + var out []Action e := db.GetEngine(ctx) @@ -607,14 +622,14 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { // Add feeds for user self and all watchers. watchers, err = repo_model.GetWatchers(ctx, act.RepoID) if err != nil { - return fmt.Errorf("get watchers: %w", err) + return nil, fmt.Errorf("get watchers: %w", err) } // Be aware that optimizing this correctly into the `GetWatchers` SQL // query is for most cases less performant than doing this. blockedDoerUserIDs, err := user_model.ListBlockedByUsersID(ctx, act.ActUserID) if err != nil { - return fmt.Errorf("user_model.ListBlockedByUsersID: %w", err) + return nil, fmt.Errorf("user_model.ListBlockedByUsersID: %w", err) } if len(blockedDoerUserIDs) > 0 { @@ -629,8 +644,9 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { // Add feed for actioner. act.UserID = act.ActUserID if _, err = e.Insert(act); err != nil { - return fmt.Errorf("insert new actioner: %w", err) + return nil, fmt.Errorf("insert new actioner: %w", err) } + out = append(out, *act) if repoChanged { act.loadRepo(ctx) @@ -638,7 +654,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { // check repo owner exist. if err := act.Repo.LoadOwner(ctx); err != nil { - return fmt.Errorf("can't get repo owner: %w", err) + return nil, fmt.Errorf("can't get repo owner: %w", err) } } else if act.Repo == nil { act.Repo = repo @@ -649,7 +665,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { act.ID = 0 act.UserID = act.Repo.Owner.ID if err = db.Insert(ctx, act); err != nil { - return fmt.Errorf("insert new actioner: %w", err) + return nil, fmt.Errorf("insert new actioner: %w", err) } } @@ -702,26 +718,29 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error { } if err = db.Insert(ctx, act); err != nil { - return fmt.Errorf("insert new action: %w", err) + return nil, fmt.Errorf("insert new action: %w", err) } } } - return nil + return out, nil } // NotifyWatchersActions creates batch of actions for every watcher. -func NotifyWatchersActions(ctx context.Context, acts []*Action) error { +func NotifyWatchersActions(ctx context.Context, acts []*Action) ([]Action, error) { ctx, committer, err := db.TxContext(ctx) if err != nil { - return err + return nil, err } defer committer.Close() + var out []Action for _, act := range acts { - if err := NotifyWatchers(ctx, act); err != nil { - return err + as, err := NotifyWatchers(ctx, act) + if err != nil { + return nil, err } + out = append(out, as...) } - return committer.Commit() + return out, committer.Commit() } // DeleteIssueActions delete all actions related with issueID diff --git a/models/activities/action_list.go b/models/activities/action_list.go index aafb7f8a26..64b92bbda1 100644 --- a/models/activities/action_list.go +++ b/models/activities/action_list.go @@ -8,12 +8,12 @@ import ( "fmt" "strconv" - "code.gitea.io/gitea/models/db" - issues_model "code.gitea.io/gitea/models/issues" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/container" + "forgejo.org/modules/util" "xorm.io/builder" ) diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 4ce030dd48..161d05bbfa 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -8,13 +8,13 @@ import ( "path" "testing" - activities_model "code.gitea.io/gitea/models/activities" - "code.gitea.io/gitea/models/db" - issue_model "code.gitea.io/gitea/models/issues" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" + activities_model "forgejo.org/models/activities" + "forgejo.org/models/db" + issue_model "forgejo.org/models/issues" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -56,7 +56,7 @@ func TestGetFeeds(t *testing.T) { require.NoError(t, err) if assert.Len(t, actions, 1) { assert.EqualValues(t, 1, actions[0].ID) - assert.EqualValues(t, user.ID, actions[0].UserID) + assert.Equal(t, user.ID, actions[0].UserID) } assert.Equal(t, int64(1), count) @@ -133,7 +133,7 @@ func TestGetFeeds2(t *testing.T) { assert.Len(t, actions, 1) if assert.Len(t, actions, 1) { assert.EqualValues(t, 2, actions[0].ID) - assert.EqualValues(t, org.ID, actions[0].UserID) + assert.Equal(t, org.ID, actions[0].UserID) } assert.Equal(t, int64(1), count) @@ -197,7 +197,8 @@ func TestNotifyWatchers(t *testing.T) { RepoID: 1, OpType: activities_model.ActionStarRepo, } - require.NoError(t, activities_model.NotifyWatchers(db.DefaultContext, action)) + _, err := activities_model.NotifyWatchers(db.DefaultContext, action) + require.NoError(t, err) // One watchers are inactive, thus action is only created for user 8, 1, 4, 11 unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ @@ -291,7 +292,7 @@ func TestDeleteIssueActions(t *testing.T) { // load an issue issue := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 4}) - assert.NotEqualValues(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex + assert.NotEqual(t, issue.ID, issue.Index) // it needs to use different ID/Index to test the DeleteIssueActions to delete some actions by IssueIndex // insert a comment err := db.Insert(db.DefaultContext, &issue_model.Comment{Type: issue_model.CommentTypeComment, IssueID: issue.ID}) diff --git a/models/activities/federated_user_activity.go b/models/activities/federated_user_activity.go new file mode 100644 index 0000000000..1ff3a855d0 --- /dev/null +++ b/models/activities/federated_user_activity.go @@ -0,0 +1,106 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package activities + +import ( + "context" + "fmt" + + "forgejo.org/models/db" + user_model "forgejo.org/models/user" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/validation" + + ap "github.com/go-ap/activitypub" +) + +type FederatedUserActivity struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"NOT NULL"` + ActorID int64 + ActorURI string + Actor *user_model.User `xorm:"-"` // transient + NoteContent string `xorm:"TEXT"` + NoteURL string `xorm:"VARCHAR(255)"` + OriginalNote string `xorm:"TEXT"` + Created timeutil.TimeStamp `xorm:"created"` +} + +func init() { + db.RegisterModel(new(FederatedUserActivity)) +} + +func NewFederatedUserActivity(userID, actorID int64, actorURI, noteContent, noteURL string, originalNote ap.Activity) (FederatedUserActivity, error) { + jsonString, err := json.Marshal(originalNote) + if err != nil { + return FederatedUserActivity{}, err + } + result := FederatedUserActivity{ + UserID: userID, + ActorID: actorID, + ActorURI: actorURI, + NoteContent: noteContent, + NoteURL: noteURL, + OriginalNote: string(jsonString), + } + if valid, err := validation.IsValid(result); !valid { + return FederatedUserActivity{}, err + } + return result, nil +} + +func (federatedUserActivity FederatedUserActivity) Validate() []string { + var result []string + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.UserID, "UserID")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.ActorID, "ActorID")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.ActorURI, "ActorURI")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.NoteContent, "NoteContent")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.NoteURL, "NoteURL")...) + result = append(result, validation.ValidateNotEmpty(federatedUserActivity.OriginalNote, "OriginalNote")...) + return result +} + +func CreateUserActivity(ctx context.Context, federatedUserActivity *FederatedUserActivity) error { + if valid, err := validation.IsValid(federatedUserActivity); !valid { + return err + } + _, err := db.GetEngine(ctx).Insert(federatedUserActivity) + return err +} + +type GetFollowingFeedsOptions struct { + db.ListOptions +} + +func GetFollowingFeeds(ctx context.Context, actorID int64, opts GetFollowingFeedsOptions) ([]*FederatedUserActivity, int64, error) { + log.Debug("user_id = %s", actorID) + sess := db.GetEngine(ctx).Where("user_id = ?", actorID) + opts.SetDefaultValues() + sess = db.SetSessionPagination(sess, &opts) + + actions := make([]*FederatedUserActivity, 0, opts.PageSize) + count, err := sess.FindAndCount(&actions) + if err != nil { + return nil, 0, fmt.Errorf("FindAndCount: %w", err) + } + for _, act := range actions { + if err := act.loadActor(ctx); err != nil { + return nil, 0, err + } + } + return actions, count, err +} + +func (federatedUserActivity *FederatedUserActivity) loadActor(ctx context.Context) error { + log.Debug("for activity %s", federatedUserActivity) + actorUser, _, err := user_model.GetFederatedUserByUserID(ctx, federatedUserActivity.ActorID) + if err != nil { + return err + } + federatedUserActivity.Actor = actorUser + + return nil +} diff --git a/models/activities/federated_user_activity_test.go b/models/activities/federated_user_activity_test.go new file mode 100644 index 0000000000..9bf4f77984 --- /dev/null +++ b/models/activities/federated_user_activity_test.go @@ -0,0 +1,24 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package activities + +import ( + "testing" + + "forgejo.org/modules/validation" +) + +func Test_FederatedUserActivityValidation(t *testing.T) { + sut := FederatedUserActivity{} + sut.UserID = 13 + sut.ActorID = 33 + sut.ActorURI = "33" + sut.NoteContent = "Any content!" + sut.NoteURL = "https://example.org/note/17" + sut.OriginalNote = "federatedUserActivityNote-17" + + if res, _ := validation.IsValid(sut); !res { + t.Errorf("sut expected to be valid: %v\n", sut.Validate()) + } +} diff --git a/models/activities/main_test.go b/models/activities/main_test.go index bda5593d02..a5245ab1d3 100644 --- a/models/activities/main_test.go +++ b/models/activities/main_test.go @@ -6,11 +6,11 @@ package activities_test import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" - _ "code.gitea.io/gitea/models" - _ "code.gitea.io/gitea/models/actions" - _ "code.gitea.io/gitea/models/forgefed" + _ "forgejo.org/models" + _ "forgejo.org/models/actions" + _ "forgejo.org/models/forgefed" ) func TestMain(m *testing.M) { diff --git a/models/activities/notification.go b/models/activities/notification.go index 09cc640224..4d13900459 100644 --- a/models/activities/notification.go +++ b/models/activities/notification.go @@ -9,14 +9,14 @@ import ( "net/url" "strconv" - "code.gitea.io/gitea/models/db" - issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/models/organization" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + "forgejo.org/models/organization" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "xorm.io/builder" ) diff --git a/models/activities/notification_list.go b/models/activities/notification_list.go index 32d2a5c051..9b266f5d96 100644 --- a/models/activities/notification_list.go +++ b/models/activities/notification_list.go @@ -6,14 +6,14 @@ package activities import ( "context" - "code.gitea.io/gitea/models/db" - issues_model "code.gitea.io/gitea/models/issues" - access_model "code.gitea.io/gitea/models/perm/access" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unit" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/log" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + access_model "forgejo.org/models/perm/access" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unit" + user_model "forgejo.org/models/user" + "forgejo.org/modules/container" + "forgejo.org/modules/log" "xorm.io/builder" ) @@ -107,7 +107,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n return err } toNotify.AddMultiple(issueWatches...) - if !(issue.IsPull && issues_model.HasWorkInProgressPrefix(issue.Title)) { + if !issue.IsPull || !issues_model.HasWorkInProgressPrefix(issue.Title) { repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID) if err != nil { return err diff --git a/models/activities/notification_test.go b/models/activities/notification_test.go index 3ff223d870..dfce92d491 100644 --- a/models/activities/notification_test.go +++ b/models/activities/notification_test.go @@ -7,11 +7,11 @@ import ( "context" "testing" - activities_model "code.gitea.io/gitea/models/activities" - "code.gitea.io/gitea/models/db" - issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" + activities_model "forgejo.org/models/activities" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -45,11 +45,11 @@ func TestNotificationsForUser(t *testing.T) { require.NoError(t, err) if assert.Len(t, notfs, 3) { assert.EqualValues(t, 5, notfs[0].ID) - assert.EqualValues(t, user.ID, notfs[0].UserID) + assert.Equal(t, user.ID, notfs[0].UserID) assert.EqualValues(t, 4, notfs[1].ID) - assert.EqualValues(t, user.ID, notfs[1].UserID) + assert.Equal(t, user.ID, notfs[1].UserID) assert.EqualValues(t, 2, notfs[2].ID) - assert.EqualValues(t, user.ID, notfs[2].UserID) + assert.Equal(t, user.ID, notfs[2].UserID) } } @@ -59,7 +59,7 @@ func TestNotification_GetRepo(t *testing.T) { repo, err := notf.GetRepo(db.DefaultContext) require.NoError(t, err) assert.Equal(t, repo, notf.Repository) - assert.EqualValues(t, notf.RepoID, repo.ID) + assert.Equal(t, notf.RepoID, repo.ID) } func TestNotification_GetIssue(t *testing.T) { @@ -68,7 +68,7 @@ func TestNotification_GetIssue(t *testing.T) { issue, err := notf.GetIssue(db.DefaultContext) require.NoError(t, err) assert.Equal(t, issue, notf.Issue) - assert.EqualValues(t, notf.IssueID, issue.ID) + assert.Equal(t, notf.IssueID, issue.ID) } func TestGetNotificationCount(t *testing.T) { @@ -137,5 +137,5 @@ func TestSetIssueReadBy(t *testing.T) { nt, err := activities_model.GetIssueNotification(db.DefaultContext, user.ID, issue.ID) require.NoError(t, err) - assert.EqualValues(t, activities_model.NotificationStatusRead, nt.Status) + assert.Equal(t, activities_model.NotificationStatusRead, nt.Status) } diff --git a/models/activities/repo_activity.go b/models/activities/repo_activity.go index ffa709ad19..3d15c22e19 100644 --- a/models/activities/repo_activity.go +++ b/models/activities/repo_activity.go @@ -9,12 +9,12 @@ import ( "sort" "time" - "code.gitea.io/gitea/models/db" - issues_model "code.gitea.io/gitea/models/issues" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/gitrepo" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/git" + "forgejo.org/modules/gitrepo" "xorm.io/xorm" ) diff --git a/models/activities/repo_activity_test.go b/models/activities/repo_activity_test.go index 06cd0e1e8a..ce930ada2d 100644 --- a/models/activities/repo_activity_test.go +++ b/models/activities/repo_activity_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -23,8 +23,8 @@ func TestGetActivityStats(t *testing.T) { stats, err := GetActivityStats(db.DefaultContext, repo, time.Unix(0, 0), true, true, true, true) require.NoError(t, err) - assert.EqualValues(t, 2, stats.ActiveIssueCount()) - assert.EqualValues(t, 2, stats.OpenedIssueCount()) - assert.EqualValues(t, 0, stats.ClosedIssueCount()) - assert.EqualValues(t, 3, stats.ActivePRCount()) + assert.Equal(t, 2, stats.ActiveIssueCount()) + assert.Equal(t, 2, stats.OpenedIssueCount()) + assert.Equal(t, 0, stats.ClosedIssueCount()) + assert.Equal(t, 3, stats.ActivePRCount()) } diff --git a/models/activities/statistic.go b/models/activities/statistic.go index ff81ad78a1..4c15cb2898 100644 --- a/models/activities/statistic.go +++ b/models/activities/statistic.go @@ -6,18 +6,18 @@ package activities import ( "context" - asymkey_model "code.gitea.io/gitea/models/asymkey" - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - git_model "code.gitea.io/gitea/models/git" - issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/models/organization" - access_model "code.gitea.io/gitea/models/perm/access" - project_model "code.gitea.io/gitea/models/project" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/models/webhook" - "code.gitea.io/gitea/modules/setting" + asymkey_model "forgejo.org/models/asymkey" + "forgejo.org/models/auth" + "forgejo.org/models/db" + git_model "forgejo.org/models/git" + issues_model "forgejo.org/models/issues" + "forgejo.org/models/organization" + access_model "forgejo.org/models/perm/access" + project_model "forgejo.org/models/project" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/models/webhook" + "forgejo.org/modules/setting" ) // Statistic contains the database statistics diff --git a/models/activities/user_heatmap.go b/models/activities/user_heatmap.go index 080075d793..11badb77e2 100644 --- a/models/activities/user_heatmap.go +++ b/models/activities/user_heatmap.go @@ -6,11 +6,17 @@ package activities import ( "context" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/organization" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + "forgejo.org/models/organization" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" + "forgejo.org/modules/timeutil" +) + +const ( + // contributionsMaxAgeSeconds How old data to retrieve for the heatmap. + // 371 days to cover the entire heatmap (53 *full* weeks) + contributionsMaxAgeSeconds = 32054400 ) // UserHeatmapData represents the data needed to create a heatmap @@ -62,7 +68,7 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi Select(groupBy+" AS timestamp, count(user_id) as contributions"). Table("action"). Where(cond). - And("created_unix > ?", timeutil.TimeStampNow()-31536000). + And("created_unix >= ?", timeutil.TimeStampNow()-contributionsMaxAgeSeconds). GroupBy("timestamp"). OrderBy("timestamp"). Find(&hdata) diff --git a/models/activities/user_heatmap_test.go b/models/activities/user_heatmap_test.go index d62565a27c..34308cb3d4 100644 --- a/models/activities/user_heatmap_test.go +++ b/models/activities/user_heatmap_test.go @@ -7,12 +7,12 @@ import ( "testing" "time" - activities_model "code.gitea.io/gitea/models/activities" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/timeutil" + activities_model "forgejo.org/models/activities" + "forgejo.org/models/db" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/json" + "forgejo.org/modules/timeutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -54,6 +54,10 @@ func TestGetUserHeatmapDataByUser(t *testing.T) { "multiple actions performed with two grouped together", 10, 10, 3, `[{"timestamp":1603009800,"contributions":1},{"timestamp":1603010700,"contributions":2}]`, }, + { + "test cutoff within", + 40, 40, 1, `[{"timestamp":1577404800,"contributions":1}]`, + }, } // Prepare require.NoError(t, unittest.PrepareTestDatabase()) diff --git a/models/admin/task.go b/models/admin/task.go index 5c2f7af49c..b4e1ac0134 100644 --- a/models/admin/task.go +++ b/models/admin/task.go @@ -7,16 +7,16 @@ import ( "context" "fmt" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/migration" - "code.gitea.io/gitea/modules/secret" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/json" + "forgejo.org/modules/migration" + "forgejo.org/modules/secret" + "forgejo.org/modules/setting" + "forgejo.org/modules/structs" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" ) // Task represents a task diff --git a/models/asymkey/asymkey.go b/models/asymkey/asymkey.go new file mode 100644 index 0000000000..1f04e8dada --- /dev/null +++ b/models/asymkey/asymkey.go @@ -0,0 +1,29 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later +package asymkey + +import ( + "context" + + "forgejo.org/models/db" +) + +// HasAsymKeyByUID returns true if the user has a GPG key or SSH key associated +// with its account. +func HasAsymKeyByUID(ctx context.Context, userID int64) (bool, error) { + hasGPGKey, err := db.Exist[GPGKey](ctx, FindGPGKeyOptions{ + OwnerID: userID, + IncludeSubKeys: true, + }.ToConds()) + if err != nil { + return false, err + } + if hasGPGKey { + return true, nil + } + + return db.Exist[PublicKey](ctx, FindPublicKeyOptions{ + OwnerID: userID, + KeyTypes: []KeyType{KeyTypeUser}, + }.ToConds()) +} diff --git a/models/asymkey/asymkey_test.go b/models/asymkey/asymkey_test.go new file mode 100644 index 0000000000..1eeb593e40 --- /dev/null +++ b/models/asymkey/asymkey_test.go @@ -0,0 +1,34 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later +package asymkey + +import ( + "testing" + + "forgejo.org/models/unittest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUserHasAsymKey(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("No key", func(t *testing.T) { + ok, err := HasAsymKeyByUID(t.Context(), 1) + require.NoError(t, err) + assert.False(t, ok) + }) + + t.Run("SSH key", func(t *testing.T) { + ok, err := HasAsymKeyByUID(t.Context(), 2) + require.NoError(t, err) + assert.True(t, ok) + }) + + t.Run("GPG key", func(t *testing.T) { + ok, err := HasAsymKeyByUID(t.Context(), 36) + require.NoError(t, err) + assert.True(t, ok) + }) +} diff --git a/models/asymkey/error.go b/models/asymkey/error.go index e38ba121c6..fc0dd88232 100644 --- a/models/asymkey/error.go +++ b/models/asymkey/error.go @@ -6,7 +6,7 @@ package asymkey import ( "fmt" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) // ErrKeyUnableVerify represents a "KeyUnableVerify" kind of error. diff --git a/models/asymkey/gpg_key.go b/models/asymkey/gpg_key.go index 6e2914e476..64866da076 100644 --- a/models/asymkey/gpg_key.go +++ b/models/asymkey/gpg_key.go @@ -5,13 +5,14 @@ package asymkey import ( "context" + "errors" "fmt" "strings" "time" - "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + user_model "forgejo.org/models/user" + "forgejo.org/modules/timeutil" "github.com/ProtonMail/go-crypto/openpgp" "github.com/ProtonMail/go-crypto/openpgp/packet" @@ -209,7 +210,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified // deleteGPGKey does the actual key deletion func deleteGPGKey(ctx context.Context, keyID string) (int64, error) { if keyID == "" { - return 0, fmt.Errorf("empty KeyId forbidden") // Should never happen but just to be sure + return 0, errors.New("empty KeyId forbidden") // Should never happen but just to be sure } // Delete imported key n, err := db.GetEngine(ctx).Where("key_id=?", keyID).Delete(new(GPGKeyImport)) diff --git a/models/asymkey/gpg_key_add.go b/models/asymkey/gpg_key_add.go index 6c0f6e01a7..06cfd09a3e 100644 --- a/models/asymkey/gpg_key_add.go +++ b/models/asymkey/gpg_key_add.go @@ -7,8 +7,8 @@ import ( "context" "strings" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" + "forgejo.org/models/db" + "forgejo.org/modules/log" "github.com/ProtonMail/go-crypto/openpgp" ) diff --git a/models/asymkey/gpg_key_commit_verification.go b/models/asymkey/gpg_key_commit_verification.go index 9aa606405e..73b066b17c 100644 --- a/models/asymkey/gpg_key_commit_verification.go +++ b/models/asymkey/gpg_key_commit_verification.go @@ -6,9 +6,9 @@ package asymkey import ( "context" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/git" ) // __________________ ________ ____ __. diff --git a/models/asymkey/gpg_key_common.go b/models/asymkey/gpg_key_common.go index db1912c316..5b8a22fe63 100644 --- a/models/asymkey/gpg_key_common.go +++ b/models/asymkey/gpg_key_common.go @@ -7,6 +7,7 @@ import ( "bytes" "crypto" "encoding/base64" + "errors" "fmt" "hash" "io" @@ -75,7 +76,7 @@ func base64DecPubKey(content string) (*packet.PublicKey, error) { // Check type pkey, ok := p.(*packet.PublicKey) if !ok { - return nil, fmt.Errorf("key is not a public key") + return nil, errors.New("key is not a public key") } return pkey, nil } @@ -122,15 +123,15 @@ func readArmoredSign(r io.Reader) (body io.Reader, err error) { func extractSignature(s string) (*packet.Signature, error) { r, err := readArmoredSign(strings.NewReader(s)) if err != nil { - return nil, fmt.Errorf("Failed to read signature armor") + return nil, errors.New("Failed to read signature armor") } p, err := packet.Read(r) if err != nil { - return nil, fmt.Errorf("Failed to read signature packet") + return nil, errors.New("Failed to read signature packet") } sig, ok := p.(*packet.Signature) if !ok { - return nil, fmt.Errorf("Packet is not a signature") + return nil, errors.New("Packet is not a signature") } return sig, nil } diff --git a/models/asymkey/gpg_key_import.go b/models/asymkey/gpg_key_import.go index c9d46d29e5..8a63ea4a35 100644 --- a/models/asymkey/gpg_key_import.go +++ b/models/asymkey/gpg_key_import.go @@ -6,7 +6,7 @@ package asymkey import ( "context" - "code.gitea.io/gitea/models/db" + "forgejo.org/models/db" ) // __________________ ________ ____ __. diff --git a/models/asymkey/gpg_key_list.go b/models/asymkey/gpg_key_list.go index 89548e495e..b2d4fb11f6 100644 --- a/models/asymkey/gpg_key_list.go +++ b/models/asymkey/gpg_key_list.go @@ -6,7 +6,7 @@ package asymkey import ( "context" - "code.gitea.io/gitea/models/db" + "forgejo.org/models/db" ) type GPGKeyList []*GPGKey diff --git a/models/asymkey/gpg_key_object_verification.go b/models/asymkey/gpg_key_object_verification.go index 24d72a52c1..745ed04869 100644 --- a/models/asymkey/gpg_key_object_verification.go +++ b/models/asymkey/gpg_key_object_verification.go @@ -6,16 +6,17 @@ package asymkey import ( "context" + "errors" "fmt" "hash" "strings" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/git" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "github.com/ProtonMail/go-crypto/openpgp/packet" ) @@ -44,6 +45,8 @@ const ( BadDefaultSignature = "gpg.error.probable_bad_default_signature" // NoKeyFound is used as the reason when no key can be found to verify the signature. NoKeyFound = "gpg.error.no_gpg_keys_found" + // NotSigned is used as the reason when the commit is not signed. + NotSigned = "gpg.error.not_signed_commit" ) type GitObject struct { @@ -101,8 +104,8 @@ func ParseObjectWithSignature(ctx context.Context, c *GitObject) *ObjectVerifica if c.Signature == nil { return &ObjectVerification{ CommittingUser: committer, - Verified: false, // Default value - Reason: "gpg.error.not_signed_commit", // Default value + Verified: false, // Default value + Reason: NotSigned, // Default value } } @@ -201,7 +204,7 @@ func ParseObjectWithSignature(ctx context.Context, c *GitObject) *ObjectVerifica } } - if setting.Repository.Signing.SigningKey != "" && setting.Repository.Signing.SigningKey != "default" && setting.Repository.Signing.SigningKey != "none" { + if setting.Repository.Signing.Format == "openpgp" && setting.Repository.Signing.SigningKey != "" && setting.Repository.Signing.SigningKey != "default" && setting.Repository.Signing.SigningKey != "none" { // OK we should try the default key gpgSettings := git.GPGSettings{ Sign: true, @@ -314,7 +317,7 @@ func verifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, si func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error { // Check if key can sign if !k.CanSign { - return fmt.Errorf("key can not sign") + return errors.New("key can not sign") } // Decode key pkey, err := base64DecPubKey(k.Content) diff --git a/models/asymkey/gpg_key_tag_verification.go b/models/asymkey/gpg_key_tag_verification.go index 5fd3983e54..f054525e8f 100644 --- a/models/asymkey/gpg_key_tag_verification.go +++ b/models/asymkey/gpg_key_tag_verification.go @@ -6,7 +6,7 @@ package asymkey import ( "context" - "code.gitea.io/gitea/modules/git" + "forgejo.org/modules/git" ) func ParseTagWithSignature(ctx context.Context, gitRepo *git.Repository, t *git.Tag) *ObjectVerification { diff --git a/models/asymkey/gpg_key_test.go b/models/asymkey/gpg_key_test.go index e9aa9cf5ec..d265f438eb 100644 --- a/models/asymkey/gpg_key_test.go +++ b/models/asymkey/gpg_key_test.go @@ -7,11 +7,11 @@ import ( "testing" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "github.com/ProtonMail/go-crypto/openpgp/packet" "github.com/stretchr/testify/assert" @@ -293,7 +293,7 @@ heiQvzkApQup5c+BhH5zFDFdKJ2CBByxw9+7QjMFI/wgLixKuE0Ob2kAokXf7RlB require.NoError(t, err) assert.Len(t, keys, 1) assert.Len(t, keys[0].Emails, 1) - assert.EqualValues(t, "no-reply@golang.com", keys[0].Emails[0].Email) + assert.Equal(t, "no-reply@golang.com", keys[0].Emails[0].Email) primaryKeyID := "D68172F48E9C5283" // Assert primary key diff --git a/models/asymkey/gpg_key_verify.go b/models/asymkey/gpg_key_verify.go index 01812a2d54..2b5ea7a1ac 100644 --- a/models/asymkey/gpg_key_verify.go +++ b/models/asymkey/gpg_key_verify.go @@ -8,10 +8,10 @@ import ( "strconv" "time" - "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/log" + "forgejo.org/models/db" + user_model "forgejo.org/models/user" + "forgejo.org/modules/base" + "forgejo.org/modules/log" ) // __________________ ________ ____ __. diff --git a/models/asymkey/main_test.go b/models/asymkey/main_test.go index 87b5c22c4a..316e8f1d54 100644 --- a/models/asymkey/main_test.go +++ b/models/asymkey/main_test.go @@ -6,7 +6,7 @@ package asymkey import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" ) func TestMain(m *testing.M) { diff --git a/models/asymkey/ssh_key.go b/models/asymkey/ssh_key.go index 7a18732c32..7f76009e7f 100644 --- a/models/asymkey/ssh_key.go +++ b/models/asymkey/ssh_key.go @@ -10,13 +10,13 @@ import ( "strings" "time" - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/perm" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/models/perm" + user_model "forgejo.org/models/user" + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "golang.org/x/crypto/ssh" "xorm.io/builder" diff --git a/models/asymkey/ssh_key_authorized_keys.go b/models/asymkey/ssh_key_authorized_keys.go index 57baf89c0c..d3bf6fe886 100644 --- a/models/asymkey/ssh_key_authorized_keys.go +++ b/models/asymkey/ssh_key_authorized_keys.go @@ -14,10 +14,10 @@ import ( "sync" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) // _____ __ .__ .__ .___ diff --git a/models/asymkey/ssh_key_authorized_principals.go b/models/asymkey/ssh_key_authorized_principals.go index f85de12aae..0b4fe13ba7 100644 --- a/models/asymkey/ssh_key_authorized_principals.go +++ b/models/asymkey/ssh_key_authorized_principals.go @@ -13,10 +13,10 @@ import ( "strings" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) // _____ __ .__ .__ .___ diff --git a/models/asymkey/ssh_key_deploy.go b/models/asymkey/ssh_key_deploy.go index 429635330d..22e80840af 100644 --- a/models/asymkey/ssh_key_deploy.go +++ b/models/asymkey/ssh_key_deploy.go @@ -8,9 +8,9 @@ import ( "fmt" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/perm" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + "forgejo.org/models/perm" + "forgejo.org/modules/timeutil" "xorm.io/builder" ) diff --git a/models/asymkey/ssh_key_fingerprint.go b/models/asymkey/ssh_key_fingerprint.go index 1ed3b5df2a..11112e4bc3 100644 --- a/models/asymkey/ssh_key_fingerprint.go +++ b/models/asymkey/ssh_key_fingerprint.go @@ -8,11 +8,11 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/process" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/process" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "golang.org/x/crypto/ssh" "xorm.io/builder" diff --git a/models/asymkey/ssh_key_object_verification.go b/models/asymkey/ssh_key_object_verification.go index 5ad6fdb0a9..5d933d35f9 100644 --- a/models/asymkey/ssh_key_object_verification.go +++ b/models/asymkey/ssh_key_object_verification.go @@ -9,11 +9,13 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" + "forgejo.org/models/db" + user_model "forgejo.org/models/user" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "github.com/42wim/sshsig" + "golang.org/x/crypto/ssh" ) // ParseObjectWithSSHSignature check if signature is good against keystore. @@ -62,6 +64,22 @@ func ParseObjectWithSSHSignature(ctx context.Context, c *GitObject, committer *u } } + // If the SSH instance key is set, try to verify it with that key. + if setting.SSHInstanceKey != nil { + instanceSSHKey := &PublicKey{ + Content: string(ssh.MarshalAuthorizedKey(setting.SSHInstanceKey)), + Fingerprint: ssh.FingerprintSHA256(setting.SSHInstanceKey), + } + instanceUser := &user_model.User{ + Name: setting.Repository.Signing.SigningName, + Email: setting.Repository.Signing.SigningEmail, + } + commitVerification := verifySSHObjectVerification(c.Signature.Signature, c.Signature.Payload, instanceSSHKey, committer, instanceUser, setting.Repository.Signing.SigningEmail) + if commitVerification != nil { + return commitVerification + } + } + return &ObjectVerification{ CommittingUser: committer, Verified: false, diff --git a/models/asymkey/ssh_key_object_verification_test.go b/models/asymkey/ssh_key_object_verification_test.go index 0d5ebabb70..4bfd79d56e 100644 --- a/models/asymkey/ssh_key_object_verification_test.go +++ b/models/asymkey/ssh_key_object_verification_test.go @@ -4,17 +4,19 @@ package asymkey import ( + "os" "testing" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" + "forgejo.org/models/db" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/git" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/crypto/ssh" ) func TestParseCommitWithSSHSignature(t *testing.T) { @@ -150,4 +152,43 @@ muPLbvEduU+Ze/1Ol1pgk= assert.Equal(t, "user2 / SHA256:TKfwbZMR7e9OnlV2l1prfah1TXH8CmqR0PvFEXVCXA4", commitVerification.Reason) assert.Equal(t, sshKey, commitVerification.SigningSSHKey) }) + + t.Run("Instance key", func(t *testing.T) { + pubKeyContent, err := os.ReadFile("../../tests/integration/ssh-signing-key.pub") + require.NoError(t, err) + pubKey, _, _, _, err := ssh.ParseAuthorizedKey(pubKeyContent) + require.NoError(t, err) + + defer test.MockVariableValue(&setting.Repository.Signing.SigningName, "UwU")() + defer test.MockVariableValue(&setting.Repository.Signing.SigningEmail, "fox@example.com")() + defer test.MockVariableValue(&setting.SSHInstanceKey, pubKey)() + + gitCommit := &git.Commit{ + Committer: &git.Signature{ + Email: "fox@example.com", + }, + Signature: &git.ObjectSignature{ + Payload: `tree f96f1a4f1a51dc42e2983592f503980b60b8849c +parent 93f84db542dd8c6e952c8130bc2fcbe2e299b8b4 +author OwO 1738961379 +0100 +committer UwU 1738961379 +0100 + +Fox +`, + Signature: `-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgV5ELwZ8XJe2LLR/UTuEu/vsFdb +t7ry0W8hyzz/b1iocAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 +AAAAQCnyMRkWVVNoZxZkvi/ZoknUhs4LNBmEwZs9e9214WIt+mhKfc6BiHoE2qeluR2McD +Y5RzHnA8Ke9wXddEePCQE= +-----END SSH SIGNATURE----- +`, + }, + } + + o := commitToGitObject(gitCommit) + commitVerification := ParseObjectWithSSHSignature(db.DefaultContext, &o, user2) + assert.True(t, commitVerification.Verified) + assert.Equal(t, "UwU / SHA256:QttK41r/zMUeAW71b5UgVSb8xGFF/DlZJ6TyADW+uoI", commitVerification.Reason) + assert.Equal(t, "SHA256:QttK41r/zMUeAW71b5UgVSb8xGFF/DlZJ6TyADW+uoI", commitVerification.SigningSSHKey.Fingerprint) + }) } diff --git a/models/asymkey/ssh_key_parse.go b/models/asymkey/ssh_key_parse.go index 3cb51b2b22..8177db6439 100644 --- a/models/asymkey/ssh_key_parse.go +++ b/models/asymkey/ssh_key_parse.go @@ -10,16 +10,17 @@ import ( "encoding/base64" "encoding/binary" "encoding/pem" + "errors" "fmt" "math/big" "os" "strconv" "strings" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/process" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/process" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "golang.org/x/crypto/ssh" ) @@ -93,7 +94,7 @@ func parseKeyString(content string) (string, error) { block, _ := pem.Decode([]byte(content)) if block == nil { - return "", fmt.Errorf("failed to parse PEM block containing the public key") + return "", errors.New("failed to parse PEM block containing the public key") } if strings.Contains(block.Type, "PRIVATE") { return "", ErrKeyIsPrivate @@ -226,7 +227,7 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) { // The ssh library can parse the key, so next we find out what key exactly we have. switch pkeyType { - case ssh.KeyAlgoDSA: + case ssh.KeyAlgoDSA: //nolint:staticcheck rawPub := struct { Name string P, Q, G, Y *big.Int diff --git a/models/asymkey/ssh_key_principals.go b/models/asymkey/ssh_key_principals.go index 4e7dee2c91..ba2a1a8c7d 100644 --- a/models/asymkey/ssh_key_principals.go +++ b/models/asymkey/ssh_key_principals.go @@ -8,11 +8,11 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/perm" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/models/perm" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) // AddPrincipalKey adds new principal to database and authorized_principals file. diff --git a/models/asymkey/ssh_key_test.go b/models/asymkey/ssh_key_test.go index 9a4ef665af..bc8cd7d36b 100644 --- a/models/asymkey/ssh_key_test.go +++ b/models/asymkey/ssh_key_test.go @@ -12,9 +12,9 @@ import ( "strings" "testing" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/db" + "forgejo.org/models/unittest" + "forgejo.org/modules/setting" "github.com/42wim/sshsig" "github.com/stretchr/testify/assert" @@ -44,7 +44,7 @@ func Test_SSHParsePublicKey(t *testing.T) { keyTypeN, lengthN, err := SSHNativeParsePublicKey(tc.content) require.NoError(t, err) assert.Equal(t, tc.keyType, keyTypeN) - assert.EqualValues(t, tc.length, lengthN) + assert.Equal(t, tc.length, lengthN) }) if tc.skipSSHKeygen { return @@ -54,19 +54,19 @@ func Test_SSHParsePublicKey(t *testing.T) { if err != nil { // Some servers do not support ecdsa format. if !strings.Contains(err.Error(), "line 1 too long:") { - assert.FailNow(t, "%v", err) + require.NoError(t, err) } } assert.Equal(t, tc.keyType, keyTypeK) - assert.EqualValues(t, tc.length, lengthK) + assert.Equal(t, tc.length, lengthK) }) t.Run("SSHParseKeyNative", func(t *testing.T) { keyTypeK, lengthK, err := SSHNativeParsePublicKey(tc.content) if err != nil { - assert.FailNow(t, "%v", err) + require.NoError(t, err) } assert.Equal(t, tc.keyType, keyTypeK) - assert.EqualValues(t, tc.length, lengthK) + assert.Equal(t, tc.length, lengthK) }) }) } diff --git a/models/asymkey/ssh_key_verify.go b/models/asymkey/ssh_key_verify.go index 208288c77b..5dd26ccc9a 100644 --- a/models/asymkey/ssh_key_verify.go +++ b/models/asymkey/ssh_key_verify.go @@ -7,8 +7,8 @@ import ( "bytes" "context" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" + "forgejo.org/models/db" + "forgejo.org/modules/log" "github.com/42wim/sshsig" ) diff --git a/models/auth/access_token.go b/models/auth/access_token.go index 3ac18940a8..695702b7a0 100644 --- a/models/auth/access_token.go +++ b/models/auth/access_token.go @@ -11,10 +11,10 @@ import ( "fmt" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/setting" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" lru "github.com/hashicorp/golang-lru/v2" "xorm.io/builder" @@ -111,12 +111,8 @@ func generateAccessToken(t *AccessToken) error { if err != nil { return err } - token, err := util.CryptoRandomBytes(20) - if err != nil { - return err - } t.TokenSalt = salt - t.Token = hex.EncodeToString(token) + t.Token = hex.EncodeToString(util.CryptoRandomBytes(20)) t.TokenHash = HashToken(t.Token, t.TokenSalt) t.TokenLastEight = t.Token[len(t.Token)-8:] return nil diff --git a/models/auth/access_token_scope.go b/models/auth/access_token_scope.go index 003ca5c9ab..d14838cf02 100644 --- a/models/auth/access_token_scope.go +++ b/models/auth/access_token_scope.go @@ -7,7 +7,7 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/models/perm" + "forgejo.org/models/perm" ) // AccessTokenScopeCategory represents the scope category for an access token @@ -283,6 +283,10 @@ func (s AccessTokenScope) Normalize() (AccessTokenScope, error) { return bitmap.toScope(), nil } +func (s AccessTokenScope) HasPermissionScope() bool { + return s != "" && s != AccessTokenScopePublicOnly +} + // PublicOnly checks if this token scope is limited to public resources func (s AccessTokenScope) PublicOnly() (bool, error) { bitmap, err := s.parse() diff --git a/models/auth/access_token_test.go b/models/auth/access_token_test.go index 976ff37493..913118433c 100644 --- a/models/auth/access_token_test.go +++ b/models/auth/access_token_test.go @@ -6,9 +6,9 @@ package auth_test import ( "testing" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go index c64af3e41f..d09aebcf85 100644 --- a/models/auth/auth_token.go +++ b/models/auth/auth_token.go @@ -10,9 +10,9 @@ import ( "fmt" "time" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" ) type AuthorizationPurpose string @@ -63,10 +63,7 @@ func (authToken *AuthorizationToken) IsExpired() bool { func GenerateAuthToken(ctx context.Context, userID int64, expiry timeutil.TimeStamp, purpose AuthorizationPurpose) (lookupKey, validator string, err error) { // Request 64 random bytes. The first 32 bytes will be used for the lookupKey // and the other 32 bytes will be used for the validator. - rBytes, err := util.CryptoRandomBytes(64) - if err != nil { - return "", "", err - } + rBytes := util.CryptoRandomBytes(64) hexEncoded := hex.EncodeToString(rBytes) validator, lookupKey = hexEncoded[64:], hexEncoded[:64] diff --git a/models/auth/main_test.go b/models/auth/main_test.go index 7b374b38da..b30db24483 100644 --- a/models/auth/main_test.go +++ b/models/auth/main_test.go @@ -6,14 +6,14 @@ package auth_test import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" - _ "code.gitea.io/gitea/models" - _ "code.gitea.io/gitea/models/actions" - _ "code.gitea.io/gitea/models/activities" - _ "code.gitea.io/gitea/models/auth" - _ "code.gitea.io/gitea/models/forgefed" - _ "code.gitea.io/gitea/models/perm/access" + _ "forgejo.org/models" + _ "forgejo.org/models/actions" + _ "forgejo.org/models/activities" + _ "forgejo.org/models/auth" + _ "forgejo.org/models/forgefed" + _ "forgejo.org/models/perm/access" ) func TestMain(m *testing.M) { diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 9a7854408f..fa68197cf0 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -14,11 +14,11 @@ import ( "net/url" "strings" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/container" + "forgejo.org/modules/setting" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" uuid "github.com/google/uuid" "golang.org/x/crypto/bcrypt" @@ -184,13 +184,9 @@ var base32Lower = base32.NewEncoding(lowerBase32Chars).WithPadding(base32.NoPadd // GenerateClientSecret will generate the client secret and returns the plaintext and saves the hash at the database func (app *OAuth2Application) GenerateClientSecret(ctx context.Context) (string, error) { - rBytes, err := util.CryptoRandomBytes(32) - if err != nil { - return "", err - } // Add a prefix to the base32, this is in order to make it easier // for code scanners to grab sensitive tokens. - clientSecret := "gto_" + base32Lower.EncodeToString(rBytes) + clientSecret := "gto_" + base32Lower.EncodeToString(util.CryptoRandomBytes(32)) hashedSecret, err := bcrypt.GenerateFromPassword([]byte(clientSecret), bcrypt.DefaultCost) if err != nil { @@ -475,13 +471,9 @@ func (grant *OAuth2Grant) TableName() string { // GenerateNewAuthorizationCode generates a new authorization code for a grant and saves it to the database func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redirectURI, codeChallenge, codeChallengeMethod string) (code *OAuth2AuthorizationCode, err error) { - rBytes, err := util.CryptoRandomBytes(32) - if err != nil { - return &OAuth2AuthorizationCode{}, err - } // Add a prefix to the base32, this is in order to make it easier // for code scanners to grab sensitive tokens. - codeSecret := "gta_" + base32Lower.EncodeToString(rBytes) + codeSecret := "gta_" + base32Lower.EncodeToString(util.CryptoRandomBytes(32)) code = &OAuth2AuthorizationCode{ Grant: grant, diff --git a/models/auth/oauth2_list.go b/models/auth/oauth2_list.go index c55f10b3c8..6f508833a0 100644 --- a/models/auth/oauth2_list.go +++ b/models/auth/oauth2_list.go @@ -4,7 +4,7 @@ package auth import ( - "code.gitea.io/gitea/models/db" + "forgejo.org/models/db" "xorm.io/builder" ) diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go index 9b562c8648..9c6836ed0d 100644 --- a/models/auth/oauth2_test.go +++ b/models/auth/oauth2_test.go @@ -4,14 +4,12 @@ package auth_test import ( - "path/filepath" "slices" "testing" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/setting" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -130,7 +128,7 @@ func TestOAuth2Application_CreateGrant(t *testing.T) { assert.NotNil(t, grant) assert.Equal(t, int64(2), grant.UserID) assert.Equal(t, int64(1), grant.ApplicationID) - assert.Equal(t, "", grant.Scope) + assert.Empty(t, grant.Scope) } //////////////////// Grant @@ -271,17 +269,11 @@ func TestOAuth2AuthorizationCode_TableName(t *testing.T) { func TestBuiltinApplicationsClientIDs(t *testing.T) { clientIDs := auth_model.BuiltinApplicationsClientIDs() slices.Sort(clientIDs) - assert.EqualValues(t, []string{"a4792ccc-144e-407e-86c9-5e7d8d9c3269", "d57cb8c4-630c-4168-8324-ec79935e18d4", "e90ee53c-94e2-48ac-9358-a874fb9e0662"}, clientIDs) + assert.Equal(t, []string{"a4792ccc-144e-407e-86c9-5e7d8d9c3269", "d57cb8c4-630c-4168-8324-ec79935e18d4", "e90ee53c-94e2-48ac-9358-a874fb9e0662"}, clientIDs) } func TestOrphanedOAuth2Applications(t *testing.T) { - defer unittest.OverrideFixtures( - unittest.FixturesOptions{ - Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"), - Base: setting.AppWorkPath, - Dirs: []string{"models/auth/TestOrphanedOAuth2Applications/"}, - }, - )() + defer unittest.OverrideFixtures("models/auth/TestOrphanedOAuth2Applications")() require.NoError(t, unittest.PrepareTestDatabase()) count, err := auth_model.CountOrphanedOAuth2Applications(db.DefaultContext) diff --git a/models/auth/session.go b/models/auth/session.go index 75a205f702..b3724dafb6 100644 --- a/models/auth/session.go +++ b/models/auth/session.go @@ -7,8 +7,8 @@ import ( "context" "fmt" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + "forgejo.org/modules/timeutil" "xorm.io/builder" ) diff --git a/models/auth/session_test.go b/models/auth/session_test.go index 3b57239704..0f07038187 100644 --- a/models/auth/session_test.go +++ b/models/auth/session_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/models/unittest" + "forgejo.org/modules/timeutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -37,7 +37,7 @@ func TestAuthSession(t *testing.T) { // New session is created. sess, err := auth.ReadSession(db.DefaultContext, key) require.NoError(t, err) - assert.EqualValues(t, key, sess.Key) + assert.Equal(t, key, sess.Key) assert.Empty(t, sess.Data) assert.EqualValues(t, now.Unix(), sess.Expiry) @@ -67,8 +67,8 @@ func TestAuthSession(t *testing.T) { // Ensure data is updated and expiry is set from the update session call. sess, err := auth.ReadSession(db.DefaultContext, key) require.NoError(t, err) - assert.EqualValues(t, key, sess.Key) - assert.EqualValues(t, data, sess.Data) + assert.Equal(t, key, sess.Key) + assert.Equal(t, data, sess.Data) assert.EqualValues(t, now.Unix(), sess.Expiry) timeutil.MockSet(now) diff --git a/models/auth/source.go b/models/auth/source.go index d7ed6b97a8..ecd3abc39d 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -9,11 +9,11 @@ import ( "fmt" "reflect" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/optional" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "xorm.io/builder" "xorm.io/xorm" diff --git a/models/auth/source_test.go b/models/auth/source_test.go index 522fecc25f..ed21aef253 100644 --- a/models/auth/source_test.go +++ b/models/auth/source_test.go @@ -7,10 +7,10 @@ import ( "strings" "testing" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/json" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/models/unittest" + "forgejo.org/modules/json" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/auth/two_factor.go b/models/auth/two_factor.go new file mode 100644 index 0000000000..e8f19c33cc --- /dev/null +++ b/models/auth/two_factor.go @@ -0,0 +1,21 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later +package auth + +import ( + "context" +) + +// HasTwoFactorByUID returns true if the user has TOTP or WebAuthn enabled for +// their account. +func HasTwoFactorByUID(ctx context.Context, userID int64) (bool, error) { + hasTOTP, err := HasTOTPByUID(ctx, userID) + if err != nil { + return false, err + } + if hasTOTP { + return true, nil + } + + return HasWebAuthnRegistrationsByUID(ctx, userID) +} diff --git a/models/auth/two_factor_test.go b/models/auth/two_factor_test.go new file mode 100644 index 0000000000..36e0404ae2 --- /dev/null +++ b/models/auth/two_factor_test.go @@ -0,0 +1,34 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later +package auth + +import ( + "testing" + + "forgejo.org/models/unittest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestHasTwoFactorByUID(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("No twofactor", func(t *testing.T) { + ok, err := HasTwoFactorByUID(t.Context(), 2) + require.NoError(t, err) + assert.False(t, ok) + }) + + t.Run("WebAuthn credential", func(t *testing.T) { + ok, err := HasTwoFactorByUID(t.Context(), 32) + require.NoError(t, err) + assert.True(t, ok) + }) + + t.Run("TOTP", func(t *testing.T) { + ok, err := HasTwoFactorByUID(t.Context(), 24) + require.NoError(t, err) + assert.True(t, ok) + }) +} diff --git a/models/auth/twofactor.go b/models/auth/twofactor.go index 67be23320e..9a53ad30e0 100644 --- a/models/auth/twofactor.go +++ b/models/auth/twofactor.go @@ -11,10 +11,10 @@ import ( "encoding/hex" "fmt" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/keying" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/keying" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "github.com/pquerna/otp/totp" "golang.org/x/crypto/pbkdf2" @@ -61,17 +61,13 @@ func init() { } // GenerateScratchToken recreates the scratch token the user is using. -func (t *TwoFactor) GenerateScratchToken() (string, error) { - tokenBytes, err := util.CryptoRandomBytes(6) - if err != nil { - return "", err - } +func (t *TwoFactor) GenerateScratchToken() string { // these chars are specially chosen, avoid ambiguous chars like `0`, `O`, `1`, `I`. const base32Chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" - token := base32.NewEncoding(base32Chars).WithPadding(base32.NoPadding).EncodeToString(tokenBytes) + token := base32.NewEncoding(base32Chars).WithPadding(base32.NoPadding).EncodeToString(util.CryptoRandomBytes(6)) t.ScratchSalt, _ = util.CryptoRandomString(10) t.ScratchHash = HashToken(token, t.ScratchSalt) - return token, nil + return token } // HashToken return the hashable salt @@ -139,9 +135,9 @@ func GetTwoFactorByUID(ctx context.Context, uid int64) (*TwoFactor, error) { return twofa, nil } -// HasTwoFactorByUID returns the two-factor authentication token associated with -// the user, if any. -func HasTwoFactorByUID(ctx context.Context, uid int64) (bool, error) { +// HasTOTPByUID returns the TOTP authentication token associated with +// the user, if the user has TOTP enabled for their account. +func HasTOTPByUID(ctx context.Context, uid int64) (bool, error) { return db.GetEngine(ctx).Where("uid=?", uid).Exist(&TwoFactor{}) } diff --git a/models/auth/webauthn.go b/models/auth/webauthn.go index b230e1665a..5b86a6e6f2 100644 --- a/models/auth/webauthn.go +++ b/models/auth/webauthn.go @@ -8,9 +8,9 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/db" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "github.com/go-webauthn/webauthn/webauthn" ) diff --git a/models/auth/webauthn_test.go b/models/auth/webauthn_test.go index e1cd652009..abf8e34408 100644 --- a/models/auth/webauthn_test.go +++ b/models/auth/webauthn_test.go @@ -6,9 +6,9 @@ package auth_test import ( "testing" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/models/unittest" "github.com/go-webauthn/webauthn/webauthn" "github.com/stretchr/testify/assert" diff --git a/models/avatars/avatar.go b/models/avatars/avatar.go index 9eb34dcbcc..ad59bd8769 100644 --- a/models/avatars/avatar.go +++ b/models/avatars/avatar.go @@ -14,10 +14,10 @@ import ( "strings" "sync/atomic" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/cache" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/db" + "forgejo.org/modules/cache" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "code.forgejo.org/forgejo-contrib/go-libravatar" ) diff --git a/models/avatars/avatar_test.go b/models/avatars/avatar_test.go index 85c40c3fa1..7850d2c096 100644 --- a/models/avatars/avatar_test.go +++ b/models/avatars/avatar_test.go @@ -6,11 +6,11 @@ package avatars_test import ( "testing" - avatars_model "code.gitea.io/gitea/models/avatars" - "code.gitea.io/gitea/models/db" - system_model "code.gitea.io/gitea/models/system" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/setting/config" + avatars_model "forgejo.org/models/avatars" + "forgejo.org/models/db" + system_model "forgejo.org/models/system" + "forgejo.org/modules/setting" + "forgejo.org/modules/setting/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/avatars/main_test.go b/models/avatars/main_test.go index c721a7dc2a..bdc66954b1 100644 --- a/models/avatars/main_test.go +++ b/models/avatars/main_test.go @@ -6,11 +6,11 @@ package avatars_test import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" - _ "code.gitea.io/gitea/models" - _ "code.gitea.io/gitea/models/activities" - _ "code.gitea.io/gitea/models/perm/access" + _ "forgejo.org/models" + _ "forgejo.org/models/activities" + _ "forgejo.org/models/perm/access" ) func TestMain(m *testing.M) { diff --git a/models/db/collation.go b/models/db/collation.go index 39d28fa2ff..768ada89e6 100644 --- a/models/db/collation.go +++ b/models/db/collation.go @@ -8,9 +8,9 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/container" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "xorm.io/xorm" "xorm.io/xorm/schemas" diff --git a/models/db/common.go b/models/db/common.go index f3fd3e72ae..c9b012597c 100644 --- a/models/db/common.go +++ b/models/db/common.go @@ -6,8 +6,8 @@ package db import ( "strings" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "xorm.io/builder" ) diff --git a/models/db/context.go b/models/db/context.go index 35526936af..3e035cd733 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -141,7 +141,7 @@ func TxContext(parentCtx context.Context) (*Context, Committer, error) { return nil, nil, err } - return newContext(DefaultContext, sess, true), sess, nil + return newContext(parentCtx, sess, true), sess, nil } // WithTx represents executing database operations on a transaction, if the transaction exist, diff --git a/models/db/context_test.go b/models/db/context_test.go index 855f360b75..d12d79ebe1 100644 --- a/models/db/context_test.go +++ b/models/db/context_test.go @@ -7,8 +7,8 @@ import ( "context" "testing" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/db" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -84,4 +84,16 @@ func TestTxContext(t *testing.T) { return nil })) } + + t.Run("Reuses parent context", func(t *testing.T) { + type unique struct{} + + ctx := context.WithValue(db.DefaultContext, unique{}, "yes!") + assert.False(t, db.InTransaction(ctx)) + + require.NoError(t, db.WithTx(ctx, func(ctx context.Context) error { + assert.Equal(t, "yes!", ctx.Value(unique{})) + return nil + })) + }) } diff --git a/models/db/convert.go b/models/db/convert.go index 956e17d411..1f37e49176 100644 --- a/models/db/convert.go +++ b/models/db/convert.go @@ -8,8 +8,8 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "xorm.io/xorm" "xorm.io/xorm/schemas" diff --git a/models/db/engine.go b/models/db/engine.go index 822618a7e3..05a119b08d 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -15,8 +15,8 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "xorm.io/xorm" "xorm.io/xorm/contexts" @@ -95,34 +95,70 @@ func init() { } } -// newXORMEngine returns a new XORM engine from the configuration -func newXORMEngine() (*xorm.Engine, error) { - connStr, err := setting.DBConnStr() +// newXORMEngineGroup creates an xorm.EngineGroup (with one master and one or more slaves). +// It assumes you have separate master and slave DSNs defined via the settings package. +func newXORMEngineGroup() (Engine, error) { + // Retrieve master DSN from settings. + masterConnStr, err := setting.DBMasterConnStr() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to determine master DSN: %w", err) } - var engine *xorm.Engine - + var masterEngine *xorm.Engine + // For PostgreSQL: if a schema is provided, we use the special "postgresschema" driver. if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 { - // OK whilst we sort out our schema issues - create a schema aware postgres registerPostgresSchemaDriver() - engine, err = xorm.NewEngine("postgresschema", connStr) + masterEngine, err = xorm.NewEngine("postgresschema", masterConnStr) } else { - engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr) + masterEngine, err = xorm.NewEngine(setting.Database.Type.String(), masterConnStr) } - if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create master engine: %w", err) } if setting.Database.Type.IsMySQL() { - engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"}) + masterEngine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"}) } - engine.SetSchema(setting.Database.Schema) - return engine, nil + masterEngine.SetSchema(setting.Database.Schema) + + slaveConnStrs, err := setting.DBSlaveConnStrs() + if err != nil { + return nil, fmt.Errorf("failed to load slave DSNs: %w", err) + } + + var slaveEngines []*xorm.Engine + // Iterate over all slave DSNs and create engines + for _, dsn := range slaveConnStrs { + slaveEngine, err := xorm.NewEngine(setting.Database.Type.String(), dsn) + if err != nil { + return nil, fmt.Errorf("failed to create slave engine for dsn %q: %w", dsn, err) + } + if setting.Database.Type.IsMySQL() { + slaveEngine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"}) + } + slaveEngine.SetSchema(setting.Database.Schema) + slaveEngines = append(slaveEngines, slaveEngine) + } + + policy := setting.BuildLoadBalancePolicy(&setting.Database, slaveEngines) + + // Create the EngineGroup using the selected policy + group, err := xorm.NewEngineGroup(masterEngine, slaveEngines, policy) + if err != nil { + return nil, fmt.Errorf("failed to create engine group: %w", err) + } + return engineGroupWrapper{group}, nil } -// SyncAllTables sync the schemas of all tables, is required by unit test code +type engineGroupWrapper struct { + *xorm.EngineGroup +} + +func (w engineGroupWrapper) AddHook(hook contexts.Hook) bool { + w.EngineGroup.AddHook(hook) + return true +} + +// SyncAllTables sync the schemas of all tables func SyncAllTables() error { _, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{ WarnIfDatabaseColumnMissed: true, @@ -130,52 +166,61 @@ func SyncAllTables() error { return err } -// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext +// InitEngine initializes the xorm EngineGroup and sets it as db.DefaultContext func InitEngine(ctx context.Context) error { - xormEngine, err := newXORMEngine() + xormEngine, err := newXORMEngineGroup() if err != nil { return fmt.Errorf("failed to connect to database: %w", err) } + // Try to cast to the concrete type to access diagnostic methods + if eng, ok := xormEngine.(engineGroupWrapper); ok { + eng.SetMapper(names.GonicMapper{}) + // WARNING: for serv command, MUST remove the output to os.Stdout, + // so use a log file instead of printing to stdout. + eng.SetLogger(NewXORMLogger(setting.Database.LogSQL)) + eng.ShowSQL(setting.Database.LogSQL) + eng.SetMaxOpenConns(setting.Database.MaxOpenConns) + eng.SetMaxIdleConns(setting.Database.MaxIdleConns) + eng.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) + eng.SetConnMaxIdleTime(setting.Database.ConnMaxIdleTime) + eng.SetDefaultContext(ctx) - xormEngine.SetMapper(names.GonicMapper{}) - // WARNING: for serv command, MUST remove the output to os.stdout, - // so use log file to instead print to stdout. - xormEngine.SetLogger(NewXORMLogger(setting.Database.LogSQL)) - xormEngine.ShowSQL(setting.Database.LogSQL) - xormEngine.SetMaxOpenConns(setting.Database.MaxOpenConns) - xormEngine.SetMaxIdleConns(setting.Database.MaxIdleConns) - xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) - xormEngine.SetConnMaxIdleTime(setting.Database.ConnMaxIdleTime) - xormEngine.SetDefaultContext(ctx) + if setting.Database.SlowQueryThreshold > 0 { + eng.AddHook(&SlowQueryHook{ + Threshold: setting.Database.SlowQueryThreshold, + Logger: log.GetLogger("xorm"), + }) + } - if setting.Database.SlowQueryThreshold > 0 { - xormEngine.AddHook(&SlowQueryHook{ - Treshold: setting.Database.SlowQueryThreshold, - Logger: log.GetLogger("xorm"), + errorLogger := log.GetLogger("xorm") + if setting.IsInTesting { + errorLogger = log.GetLogger(log.DEFAULT) + } + + eng.AddHook(&ErrorQueryHook{ + Logger: errorLogger, }) + + eng.AddHook(&TracingHook{}) + + SetDefaultEngine(ctx, eng) + } else { + // Fallback: if type assertion fails, set default engine without extended diagnostics + SetDefaultEngine(ctx, xormEngine) } - - errorLogger := log.GetLogger("xorm") - if setting.IsInTesting { - errorLogger = log.GetLogger(log.DEFAULT) - } - - xormEngine.AddHook(&ErrorQueryHook{ - Logger: errorLogger, - }) - - xormEngine.AddHook(&TracingHook{}) - - SetDefaultEngine(ctx, xormEngine) return nil } -// SetDefaultEngine sets the default engine for db -func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) { - x = eng +// SetDefaultEngine sets the default engine for db. +func SetDefaultEngine(ctx context.Context, eng Engine) { + masterEngine, err := GetMasterEngine(eng) + if err == nil { + x = masterEngine + } + DefaultContext = &Context{ Context: ctx, - e: x, + e: eng, } } @@ -191,12 +236,12 @@ func UnsetDefaultEngine() { DefaultContext = nil } -// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext +// InitEngineWithMigration initializes a new xorm EngineGroup, runs migrations, and sets it as db.DefaultContext // This function must never call .Sync() if the provided migration function fails. // When called from the "doctor" command, the migration function is a version check // that prevents the doctor from fixing anything in the database if the migration level // is different from the expected value. -func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) { +func InitEngineWithMigration(ctx context.Context, migrateFunc func(Engine) error) (err error) { if err = InitEngine(ctx); err != nil { return err } @@ -230,14 +275,14 @@ func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) return nil } -// NamesToBean return a list of beans or an error +// NamesToBean returns a list of beans given names func NamesToBean(names ...string) ([]any, error) { beans := []any{} if len(names) == 0 { beans = append(beans, tables...) return beans, nil } - // Need to map provided names to beans... + // Map provided names to beans beanMap := make(map[string]any) for _, bean := range tables { beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean @@ -259,7 +304,7 @@ func NamesToBean(names ...string) ([]any, error) { return beans, nil } -// DumpDatabase dumps all data from database according the special database SQL syntax to file system. +// DumpDatabase dumps all data from database using special SQL syntax to the file system. func DumpDatabase(filePath, dbType string) error { var tbs []*schemas.Table for _, t := range tables { @@ -295,29 +340,33 @@ func MaxBatchInsertSize(bean any) int { return 999 / len(t.ColumnsSeq()) } -// IsTableNotEmpty returns true if table has at least one record +// IsTableNotEmpty returns true if the table has at least one record func IsTableNotEmpty(beanOrTableName any) (bool, error) { return x.Table(beanOrTableName).Exist() } -// DeleteAllRecords will delete all the records of this table +// DeleteAllRecords deletes all records in the given table. func DeleteAllRecords(tableName string) error { _, err := x.Exec(fmt.Sprintf("DELETE FROM %s", tableName)) return err } -// GetMaxID will return max id of the table +// GetMaxID returns the maximum id in the table func GetMaxID(beanOrTableName any) (maxID int64, err error) { _, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID) return maxID, err } func SetLogSQL(ctx context.Context, on bool) { - e := GetEngine(ctx) - if x, ok := e.(*xorm.Engine); ok { - x.ShowSQL(on) - } else if sess, ok := e.(*xorm.Session); ok { + ctxEngine := GetEngine(ctx) + + if sess, ok := ctxEngine.(*xorm.Session); ok { sess.Engine().ShowSQL(on) + } else if wrapper, ok := ctxEngine.(engineGroupWrapper); ok { + // Handle engineGroupWrapper directly + wrapper.ShowSQL(on) + } else if masterEngine, err := GetMasterEngine(ctxEngine); err == nil { + masterEngine.ShowSQL(on) } } @@ -341,8 +390,8 @@ func (TracingHook) AfterProcess(c *contexts.ContextHook) error { } type SlowQueryHook struct { - Treshold time.Duration - Logger log.Logger + Threshold time.Duration + Logger log.Logger } var _ contexts.Hook = &SlowQueryHook{} @@ -352,7 +401,7 @@ func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, er } func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error { - if c.ExecuteTime >= h.Treshold { + if c.ExecuteTime >= h.Threshold { h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime) } return nil @@ -374,3 +423,18 @@ func (h *ErrorQueryHook) AfterProcess(c *contexts.ContextHook) error { } return nil } + +// GetMasterEngine extracts the master xorm.Engine from the provided xorm.Engine. +// This handles both direct xorm.Engine cases and engines that implement a Master() method. +func GetMasterEngine(x Engine) (*xorm.Engine, error) { + if getter, ok := x.(interface{ Master() *xorm.Engine }); ok { + return getter.Master(), nil + } + + engine, ok := x.(*xorm.Engine) + if !ok { + return nil, fmt.Errorf("unsupported engine type: %T", x) + } + + return engine, nil +} diff --git a/models/db/engine_test.go b/models/db/engine_test.go index 230ee3f2b1..98cc90c251 100644 --- a/models/db/engine_test.go +++ b/models/db/engine_test.go @@ -8,14 +8,14 @@ import ( "testing" "time" - "code.gitea.io/gitea/models/db" - issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + "forgejo.org/models/unittest" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" - _ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys + _ "forgejo.org/cmd" // for TestPrimaryKeys "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -56,7 +56,7 @@ func TestDeleteOrphanedObjects(t *testing.T) { countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{}) require.NoError(t, err) - assert.EqualValues(t, countBefore, countAfter) + assert.Equal(t, countBefore, countAfter) } func TestPrimaryKeys(t *testing.T) { @@ -64,7 +64,7 @@ func TestPrimaryKeys(t *testing.T) { // https://github.com/go-gitea/gitea/issues/21086 // https://github.com/go-gitea/gitea/issues/16802 // To avoid creating tables without primary key again, this test will check them. - // Import "code.gitea.io/gitea/cmd" to make sure each db.RegisterModel in init functions has been called. + // Import "forgejo.org/cmd" to make sure each db.RegisterModel in init functions has been called. beans, err := db.NamesToBean() if err != nil { @@ -103,8 +103,8 @@ func TestSlowQuery(t *testing.T) { // It's not possible to clean this up with XORM, but it's luckily not harmful // to leave around. engine.AddHook(&db.SlowQueryHook{ - Treshold: time.Second * 10, - Logger: log.GetLogger("slow-query"), + Threshold: time.Second * 10, + Logger: log.GetLogger("slow-query"), }) // NOOP query. @@ -114,8 +114,8 @@ func TestSlowQuery(t *testing.T) { assert.False(t, stopped) engine.AddHook(&db.SlowQueryHook{ - Treshold: 0, // Every query should be logged. - Logger: log.GetLogger("slow-query"), + Threshold: 0, // Every query should be logged. + Logger: log.GetLogger("slow-query"), }) // NOOP query. diff --git a/models/db/error.go b/models/db/error.go index 665e970e17..6b70c40eb3 100644 --- a/models/db/error.go +++ b/models/db/error.go @@ -6,7 +6,7 @@ package db import ( "fmt" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) // ErrCancelled represents an error due to context cancellation diff --git a/models/db/index.go b/models/db/index.go index 259ddd6ade..4c15dbe8a1 100644 --- a/models/db/index.go +++ b/models/db/index.go @@ -9,7 +9,7 @@ import ( "fmt" "strconv" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // ResourceIndex represents a resource index which could be used as issue/release and others diff --git a/models/db/index_test.go b/models/db/index_test.go index 11fbc70d8d..b64a816bd2 100644 --- a/models/db/index_test.go +++ b/models/db/index_test.go @@ -9,8 +9,8 @@ import ( "fmt" "testing" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/db" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -33,10 +33,11 @@ func getCurrentResourceIndex(ctx context.Context, tableName string, groupID int6 func TestSyncMaxResourceIndex(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - xe := unittest.GetXORMEngine() + xe, err := unittest.GetXORMEngine() + require.NoError(t, err) require.NoError(t, xe.Sync(&TestIndex{})) - err := db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 51) + err = db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 51) require.NoError(t, err) // sync new max index @@ -88,7 +89,8 @@ func TestSyncMaxResourceIndex(t *testing.T) { func TestGetNextResourceIndex(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - xe := unittest.GetXORMEngine() + xe, err := unittest.GetXORMEngine() + require.NoError(t, err) require.NoError(t, xe.Sync(&TestIndex{})) // create a new record diff --git a/models/db/install/db.go b/models/db/install/db.go index d4c1139637..e64e85cf01 100644 --- a/models/db/install/db.go +++ b/models/db/install/db.go @@ -4,14 +4,12 @@ package install import ( - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/setting" - - "xorm.io/xorm" + "forgejo.org/models/db" + "forgejo.org/modules/setting" ) -func getXORMEngine() *xorm.Engine { - return db.DefaultContext.(*db.Context).Engine().(*xorm.Engine) +func getXORMEngine() db.Engine { + return db.DefaultContext.(*db.Context).Engine() } // CheckDatabaseConnection checks the database connection diff --git a/models/db/iterate.go b/models/db/iterate.go index e1caefa72b..450c7d3389 100644 --- a/models/db/iterate.go +++ b/models/db/iterate.go @@ -6,7 +6,7 @@ package db import ( "context" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "xorm.io/builder" ) diff --git a/models/db/iterate_test.go b/models/db/iterate_test.go index 7535d01d56..bdeaa876d5 100644 --- a/models/db/iterate_test.go +++ b/models/db/iterate_test.go @@ -7,9 +7,9 @@ import ( "context" "testing" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,7 +17,8 @@ import ( func TestIterate(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - xe := unittest.GetXORMEngine() + xe, err := unittest.GetXORMEngine() + require.NoError(t, err) require.NoError(t, xe.Sync(&repo_model.RepoUnit{})) cnt, err := db.GetEngine(db.DefaultContext).Count(&repo_model.RepoUnit{}) diff --git a/models/db/list.go b/models/db/list.go index 5c005a0350..057221936c 100644 --- a/models/db/list.go +++ b/models/db/list.go @@ -6,7 +6,7 @@ package db import ( "context" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "xorm.io/builder" "xorm.io/xorm" diff --git a/models/db/list_test.go b/models/db/list_test.go index 82240d205b..502372782d 100644 --- a/models/db/list_test.go +++ b/models/db/list_test.go @@ -6,9 +6,9 @@ package db_test import ( "testing" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -29,11 +29,12 @@ func (opts mockListOptions) ToConds() builder.Cond { func TestFind(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - xe := unittest.GetXORMEngine() + xe, err := unittest.GetXORMEngine() + require.NoError(t, err) require.NoError(t, xe.Sync(&repo_model.RepoUnit{})) var repoUnitCount int - _, err := db.GetEngine(db.DefaultContext).SQL("SELECT COUNT(*) FROM repo_unit").Get(&repoUnitCount) + _, err = db.GetEngine(db.DefaultContext).SQL("SELECT COUNT(*) FROM repo_unit").Get(&repoUnitCount) require.NoError(t, err) assert.NotEmpty(t, repoUnitCount) @@ -48,6 +49,6 @@ func TestFind(t *testing.T) { repoUnits, newCnt, err := db.FindAndCount[repo_model.RepoUnit](db.DefaultContext, opts) require.NoError(t, err) - assert.EqualValues(t, cnt, newCnt) + assert.Equal(t, cnt, newCnt) assert.Len(t, repoUnits, repoUnitCount) } diff --git a/models/db/log.go b/models/db/log.go index 457ee80ff5..387709cc50 100644 --- a/models/db/log.go +++ b/models/db/log.go @@ -7,7 +7,7 @@ import ( "fmt" "sync/atomic" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" xormlog "xorm.io/xorm/log" ) @@ -69,6 +69,9 @@ func (l *XORMLogBridge) Warn(v ...any) { // Warnf show warning log func (l *XORMLogBridge) Warnf(format string, v ...any) { + if format == "Table %s Column %s db default is %s, struct default is %s" || format == "Table %s Column %s db nullable is %v, struct nullable is %v" { + return + } l.Log(stackLevel, log.WARN, format, v...) } diff --git a/models/db/main_test.go b/models/db/main_test.go index 7d80b400fe..4b06923950 100644 --- a/models/db/main_test.go +++ b/models/db/main_test.go @@ -6,10 +6,10 @@ package db_test import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" - _ "code.gitea.io/gitea/models" - _ "code.gitea.io/gitea/models/repo" + _ "forgejo.org/models" + _ "forgejo.org/models/repo" ) func TestMain(m *testing.M) { diff --git a/models/db/name.go b/models/db/name.go index 51be33a8bc..29b60b2373 100644 --- a/models/db/name.go +++ b/models/db/name.go @@ -9,7 +9,7 @@ import ( "strings" "unicode/utf8" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) var ( diff --git a/models/db/paginator/main_test.go b/models/db/paginator/main_test.go index 47993aed6b..e2528be121 100644 --- a/models/db/paginator/main_test.go +++ b/models/db/paginator/main_test.go @@ -6,7 +6,7 @@ package paginator import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" ) func TestMain(m *testing.M) { diff --git a/models/db/paginator/paginator_test.go b/models/db/paginator/paginator_test.go index 20602212d9..c6d0569aaa 100644 --- a/models/db/paginator/paginator_test.go +++ b/models/db/paginator/paginator_test.go @@ -6,8 +6,8 @@ package paginator import ( "testing" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/db" + "forgejo.org/modules/setting" "github.com/stretchr/testify/assert" ) diff --git a/models/db/sequence.go b/models/db/sequence.go index f49ad935de..1740e74c52 100644 --- a/models/db/sequence.go +++ b/models/db/sequence.go @@ -8,7 +8,7 @@ import ( "fmt" "regexp" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // CountBadSequences looks for broken sequences from recreate-table mistakes diff --git a/models/db/sql_postgres_with_schema.go b/models/db/sql_postgres_with_schema.go index ec63447f6f..376f984dc6 100644 --- a/models/db/sql_postgres_with_schema.go +++ b/models/db/sql_postgres_with_schema.go @@ -8,7 +8,7 @@ import ( "database/sql/driver" "sync" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "github.com/lib/pq" "xorm.io/xorm/dialects" diff --git a/models/dbfs/dbfile.go b/models/dbfs/dbfile.go index dd27b5c36b..12c0398abc 100644 --- a/models/dbfs/dbfile.go +++ b/models/dbfs/dbfile.go @@ -14,7 +14,7 @@ import ( "strings" "time" - "code.gitea.io/gitea/models/db" + "forgejo.org/models/db" ) var defaultFileBlockSize int64 = 32 * 1024 diff --git a/models/dbfs/dbfs.go b/models/dbfs/dbfs.go index f68b4a2b70..ba57e50151 100644 --- a/models/dbfs/dbfs.go +++ b/models/dbfs/dbfs.go @@ -10,7 +10,7 @@ import ( "path" "time" - "code.gitea.io/gitea/models/db" + "forgejo.org/models/db" ) /* diff --git a/models/dbfs/dbfs_test.go b/models/dbfs/dbfs_test.go index 3ad273a732..10da231e44 100644 --- a/models/dbfs/dbfs_test.go +++ b/models/dbfs/dbfs_test.go @@ -9,7 +9,7 @@ import ( "os" "testing" - "code.gitea.io/gitea/models/db" + "forgejo.org/models/db" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,15 +32,15 @@ func TestDbfsBasic(t *testing.T) { n, err := f.Write([]byte("0123456789")) // blocks: 0123 4567 89 require.NoError(t, err) - assert.EqualValues(t, 10, n) + assert.Equal(t, 10, n) _, err = f.Seek(0, io.SeekStart) require.NoError(t, err) buf, err := io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, 10, n) - assert.EqualValues(t, "0123456789", string(buf)) + assert.Equal(t, 10, n) + assert.Equal(t, "0123456789", string(buf)) // write some new data _, err = f.Seek(1, io.SeekStart) @@ -51,14 +51,14 @@ func TestDbfsBasic(t *testing.T) { // read from offset buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "9", string(buf)) + assert.Equal(t, "9", string(buf)) // read all _, err = f.Seek(0, io.SeekStart) require.NoError(t, err) buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "0bcdefghi9", string(buf)) + assert.Equal(t, "0bcdefghi9", string(buf)) // write to new size _, err = f.Seek(-1, io.SeekEnd) @@ -69,7 +69,7 @@ func TestDbfsBasic(t *testing.T) { require.NoError(t, err) buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "0bcdefghiJKLMNOP", string(buf)) + assert.Equal(t, "0bcdefghiJKLMNOP", string(buf)) // write beyond EOF and fill with zero _, err = f.Seek(5, io.SeekCurrent) @@ -80,7 +80,7 @@ func TestDbfsBasic(t *testing.T) { require.NoError(t, err) buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "0bcdefghiJKLMNOP\x00\x00\x00\x00\x00xyzu", string(buf)) + assert.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00\x00\x00xyzu", string(buf)) // write to the block with zeros _, err = f.Seek(-6, io.SeekCurrent) @@ -91,7 +91,7 @@ func TestDbfsBasic(t *testing.T) { require.NoError(t, err) buf, err = io.ReadAll(f) require.NoError(t, err) - assert.EqualValues(t, "0bcdefghiJKLMNOP\x00\x00\x00ABCDzu", string(buf)) + assert.Equal(t, "0bcdefghiJKLMNOP\x00\x00\x00ABCDzu", string(buf)) require.NoError(t, f.Close()) @@ -118,7 +118,7 @@ func TestDbfsBasic(t *testing.T) { require.NoError(t, err) stat, err := f.Stat() require.NoError(t, err) - assert.EqualValues(t, "test.txt", stat.Name()) + assert.Equal(t, "test.txt", stat.Name()) assert.EqualValues(t, 0, stat.Size()) _, err = f.Write([]byte("0123456789")) require.NoError(t, err) @@ -145,7 +145,7 @@ func TestDbfsReadWrite(t *testing.T) { line, err := f2r.ReadString('\n') require.NoError(t, err) - assert.EqualValues(t, "line 1\n", line) + assert.Equal(t, "line 1\n", line) _, err = f2r.ReadString('\n') require.ErrorIs(t, err, io.EOF) @@ -154,7 +154,7 @@ func TestDbfsReadWrite(t *testing.T) { line, err = f2r.ReadString('\n') require.NoError(t, err) - assert.EqualValues(t, "line 2\n", line) + assert.Equal(t, "line 2\n", line) _, err = f2r.ReadString('\n') require.ErrorIs(t, err, io.EOF) } @@ -187,5 +187,5 @@ func TestDbfsSeekWrite(t *testing.T) { buf, err := io.ReadAll(fr) require.NoError(t, err) - assert.EqualValues(t, "111333", string(buf)) + assert.Equal(t, "111333", string(buf)) } diff --git a/models/dbfs/main_test.go b/models/dbfs/main_test.go index 537ba0935d..3d4b2bc235 100644 --- a/models/dbfs/main_test.go +++ b/models/dbfs/main_test.go @@ -6,7 +6,7 @@ package dbfs import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" ) func TestMain(m *testing.M) { diff --git a/models/error.go b/models/error.go index 658bdc165c..ebaa8a135d 100644 --- a/models/error.go +++ b/models/error.go @@ -7,9 +7,9 @@ package models import ( "fmt" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/util" + repo_model "forgejo.org/models/repo" + "forgejo.org/modules/git" + "forgejo.org/modules/util" ) // ErrUserOwnRepos represents a "UserOwnRepos" kind of error. @@ -414,7 +414,7 @@ func IsErrSHAOrCommitIDNotProvided(err error) bool { } func (err ErrSHAOrCommitIDNotProvided) Error() string { - return "a SHA or commit ID must be proved when updating a file" + return "a SHA or commit ID must be provided when updating a file" } // ErrInvalidMergeStyle represents an error if merging with disabled merge strategy diff --git a/models/fixtures/ModerationFeatures/abuse_report.yml b/models/fixtures/ModerationFeatures/abuse_report.yml new file mode 100644 index 0000000000..f2e371ee35 --- /dev/null +++ b/models/fixtures/ModerationFeatures/abuse_report.yml @@ -0,0 +1,21 @@ +- + id: 1 + status: 1 + reporter_id: 2 # @user2 + content_type: 4 # Comment + content_id: 18 # user2/repo2/issues/2#issuecomment-18 + category: 2 # Spam + remarks: The comment I'm reporting is pure SPAM. + shadow_copy_id: null + created_unix: 1752697980 # 2025-07-16 20:33:00 + +- + id: 2 + status: 1 # Open + reporter_id: 2 # @user2 + content_type: 1 # User (users or organizations) + content_id: 1002 # @alexsmith + category: 2 # Spam + remarks: This user just posted a spammy comment on my issue. + shadow_copy_id: null + created_unix: 1752698010 # 2025-07-16 20:33:30 diff --git a/models/fixtures/ModerationFeatures/comment.yml b/models/fixtures/ModerationFeatures/comment.yml new file mode 100644 index 0000000000..a4d41ad997 --- /dev/null +++ b/models/fixtures/ModerationFeatures/comment.yml @@ -0,0 +1,7 @@ +- # This is a spam comment (abusive content), created for testing moderation functionalities. + id: 18 + type: 0 # Standard comment + poster_id: 1002 # @alexsmith + issue_id: 7 # user2/repo2#2 + content: If anyone needs help for promoting their business online using SEO, just contact me (check my profile page). + created_unix: 1752697860 # 2025-07-16 20:31:00 diff --git a/models/fixtures/ModerationFeatures/user.yml b/models/fixtures/ModerationFeatures/user.yml new file mode 100644 index 0000000000..662c61a3e9 --- /dev/null +++ b/models/fixtures/ModerationFeatures/user.yml @@ -0,0 +1,22 @@ +- # This user is a spammer and will create abusive content (for testing moderation functionalities). + id: 1002 + lower_name: alexsmith + name: alexsmith + full_name: Alex Smith + email: alexsmith@example.org + keep_email_private: false + passwd: passwdSalt:password + passwd_hash_algo: dummy + type: 0 + location: '@master@seo.net' + website: http://promote-your-business.biz + pronouns: SEO + salt: passwdSalt + description: I can help you promote your business online using SEO. + created_unix: 1752697800 # 2025-07-16 20:30:00 + is_active: true + is_admin: false + is_restricted: false + avatar: avatar-hash-1002 + avatar_email: alexsmith@example.org + use_custom_avatar: false diff --git a/models/fixtures/TestActivateUserEmail/email_address.yml b/models/fixtures/TestActivateUserEmail/email_address.yml new file mode 100644 index 0000000000..cf41ff8241 --- /dev/null +++ b/models/fixtures/TestActivateUserEmail/email_address.yml @@ -0,0 +1,7 @@ +- + id: 1001 + uid: 1001 + email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net + lower_email: anothertestuserwithuppercaseemail@otto.splvs.net + is_activated: false + is_primary: true diff --git a/models/fixtures/TestActivateUserEmail/user.yml b/models/fixtures/TestActivateUserEmail/user.yml new file mode 100644 index 0000000000..0a68e70a4a --- /dev/null +++ b/models/fixtures/TestActivateUserEmail/user.yml @@ -0,0 +1,12 @@ +- + id: 1001 + lower_name: user1001 + name: user1001 + full_name: User That loves Upper Cases + email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net + passwd: ZogKvWdyEx:password + passwd_hash_algo: dummy + avatar: '' + avatar_email: anothertestuserwithuppercaseemail@otto.splvs.net + login_name: user1 + created_unix: 1672578000 diff --git a/models/fixtures/TestAddTeamReviewRequest/issue.yml b/models/fixtures/TestAddTeamReviewRequest/issue.yml new file mode 100644 index 0000000000..a1bcf2921f --- /dev/null +++ b/models/fixtures/TestAddTeamReviewRequest/issue.yml @@ -0,0 +1,16 @@ +- + id: 23 + repo_id: 2 + index: 3 + poster_id: 2 + original_author_id: 0 + name: protected branch pull + content: pull request to a protected branch + milestone_id: 0 + priority: 0 + is_pull: true + is_closed: false + num_comments: 0 + created_unix: 1707270422 + updated_unix: 1707270422 + is_locked: false \ No newline at end of file diff --git a/models/fixtures/TestAddTeamReviewRequest/protected_branch.yml b/models/fixtures/TestAddTeamReviewRequest/protected_branch.yml new file mode 100644 index 0000000000..93909bd991 --- /dev/null +++ b/models/fixtures/TestAddTeamReviewRequest/protected_branch.yml @@ -0,0 +1,28 @@ +- id: 1 + repo_id: 2 + branch_name: protected-main + can_push: false + enable_whitelist: true + whitelist_user_i_ds: [1] + whitelist_team_i_ds: [] + enable_merge_whitelist: true + whitelist_deploy_keys: false + merge_whitelist_user_i_ds: [1] + merge_whitelist_team_i_ds: [] + enable_status_check: false + status_check_contexts: [] + enable_approvals_whitelist: true + approvals_whitelist_user_i_ds: [] + approvals_whitelist_team_i_ds: [1] + required_approvals: 1 + block_on_rejected_reviews: true + block_on_official_review_requests: true + block_on_outdated_branch: true + dismiss_stale_approvals: true + ignore_stale_approvals: false + require_signed_commits: false + protected_file_patterns: "" + unprotected_file_patterns: "" + apply_to_admins: true + created_unix: 1752513073 + updated_unix: 1752513073 \ No newline at end of file diff --git a/models/fixtures/TestAddTeamReviewRequest/pull_request.yml b/models/fixtures/TestAddTeamReviewRequest/pull_request.yml new file mode 100644 index 0000000000..067bb01324 --- /dev/null +++ b/models/fixtures/TestAddTeamReviewRequest/pull_request.yml @@ -0,0 +1,12 @@ +- + id: 11 + type: 0 # gitea pull request + status: 2 # mergeable + issue_id: 23 + index: 3 + head_repo_id: 2 + base_repo_id: 2 + head_branch: feature/protected-branch-pr + base_branch: protected-main + merge_base: 4a357436d925b5c974181ff12a994538ddc5a269 + has_merged: false \ No newline at end of file diff --git a/models/fixtures/TestGetUsedForUser/action_artifact.yaml b/models/fixtures/TestGetUsedForUser/action_artifact.yaml new file mode 100644 index 0000000000..db5392126d --- /dev/null +++ b/models/fixtures/TestGetUsedForUser/action_artifact.yaml @@ -0,0 +1,17 @@ +- + id: 1001 + run_id: 792 + runner_id: 1 + repo_id: 4 + owner_id: 1 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 + storage_path: "27/5/1730330775594233150.chunk" + file_size: 693147180559 + file_compressed_size: 693147180559 + content_encoding: "application/zip" + artifact_path: "big-file.zip" + artifact_name: "big-file" + status: 4 + created_unix: 1730330775 + updated_unix: 1730330775 + expired_unix: 1738106775 diff --git a/models/fixtures/TestPackagesGetOrInsertBlob/package_blob.yml b/models/fixtures/TestPackagesGetOrInsertBlob/package_blob.yml new file mode 100644 index 0000000000..ec90787c43 --- /dev/null +++ b/models/fixtures/TestPackagesGetOrInsertBlob/package_blob.yml @@ -0,0 +1,17 @@ +- + id: 1 + size: 10 + hash_md5: HASHMD5_1 + hash_sha1: HASHSHA1_1 + hash_sha256: HASHSHA256_1 + hash_sha512: HASHSHA512_1 + hash_blake2b: HASHBLAKE2B_1 + created_unix: 946687980 +- + id: 2 + size: 20 + hash_md5: HASHMD5_2 + hash_sha1: HASHSHA1_2 + hash_sha256: HASHSHA256_2 + hash_sha512: HASHSHA512_2 + created_unix: 946687980 diff --git a/models/fixtures/action.yml b/models/fixtures/action.yml index b2febb4ed8..f1592d4569 100644 --- a/models/fixtures/action.yml +++ b/models/fixtures/action.yml @@ -74,3 +74,11 @@ is_private: false created_unix: 1680454039 content: '4|' # issueId 5 + +- id: 10 + user_id: 40 + op_type: 1 # create repo + act_user_id: 40 + repo_id: 60 # public + is_private: false + created_unix: 1577404800 # end of heatmap \ No newline at end of file diff --git a/models/fixtures/action_run.yml b/models/fixtures/action_run.yml index 7a7bf34197..5b6f89ae0e 100644 --- a/models/fixtures/action_run.yml +++ b/models/fixtures/action_run.yml @@ -471,3 +471,64 @@ need_approval: 0 approved_by: 0 event_payload: '{"head_commit":{"id":"5f22f7d0d95d614d25a5b68592adb345a4b5c7fd"}}' + + +# GET action run(s) test +- + id: 892 + title: "successful push run" + repo_id: 63 + owner_id: 2 + workflow_id: "success.yaml" + index: 1 + trigger_user_id: 2 + ref: "refs/heads/main" + commit_sha: "97f29ee599c373c729132a5c46a046978311e0ee" + event: "push" + is_fork_pull_request: 0 + status: 1 # success + started: 1683636528 + stopped: 1683636626 + created: 1683636108 + updated: 1683636626 + need_approval: 0 + approved_by: 0 + +- + id: 893 + title: "failed pull_request run" + repo_id: 63 + owner_id: 2 + workflow_id: "failed.yaml" + index: 2 + trigger_user_id: 2 + ref: "refs/heads/bugfix-1" + commit_sha: "35c5cddfc19397501ec8f4f7bb808a7c8f04445f" + event: "pull_request" + is_fork_pull_request: 0 + status: 2 # failure + started: 1683636528 + stopped: 1683636626 + created: 1683636108 + updated: 1683636626 + need_approval: 0 + approved_by: 0 + +- + id: 894 + title: "running workflow_dispatch run" + repo_id: 63 + owner_id: 2 + workflow_id: "running.yaml" + index: 3 + trigger_user_id: 2 + ref: "refs/heads/main" + commit_sha: "97f29ee599c373c729132a5c46a046978311e0ee" + event: "workflow_dispatch" + is_fork_pull_request: 0 + status: 6 # running + started: 1683636528 + created: 1683636108 + updated: 1683636626 + need_approval: 0 + approved_by: 0 diff --git a/models/fixtures/action_runner.yml b/models/fixtures/action_runner.yml index 94deac998e..fcf26d49b6 100644 --- a/models/fixtures/action_runner.yml +++ b/models/fixtures/action_runner.yml @@ -18,3 +18,122 @@ created: 1716104432 updated: 1716104432 deleted: ~ +- id: 10000001 + uuid: 10d3b248-6460-4bf5-b819-1f5b3109e10f + name: global-online + version: v6.3.1+7-gc4c0ca0 + owner_id: 0 + repo_id: 0 + description: "" + base: 0 + repo_range: "" + token_hash: 7e9ed71f64e98ce1f70e94c63f3cb6c41a8cb0b90de3e1daf7ec5c35361d60ed44da67c5ac393b7aaf443dcfc766007dc828 + token_salt: WUcgZWl7mW + last_online: 1716104422 + last_active: 0 + agent_labels: '["docker"]' + created: 1716104431 + updated: 1716104422 + deleted: ~ +- id: 10000002 + uuid: 1d188484-dd97-4a70-b707-5e87b578ab6b + name: repo-never-used + version: v6.3.1+7-gc4c0ca0 + owner_id: 0 + repo_id: 1 + description: "" + base: 0 + repo_range: "" + token_hash: 51e88c17ac8b54dd101dc2e4f530a71643c703adba7170f4b1a28f1cb483b4cfb107798c521e0532ef3c6480b64518a5c6a5 + token_salt: 4rh8ncXYIO + last_online: 0 + last_active: 0 + agent_labels: '["docker"]' + created: 1713512432 + updated: 1713512432 + deleted: ~ +- id: 10000003 + uuid: 7a039c6b-b0b2-4cf5-a93d-715d617f99e2 + name: global-offline + version: v6.3.1+7-gc4c0ca0 + owner_id: 0 + repo_id: 0 + description: "" + base: 0 + repo_range: "" + token_hash: c76960c56bc6069f0d1648991ec626500abe8c15286f5c355d565c3b5ba945d7d6f1272a6c77849e592528179511b94f5d69 + token_salt: TFMe2jhOkB + last_online: 1715499632 + last_active: 0 + agent_labels: '["docker"]' + created: 1715499632 + updated: 1715499632 + deleted: ~ +- id: 10000004 + uuid: 93ca7fdd-faca-4df6-a474-8345263ef10b + name: user-online + version: v6.3.1+7-gc4c0ca0 + owner_id: 1 + repo_id: 0 + description: "" + base: 0 + repo_range: "" + token_hash: 6ddf7f0f2301d2b3f66418145dc497a6d09fa6586e659afcb5ae2a0c5b639561d795aff8062537db9df73b396842ea826134 + token_salt: QcdGuReAp4 + last_online: 1716104422 + last_active: 0 + agent_labels: '["docker"]' + created: 1716104431 + updated: 1716104422 + deleted: ~ +- id: 10000005 + uuid: a8534df6-c4be-40f4-9714-903b69d973d9 + name: user-never-used + version: v6.3.1+7-gc4c0ca0 + owner_id: 1 + repo_id: 0 + description: desc + base: 0 + repo_range: "" + token_hash: 4441de7defcfc3d21baa608dec66a562cf23307abddaabdbb836907ac5f48c8780c354891916c525b79ec7af8e95be7a09b4 + token_salt: ONNqIOnj3t + last_online: 0 + last_active: 0 + agent_labels: '["docker"]' + created: 1713512433 + updated: 1713512433 + deleted: ~ +- id: 10000006 + uuid: e1c5bb6c-de68-4335-8955-5192f76708ac + name: orga-fresh-created + version: v6.3.1+7-gc4c0ca0 + owner_id: 35 + repo_id: 0 + description: "" + base: 0 + repo_range: "" + token_hash: a61f9ee48c6847d243ace0a8936efe80af9277c7bc46d6da6e03d1d406608b8023ee66600ad24f0effaa8e3338f92ac97ac9 + token_salt: fZJKjrFGWA + last_online: 0 + last_active: 0 + agent_labels: '["docker"]' + created: 1716100832 + updated: 1716100832 + deleted: ~ +- id: 10000007 + uuid: ff755f06-948e-479b-8031-5b3e9f123e32 + name: orga-offline + version: v6.3.1+7-gc4c0ca0 + owner_id: 35 + repo_id: 0 + description: "" + base: 0 + repo_range: "" + token_hash: 9372efb38f9b64efe65065380abe2f24ef34a59d9619f4cdc08f1151e9849f0b6e722aa10538e8730288de6e2f09acdac695 + token_salt: TnU7iiIdCb + last_online: 1716100832 + last_active: 0 + agent_labels: '["docker"]' + created: 1736085520 + updated: 1716100832 + deleted: ~ diff --git a/models/fixtures/action_variable.yml b/models/fixtures/action_variable.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/action_variable.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/comment.yml b/models/fixtures/comment.yml index f4121284a6..6908d85dda 100644 --- a/models/fixtures/comment.yml +++ b/models/fixtures/comment.yml @@ -113,3 +113,380 @@ review_id: 22 assignee_id: 5 created_unix: 946684817 + +- + id: 13 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":false,"commit_ids":["4ca8bcaf27e28504df7bf996819665986b01c847","96cef4a7b72b3c208340ae6f0cf55a93e9077c93","c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2"]}' + created_unix: 1688672373 + +- + id: 14 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":false,"commit_ids":["23576dd018294e476c06e569b6b0f170d0558705"]}' + created_unix: 1688672374 + +- + id: 15 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":false,"commit_ids":["3e64625bd6eb5bcba69ac97de6c8f507402df861", "c704db5794097441aa2d9dd834d5b7e2f8f08108"]}' + created_unix: 1688672375 + +- + id: 16 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":false,"commit_ids":["811d46c7e518f4f180afb862c0db5cb8c80529ce", "747ddb3506a4fa04a7747808eb56ae16f9e933dc", "837d5c8125633d7d258f93b998e867eab0145520", "1978192d98bb1b65e11c2cf37da854fbf94bffd6"]}' + created_unix: 1688672376 + +- + id: 17 + type: 29 # push + poster_id: 2 + issue_id: 19 # in repo_id 58 + content: '{"is_force_push":true,"commit_ids":["1978192d98bb1b65e11c2cf37da854fbf94bffd6", "9b93963cf6de4dc33f915bb67f192d099c301f43"]}' + created_unix: 1749734240 + +- + id: 2000 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 1 + old_milestone_id: 0 + created_unix: 946684820 + +- + id: 2001 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 2 + old_milestone_id: 1 + created_unix: 946684920 + +- + id: 2002 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 0 + old_milestone_id: 2 + created_unix: 946685020 + +- + id: 2003 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 10 # not existing milestone + old_milestone_id: 0 + created_unix: 946685080 + +- + id: 2004 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 1 + old_milestone_id: 10 # not existing (ghost) milestone + created_unix: 946685085 + +- + id: 2005 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 10 # not existing (ghost) milestone + old_milestone_id: 1 + created_unix: 946685090 + +- + id: 2006 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 11 # not existing (ghost) milestone + old_milestone_id: 10 # not existing (ghost) milestone + created_unix: 946685095 + +- + id: 2007 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 0 + old_milestone_id: 11 # not existing (ghost) milestone + created_unix: 946685100 + +- + id: 2010 + type: 30 # project + poster_id: 1 + issue_id: 1 # in repo_id 1 + project_id: 1 + old_project_id: 0 + created_unix: 946685120 + +- + id: 2011 + type: 30 # project + poster_id: 1 + issue_id: 1 # in repo_id 1 + project_id: 2 + old_project_id: 1 + created_unix: 946685220 + +- + id: 2012 + type: 30 # project + poster_id: 1 + issue_id: 1 # in repo_id 1 + project_id: 0 + old_project_id: 2 + created_unix: 946685320 + +- + id: 2013 + type: 30 # project + poster_id: 1 + issue_id: 1 # in repo_id 1 + project_id: 10 # not existing project + old_project_id: 0 + created_unix: 946685420 + +- + id: 2020 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 1 + content: 1 # add label + created_unix: 946685520 + +- + id: 2021 + type: 7 # label + poster_id: 1 + issue_id: 1 + label_id: 2 + content: 1 # add label + created_unix: 946685620 + +- + id: 2022 + type: 7 # label + poster_id: 2 + issue_id: 1 # in repo_id 1 + label_id: 1 + content: 0 # remove label + created_unix: 946685720 + +- + id: 2023 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 1 + content: 1 # add label + created_unix: 946685720 + +- + id: 2024 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 2 + content: 0 # remove label + created_unix: 946685720 + +- + id: 2025 + type: 7 # label + poster_id: 2 + issue_id: 1 # in repo_id 1 + label_id: 2 + content: 1 # add label + created_unix: 946685820 + +- + id: 2026 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 1 + content: 0 # remove label + created_unix: 946685920 + +- + id: 2027 + type: 7 # label + poster_id: 1 + issue_id: 1 # in repo_id 1 + label_id: 2 + content: 0 # remove label + created_unix: 946685920 + +- id: 2040 + type: 9 # assignee + poster_id: 1 + issue_id: 1 # in repo_id 1 + assignee_id: 1 # self + removed_assignee: false # add assignee + created_unix: 946688020 + +- id: 2041 + type: 9 # assignee + poster_id: 2 + issue_id: 1 # in repo_id 1 + assignee_id: 1 + removed_assignee: true # remove assignee + created_unix: 946688120 + +- id: 2042 + type: 9 # assignee + poster_id: 1 + issue_id: 1 # in repo_id 1 + assignee_id: 2 + removed_assignee: false # add assignee + created_unix: 946688220 + +- id: 2043 + type: 9 # assignee + poster_id: 2 + issue_id: 1 # in repo_id 1 + assignee_id: 2 # self + removed_assignee: true # remove assignee + created_unix: 946688320 + +- id: 2050 + type: 23 # lock + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688420 + +- id: 2051 + type: 24 # unlock + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688520 + +- id: 2052 + type: 23 # lock + poster_id: 1 + issue_id: 1 # in repo_id 1 + content: "Too heated" + created_unix: 946688620 + +- id: 2053 + type: 24 # unlock + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688720 + +- id: 2060 + type: 36 # pin + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688820 + +- id: 2061 + type: 37 # unpin + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946688920 + +- id: 2070 + type: 2 # close + poster_id: 1 + issue_id: 1 # in repo_id 1 + created_unix: 946689020 + +- id: 2071 + type: 1 # reopen + poster_id: 2 + issue_id: 1 # in repo_id 1 + created_unix: 946689220 + +- id: 2072 + type: 2 # close + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + created_unix: 946689320 + +- id: 2073 + type: 1 # reopen + poster_id: 2 + issue_id: 2 # pull in repo_id 1 + created_unix: 946689420 + +- id: 2080 + type: 3 # issue reference + poster_id: 1 + issue_id: 1 # issue in repo_id 1 + ref_repo_id: 1 + ref_issue_id: 5 # issue in repo_id 1 + created_unix: 946689500 + +- id: 2081 + type: 3 # issue reference + poster_id: 1 + issue_id: 1 # issue in repo_id 1 + ref_repo_id: 1 + ref_issue_id: 2 # pull in repo_id 1 + created_unix: 946689600 + +- id: 2082 + type: 3 # issue reference + poster_id: 1 + issue_id: 1 # issue in repo_id 1 + ref_repo_id: 32 + ref_issue_id: 16 # issue in repo_id 32 + created_unix: 946689700 + +- id: 2083 + type: 3 # issue reference + poster_id: 1 + issue_id: 1 # issue in repo_id 1 + ref_repo_id: 10 + ref_issue_id: 8 # pull in repo_id 10 + created_unix: 946689800 + +- id: 2090 + type: 6 # pull reference + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + ref_repo_id: 1 + ref_issue_id: 1 # issue in repo_id 1 + created_unix: 946689900 + +- id: 2091 + type: 6 # pull reference + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + ref_repo_id: 1 + ref_issue_id: 2 # pull in repo_id 1 + created_unix: 946690000 + +- id: 2092 + type: 6 # pull reference + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + ref_repo_id: 32 + ref_issue_id: 16 # issue in repo_id 32 + created_unix: 946690050 + +- id: 2093 + type: 6 # pull reference + poster_id: 1 + issue_id: 2 # pull in repo_id 1 + ref_repo_id: 10 + ref_issue_id: 8 # pull in repo_id 10 + created_unix: 946690100 diff --git a/models/fixtures/follow.yml b/models/fixtures/follow.yml index b8d35828bf..da3d4a60c1 100644 --- a/models/fixtures/follow.yml +++ b/models/fixtures/follow.yml @@ -17,3 +17,13 @@ id: 4 user_id: 31 follow_id: 33 + +- + id: 5 + user_id: 4 + follow_id: 8 + +- + id: 6 + user_id: 5 + follow_id: 8 diff --git a/models/fixtures/hook_task.yml b/models/fixtures/hook_task.yml index fc0e03bca1..c62d451868 100644 --- a/models/fixtures/hook_task.yml +++ b/models/fixtures/hook_task.yml @@ -18,7 +18,7 @@ id: 2 hook_id: 1 uuid: uuid2 - is_delivered: false + is_delivered: true - id: 3 @@ -40,4 +40,4 @@ id: 4 hook_id: 3 uuid: uuid4 - is_delivered: false + is_delivered: true diff --git a/models/fixtures/pull_auto_merge.yml b/models/fixtures/pull_auto_merge.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/pull_auto_merge.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/pull_request.yml b/models/fixtures/pull_request.yml index fbc0d504f8..79051ffb6c 100644 --- a/models/fixtures/pull_request.yml +++ b/models/fixtures/pull_request.yml @@ -65,6 +65,7 @@ merge_base: 985f0301dba5e7b34be866819cd15ad3d8f508ee has_merged: false allow_maintainer_edit: true + commits_behind: 1 - id: 6 diff --git a/models/fixtures/repo_unit.yml b/models/fixtures/repo_unit.yml index cd49a51796..773f238645 100644 --- a/models/fixtures/repo_unit.yml +++ b/models/fixtures/repo_unit.yml @@ -795,3 +795,10 @@ type: 10 config: "{}" created_unix: 946684810 + +- + id: 115 + repo_id: 63 + type: 10 + config: "{}" + created_unix: 946684810 diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index f7aaad1f31..c383fa43ac 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -31,6 +31,8 @@ close_issues_via_commit_in_any_branch: false created_unix: 1731254961 updated_unix: 1731254961 + topics: '[]' + - id: 2 owner_id: 2 @@ -61,7 +63,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: true - + topics: '[]' - id: 3 owner_id: 3 @@ -94,6 +96,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1700000001 updated_unix: 1700000001 + topics: '[]' - id: 4 @@ -125,6 +128,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 5 @@ -132,6 +136,7 @@ owner_name: org3 lower_name: repo5 name: repo5 + default_branch: master num_watches: 0 num_stars: 0 num_forks: 0 @@ -157,6 +162,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1700000002 updated_unix: 1700000002 + topics: '[]' - id: 6 @@ -189,6 +195,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1710000001 updated_unix: 1710000001 + topics: '[]' - id: 7 @@ -221,6 +228,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1710000003 updated_unix: 1710000003 + topics: '[]' - id: 8 @@ -253,6 +261,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1710000002 updated_unix: 1710000002 + topics: '[]' - id: 9 @@ -283,6 +292,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 10 @@ -314,6 +324,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 11 @@ -345,6 +356,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 12 @@ -375,6 +387,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 13 @@ -405,6 +418,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 14 @@ -436,6 +450,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 15 @@ -467,6 +482,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 16 @@ -498,6 +514,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 17 @@ -528,6 +545,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 18 @@ -558,6 +576,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 19 @@ -588,6 +607,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 20 @@ -618,6 +638,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 21 @@ -648,6 +669,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 22 @@ -678,6 +700,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 23 @@ -708,6 +731,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 24 @@ -738,6 +762,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 25 @@ -768,6 +793,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 26 @@ -798,6 +824,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 27 @@ -828,6 +855,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 28 @@ -858,6 +886,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 29 @@ -888,6 +917,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 30 @@ -918,6 +948,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 31 @@ -949,6 +980,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 32 # org public repo @@ -981,6 +1013,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1700000003 updated_unix: 1700000003 + topics: '[]' - id: 33 @@ -1012,6 +1045,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 34 @@ -1042,6 +1076,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 35 @@ -1072,6 +1107,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 36 @@ -1103,6 +1139,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 37 @@ -1134,6 +1171,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 38 @@ -1165,6 +1203,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 39 @@ -1196,6 +1235,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 40 @@ -1227,6 +1267,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 41 @@ -1258,6 +1299,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 42 @@ -1289,6 +1331,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 43 @@ -1319,6 +1362,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 44 @@ -1350,6 +1394,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 45 @@ -1380,6 +1425,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 46 @@ -1411,6 +1457,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 47 @@ -1442,6 +1489,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 48 @@ -1473,6 +1521,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 49 @@ -1505,6 +1554,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 50 @@ -1536,6 +1586,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 51 @@ -1567,6 +1618,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 52 @@ -1598,6 +1650,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 53 @@ -1626,6 +1679,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 54 @@ -1638,6 +1692,7 @@ is_archived: false is_private: true status: 0 + topics: '[]' - id: 55 @@ -1650,6 +1705,7 @@ is_private: true num_issues: 1 status: 0 + topics: '[]' - id: 56 @@ -1663,6 +1719,7 @@ is_private: true status: 0 num_issues: 0 + topics: '[]' - id: 57 @@ -1676,6 +1733,7 @@ is_private: false status: 0 num_issues: 0 + topics: '[]' - id: 58 # org public repo @@ -1707,6 +1765,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 1059 @@ -1720,6 +1779,7 @@ is_private: false status: 0 num_issues: 0 + topics: '[]' - id: 59 @@ -1733,6 +1793,7 @@ is_private: true status: 0 num_issues: 0 + topics: '[]' - id: 60 @@ -1764,6 +1825,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 61 @@ -1795,6 +1857,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 62 owner_id: 2 @@ -1825,3 +1888,36 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' + +- + id: 63 + owner_id: 2 + owner_name: user2 + lower_name: test_action_run_search + name: test_action_run_search + default_branch: main + num_watches: 0 + num_stars: 0 + num_forks: 0 + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + num_milestones: 0 + num_closed_milestones: 0 + num_projects: 0 + num_closed_projects: 0 + is_private: true + is_empty: false + is_archived: false + is_mirror: false + status: 0 + is_fork: false + fork_id: 0 + is_template: false + template_id: 0 + size: 0 + is_fsck_enabled: true + close_issues_via_commit_in_any_branch: false + topics: '[]' diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml index 149fe90888..a863f1203a 100644 --- a/models/fixtures/team.yml +++ b/models/fixtures/team.yml @@ -239,3 +239,15 @@ num_members: 2 includes_all_repositories: false can_create_org_repo: false + +- + id: 25 + org_id: 17 + lower_name: super-user + name: super-user + description: "" + authorize: 3 + num_repos: 0 + num_members: 0 + includes_all_repositories: 0 + can_create_org_repo: 0 diff --git a/models/fixtures/team_unit.yml b/models/fixtures/team_unit.yml index e8f8d0e422..4d282a7eb5 100644 --- a/models/fixtures/team_unit.yml +++ b/models/fixtures/team_unit.yml @@ -329,3 +329,10 @@ team_id: 22 type: 3 access_mode: 1 + +- + id: 84 + org_id: 17 + team_id: 25 + type: 3 + access_mode: 3 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 630505b8b4..00aa182540 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -53,6 +53,7 @@ login_source: 0 login_name: user2 type: 0 + website: https://keyoxide.org/eb114f5e6c0dc2bcdd183550a4b61a2dc5923710 salt: ZogKvWdyEx max_repo_creation: -1 is_active: true @@ -69,7 +70,7 @@ num_followers: 2 num_following: 1 num_stars: 2 - num_repos: 17 + num_repos: 18 num_teams: 0 num_members: 0 visibility: 0 @@ -93,7 +94,7 @@ login_name: org3 type: 1 salt: ZogKvWdyEx - max_repo_creation: -1 + max_repo_creation: 1000 is_active: false is_admin: false is_restricted: false @@ -143,7 +144,7 @@ avatar_email: user4@example.com use_custom_avatar: true num_followers: 0 - num_following: 1 + num_following: 2 num_stars: 0 num_repos: 0 num_teams: 0 @@ -181,7 +182,7 @@ avatar_email: user5@example.com use_custom_avatar: true num_followers: 0 - num_following: 0 + num_following: 1 num_stars: 0 num_repos: 1 num_teams: 0 @@ -294,7 +295,7 @@ avatar: "" avatar_email: user8@example.com use_custom_avatar: true - num_followers: 1 + num_followers: 3 num_following: 1 num_stars: 0 num_repos: 0 @@ -641,7 +642,7 @@ num_following: 0 num_stars: 0 num_repos: 2 - num_teams: 3 + num_teams: 4 num_members: 4 visibility: 0 repo_admin_change_team_access: false diff --git a/models/forgefed/federationhost.go b/models/forgefed/federationhost.go index b60c0c39cf..978847bd95 100644 --- a/models/forgefed/federationhost.go +++ b/models/forgefed/federationhost.go @@ -1,33 +1,41 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. +// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package forgefed import ( + "database/sql" "fmt" + "net/url" "strings" "time" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/validation" ) // FederationHost data type // swagger:model type FederationHost struct { - ID int64 `xorm:"pk autoincr"` - HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"` - NodeInfo NodeInfo `xorm:"extends NOT NULL"` - LatestActivity time.Time `xorm:"NOT NULL"` - Created timeutil.TimeStamp `xorm:"created"` - Updated timeutil.TimeStamp `xorm:"updated"` + ID int64 `xorm:"pk autoincr"` + HostFqdn string `xorm:"host_fqdn UNIQUE(federation_host) INDEX VARCHAR(255) NOT NULL"` + HostPort uint16 `xorm:" UNIQUE(federation_host) INDEX NOT NULL DEFAULT 443"` + NodeInfo NodeInfo `xorm:"extends NOT NULL"` + HostSchema string `xorm:"NOT NULL DEFAULT 'https'"` + LatestActivity time.Time `xorm:"NOT NULL"` + KeyID sql.NullString `xorm:"key_id UNIQUE"` + PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"` + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` } // Factory function for FederationHost. Created struct is asserted to be valid. -func NewFederationHost(nodeInfo NodeInfo, hostFqdn string) (FederationHost, error) { +func NewFederationHost(hostFqdn string, nodeInfo NodeInfo, port uint16, schema string) (FederationHost, error) { result := FederationHost{ - HostFqdn: strings.ToLower(hostFqdn), - NodeInfo: nodeInfo, + HostFqdn: strings.ToLower(hostFqdn), + NodeInfo: nodeInfo, + HostPort: port, + HostSchema: schema, } if valid, err := validation.IsValid(result); !valid { return FederationHost{}, err @@ -35,11 +43,20 @@ func NewFederationHost(nodeInfo NodeInfo, hostFqdn string) (FederationHost, erro return result, nil } +func (host FederationHost) AsURL() url.URL { + return url.URL{ + Scheme: host.HostSchema, + Host: fmt.Sprintf("%v:%v", host.HostFqdn, host.HostPort), + } +} + // Validate collects error strings in a slice and returns this func (host FederationHost) Validate() []string { var result []string result = append(result, validation.ValidateNotEmpty(host.HostFqdn, "HostFqdn")...) result = append(result, validation.ValidateMaxLen(host.HostFqdn, 255, "HostFqdn")...) + result = append(result, validation.ValidateNotEmpty(host.HostPort, "HostPort")...) + result = append(result, validation.ValidateNotEmpty(host.HostSchema, "HostSchema")...) result = append(result, host.NodeInfo.Validate()...) if host.HostFqdn != strings.ToLower(host.HostFqdn) { result = append(result, fmt.Sprintf("HostFqdn has to be lower case but was: %v", host.HostFqdn)) diff --git a/models/forgefed/federationhost_repository.go b/models/forgefed/federationhost_repository.go index 03d8741c58..687966605f 100644 --- a/models/forgefed/federationhost_repository.go +++ b/models/forgefed/federationhost_repository.go @@ -6,10 +6,9 @@ package forgefed import ( "context" "fmt" - "strings" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/models/db" + "forgejo.org/modules/validation" ) func init() { @@ -30,9 +29,9 @@ func GetFederationHost(ctx context.Context, ID int64) (*FederationHost, error) { return host, nil } -func FindFederationHostByFqdn(ctx context.Context, fqdn string) (*FederationHost, error) { +func findFederationHostFromDB(ctx context.Context, searchKey, searchValue string) (*FederationHost, error) { host := new(FederationHost) - has, err := db.GetEngine(ctx).Where("host_fqdn=?", strings.ToLower(fqdn)).Get(host) + has, err := db.GetEngine(ctx).Where(searchKey, searchValue).Get(host) if err != nil { return nil, err } else if !has { @@ -44,6 +43,24 @@ func FindFederationHostByFqdn(ctx context.Context, fqdn string) (*FederationHost return host, nil } +func FindFederationHostByFqdnAndPort(ctx context.Context, fqdn string, port uint16) (*FederationHost, error) { + host := new(FederationHost) + has, err := db.GetEngine(ctx).Where("host_fqdn=? AND host_port=?", fqdn, port).Get(host) + if err != nil { + return nil, err + } else if !has { + return nil, nil + } + if res, err := validation.IsValid(host); !res { + return nil, err + } + return host, nil +} + +func FindFederationHostByKeyID(ctx context.Context, keyID string) (*FederationHost, error) { + return findFederationHostFromDB(ctx, "key_id=?", keyID) +} + func CreateFederationHost(ctx context.Context, host *FederationHost) error { if res, err := validation.IsValid(host); !res { return err diff --git a/models/forgefed/federationhost_test.go b/models/forgefed/federationhost_test.go index ea5494c6e9..d11affbae0 100644 --- a/models/forgefed/federationhost_test.go +++ b/models/forgefed/federationhost_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/validation" ) func Test_FederationHostValidation(t *testing.T) { @@ -18,6 +18,8 @@ func Test_FederationHostValidation(t *testing.T) { SoftwareName: "forgejo", }, LatestActivity: time.Now(), + HostPort: 443, + HostSchema: "https", } if res, err := validation.IsValid(sut); !res { t.Errorf("sut should be valid but was %q", err) @@ -29,9 +31,11 @@ func Test_FederationHostValidation(t *testing.T) { SoftwareName: "forgejo", }, LatestActivity: time.Now(), + HostPort: 443, + HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: HostFqdn empty") + t.Error("sut should be invalid: HostFqdn empty") } sut = FederationHost{ @@ -40,18 +44,22 @@ func Test_FederationHostValidation(t *testing.T) { SoftwareName: "forgejo", }, LatestActivity: time.Now(), + HostPort: 443, + HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: HostFqdn too long (len=256)") + t.Error("sut should be invalid: HostFqdn too long (len=256)") } sut = FederationHost{ HostFqdn: "host.do.main", NodeInfo: NodeInfo{}, LatestActivity: time.Now(), + HostPort: 443, + HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: NodeInfo invalid") + t.Error("sut should be invalid: NodeInfo invalid") } sut = FederationHost{ @@ -60,9 +68,11 @@ func Test_FederationHostValidation(t *testing.T) { SoftwareName: "forgejo", }, LatestActivity: time.Now().Add(1 * time.Hour), + HostPort: 443, + HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: Future timestamp") + t.Error("sut should be invalid: Future timestamp") } sut = FederationHost{ @@ -71,8 +81,10 @@ func Test_FederationHostValidation(t *testing.T) { SoftwareName: "forgejo", }, LatestActivity: time.Now(), + HostPort: 443, + HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: HostFqdn lower case") + t.Error("sut should be invalid: HostFqdn lower case") } } diff --git a/models/forgefed/nodeinfo.go b/models/forgefed/nodeinfo.go index 66d2eca7aa..38f51304c5 100644 --- a/models/forgefed/nodeinfo.go +++ b/models/forgefed/nodeinfo.go @@ -6,7 +6,7 @@ package forgefed import ( "net/url" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/validation" "github.com/valyala/fastjson" ) @@ -17,12 +17,14 @@ type ( ) const ( - ForgejoSourceType SoftwareNameType = "forgejo" - GiteaSourceType SoftwareNameType = "gitea" + ForgejoSourceType SoftwareNameType = "forgejo" + GiteaSourceType SoftwareNameType = "gitea" + MastodonSourceType SoftwareNameType = "mastodon" + GoToSocialSourceType SoftwareNameType = "gotosocial" ) var KnownSourceTypes = []any{ - ForgejoSourceType, GiteaSourceType, + ForgejoSourceType, GiteaSourceType, MastodonSourceType, GoToSocialSourceType, } // ------------------------------------------------ NodeInfoWellKnown ------------------------------------------------ diff --git a/models/forgefed/nodeinfo_test.go b/models/forgefed/nodeinfo_test.go index 4c73bb44d8..a0c9781b90 100644 --- a/models/forgefed/nodeinfo_test.go +++ b/models/forgefed/nodeinfo_test.go @@ -4,12 +4,12 @@ package forgefed import ( - "fmt" + "errors" "reflect" "strings" "testing" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/validation" ) func Test_NodeInfoWellKnownUnmarshalJSON(t *testing.T) { @@ -28,7 +28,7 @@ func Test_NodeInfoWellKnownUnmarshalJSON(t *testing.T) { }, "empty": { item: []byte(``), - wantErr: fmt.Errorf("cannot parse JSON: cannot parse empty string; unparsed tail: \"\""), + wantErr: errors.New("cannot parse JSON: cannot parse empty string; unparsed tail: \"\""), }, } @@ -74,7 +74,7 @@ func Test_NewNodeInfoWellKnown(t *testing.T) { _, err := NewNodeInfoWellKnown([]byte(`invalid`)) if err == nil { - t.Errorf("error was expected here") + t.Error("error was expected here") } } @@ -87,6 +87,6 @@ func Test_NewNodeInfo(t *testing.T) { _, err := NewNodeInfo([]byte(`invalid`)) if err == nil { - t.Errorf("error was expected here") + t.Error("error was expected here") } } diff --git a/models/forgejo/semver/main_test.go b/models/forgejo/semver/main_test.go index 10875f14fe..dcc9d588cd 100644 --- a/models/forgejo/semver/main_test.go +++ b/models/forgejo/semver/main_test.go @@ -5,12 +5,12 @@ package semver import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" - _ "code.gitea.io/gitea/models" - _ "code.gitea.io/gitea/models/actions" - _ "code.gitea.io/gitea/models/activities" - _ "code.gitea.io/gitea/models/forgefed" + _ "forgejo.org/models" + _ "forgejo.org/models/actions" + _ "forgejo.org/models/activities" + _ "forgejo.org/models/forgefed" ) func TestMain(m *testing.M) { diff --git a/models/forgejo/semver/semver.go b/models/forgejo/semver/semver.go index 7f122d2301..24a3db9181 100644 --- a/models/forgejo/semver/semver.go +++ b/models/forgejo/semver/semver.go @@ -5,7 +5,7 @@ package semver import ( "context" - "code.gitea.io/gitea/models/db" + "forgejo.org/models/db" "github.com/hashicorp/go-version" ) diff --git a/models/forgejo/semver/semver_test.go b/models/forgejo/semver/semver_test.go index a508c69b18..10989ecad3 100644 --- a/models/forgejo/semver/semver_test.go +++ b/models/forgejo/semver/semver_test.go @@ -5,8 +5,8 @@ package semver import ( "testing" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/db" + "forgejo.org/models/unittest" "github.com/hashicorp/go-version" "github.com/stretchr/testify/assert" @@ -22,7 +22,7 @@ func TestForgejoSemVerSetGet(t *testing.T) { require.NoError(t, SetVersionString(ctx, newVersion.String())) databaseVersion, err := GetVersion(ctx) require.NoError(t, err) - assert.EqualValues(t, newVersion.String(), databaseVersion.String()) + assert.Equal(t, newVersion.String(), databaseVersion.String()) assert.True(t, newVersion.Equal(databaseVersion)) } @@ -36,12 +36,12 @@ func TestForgejoSemVerMissing(t *testing.T) { v, err := GetVersion(ctx) require.NoError(t, err) - assert.EqualValues(t, "1.0.0", v.String()) + assert.Equal(t, "1.0.0", v.String()) _, err = e.Exec("drop table forgejo_sem_ver") require.NoError(t, err) v, err = GetVersion(ctx) require.NoError(t, err) - assert.EqualValues(t, "1.0.0", v.String()) + assert.Equal(t, "1.0.0", v.String()) } diff --git a/models/forgejo_migrations/main_test.go b/models/forgejo_migrations/main_test.go index 2297f74f73..031fe8090d 100644 --- a/models/forgejo_migrations/main_test.go +++ b/models/forgejo_migrations/main_test.go @@ -6,7 +6,7 @@ package forgejo_migrations //nolint:revive import ( "testing" - migration_tests "code.gitea.io/gitea/models/migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 935b072331..94469b7371 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -5,15 +5,16 @@ package forgejo_migrations //nolint:revive import ( "context" + "errors" "fmt" "os" - "code.gitea.io/gitea/models/forgejo/semver" - forgejo_v1_20 "code.gitea.io/gitea/models/forgejo_migrations/v1_20" - forgejo_v1_22 "code.gitea.io/gitea/models/forgejo_migrations/v1_22" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/forgejo/semver" + forgejo_v1_20 "forgejo.org/models/forgejo_migrations/v1_20" + forgejo_v1_22 "forgejo.org/models/forgejo_migrations/v1_22" + "forgejo.org/modules/git" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "xorm.io/xorm" "xorm.io/xorm/names" @@ -94,6 +95,22 @@ var migrations = []*Migration{ NewMigration("Add `created_unix` column to `user_redirect` table", AddCreatedUnixToRedirect), // v27 -> v28 NewMigration("Add pronoun privacy settings to user", AddHidePronounsOptionToUser), + // v28 -> v29 + NewMigration("Add public key information to `FederatedUser` and `FederationHost`", AddPublicKeyInformationForFederation), + // v29 -> v30 + NewMigration("Migrate `User.NormalizedFederatedURI` column to extract port & schema into FederatedHost", MigrateNormalizedFederatedURI), + // v30 -> v31 + NewMigration("Normalize repository.topics to empty slice instead of null", SetTopicsAsEmptySlice), + // v31 -> v32 + NewMigration("Migrate maven package name concatenation", ChangeMavenArtifactConcatenation), + // v32 -> v33 + NewMigration("Add federated user activity tables, update the `federated_user` table & add indexes", FederatedUserActivityMigration), + // v33 -> v34 + NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun), + // v34 -> v35 + NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), + // v35 -> v36 + NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission), } // GetCurrentDBVersion returns the current Forgejo database version. @@ -126,7 +143,7 @@ func EnsureUpToDate(x *xorm.Engine) error { } if currentDB < 0 { - return fmt.Errorf("database has not been initialized") + return errors.New("database has not been initialized") } expected := ExpectedVersion() diff --git a/models/forgejo_migrations/migrate_test.go b/models/forgejo_migrations/migrate_test.go index 48ee4f77b1..20653929a3 100644 --- a/models/forgejo_migrations/migrate_test.go +++ b/models/forgejo_migrations/migrate_test.go @@ -6,7 +6,7 @@ package forgejo_migrations //nolint:revive import ( "testing" - migration_tests "code.gitea.io/gitea/models/migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/require" ) diff --git a/models/forgejo_migrations/v14.go b/models/forgejo_migrations/v14.go index f6dd35ecf0..53f1ef2223 100644 --- a/models/forgejo_migrations/v14.go +++ b/models/forgejo_migrations/v14.go @@ -4,7 +4,7 @@ package forgejo_migrations //nolint:revive import ( - "code.gitea.io/gitea/models/migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/forgejo_migrations/v15.go b/models/forgejo_migrations/v15.go index d7ed19ca7c..5e5588dd05 100644 --- a/models/forgejo_migrations/v15.go +++ b/models/forgejo_migrations/v15.go @@ -6,7 +6,7 @@ package forgejo_migrations //nolint:revive import ( "time" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/timeutil" "xorm.io/xorm" ) diff --git a/models/forgejo_migrations/v1_20/v1.go b/models/forgejo_migrations/v1_20/v1.go index 1097613655..72beaf23de 100644 --- a/models/forgejo_migrations/v1_20/v1.go +++ b/models/forgejo_migrations/v1_20/v1.go @@ -4,7 +4,7 @@ package forgejo_v1_20 //nolint:revive import ( - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/timeutil" "xorm.io/xorm" ) diff --git a/models/forgejo_migrations/v1_20/v3.go b/models/forgejo_migrations/v1_20/v3.go index caa4f1aa99..cce227e6eb 100644 --- a/models/forgejo_migrations/v1_20/v3.go +++ b/models/forgejo_migrations/v1_20/v3.go @@ -4,7 +4,7 @@ package forgejo_v1_20 //nolint:revive import ( - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/timeutil" "xorm.io/xorm" ) diff --git a/models/forgejo_migrations/v1_22/main_test.go b/models/forgejo_migrations/v1_22/main_test.go index 097110893f..03c4c5272c 100644 --- a/models/forgejo_migrations/v1_22/main_test.go +++ b/models/forgejo_migrations/v1_22/main_test.go @@ -6,7 +6,7 @@ package v1_22 //nolint import ( "testing" - migration_tests "code.gitea.io/gitea/models/migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/forgejo_migrations/v1_22/v11.go b/models/forgejo_migrations/v1_22/v11.go index c693993565..17bb592379 100644 --- a/models/forgejo_migrations/v1_22/v11.go +++ b/models/forgejo_migrations/v1_22/v11.go @@ -4,7 +4,7 @@ package v1_22 //nolint import ( - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/timeutil" "xorm.io/xorm" ) diff --git a/models/forgejo_migrations/v1_22/v8_test.go b/models/forgejo_migrations/v1_22/v8_test.go index 128fd08ab0..baaba7290f 100644 --- a/models/forgejo_migrations/v1_22/v8_test.go +++ b/models/forgejo_migrations/v1_22/v8_test.go @@ -6,7 +6,7 @@ package v1_22 //nolint import ( "testing" - migration_tests "code.gitea.io/gitea/models/migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -29,7 +29,7 @@ func Test_RemoveSSHSignaturesFromReleaseNotes(t *testing.T) { require.NoError(t, err) assert.Len(t, releases, 3) - assert.Equal(t, "", releases[0].Note) + assert.Empty(t, releases[0].Note) assert.Equal(t, "A message.\n", releases[1].Note) assert.Equal(t, "no signature present here", releases[2].Note) } diff --git a/models/forgejo_migrations/v25.go b/models/forgejo_migrations/v25.go index 5e3dff80b3..8e3032a40c 100644 --- a/models/forgejo_migrations/v25.go +++ b/models/forgejo_migrations/v25.go @@ -9,11 +9,11 @@ import ( "encoding/base64" "fmt" - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/secret" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/secret" + "forgejo.org/modules/setting" "xorm.io/xorm" "xorm.io/xorm/schemas" diff --git a/models/forgejo_migrations/v25_test.go b/models/forgejo_migrations/v25_test.go index 73b97f7a35..e7402fd021 100644 --- a/models/forgejo_migrations/v25_test.go +++ b/models/forgejo_migrations/v25_test.go @@ -6,10 +6,10 @@ package forgejo_migrations //nolint:revive import ( "testing" - "code.gitea.io/gitea/models/auth" - migration_tests "code.gitea.io/gitea/models/migrations/test" - "code.gitea.io/gitea/modules/keying" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/auth" + migration_tests "forgejo.org/models/migrations/test" + "forgejo.org/modules/keying" + "forgejo.org/modules/timeutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/forgejo_migrations/v26.go b/models/forgejo_migrations/v26.go index 5efe48a302..3292d93ffd 100644 --- a/models/forgejo_migrations/v26.go +++ b/models/forgejo_migrations/v26.go @@ -7,8 +7,8 @@ import "xorm.io/xorm" func AddHashBlake2bToPackageBlob(x *xorm.Engine) error { type PackageBlob struct { - ID int64 `xorm:"pk autoincr"` - HashBlake2b string + ID int64 `xorm:"pk autoincr"` + HashBlake2b string `xorm:"hash_blake2b char(128) UNIQUE(blake2b) INDEX"` } return x.Sync(&PackageBlob{}) } diff --git a/models/forgejo_migrations/v27.go b/models/forgejo_migrations/v27.go index b3a93a9aad..2efa3485a8 100644 --- a/models/forgejo_migrations/v27.go +++ b/models/forgejo_migrations/v27.go @@ -4,7 +4,7 @@ package forgejo_migrations //nolint:revive import ( - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/timeutil" "xorm.io/xorm" ) diff --git a/models/forgejo_migrations/v29.go b/models/forgejo_migrations/v29.go new file mode 100644 index 0000000000..d0c2f723ae --- /dev/null +++ b/models/forgejo_migrations/v29.go @@ -0,0 +1,29 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forgejo_migrations //nolint:revive + +import ( + "database/sql" + + "xorm.io/xorm" +) + +func AddPublicKeyInformationForFederation(x *xorm.Engine) error { + type FederationHost struct { + KeyID sql.NullString `xorm:"key_id UNIQUE"` + PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"` + } + + err := x.Sync(&FederationHost{}) + if err != nil { + return err + } + + type FederatedUser struct { + KeyID sql.NullString `xorm:"key_id UNIQUE"` + PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"` + } + + return x.Sync(&FederatedUser{}) +} diff --git a/models/forgejo_migrations/v30.go b/models/forgejo_migrations/v30.go new file mode 100644 index 0000000000..6c41a55316 --- /dev/null +++ b/models/forgejo_migrations/v30.go @@ -0,0 +1,106 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forgejo_migrations //nolint:revive + +import ( + "time" + + "forgejo.org/models/migrations/base" + "forgejo.org/modules/forgefed" + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" + + "xorm.io/xorm" +) + +func MigrateNormalizedFederatedURI(x *xorm.Engine) error { + // Update schema + type FederatedUser struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"NOT NULL"` + ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + NormalizedOriginalURL string + } + type User struct { + ID int64 `xorm:"pk autoincr"` + NormalizedFederatedURI string + } + type FederationHost struct { + ID int64 `xorm:"pk autoincr"` + HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"` + NodeInfo NodeInfo `xorm:"extends NOT NULL"` + HostPort uint16 `xorm:"NOT NULL DEFAULT 443"` + HostSchema string `xorm:"NOT NULL DEFAULT 'https'"` + LatestActivity time.Time `xorm:"NOT NULL"` + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` + } + if err := x.Sync(new(User), new(FederatedUser), new(FederationHost)); err != nil { + return err + } + + // Migrate + sessMigration := x.NewSession() + defer sessMigration.Close() + if err := sessMigration.Begin(); err != nil { + return err + } + federatedUsers := make([]*FederatedUser, 0) + err := sessMigration.OrderBy("id").Find(&federatedUsers) + if err != nil { + return err + } + + for _, federatedUser := range federatedUsers { + if federatedUser.NormalizedOriginalURL != "" { + log.Trace("migration[30]: FederatedUser was already migrated %v", federatedUser) + } else { + user := &User{} + has, err := sessMigration.Where("id=?", federatedUser.UserID).Get(user) + if err != nil { + return err + } + + if !has { + log.Debug("migration[30]: User missing for federated user: %v", federatedUser) + _, err := sessMigration.Delete(federatedUser) + if err != nil { + return err + } + } else { + // Migrate User.NormalizedFederatedURI -> FederatedUser.NormalizedOriginalUrl + sql := "UPDATE `federated_user` SET `normalized_original_url` = ? WHERE `id` = ?" + if _, err := sessMigration.Exec(sql, user.NormalizedFederatedURI, federatedUser.FederationHostID); err != nil { + return err + } + + // Migrate (Port, Schema) FederatedUser.NormalizedOriginalUrl -> FederationHost.(Port, Schema) + actorID, err := forgefed.NewActorID(user.NormalizedFederatedURI) + if err != nil { + return err + } + sql = "UPDATE `federation_host` SET `host_port` = ?, `host_schema` = ? WHERE `id` = ?" + if _, err := sessMigration.Exec(sql, actorID.HostPort, actorID.HostSchema, federatedUser.FederationHostID); err != nil { + return err + } + } + } + } + + if err := sessMigration.Commit(); err != nil { + return err + } + + // Drop User.NormalizedFederatedURI field in extra transaction + sessSchema := x.NewSession() + defer sessSchema.Close() + if err := sessSchema.Begin(); err != nil { + return err + } + if err := base.DropTableColumns(sessSchema, "user", "normalized_federated_uri"); err != nil { + return err + } + return sessSchema.Commit() +} diff --git a/models/forgejo_migrations/v30_test.go b/models/forgejo_migrations/v30_test.go new file mode 100644 index 0000000000..f826dab815 --- /dev/null +++ b/models/forgejo_migrations/v30_test.go @@ -0,0 +1,81 @@ +// Copyright 2025 The Forgejo Authors. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "testing" + "time" + + migration_tests "forgejo.org/models/migrations/test" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/require" + "xorm.io/xorm/schemas" +) + +func Test_MigrateNormalizedFederatedURI(t *testing.T) { + // Old structs + type User struct { + ID int64 `xorm:"pk autoincr"` + NormalizedFederatedURI string + } + type FederatedUser struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"NOT NULL"` + ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + } + type FederationHost struct { + ID int64 `xorm:"pk autoincr"` + HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"` + SoftwareName string `xorm:"NOT NULL"` + LatestActivity time.Time `xorm:"NOT NULL"` + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` + } + + // Prepare TestEnv + x, deferable := migration_tests.PrepareTestEnv(t, 0, + new(User), + new(FederatedUser), + new(FederationHost), + ) + defer deferable() + if x == nil || t.Failed() { + return + } + + // test for expected results + getColumn := func(tn, co string) *schemas.Column { + tables, err := x.DBMetas() + require.NoError(t, err) + var table *schemas.Table + for _, elem := range tables { + if elem.Name == tn { + table = elem + break + } + } + return table.GetColumn(co) + } + + require.NotNil(t, getColumn("user", "normalized_federated_uri")) + require.Nil(t, getColumn("federation_host", "host_port")) + require.Nil(t, getColumn("federation_host", "host_schema")) + cnt1, err := x.Table("federated_user").Count() + require.NoError(t, err) + require.Equal(t, int64(2), cnt1) + + require.NoError(t, MigrateNormalizedFederatedURI(x)) + + require.Nil(t, getColumn("user", "normalized_federated_uri")) + require.NotNil(t, getColumn("federation_host", "host_port")) + require.NotNil(t, getColumn("federation_host", "host_schema")) + cnt2, err := x.Table("federated_user").Count() + require.NoError(t, err) + require.Equal(t, int64(1), cnt2) + + // idempotent + require.NoError(t, MigrateNormalizedFederatedURI(x)) +} diff --git a/models/forgejo_migrations/v31.go b/models/forgejo_migrations/v31.go new file mode 100644 index 0000000000..fdcab21b1a --- /dev/null +++ b/models/forgejo_migrations/v31.go @@ -0,0 +1,58 @@ +// Copyright 2025 The Forgejo Authors. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "xorm.io/xorm" + "xorm.io/xorm/schemas" +) + +func SetTopicsAsEmptySlice(x *xorm.Engine) error { + var err error + switch x.Dialect().URI().DBType { + case schemas.MYSQL: + _, err = x.Exec("UPDATE `repository` SET topics = '[]' WHERE topics IS NULL OR topics = 'null'") + case schemas.SQLITE: + _, err = x.Exec("UPDATE `repository` SET topics = '[]' WHERE topics IS NULL OR topics = 'null'") + case schemas.POSTGRES: + _, err = x.Exec("UPDATE `repository` SET topics = '[]' WHERE topics IS NULL OR topics::text = 'null'") + } + + if err != nil { + return err + } + + if x.Dialect().URI().DBType == schemas.SQLITE { + sessMigration := x.NewSession() + defer sessMigration.Close() + if err := sessMigration.Begin(); err != nil { + return err + } + _, err = sessMigration.Exec("ALTER TABLE `repository` RENAME COLUMN `topics` TO `topics_backup`") + if err != nil { + return err + } + _, err = sessMigration.Exec("ALTER TABLE `repository` ADD COLUMN `topics` TEXT NOT NULL DEFAULT '[]'") + if err != nil { + return err + } + _, err = sessMigration.Exec("UPDATE `repository` SET `topics` = `topics_backup`") + if err != nil { + return err + } + _, err = sessMigration.Exec("ALTER TABLE `repository` DROP COLUMN `topics_backup`") + if err != nil { + return err + } + + return sessMigration.Commit() + } + + type Repository struct { + ID int64 `xorm:"pk autoincr"` + Topics []string `xorm:"TEXT JSON NOT NULL"` + } + + return x.Sync(new(Repository)) +} diff --git a/models/forgejo_migrations/v31_test.go b/models/forgejo_migrations/v31_test.go new file mode 100644 index 0000000000..5b4aac2a60 --- /dev/null +++ b/models/forgejo_migrations/v31_test.go @@ -0,0 +1,38 @@ +// Copyright 2025 The Forgejo Authors. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "testing" + + migration_tests "forgejo.org/models/migrations/test" + + "github.com/stretchr/testify/require" +) + +func Test_SetTopicsAsEmptySlice(t *testing.T) { + type Repository struct { + ID int64 `xorm:"pk autoincr"` + Topics []string `xorm:"TEXT JSON"` + } + + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Repository)) + defer deferable() + if x == nil || t.Failed() { + return + } + + require.NoError(t, SetTopicsAsEmptySlice(x)) + + var repos []Repository + require.NoError(t, x.Find(&repos)) + + for _, repo := range repos { + if repo.ID == 2 { + require.Equal(t, []string{"go", "dev"}, repo.Topics, "Valid topics should remain unchanged") + } else { + require.Equal(t, []string{}, repo.Topics, "NULL topics should be set to empty array") + } + } +} diff --git a/models/forgejo_migrations/v32.go b/models/forgejo_migrations/v32.go new file mode 100644 index 0000000000..2460003597 --- /dev/null +++ b/models/forgejo_migrations/v32.go @@ -0,0 +1,407 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "encoding/xml" + "fmt" + "regexp" + "slices" + "sort" + "strconv" + "strings" + + "forgejo.org/models/db" + "forgejo.org/models/packages" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/packages/maven" + packages_service "forgejo.org/services/packages" + + "golang.org/x/net/context" + "xorm.io/xorm" +) + +var getPackage = packages_service.GetPackageFileStream + +type Snapshot struct { + baseVersion string + date string + time string + build int +} + +type Metadata struct { + XMLName xml.Name `xml:"metadata"` + ModelVersion string `xml:"modelVersion,attr"` + GroupID string `xml:"groupId"` + ArtifactID string `xml:"artifactId"` + Version string `xml:"version"` +} + +type mavenPackageResult struct { + PackageFile *packages.PackageFile `xorm:"extends"` + PackageVersion *packages.PackageVersion `xorm:"extends"` + Package *packages.Package `xorm:"extends"` + PackageName string `xorm:"-"` + Snapshot *Snapshot `xorm:"-"` + GroupID string `xorm:"-"` + ArtifactID string `xorm:"-"` +} + +// ChangeMavenArtifactConcatenation resolves old dash-concatenated Maven coordinates and regenerates metadata. +// Note: runs per-owner in a single transaction; failures roll back all owners. +func ChangeMavenArtifactConcatenation(x *xorm.Engine) error { + return db.WithTx(db.DefaultContext, func(ctx context.Context) error { + // get unique owner IDs of Maven packages + var ownerIDs []*int64 + if err := db.GetEngine(ctx). + Table("package"). + Select("package.owner_id"). + Where("package.type = 'maven'"). + GroupBy("package.owner_id"). + OrderBy("package.owner_id DESC"). + Find(&ownerIDs); err != nil { + return err + } + + for _, id := range ownerIDs { + if err := fixMavenArtifactPerOwner(ctx, id); err != nil { + log.Error("owner %d migration failed: %v", id, err) + return err // rollback all + } + } + + return nil + }) +} + +func fixMavenArtifactPerOwner(ctx context.Context, ownerID *int64) error { + results, err := getMavenPackageResultsToUpdate(ctx, ownerID) + if err != nil { + return err + } + + if err = resolvePackageCollisions(ctx, results); err != nil { + return err + } + + if err = processPackageVersions(ctx, results); err != nil { + return err + } + + return processPackageFiles(ctx, results) +} + +// processPackageFiles updates Maven package files and versions in the database +// Returns an error if any database or processing operation fails. +func processPackageFiles(ctx context.Context, results []*mavenPackageResult) error { + processedVersion := make(map[string][]*mavenPackageResult) + + for _, r := range results { + if r.Snapshot != nil { + key := fmt.Sprintf("%s:%s", r.PackageName, r.PackageVersion.LowerVersion) + processedVersion[key] = append(processedVersion[key], r) + } + + // Only update version_id when it differs + if r.PackageVersion.ID != r.PackageFile.VersionID { + pattern := strings.TrimSuffix(r.PackageFile.Name, ".pom") + "%" + // Per routers/api/packages/maven/maven.go:338, POM files already have the `IsLead`, so no update needed for this prop + if _, err := db.GetEngine(ctx).Exec("UPDATE package_file SET version_id = ? WHERE version_id = ? and name like ?", r.PackageVersion.ID, r.PackageFile.VersionID, pattern); err != nil { + return err + } + } + } + + // If maven-metadata.xml is missing (snapshot path collision), skip regeneration + // Without this metadata, Maven cannot resolve snapshot details + for _, packageResults := range processedVersion { + sort.Slice(packageResults, func(i, j int) bool { + return packageResults[i].Snapshot.build > packageResults[j].Snapshot.build + }) + + rs := packageResults[0] + + pf, md, err := parseMetadata(ctx, rs) + if err != nil { + return err + } + + if pf != nil && md != nil && md.GroupID == rs.GroupID && md.ArtifactID == rs.ArtifactID { + if pf.VersionID != rs.PackageFile.VersionID { + if _, err := db.GetEngine(ctx).ID(pf.ID).Cols("version_id").Update(pf); err != nil { + return err + } + } + continue + } + + log.Warn("no maven-metadata.xml found for (id: %d) [%s:%s]", rs.PackageVersion.ID, rs.PackageName, rs.PackageVersion.Version) + } + + return nil +} + +// parseMetadata retrieves metadata for a Maven package file from the database and decodes it into a Metadata object. +// Returns the associated PackageFile, Metadata, and any error encountered during processing. +func parseMetadata(ctx context.Context, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { + var pf packages.PackageFile + found, err := db.GetEngine(ctx).Table(pf). + Where("version_id = ?", snapshot.PackageFile.VersionID). // still the old id + And("lower_name = ?", "maven-metadata.xml"). + Get(&pf) + if err != nil { + return nil, nil, err + } + + if !found { + return nil, nil, nil + } + + s, _, _, err := getPackage(ctx, &pf) + if err != nil { + return nil, nil, err + } + + defer s.Close() + dec := xml.NewDecoder(s) + var m Metadata + if err := dec.Decode(&m); err != nil { + return nil, nil, err + } + + return &pf, &m, nil +} + +// processPackageVersions processes Maven package versions by updating metadata or inserting new records as necessary. +// It avoids redundant updates by tracking already processed versions using a map. Returns an error on failure. +func processPackageVersions(ctx context.Context, results []*mavenPackageResult) error { + processedVersion := make(map[string]int64) + + for _, r := range results { + key := fmt.Sprintf("%s:%s", r.PackageName, r.PackageVersion.Version) + + if id, ok := processedVersion[key]; ok { + r.PackageVersion.ID = id + continue + } + + // for non collisions, just update the metadata + if r.PackageVersion.PackageID == r.Package.ID { + if _, err := db.GetEngine(ctx).ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil { + return err + } + } else { + log.Info("Create new maven package version for %s:%s", r.PackageName, r.PackageVersion.Version) + r.PackageVersion.ID = 0 + r.PackageVersion.PackageID = r.Package.ID + if _, err := db.GetEngine(ctx).Insert(r.PackageVersion); err != nil { + return err + } + } + + processedVersion[key] = r.PackageVersion.ID + } + + return nil +} + +// getMavenPackageResultsToUpdate retrieves Maven package results that need updates based on the owner ID. +// It processes POM metadata, fixes package inconsistencies, and filters corrupted package versions. +func getMavenPackageResultsToUpdate(ctx context.Context, ownerID *int64) ([]*mavenPackageResult, error) { + var candidates []*mavenPackageResult + if err := db.GetEngine(ctx). + Table("package_file"). + Select("package_file.*, package_version.*, package.*"). + Join("INNER", "package_version", "package_version.id = package_file.version_id"). + Join("INNER", "package", "package.id = package_version.package_id"). + Where("package_file.lower_name LIKE ?", "%.pom"). + And("package.type = ?", "maven"). + And("package.owner_id = ?", ownerID). + OrderBy("package_version.id DESC, package_file.id DESC"). + Find(&candidates); err != nil { + return nil, err + } + + var results []*mavenPackageResult + var corruptedVersionIDs []int64 + + // fetch actual metadata from blob as all packages needs to be fixed following the new string concatenation + for _, r := range candidates { + if err := processPomMetadata(ctx, r); err != nil { + // Skip corrupted versions; admin intervention may be needed to repair these files. + log.Warn("Failed to process package file [id: %d] ignoring package version[%d]: %v", r.PackageFile.ID, r.PackageVersion.ID, err) + + corruptedVersionIDs = append(corruptedVersionIDs, r.PackageVersion.ID) + + continue + } + + results = append(results, r) + log.Debug("Resolved id [%d] from [%s:%s] to [%s:%s] [Snapshot: %v]", r.Package.ID, r.Package.Name, r.PackageVersion.Version, r.PackageName, r.PackageVersion.Version, r.Snapshot) + } + + for _, corruptedVersionID := range corruptedVersionIDs { + for i := 0; i < len(results); { + if corruptedVersionID == results[i].PackageVersion.ID { + results = append(results[:i], results[i+1:]...) + } else { + i++ + } + } + } + + return results, nil +} + +// resolvePackageCollisions handles name collisions by keeping the first existing record and inserting new Package records for subsequent collisions. +// Returns a map from PackageName to its resolved Package.ID. +func resolvePackageCollisions(ctx context.Context, results []*mavenPackageResult) error { + // Group new names by lowerName + collisions := make(map[string][]string) + for _, r := range results { + names := collisions[r.Package.LowerName] + if !slices.Contains(names, r.PackageName) { + collisions[r.Package.LowerName] = append(names, r.PackageName) + } + } + + pkgIDByName := make(map[string]int64) + var err error + + for _, r := range results { + list := collisions[r.Package.LowerName] + + // update to the upcoming package name which is colon separated + r.Package.Name = r.PackageName + r.Package.LowerName = r.PackageName + + // exiting entry + if id, ok := pkgIDByName[r.PackageName]; ok { + r.Package.ID = id + // first package kept the current id + } else if list[0] == r.PackageName { + pkgIDByName[r.PackageName] = r.Package.ID + + if _, err = db.GetEngine(ctx).ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { + return err + } + // create a new entry + } else { + log.Info("Create new maven package for %s", r.Package.Name) + + r.Package.ID = 0 + if _, err = db.GetEngine(ctx).Insert(r.Package); err != nil { + return err + } + + pkgIDByName[r.PackageName] = r.Package.ID + } + } + + return nil +} + +// processPomMetadata processes a Maven package file, parses its POM metadata, and updates PackageVersion information. +func processPomMetadata(ctx context.Context, mpr *mavenPackageResult) error { + s, _, _, err := getPackage(ctx, mpr.PackageFile) + if err != nil { + return fmt.Errorf("unable to get package stream: %v", err) + } + defer s.Close() + + actualPom, err := maven.ParsePackageMetaData(s) + if err != nil { + return fmt.Errorf("failed to parse POM metadata: %v", err) + } + + raw, err := json.Marshal(actualPom) + if err != nil { + return fmt.Errorf("failed to marshal metadata: %v", err) + } + + var currentPom *maven.Metadata + if err = json.Unmarshal([]byte(mpr.PackageVersion.MetadataJSON), ¤tPom); err != nil { + return fmt.Errorf("failed to unmarshal metadata: %v", err) + } + + // since the rest api can also be (ab)used to upload artifacts wrong, just ignore them + if isInvalidMatch(currentPom, actualPom) { + return fmt.Errorf("artifact mismatch: actual [%s] expected [%s]", actualPom.ArtifactID, currentPom.ArtifactID) + } + + // this will also fix packages that missed its groupID + // Ref: https://codeberg.org/forgejo/forgejo/pulls/6329 + mpr.PackageVersion.MetadataJSON = string(raw) + + // Since Maven packages are case-sensitive, avoid potential clashes and clean-ups + // by enforcing consistent case handling similar to RPM packages. + mpr.PackageName = fmt.Sprintf("%s:%s", actualPom.GroupID, actualPom.ArtifactID) + + mpr.GroupID = actualPom.GroupID + mpr.ArtifactID = actualPom.ArtifactID + + if strings.HasSuffix(mpr.PackageVersion.Version, "-SNAPSHOT") { + snap, err := extraSnapshotDetails(currentPom, actualPom, mpr) + if err != nil { + return err + } + mpr.Snapshot = snap + } else { + // only snapshots are affected but kept in case of not complete fixtures + expectedFileName := fmt.Sprintf("%s-%s.pom", actualPom.ArtifactID, mpr.PackageVersion.Version) + if mpr.PackageFile.Name != expectedFileName { + log.Warn("invalid package file name - this is a collision which needs to be resolved expected [%s], actual [%s]", expectedFileName, mpr.PackageFile.Name) + } + } + + return nil +} + +// extraSnapshotDetails extracts detailed snapshot information +// Returns a Snapshot object encapsulating the extracted details or an error if the filename is invalid or parsing fails. +func extraSnapshotDetails(currentPom, actualPom *maven.Metadata, mpr *mavenPackageResult) (*Snapshot, error) { + pattern := `^%s-` + + `(?P[\d\.]+)-` + + `(?P\d{8})\.` + + `(?P

@forgejo@floss.social

`) + + test( + "!forgejo@programming.dev", + `

!forgejo@programming.dev

`) + + test( + "@#&@forgejo.org", + `

@#&@forgejo.org

`) } func TestRender_emoji(t *testing.T) { @@ -721,7 +734,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 2 to 3 in 190d949`+ ``+ ``+ @@ -755,7 +768,7 @@ func TestRender_FilePreview(t *testing.T) { `gogits/gogs – `+ `path/to/file.go`+ ``+ - ``+ + ``+ `Lines 2 to 3 in gogits/gogs@190d949`+ ``+ ``+ @@ -791,7 +804,7 @@ func TestRender_FilePreview(t *testing.T) { `gogits/gogs – `+ `single-line.txt`+ ``+ - ``+ + ``+ `Line 1 in gogits/gogs@4c1aaf5`+ ``+ ``+ @@ -834,7 +847,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 2 to 3 in 190d949`+ ``+ ``+ @@ -865,7 +878,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 2 to 3 in 190d949`+ ``+ ``+ @@ -898,7 +911,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 2 to 3 in 190d949`+ ``+ ``+ @@ -923,7 +936,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 2 to 3 in 190d949`+ ``+ ``+ @@ -954,7 +967,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 2 to 3 in 190d949`+ ``+ ``+ @@ -979,7 +992,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 2 to 3 in 190d949`+ ``+ ``+ @@ -1004,7 +1017,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 2 to 3 in 190d949`+ ``+ ``+ @@ -1039,7 +1052,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 1 to 2 in c991312`+ ``+ ``+ @@ -1072,7 +1085,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 1 to 2 in c991312`+ ``+ ``+ @@ -1107,7 +1120,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Lines 2 to 3 in 190d949`+ ``+ ``+ @@ -1142,7 +1155,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ - ``+ + ``+ `Line 1 in eeb243c`+ ``+ ``+ diff --git a/modules/markup/markdown/callout/github.go b/modules/markup/markdown/callout/github.go index adc2071823..49ad249696 100644 --- a/modules/markup/markdown/callout/github.go +++ b/modules/markup/markdown/callout/github.go @@ -7,7 +7,7 @@ package callout import ( "strings" - "code.gitea.io/gitea/modules/svg" + "forgejo.org/modules/svg" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" diff --git a/modules/markup/markdown/callout/github_legacy.go b/modules/markup/markdown/callout/github_legacy.go index 39ea8619d8..e77da73dd9 100644 --- a/modules/markup/markdown/callout/github_legacy.go +++ b/modules/markup/markdown/callout/github_legacy.go @@ -7,7 +7,7 @@ package callout import ( "strings" - "code.gitea.io/gitea/modules/markup/markdown/util" + "forgejo.org/modules/markup/markdown/util" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index 1d3e04224f..d229afa8e3 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -8,8 +8,8 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/markup" + "forgejo.org/modules/setting" "github.com/yuin/goldmark/ast" east "github.com/yuin/goldmark/extension/ast" @@ -131,7 +131,7 @@ func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast. if entering { _, err = w.WriteString("') @@ -203,7 +203,7 @@ func (r *HTMLRenderer) renderIcon(w util.BufWriter, source []byte, node ast.Node return ast.WalkContinue, nil } - _, err := w.WriteString(fmt.Sprintf(``, name)) + _, err := fmt.Fprintf(w, ``, name) if err != nil { return ast.WalkStop, err } diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index d249d25014..e811d29994 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -5,19 +5,19 @@ package markdown import ( - "fmt" + "errors" "html/template" "io" "strings" "sync" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/common" - "code.gitea.io/gitea/modules/markup/markdown/callout" - "code.gitea.io/gitea/modules/markup/markdown/math" - "code.gitea.io/gitea/modules/setting" - giteautil "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/markup" + "forgejo.org/modules/markup/common" + "forgejo.org/modules/markup/markdown/callout" + "forgejo.org/modules/markup/markdown/math" + "forgejo.org/modules/setting" + giteautil "forgejo.org/modules/util" chromahtml "github.com/alecthomas/chroma/v2/formatters/html" "github.com/yuin/goldmark" @@ -54,7 +54,7 @@ func (l *limitWriter) Write(data []byte) (int, error) { if err != nil { return n, err } - return n, fmt.Errorf("rendered content too large - truncating render") + return n, errors.New("rendered content too large - truncating render") } n, err := l.w.Write(data) l.sum += int64(n) @@ -267,8 +267,13 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error // RenderString renders Markdown string to HTML with all specific handling stuff and return string func RenderString(ctx *markup.RenderContext, content string) (template.HTML, error) { + return RenderReader(ctx, strings.NewReader(content)) +} + +// RenderReader renders Markdown io.Reader to HTML with all specific handling stuff and return string +func RenderReader(ctx *markup.RenderContext, input io.Reader) (template.HTML, error) { var buf strings.Builder - if err := Render(ctx, strings.NewReader(content), &buf); err != nil { + if err := Render(ctx, input, &buf); err != nil { return "", err } return template.HTML(buf.String()), nil diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index bb59d72957..f7955115e0 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -10,14 +10,14 @@ import ( "strings" "testing" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/markdown" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/unittest" + "forgejo.org/modules/git" + "forgejo.org/modules/log" + "forgejo.org/modules/markup" + "forgejo.org/modules/markup/markdown" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/modules/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -104,7 +104,7 @@ func TestRender_Images(t *testing.T) { test( "!["+title+"]("+url+")", - `

`+title+`

`) + `

`+title+`

`) test( "[["+title+"|"+url+"]]", @@ -115,7 +115,7 @@ func TestRender_Images(t *testing.T) { test( "!["+title+"]("+url+")", - `

`+title+`

`) + `

`+title+`

`) test( "[["+title+"|"+url+"]]", @@ -412,8 +412,8 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) { testcase := `![image1](/image1) ![image2](/image2) ` - expected := `

image1
-image2

+ expected := `

image1
+image2

` res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase) require.NoError(t, err) @@ -845,10 +845,10 @@ mail@domain.com remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -872,10 +872,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -901,10 +901,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -930,10 +930,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -959,10 +959,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -988,10 +988,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1018,10 +1018,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1048,10 +1048,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1078,10 +1078,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1108,10 +1108,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1139,10 +1139,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1170,10 +1170,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
diff --git a/modules/markup/markdown/math/inline_parser.go b/modules/markup/markdown/math/inline_parser.go index b11195d551..da28d4fd9c 100644 --- a/modules/markup/markdown/math/inline_parser.go +++ b/modules/markup/markdown/math/inline_parser.go @@ -96,12 +96,12 @@ func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser. if len(line) <= pos { break } - suceedingCharacter := line[pos] + succeedingCharacter := line[pos] // check valid ending character - if !isPunctuation(suceedingCharacter) && - !(suceedingCharacter == ' ') && - !(suceedingCharacter == '\n') && - !isBracket(suceedingCharacter) { + if !isPunctuation(succeedingCharacter) && + (succeedingCharacter != ' ') && + (succeedingCharacter != '\n') && + !isBracket(succeedingCharacter) { return nil } if line[ender-1] != '\\' { @@ -139,12 +139,12 @@ func trimBlock(node *Inline, block text.Reader) { // trim first space and last space first := node.FirstChild().(*ast.Text) - if !(!first.Segment.IsEmpty() && block.Source()[first.Segment.Start] == ' ') { + if first.Segment.IsEmpty() || block.Source()[first.Segment.Start] != ' ' { return } last := node.LastChild().(*ast.Text) - if !(!last.Segment.IsEmpty() && block.Source()[last.Segment.Stop-1] == ' ') { + if last.Segment.IsEmpty() || block.Source()[last.Segment.Stop-1] != ' ' { return } diff --git a/modules/markup/markdown/meta_test.go b/modules/markup/markdown/meta_test.go index d341ae43e4..aaf116ff20 100644 --- a/modules/markup/markdown/meta_test.go +++ b/modules/markup/markdown/meta_test.go @@ -54,7 +54,7 @@ func TestExtractMetadata(t *testing.T) { var meta IssueTemplate body, err := ExtractMetadata(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest), &meta) require.NoError(t, err) - assert.Equal(t, "", body) + assert.Empty(t, body) assert.Equal(t, metaTest, meta) assert.True(t, meta.Valid()) }) @@ -86,7 +86,7 @@ func TestExtractMetadataBytes(t *testing.T) { var meta IssueTemplate body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest)), &meta) require.NoError(t, err) - assert.Equal(t, "", string(body)) + assert.Empty(t, string(body)) assert.Equal(t, metaTest, meta) assert.True(t, meta.Valid()) }) diff --git a/modules/markup/markdown/prefixed_id.go b/modules/markup/markdown/prefixed_id.go index 63d7fadc0a..036481dc05 100644 --- a/modules/markup/markdown/prefixed_id.go +++ b/modules/markup/markdown/prefixed_id.go @@ -7,9 +7,9 @@ import ( "bytes" "fmt" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/markup/common" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/container" + "forgejo.org/modules/markup/common" + "forgejo.org/modules/util" "github.com/yuin/goldmark/ast" ) diff --git a/modules/markup/markdown/renderconfig.go b/modules/markup/markdown/renderconfig.go index f4c48d1b3d..5c3eb1beec 100644 --- a/modules/markup/markdown/renderconfig.go +++ b/modules/markup/markdown/renderconfig.go @@ -7,7 +7,7 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/modules/markup" + "forgejo.org/modules/markup" "github.com/yuin/goldmark/ast" "gopkg.in/yaml.v3" diff --git a/modules/markup/markdown/toc.go b/modules/markup/markdown/toc.go index 38f744a25f..dbfab3e9dc 100644 --- a/modules/markup/markdown/toc.go +++ b/modules/markup/markdown/toc.go @@ -7,8 +7,8 @@ import ( "fmt" "net/url" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/translation" + "forgejo.org/modules/markup" + "forgejo.org/modules/translation" "github.com/yuin/goldmark/ast" ) diff --git a/modules/markup/markdown/transform_codespan.go b/modules/markup/markdown/transform_codespan.go index 45e3c20c8e..fc88db026b 100644 --- a/modules/markup/markdown/transform_codespan.go +++ b/modules/markup/markdown/transform_codespan.go @@ -8,8 +8,8 @@ import ( "fmt" "strings" - "code.gitea.io/gitea/modules/markup" - mdutil "code.gitea.io/gitea/modules/markup/markdown/util" + "forgejo.org/modules/markup" + mdutil "forgejo.org/modules/markup/markdown/util" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/renderer/html" @@ -40,7 +40,7 @@ func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Nod r.Writer.RawWrite(w, value) } case *ColorPreview: - _, _ = w.WriteString(fmt.Sprintf(``, string(v.Color))) + _, _ = fmt.Fprintf(w, ``, string(v.Color)) } } return ast.WalkSkipChildren, nil diff --git a/modules/markup/markdown/transform_heading.go b/modules/markup/markdown/transform_heading.go index 79fc9a3067..eedaf58556 100644 --- a/modules/markup/markdown/transform_heading.go +++ b/modules/markup/markdown/transform_heading.go @@ -6,9 +6,9 @@ package markdown import ( "fmt" - "code.gitea.io/gitea/modules/markup" - mdutil "code.gitea.io/gitea/modules/markup/markdown/util" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/markup" + mdutil "forgejo.org/modules/markup/markdown/util" + "forgejo.org/modules/util" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/text" diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go index b34a710fed..b86c9e3d41 100644 --- a/modules/markup/markdown/transform_image.go +++ b/modules/markup/markdown/transform_image.go @@ -6,8 +6,8 @@ package markdown import ( "strings" - "code.gitea.io/gitea/modules/markup" - giteautil "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/markup" + giteautil "forgejo.org/modules/util" "github.com/yuin/goldmark/ast" ) @@ -44,6 +44,7 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image) for _, attr := range v.Attributes() { image.SetAttribute(attr.Name, attr.Value) } + image.SetAttributeString("loading", []byte("lazy")) for child := v.FirstChild(); child != nil; { next := child.NextSibling() image.AppendChild(image, child) diff --git a/modules/markup/markdown/transform_link.go b/modules/markup/markdown/transform_link.go index e6f3836412..48e3479563 100644 --- a/modules/markup/markdown/transform_link.go +++ b/modules/markup/markdown/transform_link.go @@ -7,9 +7,9 @@ import ( "bytes" "slices" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/setting" - giteautil "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/markup" + "forgejo.org/modules/setting" + giteautil "forgejo.org/modules/util" "github.com/yuin/goldmark/ast" ) diff --git a/modules/markup/markdown/transform_list.go b/modules/markup/markdown/transform_list.go index b982fd4a83..03b3c4e89c 100644 --- a/modules/markup/markdown/transform_list.go +++ b/modules/markup/markdown/transform_list.go @@ -6,7 +6,7 @@ package markdown import ( "fmt" - "code.gitea.io/gitea/modules/markup" + "forgejo.org/modules/markup" "github.com/yuin/goldmark/ast" east "github.com/yuin/goldmark/extension/ast" diff --git a/modules/markup/mdstripper/mdstripper.go b/modules/markup/mdstripper/mdstripper.go index aefa41d3d2..7e5abc2ebe 100644 --- a/modules/markup/mdstripper/mdstripper.go +++ b/modules/markup/mdstripper/mdstripper.go @@ -10,9 +10,9 @@ import ( "strings" "sync" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup/common" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/log" + "forgejo.org/modules/markup/common" + "forgejo.org/modules/setting" "github.com/yuin/goldmark" "github.com/yuin/goldmark/ast" @@ -107,11 +107,12 @@ func (r *stripRenderer) processAutoLink(w io.Writer, link []byte) { } var sep string - if parts[3] == "issues" { + switch parts[3] { + case "issues": sep = "#" - } else if parts[3] == "pulls" { + case "pulls": sep = "!" - } else { + default: // Process out of band r.links = append(r.links, linkStr) return diff --git a/modules/markup/mdstripper/mdstripper_test.go b/modules/markup/mdstripper/mdstripper_test.go index ea34df0a3b..7fb49c1e01 100644 --- a/modules/markup/mdstripper/mdstripper_test.go +++ b/modules/markup/mdstripper/mdstripper_test.go @@ -79,7 +79,7 @@ A HIDDEN ` + "`" + `GHOST` + "`" + ` IN THIS LINE. lines = append(lines, line) } } - assert.EqualValues(t, test.expectedText, lines) - assert.EqualValues(t, test.expectedLinks, links) + assert.Equal(t, test.expectedText, lines) + assert.Equal(t, test.expectedLinks, links) } } diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index 391ee6c12b..b9d7b21db0 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -9,11 +9,11 @@ import ( "io" "strings" - "code.gitea.io/gitea/modules/highlight" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/highlight" + "forgejo.org/modules/log" + "forgejo.org/modules/markup" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "github.com/alecthomas/chroma/v2" "github.com/alecthomas/chroma/v2/lexers" diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go index 58fede7eb8..cdaa9f18ce 100644 --- a/modules/markup/orgmode/orgmode_test.go +++ b/modules/markup/orgmode/orgmode_test.go @@ -7,10 +7,10 @@ import ( "strings" "testing" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/git" + "forgejo.org/modules/markup" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 2137302f43..8eec764bbe 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -14,9 +14,9 @@ import ( "strings" "sync" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/git" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "github.com/yuin/goldmark/ast" ) @@ -67,14 +67,18 @@ type Header struct { // RenderContext represents a render context type RenderContext struct { - Ctx context.Context - RelativePath string // relative path from tree root of the branch - Type string - IsWiki bool - Links Links - Metas map[string]string - DefaultLink string - GitRepo *git.Repository + Ctx context.Context + RelativePath string // relative path from tree root of the branch + Type string + IsWiki bool + Links Links + Metas map[string]string + DefaultLink string + GitRepo *git.Repository + // reporting the target blob that is to-be-rendered enables + // deeper inspection in the handler for external renderer + // (i.e., more targeted handling of annexed files) + Blob *git.Blob ShaExistCache map[string]bool cancelFn func() SidebarTocNode ast.Node diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index 72d6571e4e..aacc2536bf 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -10,7 +10,7 @@ import ( "regexp" "sync" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "github.com/microcosm-cc/bluemonday" ) @@ -108,6 +108,9 @@ func createDefaultPolicy() *bluemonday.Policy { // Allow classes for emojis policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img") + // Allow attributes for images + policy.AllowAttrs("loading").Matching(regexp.MustCompile(`^lazy$`)).OnElements("img") + // Allow icons, emojis, chroma syntax and keyword markup on span policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span") policy.AllowAttrs("data-alias").Matching(regexp.MustCompile(`^[a-zA-Z0-9-_+]+$`)).OnElements("span") @@ -122,7 +125,7 @@ func createDefaultPolicy() *bluemonday.Policy { policy.AllowAttrs("class").Matching(regexp.MustCompile("^ui table$")).OnElements("div") policy.AllowAttrs("class").Matching(regexp.MustCompile("^header$")).OnElements("div") policy.AllowAttrs("data-line-number").Matching(regexp.MustCompile("^[0-9]+$")).OnElements("span") - policy.AllowAttrs("class").Matching(regexp.MustCompile("^text small grey$")).OnElements("span") + policy.AllowAttrs("class").Matching(regexp.MustCompile("^text grey$")).OnElements("span") policy.AllowAttrs("class").Matching(regexp.MustCompile("^file-preview$")).OnElements("table") policy.AllowAttrs("class").Matching(regexp.MustCompile("^lines-escape$")).OnElements("td") policy.AllowAttrs("class").Matching(regexp.MustCompile("^toggle-escape-button btn interact-bg$")).OnElements("button") diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go index 9805a34910..a0faff0494 100644 --- a/modules/markup/sanitizer_test.go +++ b/modules/markup/sanitizer_test.go @@ -75,6 +75,10 @@ func Test_Sanitizer(t *testing.T) { // Emoji `THUMBS UP`, `THUMBS UP`, `THUMBS UP`, `THUMBS UP`, + + // Images lazy loading + `image1`, `image1`, + `image1`, `image1`, } for i := 0; i < len(testCases); i += 2 { diff --git a/modules/mcaptcha/mcaptcha.go b/modules/mcaptcha/mcaptcha.go index 74142aa863..dbcafce29f 100644 --- a/modules/mcaptcha/mcaptcha.go +++ b/modules/mcaptcha/mcaptcha.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "codeberg.org/gusted/mcaptcha" ) diff --git a/modules/metrics/collector.go b/modules/metrics/collector.go index 230260ff94..5b6787d2f7 100755 --- a/modules/metrics/collector.go +++ b/modules/metrics/collector.go @@ -6,9 +6,9 @@ package metrics import ( "runtime" - activities_model "code.gitea.io/gitea/models/activities" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/setting" + activities_model "forgejo.org/models/activities" + "forgejo.org/models/db" + "forgejo.org/modules/setting" "github.com/prometheus/client_golang/prometheus" ) diff --git a/modules/migration/downloader.go b/modules/migration/downloader.go index 08dbbc29a9..48bdf0456d 100644 --- a/modules/migration/downloader.go +++ b/modules/migration/downloader.go @@ -7,7 +7,7 @@ package migration import ( "context" - "code.gitea.io/gitea/modules/structs" + "forgejo.org/modules/structs" ) // Downloader downloads the site repo information diff --git a/modules/migration/file_format.go b/modules/migration/file_format.go index d29d24dd0b..8851ad6de7 100644 --- a/modules/migration/file_format.go +++ b/modules/migration/file_format.go @@ -9,8 +9,8 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/json" + "forgejo.org/modules/log" "github.com/santhosh-tekuri/jsonschema/v6" "gopkg.in/yaml.v3" diff --git a/modules/migration/options.go b/modules/migration/options.go index 234e72c295..63bbe60758 100644 --- a/modules/migration/options.go +++ b/modules/migration/options.go @@ -4,7 +4,7 @@ package migration -import "code.gitea.io/gitea/modules/structs" +import "forgejo.org/modules/structs" // MigrateOptions defines the way a repository gets migrated // this is for internal usage by migrations module and func who interact with it diff --git a/modules/migration/pullrequest.go b/modules/migration/pullrequest.go index fbfdff0315..0861ab24f1 100644 --- a/modules/migration/pullrequest.go +++ b/modules/migration/pullrequest.go @@ -8,7 +8,7 @@ import ( "fmt" "time" - "code.gitea.io/gitea/modules/git" + "forgejo.org/modules/git" ) // PullRequest defines a standard pull request information @@ -34,6 +34,7 @@ type PullRequest struct { Assignees []string IsLocked bool `yaml:"is_locked"` Reactions []*Reaction + Flow int64 ForeignIndex int64 Context DownloaderContext `yaml:"-"` EnsuredSafe bool `yaml:"ensured_safe"` diff --git a/modules/nosql/manager.go b/modules/nosql/manager.go index 0ba21585fa..7eea069e09 100644 --- a/modules/nosql/manager.go +++ b/modules/nosql/manager.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/process" + "forgejo.org/modules/process" "github.com/redis/go-redis/v9" "github.com/syndtr/goleveldb/leveldb" diff --git a/modules/nosql/manager_leveldb.go b/modules/nosql/manager_leveldb.go index 4d2c90debc..087aac3e9a 100644 --- a/modules/nosql/manager_leveldb.go +++ b/modules/nosql/manager_leveldb.go @@ -10,7 +10,7 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/errors" diff --git a/modules/nosql/manager_redis.go b/modules/nosql/manager_redis.go index 79a533bd6b..bdaade1b47 100644 --- a/modules/nosql/manager_redis.go +++ b/modules/nosql/manager_redis.go @@ -11,7 +11,7 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" "github.com/redis/go-redis/v9" ) diff --git a/modules/optional/option.go b/modules/optional/option.go index af9e5ac852..ccbad259c2 100644 --- a/modules/optional/option.go +++ b/modules/optional/option.go @@ -3,6 +3,8 @@ package optional +import "strconv" + type Option[T any] []T func None[T any]() Option[T] { @@ -43,3 +45,12 @@ func (o Option[T]) ValueOrDefault(v T) T { } return v } + +// ParseBool get the corresponding optional.Option[bool] of a string using strconv.ParseBool +func ParseBool(s string) Option[bool] { + v, e := strconv.ParseBool(s) + if e != nil { + return None[bool]() + } + return Some(v) +} diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go index 203e9221e3..a674caf633 100644 --- a/modules/optional/option_test.go +++ b/modules/optional/option_test.go @@ -6,7 +6,7 @@ package optional_test import ( "testing" - "code.gitea.io/gitea/modules/optional" + "forgejo.org/modules/optional" "github.com/stretchr/testify/assert" ) @@ -57,3 +57,16 @@ func TestOption(t *testing.T) { assert.True(t, opt3.Has()) assert.Equal(t, int(1), opt3.Value()) } + +func Test_ParseBool(t *testing.T) { + assert.Equal(t, optional.None[bool](), optional.ParseBool("")) + assert.Equal(t, optional.None[bool](), optional.ParseBool("x")) + + assert.Equal(t, optional.Some(false), optional.ParseBool("0")) + assert.Equal(t, optional.Some(false), optional.ParseBool("f")) + assert.Equal(t, optional.Some(false), optional.ParseBool("False")) + + assert.Equal(t, optional.Some(true), optional.ParseBool("1")) + assert.Equal(t, optional.Some(true), optional.ParseBool("t")) + assert.Equal(t, optional.Some(true), optional.ParseBool("True")) +} diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go index b120a0edf6..86c1c97341 100644 --- a/modules/optional/serialization.go +++ b/modules/optional/serialization.go @@ -4,7 +4,7 @@ package optional import ( - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" "gopkg.in/yaml.v3" ) diff --git a/modules/optional/serialization_test.go b/modules/optional/serialization_test.go index c852b8a70f..14bf3b7814 100644 --- a/modules/optional/serialization_test.go +++ b/modules/optional/serialization_test.go @@ -7,8 +7,8 @@ import ( std_json "encoding/json" //nolint:depguard "testing" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/optional" + "forgejo.org/modules/json" + "forgejo.org/modules/optional" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -52,11 +52,11 @@ func TestOptionalToJson(t *testing.T) { t.Run(tc.name, func(t *testing.T) { b, err := json.Marshal(tc.obj) require.NoError(t, err) - assert.EqualValues(t, tc.want, string(b), "gitea json module returned unexpected") + assert.Equal(t, tc.want, string(b), "gitea json module returned unexpected") b, err = std_json.Marshal(tc.obj) require.NoError(t, err) - assert.EqualValues(t, tc.want, string(b), "std json module returned unexpected") + assert.Equal(t, tc.want, string(b), "std json module returned unexpected") }) } } @@ -90,12 +90,12 @@ func TestOptionalFromJson(t *testing.T) { var obj1 testSerializationStruct err := json.Unmarshal([]byte(tc.data), &obj1) require.NoError(t, err) - assert.EqualValues(t, tc.want, obj1, "gitea json module returned unexpected") + assert.Equal(t, tc.want, obj1, "gitea json module returned unexpected") var obj2 testSerializationStruct err = std_json.Unmarshal([]byte(tc.data), &obj2) require.NoError(t, err) - assert.EqualValues(t, tc.want, obj2, "std json module returned unexpected") + assert.Equal(t, tc.want, obj2, "std json module returned unexpected") }) } } @@ -136,7 +136,7 @@ optional_two_string: null t.Run(tc.name, func(t *testing.T) { b, err := yaml.Marshal(tc.obj) require.NoError(t, err) - assert.EqualValues(t, tc.want, string(b), "yaml module returned unexpected") + assert.Equal(t, tc.want, string(b), "yaml module returned unexpected") }) } } @@ -185,7 +185,7 @@ optional_twostring: null var obj testSerializationStruct err := yaml.Unmarshal([]byte(tc.data), &obj) require.NoError(t, err) - assert.EqualValues(t, tc.want, obj, "yaml module returned unexpected") + assert.Equal(t, tc.want, obj, "yaml module returned unexpected") }) } } diff --git a/modules/options/base.go b/modules/options/base.go index 6c6e3839f4..3ae8c56b79 100644 --- a/modules/options/base.go +++ b/modules/options/base.go @@ -4,8 +4,8 @@ package options import ( - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/setting" ) func CustomAssets() *assetfs.Layer { diff --git a/modules/options/dynamic.go b/modules/options/dynamic.go index 085492d11c..8eed8516ab 100644 --- a/modules/options/dynamic.go +++ b/modules/options/dynamic.go @@ -6,8 +6,8 @@ package options import ( - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/setting" ) func BuiltinAssets() *assetfs.Layer { diff --git a/modules/options/static.go b/modules/options/static.go index 72b28e990e..02091a2b1c 100644 --- a/modules/options/static.go +++ b/modules/options/static.go @@ -6,7 +6,7 @@ package options import ( - "code.gitea.io/gitea/modules/assetfs" + "forgejo.org/modules/assetfs" ) func BuiltinAssets() *assetfs.Layer { diff --git a/modules/packages/alpine/metadata.go b/modules/packages/alpine/metadata.go index 582c42610d..8562612206 100644 --- a/modules/packages/alpine/metadata.go +++ b/modules/packages/alpine/metadata.go @@ -13,8 +13,8 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" ) var ( diff --git a/modules/packages/arch/metadata.go b/modules/packages/arch/metadata.go index 0a51472d9c..f967bd25a0 100644 --- a/modules/packages/arch/metadata.go +++ b/modules/packages/arch/metadata.go @@ -15,9 +15,9 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/packages" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/packages" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "github.com/mholt/archiver/v3" ) diff --git a/modules/packages/arch/metadata_test.go b/modules/packages/arch/metadata_test.go index eb8de333ad..16c1c1637d 100644 --- a/modules/packages/arch/metadata_test.go +++ b/modules/packages/arch/metadata_test.go @@ -12,7 +12,7 @@ import ( "testing/fstest" "time" - "code.gitea.io/gitea/modules/packages" + "forgejo.org/modules/packages" "github.com/mholt/archiver/v3" "github.com/stretchr/testify/require" diff --git a/modules/packages/cargo/parser.go b/modules/packages/cargo/parser.go index a09cfc1f73..f2c75538b5 100644 --- a/modules/packages/cargo/parser.go +++ b/modules/packages/cargo/parser.go @@ -9,8 +9,8 @@ import ( "io" "regexp" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/json" + "forgejo.org/modules/validation" "github.com/hashicorp/go-version" ) diff --git a/modules/packages/cargo/parser_test.go b/modules/packages/cargo/parser_test.go index 8792a7a977..d2470fab42 100644 --- a/modules/packages/cargo/parser_test.go +++ b/modules/packages/cargo/parser_test.go @@ -98,8 +98,8 @@ func TestParsePackage(t *testing.T) { assert.Equal(t, []string{author}, cp.Metadata.Authors) assert.Len(t, cp.Metadata.Dependencies, 2) assert.Equal(t, "dep", cp.Metadata.Dependencies[0].Name) - assert.EqualValues(t, "v4l2-sys-mit", cp.Metadata.Dependencies[1].Name) - assert.EqualValues(t, "v4l2-sys", *cp.Metadata.Dependencies[1].Package) + assert.Equal(t, "v4l2-sys-mit", cp.Metadata.Dependencies[1].Name) + assert.Equal(t, "v4l2-sys", *cp.Metadata.Dependencies[1].Package) assert.Equal(t, homepage, cp.Metadata.ProjectURL) assert.Equal(t, license, cp.Metadata.License) content, _ := io.ReadAll(cp.Content) diff --git a/modules/packages/chef/metadata.go b/modules/packages/chef/metadata.go index a1c91870c2..951606bbc5 100644 --- a/modules/packages/chef/metadata.go +++ b/modules/packages/chef/metadata.go @@ -10,9 +10,9 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/json" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" ) const ( diff --git a/modules/packages/composer/metadata.go b/modules/packages/composer/metadata.go index 6035eae8ca..940309b769 100644 --- a/modules/packages/composer/metadata.go +++ b/modules/packages/composer/metadata.go @@ -10,9 +10,9 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/json" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "github.com/hashicorp/go-version" ) diff --git a/modules/packages/composer/metadata_test.go b/modules/packages/composer/metadata_test.go index 2bdb23965b..e2bbff4e58 100644 --- a/modules/packages/composer/metadata_test.go +++ b/modules/packages/composer/metadata_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/modules/packages/conan/conaninfo_parser.go b/modules/packages/conan/conaninfo_parser.go index de11dbee45..6027e51401 100644 --- a/modules/packages/conan/conaninfo_parser.go +++ b/modules/packages/conan/conaninfo_parser.go @@ -8,7 +8,7 @@ import ( "io" "strings" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) // Conaninfo represents infos of a Conan package diff --git a/modules/packages/conan/reference.go b/modules/packages/conan/reference.go index 58f268bd48..0b863240cb 100644 --- a/modules/packages/conan/reference.go +++ b/modules/packages/conan/reference.go @@ -8,8 +8,8 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/util" ) const ( diff --git a/modules/packages/conda/metadata.go b/modules/packages/conda/metadata.go index 76ba95eace..f61cc61c2a 100644 --- a/modules/packages/conda/metadata.go +++ b/modules/packages/conda/metadata.go @@ -10,10 +10,10 @@ import ( "io" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" - "code.gitea.io/gitea/modules/zstd" + "forgejo.org/modules/json" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" + "forgejo.org/modules/zstd" ) var ( diff --git a/modules/packages/conda/metadata_test.go b/modules/packages/conda/metadata_test.go index 25b0295157..959f9c4727 100644 --- a/modules/packages/conda/metadata_test.go +++ b/modules/packages/conda/metadata_test.go @@ -10,7 +10,7 @@ import ( "io" "testing" - "code.gitea.io/gitea/modules/zstd" + "forgejo.org/modules/zstd" "github.com/dsnet/compress/bzip2" "github.com/stretchr/testify/assert" diff --git a/modules/packages/container/metadata.go b/modules/packages/container/metadata.go index 2a41fb9105..6cac77b7ff 100644 --- a/modules/packages/container/metadata.go +++ b/modules/packages/container/metadata.go @@ -8,9 +8,9 @@ import ( "io" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/packages/container/helm" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/json" + "forgejo.org/modules/packages/container/helm" + "forgejo.org/modules/validation" oci "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -84,6 +84,13 @@ func ParseImageConfig(mt string, r io.Reader) (*Metadata, error) { func parseOCIImageConfig(r io.Reader) (*Metadata, error) { var image oci.Image if err := json.NewDecoder(r).Decode(&image); err != nil { + // Handle empty config blobs (common in OCI artifacts) + if err == io.EOF { + return &Metadata{ + Type: TypeOCI, + Platform: DefaultPlatform, + }, nil + } return nil, err } diff --git a/modules/packages/container/metadata_test.go b/modules/packages/container/metadata_test.go index 930cf48f68..5596c95751 100644 --- a/modules/packages/container/metadata_test.go +++ b/modules/packages/container/metadata_test.go @@ -4,10 +4,11 @@ package container import ( + "io" "strings" "testing" - "code.gitea.io/gitea/modules/packages/container/helm" + "forgejo.org/modules/packages/container/helm" oci "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/assert" @@ -60,3 +61,49 @@ func TestParseImageConfig(t *testing.T) { assert.Equal(t, projectURL, metadata.ProjectURL) assert.Equal(t, repositoryURL, metadata.RepositoryURL) } + +func TestParseImageConfigEmptyBlob(t *testing.T) { + t.Run("Empty config blob (EOF)", func(t *testing.T) { + // Test empty reader (simulates empty config blob common in OCI artifacts) + metadata, err := ParseImageConfig(oci.MediaTypeImageManifest, strings.NewReader("")) + require.NoError(t, err) + + assert.Equal(t, TypeOCI, metadata.Type) + assert.Equal(t, DefaultPlatform, metadata.Platform) + assert.Empty(t, metadata.Description) + assert.Empty(t, metadata.Authors) + assert.Empty(t, metadata.Labels) + assert.Empty(t, metadata.Manifests) + }) + + t.Run("Empty JSON object", func(t *testing.T) { + // Test minimal valid JSON config + metadata, err := ParseImageConfig(oci.MediaTypeImageManifest, strings.NewReader("{}")) + require.NoError(t, err) + + assert.Equal(t, TypeOCI, metadata.Type) + assert.Equal(t, DefaultPlatform, metadata.Platform) + assert.Empty(t, metadata.Description) + assert.Empty(t, metadata.Authors) + }) + + t.Run("Invalid JSON still returns error", func(t *testing.T) { + // Test that actual JSON errors (not EOF) are still returned + _, err := ParseImageConfig(oci.MediaTypeImageManifest, strings.NewReader("{invalid json")) + require.Error(t, err) + assert.NotEqual(t, io.EOF, err) + }) + + t.Run("OCI artifact with empty config", func(t *testing.T) { + // Test OCI artifact scenario with minimal config + configOCI := `{"config": {}}` + metadata, err := ParseImageConfig(oci.MediaTypeImageManifest, strings.NewReader(configOCI)) + require.NoError(t, err) + + assert.Equal(t, TypeOCI, metadata.Type) + assert.Equal(t, DefaultPlatform, metadata.Platform) + assert.Empty(t, metadata.Description) + assert.Empty(t, metadata.Authors) + assert.Empty(t, metadata.ImageLayers) + }) +} diff --git a/modules/packages/content_store.go b/modules/packages/content_store.go index 6438fb174f..f4578d91e0 100644 --- a/modules/packages/content_store.go +++ b/modules/packages/content_store.go @@ -9,9 +9,9 @@ import ( "path" "strings" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/setting" + "forgejo.org/modules/storage" + "forgejo.org/modules/util" ) // BlobHash256Key is the key to address a blob content diff --git a/modules/packages/cran/metadata.go b/modules/packages/cran/metadata.go index 0b0bfb07c6..547fe87ccb 100644 --- a/modules/packages/cran/metadata.go +++ b/modules/packages/cran/metadata.go @@ -13,7 +13,7 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) const ( diff --git a/modules/packages/debian/metadata.go b/modules/packages/debian/metadata.go index e76db63975..e44801654b 100644 --- a/modules/packages/debian/metadata.go +++ b/modules/packages/debian/metadata.go @@ -12,9 +12,9 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" - "code.gitea.io/gitea/modules/zstd" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" + "forgejo.org/modules/zstd" "github.com/blakesmith/ar" "github.com/ulikunitz/xz" diff --git a/modules/packages/debian/metadata_test.go b/modules/packages/debian/metadata_test.go index 6f6c469989..cfcbc57ee0 100644 --- a/modules/packages/debian/metadata_test.go +++ b/modules/packages/debian/metadata_test.go @@ -10,7 +10,7 @@ import ( "io" "testing" - "code.gitea.io/gitea/modules/zstd" + "forgejo.org/modules/zstd" "github.com/blakesmith/ar" "github.com/stretchr/testify/assert" diff --git a/modules/packages/goproxy/metadata.go b/modules/packages/goproxy/metadata.go index 40f7d20508..2dae4100e7 100644 --- a/modules/packages/goproxy/metadata.go +++ b/modules/packages/goproxy/metadata.go @@ -10,7 +10,7 @@ import ( "path" "strings" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) const ( diff --git a/modules/packages/hashed_buffer.go b/modules/packages/hashed_buffer.go index 70a086da30..93c693efc9 100644 --- a/modules/packages/hashed_buffer.go +++ b/modules/packages/hashed_buffer.go @@ -6,7 +6,7 @@ package packages import ( "io" - "code.gitea.io/gitea/modules/util/filebuffer" + "forgejo.org/modules/util/filebuffer" ) // HashedSizeReader provide methods to read, sum hashes and a Size method diff --git a/modules/packages/helm/metadata.go b/modules/packages/helm/metadata.go index 421fc5e725..19a30c5ffa 100644 --- a/modules/packages/helm/metadata.go +++ b/modules/packages/helm/metadata.go @@ -9,8 +9,8 @@ import ( "io" "strings" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "github.com/hashicorp/go-version" "gopkg.in/yaml.v3" diff --git a/modules/packages/maven/metadata.go b/modules/packages/maven/metadata.go index fa48b4e0d7..bc0dc0155e 100644 --- a/modules/packages/maven/metadata.go +++ b/modules/packages/maven/metadata.go @@ -7,8 +7,8 @@ import ( "encoding/xml" "io" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "golang.org/x/net/html/charset" ) diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index 7d3d7cd6b5..ed163d30ac 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -14,9 +14,9 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/json" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "github.com/hashicorp/go-version" ) diff --git a/modules/packages/npm/creator_test.go b/modules/packages/npm/creator_test.go index b2cf1aae0e..5cbaf0d865 100644 --- a/modules/packages/npm/creator_test.go +++ b/modules/packages/npm/creator_test.go @@ -10,7 +10,7 @@ import ( "strings" "testing" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/modules/packages/nuget/metadata.go b/modules/packages/nuget/metadata.go index dfb81baf4e..126a0ad494 100644 --- a/modules/packages/nuget/metadata.go +++ b/modules/packages/nuget/metadata.go @@ -13,8 +13,8 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "github.com/hashicorp/go-version" ) diff --git a/modules/packages/nuget/symbol_extractor.go b/modules/packages/nuget/symbol_extractor.go index 81bf0371a0..992ade7e8f 100644 --- a/modules/packages/nuget/symbol_extractor.go +++ b/modules/packages/nuget/symbol_extractor.go @@ -13,8 +13,8 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/modules/packages" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/packages" + "forgejo.org/modules/util" ) var ( diff --git a/modules/packages/pub/metadata.go b/modules/packages/pub/metadata.go index afb464e462..f8afdf7218 100644 --- a/modules/packages/pub/metadata.go +++ b/modules/packages/pub/metadata.go @@ -10,8 +10,8 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "github.com/hashicorp/go-version" "gopkg.in/yaml.v3" diff --git a/modules/packages/rpm/metadata.go b/modules/packages/rpm/metadata.go index 02003aba3d..503b7b1a24 100644 --- a/modules/packages/rpm/metadata.go +++ b/modules/packages/rpm/metadata.go @@ -8,10 +8,10 @@ import ( "io" "strings" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/validation" - "github.com/sassoftware/go-rpmutils" + "code.forgejo.org/forgejo/go-rpmutils" ) const ( @@ -232,9 +232,10 @@ func getEntries(h *rpmutils.RpmHeader, namesTag, versionsTag, flagsTag int, repo case "alt": for i := range names { e := &Entry{ + Name: names[i], AltFlags: uint32(flags[i]), + Version: versions[i], } - e.Version = versions[i] entries = append(entries, e) } } diff --git a/modules/packages/rubygems/marshal.go b/modules/packages/rubygems/marshal.go index 4e6a5fc5f8..191efc7c0e 100644 --- a/modules/packages/rubygems/marshal.go +++ b/modules/packages/rubygems/marshal.go @@ -9,7 +9,7 @@ import ( "io" "reflect" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) const ( diff --git a/modules/packages/rubygems/metadata.go b/modules/packages/rubygems/metadata.go index 8a9794860e..6d021a17ab 100644 --- a/modules/packages/rubygems/metadata.go +++ b/modules/packages/rubygems/metadata.go @@ -10,8 +10,8 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "gopkg.in/yaml.v3" ) diff --git a/modules/packages/swift/metadata.go b/modules/packages/swift/metadata.go index 24c4262ab7..34fc4f1784 100644 --- a/modules/packages/swift/metadata.go +++ b/modules/packages/swift/metadata.go @@ -11,9 +11,9 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/json" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "github.com/hashicorp/go-version" ) diff --git a/modules/packages/vagrant/metadata.go b/modules/packages/vagrant/metadata.go index 6789533339..24684249b7 100644 --- a/modules/packages/vagrant/metadata.go +++ b/modules/packages/vagrant/metadata.go @@ -9,8 +9,8 @@ import ( "io" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/json" + "forgejo.org/modules/validation" ) const ( diff --git a/modules/packages/vagrant/metadata_test.go b/modules/packages/vagrant/metadata_test.go index f467781a08..f1950685be 100644 --- a/modules/packages/vagrant/metadata_test.go +++ b/modules/packages/vagrant/metadata_test.go @@ -10,7 +10,7 @@ import ( "io" "testing" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/modules/pprof/pprof.go b/modules/pprof/pprof.go index c611c14270..d46790458e 100644 --- a/modules/pprof/pprof.go +++ b/modules/pprof/pprof.go @@ -9,7 +9,7 @@ import ( "runtime" "runtime/pprof" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // DumpMemProfileForUsername dumps a memory profile at pprofDataPath as memprofile__ diff --git a/modules/private/actions.go b/modules/private/actions.go index 311a283650..8e4b44c226 100644 --- a/modules/private/actions.go +++ b/modules/private/actions.go @@ -6,7 +6,7 @@ package private import ( "context" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) type GenerateTokenRequest struct { diff --git a/modules/private/hook.go b/modules/private/hook.go index 93cbcd469d..2d64c1dec9 100644 --- a/modules/private/hook.go +++ b/modules/private/hook.go @@ -9,10 +9,10 @@ import ( "net/url" "time" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/git/pushoptions" - "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/git" + "forgejo.org/modules/git/pushoptions" + "forgejo.org/modules/repository" + "forgejo.org/modules/setting" ) // Git environment variables diff --git a/modules/private/internal.go b/modules/private/internal.go index 9c330a24a8..65fddbbe6b 100644 --- a/modules/private/internal.go +++ b/modules/private/internal.go @@ -13,11 +13,11 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/httplib" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/proxyprotocol" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/httplib" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/proxyprotocol" + "forgejo.org/modules/setting" ) // Response is used for internal request response (for user message and error message) diff --git a/modules/private/key.go b/modules/private/key.go index dcd1714856..422ff16d9a 100644 --- a/modules/private/key.go +++ b/modules/private/key.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // UpdatePublicKeyInRepo update public key and if necessary deploy key updates diff --git a/modules/private/mail.go b/modules/private/mail.go index 08de5b7e28..f6054f9c74 100644 --- a/modules/private/mail.go +++ b/modules/private/mail.go @@ -6,7 +6,7 @@ package private import ( "context" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // Email structure holds a data for sending general emails diff --git a/modules/private/manager.go b/modules/private/manager.go index 6055e553bd..fa2e0b0d40 100644 --- a/modules/private/manager.go +++ b/modules/private/manager.go @@ -12,7 +12,7 @@ import ( "strconv" "time" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // Shutdown calls the internal shutdown function diff --git a/modules/private/request.go b/modules/private/request.go index 58cd261239..b80167adb6 100644 --- a/modules/private/request.go +++ b/modules/private/request.go @@ -8,8 +8,8 @@ import ( "io" "net/http" - "code.gitea.io/gitea/modules/httplib" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/httplib" + "forgejo.org/modules/json" ) // ResponseText is used to get the response as text, instead of parsing it as JSON. diff --git a/modules/private/restore_repo.go b/modules/private/restore_repo.go index 496209d3cb..2192d3048d 100644 --- a/modules/private/restore_repo.go +++ b/modules/private/restore_repo.go @@ -8,7 +8,7 @@ import ( "fmt" "time" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // RestoreParams structure holds a data for restore repository diff --git a/modules/private/serv.go b/modules/private/serv.go index 480a446954..af4a016cb8 100644 --- a/modules/private/serv.go +++ b/modules/private/serv.go @@ -8,10 +8,10 @@ import ( "fmt" "net/url" - asymkey_model "code.gitea.io/gitea/models/asymkey" - "code.gitea.io/gitea/models/perm" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" + asymkey_model "forgejo.org/models/asymkey" + "forgejo.org/models/perm" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" ) // KeyAndOwner is the response from ServNoCommand @@ -40,6 +40,7 @@ type ServCommandResults struct { UserName string UserEmail string UserID int64 + UserMode perm.AccessMode OwnerName string RepoName string RepoID int64 diff --git a/modules/process/manager_stacktraces_test.go b/modules/process/manager_stacktraces_test.go index 509b424d8b..936fab40d8 100644 --- a/modules/process/manager_stacktraces_test.go +++ b/modules/process/manager_stacktraces_test.go @@ -23,19 +23,19 @@ func TestProcessStacktraces(t *testing.T) { t.Run("No flat with no system process", func(t *testing.T) { processes, processCount, _, err := GetManager().ProcessStacktraces(false, true) require.NoError(t, err) - assert.EqualValues(t, 4, processCount) + assert.Equal(t, 4, processCount) assert.Len(t, processes, 2) - assert.EqualValues(t, "Children normal process", processes[0].Description) - assert.EqualValues(t, NormalProcessType, processes[0].Type) + assert.Equal(t, "Children normal process", processes[0].Description) + assert.Equal(t, NormalProcessType, processes[0].Type) assert.Empty(t, processes[0].ParentPID) assert.Len(t, processes[0].Children, 1) - assert.EqualValues(t, "Children process", processes[0].Children[0].Description) - assert.EqualValues(t, processes[0].PID, processes[0].Children[0].ParentPID) + assert.Equal(t, "Children process", processes[0].Children[0].Description) + assert.Equal(t, processes[0].PID, processes[0].Children[0].ParentPID) - assert.EqualValues(t, "Normal process", processes[1].Description) - assert.EqualValues(t, NormalProcessType, processes[1].Type) + assert.Equal(t, "Normal process", processes[1].Description) + assert.Equal(t, NormalProcessType, processes[1].Type) assert.Empty(t, processes[1].ParentPID) assert.Empty(t, processes[1].Children) }) @@ -43,21 +43,21 @@ func TestProcessStacktraces(t *testing.T) { t.Run("Flat with no system process", func(t *testing.T) { processes, processCount, _, err := GetManager().ProcessStacktraces(true, true) require.NoError(t, err) - assert.EqualValues(t, 4, processCount) + assert.Equal(t, 4, processCount) assert.Len(t, processes, 3) - assert.EqualValues(t, "Children process", processes[0].Description) - assert.EqualValues(t, NormalProcessType, processes[0].Type) - assert.EqualValues(t, processes[1].PID, processes[0].ParentPID) + assert.Equal(t, "Children process", processes[0].Description) + assert.Equal(t, NormalProcessType, processes[0].Type) + assert.Equal(t, processes[1].PID, processes[0].ParentPID) assert.Empty(t, processes[0].Children) - assert.EqualValues(t, "Children normal process", processes[1].Description) - assert.EqualValues(t, NormalProcessType, processes[1].Type) + assert.Equal(t, "Children normal process", processes[1].Description) + assert.Equal(t, NormalProcessType, processes[1].Type) assert.Empty(t, processes[1].ParentPID) assert.Empty(t, processes[1].Children) - assert.EqualValues(t, "Normal process", processes[2].Description) - assert.EqualValues(t, NormalProcessType, processes[2].Type) + assert.Equal(t, "Normal process", processes[2].Description) + assert.Equal(t, NormalProcessType, processes[2].Type) assert.Empty(t, processes[2].ParentPID) assert.Empty(t, processes[2].Children) }) @@ -65,27 +65,27 @@ func TestProcessStacktraces(t *testing.T) { t.Run("System process", func(t *testing.T) { processes, processCount, _, err := GetManager().ProcessStacktraces(false, false) require.NoError(t, err) - assert.EqualValues(t, 4, processCount) + assert.Equal(t, 4, processCount) assert.Len(t, processes, 4) - assert.EqualValues(t, "System process", processes[0].Description) - assert.EqualValues(t, SystemProcessType, processes[0].Type) + assert.Equal(t, "System process", processes[0].Description) + assert.Equal(t, SystemProcessType, processes[0].Type) assert.Empty(t, processes[0].ParentPID) assert.Empty(t, processes[0].Children) - assert.EqualValues(t, "Children normal process", processes[1].Description) - assert.EqualValues(t, NormalProcessType, processes[1].Type) + assert.Equal(t, "Children normal process", processes[1].Description) + assert.Equal(t, NormalProcessType, processes[1].Type) assert.Empty(t, processes[1].ParentPID) assert.Len(t, processes[1].Children, 1) - assert.EqualValues(t, "Normal process", processes[2].Description) - assert.EqualValues(t, NormalProcessType, processes[2].Type) + assert.Equal(t, "Normal process", processes[2].Description) + assert.Equal(t, NormalProcessType, processes[2].Type) assert.Empty(t, processes[2].ParentPID) assert.Empty(t, processes[2].Children) // This is the "main" pid, testing code always runs in a goroutine. - assert.EqualValues(t, "(unassociated)", processes[3].Description) - assert.EqualValues(t, NoneProcessType, processes[3].Type) + assert.Equal(t, "(unassociated)", processes[3].Description) + assert.Equal(t, NoneProcessType, processes[3].Type) assert.Empty(t, processes[3].ParentPID) assert.Empty(t, processes[3].Children) }) diff --git a/modules/proxy/proxy.go b/modules/proxy/proxy.go index 1a6bdad7fb..8c460dba30 100644 --- a/modules/proxy/proxy.go +++ b/modules/proxy/proxy.go @@ -10,8 +10,8 @@ import ( "strings" "sync" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "github.com/gobwas/glob" ) diff --git a/modules/proxyprotocol/conn.go b/modules/proxyprotocol/conn.go index f437f13683..beac5de120 100644 --- a/modules/proxyprotocol/conn.go +++ b/modules/proxyprotocol/conn.go @@ -14,7 +14,7 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) var ( diff --git a/modules/public/mime_types.go b/modules/public/mime_types.go index 32bdf3bfa2..87ee2854ae 100644 --- a/modules/public/mime_types.go +++ b/modules/public/mime_types.go @@ -23,6 +23,11 @@ var wellKnownMimeTypesLower = map[string]string{ ".wasm": "application/wasm", ".webp": "image/webp", ".xml": "text/xml; charset=utf-8", + ".glb": "model/gltf-binary", + ".gltf": "model/gltf+json", + ".obj": "model/obj", + ".stl": "model/stl", + ".3mf": "model/3mf", // well, there are some types missing from the builtin list ".txt": "text/plain; charset=utf-8", diff --git a/modules/public/public.go b/modules/public/public.go index abc6b46158..a7db5b62e9 100644 --- a/modules/public/public.go +++ b/modules/public/public.go @@ -6,18 +6,19 @@ package public import ( "bytes" "io" + "io/fs" "net/http" "os" "path" "strings" "time" - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/httpcache" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/container" + "forgejo.org/modules/httpcache" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) func CustomAssets() *assetfs.Layer { @@ -59,7 +60,7 @@ func setWellKnownContentType(w http.ResponseWriter, file string) { } } -func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) { +func handleRequest(w http.ResponseWriter, req *http.Request, fs fs.FS, file string) { // actually, fs (http.FileSystem) is designed to be a safe interface, relative paths won't bypass its parent directory, it's also fine to do a clean here f, err := fs.Open(util.PathJoinRelX(file)) if err != nil { @@ -86,33 +87,31 @@ func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, return } - serveContent(w, req, fi, fi.ModTime(), f) + serveContent(w, req, fi.Name(), fi.ModTime(), f.(io.ReadSeeker)) } -type GzipBytesProvider interface { - GzipBytes() []byte +type ZstdBytesProvider interface { + ZstdBytes() []byte } // serveContent serve http content -func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtime time.Time, content io.ReadSeeker) { - setWellKnownContentType(w, fi.Name()) +func serveContent(w http.ResponseWriter, req *http.Request, name string, modtime time.Time, content io.ReadSeeker) { + setWellKnownContentType(w, name) encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding")) - if encodings.Contains("gzip") { - // try to provide gzip content directly from bindata (provided by vfsgen۰CompressedFileInfo) - if compressed, ok := fi.(GzipBytesProvider); ok { - rdGzip := bytes.NewReader(compressed.GzipBytes()) - // all gzipped static files (from bindata) are managed by Gitea, so we can make sure every file has the correct ext name - // then we can get the correct Content-Type, we do not need to do http.DetectContentType on the decompressed data + if encodings.Contains("zstd") { + // If the file was compressed, use the bytes directly. + if compressed, ok := content.(ZstdBytesProvider); ok { + rdZstd := bytes.NewReader(compressed.ZstdBytes()) if w.Header().Get("Content-Type") == "" { w.Header().Set("Content-Type", "application/octet-stream") } - w.Header().Set("Content-Encoding", "gzip") - httpcache.ServeContentWithCacheControl(w, req, fi.Name(), modtime, rdGzip) + w.Header().Set("Content-Encoding", "zstd") + httpcache.ServeContentWithCacheControl(w, req, name, modtime, rdZstd) return } } - httpcache.ServeContentWithCacheControl(w, req, fi.Name(), modtime, content) + httpcache.ServeContentWithCacheControl(w, req, name, modtime, content) return } diff --git a/modules/public/public_test.go b/modules/public/public_test.go index 5e4bf5d671..4bfbb7ef31 100644 --- a/modules/public/public_test.go +++ b/modules/public/public_test.go @@ -6,7 +6,7 @@ package public import ( "testing" - "code.gitea.io/gitea/modules/container" + "forgejo.org/modules/container" "github.com/stretchr/testify/assert" ) diff --git a/modules/public/serve_dynamic.go b/modules/public/serve_dynamic.go index a668b17c34..e5bd89b1cd 100644 --- a/modules/public/serve_dynamic.go +++ b/modules/public/serve_dynamic.go @@ -6,8 +6,8 @@ package public import ( - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/setting" ) func BuiltinAssets() *assetfs.Layer { diff --git a/modules/public/serve_static.go b/modules/public/serve_static.go index e79085021e..148d789bba 100644 --- a/modules/public/serve_static.go +++ b/modules/public/serve_static.go @@ -8,12 +8,10 @@ package public import ( "time" - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/timeutil" ) -var _ GzipBytesProvider = (*vfsgen۰CompressedFileInfo)(nil) - // GlobalModTime provide a global mod time for embedded asset files func GlobalModTime(filename string) time.Time { return timeutil.GetExecutableModTime() diff --git a/modules/queue/base_channel.go b/modules/queue/base_channel.go index dd8ccb15f4..1be4edf144 100644 --- a/modules/queue/base_channel.go +++ b/modules/queue/base_channel.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/container" + "forgejo.org/modules/container" ) var errChannelClosed = errors.New("channel is closed") diff --git a/modules/queue/base_levelqueue.go b/modules/queue/base_levelqueue.go index 06cb5f4819..12c805c0be 100644 --- a/modules/queue/base_levelqueue.go +++ b/modules/queue/base_levelqueue.go @@ -7,8 +7,8 @@ import ( "context" "sync/atomic" - "code.gitea.io/gitea/modules/nosql" - "code.gitea.io/gitea/modules/queue/lqinternal" + "forgejo.org/modules/nosql" + "forgejo.org/modules/queue/lqinternal" "code.forgejo.org/forgejo/levelqueue" "github.com/syndtr/goleveldb/leveldb" diff --git a/modules/queue/base_levelqueue_common.go b/modules/queue/base_levelqueue_common.go index ee334c4571..8b4f35c47d 100644 --- a/modules/queue/base_levelqueue_common.go +++ b/modules/queue/base_levelqueue_common.go @@ -11,7 +11,7 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/nosql" + "forgejo.org/modules/nosql" "code.forgejo.org/forgejo/levelqueue" "github.com/syndtr/goleveldb/leveldb" diff --git a/modules/queue/base_levelqueue_test.go b/modules/queue/base_levelqueue_test.go index a4dc7a3062..0f02b9f3ee 100644 --- a/modules/queue/base_levelqueue_test.go +++ b/modules/queue/base_levelqueue_test.go @@ -6,8 +6,8 @@ package queue import ( "testing" - "code.gitea.io/gitea/modules/queue/lqinternal" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/queue/lqinternal" + "forgejo.org/modules/setting" "code.forgejo.org/forgejo/levelqueue" "github.com/stretchr/testify/assert" diff --git a/modules/queue/base_levelqueue_unique.go b/modules/queue/base_levelqueue_unique.go index ac133bdbf4..91d2e68500 100644 --- a/modules/queue/base_levelqueue_unique.go +++ b/modules/queue/base_levelqueue_unique.go @@ -8,8 +8,8 @@ import ( "sync" "sync/atomic" - "code.gitea.io/gitea/modules/nosql" - "code.gitea.io/gitea/modules/queue/lqinternal" + "forgejo.org/modules/nosql" + "forgejo.org/modules/queue/lqinternal" "code.forgejo.org/forgejo/levelqueue" "github.com/syndtr/goleveldb/leveldb" diff --git a/modules/queue/base_redis.go b/modules/queue/base_redis.go index 62df30f68f..ec3c6dc16d 100644 --- a/modules/queue/base_redis.go +++ b/modules/queue/base_redis.go @@ -8,9 +8,9 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/nosql" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/nosql" "github.com/redis/go-redis/v9" ) diff --git a/modules/queue/base_redis_test.go b/modules/queue/base_redis_test.go index fa1700dc2e..bf3ad5b97b 100644 --- a/modules/queue/base_redis_test.go +++ b/modules/queue/base_redis_test.go @@ -7,8 +7,8 @@ import ( "context" "testing" - "code.gitea.io/gitea/modules/queue/mock" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/queue/mock" + "forgejo.org/modules/setting" "github.com/redis/go-redis/v9" "github.com/stretchr/testify/suite" diff --git a/modules/queue/base_redis_with_server_test.go b/modules/queue/base_redis_with_server_test.go index b73404f4e5..e1f552bfb2 100644 --- a/modules/queue/base_redis_with_server_test.go +++ b/modules/queue/base_redis_with_server_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "code.gitea.io/gitea/modules/nosql" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/nosql" + "forgejo.org/modules/setting" "github.com/stretchr/testify/suite" ) diff --git a/modules/queue/base_test.go b/modules/queue/base_test.go index d852a80b16..caa930158c 100644 --- a/modules/queue/base_test.go +++ b/modules/queue/base_test.go @@ -22,7 +22,7 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) _ = q.RemoveAll(ctx) cnt, err := q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, 0, cnt) + assert.Equal(t, 0, cnt) // push the first item err = q.PushItem(ctx, []byte("foo")) @@ -30,7 +30,7 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) cnt, err = q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, 1, cnt) + assert.Equal(t, 1, cnt) // push a duplicate item err = q.PushItem(ctx, []byte("foo")) @@ -46,10 +46,10 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) has, err := q.HasItem(ctx, []byte("foo")) require.NoError(t, err) if !isUnique { - assert.EqualValues(t, 2, cnt) + assert.Equal(t, 2, cnt) assert.False(t, has) // non-unique queues don't check for duplicates } else { - assert.EqualValues(t, 1, cnt) + assert.Equal(t, 1, cnt) assert.True(t, has) } @@ -60,18 +60,18 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) // pop the first item (and the duplicate if non-unique) it, err := q.PopItem(ctx) require.NoError(t, err) - assert.EqualValues(t, "foo", string(it)) + assert.Equal(t, "foo", string(it)) if !isUnique { it, err = q.PopItem(ctx) require.NoError(t, err) - assert.EqualValues(t, "foo", string(it)) + assert.Equal(t, "foo", string(it)) } // pop another item it, err = q.PopItem(ctx) require.NoError(t, err) - assert.EqualValues(t, "bar", string(it)) + assert.Equal(t, "bar", string(it)) // pop an empty queue (timeout, cancel) ctxTimed, cancel := context.WithTimeout(ctx, 10*time.Millisecond) @@ -108,13 +108,13 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) // remove all cnt, err = q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, cfg.Length, cnt) + assert.Equal(t, cfg.Length, cnt) _ = q.RemoveAll(ctx) cnt, err = q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, 0, cnt) + assert.Equal(t, 0, cnt) }) } @@ -127,7 +127,7 @@ func TestBaseDummy(t *testing.T) { cnt, err := q.Len(ctx) require.NoError(t, err) - assert.EqualValues(t, 0, cnt) + assert.Equal(t, 0, cnt) has, err := q.HasItem(ctx, []byte("foo")) require.NoError(t, err) diff --git a/modules/queue/config.go b/modules/queue/config.go index c5bc16b6f0..f736a5aa12 100644 --- a/modules/queue/config.go +++ b/modules/queue/config.go @@ -4,7 +4,7 @@ package queue import ( - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) type BaseConfig struct { diff --git a/modules/queue/manager.go b/modules/queue/manager.go index 8b964c0c28..8f1a93f273 100644 --- a/modules/queue/manager.go +++ b/modules/queue/manager.go @@ -8,8 +8,8 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" ) // Manager is a manager for the queues created by "CreateXxxQueue" functions, these queues are called "managed queues". diff --git a/modules/queue/manager_test.go b/modules/queue/manager_test.go index 5806cbd6c9..fd5d21570c 100644 --- a/modules/queue/manager_test.go +++ b/modules/queue/manager_test.go @@ -7,18 +7,15 @@ import ( "path/filepath" "testing" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestManager(t *testing.T) { - oldAppDataPath := setting.AppDataPath - setting.AppDataPath = t.TempDir() - defer func() { - setting.AppDataPath = oldAppDataPath - }() + defer test.MockVariableValue(&setting.AppDataPath, t.TempDir())() newQueueFromConfig := func(name, cfg string) (*WorkerPoolQueue[int], error) { cfgProvider, err := setting.NewConfigProviderFromData(cfg) @@ -48,7 +45,7 @@ CONN_STR = redis:// assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/common"), q.baseConfig.DataFullDir) assert.Equal(t, 100000, q.baseConfig.Length) assert.Equal(t, 20, q.batchLength) - assert.Equal(t, "", q.baseConfig.ConnStr) + assert.Empty(t, q.baseConfig.ConnStr) assert.Equal(t, "default_queue", q.baseConfig.QueueFullName) assert.Equal(t, "default_queue_unique", q.baseConfig.SetFullName) assert.NotZero(t, q.GetWorkerMaxNumber()) @@ -102,7 +99,7 @@ MAX_WORKERS = 123 assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/dir2"), q2.baseConfig.DataFullDir) assert.Equal(t, 102, q2.baseConfig.Length) assert.Equal(t, 22, q2.batchLength) - assert.Equal(t, "", q2.baseConfig.ConnStr) + assert.Empty(t, q2.baseConfig.ConnStr) assert.Equal(t, "sub_q2", q2.baseConfig.QueueFullName) assert.Equal(t, "sub_q2_u2", q2.baseConfig.SetFullName) assert.Equal(t, 123, q2.GetWorkerMaxNumber()) diff --git a/modules/queue/mock/redisuniversalclient.go b/modules/queue/mock/redisuniversalclient.go index 36e4b7cd5d..a19e639ac1 100644 --- a/modules/queue/mock/redisuniversalclient.go +++ b/modules/queue/mock/redisuniversalclient.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: code.gitea.io/gitea/modules/nosql (interfaces: RedisClient) +// Source: forgejo.org/modules/nosql (interfaces: RedisClient) // // Generated by this command: // -// mockgen -package mock -destination ./modules/queue/mock/redisuniversalclient.go code.gitea.io/gitea/modules/nosql RedisClient +// mockgen -package mock -destination ./modules/queue/mock/redisuniversalclient.go forgejo.org/modules/nosql RedisClient // // Package mock is a generated GoMock package. @@ -22,6 +22,7 @@ import ( type MockRedisClient struct { ctrl *gomock.Controller recorder *MockRedisClientMockRecorder + isgomock struct{} } // MockRedisClientMockRecorder is the mock recorder for MockRedisClient. @@ -56,38 +57,38 @@ func (mr *MockRedisClientMockRecorder) Close() *gomock.Call { } // DBSize mocks base method. -func (m *MockRedisClient) DBSize(arg0 context.Context) *redis.IntCmd { +func (m *MockRedisClient) DBSize(ctx context.Context) *redis.IntCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DBSize", arg0) + ret := m.ctrl.Call(m, "DBSize", ctx) ret0, _ := ret[0].(*redis.IntCmd) return ret0 } // DBSize indicates an expected call of DBSize. -func (mr *MockRedisClientMockRecorder) DBSize(arg0 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) DBSize(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DBSize", reflect.TypeOf((*MockRedisClient)(nil).DBSize), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DBSize", reflect.TypeOf((*MockRedisClient)(nil).DBSize), ctx) } // Decr mocks base method. -func (m *MockRedisClient) Decr(arg0 context.Context, arg1 string) *redis.IntCmd { +func (m *MockRedisClient) Decr(ctx context.Context, key string) *redis.IntCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Decr", arg0, arg1) + ret := m.ctrl.Call(m, "Decr", ctx, key) ret0, _ := ret[0].(*redis.IntCmd) return ret0 } // Decr indicates an expected call of Decr. -func (mr *MockRedisClientMockRecorder) Decr(arg0, arg1 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) Decr(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decr", reflect.TypeOf((*MockRedisClient)(nil).Decr), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decr", reflect.TypeOf((*MockRedisClient)(nil).Decr), ctx, key) } // Del mocks base method. -func (m *MockRedisClient) Del(arg0 context.Context, arg1 ...string) *redis.IntCmd { +func (m *MockRedisClient) Del(ctx context.Context, keys ...string) *redis.IntCmd { m.ctrl.T.Helper() - varargs := []any{arg0} - for _, a := range arg1 { + varargs := []any{ctx} + for _, a := range keys { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Del", varargs...) @@ -96,17 +97,17 @@ func (m *MockRedisClient) Del(arg0 context.Context, arg1 ...string) *redis.IntCm } // Del indicates an expected call of Del. -func (mr *MockRedisClientMockRecorder) Del(arg0 any, arg1 ...any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) Del(ctx any, keys ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0}, arg1...) + varargs := append([]any{ctx}, keys...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockRedisClient)(nil).Del), varargs...) } // Exists mocks base method. -func (m *MockRedisClient) Exists(arg0 context.Context, arg1 ...string) *redis.IntCmd { +func (m *MockRedisClient) Exists(ctx context.Context, keys ...string) *redis.IntCmd { m.ctrl.T.Helper() - varargs := []any{arg0} - for _, a := range arg1 { + varargs := []any{ctx} + for _, a := range keys { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Exists", varargs...) @@ -115,45 +116,45 @@ func (m *MockRedisClient) Exists(arg0 context.Context, arg1 ...string) *redis.In } // Exists indicates an expected call of Exists. -func (mr *MockRedisClientMockRecorder) Exists(arg0 any, arg1 ...any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) Exists(ctx any, keys ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0}, arg1...) + varargs := append([]any{ctx}, keys...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exists", reflect.TypeOf((*MockRedisClient)(nil).Exists), varargs...) } // FlushDB mocks base method. -func (m *MockRedisClient) FlushDB(arg0 context.Context) *redis.StatusCmd { +func (m *MockRedisClient) FlushDB(ctx context.Context) *redis.StatusCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FlushDB", arg0) + ret := m.ctrl.Call(m, "FlushDB", ctx) ret0, _ := ret[0].(*redis.StatusCmd) return ret0 } // FlushDB indicates an expected call of FlushDB. -func (mr *MockRedisClientMockRecorder) FlushDB(arg0 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) FlushDB(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushDB", reflect.TypeOf((*MockRedisClient)(nil).FlushDB), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushDB", reflect.TypeOf((*MockRedisClient)(nil).FlushDB), ctx) } // Get mocks base method. -func (m *MockRedisClient) Get(arg0 context.Context, arg1 string) *redis.StringCmd { +func (m *MockRedisClient) Get(ctx context.Context, key string) *redis.StringCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", arg0, arg1) + ret := m.ctrl.Call(m, "Get", ctx, key) ret0, _ := ret[0].(*redis.StringCmd) return ret0 } // Get indicates an expected call of Get. -func (mr *MockRedisClientMockRecorder) Get(arg0, arg1 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) Get(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRedisClient)(nil).Get), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRedisClient)(nil).Get), ctx, key) } // HDel mocks base method. -func (m *MockRedisClient) HDel(arg0 context.Context, arg1 string, arg2 ...string) *redis.IntCmd { +func (m *MockRedisClient) HDel(ctx context.Context, key string, fields ...string) *redis.IntCmd { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, key} + for _, a := range fields { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "HDel", varargs...) @@ -162,31 +163,31 @@ func (m *MockRedisClient) HDel(arg0 context.Context, arg1 string, arg2 ...string } // HDel indicates an expected call of HDel. -func (mr *MockRedisClientMockRecorder) HDel(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) HDel(ctx, key any, fields ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, key}, fields...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HDel", reflect.TypeOf((*MockRedisClient)(nil).HDel), varargs...) } // HKeys mocks base method. -func (m *MockRedisClient) HKeys(arg0 context.Context, arg1 string) *redis.StringSliceCmd { +func (m *MockRedisClient) HKeys(ctx context.Context, key string) *redis.StringSliceCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HKeys", arg0, arg1) + ret := m.ctrl.Call(m, "HKeys", ctx, key) ret0, _ := ret[0].(*redis.StringSliceCmd) return ret0 } // HKeys indicates an expected call of HKeys. -func (mr *MockRedisClientMockRecorder) HKeys(arg0, arg1 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) HKeys(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HKeys", reflect.TypeOf((*MockRedisClient)(nil).HKeys), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HKeys", reflect.TypeOf((*MockRedisClient)(nil).HKeys), ctx, key) } // HSet mocks base method. -func (m *MockRedisClient) HSet(arg0 context.Context, arg1 string, arg2 ...any) *redis.IntCmd { +func (m *MockRedisClient) HSet(ctx context.Context, key string, values ...any) *redis.IntCmd { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, key} + for _, a := range values { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "HSet", varargs...) @@ -195,73 +196,73 @@ func (m *MockRedisClient) HSet(arg0 context.Context, arg1 string, arg2 ...any) * } // HSet indicates an expected call of HSet. -func (mr *MockRedisClientMockRecorder) HSet(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) HSet(ctx, key any, values ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, key}, values...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HSet", reflect.TypeOf((*MockRedisClient)(nil).HSet), varargs...) } // Incr mocks base method. -func (m *MockRedisClient) Incr(arg0 context.Context, arg1 string) *redis.IntCmd { +func (m *MockRedisClient) Incr(ctx context.Context, key string) *redis.IntCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Incr", arg0, arg1) + ret := m.ctrl.Call(m, "Incr", ctx, key) ret0, _ := ret[0].(*redis.IntCmd) return ret0 } // Incr indicates an expected call of Incr. -func (mr *MockRedisClientMockRecorder) Incr(arg0, arg1 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) Incr(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Incr", reflect.TypeOf((*MockRedisClient)(nil).Incr), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Incr", reflect.TypeOf((*MockRedisClient)(nil).Incr), ctx, key) } // LLen mocks base method. -func (m *MockRedisClient) LLen(arg0 context.Context, arg1 string) *redis.IntCmd { +func (m *MockRedisClient) LLen(ctx context.Context, key string) *redis.IntCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LLen", arg0, arg1) + ret := m.ctrl.Call(m, "LLen", ctx, key) ret0, _ := ret[0].(*redis.IntCmd) return ret0 } // LLen indicates an expected call of LLen. -func (mr *MockRedisClientMockRecorder) LLen(arg0, arg1 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) LLen(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LLen", reflect.TypeOf((*MockRedisClient)(nil).LLen), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LLen", reflect.TypeOf((*MockRedisClient)(nil).LLen), ctx, key) } // LPop mocks base method. -func (m *MockRedisClient) LPop(arg0 context.Context, arg1 string) *redis.StringCmd { +func (m *MockRedisClient) LPop(ctx context.Context, key string) *redis.StringCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LPop", arg0, arg1) + ret := m.ctrl.Call(m, "LPop", ctx, key) ret0, _ := ret[0].(*redis.StringCmd) return ret0 } // LPop indicates an expected call of LPop. -func (mr *MockRedisClientMockRecorder) LPop(arg0, arg1 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) LPop(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LPop", reflect.TypeOf((*MockRedisClient)(nil).LPop), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LPop", reflect.TypeOf((*MockRedisClient)(nil).LPop), ctx, key) } // Ping mocks base method. -func (m *MockRedisClient) Ping(arg0 context.Context) *redis.StatusCmd { +func (m *MockRedisClient) Ping(ctx context.Context) *redis.StatusCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Ping", arg0) + ret := m.ctrl.Call(m, "Ping", ctx) ret0, _ := ret[0].(*redis.StatusCmd) return ret0 } // Ping indicates an expected call of Ping. -func (mr *MockRedisClientMockRecorder) Ping(arg0 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) Ping(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockRedisClient)(nil).Ping), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockRedisClient)(nil).Ping), ctx) } // RPush mocks base method. -func (m *MockRedisClient) RPush(arg0 context.Context, arg1 string, arg2 ...any) *redis.IntCmd { +func (m *MockRedisClient) RPush(ctx context.Context, key string, values ...any) *redis.IntCmd { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, key} + for _, a := range values { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "RPush", varargs...) @@ -270,17 +271,17 @@ func (m *MockRedisClient) RPush(arg0 context.Context, arg1 string, arg2 ...any) } // RPush indicates an expected call of RPush. -func (mr *MockRedisClientMockRecorder) RPush(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) RPush(ctx, key any, values ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, key}, values...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RPush", reflect.TypeOf((*MockRedisClient)(nil).RPush), varargs...) } // SAdd mocks base method. -func (m *MockRedisClient) SAdd(arg0 context.Context, arg1 string, arg2 ...any) *redis.IntCmd { +func (m *MockRedisClient) SAdd(ctx context.Context, key string, members ...any) *redis.IntCmd { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, key} + for _, a := range members { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SAdd", varargs...) @@ -289,31 +290,31 @@ func (m *MockRedisClient) SAdd(arg0 context.Context, arg1 string, arg2 ...any) * } // SAdd indicates an expected call of SAdd. -func (mr *MockRedisClientMockRecorder) SAdd(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) SAdd(ctx, key any, members ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, key}, members...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SAdd", reflect.TypeOf((*MockRedisClient)(nil).SAdd), varargs...) } // SIsMember mocks base method. -func (m *MockRedisClient) SIsMember(arg0 context.Context, arg1 string, arg2 any) *redis.BoolCmd { +func (m *MockRedisClient) SIsMember(ctx context.Context, key string, member any) *redis.BoolCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SIsMember", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "SIsMember", ctx, key, member) ret0, _ := ret[0].(*redis.BoolCmd) return ret0 } // SIsMember indicates an expected call of SIsMember. -func (mr *MockRedisClientMockRecorder) SIsMember(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) SIsMember(ctx, key, member any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SIsMember", reflect.TypeOf((*MockRedisClient)(nil).SIsMember), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SIsMember", reflect.TypeOf((*MockRedisClient)(nil).SIsMember), ctx, key, member) } // SRem mocks base method. -func (m *MockRedisClient) SRem(arg0 context.Context, arg1 string, arg2 ...any) *redis.IntCmd { +func (m *MockRedisClient) SRem(ctx context.Context, key string, members ...any) *redis.IntCmd { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, key} + for _, a := range members { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "SRem", varargs...) @@ -322,22 +323,22 @@ func (m *MockRedisClient) SRem(arg0 context.Context, arg1 string, arg2 ...any) * } // SRem indicates an expected call of SRem. -func (mr *MockRedisClientMockRecorder) SRem(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) SRem(ctx, key any, members ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, key}, members...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SRem", reflect.TypeOf((*MockRedisClient)(nil).SRem), varargs...) } // Set mocks base method. -func (m *MockRedisClient) Set(arg0 context.Context, arg1 string, arg2 any, arg3 time.Duration) *redis.StatusCmd { +func (m *MockRedisClient) Set(ctx context.Context, key string, value any, expiration time.Duration) *redis.StatusCmd { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Set", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "Set", ctx, key, value, expiration) ret0, _ := ret[0].(*redis.StatusCmd) return ret0 } // Set indicates an expected call of Set. -func (mr *MockRedisClientMockRecorder) Set(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockRedisClientMockRecorder) Set(ctx, key, value, expiration any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockRedisClient)(nil).Set), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockRedisClient)(nil).Set), ctx, key, value, expiration) } diff --git a/modules/queue/queue.go b/modules/queue/queue.go index 56835014a5..f16b3c1f34 100644 --- a/modules/queue/queue.go +++ b/modules/queue/queue.go @@ -61,7 +61,7 @@ // func handler(items ...*mypkg.QueueItem) []*mypkg.QueueItem { ... } package queue -import "code.gitea.io/gitea/modules/util" +import "forgejo.org/modules/util" type HandlerFuncT[T any] func(...T) (unhandled []T) diff --git a/modules/queue/workergroup.go b/modules/queue/workergroup.go index ea4c0020c5..3fb821ce69 100644 --- a/modules/queue/workergroup.go +++ b/modules/queue/workergroup.go @@ -10,7 +10,7 @@ import ( "sync/atomic" "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) var ( diff --git a/modules/queue/workerqueue.go b/modules/queue/workerqueue.go index 041ce9a3f2..6a71fc4fb4 100644 --- a/modules/queue/workerqueue.go +++ b/modules/queue/workerqueue.go @@ -10,10 +10,10 @@ import ( "sync/atomic" "time" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/process" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/process" + "forgejo.org/modules/setting" ) // WorkerPoolQueue is a queue that uses a pool of workers to process items diff --git a/modules/queue/workerqueue_test.go b/modules/queue/workerqueue_test.go index 0060d88ec6..8d907ed8cd 100644 --- a/modules/queue/workerqueue_test.go +++ b/modules/queue/workerqueue_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -64,9 +64,9 @@ func TestWorkerPoolQueueUnhandled(t *testing.T) { ok := true for i := 0; i < queueSetting.Length; i++ { if i%2 == 0 { - ok = ok && assert.EqualValues(t, 2, m[i], "test %s: item %d", t.Name(), i) + ok = ok && assert.Equal(t, 2, m[i], "test %s: item %d", t.Name(), i) } else { - ok = ok && assert.EqualValues(t, 1, m[i], "test %s: item %d", t.Name(), i) + ok = ok && assert.Equal(t, 1, m[i], "test %s: item %d", t.Name(), i) } } if !ok { @@ -174,7 +174,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett assert.NotEmpty(t, tasksQ1) assert.NotEmpty(t, tasksQ2) - assert.EqualValues(t, testCount, len(tasksQ1)+len(tasksQ2)) + assert.Equal(t, testCount, len(tasksQ1)+len(tasksQ2)) } func TestWorkerPoolQueueActiveWorkers(t *testing.T) { @@ -192,13 +192,13 @@ func TestWorkerPoolQueueActiveWorkers(t *testing.T) { } time.Sleep(50 * time.Millisecond) - assert.EqualValues(t, 1, q.GetWorkerNumber()) - assert.EqualValues(t, 1, q.GetWorkerActiveNumber()) + assert.Equal(t, 1, q.GetWorkerNumber()) + assert.Equal(t, 1, q.GetWorkerActiveNumber()) time.Sleep(500 * time.Millisecond) - assert.EqualValues(t, 1, q.GetWorkerNumber()) - assert.EqualValues(t, 0, q.GetWorkerActiveNumber()) + assert.Equal(t, 1, q.GetWorkerNumber()) + assert.Equal(t, 0, q.GetWorkerActiveNumber()) time.Sleep(workerIdleDuration) - assert.EqualValues(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working + assert.Equal(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working stop() q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 3, Length: 100}, handler, false) @@ -208,13 +208,13 @@ func TestWorkerPoolQueueActiveWorkers(t *testing.T) { } time.Sleep(50 * time.Millisecond) - assert.EqualValues(t, 3, q.GetWorkerNumber()) - assert.EqualValues(t, 3, q.GetWorkerActiveNumber()) + assert.Equal(t, 3, q.GetWorkerNumber()) + assert.Equal(t, 3, q.GetWorkerActiveNumber()) time.Sleep(500 * time.Millisecond) - assert.EqualValues(t, 3, q.GetWorkerNumber()) - assert.EqualValues(t, 0, q.GetWorkerActiveNumber()) + assert.Equal(t, 3, q.GetWorkerNumber()) + assert.Equal(t, 0, q.GetWorkerActiveNumber()) time.Sleep(workerIdleDuration) - assert.EqualValues(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working + assert.Equal(t, 1, q.GetWorkerNumber()) // there is at least one worker after the queue begins working stop() } @@ -241,13 +241,13 @@ func TestWorkerPoolQueueShutdown(t *testing.T) { } <-handlerCalled time.Sleep(200 * time.Millisecond) // wait for a while to make sure all workers are active - assert.EqualValues(t, 4, q.GetWorkerActiveNumber()) + assert.Equal(t, 4, q.GetWorkerActiveNumber()) stop() // stop triggers shutdown - assert.EqualValues(t, 0, q.GetWorkerActiveNumber()) + assert.Equal(t, 0, q.GetWorkerActiveNumber()) // no item was ever handled, so we still get all of them again q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", qs, handler, false) - assert.EqualValues(t, 20, q.GetQueueItemNumber()) + assert.Equal(t, 20, q.GetQueueItemNumber()) } func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) { diff --git a/modules/recaptcha/recaptcha.go b/modules/recaptcha/recaptcha.go index 1777d169c1..95b0a77a43 100644 --- a/modules/recaptcha/recaptcha.go +++ b/modules/recaptcha/recaptcha.go @@ -11,9 +11,9 @@ import ( "net/url" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/json" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) // Response is the structure of JSON returned from API diff --git a/modules/references/references.go b/modules/references/references.go index 3b4bcb3706..7df5119393 100644 --- a/modules/references/references.go +++ b/modules/references/references.go @@ -11,10 +11,10 @@ import ( "strings" "sync" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup/mdstripper" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/markup/mdstripper" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) var ( @@ -460,15 +460,17 @@ func findAllIssueReferencesBytes(content []byte, links []string) []*rawReference } parts := strings.Split(u.EscapedPath(), "/") // /user/repo/issues/3 - if len(parts) != 5 || parts[0] != "" { + // /user/repo/pulls/7/files/... + if len(parts) < 5 || parts[0] != "" { continue } var sep string - if parts[3] == "issues" { + switch parts[3] { + case "issues": sep = "#" - } else if parts[3] == "pulls" { + case "pulls": sep = "!" - } else { + default: continue } // Note: closing/reopening keywords not supported with URLs diff --git a/modules/references/references_test.go b/modules/references/references_test.go index d5f7c4b4c5..bb22c0bd59 100644 --- a/modules/references/references_test.go +++ b/modules/references/references_test.go @@ -7,7 +7,7 @@ import ( "regexp" "testing" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "github.com/stretchr/testify/assert" ) @@ -46,7 +46,7 @@ owner/repo!123456789 contentBytes := []byte(test) convertFullHTMLReferencesToShortRefs(re, &contentBytes) result := string(contentBytes) - assert.EqualValues(t, expect, result) + assert.Equal(t, expect, result) } func TestFindAllIssueReferences(t *testing.T) { @@ -132,6 +132,30 @@ func TestFindAllIssueReferences(t *testing.T) { {203, "user4", "repo5", "203", true, XRefActionNone, nil, nil, ""}, }, }, + { + "This http://gitea.com:3000/user4/repo5/pulls/202#x yes.", + []testResult{ + {202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""}, + }, + }, + { + "This http://gitea.com:3000/user4/repo5/pulls/202/commits yes.", + []testResult{ + {202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""}, + }, + }, + { + "This http://gitea.com:3000/user4/repo5/pulls/202/files yes.", + []testResult{ + {202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""}, + }, + }, + { + "This http://gitea.com:3000/user4/repo5/pulls/202/files#diff- yes.", + []testResult{ + {202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""}, + }, + }, { "This http://GiTeA.COM:3000/user4/repo6/pulls/205 yes.", []testResult{ @@ -284,9 +308,9 @@ func testFixtures(t *testing.T, fixtures []testFixture, context string) { } expref := rawToIssueReferenceList(expraw) refs := FindAllIssueReferencesMarkdown(fixture.input) - assert.EqualValues(t, expref, refs, "[%s] Failed to parse: {%s}", context, fixture.input) + assert.Equal(t, expref, refs, "[%s] Failed to parse: {%s}", context, fixture.input) rawrefs := findAllIssueReferencesMarkdown(fixture.input) - assert.EqualValues(t, expraw, rawrefs, "[%s] Failed to parse: {%s}", context, fixture.input) + assert.Equal(t, expraw, rawrefs, "[%s] Failed to parse: {%s}", context, fixture.input) } // Restore for other tests that may rely on the original value @@ -295,7 +319,7 @@ func testFixtures(t *testing.T, fixtures []testFixture, context string) { func TestFindAllMentions(t *testing.T) { res := FindAllMentionsBytes([]byte("@tasha, @mike; @lucy: @john")) - assert.EqualValues(t, []RefSpan{ + assert.Equal(t, []RefSpan{ {Start: 0, End: 6}, {Start: 8, End: 13}, {Start: 15, End: 20}, @@ -558,7 +582,7 @@ func TestParseCloseKeywords(t *testing.T) { res := pat.FindAllStringSubmatch(test.match, -1) assert.Len(t, res, 1) assert.Len(t, res[0], 2) - assert.EqualValues(t, test.expected, res[0][1]) + assert.Equal(t, test.expected, res[0][1]) } } } diff --git a/modules/regexplru/regexplru.go b/modules/regexplru/regexplru.go index 8f66dcf3f7..b452094c16 100644 --- a/modules/regexplru/regexplru.go +++ b/modules/regexplru/regexplru.go @@ -6,7 +6,7 @@ package regexplru import ( "regexp" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" lru "github.com/hashicorp/golang-lru/v2" ) diff --git a/modules/regexplru/regexplru_test.go b/modules/regexplru/regexplru_test.go index 8c0c722336..6e15e88e14 100644 --- a/modules/regexplru/regexplru_test.go +++ b/modules/regexplru/regexplru_test.go @@ -19,9 +19,9 @@ func TestRegexpLru(t *testing.T) { require.NoError(t, err) assert.True(t, r.MatchString("a")) - assert.EqualValues(t, 1, lruCache.Len()) + assert.Equal(t, 1, lruCache.Len()) _, err = GetCompiled("(") require.Error(t, err) - assert.EqualValues(t, 2, lruCache.Len()) + assert.Equal(t, 2, lruCache.Len()) } diff --git a/modules/repository/branch.go b/modules/repository/branch.go index 2bf9930f19..59b5f9e7d5 100644 --- a/modules/repository/branch.go +++ b/modules/repository/branch.go @@ -7,14 +7,14 @@ import ( "context" "fmt" - "code.gitea.io/gitea/models/db" - git_model "code.gitea.io/gitea/models/git" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/gitrepo" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + git_model "forgejo.org/models/git" + repo_model "forgejo.org/models/repo" + "forgejo.org/modules/container" + "forgejo.org/modules/git" + "forgejo.org/modules/gitrepo" + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" ) // SyncRepoBranches synchronizes branch table with repository branches diff --git a/modules/repository/branch_test.go b/modules/repository/branch_test.go index b98618a16b..31e27f222f 100644 --- a/modules/repository/branch_test.go +++ b/modules/repository/branch_test.go @@ -6,10 +6,10 @@ package repository import ( "testing" - "code.gitea.io/gitea/models/db" - git_model "code.gitea.io/gitea/models/git" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/db" + git_model "forgejo.org/models/git" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -28,5 +28,5 @@ func TestSyncRepoBranches(t *testing.T) { assert.Equal(t, "sha1", repo.ObjectFormatName) branch, err := git_model.GetBranch(db.DefaultContext, 1, "master") require.NoError(t, err) - assert.EqualValues(t, "master", branch.Name) + assert.Equal(t, "master", branch.Name) } diff --git a/modules/repository/collaborator.go b/modules/repository/collaborator.go index 17915d34b7..5a0c4451b7 100644 --- a/modules/repository/collaborator.go +++ b/modules/repository/collaborator.go @@ -6,11 +6,11 @@ package repository import ( "context" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/perm" - access_model "code.gitea.io/gitea/models/perm/access" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" + "forgejo.org/models/db" + "forgejo.org/models/perm" + access_model "forgejo.org/models/perm/access" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" "xorm.io/builder" ) diff --git a/modules/repository/collaborator_test.go b/modules/repository/collaborator_test.go index 63352e33e8..dae173506b 100644 --- a/modules/repository/collaborator_test.go +++ b/modules/repository/collaborator_test.go @@ -6,14 +6,14 @@ package repository import ( "testing" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/organization" - perm_model "code.gitea.io/gitea/models/perm" - access_model "code.gitea.io/gitea/models/perm/access" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unit" - "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" + "forgejo.org/models/db" + "forgejo.org/models/organization" + perm_model "forgejo.org/models/perm" + access_model "forgejo.org/models/perm/access" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unit" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/modules/repository/commits.go b/modules/repository/commits.go index ede60429a1..261b6f7a22 100644 --- a/modules/repository/commits.go +++ b/modules/repository/commits.go @@ -1,4 +1,5 @@ // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package repository @@ -9,13 +10,14 @@ import ( "net/url" "time" - "code.gitea.io/gitea/models/avatars" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/cache" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" + asymkey_model "forgejo.org/models/asymkey" + "forgejo.org/models/avatars" + user_model "forgejo.org/models/user" + "forgejo.org/modules/cache" + "forgejo.org/modules/git" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + api "forgejo.org/modules/structs" ) // PushCommit represents a commit in a push operation. @@ -26,6 +28,8 @@ type PushCommit struct { AuthorName string CommitterEmail string CommitterName string + Signature *git.ObjectSignature + Verification *asymkey_model.ObjectVerification Timestamp time.Time } @@ -145,6 +149,32 @@ func (pc *PushCommits) AvatarLink(ctx context.Context, email string) string { return v } +// PushCommitToCommit transforms a PushCommit to a git.Commit type on a best effort basis. +// +// Attention: Converting a Commit to a PushCommit and converting back to a Commit will not result in an identical object! +func PushCommitToCommit(commit *PushCommit) (*git.Commit, error) { + id, err := git.NewIDFromString(commit.Sha1) + if err != nil { + return nil, err + } + return &git.Commit{ + ID: id, + Author: &git.Signature{ + Name: commit.AuthorName, + Email: commit.AuthorEmail, + When: commit.Timestamp, + }, + Committer: &git.Signature{ + Name: commit.CommitterName, + Email: commit.CommitterEmail, + When: commit.Timestamp, + }, + CommitMessage: commit.Message, + Signature: commit.Signature, + Parents: []git.ObjectID{}, + }, nil +} + // CommitToPushCommit transforms a git.Commit to PushCommit type. func CommitToPushCommit(commit *git.Commit) *PushCommit { return &PushCommit{ @@ -154,6 +184,7 @@ func CommitToPushCommit(commit *git.Commit) *PushCommit { AuthorName: commit.Author.Name, CommitterEmail: commit.Committer.Email, CommitterName: commit.Committer.Name, + Signature: commit.Signature, Timestamp: commit.Author.When, } } diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go index a22f3d07b8..4b6d4bfe51 100644 --- a/modules/repository/commits_test.go +++ b/modules/repository/commits_test.go @@ -1,4 +1,5 @@ // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package repository @@ -8,11 +9,11 @@ import ( "testing" "time" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + "forgejo.org/modules/git" + "forgejo.org/modules/setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -63,9 +64,9 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { assert.Equal(t, "user2", payloadCommits[0].Committer.UserName) assert.Equal(t, "User2", payloadCommits[0].Author.Name) assert.Equal(t, "user2", payloadCommits[0].Author.UserName) - assert.EqualValues(t, []string{}, payloadCommits[0].Added) - assert.EqualValues(t, []string{}, payloadCommits[0].Removed) - assert.EqualValues(t, []string{"readme.md"}, payloadCommits[0].Modified) + assert.Equal(t, []string{}, payloadCommits[0].Added) + assert.Equal(t, []string{}, payloadCommits[0].Removed) + assert.Equal(t, []string{"readme.md"}, payloadCommits[0].Modified) assert.Equal(t, "27566bd", payloadCommits[1].ID) assert.Equal(t, "good signed commit (with not yet validated email)", payloadCommits[1].Message) @@ -74,9 +75,9 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { assert.Equal(t, "user2", payloadCommits[1].Committer.UserName) assert.Equal(t, "User2", payloadCommits[1].Author.Name) assert.Equal(t, "user2", payloadCommits[1].Author.UserName) - assert.EqualValues(t, []string{}, payloadCommits[1].Added) - assert.EqualValues(t, []string{}, payloadCommits[1].Removed) - assert.EqualValues(t, []string{"readme.md"}, payloadCommits[1].Modified) + assert.Equal(t, []string{}, payloadCommits[1].Added) + assert.Equal(t, []string{}, payloadCommits[1].Removed) + assert.Equal(t, []string{"readme.md"}, payloadCommits[1].Modified) assert.Equal(t, "5099b81", payloadCommits[2].ID) assert.Equal(t, "good signed commit", payloadCommits[2].Message) @@ -85,9 +86,9 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { assert.Equal(t, "user2", payloadCommits[2].Committer.UserName) assert.Equal(t, "User2", payloadCommits[2].Author.Name) assert.Equal(t, "user2", payloadCommits[2].Author.UserName) - assert.EqualValues(t, []string{"readme.md"}, payloadCommits[2].Added) - assert.EqualValues(t, []string{}, payloadCommits[2].Removed) - assert.EqualValues(t, []string{}, payloadCommits[2].Modified) + assert.Equal(t, []string{"readme.md"}, payloadCommits[2].Added) + assert.Equal(t, []string{}, payloadCommits[2].Removed) + assert.Equal(t, []string{}, payloadCommits[2].Modified) assert.Equal(t, "69554a6", headCommit.ID) assert.Equal(t, "not signed commit", headCommit.Message) @@ -96,9 +97,9 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) { assert.Equal(t, "user2", headCommit.Committer.UserName) assert.Equal(t, "User2", headCommit.Author.Name) assert.Equal(t, "user2", headCommit.Author.UserName) - assert.EqualValues(t, []string{}, headCommit.Added) - assert.EqualValues(t, []string{}, headCommit.Removed) - assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified) + assert.Equal(t, []string{}, headCommit.Added) + assert.Equal(t, []string{}, headCommit.Removed) + assert.Equal(t, []string{"readme.md"}, headCommit.Modified) } func TestPushCommits_AvatarLink(t *testing.T) { @@ -133,6 +134,50 @@ func TestPushCommits_AvatarLink(t *testing.T) { pushCommits.AvatarLink(db.DefaultContext, "nonexistent@example.com")) } +func TestPushCommitToCommit(t *testing.T) { + now := time.Now() + sig := &git.Signature{ + Email: "example@example.com", + Name: "John Doe", + When: now, + } + const hexString = "0123456789abcdef0123456789abcdef01234567" + sha1, err := git.NewIDFromString(hexString) + require.NoError(t, err) + commit, err := PushCommitToCommit(&PushCommit{ + Sha1: sha1.String(), + Message: "Commit Message", + AuthorEmail: "example@example.com", + AuthorName: "John Doe", + CommitterEmail: "example@example.com", + CommitterName: "John Doe", + Signature: nil, + Timestamp: now, + }) + require.NoError(t, err) + assert.Equal(t, sha1, commit.ID) + assert.Equal(t, "Commit Message", commit.CommitMessage) + assert.Equal(t, sig, commit.Author) + assert.Equal(t, sig, commit.Committer) + assert.Nil(t, commit.Signature) +} + +func TestPushCommitToCommitInvalidSha(t *testing.T) { + now := time.Now() + const hexString = "012" + _, err := PushCommitToCommit(&PushCommit{ + Sha1: hexString, + Message: "Commit Message", + AuthorEmail: "example@example.com", + AuthorName: "John Doe", + CommitterEmail: "example@example.com", + CommitterName: "John Doe", + Signature: nil, + Timestamp: now, + }) + require.Error(t, err) +} + func TestCommitToPushCommit(t *testing.T) { now := time.Now() sig := &git.Signature{ diff --git a/modules/repository/create.go b/modules/repository/create.go index 32c6235544..becfed0370 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -1,4 +1,5 @@ // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package repository @@ -11,22 +12,22 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/models" - activities_model "code.gitea.io/gitea/models/activities" - "code.gitea.io/gitea/models/db" - git_model "code.gitea.io/gitea/models/git" - "code.gitea.io/gitea/models/organization" - "code.gitea.io/gitea/models/perm" - access_model "code.gitea.io/gitea/models/perm/access" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unit" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/models/webhook" - issue_indexer "code.gitea.io/gitea/modules/indexer/issues" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models" + activities_model "forgejo.org/models/activities" + "forgejo.org/models/db" + git_model "forgejo.org/models/git" + "forgejo.org/models/organization" + "forgejo.org/models/perm" + access_model "forgejo.org/models/perm/access" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unit" + user_model "forgejo.org/models/user" + "forgejo.org/models/webhook" + issue_indexer "forgejo.org/modules/indexer/issues" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + api "forgejo.org/modules/structs" + "forgejo.org/modules/util" ) // CreateRepositoryByExample creates a repository for the user/organization. @@ -68,12 +69,16 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re // insert units for repo defaultUnits := unit.DefaultRepoUnits - if isFork { + switch { + case isFork: defaultUnits = unit.DefaultForkRepoUnits + case repo.IsMirror: + defaultUnits = unit.DefaultMirrorRepoUnits } units := make([]repo_model.RepoUnit, 0, len(defaultUnits)) for _, tp := range defaultUnits { - if tp == unit.TypeIssues { + switch tp { + case unit.TypeIssues: units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: tp, @@ -83,7 +88,7 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re EnableDependencies: setting.Service.DefaultEnableDependencies, }, }) - } else if tp == unit.TypePullRequests { + case unit.TypePullRequests: units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: tp, @@ -94,7 +99,7 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re AllowRebaseUpdate: true, }, }) - } else { + default: units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: tp, @@ -240,6 +245,11 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili e := db.GetEngine(ctx) + // If the repository was reported as abusive, a shadow copy should be created before first update. + if err := repo_model.IfNeededCreateShadowCopyForRepository(ctx, repo, true); err != nil { + return err + } + if _, err = e.ID(repo.ID).AllCols().Update(repo); err != nil { return fmt.Errorf("update: %w", err) } diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go index c743271c26..45f7f8e853 100644 --- a/modules/repository/create_test.go +++ b/modules/repository/create_test.go @@ -6,10 +6,10 @@ package repository import ( "testing" - activities_model "code.gitea.io/gitea/models/activities" - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unittest" + activities_model "forgejo.org/models/activities" + "forgejo.org/models/db" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -42,5 +42,5 @@ func TestGetDirectorySize(t *testing.T) { size, err := getDirectorySize(repo.RepoPath()) require.NoError(t, err) - assert.EqualValues(t, size, repo.Size) + assert.Equal(t, size, repo.Size) } diff --git a/modules/repository/delete.go b/modules/repository/delete.go index 04af98beef..6fff16b406 100644 --- a/modules/repository/delete.go +++ b/modules/repository/delete.go @@ -6,9 +6,9 @@ package repository import ( "context" - "code.gitea.io/gitea/models/organization" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" + "forgejo.org/models/organization" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" ) // CanUserDelete returns true if user could delete the repository diff --git a/modules/repository/env.go b/modules/repository/env.go index e4f32092fc..110f6ca674 100644 --- a/modules/repository/env.go +++ b/modules/repository/env.go @@ -8,9 +8,9 @@ import ( "os" "strings" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" ) // env keys for git hooks need diff --git a/modules/repository/fork.go b/modules/repository/fork.go index fbf0008716..42801fa80d 100644 --- a/modules/repository/fork.go +++ b/modules/repository/fork.go @@ -6,9 +6,9 @@ package repository import ( "context" - "code.gitea.io/gitea/models/organization" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" + "forgejo.org/models/organization" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" ) // CanUserForkRepo returns true if specified user can fork repository. diff --git a/modules/repository/hooks.go b/modules/repository/hooks.go index 75a21a09dd..0f5e3afc34 100644 --- a/modules/repository/hooks.go +++ b/modules/repository/hooks.go @@ -8,8 +8,8 @@ import ( "os" "path/filepath" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) func getHookTemplates() (hookNames, hookTpls, giteaHookTpls []string) { diff --git a/modules/repository/init.go b/modules/repository/init.go index 5f500c5233..7b1442be93 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -10,14 +10,14 @@ import ( "sort" "strings" - issues_model "code.gitea.io/gitea/models/issues" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/label" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/options" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + issues_model "forgejo.org/models/issues" + repo_model "forgejo.org/models/repo" + "forgejo.org/modules/git" + "forgejo.org/modules/label" + "forgejo.org/modules/log" + "forgejo.org/modules/options" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) type OptionFile struct { diff --git a/modules/repository/init_test.go b/modules/repository/init_test.go index 227efdc1db..1fa928105c 100644 --- a/modules/repository/init_test.go +++ b/modules/repository/init_test.go @@ -14,17 +14,17 @@ func TestMergeCustomLabels(t *testing.T) { all: []string{"a", "a.yaml", "a.yml"}, custom: nil, }) - assert.EqualValues(t, []string{"a.yaml"}, files, "yaml file should win") + assert.Equal(t, []string{"a.yaml"}, files, "yaml file should win") files = mergeCustomLabelFiles(optionFileList{ all: []string{"a", "a.yaml"}, custom: []string{"a"}, }) - assert.EqualValues(t, []string{"a"}, files, "custom file should win") + assert.Equal(t, []string{"a"}, files, "custom file should win") files = mergeCustomLabelFiles(optionFileList{ all: []string{"a", "a.yml", "a.yaml"}, custom: []string{"a", "a.yml"}, }) - assert.EqualValues(t, []string{"a.yml"}, files, "custom yml file should win if no yaml") + assert.Equal(t, []string{"a.yml"}, files, "custom yml file should win if no yaml") } diff --git a/modules/repository/license.go b/modules/repository/license.go index dcbefc8ded..9776f047af 100644 --- a/modules/repository/license.go +++ b/modules/repository/license.go @@ -11,7 +11,7 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/options" + "forgejo.org/modules/options" ) type LicenseValues struct { @@ -99,7 +99,8 @@ func getLicensePlaceholder(name string) *licensePlaceholder { // Some special placeholders for specific licenses. // It's unsafe to apply them to all licenses. - if name == "0BSD" { + switch name { + case "0BSD": return &licensePlaceholder{ Owner: []string{"AUTHOR"}, Email: []string{"EMAIL"}, @@ -108,7 +109,7 @@ func getLicensePlaceholder(name string) *licensePlaceholder { } // Other special placeholders can be added here. - } else if name == "BSD-4-Clause" { + case "BSD-4-Clause": ret.Owner = append(ret.Owner, "COPYRIGHT HOLDER") ret.Owner = append(ret.Owner, "the organization") } diff --git a/modules/repository/main_test.go b/modules/repository/main_test.go index 7b245cf118..5906b10865 100644 --- a/modules/repository/main_test.go +++ b/modules/repository/main_test.go @@ -6,10 +6,10 @@ package repository import ( "testing" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/unittest" - _ "code.gitea.io/gitea/models/actions" - _ "code.gitea.io/gitea/models/forgefed" + _ "forgejo.org/models/actions" + _ "forgejo.org/models/forgefed" ) func TestMain(m *testing.M) { diff --git a/modules/repository/push.go b/modules/repository/push.go index 66d0417caf..d8be0a3e8c 100644 --- a/modules/repository/push.go +++ b/modules/repository/push.go @@ -4,7 +4,7 @@ package repository import ( - "code.gitea.io/gitea/modules/git" + "forgejo.org/modules/git" ) // PushUpdateOptions defines the push update options diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 98e7fcbc0a..c86d48fe52 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -12,17 +12,17 @@ import ( "strings" "time" - "code.gitea.io/gitea/models/db" - git_model "code.gitea.io/gitea/models/git" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/gitrepo" - "code.gitea.io/gitea/modules/lfs" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/db" + git_model "forgejo.org/models/git" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/container" + "forgejo.org/modules/git" + "forgejo.org/modules/gitrepo" + "forgejo.org/modules/lfs" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/timeutil" ) /* diff --git a/modules/repository/repo_test.go b/modules/repository/repo_test.go index f3e7be6d7d..45a650ba42 100644 --- a/modules/repository/repo_test.go +++ b/modules/repository/repo_test.go @@ -6,7 +6,7 @@ package repository import ( "testing" - "code.gitea.io/gitea/modules/git" + "forgejo.org/modules/git" "github.com/stretchr/testify/assert" ) @@ -63,7 +63,7 @@ func Test_calcSync(t *testing.T) { inserts, deletes, updates := calcSync(gitTags, dbReleases) if assert.Len(t, inserts, 1, "inserts") { - assert.EqualValues(t, *gitTags[2], *inserts[0], "inserts equal") + assert.Equal(t, *gitTags[2], *inserts[0], "inserts equal") } if assert.Len(t, deletes, 1, "deletes") { @@ -71,6 +71,6 @@ func Test_calcSync(t *testing.T) { } if assert.Len(t, updates, 1, "updates") { - assert.EqualValues(t, *gitTags[1], *updates[0], "updates equal") + assert.Equal(t, *gitTags[1], *updates[0], "updates equal") } } diff --git a/modules/repository/temp.go b/modules/repository/temp.go index 04faa9db3d..6048c43a8e 100644 --- a/modules/repository/temp.go +++ b/modules/repository/temp.go @@ -9,9 +9,9 @@ import ( "path" "path/filepath" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) // LocalCopyPath returns the local repository temporary copy path. diff --git a/modules/session/db.go b/modules/session/db.go index 3b12b93521..eea7e2136e 100644 --- a/modules/session/db.go +++ b/modules/session/db.go @@ -7,9 +7,9 @@ import ( "log" "sync" - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/models/auth" + "forgejo.org/models/db" + "forgejo.org/modules/timeutil" "code.forgejo.org/go-chi/session" ) diff --git a/modules/session/redis.go b/modules/session/redis.go index 230b501080..cf84ef21d9 100644 --- a/modules/session/redis.go +++ b/modules/session/redis.go @@ -22,8 +22,8 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/nosql" + "forgejo.org/modules/graceful" + "forgejo.org/modules/nosql" "code.forgejo.org/go-chi/session" ) diff --git a/modules/session/virtual.go b/modules/session/virtual.go index 9cf3683a71..1c3e1c778b 100644 --- a/modules/session/virtual.go +++ b/modules/session/virtual.go @@ -7,8 +7,8 @@ import ( "fmt" "sync" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/json" + "forgejo.org/modules/log" "code.forgejo.org/go-chi/session" memcache "code.forgejo.org/go-chi/session/memcache" diff --git a/modules/setting/actions_test.go b/modules/setting/actions_test.go index 4bff6e02ad..a3cd5ced44 100644 --- a/modules/setting/actions_test.go +++ b/modules/setting/actions_test.go @@ -21,9 +21,9 @@ func Test_getStorageInheritNameSectionTypeForActions(t *testing.T) { require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "minio", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) iniStr = ` [storage.actions_log] @@ -34,9 +34,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "minio", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) + assert.Equal(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) iniStr = ` [storage.actions_log] @@ -50,9 +50,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "minio", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) + assert.Equal(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) iniStr = ` [storage.actions_artifacts] @@ -66,9 +66,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "local", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) + assert.Equal(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) iniStr = ` [storage.actions_artifacts] @@ -82,9 +82,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "local", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) + assert.Equal(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) + assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) iniStr = `` cfg, err = NewConfigProviderFromData(iniStr) @@ -92,9 +92,9 @@ STORAGE_TYPE = minio require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "local", Actions.LogStorage.Type) - assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) + assert.Equal(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) + assert.Equal(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) } func Test_getDefaultActionsURLForActions(t *testing.T) { @@ -151,7 +151,7 @@ DEFAULT_ACTIONS_URL = https://example.com require.NoError(t, err) require.NoError(t, loadActionsFrom(cfg)) - assert.EqualValues(t, tt.wantURL, Actions.DefaultActionsURL.URL()) + assert.Equal(t, tt.wantURL, Actions.DefaultActionsURL.URL()) }) } } diff --git a/modules/setting/admin.go b/modules/setting/admin.go index eed3aa22cf..7a1e071bac 100644 --- a/modules/setting/admin.go +++ b/modules/setting/admin.go @@ -4,7 +4,7 @@ package setting import ( - "code.gitea.io/gitea/modules/container" + "forgejo.org/modules/container" ) // Admin settings diff --git a/modules/setting/admin_test.go b/modules/setting/admin_test.go index 0c6c24b038..744f069d82 100644 --- a/modules/setting/admin_test.go +++ b/modules/setting/admin_test.go @@ -6,7 +6,7 @@ package setting import ( "testing" - "code.gitea.io/gitea/modules/container" + "forgejo.org/modules/container" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -26,8 +26,8 @@ func Test_loadAdminFrom(t *testing.T) { loadAdminFrom(cfg) assert.True(t, Admin.DisableRegularOrgCreation) - assert.EqualValues(t, "z", Admin.DefaultEmailNotification) + assert.Equal(t, "z", Admin.DefaultEmailNotification) assert.True(t, Admin.SendNotificationEmailOnNewUser) - assert.EqualValues(t, container.SetOf("a", "b"), Admin.UserDisabledFeatures) - assert.EqualValues(t, container.SetOf("x", "y"), Admin.ExternalUserDisableFeatures) + assert.Equal(t, container.SetOf("a", "b"), Admin.UserDisabledFeatures) + assert.Equal(t, container.SetOf("x", "y"), Admin.ExternalUserDisableFeatures) } diff --git a/modules/setting/annex.go b/modules/setting/annex.go new file mode 100644 index 0000000000..aa41c14ff0 --- /dev/null +++ b/modules/setting/annex.go @@ -0,0 +1,25 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +import ( + "forgejo.org/modules/log" +) + +// Annex represents the configuration for git-annex +var Annex = struct { + Enabled bool `ini:"ENABLED"` + DisableP2PHTTP bool `ini:"DISABLE_P2PHTTP"` +}{} + +func loadAnnexFrom(rootCfg ConfigProvider) { + sec := rootCfg.Section("annex") + if err := sec.MapTo(&Annex); err != nil { + log.Fatal("Failed to map Annex settings: %v", err) + } + if !sec.HasKey("DISABLE_P2PHTTP") { + // If DisableP2PHTTP is not explicitly set then use DisableHTTPGit as its default + Annex.DisableP2PHTTP = Repository.DisableHTTPGit + } +} diff --git a/modules/setting/api.go b/modules/setting/api.go index c36f05cfd1..18180c3d07 100644 --- a/modules/setting/api.go +++ b/modules/setting/api.go @@ -7,7 +7,7 @@ import ( "net/url" "path" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // API settings diff --git a/modules/setting/attachment_test.go b/modules/setting/attachment_test.go index f8085c1657..a56fcf1c55 100644 --- a/modules/setting/attachment_test.go +++ b/modules/setting/attachment_test.go @@ -26,9 +26,9 @@ MINIO_ENDPOINT = my_minio:9000 require.NoError(t, loadAttachmentFrom(cfg)) assert.EqualValues(t, "minio", Attachment.Storage.Type) - assert.EqualValues(t, "my_minio:9000", Attachment.Storage.MinioConfig.Endpoint) - assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "my_minio:9000", Attachment.Storage.MinioConfig.Endpoint) + assert.Equal(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) } func Test_getStorageTypeSectionOverridesStorageSection(t *testing.T) { @@ -48,8 +48,8 @@ MINIO_BUCKET = gitea require.NoError(t, loadAttachmentFrom(cfg)) assert.EqualValues(t, "minio", Attachment.Storage.Type) - assert.EqualValues(t, "gitea-minio", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-minio", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) } func Test_getStorageSpecificOverridesStorage(t *testing.T) { @@ -70,8 +70,8 @@ STORAGE_TYPE = local require.NoError(t, loadAttachmentFrom(cfg)) assert.EqualValues(t, "minio", Attachment.Storage.Type) - assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) } func Test_getStorageGetDefaults(t *testing.T) { @@ -81,7 +81,7 @@ func Test_getStorageGetDefaults(t *testing.T) { require.NoError(t, loadAttachmentFrom(cfg)) // default storage is local, so bucket is empty - assert.EqualValues(t, "", Attachment.Storage.MinioConfig.Bucket) + assert.Empty(t, Attachment.Storage.MinioConfig.Bucket) } func Test_getStorageInheritNameSectionType(t *testing.T) { @@ -116,7 +116,7 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Attachment.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) } func Test_AttachmentStorage1(t *testing.T) { @@ -129,6 +129,6 @@ STORAGE_TYPE = minio require.NoError(t, loadAttachmentFrom(cfg)) assert.EqualValues(t, "minio", Attachment.Storage.Type) - assert.EqualValues(t, "gitea", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) } diff --git a/modules/setting/cache.go b/modules/setting/cache.go index bfa6ca0e61..cdc7e1a971 100644 --- a/modules/setting/cache.go +++ b/modules/setting/cache.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // Cache represents cache settings diff --git a/modules/setting/camo.go b/modules/setting/camo.go index 608ecf8363..5d31446a41 100644 --- a/modules/setting/camo.go +++ b/modules/setting/camo.go @@ -6,7 +6,7 @@ package setting import ( "strconv" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) var Camo = struct { diff --git a/modules/setting/config.go b/modules/setting/config.go index 03558574c2..6299640e61 100644 --- a/modules/setting/config.go +++ b/modules/setting/config.go @@ -6,8 +6,8 @@ package setting import ( "sync" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting/config" + "forgejo.org/modules/log" + "forgejo.org/modules/setting/config" ) type PictureStruct struct { diff --git a/modules/setting/config/value.go b/modules/setting/config/value.go index f0ec120544..3409f61b76 100644 --- a/modules/setting/config/value.go +++ b/modules/setting/config/value.go @@ -7,9 +7,9 @@ import ( "context" "sync" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/util" ) type CfgSecKey struct { diff --git a/modules/setting/config_env.go b/modules/setting/config_env.go index 2bc1a5c341..458dbb51bb 100644 --- a/modules/setting/config_env.go +++ b/modules/setting/config_env.go @@ -10,7 +10,7 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) const ( diff --git a/modules/setting/config_env_test.go b/modules/setting/config_env_test.go index bec3e584ef..fed0f668aa 100644 --- a/modules/setting/config_env_test.go +++ b/modules/setting/config_env_test.go @@ -30,8 +30,8 @@ func TestDecodeEnvSectionKey(t *testing.T) { ok, section, key = decodeEnvSectionKey("SEC") assert.False(t, ok) - assert.Equal(t, "", section) - assert.Equal(t, "", key) + assert.Empty(t, section) + assert.Empty(t, key) } func TestDecodeEnvironmentKey(t *testing.T) { @@ -40,19 +40,19 @@ func TestDecodeEnvironmentKey(t *testing.T) { ok, section, key, file := decodeEnvironmentKey(prefix, suffix, "SEC__KEY") assert.False(t, ok) - assert.Equal(t, "", section) - assert.Equal(t, "", key) + assert.Empty(t, section) + assert.Empty(t, key) assert.False(t, file) ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC") assert.False(t, ok) - assert.Equal(t, "", section) - assert.Equal(t, "", key) + assert.Empty(t, section) + assert.Empty(t, key) assert.False(t, file) ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA____KEY") assert.True(t, ok) - assert.Equal(t, "", section) + assert.Empty(t, section) assert.Equal(t, "KEY", key) assert.False(t, file) @@ -72,8 +72,8 @@ func TestDecodeEnvironmentKey(t *testing.T) { // but it could be fixed in the future by adding a new suffix like "__VALUE" (no such key VALUE is used in Gitea either) ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC__FILE") assert.False(t, ok) - assert.Equal(t, "", section) - assert.Equal(t, "", key) + assert.Empty(t, section) + assert.Empty(t, key) assert.True(t, file) ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC__KEY__FILE") diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go index 12cf36aa59..19f3b9008a 100644 --- a/modules/setting/config_provider.go +++ b/modules/setting/config_provider.go @@ -12,8 +12,8 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/util" "gopkg.in/ini.v1" //nolint:depguard ) @@ -262,7 +262,7 @@ func (p *iniConfigProvider) Save() error { } filename := p.file if filename == "" { - return fmt.Errorf("config file path must not be empty") + return errors.New("config file path must not be empty") } if p.loadedFromEmpty { if err := os.MkdirAll(filepath.Dir(filename), os.ModePerm); err != nil { diff --git a/modules/setting/config_provider_test.go b/modules/setting/config_provider_test.go index 702be80861..3b99911f38 100644 --- a/modules/setting/config_provider_test.go +++ b/modules/setting/config_provider_test.go @@ -63,17 +63,17 @@ key = 123 // test default behavior assert.Equal(t, "123", ConfigSectionKeyString(sec, "key")) - assert.Equal(t, "", ConfigSectionKeyString(secSub, "key")) + assert.Empty(t, ConfigSectionKeyString(secSub, "key")) assert.Equal(t, "def", ConfigSectionKeyString(secSub, "key", "def")) assert.Equal(t, "123", ConfigInheritedKeyString(secSub, "key")) // Workaround for ini package's BuggyKeyOverwritten behavior - assert.Equal(t, "", ConfigSectionKeyString(sec, "empty")) - assert.Equal(t, "", ConfigSectionKeyString(secSub, "empty")) + assert.Empty(t, ConfigSectionKeyString(sec, "empty")) + assert.Empty(t, ConfigSectionKeyString(secSub, "empty")) assert.Equal(t, "def", ConfigInheritedKey(secSub, "empty").MustString("def")) assert.Equal(t, "def", ConfigInheritedKey(secSub, "empty").MustString("xyz")) - assert.Equal(t, "", ConfigSectionKeyString(sec, "empty")) + assert.Empty(t, ConfigSectionKeyString(sec, "empty")) assert.Equal(t, "def", ConfigSectionKeyString(secSub, "empty")) } diff --git a/modules/setting/cron_test.go b/modules/setting/cron_test.go index 32f8ecffd2..cabfb3a325 100644 --- a/modules/setting/cron_test.go +++ b/modules/setting/cron_test.go @@ -39,6 +39,6 @@ EXTEND = true _, err = getCronSettings(cfg, "test", extended) require.NoError(t, err) assert.True(t, extended.Base) - assert.EqualValues(t, "white rabbit", extended.Second) + assert.Equal(t, "white rabbit", extended.Second) assert.True(t, extended.Extend) } diff --git a/modules/setting/database.go b/modules/setting/database.go index 76fae27164..96859e4cf4 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -10,8 +10,13 @@ import ( "net/url" "os" "path/filepath" + "strconv" "strings" "time" + + "forgejo.org/modules/log" + + "xorm.io/xorm" ) var ( @@ -24,35 +29,41 @@ var ( EnableSQLite3 bool // Database holds the database settings - Database = struct { - Type DatabaseType - Host string - Name string - User string - Passwd string - Schema string - SSLMode string - Path string - LogSQL bool - MysqlCharset string - CharsetCollation string - Timeout int // seconds - SQLiteJournalMode string - DBConnectRetries int - DBConnectBackoff time.Duration - MaxIdleConns int - MaxOpenConns int - ConnMaxIdleTime time.Duration - ConnMaxLifetime time.Duration - IterateBufferSize int - AutoMigration bool - SlowQueryThreshold time.Duration - }{ + Database = DatabaseSettings{ Timeout: 500, IterateBufferSize: 50, } ) +type DatabaseSettings struct { + Type DatabaseType + Host string + HostPrimary string + HostReplica string + LoadBalancePolicy string + LoadBalanceWeights string + Name string + User string + Passwd string + Schema string + SSLMode string + Path string + LogSQL bool + MysqlCharset string + CharsetCollation string + Timeout int // seconds + SQLiteJournalMode string + DBConnectRetries int + DBConnectBackoff time.Duration + MaxIdleConns int + MaxOpenConns int + ConnMaxIdleTime time.Duration + ConnMaxLifetime time.Duration + IterateBufferSize int + AutoMigration bool + SlowQueryThreshold time.Duration +} + // LoadDBSetting loads the database settings func LoadDBSetting() { loadDBSetting(CfgProvider) @@ -63,6 +74,10 @@ func loadDBSetting(rootCfg ConfigProvider) { Database.Type = DatabaseType(sec.Key("DB_TYPE").String()) Database.Host = sec.Key("HOST").String() + Database.HostPrimary = sec.Key("HOST_PRIMARY").String() + Database.HostReplica = sec.Key("HOST_REPLICA").String() + Database.LoadBalancePolicy = sec.Key("LOAD_BALANCE_POLICY").String() + Database.LoadBalanceWeights = sec.Key("LOAD_BALANCE_WEIGHTS").String() Database.Name = sec.Key("NAME").String() Database.User = sec.Key("USER").String() if len(Database.Passwd) == 0 { @@ -99,8 +114,114 @@ func loadDBSetting(rootCfg ConfigProvider) { } } -// DBConnStr returns database connection string -func DBConnStr() (string, error) { +// DBMasterConnStr returns the connection string for the master (primary) database. +// If a primary host is defined in the configuration, it is used; +// otherwise, it falls back to Database.Host. +// Returns an error if no master host is provided but a slave is defined. +func DBMasterConnStr() (string, error) { + var host string + if Database.HostPrimary != "" { + host = Database.HostPrimary + } else { + host = Database.Host + } + if host == "" && Database.HostReplica != "" { + return "", errors.New("master host is not defined while slave is defined; cannot proceed") + } + + // For SQLite, no host is needed + if host == "" && !Database.Type.IsSQLite3() { + return "", errors.New("no database host defined") + } + + return dbConnStrWithHost(host) +} + +// DBSlaveConnStrs returns one or more connection strings for the replica databases. +// If a replica host is defined (possibly as a comma-separated list) then those DSNs are returned. +// Otherwise, this function falls back to the master DSN (with a warning log). +func DBSlaveConnStrs() ([]string, error) { + var dsns []string + if Database.HostReplica != "" { + // support multiple replica hosts separated by commas + replicas := strings.SplitSeq(Database.HostReplica, ",") + for r := range replicas { + trimmed := strings.TrimSpace(r) + if trimmed == "" { + continue + } + dsn, err := dbConnStrWithHost(trimmed) + if err != nil { + return nil, err + } + dsns = append(dsns, dsn) + } + } + // Fall back to master if no slave DSN was provided. + if len(dsns) == 0 { + master, err := DBMasterConnStr() + if err != nil { + return nil, err + } + log.Debug("Database: No dedicated replica host defined; falling back to primary DSN for replica connections") + dsns = append(dsns, master) + } + return dsns, nil +} + +func BuildLoadBalancePolicy(settings *DatabaseSettings, slaveEngines []*xorm.Engine) xorm.GroupPolicy { + var policy xorm.GroupPolicy + switch settings.LoadBalancePolicy { // Use the settings parameter directly + case "WeightRandom": + var weights []int + if settings.LoadBalanceWeights != "" { // Use the settings parameter directly + for part := range strings.SplitSeq(settings.LoadBalanceWeights, ",") { + w, err := strconv.Atoi(strings.TrimSpace(part)) + if err != nil { + w = 1 // use a default weight if conversion fails + } + weights = append(weights, w) + } + } + // If no valid weights were provided, default each slave to weight 1 + if len(weights) == 0 { + weights = make([]int, len(slaveEngines)) + for i := range weights { + weights[i] = 1 + } + } + policy = xorm.WeightRandomPolicy(weights) + case "WeightRoundRobin": + var weights []int + if settings.LoadBalanceWeights != "" { + for part := range strings.SplitSeq(settings.LoadBalanceWeights, ",") { + w, err := strconv.Atoi(strings.TrimSpace(part)) + if err != nil { + w = 1 // use a default weight if conversion fails + } + weights = append(weights, w) + } + } + // If no valid weights were provided, default each slave to weight 1 + if len(weights) == 0 { + weights = make([]int, len(slaveEngines)) + for i := range weights { + weights[i] = 1 + } + } + policy = xorm.WeightRoundRobinPolicy(weights) + case "RoundRobin": + policy = xorm.RoundRobinPolicy() + case "LeastConn": + policy = xorm.LeastConnPolicy() + default: + policy = xorm.RandomPolicy() + } + return policy +} + +// dbConnStrWithHost constructs the connection string, given a host value. +func dbConnStrWithHost(host string) (string, error) { var connStr string paramSep := "?" if strings.Contains(Database.Name, paramSep) { @@ -109,23 +230,25 @@ func DBConnStr() (string, error) { switch Database.Type { case "mysql": connType := "tcp" - if len(Database.Host) > 0 && Database.Host[0] == '/' { // looks like a unix socket + // if the host starts with '/' it is assumed to be a unix socket path + if len(host) > 0 && host[0] == '/' { connType = "unix" } tls := Database.SSLMode - if tls == "disable" { // allow (Postgres-inspired) default value to work in MySQL + // allow the "disable" value (borrowed from Postgres defaults) to behave as false + if tls == "disable" { tls = "false" } connStr = fmt.Sprintf("%s:%s@%s(%s)/%s%sparseTime=true&tls=%s", - Database.User, Database.Passwd, connType, Database.Host, Database.Name, paramSep, tls) + Database.User, Database.Passwd, connType, host, Database.Name, paramSep, tls) case "postgres": - connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Database.SSLMode) + connStr = getPostgreSQLConnectionString(host, Database.User, Database.Passwd, Database.Name, Database.SSLMode) case "sqlite3": if !EnableSQLite3 { return "", errors.New("this Gitea binary was not built with SQLite3 support") } if err := os.MkdirAll(filepath.Dir(Database.Path), os.ModePerm); err != nil { - return "", fmt.Errorf("Failed to create directories: %w", err) + return "", fmt.Errorf("failed to create directories: %w", err) } journalMode := "" if Database.SQLiteJournalMode != "" { @@ -136,7 +259,6 @@ func DBConnStr() (string, error) { default: return "", fmt.Errorf("unknown database type: %s", Database.Type) } - return connStr, nil } diff --git a/modules/setting/database_test.go b/modules/setting/database_test.go index a742d54f8c..ce816d53e8 100644 --- a/modules/setting/database_test.go +++ b/modules/setting/database_test.go @@ -4,6 +4,7 @@ package setting import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -107,3 +108,104 @@ func Test_getPostgreSQLConnectionString(t *testing.T) { assert.Equal(t, test.Output, connStr) } } + +func getPostgreSQLEngineGroupConnectionStrings(primaryHost, replicaHosts, user, passwd, name, sslmode string) (string, []string) { + // Determine the primary connection string. + primary := primaryHost + if strings.TrimSpace(primary) == "" { + primary = "127.0.0.1:5432" + } + primaryConn := getPostgreSQLConnectionString(primary, user, passwd, name, sslmode) + + // Build the replica connection strings. + replicaConns := []string{} + if strings.TrimSpace(replicaHosts) != "" { + // Split comma-separated replica host values. + hosts := strings.Split(replicaHosts, ",") + for _, h := range hosts { + trimmed := strings.TrimSpace(h) + if trimmed != "" { + replicaConns = append(replicaConns, + getPostgreSQLConnectionString(trimmed, user, passwd, name, sslmode)) + } + } + } + + return primaryConn, replicaConns +} + +func Test_getPostgreSQLEngineGroupConnectionStrings(t *testing.T) { + tests := []struct { + primaryHost string // primary host setting (e.g. "localhost" or "[::1]:1234") + replicaHosts string // comma-separated replica hosts (e.g. "replica1,replica2:2345") + user string + passwd string + name string + sslmode string + outputPrimary string + outputReplicas []string + }{ + { + // No primary override (empty => default) and no replicas. + primaryHost: "", + replicaHosts: "", + user: "", + passwd: "", + name: "", + sslmode: "", + outputPrimary: "postgres://:@127.0.0.1:5432?sslmode=", + outputReplicas: []string{}, + }, + { + // Primary set and one replica. + primaryHost: "localhost", + replicaHosts: "replicahost", + user: "user", + passwd: "pass", + name: "gitea", + sslmode: "disable", + outputPrimary: "postgres://user:pass@localhost:5432/gitea?sslmode=disable", + outputReplicas: []string{"postgres://user:pass@replicahost:5432/gitea?sslmode=disable"}, + }, + { + // Primary with explicit port; multiple replicas (one without and one with an explicit port). + primaryHost: "localhost:5433", + replicaHosts: "replica1,replica2:5434", + user: "test", + passwd: "secret", + name: "db", + sslmode: "require", + outputPrimary: "postgres://test:secret@localhost:5433/db?sslmode=require", + outputReplicas: []string{ + "postgres://test:secret@replica1:5432/db?sslmode=require", + "postgres://test:secret@replica2:5434/db?sslmode=require", + }, + }, + { + // IPv6 addresses for primary and replica. + primaryHost: "[::1]:1234", + replicaHosts: "[::2]:2345", + user: "ipv6", + passwd: "ipv6pass", + name: "ipv6db", + sslmode: "disable", + outputPrimary: "postgres://ipv6:ipv6pass@[::1]:1234/ipv6db?sslmode=disable", + outputReplicas: []string{ + "postgres://ipv6:ipv6pass@[::2]:2345/ipv6db?sslmode=disable", + }, + }, + } + + for _, test := range tests { + primary, replicas := getPostgreSQLEngineGroupConnectionStrings( + test.primaryHost, + test.replicaHosts, + test.user, + test.passwd, + test.name, + test.sslmode, + ) + assert.Equal(t, test.outputPrimary, primary) + assert.Equal(t, test.outputReplicas, replicas) + } +} diff --git a/modules/setting/f3.go b/modules/setting/f3.go index 8669b70562..31d12294b8 100644 --- a/modules/setting/f3.go +++ b/modules/setting/f3.go @@ -3,7 +3,7 @@ package setting import ( - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // Friendly Forge Format (F3) settings diff --git a/modules/setting/federation.go b/modules/setting/federation.go index edb18e0054..510ac128ee 100644 --- a/modules/setting/federation.go +++ b/modules/setting/federation.go @@ -4,7 +4,7 @@ package setting import ( - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" "github.com/42wim/httpsig" ) @@ -15,18 +15,20 @@ var ( Enabled bool ShareUserStatistics bool MaxSize int64 - Algorithms []string + SignatureAlgorithms []string DigestAlgorithm string GetHeaders []string PostHeaders []string + SignatureEnforced bool }{ Enabled: false, ShareUserStatistics: true, MaxSize: 4, - Algorithms: []string{"rsa-sha256", "rsa-sha512", "ed25519"}, + SignatureAlgorithms: []string{"rsa-sha256", "rsa-sha512", "ed25519"}, DigestAlgorithm: "SHA-256", GetHeaders: []string{"(request-target)", "Date", "Host"}, PostHeaders: []string{"(request-target)", "Date", "Host", "Digest"}, + SignatureEnforced: true, } ) @@ -44,8 +46,8 @@ func loadFederationFrom(rootCfg ConfigProvider) { // Get MaxSize in bytes instead of MiB Federation.MaxSize = 1 << 20 * Federation.MaxSize - HttpsigAlgs = make([]httpsig.Algorithm, len(Federation.Algorithms)) - for i, alg := range Federation.Algorithms { + HttpsigAlgs = make([]httpsig.Algorithm, len(Federation.SignatureAlgorithms)) + for i, alg := range Federation.SignatureAlgorithms { HttpsigAlgs[i] = httpsig.Algorithm(alg) } } diff --git a/modules/setting/forgejo_storage_test.go b/modules/setting/forgejo_storage_test.go index d91bff59e9..42c46beb77 100644 --- a/modules/setting/forgejo_storage_test.go +++ b/modules/setting/forgejo_storage_test.go @@ -259,6 +259,6 @@ func testStoragePathMatch(t *testing.T, iniStr string, storageType StorageType, cfg, err := NewConfigProviderFromData(iniStr) require.NoError(t, err, iniStr) require.NoError(t, loadCommonSettingsFrom(cfg), iniStr) - assert.EqualValues(t, testSectionToPath(storageType, section), testStorageGetPath(*storage), iniStr) - assert.EqualValues(t, storageType, (*storage).Type, iniStr) + assert.Equal(t, testSectionToPath(storageType, section), testStorageGetPath(*storage), iniStr) + assert.Equal(t, storageType, (*storage).Type, iniStr) } diff --git a/modules/setting/git.go b/modules/setting/git.go index 812c4fe6c9..f35592a924 100644 --- a/modules/setting/git.go +++ b/modules/setting/git.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // Git settings diff --git a/modules/setting/git_test.go b/modules/setting/git_test.go index 34427f908f..5604151907 100644 --- a/modules/setting/git_test.go +++ b/modules/setting/git_test.go @@ -6,17 +6,15 @@ package setting import ( "testing" + "forgejo.org/modules/test" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGitConfig(t *testing.T) { - oldGit := Git - oldGitConfig := GitConfig - defer func() { - Git = oldGit - GitConfig = oldGitConfig - }() + defer test.MockProtect(&Git)() + defer test.MockProtect(&GitConfig)() cfg, err := NewConfigProviderFromData(` [git.config] @@ -24,8 +22,8 @@ a.b = 1 `) require.NoError(t, err) loadGitFrom(cfg) - assert.EqualValues(t, "1", GitConfig.Options["a.b"]) - assert.EqualValues(t, "histogram", GitConfig.Options["diff.algorithm"]) + assert.Equal(t, "1", GitConfig.Options["a.b"]) + assert.Equal(t, "histogram", GitConfig.Options["diff.algorithm"]) cfg, err = NewConfigProviderFromData(` [git.config] @@ -33,24 +31,20 @@ diff.algorithm = other `) require.NoError(t, err) loadGitFrom(cfg) - assert.EqualValues(t, "other", GitConfig.Options["diff.algorithm"]) + assert.Equal(t, "other", GitConfig.Options["diff.algorithm"]) } func TestGitReflog(t *testing.T) { - oldGit := Git - oldGitConfig := GitConfig - defer func() { - Git = oldGit - GitConfig = oldGitConfig - }() + defer test.MockProtect(&Git)() + defer test.MockProtect(&GitConfig)() // default reflog config without legacy options cfg, err := NewConfigProviderFromData(``) require.NoError(t, err) loadGitFrom(cfg) - assert.EqualValues(t, "true", GitConfig.GetOption("core.logAllRefUpdates")) - assert.EqualValues(t, "90", GitConfig.GetOption("gc.reflogExpire")) + assert.Equal(t, "true", GitConfig.GetOption("core.logAllRefUpdates")) + assert.Equal(t, "90", GitConfig.GetOption("gc.reflogExpire")) // custom reflog config by legacy options cfg, err = NewConfigProviderFromData(` @@ -61,6 +55,6 @@ EXPIRATION = 123 require.NoError(t, err) loadGitFrom(cfg) - assert.EqualValues(t, "false", GitConfig.GetOption("core.logAllRefUpdates")) - assert.EqualValues(t, "123", GitConfig.GetOption("gc.reflogExpire")) + assert.Equal(t, "false", GitConfig.GetOption("core.logAllRefUpdates")) + assert.Equal(t, "123", GitConfig.GetOption("gc.reflogExpire")) } diff --git a/modules/setting/i18n.go b/modules/setting/i18n.go index c2e5822cf0..a400cf844c 100644 --- a/modules/setting/i18n.go +++ b/modules/setting/i18n.go @@ -9,7 +9,7 @@ var defaultI18nLangNames = []string{ "zh-CN", "简体中文", "zh-HK", "繁體中文(香港)", "zh-TW", "繁體中文(台灣)", - "da", "Danish", + "da", "Dansk", "de-DE", "Deutsch", "nds", "Plattdüütsch", "fr-FR", "Français", diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index 287e72941c..e592220de6 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -4,11 +4,12 @@ package setting import ( + "errors" "fmt" "net/mail" "strings" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) var IncomingEmail = struct { @@ -68,7 +69,7 @@ func checkReplyToAddress() error { } if parsed.Name != "" { - return fmt.Errorf("name must not be set") + return errors.New("name must not be set") } c := strings.Count(IncomingEmail.ReplyToAddress, IncomingEmail.TokenPlaceholder) diff --git a/modules/setting/incoming_email_test.go b/modules/setting/incoming_email_test.go index 0fdd44d333..6d181cae3c 100644 --- a/modules/setting/incoming_email_test.go +++ b/modules/setting/incoming_email_test.go @@ -31,8 +31,8 @@ func Test_loadIncomingEmailFrom(t *testing.T) { loadIncomingEmailFrom(cfg) - assert.EqualValues(t, "jane.doe@example.com", IncomingEmail.Username) - assert.EqualValues(t, "y0u'll n3v3r gUess th1S!!1", IncomingEmail.Password) + assert.Equal(t, "jane.doe@example.com", IncomingEmail.Username) + assert.Equal(t, "y0u'll n3v3r gUess th1S!!1", IncomingEmail.Password) }) t.Run("Port settings", func(t *testing.T) { @@ -45,7 +45,7 @@ func Test_loadIncomingEmailFrom(t *testing.T) { loadIncomingEmailFrom(cfg) - assert.EqualValues(t, 143, IncomingEmail.Port) + assert.Equal(t, 143, IncomingEmail.Port) }) t.Run("no port, with tls", func(t *testing.T) { @@ -56,7 +56,7 @@ func Test_loadIncomingEmailFrom(t *testing.T) { loadIncomingEmailFrom(cfg) - assert.EqualValues(t, 993, IncomingEmail.Port) + assert.Equal(t, 993, IncomingEmail.Port) }) t.Run("port overrides tls", func(t *testing.T) { @@ -68,7 +68,7 @@ func Test_loadIncomingEmailFrom(t *testing.T) { loadIncomingEmailFrom(cfg) - assert.EqualValues(t, 1993, IncomingEmail.Port) + assert.Equal(t, 1993, IncomingEmail.Port) }) }) } diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go index 4c4f63bc61..6a464ee0de 100644 --- a/modules/setting/indexer.go +++ b/modules/setting/indexer.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" "github.com/gobwas/glob" ) diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go index f55b0cdae9..452bfae737 100644 --- a/modules/setting/lfs.go +++ b/modules/setting/lfs.go @@ -7,7 +7,7 @@ import ( "fmt" "time" - "code.gitea.io/gitea/modules/generate" + "forgejo.org/modules/generate" ) // LFS represents the server-side configuration for Git LFS. @@ -80,10 +80,7 @@ func loadLFSFrom(rootCfg ConfigProvider) error { jwtSecretBase64 := loadSecret(rootCfg.Section("server"), "LFS_JWT_SECRET_URI", "LFS_JWT_SECRET") LFS.JWTSecretBytes, err = generate.DecodeJwtSecret(jwtSecretBase64) if err != nil { - LFS.JWTSecretBytes, jwtSecretBase64, err = generate.NewJwtSecret() - if err != nil { - return fmt.Errorf("error generating JWT Secret for custom config: %v", err) - } + LFS.JWTSecretBytes, jwtSecretBase64 = generate.NewJwtSecret() // Save secret saveCfg, err := rootCfg.PrepareSaving() diff --git a/modules/setting/lfs_test.go b/modules/setting/lfs_test.go index 2b204282a8..0abf401fa0 100644 --- a/modules/setting/lfs_test.go +++ b/modules/setting/lfs_test.go @@ -20,7 +20,7 @@ func Test_getStorageInheritNameSectionTypeForLFS(t *testing.T) { require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) iniStr = ` [server] @@ -55,7 +55,7 @@ STORAGE_TYPE = minio require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) iniStr = ` [lfs] @@ -69,7 +69,7 @@ STORAGE_TYPE = minio require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) iniStr = ` [lfs] @@ -84,7 +84,7 @@ STORAGE_TYPE = minio require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "my_lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "my_lfs/", LFS.Storage.MinioConfig.BasePath) } func Test_LFSStorage1(t *testing.T) { @@ -97,8 +97,8 @@ STORAGE_TYPE = minio require.NoError(t, loadLFSFrom(cfg)) assert.EqualValues(t, "minio", LFS.Storage.Type) - assert.EqualValues(t, "gitea", LFS.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", LFS.Storage.MinioConfig.Bucket) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) } func Test_LFSClientServerConfigs(t *testing.T) { @@ -113,9 +113,9 @@ BATCH_SIZE = 0 assert.NoError(t, err) assert.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, 100, LFS.MaxBatchSize) - assert.EqualValues(t, 20, LFSClient.BatchSize) - assert.EqualValues(t, 8, LFSClient.BatchOperationConcurrency) + assert.Equal(t, 100, LFS.MaxBatchSize) + assert.Equal(t, 20, LFSClient.BatchSize) + assert.Equal(t, 8, LFSClient.BatchOperationConcurrency) iniStr = ` [lfs_client] @@ -126,6 +126,6 @@ BATCH_OPERATION_CONCURRENCY = 10 assert.NoError(t, err) assert.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, 50, LFSClient.BatchSize) - assert.EqualValues(t, 10, LFSClient.BatchOperationConcurrency) + assert.Equal(t, 50, LFSClient.BatchSize) + assert.Equal(t, 10, LFSClient.BatchOperationConcurrency) } diff --git a/modules/setting/log.go b/modules/setting/log.go index a141188c0c..0747ac4dac 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -11,8 +11,8 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/util" ) type LogGlobalConfig struct { diff --git a/modules/setting/log_test.go b/modules/setting/log_test.go index 3134d3e75c..eda6dc36af 100644 --- a/modules/setting/log_test.go +++ b/modules/setting/log_test.go @@ -8,8 +8,8 @@ import ( "strings" "testing" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/json" + "forgejo.org/modules/log" "github.com/stretchr/testify/require" ) diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 0804fbd717..9c004c6ce0 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -11,7 +11,7 @@ import ( "text/template" "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" shellquote "github.com/kballard/go-shellquote" ) @@ -215,6 +215,11 @@ func loadMailerFrom(rootCfg ConfigProvider) { if err != nil { log.Error("Failed to parse Sendmail args: '%s' with error %v", sec.Key("SENDMAIL_ARGS").String(), err) } + + if len(MailService.SendmailArgs) == 0 || MailService.SendmailArgs[len(MailService.SendmailArgs)-1] != "--" { + log.Warn("SENDMAIL_ARGS setting does not end in \"--\", appending it to prevent argument injection") + MailService.SendmailArgs = append(MailService.SendmailArgs, "--") + } case "smtp", "smtps", "smtp+starttls", "smtp+unix": ips := tryResolveAddr(MailService.SMTPAddr) if MailService.Protocol == "smtp" { diff --git a/modules/setting/mailer_test.go b/modules/setting/mailer_test.go index f8af4a78c1..4523cc91dd 100644 --- a/modules/setting/mailer_test.go +++ b/modules/setting/mailer_test.go @@ -34,8 +34,8 @@ func Test_loadMailerFrom(t *testing.T) { // Check mailer setting loadMailerFrom(cfg) - assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr) - assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort) + assert.Equal(t, kase.SMTPAddr, MailService.SMTPAddr) + assert.Equal(t, kase.SMTPPort, MailService.SMTPPort) }) } @@ -48,7 +48,31 @@ func Test_loadMailerFrom(t *testing.T) { loadMailerFrom(cfg) - assert.EqualValues(t, "jane.doe@example.com", MailService.User) - assert.EqualValues(t, "y0u'll n3v3r gUess th1S!!1", MailService.Passwd) + assert.Equal(t, "jane.doe@example.com", MailService.User) + assert.Equal(t, "y0u'll n3v3r gUess th1S!!1", MailService.Passwd) + }) + + t.Run("sendmail argument sanitization", func(t *testing.T) { + cfg, _ := NewConfigProviderFromData("") + sec := cfg.Section("mailer") + sec.NewKey("ENABLED", "true") + sec.NewKey("PROTOCOL", "sendmail") + sec.NewKey("SENDMAIL_ARGS", "-B 8BITMIME") + + loadMailerFrom(cfg) + + assert.Equal(t, []string{"-B", "8BITMIME", "--"}, MailService.SendmailArgs) + }) + + t.Run("empty sendmail args", func(t *testing.T) { + cfg, _ := NewConfigProviderFromData("") + sec := cfg.Section("mailer") + sec.NewKey("ENABLED", "true") + sec.NewKey("PROTOCOL", "sendmail") + sec.NewKey("SENDMAIL_ARGS", "") + + loadMailerFrom(cfg) + + assert.Equal(t, []string{"--"}, MailService.SendmailArgs) }) } diff --git a/modules/setting/markup.go b/modules/setting/markup.go index e893c1c2f1..4ab9e7b2d1 100644 --- a/modules/setting/markup.go +++ b/modules/setting/markup.go @@ -7,7 +7,7 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // ExternalMarkupRenderers represents the external markup renderers @@ -62,7 +62,7 @@ type MarkupSanitizerRule struct { func loadMarkupFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "markdown", &Markdown) - MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) + MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(50000) FilePreviewMaxLines = rootCfg.Section("markup").Key("FILEPREVIEW_MAX_LINES").MustInt(50) ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10) ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10) diff --git a/modules/setting/mirror.go b/modules/setting/mirror.go index 3aa530a1f4..58c57c5c95 100644 --- a/modules/setting/mirror.go +++ b/modules/setting/mirror.go @@ -6,7 +6,7 @@ package setting import ( "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // Mirror settings diff --git a/modules/setting/moderation.go b/modules/setting/moderation.go new file mode 100644 index 0000000000..5f35a284d6 --- /dev/null +++ b/modules/setting/moderation.go @@ -0,0 +1,15 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package setting + +// Moderation settings +var Moderation = struct { + Enabled bool `ini:"ENABLED"` +}{ + Enabled: false, +} + +func loadModerationFrom(rootCfg ConfigProvider) { + mustMapSetting(rootCfg, "moderation", &Moderation) +} diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go index 49288e2639..9e95e1c6a9 100644 --- a/modules/setting/oauth2.go +++ b/modules/setting/oauth2.go @@ -8,8 +8,8 @@ import ( "path/filepath" "sync/atomic" - "code.gitea.io/gitea/modules/generate" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/generate" + "forgejo.org/modules/log" ) // OAuth2UsernameType is enum describing the way gitea 'name' should be generated from oauth2 data @@ -138,10 +138,7 @@ func loadOAuth2From(rootCfg ConfigProvider) { if InstallLock { jwtSecretBytes, err := generate.DecodeJwtSecret(jwtSecretBase64) if err != nil { - jwtSecretBytes, jwtSecretBase64, err = generate.NewJwtSecret() - if err != nil { - log.Fatal("error generating JWT secret: %v", err) - } + jwtSecretBytes, jwtSecretBase64 = generate.NewJwtSecret() saveCfg, err := rootCfg.PrepareSaving() if err != nil { log.Fatal("save oauth2.JWT_SECRET failed: %v", err) @@ -161,10 +158,7 @@ var generalSigningSecret atomic.Pointer[[]byte] func GetGeneralTokenSigningSecret() []byte { old := generalSigningSecret.Load() if old == nil || len(*old) == 0 { - jwtSecret, _, err := generate.NewJwtSecret() - if err != nil { - log.Fatal("Unable to generate general JWT secret: %v", err) - } + jwtSecret, _ := generate.NewJwtSecret() if generalSigningSecret.CompareAndSwap(old, &jwtSecret) { return jwtSecret } diff --git a/modules/setting/oauth2_test.go b/modules/setting/oauth2_test.go index 18252b2447..7a1f4842a4 100644 --- a/modules/setting/oauth2_test.go +++ b/modules/setting/oauth2_test.go @@ -7,8 +7,8 @@ import ( "os" "testing" - "code.gitea.io/gitea/modules/generate" - "code.gitea.io/gitea/modules/test" + "forgejo.org/modules/generate" + "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,7 +32,7 @@ JWT_SECRET = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB actual := GetGeneralTokenSigningSecret() expected, _ := generate.DecodeJwtSecret("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB") assert.Len(t, actual, 32) - assert.EqualValues(t, expected, actual) + assert.Equal(t, expected, actual) } func TestGetGeneralSigningSecretSave(t *testing.T) { diff --git a/modules/setting/other.go b/modules/setting/other.go index 4ba494765b..db60cd2205 100644 --- a/modules/setting/other.go +++ b/modules/setting/other.go @@ -3,7 +3,7 @@ package setting -import "code.gitea.io/gitea/modules/log" +import "forgejo.org/modules/log" type OtherConfig struct { ShowFooterVersion bool diff --git a/modules/setting/packages_test.go b/modules/setting/packages_test.go index 78eb4b4bbc..85a4656da0 100644 --- a/modules/setting/packages_test.go +++ b/modules/setting/packages_test.go @@ -42,7 +42,7 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath) // we can also configure packages storage directly iniStr = ` @@ -54,7 +54,7 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath) // or we can indicate the storage type in the packages section iniStr = ` @@ -69,7 +69,7 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath) // or we can indicate the storage type and minio base path in the packages section iniStr = ` @@ -85,7 +85,7 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "my_packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "my_packages/", Packages.Storage.MinioConfig.BasePath) } func Test_PackageStorage1(t *testing.T) { @@ -110,8 +110,8 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Packages.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) - assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "packages/", storage.MinioConfig.BasePath) assert.True(t, storage.MinioConfig.ServeDirect) } @@ -137,8 +137,8 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Packages.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) - assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "packages/", storage.MinioConfig.BasePath) assert.True(t, storage.MinioConfig.ServeDirect) } @@ -165,8 +165,8 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Packages.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) - assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "my_packages/", storage.MinioConfig.BasePath) assert.True(t, storage.MinioConfig.ServeDirect) } @@ -193,7 +193,7 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := Packages.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) - assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "my_packages/", storage.MinioConfig.BasePath) assert.True(t, storage.MinioConfig.ServeDirect) } diff --git a/modules/setting/path.go b/modules/setting/path.go index b99f1977bb..33f27db8fd 100644 --- a/modules/setting/path.go +++ b/modules/setting/path.go @@ -10,7 +10,7 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) var ( diff --git a/modules/setting/proxy.go b/modules/setting/proxy.go index 4ff420d090..7a9de9568b 100644 --- a/modules/setting/proxy.go +++ b/modules/setting/proxy.go @@ -6,7 +6,7 @@ package setting import ( "net/url" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // Proxy settings diff --git a/modules/setting/queue.go b/modules/setting/queue.go index 251a6c1e30..06d007c140 100644 --- a/modules/setting/queue.go +++ b/modules/setting/queue.go @@ -7,8 +7,8 @@ import ( "path/filepath" "runtime" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/json" + "forgejo.org/modules/log" ) // QueueSettings represent the settings for a queue from the ini diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 93c01cb8fa..7e774f0139 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -1,15 +1,19 @@ // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package setting import ( + "os" "os/exec" "path" "path/filepath" "strings" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" + + "golang.org/x/crypto/ssh" ) // enumerates all the policy repository creating @@ -26,6 +30,8 @@ var MaxUserCardsPerPage = 36 // MaxForksPerPage sets maximum amount of forks shown per page var MaxForksPerPage = 40 +var SSHInstanceKey ssh.PublicKey + // Repository settings var ( Repository = struct { @@ -47,6 +53,7 @@ var ( DisabledRepoUnits []string DefaultRepoUnits []string DefaultForkRepoUnits []string + DefaultMirrorRepoUnits []string PrefixArchiveFiles bool DisableMigrations bool DisableStars bool @@ -90,7 +97,6 @@ var ( DefaultUpdateStyle string PopulateSquashCommentWithCommitMessages bool AddCoCommitterTrailers bool - TestConflictingPatchesWithGitApply bool RetargetChildrenOnMerge bool } `ini:"repository.pull-request"` @@ -109,6 +115,7 @@ var ( SigningKey string SigningName string SigningEmail string + Format string InitialCommit []string CRUDActions []string `ini:"CRUD_ACTIONS"` Merges []string @@ -170,6 +177,7 @@ var ( DisabledRepoUnits: []string{}, DefaultRepoUnits: []string{}, DefaultForkRepoUnits: []string{}, + DefaultMirrorRepoUnits: []string{}, PrefixArchiveFiles: true, DisableMigrations: false, DisableStars: false, @@ -220,7 +228,6 @@ var ( DefaultUpdateStyle string PopulateSquashCommentWithCommitMessages bool AddCoCommitterTrailers bool - TestConflictingPatchesWithGitApply bool RetargetChildrenOnMerge bool }{ WorkInProgressPrefixes: []string{"WIP:", "[WIP]"}, @@ -262,6 +269,7 @@ var ( SigningKey string SigningName string SigningEmail string + Format string InitialCommit []string CRUDActions []string `ini:"CRUD_ACTIONS"` Merges []string @@ -271,6 +279,7 @@ var ( SigningKey: "default", SigningName: "", SigningEmail: "", + Format: "openpgp", InitialCommit: []string{"always"}, CRUDActions: []string{"pubkey", "twofa", "parentsigned"}, Merges: []string{"pubkey", "twofa", "basesigned", "commitssigned"}, @@ -376,4 +385,15 @@ func loadRepositoryFrom(rootCfg ConfigProvider) { log.Fatal("loadRepoArchiveFrom: %v", err) } Repository.EnableFlags = sec.Key("ENABLE_FLAGS").MustBool() + + if Repository.Signing.Format == "ssh" && Repository.Signing.SigningKey != "none" && Repository.Signing.SigningKey != "" { + sshPublicKey, err := os.ReadFile(Repository.Signing.SigningKey) + if err != nil { + log.Fatal("Could not read repository signing key in %q: %v", Repository.Signing.SigningKey, err) + } + SSHInstanceKey, _, _, _, err = ssh.ParseAuthorizedKey(sshPublicKey) + if err != nil { + log.Fatal("Could not parse the SSH signing key %q: %v", sshPublicKey, err) + } + } } diff --git a/modules/setting/repository_archive_test.go b/modules/setting/repository_archive_test.go index d3901b6e47..cff59f3663 100644 --- a/modules/setting/repository_archive_test.go +++ b/modules/setting/repository_archive_test.go @@ -21,7 +21,7 @@ STORAGE_TYPE = minio require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) // we can also configure packages storage directly iniStr = ` @@ -33,7 +33,7 @@ STORAGE_TYPE = minio require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) // or we can indicate the storage type in the packages section iniStr = ` @@ -48,7 +48,7 @@ STORAGE_TYPE = minio require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) // or we can indicate the storage type and minio base path in the packages section iniStr = ` @@ -64,7 +64,7 @@ STORAGE_TYPE = minio require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "my_archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "my_archive/", RepoArchive.Storage.MinioConfig.BasePath) } func Test_RepoArchiveStorage(t *testing.T) { @@ -86,7 +86,7 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage := RepoArchive.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) iniStr = ` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -108,5 +108,5 @@ MINIO_SECRET_ACCESS_KEY = correct_key storage = RepoArchive.Storage assert.EqualValues(t, "minio", storage.Type) - assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) + assert.Equal(t, "gitea", storage.MinioConfig.Bucket) } diff --git a/modules/setting/repository_test.go b/modules/setting/repository_test.go new file mode 100644 index 0000000000..d36e739f6b --- /dev/null +++ b/modules/setting/repository_test.go @@ -0,0 +1,59 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package setting + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/crypto/ssh" +) + +func TestSSHInstanceKey(t *testing.T) { + sshSigningKeyPath, err := filepath.Abs("../../tests/integration/ssh-signing-key.pub") + require.NoError(t, err) + + t.Run("None value", func(t *testing.T) { + cfg, err := NewConfigProviderFromData(` +[repository.signing] +FORMAT = ssh +SIGNING_KEY = none +`) + require.NoError(t, err) + + loadRepositoryFrom(cfg) + + assert.Nil(t, SSHInstanceKey) + }) + + t.Run("No value", func(t *testing.T) { + cfg, err := NewConfigProviderFromData(` +[repository.signing] +FORMAT = ssh +`) + require.NoError(t, err) + + loadRepositoryFrom(cfg) + + assert.Nil(t, SSHInstanceKey) + }) + t.Run("Normal", func(t *testing.T) { + iniStr := fmt.Sprintf(` +[repository.signing] +FORMAT = ssh +SIGNING_KEY = %s +`, sshSigningKeyPath) + cfg, err := NewConfigProviderFromData(iniStr) + require.NoError(t, err) + + loadRepositoryFrom(cfg) + + assert.NotNil(t, SSHInstanceKey) + assert.Equal(t, "ssh-ed25519", SSHInstanceKey.Type()) + assert.EqualValues(t, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFeRC8GfFyXtiy0f1E7hLv77BXW7e68tFvIcs8/29YqH\n", ssh.MarshalAuthorizedKey(SSHInstanceKey)) + }) +} diff --git a/modules/setting/security.go b/modules/setting/security.go index 678a57cb30..f3480d1056 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -8,10 +8,10 @@ import ( "os" "strings" - "code.gitea.io/gitea/modules/auth/password/hash" - "code.gitea.io/gitea/modules/generate" - "code.gitea.io/gitea/modules/keying" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/auth/password/hash" + "forgejo.org/modules/generate" + "forgejo.org/modules/keying" + "forgejo.org/modules/log" ) var ( diff --git a/modules/setting/server.go b/modules/setting/server.go index c874a58069..3ff91d2cde 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -13,9 +13,11 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/util" + + "github.com/caddyserver/certmagic" ) // Scheme describes protocol types @@ -206,7 +208,7 @@ func loadServerFrom(rootCfg ConfigProvider) { EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) } if EnableAcme { - AcmeURL = sec.Key("ACME_URL").MustString("") + AcmeURL = sec.Key("ACME_URL").MustString(certmagic.LetsEncryptProductionCA) AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("") if sec.HasKey("ACME_ACCEPTTOS") { diff --git a/modules/setting/server_test.go b/modules/setting/server_test.go index 4b95961a26..3c6faa2311 100644 --- a/modules/setting/server_test.go +++ b/modules/setting/server_test.go @@ -6,7 +6,7 @@ package setting import ( "testing" - "code.gitea.io/gitea/modules/test" + "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -84,5 +84,5 @@ func TestUnixSocketAbstractNamespace(t *testing.T) { require.NoError(t, err) loadServerFrom(cfg) - assert.EqualValues(t, "@forgejo", HTTPAddr) + assert.Equal(t, "@forgejo", HTTPAddr) } diff --git a/modules/setting/service.go b/modules/setting/service.go index cc84ac7257..5717225578 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -11,8 +11,8 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/structs" + "forgejo.org/modules/log" + "forgejo.org/modules/structs" "github.com/gobwas/glob" ) @@ -206,7 +206,7 @@ func loadServiceFrom(rootCfg ConfigProvider) { } Service.EmailDomainBlockList = append(Service.EmailDomainBlockList, toAdd...) } - Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration)) + Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!Service.DisableRegistration && !Service.AllowOnlyExternalRegistration) Service.EnableInternalSignIn = sec.Key("ENABLE_INTERNAL_SIGNIN").MustBool(true) Service.ShowMilestonesDashboardPage = sec.Key("SHOW_MILESTONES_DASHBOARD_PAGE").MustBool(true) Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool() diff --git a/modules/setting/service_test.go b/modules/setting/service_test.go index e504bc68dc..4fc09021b6 100644 --- a/modules/setting/service_test.go +++ b/modules/setting/service_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - "code.gitea.io/gitea/modules/structs" + "forgejo.org/modules/structs" "github.com/gobwas/glob" "github.com/stretchr/testify/assert" diff --git a/modules/setting/session.go b/modules/setting/session.go index 29ee67914d..e9ff9bf0bc 100644 --- a/modules/setting/session.go +++ b/modules/setting/session.go @@ -9,8 +9,8 @@ import ( "path/filepath" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/json" + "forgejo.org/modules/log" ) // SessionConfig defines Session settings diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 487f2bb0d5..1349325f81 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -11,9 +11,9 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/user" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/optional" + "forgejo.org/modules/user" ) var ForgejoVersion = "1.0.0" @@ -148,6 +148,7 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error { loadCamoFrom(cfg) loadI18nFrom(cfg) loadGitFrom(cfg) + loadAnnexFrom(cfg) loadMirrorFrom(cfg) loadMarkupFrom(cfg) loadQuotaFrom(cfg) @@ -162,7 +163,7 @@ func loadRunModeFrom(rootCfg ConfigProvider) { // The following is a purposefully undocumented option. Please do not run Forgejo as root. It will only cause future headaches. // Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly. unsafeAllowRunAsRoot := ConfigSectionKeyBool(rootSec, "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT") - unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || util.OptionalBoolParse(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value() + unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || optional.ParseBool(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value() RunMode = os.Getenv("GITEA_RUN_MODE") if RunMode == "" { RunMode = rootSec.Key("RUN_MODE").MustString("prod") @@ -221,6 +222,7 @@ func LoadSettings() { loadProjectFrom(CfgProvider) loadMimeTypeMapFrom(CfgProvider) loadF3From(CfgProvider) + loadModerationFrom(CfgProvider) } // LoadSettingsForInstall initializes the settings for install diff --git a/modules/setting/setting_test.go b/modules/setting/setting_test.go index 6801844729..1fef9e068a 100644 --- a/modules/setting/setting_test.go +++ b/modules/setting/setting_test.go @@ -7,7 +7,7 @@ package setting import ( "testing" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/modules/setting/ssh.go b/modules/setting/ssh.go index ea387e521f..1ad826a17a 100644 --- a/modules/setting/ssh.go +++ b/modules/setting/ssh.go @@ -11,8 +11,8 @@ import ( "text/template" "time" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/util" gossh "golang.org/x/crypto/ssh" ) @@ -56,7 +56,7 @@ var SSH = struct { Domain: "", Port: 22, ServerCiphers: []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"}, - ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"}, + ServerKeyExchanges: []string{"mlkem768x25519-sha256", "curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"}, ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, KeygenPath: "", MinimumKeySizeCheck: true, diff --git a/modules/setting/storage.go b/modules/setting/storage.go index 8ee5c0f0ab..532842064c 100644 --- a/modules/setting/storage.go +++ b/modules/setting/storage.go @@ -170,8 +170,8 @@ func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec Confi targetSec, _ := rootCfg.GetSection(storageSectionName + "." + name) if targetSec != nil { targetType := targetSec.Key("STORAGE_TYPE").String() - switch { - case targetType == "": + switch targetType { + case "": if targetSec.Key("PATH").String() == "" { // both storage type and path are empty, use default return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil } diff --git a/modules/setting/storage_test.go b/modules/setting/storage_test.go index 59135b5911..a1b687ba5b 100644 --- a/modules/setting/storage_test.go +++ b/modules/setting/storage_test.go @@ -27,16 +27,16 @@ MINIO_BUCKET = gitea-storage require.NoError(t, err) require.NoError(t, loadAttachmentFrom(cfg)) - assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) require.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) require.NoError(t, loadAvatarsFrom(cfg)) - assert.EqualValues(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "avatars/", Avatar.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket) + assert.Equal(t, "avatars/", Avatar.Storage.MinioConfig.BasePath) } func Test_getStorageUseOtherNameAsType(t *testing.T) { @@ -52,12 +52,12 @@ MINIO_BUCKET = gitea-storage require.NoError(t, err) require.NoError(t, loadAttachmentFrom(cfg)) - assert.EqualValues(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket) + assert.Equal(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) require.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket) + assert.Equal(t, "lfs/", LFS.Storage.MinioConfig.BasePath) } func Test_getStorageInheritStorageType(t *testing.T) { @@ -70,32 +70,32 @@ STORAGE_TYPE = minio require.NoError(t, loadPackagesFrom(cfg)) assert.EqualValues(t, "minio", Packages.Storage.Type) - assert.EqualValues(t, "gitea", Packages.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Packages.Storage.MinioConfig.Bucket) + assert.Equal(t, "packages/", Packages.Storage.MinioConfig.BasePath) require.NoError(t, loadRepoArchiveFrom(cfg)) assert.EqualValues(t, "minio", RepoArchive.Storage.Type) - assert.EqualValues(t, "gitea", RepoArchive.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", RepoArchive.Storage.MinioConfig.Bucket) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) require.NoError(t, loadActionsFrom(cfg)) assert.EqualValues(t, "minio", Actions.LogStorage.Type) - assert.EqualValues(t, "gitea", Actions.LogStorage.MinioConfig.Bucket) - assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Actions.LogStorage.MinioConfig.Bucket) + assert.Equal(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) - assert.EqualValues(t, "gitea", Actions.ArtifactStorage.MinioConfig.Bucket) - assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Actions.ArtifactStorage.MinioConfig.Bucket) + assert.Equal(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) require.NoError(t, loadAvatarsFrom(cfg)) assert.EqualValues(t, "minio", Avatar.Storage.Type) - assert.EqualValues(t, "gitea", Avatar.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "avatars/", Avatar.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", Avatar.Storage.MinioConfig.Bucket) + assert.Equal(t, "avatars/", Avatar.Storage.MinioConfig.BasePath) require.NoError(t, loadRepoAvatarFrom(cfg)) assert.EqualValues(t, "minio", RepoAvatar.Storage.Type) - assert.EqualValues(t, "gitea", RepoAvatar.Storage.MinioConfig.Bucket) - assert.EqualValues(t, "repo-avatars/", RepoAvatar.Storage.MinioConfig.BasePath) + assert.Equal(t, "gitea", RepoAvatar.Storage.MinioConfig.Bucket) + assert.Equal(t, "repo-avatars/", RepoAvatar.Storage.MinioConfig.BasePath) } type testLocalStoragePathCase struct { @@ -114,7 +114,7 @@ func testLocalStoragePath(t *testing.T, appDataPath, iniStr string, cases []test assert.EqualValues(t, "local", storage.Type) assert.True(t, filepath.IsAbs(storage.Path)) - assert.EqualValues(t, filepath.Clean(c.expectedPath), filepath.Clean(storage.Path)) + assert.Equal(t, filepath.Clean(c.expectedPath), filepath.Clean(storage.Path)) } } @@ -352,8 +352,8 @@ MINIO_SECRET_ACCESS_KEY = my_secret_key require.NoError(t, loadRepoArchiveFrom(cfg)) cp := RepoArchive.Storage.ToShadowCopy() - assert.EqualValues(t, "******", cp.MinioConfig.AccessKeyID) - assert.EqualValues(t, "******", cp.MinioConfig.SecretAccessKey) + assert.Equal(t, "******", cp.MinioConfig.AccessKeyID) + assert.Equal(t, "******", cp.MinioConfig.SecretAccessKey) } func Test_getStorageConfiguration24(t *testing.T) { @@ -408,10 +408,10 @@ MINIO_USE_SSL = true `) require.NoError(t, err) require.NoError(t, loadRepoArchiveFrom(cfg)) - assert.EqualValues(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID) - assert.EqualValues(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey) + assert.Equal(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID) + assert.Equal(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey) assert.True(t, RepoArchive.Storage.MinioConfig.UseSSL) - assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) } func Test_getStorageConfiguration28(t *testing.T) { @@ -425,10 +425,10 @@ MINIO_BASE_PATH = /prefix `) require.NoError(t, err) require.NoError(t, loadRepoArchiveFrom(cfg)) - assert.EqualValues(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID) - assert.EqualValues(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey) + assert.Equal(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID) + assert.Equal(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey) assert.True(t, RepoArchive.Storage.MinioConfig.UseSSL) - assert.EqualValues(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) + assert.Equal(t, "/prefix/repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) cfg, err = NewConfigProviderFromData(` [storage] @@ -443,10 +443,10 @@ MINIO_BASE_PATH = /lfs `) require.NoError(t, err) require.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) - assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) + assert.Equal(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) + assert.Equal(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) assert.True(t, LFS.Storage.MinioConfig.UseSSL) - assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "/lfs", LFS.Storage.MinioConfig.BasePath) cfg, err = NewConfigProviderFromData(` [storage] @@ -461,8 +461,8 @@ MINIO_BASE_PATH = /lfs `) require.NoError(t, err) require.NoError(t, loadLFSFrom(cfg)) - assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) - assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) + assert.Equal(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) + assert.Equal(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) assert.True(t, LFS.Storage.MinioConfig.UseSSL) - assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath) + assert.Equal(t, "/lfs", LFS.Storage.MinioConfig.BasePath) } diff --git a/modules/setting/time.go b/modules/setting/time.go index 97988211a9..1211fd475a 100644 --- a/modules/setting/time.go +++ b/modules/setting/time.go @@ -6,7 +6,7 @@ package setting import ( "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // DefaultUILocation is the location on the UI, so that we can display the time on UI. diff --git a/modules/setting/ui.go b/modules/setting/ui.go index 40f1812839..2e6a3df4c6 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -6,8 +6,8 @@ package setting import ( "time" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/container" + "forgejo.org/modules/log" ) // UI settings diff --git a/modules/setting/webhook.go b/modules/setting/webhook.go index dd60acf210..071b729aa1 100644 --- a/modules/setting/webhook.go +++ b/modules/setting/webhook.go @@ -6,7 +6,7 @@ package setting import ( "net/url" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) // Webhook settings diff --git a/modules/ssh/init.go b/modules/ssh/init.go index 21d4f89936..1ccd95b18b 100644 --- a/modules/ssh/init.go +++ b/modules/ssh/init.go @@ -11,8 +11,8 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" ) func Init() error { diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index 6ee10f718b..19cac0b603 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -21,12 +21,12 @@ import ( "sync" "syscall" - asymkey_model "code.gitea.io/gitea/models/asymkey" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/process" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + asymkey_model "forgejo.org/models/asymkey" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/process" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "github.com/gliderlabs/ssh" gossh "golang.org/x/crypto/ssh" diff --git a/modules/ssh/ssh_graceful.go b/modules/ssh/ssh_graceful.go index cad2c685bd..825313ab1c 100644 --- a/modules/ssh/ssh_graceful.go +++ b/modules/ssh/ssh_graceful.go @@ -4,9 +4,9 @@ package ssh import ( - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" "github.com/gliderlabs/ssh" ) diff --git a/modules/storage/local.go b/modules/storage/local.go index 00c7f668aa..6f851983b1 100644 --- a/modules/storage/local.go +++ b/modules/storage/local.go @@ -11,9 +11,9 @@ import ( "os" "path/filepath" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" ) var _ ObjectStorage = &LocalStorage{} diff --git a/modules/storage/local_test.go b/modules/storage/local_test.go index e230323f67..d74c6bbc41 100644 --- a/modules/storage/local_test.go +++ b/modules/storage/local_test.go @@ -8,7 +8,7 @@ import ( "path/filepath" "testing" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "github.com/stretchr/testify/assert" ) @@ -50,7 +50,7 @@ func TestBuildLocalPath(t *testing.T) { t.Run(k.path, func(t *testing.T) { l := LocalStorage{dir: k.localDir} - assert.EqualValues(t, k.expected, l.buildLocalPath(k.path)) + assert.Equal(t, k.expected, l.buildLocalPath(k.path)) }) } } diff --git a/modules/storage/minio.go b/modules/storage/minio.go index b02eec7aa0..bf51a1642a 100644 --- a/modules/storage/minio.go +++ b/modules/storage/minio.go @@ -15,9 +15,9 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" @@ -231,7 +231,7 @@ type minioFileInfo struct { } func (m minioFileInfo) Name() string { - return path.Base(m.ObjectInfo.Key) + return path.Base(m.Key) } func (m minioFileInfo) Size() int64 { @@ -243,7 +243,7 @@ func (m minioFileInfo) ModTime() time.Time { } func (m minioFileInfo) IsDir() bool { - return strings.HasSuffix(m.ObjectInfo.Key, "/") + return strings.HasSuffix(m.Key, "/") } func (m minioFileInfo) Mode() os.FileMode { diff --git a/modules/storage/minio_test.go b/modules/storage/minio_test.go index 9ce1dbc7b4..ec1b2fc77a 100644 --- a/modules/storage/minio_test.go +++ b/modules/storage/minio_test.go @@ -10,7 +10,7 @@ import ( "os" "testing" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "github.com/minio/minio-go/v7" "github.com/stretchr/testify/assert" @@ -18,13 +18,14 @@ import ( ) func TestMinioStorageIterator(t *testing.T) { - if os.Getenv("CI") == "" { - t.Skip("minioStorage not present outside of CI") + endpoint := os.Getenv("TEST_MINIO_ENDPOINT") + if endpoint == "" { + t.Skip("TEST_MINIO_ENDPOINT not set") return } testStorageIterator(t, setting.MinioStorageType, &setting.Storage{ MinioConfig: setting.MinioStorageConfig{ - Endpoint: "minio:9000", + Endpoint: endpoint, AccessKeyID: "123456", SecretAccessKey: "12345678", Bucket: "gitea", @@ -34,13 +35,14 @@ func TestMinioStorageIterator(t *testing.T) { } func TestVirtualHostMinioStorage(t *testing.T) { - if os.Getenv("CI") == "" { - t.Skip("minioStorage not present outside of CI") + endpoint := os.Getenv("TEST_MINIO_ENDPOINT") + if endpoint == "" { + t.Skip("TEST_MINIO_ENDPOINT not set") return } testStorageIterator(t, setting.MinioStorageType, &setting.Storage{ MinioConfig: setting.MinioStorageConfig{ - Endpoint: "minio:9000", + Endpoint: endpoint, AccessKeyID: "123456", SecretAccessKey: "12345678", Bucket: "gitea", @@ -52,19 +54,19 @@ func TestVirtualHostMinioStorage(t *testing.T) { func TestMinioStoragePath(t *testing.T) { m := &MinioStorage{basePath: ""} - assert.Equal(t, "", m.buildMinioPath("/")) - assert.Equal(t, "", m.buildMinioPath(".")) + assert.Empty(t, m.buildMinioPath("/")) + assert.Empty(t, m.buildMinioPath(".")) assert.Equal(t, "a", m.buildMinioPath("/a")) assert.Equal(t, "a/b", m.buildMinioPath("/a/b/")) - assert.Equal(t, "", m.buildMinioDirPrefix("")) + assert.Empty(t, m.buildMinioDirPrefix("")) assert.Equal(t, "a/", m.buildMinioDirPrefix("/a/")) m = &MinioStorage{basePath: "/"} - assert.Equal(t, "", m.buildMinioPath("/")) - assert.Equal(t, "", m.buildMinioPath(".")) + assert.Empty(t, m.buildMinioPath("/")) + assert.Empty(t, m.buildMinioPath(".")) assert.Equal(t, "a", m.buildMinioPath("/a")) assert.Equal(t, "a/b", m.buildMinioPath("/a/b/")) - assert.Equal(t, "", m.buildMinioDirPrefix("")) + assert.Empty(t, m.buildMinioDirPrefix("")) assert.Equal(t, "a/", m.buildMinioDirPrefix("/a/")) m = &MinioStorage{basePath: "/base"} @@ -85,13 +87,14 @@ func TestMinioStoragePath(t *testing.T) { } func TestS3StorageBadRequest(t *testing.T) { - if os.Getenv("CI") == "" { - t.Skip("S3Storage not present outside of CI") + endpoint := os.Getenv("TEST_MINIO_ENDPOINT") + if endpoint == "" { + t.Skip("TEST_MINIO_ENDPOINT not set") return } cfg := &setting.Storage{ MinioConfig: setting.MinioStorageConfig{ - Endpoint: "minio:9000", + Endpoint: endpoint, AccessKeyID: "123456", SecretAccessKey: "12345678", Bucket: "bucket", diff --git a/modules/storage/storage.go b/modules/storage/storage.go index 453d755fbb..db081e0768 100644 --- a/modules/storage/storage.go +++ b/modules/storage/storage.go @@ -11,8 +11,8 @@ import ( "net/url" "os" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" ) // ErrURLNotSupported represents url is not supported diff --git a/modules/storage/storage_test.go b/modules/storage/storage_test.go index 70bcd3155a..af3dd9520e 100644 --- a/modules/storage/storage_test.go +++ b/modules/storage/storage_test.go @@ -7,7 +7,7 @@ import ( "bytes" "testing" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/modules/structs/action.go b/modules/structs/action.go index df9f845adc..f47b228d75 100644 --- a/modules/structs/action.go +++ b/modules/structs/action.go @@ -3,6 +3,10 @@ package structs +import ( + "time" +) + // ActionRunJob represents a job of a run // swagger:model type ActionRunJob struct { @@ -23,3 +27,60 @@ type ActionRunJob struct { // the action run job status Status string `json:"status"` } + +// ActionRun represents an action run +// swagger:model +type ActionRun struct { + // the action run id + ID int64 `json:"id"` + // the action run's title + Title string `json:"title"` + // the repo this action is part of + Repo *Repository `json:"repository"` + // the name of workflow file + WorkflowID string `json:"workflow_id"` + // a unique number for each run of a repository + Index int64 `json:"index_in_repo"` + // the user that triggered this action run + TriggerUser *User `json:"trigger_user"` + // the cron id for the schedule trigger + ScheduleID int64 + // the commit/tag/… the action run ran on + PrettyRef string `json:"prettyref"` + // has the commit/tag/… the action run ran on been deleted + IsRefDeleted bool `json:"is_ref_deleted"` + // the commit sha the action run ran on + CommitSHA string `json:"commit_sha"` + // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow. + IsForkPullRequest bool `json:"is_fork_pull_request"` + // may need approval if it's a fork pull request + NeedApproval bool `json:"need_approval"` + // who approved this action run + ApprovedBy int64 `json:"approved_by"` + // the webhook event that causes the workflow to run + Event string `json:"event"` + // the payload of the webhook event that causes the workflow to run + EventPayload string `json:"event_payload"` + // the trigger event defined in the `on` configuration of the triggered workflow + TriggerEvent string `json:"trigger_event"` + // the current status of this run + Status string `json:"status"` + // when the action run was started + Started time.Time `json:"started,omitempty"` + // when the action run was stopped + Stopped time.Time `json:"stopped,omitempty"` + // when the action run was created + Created time.Time `json:"created,omitempty"` + // when the action run was last updated + Updated time.Time `json:"updated,omitempty"` + // how long the action run ran for + Duration time.Duration `json:"duration,omitempty"` + // the url of this action run + HTMLURL string `json:"html_url"` +} + +// ListActionRunResponse return a list of ActionRun +type ListActionRunResponse struct { + Entries []*ActionRun `json:"workflow_runs"` + TotalCount int64 `json:"total_count"` +} diff --git a/modules/structs/attachment.go b/modules/structs/attachment.go index c97cdcb83c..746f618cf0 100644 --- a/modules/structs/attachment.go +++ b/modules/structs/attachment.go @@ -1,7 +1,7 @@ // Copyright 2017 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package structs // import "code.gitea.io/gitea/modules/structs" +package structs // import "forgejo.org/modules/structs" import ( "time" @@ -22,6 +22,12 @@ type Attachment struct { Type string `json:"type"` } +// WebAttachment the generic attachment with mime type +type WebAttachment struct { + *Attachment + MimeType string `json:"mime_type"` +} + // EditAttachmentOptions options for editing attachments // swagger:model type EditAttachmentOptions struct { diff --git a/modules/structs/fork.go b/modules/structs/fork.go index eb7774afbc..8fc73647bd 100644 --- a/modules/structs/fork.go +++ b/modules/structs/fork.go @@ -10,3 +10,11 @@ type CreateForkOption struct { // name of the forked repository Name *string `json:"name"` } + +// SyncForkInfo information about syncing a fork +type SyncForkInfo struct { + Allowed bool `json:"allowed"` + ForkCommit string `json:"fork_commit"` + BaseCommit string `json:"base_commit"` + CommitsBehind int `json:"commits_behind"` +} diff --git a/modules/structs/git_blob.go b/modules/structs/git_blob.go index 96c7a271a9..ef06693905 100644 --- a/modules/structs/git_blob.go +++ b/modules/structs/git_blob.go @@ -3,8 +3,8 @@ package structs -// GitBlobResponse represents a git blob -type GitBlobResponse struct { +// GitBlob represents a git blob +type GitBlob struct { Content string `json:"content"` Encoding string `json:"encoding"` URL string `json:"url"` diff --git a/modules/structs/hook.go b/modules/structs/hook.go index 10b3a9db9b..11372ca6e1 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" ) // ErrInvalidReceiveHook FIXME @@ -118,6 +118,7 @@ var ( _ Payloader = &RepositoryPayload{} _ Payloader = &ReleasePayload{} _ Payloader = &PackagePayload{} + _ Payloader = &ActionPayload{} ) // _________ __ @@ -483,3 +484,36 @@ type PackagePayload struct { func (p *PackagePayload) JSONPayload() ([]byte, error) { return json.MarshalIndent(p, "", " ") } + +// _ _ _ +// / \ ___| |_(_) ___ _ __ +// / _ \ / __| __| |/ _ \| '_ \ +// / ___ \ (__| |_| | (_) | | | | +// /_/ \_\___|\__|_|\___/|_| |_| + +// this name is ridiculous, yes +// it's the sub-type of hook that has something to do with Forgejo Actions +type HookActionAction string + +const ( + HookActionFailure HookActionAction = "failure" + HookActionRecover HookActionAction = "recover" + HookActionSuccess HookActionAction = "success" +) + +// ActionPayload payload for action webhooks +type ActionPayload struct { + Action HookActionAction `json:"action"` + Run *ActionRun `json:"run"` + // the status of this run before it completed + // this must be a not done status + PriorStatus string `json:"prior_status"` + // the last run for the same workflow + // could be nil when Run is the first for it's workflow + LastRun *ActionRun `json:"last_run,omitempty"` +} + +// JSONPayload return payload information +func (p *ActionPayload) JSONPayload() ([]byte, error) { + return json.MarshalIndent(p, "", " ") +} diff --git a/modules/structs/pull.go b/modules/structs/pull.go index ab627666c9..1ce7550e19 100644 --- a/modules/structs/pull.go +++ b/modules/structs/pull.go @@ -57,7 +57,8 @@ type PullRequest struct { // swagger:strfmt date-time Closed *time.Time `json:"closed_at"` - PinOrder int `json:"pin_order"` + PinOrder int `json:"pin_order"` + Flow int64 `json:"flow"` } // PRBranchInfo information about a branch diff --git a/modules/structs/repo.go b/modules/structs/repo.go index b5f54a2a7a..c9cd729cf3 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -224,10 +224,10 @@ type EditRepoOption struct { AllowRebaseUpdate *bool `json:"allow_rebase_update,omitempty"` // set to `true` to delete pr branch after merge by default DefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"` - // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", "squash", or "fast-forward-only". - DefaultMergeStyle *string `json:"default_merge_style,omitempty"` + // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", "squash", "fast-forward-only", "manually-merged", or "rebase-update-only". + DefaultMergeStyle *string `json:"default_merge_style,omitempty" binding:"In(merge,rebase,rebase-merge,squash,fast-forward-only,manually-merged,rebase-update-only)"` // set to a update style to be used by this repository: "rebase" or "merge" - DefaultUpdateStyle *string `json:"default_update_style,omitempty"` + DefaultUpdateStyle *string `json:"default_update_style,omitempty" binding:"In(merge,rebase)"` // set to `true` to allow edits from maintainers by default DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"` // set to `true` to archive this repository. diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index 00c804146a..242343493b 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -4,6 +4,8 @@ package structs +import "time" + // FileOptions options for all file APIs type FileOptions struct { // message (optional) for the commit of this file. if not supplied, a default message will be used @@ -31,7 +33,7 @@ type CreateFileOptions struct { // Branch returns branch name func (o *CreateFileOptions) Branch() string { - return o.FileOptions.BranchName + return o.BranchName } // DeleteFileOptions options for deleting files (used for other File structs below) @@ -45,7 +47,7 @@ type DeleteFileOptions struct { // Branch returns branch name func (o *DeleteFileOptions) Branch() string { - return o.FileOptions.BranchName + return o.BranchName } // UpdateFileOptions options for updating files @@ -61,7 +63,7 @@ type UpdateFileOptions struct { // Branch returns branch name func (o *UpdateFileOptions) Branch() string { - return o.FileOptions.BranchName + return o.BranchName } // ChangeFileOperation for creating, updating or deleting a file @@ -92,7 +94,7 @@ type ChangeFilesOptions struct { // Branch returns branch name func (o *ChangeFilesOptions) Branch() string { - return o.FileOptions.BranchName + return o.BranchName } // FileOptionInterface provides a unified interface for the different file options @@ -121,6 +123,8 @@ type ContentsResponse struct { Path string `json:"path"` SHA string `json:"sha"` LastCommitSHA string `json:"last_commit_sha"` + // swagger:strfmt date-time + LastCommitWhen time.Time `json:"last_commit_when"` // `type` will be `file`, `dir`, `symlink`, or `submodule` Type string `json:"type"` Size int64 `json:"size"` diff --git a/modules/structs/user.go b/modules/structs/user.go index 6c0468b5fb..49e4c495cf 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -6,7 +6,7 @@ package structs import ( "time" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" ) // User represents a user diff --git a/modules/structs/user_app.go b/modules/structs/user_app.go index 7f78fbd495..6592c1cd48 100644 --- a/modules/structs/user_app.go +++ b/modules/structs/user_app.go @@ -23,9 +23,11 @@ type AccessToken struct { type AccessTokenList []*AccessToken // CreateAccessTokenOption options when create access token +// swagger:model CreateAccessTokenOption type CreateAccessTokenOption struct { // required: true - Name string `json:"name" binding:"Required"` + Name string `json:"name" binding:"Required"` + // example: ["all", "read:activitypub","read:issue", "write:misc", "read:notification", "read:organization", "read:package", "read:repository", "read:user"] Scopes []string `json:"scopes"` } diff --git a/modules/svg/svg.go b/modules/svg/svg.go index 016e1dc08b..e00d8de2d1 100644 --- a/modules/svg/svg.go +++ b/modules/svg/svg.go @@ -9,9 +9,9 @@ import ( "path" "strings" - gitea_html "code.gitea.io/gitea/modules/html" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/public" + gitea_html "forgejo.org/modules/html" + "forgejo.org/modules/log" + "forgejo.org/modules/public" ) var svgIcons map[string]string diff --git a/modules/sync/status_pool.go b/modules/sync/status_pool.go index 6f075d54b7..f22e3e155b 100644 --- a/modules/sync/status_pool.go +++ b/modules/sync/status_pool.go @@ -6,7 +6,7 @@ package sync import ( "sync" - "code.gitea.io/gitea/modules/container" + "forgejo.org/modules/container" ) // StatusTable is a table maintains true/false values. diff --git a/modules/system/appstate_test.go b/modules/system/appstate_test.go index 2f44c7b845..d19900c03d 100644 --- a/modules/system/appstate_test.go +++ b/modules/system/appstate_test.go @@ -6,8 +6,8 @@ package system import ( "testing" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/unittest" + "forgejo.org/models/db" + "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -43,8 +43,8 @@ func TestAppStateDB(t *testing.T) { item1 := new(testItem1) require.NoError(t, as.Get(db.DefaultContext, item1)) - assert.Equal(t, "", item1.Val1) - assert.EqualValues(t, 0, item1.Val2) + assert.Empty(t, item1.Val1) + assert.Equal(t, 0, item1.Val2) item1 = new(testItem1) item1.Val1 = "a" @@ -58,7 +58,7 @@ func TestAppStateDB(t *testing.T) { item1 = new(testItem1) require.NoError(t, as.Get(db.DefaultContext, item1)) assert.Equal(t, "a", item1.Val1) - assert.EqualValues(t, 2, item1.Val2) + assert.Equal(t, 2, item1.Val2) item2 = new(testItem2) require.NoError(t, as.Get(db.DefaultContext, item2)) diff --git a/modules/system/db.go b/modules/system/db.go index 17178283d9..384087ab4f 100644 --- a/modules/system/db.go +++ b/modules/system/db.go @@ -6,9 +6,9 @@ package system import ( "context" - "code.gitea.io/gitea/models/system" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/util" + "forgejo.org/models/system" + "forgejo.org/modules/json" + "forgejo.org/modules/util" ) // DBStore can be used to store app state items in local filesystem diff --git a/modules/templates/base.go b/modules/templates/base.go index 2c2f35bbed..76d8e3271e 100644 --- a/modules/templates/base.go +++ b/modules/templates/base.go @@ -7,8 +7,8 @@ import ( "slices" "strings" - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/setting" ) func AssetFS() *assetfs.LayeredFS { diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go index e1babd83c9..c5752c8c72 100644 --- a/modules/templates/dynamic.go +++ b/modules/templates/dynamic.go @@ -6,8 +6,8 @@ package templates import ( - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/setting" ) func BuiltinAssets() *assetfs.Layer { diff --git a/modules/templates/eval/eval.go b/modules/templates/eval/eval.go index 5d4ac915b9..487a1de4b0 100644 --- a/modules/templates/eval/eval.go +++ b/modules/templates/eval/eval.go @@ -8,7 +8,7 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) type Num struct { diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 023ba60fa9..02b175e6f6 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -14,14 +14,14 @@ import ( "strings" "time" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/svg" - "code.gitea.io/gitea/modules/templates/eval" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/services/gitdiff" + user_model "forgejo.org/models/user" + "forgejo.org/modules/base" + "forgejo.org/modules/markup" + "forgejo.org/modules/setting" + "forgejo.org/modules/svg" + "forgejo.org/modules/templates/eval" + "forgejo.org/modules/util" + "forgejo.org/services/gitdiff" ) // NewFuncMap returns functions for injecting to templates diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index 55a55dd7f4..d60397df08 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -19,12 +19,12 @@ import ( "sync/atomic" texttemplate "text/template" - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/templates/scopedtmpl" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/templates/scopedtmpl" + "forgejo.org/modules/util" ) type TemplateExecutor scopedtmpl.TemplateExecutor diff --git a/modules/templates/htmlrenderer_test.go b/modules/templates/htmlrenderer_test.go index a1d3783a75..d7a4cd7161 100644 --- a/modules/templates/htmlrenderer_test.go +++ b/modules/templates/htmlrenderer_test.go @@ -10,7 +10,7 @@ import ( "strings" "testing" - "code.gitea.io/gitea/modules/assetfs" + "forgejo.org/modules/assetfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -66,7 +66,7 @@ func TestHandleError(t *testing.T) { _, err = tmpl.Parse(s) require.Error(t, err) msg := h(err) - assert.EqualValues(t, strings.TrimSpace(expect), strings.TrimSpace(msg)) + assert.Equal(t, strings.TrimSpace(expect), strings.TrimSpace(msg)) } test("{{", p.handleGenericTemplateError, ` @@ -103,5 +103,5 @@ god knows XXX ---------------------------------------------------------------------- ` actualMsg := p.handleExpectedEndError(errors.New("template: test:1: expected end; found XXX")) - assert.EqualValues(t, strings.TrimSpace(expectedMsg), strings.TrimSpace(actualMsg)) + assert.Equal(t, strings.TrimSpace(expectedMsg), strings.TrimSpace(actualMsg)) } diff --git a/modules/templates/mailer.go b/modules/templates/mailer.go index ee79755dbb..a40728d7c7 100644 --- a/modules/templates/mailer.go +++ b/modules/templates/mailer.go @@ -11,9 +11,9 @@ import ( "strings" texttmpl "text/template" - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/base" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" ) var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}\s*$`) diff --git a/modules/templates/main_test.go b/modules/templates/main_test.go index 977699f9c7..946bc603f6 100644 --- a/modules/templates/main_test.go +++ b/modules/templates/main_test.go @@ -7,12 +7,12 @@ import ( "context" "testing" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/markup" + "forgejo.org/models/unittest" + "forgejo.org/modules/markup" - _ "code.gitea.io/gitea/models" - _ "code.gitea.io/gitea/models/forgefed" - _ "code.gitea.io/gitea/models/issues" + _ "forgejo.org/models" + _ "forgejo.org/models/forgefed" + _ "forgejo.org/models/issues" ) func TestMain(m *testing.M) { diff --git a/modules/templates/scopedtmpl/scopedtmpl.go b/modules/templates/scopedtmpl/scopedtmpl.go index 2722ba97a2..41a8ca86e9 100644 --- a/modules/templates/scopedtmpl/scopedtmpl.go +++ b/modules/templates/scopedtmpl/scopedtmpl.go @@ -192,21 +192,21 @@ func newScopedTemplateSet(all *template.Template, name string) (*scopedTemplateS collectTemplates(nodeList.Nodes) } else if node.Type() == parse.NodeIf { nodeIf := node.(*parse.IfNode) - collectTemplates(nodeIf.BranchNode.List.Nodes) - if nodeIf.BranchNode.ElseList != nil { - collectTemplates(nodeIf.BranchNode.ElseList.Nodes) + collectTemplates(nodeIf.List.Nodes) + if nodeIf.ElseList != nil { + collectTemplates(nodeIf.ElseList.Nodes) } } else if node.Type() == parse.NodeRange { nodeRange := node.(*parse.RangeNode) - collectTemplates(nodeRange.BranchNode.List.Nodes) - if nodeRange.BranchNode.ElseList != nil { - collectTemplates(nodeRange.BranchNode.ElseList.Nodes) + collectTemplates(nodeRange.List.Nodes) + if nodeRange.ElseList != nil { + collectTemplates(nodeRange.ElseList.Nodes) } } else if node.Type() == parse.NodeWith { nodeWith := node.(*parse.WithNode) - collectTemplates(nodeWith.BranchNode.List.Nodes) - if nodeWith.BranchNode.ElseList != nil { - collectTemplates(nodeWith.BranchNode.ElseList.Nodes) + collectTemplates(nodeWith.List.Nodes) + if nodeWith.ElseList != nil { + collectTemplates(nodeWith.ElseList.Nodes) } } } diff --git a/modules/templates/static.go b/modules/templates/static.go index b5a7e561ec..776548c853 100644 --- a/modules/templates/static.go +++ b/modules/templates/static.go @@ -8,8 +8,8 @@ package templates import ( "time" - "code.gitea.io/gitea/modules/assetfs" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/assetfs" + "forgejo.org/modules/timeutil" ) // GlobalModTime provide a global mod time for embedded asset files diff --git a/modules/templates/util_avatar.go b/modules/templates/util_avatar.go index a468361101..93ebec51e4 100644 --- a/modules/templates/util_avatar.go +++ b/modules/templates/util_avatar.go @@ -9,13 +9,13 @@ import ( "html" "html/template" - activities_model "code.gitea.io/gitea/models/activities" - "code.gitea.io/gitea/models/avatars" - "code.gitea.io/gitea/models/organization" - repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" - gitea_html "code.gitea.io/gitea/modules/html" - "code.gitea.io/gitea/modules/setting" + activities_model "forgejo.org/models/activities" + "forgejo.org/models/avatars" + "forgejo.org/models/organization" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + gitea_html "forgejo.org/modules/html" + "forgejo.org/modules/setting" ) type AvatarUtils struct { diff --git a/modules/templates/util_date.go b/modules/templates/util_date.go index f521b39b79..bb83bf692a 100644 --- a/modules/templates/util_date.go +++ b/modules/templates/util_date.go @@ -10,9 +10,9 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/translation" + "forgejo.org/modules/setting" + "forgejo.org/modules/timeutil" + "forgejo.org/modules/translation" ) type DateUtils struct{} diff --git a/modules/templates/util_date_test.go b/modules/templates/util_date_test.go index 2f5c79f762..37caf0d422 100644 --- a/modules/templates/util_date_test.go +++ b/modules/templates/util_date_test.go @@ -8,9 +8,9 @@ import ( "testing" "time" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/modules/timeutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/modules/templates/util_dict.go b/modules/templates/util_dict.go index 8d6376b522..16f722e61b 100644 --- a/modules/templates/util_dict.go +++ b/modules/templates/util_dict.go @@ -4,14 +4,15 @@ package templates import ( + "errors" "fmt" "html" "html/template" "reflect" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/container" + "forgejo.org/modules/json" + "forgejo.org/modules/setting" ) func dictMerge(base map[string]any, arg any) bool { @@ -33,7 +34,7 @@ func dictMerge(base map[string]any, arg any) bool { // The dot syntax is highly discouraged because it might cause unclear key conflicts. It's always good to use explicit keys. func dict(args ...any) (map[string]any, error) { if len(args)%2 != 0 { - return nil, fmt.Errorf("invalid dict constructor syntax: must have key-value pairs") + return nil, errors.New("invalid dict constructor syntax: must have key-value pairs") } m := make(map[string]any, len(args)/2) for i := 0; i < len(args); i += 2 { diff --git a/modules/templates/util_json.go b/modules/templates/util_json.go index 71a4e23d36..3bc80e8f21 100644 --- a/modules/templates/util_json.go +++ b/modules/templates/util_json.go @@ -6,7 +6,7 @@ package templates import ( "bytes" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" ) type JsonUtils struct{} //nolint:revive diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go index 774385483b..60f918be47 100644 --- a/modules/templates/util_misc.go +++ b/modules/templates/util_misc.go @@ -1,4 +1,5 @@ // Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package templates @@ -12,14 +13,16 @@ import ( "strings" "time" - activities_model "code.gitea.io/gitea/models/activities" - repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/git" - giturl "code.gitea.io/gitea/modules/git/url" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/repository" - "code.gitea.io/gitea/modules/svg" + activities_model "forgejo.org/models/activities" + asymkey_model "forgejo.org/models/asymkey" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" + "forgejo.org/modules/git" + giturl "forgejo.org/modules/git/url" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/repository" + "forgejo.org/modules/svg" "github.com/editorconfig/editorconfig-core-go/v2" ) @@ -38,10 +41,11 @@ func SortArrow(normSort, revSort, urlSort string, isDefault bool) template.HTML } else { // if sort arg is in url test if it correlates with column header sort arguments // the direction of the arrow should indicate the "current sort order", up means ASC(normal), down means DESC(rev) - if urlSort == normSort { + switch urlSort { + case normSort: // the table is sorted with this header normal return svg.RenderHTML("octicon-triangle-up", 16) - } else if urlSort == revSort { + case revSort: // the table is sorted with this header reverse return svg.RenderHTML("octicon-triangle-down", 16) } @@ -59,6 +63,7 @@ func IsMultilineCommitMessage(msg string) bool { type Actioner interface { GetOpType() activities_model.ActionType GetActUserName(ctx context.Context) string + GetRepo(ctx context.Context) *repo_model.Repository GetRepoUserName(ctx context.Context) string GetRepoName(ctx context.Context) string GetRepoPath(ctx context.Context) string @@ -108,7 +113,7 @@ func ActionIcon(opType activities_model.ActionType) string { } // ActionContent2Commits converts action content to push commits -func ActionContent2Commits(act Actioner) *repository.PushCommits { +func ActionContent2Commits(ctx context.Context, act Actioner) *repository.PushCommits { push := repository.NewPushCommits() if act == nil || act.GetContent() == "" { @@ -122,6 +127,23 @@ func ActionContent2Commits(act Actioner) *repository.PushCommits { if push.Len == 0 { push.Len = len(push.Commits) } + repo := act.GetRepo(ctx) + for _, commit := range push.Commits { + gitCommit, err := repository.PushCommitToCommit(commit) + if err != nil { + // Only happens if the commit has an invalid sha + commit.Verification = &asymkey_model.ObjectVerification{ + Verified: false, + Reason: "git.error.invalid_commit_id", + } + continue + } + verification := asymkey_model.ParseCommitWithSignature(ctx, gitCommit) + _ = asymkey_model.CalculateTrustStatus(verification, repo.GetTrustModel(), func(user *user_model.User) (bool, error) { + return repo_model.IsOwnerMemberCollaborator(ctx, repo, user.ID) + }, nil) + commit.Verification = verification + } return push } diff --git a/modules/templates/util_misc_test.go b/modules/templates/util_misc_test.go new file mode 100644 index 0000000000..3d59f29bd1 --- /dev/null +++ b/modules/templates/util_misc_test.go @@ -0,0 +1,124 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package templates + +import ( + "testing" + + activities_model "forgejo.org/models/activities" + asymkey_model "forgejo.org/models/asymkey" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/git" + "forgejo.org/modules/json" + "forgejo.org/modules/repository" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func pushCommits() *repository.PushCommits { + pushCommits := repository.NewPushCommits() + pushCommits.Commits = []*repository.PushCommit{ + { + Sha1: "x", + CommitterEmail: "user2@example.com", + CommitterName: "User2", + AuthorEmail: "user2@example.com", + AuthorName: "User2", + Message: "invalid sha1", + }, + { + Sha1: "2c54faec6c45d31c1abfaecdab471eac6633738a", + CommitterEmail: "user2@example.com", + CommitterName: "User2", + AuthorEmail: "user2@example.com", + AuthorName: "User2", + Message: "not signed commit", + }, + { + Sha1: "2d491b2985a7ff848d5c02748e7ea9f9f7619f9f", + CommitterEmail: "non-existent", + CommitterName: "user2", + AuthorEmail: "non-existent", + AuthorName: "user2", + Message: "Using email that isn't known to Forgejo", + Signature: &git.ObjectSignature{ + Payload: `tree 2d491b2985a7ff848d5c02748e7ea9f9f7619f9f +parent 45b03601635a1f463b81963a4022c7f87ce96ef9 +author user2 1699710556 +0100 +committer user2 1699710556 +0100 + +Using email that isn't known to Forgejo +`, + Signature: `-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgoGSe9Zy7Ez9bSJcaTNjh/Y7p95 +f5DujjqkpzFRtw6CEAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 +AAAAQIMufOuSjZeDUujrkVK4sl7ICa0WwEftas8UAYxx0Thdkiw2qWjR1U1PKfTLm16/w8 +/bS1LX1lZNuzm2LR2qEgw= +-----END SSH SIGNATURE----- +`, + }, + }, + { + Sha1: "853694aae8816094a0d875fee7ea26278dbf5d0f", + CommitterEmail: "user2@example.com", + CommitterName: "user2", + AuthorEmail: "user2@example.com", + AuthorName: "user2", + Message: "Add content", + Signature: &git.ObjectSignature{ + Payload: `tree 853694aae8816094a0d875fee7ea26278dbf5d0f +parent c2780d5c313da2a947eae22efd7dacf4213f4e7f +author user2 1699707877 +0100 +committer user2 1699707877 +0100 + +Add content +`, + Signature: `-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgoGSe9Zy7Ez9bSJcaTNjh/Y7p95 +f5DujjqkpzFRtw6CEAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 +AAAAQBe2Fwk/FKY3SBCnG6jSYcO6ucyahp2SpQ/0P+otslzIHpWNW8cQ0fGLdhhaFynJXQ +fs9cMpZVM9BfIKNUSO8QY= +-----END SSH SIGNATURE----- +`, + }, + }, + } + return pushCommits +} + +func TestActionContent2Commits_VerificationState(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/TestParseCommitWithSSHSignature/")() + require.NoError(t, unittest.PrepareTestDatabase()) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: user2.ID}) + commits, err := json.Marshal(pushCommits()) + require.NoError(t, err) + + act := &activities_model.Action{ + OpType: activities_model.ActionCommitRepo, + Repo: repo, + Content: string(commits), + } + push := ActionContent2Commits(t.Context(), act) + + assert.Equal(t, 4, push.Len) + assert.False(t, push.Commits[0].Verification.Verified) + assert.Empty(t, push.Commits[0].Verification.TrustStatus) + assert.Equal(t, "git.error.invalid_commit_id", push.Commits[0].Verification.Reason) + + assert.False(t, push.Commits[1].Verification.Verified) + assert.Empty(t, push.Commits[1].Verification.TrustStatus) + assert.Equal(t, asymkey_model.NotSigned, push.Commits[1].Verification.Reason) + + assert.False(t, push.Commits[2].Verification.Verified) + assert.Empty(t, push.Commits[2].Verification.TrustStatus) + assert.Equal(t, asymkey_model.NoKeyFound, push.Commits[2].Verification.Reason) + + assert.True(t, push.Commits[3].Verification.Verified) + assert.Equal(t, "user2 / SHA256:TKfwbZMR7e9OnlV2l1prfah1TXH8CmqR0PvFEXVCXA4", push.Commits[3].Verification.Reason) + assert.Equal(t, "trusted", push.Commits[3].Verification.TrustStatus) +} diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 3c337ae895..a4d7a82eea 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -14,14 +14,14 @@ import ( "strings" "unicode" - issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/modules/emoji" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/markdown" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/translation" - "code.gitea.io/gitea/modules/util" + issues_model "forgejo.org/models/issues" + "forgejo.org/modules/emoji" + "forgejo.org/modules/log" + "forgejo.org/modules/markup" + "forgejo.org/modules/markup/markdown" + "forgejo.org/modules/setting" + "forgejo.org/modules/translation" + "forgejo.org/modules/util" ) // RenderCommitMessage renders commit message with XSS-safe and special links. diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 25a41f02c0..62e063213c 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -8,10 +8,10 @@ import ( "html/template" "testing" - "code.gitea.io/gitea/models/db" - issues_model "code.gitea.io/gitea/models/issues" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/translation" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + "forgejo.org/models/unittest" + "forgejo.org/modules/translation" "github.com/stretchr/testify/assert" ) @@ -47,12 +47,12 @@ var testMetas = map[string]string{ func TestApostrophesInMentions(t *testing.T) { rendered := RenderMarkdownToHtml(t.Context(), "@mention-user's comment") - assert.EqualValues(t, template.HTML("

@mention-user's comment

\n"), rendered) + assert.Equal(t, template.HTML("

@mention-user's comment

\n"), rendered) } func TestNonExistantUserMention(t *testing.T) { rendered := RenderMarkdownToHtml(t.Context(), "@ThisUserDoesNotExist @mention-user") - assert.EqualValues(t, template.HTML("

@ThisUserDoesNotExist @mention-user

\n"), rendered) + assert.Equal(t, template.HTML("

@ThisUserDoesNotExist @mention-user

\n"), rendered) } func TestRenderCommitBody(t *testing.T) { @@ -192,8 +192,8 @@ func TestRenderMarkdownToHtml(t *testing.T) { remote link local link remote link -local image -remote image +local image +remote image 88fc37a3c0...12fc37a3c0 (hash) diff --git a/modules/templates/util_string.go b/modules/templates/util_string.go index 685759dcbc..2d255e54a7 100644 --- a/modules/templates/util_string.go +++ b/modules/templates/util_string.go @@ -8,7 +8,7 @@ import ( "html/template" "strings" - "code.gitea.io/gitea/modules/base" + "forgejo.org/modules/base" ) type StringUtils struct{} diff --git a/modules/templates/util_test.go b/modules/templates/util_test.go index 79aaba4a0e..e28da8090b 100644 --- a/modules/templates/util_test.go +++ b/modules/templates/util_test.go @@ -29,7 +29,7 @@ func TestDict(t *testing.T) { for _, c := range cases { got, err := dict(c.args...) require.NoError(t, err) - assert.EqualValues(t, c.want, got) + assert.Equal(t, c.want, got) } bads := []struct { diff --git a/modules/templates/vars/vars_test.go b/modules/templates/vars/vars_test.go index c54342204d..a0c3490c3a 100644 --- a/modules/templates/vars/vars_test.go +++ b/modules/templates/vars/vars_test.go @@ -61,7 +61,7 @@ func TestExpandVars(t *testing.T) { for _, kase := range kases { t.Run(kase.tmpl, func(t *testing.T) { res, err := Expand(kase.tmpl, kase.data) - assert.EqualValues(t, kase.out, res) + assert.Equal(t, kase.out, res) if kase.error { require.Error(t, err) } else { diff --git a/modules/test/distant_federation_server_mock.go b/modules/test/distant_federation_server_mock.go index fd68c88a40..9bd908e2b9 100644 --- a/modules/test/distant_federation_server_mock.go +++ b/modules/test/distant_federation_server_mock.go @@ -95,7 +95,7 @@ func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server { }) } for _, repository := range mock.Repositories { - federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox/", repository.ID), + federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox", repository.ID), func(res http.ResponseWriter, req *http.Request) { if req.Method != "POST" { t.Errorf("POST expected at: %q", req.URL.EscapedPath()) diff --git a/modules/test/logchecker.go b/modules/test/logchecker.go index 0f12257f3e..8e8fc32216 100644 --- a/modules/test/logchecker.go +++ b/modules/test/logchecker.go @@ -11,7 +11,7 @@ import ( "sync/atomic" "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) type LogChecker struct { diff --git a/modules/test/logchecker_test.go b/modules/test/logchecker_test.go index 0f410fed12..d81142bf1c 100644 --- a/modules/test/logchecker_test.go +++ b/modules/test/logchecker_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" "github.com/stretchr/testify/assert" ) diff --git a/modules/test/utils.go b/modules/test/utils.go index 3d884b6cbe..db131f19d0 100644 --- a/modules/test/utils.go +++ b/modules/test/utils.go @@ -7,8 +7,9 @@ import ( "net/http" "net/http/httptest" "strings" + "time" - "code.gitea.io/gitea/modules/json" + "forgejo.org/modules/json" ) // RedirectURL returns the redirect URL of a http response. @@ -46,3 +47,8 @@ func MockProtect[T any](p *T) (reset func()) { old := *p return func() { *p = old } } + +// When this is called, sleep until the unix time was increased by one. +func SleepTillNextSecond() { + time.Sleep(time.Second - time.Since(time.Now().Truncate(time.Second))) +} diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index caa8abd07b..772ae47e71 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -16,9 +16,9 @@ import ( "testing" "time" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/queue" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/queue" + "forgejo.org/modules/util" ) var ( @@ -363,6 +363,12 @@ var ignoredErrorMessage = []string{ // TestDatabaseCollation `[E] [Error SQL Query] INSERT INTO test_collation_tbl (txt) VALUES ('main') []`, + + // Test_CmdForgejo_Actions + `DB: No dedicated replica host defined; falling back to primary DSN for replica connections`, + + // TestDevtestErrorpages + `ErrorPage() [E] Example error: Example error`, } func (w *testLoggerWriterCloser) recordError(msg string) { diff --git a/modules/timeutil/executable.go b/modules/timeutil/executable.go index 57ae8b2a9d..7b30176df0 100644 --- a/modules/timeutil/executable.go +++ b/modules/timeutil/executable.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/log" + "forgejo.org/modules/log" ) var ( diff --git a/modules/timeutil/since.go b/modules/timeutil/since.go index 2c89ae38d5..f296a2dc86 100644 --- a/modules/timeutil/since.go +++ b/modules/timeutil/since.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/translation" + "forgejo.org/modules/translation" ) // Seconds-based time units @@ -96,11 +96,7 @@ func timeSincePro(then, now time.Time, lang translation.Locale) string { } var timeStr, diffStr string - for { - if diff == 0 { - break - } - + for diff != 0 { diff, diffStr = computeTimeDiffFloor(diff, lang) timeStr += ", " + diffStr } diff --git a/modules/timeutil/since_test.go b/modules/timeutil/since_test.go index 40fefe8700..b47b2c76dd 100644 --- a/modules/timeutil/since_test.go +++ b/modules/timeutil/since_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/translation" + "forgejo.org/modules/setting" + "forgejo.org/modules/translation" "github.com/stretchr/testify/assert" ) diff --git a/modules/timeutil/timestamp.go b/modules/timeutil/timestamp.go index 27a80b6682..783ccba30b 100644 --- a/modules/timeutil/timestamp.go +++ b/modules/timeutil/timestamp.go @@ -6,7 +6,7 @@ package timeutil import ( "time" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // TimeStamp defines a timestamp diff --git a/modules/timeutil/timestampnano.go b/modules/timeutil/timestampnano.go index 4a9f7955b9..e2e86b863f 100644 --- a/modules/timeutil/timestampnano.go +++ b/modules/timeutil/timestampnano.go @@ -6,7 +6,7 @@ package timeutil import ( "time" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // TimeStampNano is for nano time in database, do not use it unless there is a real requirement. diff --git a/modules/translation/i18n/dummy.go b/modules/translation/i18n/dummy.go index 861672c619..9f1e682f11 100644 --- a/modules/translation/i18n/dummy.go +++ b/modules/translation/i18n/dummy.go @@ -15,6 +15,10 @@ type KeyLocale struct{} var _ Locale = (*KeyLocale)(nil) +func (k *KeyLocale) Language() string { + return "dummy" +} + // HasKey implements Locale. func (k *KeyLocale) HasKey(trKey string) bool { return true @@ -35,6 +39,11 @@ func (k *KeyLocale) TrPluralString(count any, trKey string, trArgs ...any) templ return template.HTML(FormatDummy(trKey, PrepareArgsForHTML(trArgs...)...)) } +// TrPluralStringAllForms implements Locale. +func (k *KeyLocale) TrPluralStringAllForms(trKey string) ([]string, []string) { + return []string{trKey}, nil +} + func FormatDummy(trKey string, args ...any) string { if len(args) == 0 { return fmt.Sprintf("(%s)", trKey) diff --git a/modules/translation/i18n/dummy_test.go b/modules/translation/i18n/dummy_test.go index 7bc29ef839..1df3d0c348 100644 --- a/modules/translation/i18n/dummy_test.go +++ b/modules/translation/i18n/dummy_test.go @@ -6,7 +6,7 @@ package i18n_test import ( "testing" - "code.gitea.io/gitea/modules/translation/i18n" + "forgejo.org/modules/translation/i18n" "github.com/stretchr/testify/assert" ) diff --git a/modules/translation/i18n/errors.go b/modules/translation/i18n/errors.go index ee9436a8f7..63a5f48dfa 100644 --- a/modules/translation/i18n/errors.go +++ b/modules/translation/i18n/errors.go @@ -4,7 +4,7 @@ package i18n import ( - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) var ( diff --git a/modules/translation/i18n/i18n.go b/modules/translation/i18n/i18n.go index e447502a3b..10ed8ac199 100644 --- a/modules/translation/i18n/i18n.go +++ b/modules/translation/i18n/i18n.go @@ -25,6 +25,7 @@ const ( var DefaultLocales = NewLocaleStore() type Locale interface { + Language() string // TrString translates a given key and arguments for a language TrString(trKey string, trArgs ...any) string // TrPluralString translates a given pluralized key and arguments for a language. @@ -34,6 +35,9 @@ type Locale interface { TrHTML(trKey string, trArgs ...any) template.HTML // HasKey reports if a locale has a translation for a given key HasKey(trKey string) bool + // TrPluralStringAllForms returns all plural form variants for a given string, and also + // the fallbacks for the default language if the translation is incomplete. + TrPluralStringAllForms(trKey string) ([]string, []string) } // LocaleStore provides the functions common to all locale stores @@ -42,6 +46,8 @@ type LocaleStore interface { // SetDefaultLang sets the default language to fall back to SetDefaultLang(lang string) + // GetDefaultLang returns the name of the default language to fall back to + GetDefaultLang() string // ListLangNameDesc provides paired slices of language names to descriptors ListLangNameDesc() (names, desc []string) // Locale return the locale for the provided language or the default language if not found @@ -49,7 +55,7 @@ type LocaleStore interface { // HasLang returns whether a given language is present in the store HasLang(langName string) bool // AddLocaleByIni adds a new old-style language to the store - AddLocaleByIni(langName, langDesc string, pluralRule PluralFormRule, source, moreSource []byte) error + AddLocaleByIni(langName, langDesc string, pluralRule PluralFormRule, usedPluralForms []PluralFormIndex, source, moreSource []byte) error // AddLocaleByJSON adds new-style content to an existing language to the store AddToLocaleFromJSON(langName string, source []byte) error } diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go index a0458f0b8e..ac086d75d9 100644 --- a/modules/translation/i18n/i18n_test.go +++ b/modules/translation/i18n/i18n_test.go @@ -32,6 +32,11 @@ var MockPluralRuleEnglish PluralFormRule = func(n int64) PluralFormIndex { return PluralFormOther } +var ( + UsedPluralFormsEnglish = []PluralFormIndex{PluralFormOne, PluralFormOther} + UsedPluralFormsMock = []PluralFormIndex{PluralFormZero, PluralFormOne, PluralFormFew, PluralFormOther} +) + func TestLocaleStore(t *testing.T) { testData1 := []byte(` .dot.name = Dot Name @@ -85,8 +90,8 @@ commits = fallback value for commits `) ls := NewLocaleStore() - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRuleEnglish, testData1, nil)) - require.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", MockPluralRule, testData2, nil)) + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRuleEnglish, UsedPluralFormsEnglish, testData1, nil)) + require.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", MockPluralRule, UsedPluralFormsMock, testData2, nil)) require.NoError(t, ls.AddToLocaleFromJSON("lang1", testDataJSON1)) require.NoError(t, ls.AddToLocaleFromJSON("lang2", testDataJSON2)) ls.SetDefaultLang("lang1") @@ -166,7 +171,7 @@ commits = fallback value for commits found = lang1.HasKey("no-such") assert.False(t, found) - assert.EqualValues(t, "no-such", lang1.TrString("no-such")) + assert.Equal(t, "no-such", lang1.TrString("no-such")) require.NoError(t, ls.Close()) } @@ -182,7 +187,7 @@ c=22 `) ls := NewLocaleStore() - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, testData1, testData2)) + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, UsedPluralFormsMock, testData1, testData2)) lang1, _ := ls.Locale("lang1") assert.Equal(t, "11", lang1.TrString("a")) assert.Equal(t, "21", lang1.TrString("b")) @@ -223,7 +228,7 @@ func (e *errorPointerReceiver) Error() string { func TestLocaleWithTemplate(t *testing.T) { ls := NewLocaleStore() - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, []byte(`key=%s`), nil)) + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, UsedPluralFormsMock, []byte(`key=%s`), nil)) lang1, _ := ls.Locale("lang1") tmpl := template.New("test").Funcs(template.FuncMap{"tr": lang1.TrHTML}) @@ -286,7 +291,7 @@ func TestLocaleStoreQuirks(t *testing.T) { for _, testData := range testDataList { ls := NewLocaleStore() - err := ls.AddLocaleByIni("lang1", "Lang1", nil, []byte("a="+testData.in), nil) + err := ls.AddLocaleByIni("lang1", "Lang1", nil, nil, []byte("a="+testData.in), nil) lang1, _ := ls.Locale("lang1") require.NoError(t, err, testData.hint) assert.Equal(t, testData.out, lang1.TrString("a"), testData.hint) diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index 0cfa96810e..fc27c75d13 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -1,4 +1,5 @@ // Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package i18n @@ -8,10 +9,10 @@ import ( "html/template" "slices" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/setting" + "forgejo.org/modules/translation/localeiter" + "forgejo.org/modules/util" ) // This file implements the static LocaleStore that will not watch for changes @@ -23,6 +24,7 @@ type locale struct { newStyleMessages map[string]string pluralRule PluralFormRule + usedPluralForms []PluralFormIndex } var _ Locale = (*locale)(nil) @@ -55,7 +57,7 @@ const ( // the correct plural form for a given count and language. // AddLocaleByIni adds locale by ini into the store -func (store *localeStore) AddLocaleByIni(langName, langDesc string, pluralRule PluralFormRule, source, moreSource []byte) error { +func (store *localeStore) AddLocaleByIni(langName, langDesc string, pluralRule PluralFormRule, usedPluralForms []PluralFormIndex, source, moreSource []byte) error { if _, ok := store.localeMap[langName]; ok { return ErrLocaleAlreadyExist } @@ -63,7 +65,7 @@ func (store *localeStore) AddLocaleByIni(langName, langDesc string, pluralRule P store.langNames = append(store.langNames, langName) store.langDescs = append(store.langDescs, langDesc) - l := &locale{store: store, langName: langName, idxToMsgMap: make(map[int]string), pluralRule: pluralRule, newStyleMessages: make(map[string]string)} + l := &locale{store: store, langName: langName, idxToMsgMap: make(map[int]string), pluralRule: pluralRule, usedPluralForms: usedPluralForms, newStyleMessages: make(map[string]string)} store.localeMap[l.langName] = l iniFile, err := setting.NewConfigProviderForLocale(source, moreSource) @@ -94,54 +96,20 @@ func (store *localeStore) AddLocaleByIni(langName, langDesc string, pluralRule P return nil } -func RecursivelyAddTranslationsFromJSON(locale *locale, object map[string]any, prefix string) error { - for key, value := range object { - var fullkey string - if prefix != "" { - fullkey = prefix + "." + key - } else { - fullkey = key - } - - switch v := value.(type) { - case string: - // Check whether we are adding a plural form to the parent object, or a new nested JSON object. - - if key == "zero" || key == "one" || key == "two" || key == "few" || key == "many" { - locale.newStyleMessages[prefix+PluralFormSeparator+key] = v - } else if key == "other" { - locale.newStyleMessages[prefix] = v - } else { - locale.newStyleMessages[fullkey] = v - } - - case map[string]any: - err := RecursivelyAddTranslationsFromJSON(locale, v, fullkey) - if err != nil { - return err - } - - case nil: - default: - return fmt.Errorf("Unrecognized JSON value '%s'", value) - } - } - - return nil -} - func (store *localeStore) AddToLocaleFromJSON(langName string, source []byte) error { locale, ok := store.localeMap[langName] if !ok { return ErrLocaleDoesNotExist } - var result map[string]any - if err := json.Unmarshal(source, &result); err != nil { - return err - } - - return RecursivelyAddTranslationsFromJSON(locale, result, "") + return localeiter.IterateMessagesNextContent(source, func(key, pluralForm, value string) error { + msgKey := key + if pluralForm != "" { + msgKey = key + PluralFormSeparator + pluralForm + } + locale.newStyleMessages[msgKey] = value + return nil + }) } func (l *locale) LookupNewStyleMessage(trKey string) string { @@ -151,7 +119,7 @@ func (l *locale) LookupNewStyleMessage(trKey string) string { return "" } -func (l *locale) LookupPlural(trKey string, count any) string { +func (l *locale) LookupPluralByCount(trKey string, count any) string { n, err := util.ToInt64(count) if err != nil { log.Error("Invalid plural count '%s'", count) @@ -159,6 +127,10 @@ func (l *locale) LookupPlural(trKey string, count any) string { } pluralForm := l.pluralRule(n) + return l.LookupPluralByForm(trKey, pluralForm) +} + +func (l *locale) LookupPluralByForm(trKey string, pluralForm PluralFormIndex) string { suffix := "" switch pluralForm { case PluralFormZero: @@ -174,7 +146,7 @@ func (l *locale) LookupPlural(trKey string, count any) string { case PluralFormOther: // No suffix for the "other" string. default: - log.Error("Invalid plural form index %d for count %d", pluralForm, count) + log.Error("Invalid plural form index %d", pluralForm) return "" } @@ -182,7 +154,7 @@ func (l *locale) LookupPlural(trKey string, count any) string { return result } - log.Error("Missing translation for plural form index %d for count %d", pluralForm, count) + log.Error("Missing translation for plural form %s", suffix) return "" } @@ -200,6 +172,10 @@ func (store *localeStore) SetDefaultLang(lang string) { store.defaultLang = lang } +func (store *localeStore) GetDefaultLang() string { + return store.defaultLang +} + // Locale returns the locale for the lang or the default language func (store *localeStore) Locale(lang string) (Locale, bool) { l, found := store.localeMap[lang] @@ -218,6 +194,10 @@ func (store *localeStore) Close() error { return nil } +func (l *locale) Language() string { + return l.langName +} + func (l *locale) TrString(trKey string, trArgs ...any) string { format := trKey @@ -239,6 +219,7 @@ func (l *locale) TrString(trKey string, trArgs ...any) string { if defaultLang, ok := l.store.localeMap[l.store.defaultLang]; ok { if msg := defaultLang.LookupNewStyleMessage(trKey); msg != "" { format = msg + found = true } else if foundIndex { // Third fallback: old-style default language if msg, ok := defaultLang.idxToMsgMap[idx]; ok { @@ -283,11 +264,11 @@ func (l *locale) TrHTML(trKey string, trArgs ...any) template.HTML { } func (l *locale) TrPluralString(count any, trKey string, trArgs ...any) template.HTML { - message := l.LookupPlural(trKey, count) + message := l.LookupPluralByCount(trKey, count) if message == "" { if defaultLang, ok := l.store.localeMap[l.store.defaultLang]; ok { - message = defaultLang.LookupPlural(trKey, count) + message = defaultLang.LookupPluralByCount(trKey, count) } if message == "" { message = trKey @@ -301,6 +282,36 @@ func (l *locale) TrPluralString(count any, trKey string, trArgs ...any) template return template.HTML(message) } +func (l *locale) TrPluralStringAllForms(trKey string) ([]string, []string) { + defaultLang, hasDefaultLang := l.store.localeMap[l.store.defaultLang] + + var fallback []string + fallback = nil + + result := make([]string, len(l.usedPluralForms)) + allPresent := true + + for i, form := range l.usedPluralForms { + result[i] = l.LookupPluralByForm(trKey, form) + if result[i] == "" { + allPresent = false + } + } + + if !allPresent { + if hasDefaultLang { + fallback = make([]string, len(defaultLang.usedPluralForms)) + for i, form := range defaultLang.usedPluralForms { + fallback[i] = defaultLang.LookupPluralByForm(trKey, form) + } + } else { + log.Error("Plural set for '%s' is incomplete and no fallback language is set.", trKey) + } + } + + return result, fallback +} + // HasKey returns whether a key is present in this locale or not func (l *locale) HasKey(trKey string) bool { _, ok := l.newStyleMessages[trKey] diff --git a/modules/translation/localeiter/utils.go b/modules/translation/localeiter/utils.go new file mode 100644 index 0000000000..de398258e2 --- /dev/null +++ b/modules/translation/localeiter/utils.go @@ -0,0 +1,89 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// extracted from `/build/lint-locale.go`, `/build/lint-locale-usage.go` + +package localeiter + +import ( + "encoding/json" //nolint:depguard + "fmt" + + "forgejo.org/modules/setting" +) + +func IterateMessagesContent(localeContent []byte, onMsgid func(string, string) error) error { + cfg, err := setting.NewConfigProviderForLocale(localeContent) + if err != nil { + return err + } + + for _, section := range cfg.Sections() { + for _, key := range section.Keys() { + var trKey string + // see https://codeberg.org/forgejo/discussions/issues/104 + // https://github.com/WeblateOrg/weblate/issues/10831 + // for an explanation of why "common" is an alternative + if section.Name() == "" || section.Name() == "DEFAULT" || section.Name() == "common" { + trKey = key.Name() + } else { + trKey = section.Name() + "." + key.Name() + } + if err := onMsgid(trKey, key.Value()); err != nil { + return err + } + } + } + + return nil +} + +func iterateMessagesNextInner(onMsgid func(string, string, string) error, data map[string]any, trKey string) error { + for key, value := range data { + fullKey := key + if trKey != "" { + fullKey = trKey + "." + key + } + switch value := value.(type) { + case string: + // Check whether we are adding a plural form to the parent object, or a new nested JSON object. + realKey := trKey + pluralSuffix := "" + + switch key { + case "zero", "one", "two", "few", "many": + pluralSuffix = key + case "other": + // do nothing + default: + realKey = fullKey + } + + if err := onMsgid(realKey, pluralSuffix, value); err != nil { + return err + } + + case map[string]any: + if err := iterateMessagesNextInner(onMsgid, value, fullKey); err != nil { + return err + } + + case nil: + // do nothing + + default: + return fmt.Errorf("Unexpected JSON type: %s - %T", fullKey, value) + } + } + + return nil +} + +func IterateMessagesNextContent(localeContent []byte, onMsgid func(string, string, string) error) error { + var localeData map[string]any + if err := json.Unmarshal(localeContent, &localeData); err != nil { + return err + } + return iterateMessagesNextInner(onMsgid, localeData, "") +} diff --git a/modules/translation/mock.go b/modules/translation/mock.go index 72a15b7438..fc1c6a83fd 100644 --- a/modules/translation/mock.go +++ b/modules/translation/mock.go @@ -6,11 +6,15 @@ package translation import ( "fmt" "html/template" + + "forgejo.org/modules/translation/i18n" ) -// MockLocale provides a mocked locale without any translations +// MockLocale provides a mocked locale without any translations, other than those inserted into MockTranslations by a testcase type MockLocale struct { Lang, LangName string // these fields are used directly in templates: ctx.Locale.Lang + + MockTranslations map[string]string } var _ Locale = (*MockLocale)(nil) @@ -20,11 +24,14 @@ func (l MockLocale) Language() string { } func (l MockLocale) TrString(s string, _ ...any) string { + if val, ok := l.MockTranslations[s]; ok { + return val + } return s } func (l MockLocale) Tr(s string, a ...any) template.HTML { - return template.HTML(s) + return template.HTML(l.TrString(s)) } func (l MockLocale) TrN(cnt any, key1, keyN string, args ...any) template.HTML { @@ -35,6 +42,11 @@ func (l MockLocale) TrPluralString(count any, trKey string, trArgs ...any) templ return template.HTML(trKey) } +// TrPluralStringAllForms implements Locale. +func (l MockLocale) TrPluralStringAllForms(trKey string) ([]string, []string) { + return []string{l.TrString(trKey + i18n.PluralFormSeparator + "one"), l.TrString(trKey + i18n.PluralFormSeparator + "other")}, nil +} + func (l MockLocale) TrSize(s int64) ReadableSize { return ReadableSize{fmt.Sprint(s), ""} } diff --git a/modules/translation/plural_rules.go b/modules/translation/plural_rules.go index b8c00ceef7..59665da255 100644 --- a/modules/translation/plural_rules.go +++ b/modules/translation/plural_rules.go @@ -12,8 +12,8 @@ package translation import ( "strings" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/translation/i18n" + "forgejo.org/modules/log" + "forgejo.org/modules/translation/i18n" ) // The constants refer to indices below in `PluralRules` and also in i18n.js, keep them in sync! @@ -251,3 +251,34 @@ var PluralRules = []i18n.PluralFormRule{ return i18n.PluralFormOther }, } + +var UsedPluralForms = [][]i18n.PluralFormIndex{ + // [ 0] Common 2-form, e.g. English, German + {i18n.PluralFormOne, i18n.PluralFormOther}, + // [ 1] Bengali + {i18n.PluralFormOne, i18n.PluralFormOther}, + // [ 2] Icelandic + {i18n.PluralFormOne, i18n.PluralFormOther}, + // [ 3] Filipino + {i18n.PluralFormOne, i18n.PluralFormOther}, + // [ 4] OneForm + {i18n.PluralFormOther}, + // [ 5] Czech + {i18n.PluralFormOne, i18n.PluralFormFew, i18n.PluralFormOther}, + // [ 6] Russian + {i18n.PluralFormOne, i18n.PluralFormFew, i18n.PluralFormMany}, + // [ 7] Polish + {i18n.PluralFormOne, i18n.PluralFormFew, i18n.PluralFormOther}, + // [ 8] Latvian + {i18n.PluralFormZero, i18n.PluralFormOne, i18n.PluralFormOther}, + // [ 9] Lithuanian + {i18n.PluralFormOne, i18n.PluralFormFew, i18n.PluralFormMany}, + // [10] French + {i18n.PluralFormOne, i18n.PluralFormMany, i18n.PluralFormOther}, + // [11] Catalan + {i18n.PluralFormOne, i18n.PluralFormMany, i18n.PluralFormOther}, + // [12] Slovenian + {i18n.PluralFormOne, i18n.PluralFormTwo, i18n.PluralFormFew, i18n.PluralFormOther}, + // [13] Arabic + {i18n.PluralFormZero, i18n.PluralFormOne, i18n.PluralFormTwo, i18n.PluralFormFew, i18n.PluralFormMany, i18n.PluralFormOther}, +} diff --git a/modules/translation/translation.go b/modules/translation/translation.go index 7be77536ca..17c7cc068b 100644 --- a/modules/translation/translation.go +++ b/modules/translation/translation.go @@ -10,11 +10,11 @@ import ( "strings" "sync" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/options" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/translation/i18n" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/log" + "forgejo.org/modules/options" + "forgejo.org/modules/setting" + "forgejo.org/modules/translation/i18n" + "forgejo.org/modules/util" "github.com/dustin/go-humanize" "golang.org/x/text/language" @@ -27,6 +27,10 @@ type contextKey struct{} var ContextKey any = &contextKey{} // Locale represents an interface to translation +// +// If this gets modified, remember to also adjust +// build/lint-locale-usage/lint-locale-usage.go's InitLocaleTrFunctions(), +// which requires to know in what argument positions `trKey`'s are given. type Locale interface { Language() string TrString(string, ...any) string @@ -42,6 +46,8 @@ type Locale interface { HasKey(trKey string) bool PrettyNumber(v any) string + + TrPluralStringAllForms(trKey string) ([]string, []string) } // LangType represents a lang type @@ -104,8 +110,9 @@ func InitLocales(ctx context.Context) { } } + pluralRuleIndex := GetPluralRuleImpl(setting.Langs[i]) key := "locale_" + setting.Langs[i] + ".ini" - if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], PluralRules[GetPluralRuleImpl(setting.Langs[i])], localeDataBase, localeData[key]); err != nil { + if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], PluralRules[pluralRuleIndex], UsedPluralForms[pluralRuleIndex], localeDataBase, localeData[key]); err != nil { log.Error("Failed to set old-style messages to %s: %v", setting.Langs[i], err) } @@ -320,6 +327,14 @@ func (l *locale) PrettyNumber(v any) string { return l.msgPrinter.Sprintf("%v", number.Decimal(v)) } +func GetPluralRule(l Locale) int { + return GetPluralRuleImpl(l.Language()) +} + +func GetDefaultPluralRule() int { + return GetPluralRuleImpl(i18n.DefaultLocales.GetDefaultLang()) +} + func init() { // prepare a default matcher, especially for tests supportedTags = []language.Tag{language.English} diff --git a/modules/translation/translation_test.go b/modules/translation/translation_test.go index 5b3eefb355..7584490941 100644 --- a/modules/translation/translation_test.go +++ b/modules/translation/translation_test.go @@ -8,7 +8,7 @@ package translation import ( "testing" - "code.gitea.io/gitea/modules/translation/i18n" + "forgejo.org/modules/translation/i18n" "github.com/stretchr/testify/assert" ) @@ -16,19 +16,19 @@ import ( func TestTrSize(t *testing.T) { l := NewLocale("") size := int64(1) - assert.EqualValues(t, "1 munits.data.b", l.TrSize(size).String()) + assert.Equal(t, "1 munits.data.b", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "2 munits.data.kib", l.TrSize(size).String()) + assert.Equal(t, "2 munits.data.kib", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "4 munits.data.mib", l.TrSize(size).String()) + assert.Equal(t, "4 munits.data.mib", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "8 munits.data.gib", l.TrSize(size).String()) + assert.Equal(t, "8 munits.data.gib", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "16 munits.data.tib", l.TrSize(size).String()) + assert.Equal(t, "16 munits.data.tib", l.TrSize(size).String()) size *= 2048 - assert.EqualValues(t, "32 munits.data.pib", l.TrSize(size).String()) + assert.Equal(t, "32 munits.data.pib", l.TrSize(size).String()) size *= 128 - assert.EqualValues(t, "4 munits.data.eib", l.TrSize(size).String()) + assert.Equal(t, "4 munits.data.eib", l.TrSize(size).String()) } func TestPrettyNumber(t *testing.T) { @@ -38,15 +38,15 @@ func TestPrettyNumber(t *testing.T) { allLangMap["id-ID"] = &LangType{Lang: "id-ID", Name: "Bahasa Indonesia"} l := NewLocale("id-ID") - assert.EqualValues(t, "1.000.000", l.PrettyNumber(1000000)) - assert.EqualValues(t, "1.000.000,1", l.PrettyNumber(1000000.1)) - assert.EqualValues(t, "1.000.000", l.PrettyNumber("1000000")) - assert.EqualValues(t, "1.000.000", l.PrettyNumber("1000000.0")) - assert.EqualValues(t, "1.000.000,1", l.PrettyNumber("1000000.1")) + assert.Equal(t, "1.000.000", l.PrettyNumber(1000000)) + assert.Equal(t, "1.000.000,1", l.PrettyNumber(1000000.1)) + assert.Equal(t, "1.000.000", l.PrettyNumber("1000000")) + assert.Equal(t, "1.000.000", l.PrettyNumber("1000000.0")) + assert.Equal(t, "1.000.000,1", l.PrettyNumber("1000000.1")) l = NewLocale("nosuch") - assert.EqualValues(t, "1,000,000", l.PrettyNumber(1000000)) - assert.EqualValues(t, "1,000,000.1", l.PrettyNumber(1000000.1)) + assert.Equal(t, "1,000,000", l.PrettyNumber(1000000)) + assert.Equal(t, "1,000,000.1", l.PrettyNumber(1000000.1)) } func TestGetPluralRule(t *testing.T) { diff --git a/modules/turnstile/turnstile.go b/modules/turnstile/turnstile.go index 38d0233446..31ba256195 100644 --- a/modules/turnstile/turnstile.go +++ b/modules/turnstile/turnstile.go @@ -11,8 +11,8 @@ import ( "net/url" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/json" + "forgejo.org/modules/setting" ) // Response is the structure of JSON returned from API diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go index 212e50c4f1..262feb2b05 100644 --- a/modules/typesniffer/typesniffer.go +++ b/modules/typesniffer/typesniffer.go @@ -11,7 +11,7 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/util" ) // Use at most this many bytes to determine Content Type. @@ -24,6 +24,16 @@ const ( AvifMimeType = "image/avif" // ApplicationOctetStream MIME type of binary files. ApplicationOctetStream = "application/octet-stream" + // GLTFMimeType MIME type of GLTF files. + GLTFMimeType = "model/gltf+json" + // GLBMimeType MIME type of GLB files. + GLBMimeType = "model/gltf-binary" + // OBJMimeType MIME type of OBJ files. + OBJMimeType = "model/obj" + // STLMimeType MIME type of STL files. + STLMimeType = "model/stl" + // 3MFMimeType MIME type of 3MF files. + ThreeMFMimeType = "model/3mf" ) var ( @@ -67,6 +77,36 @@ func (ct SniffedType) IsAudio() bool { return strings.Contains(ct.contentType, "audio/") } +// Is3DModel detects if data is a 3D format +func (ct SniffedType) Is3DModel() bool { + return strings.Contains(ct.contentType, "model/") +} + +// IsGLTFFile detects if data is an SVG image format +func (ct SniffedType) IsGLTF() bool { + return strings.Contains(ct.contentType, GLTFMimeType) +} + +// IsGLBFile detects if data is an GLB image format +func (ct SniffedType) IsGLB() bool { + return strings.Contains(ct.contentType, GLBMimeType) +} + +// IsOBJFile detects if data is an OBJ image format +func (ct SniffedType) IsOBJ() bool { + return strings.Contains(ct.contentType, OBJMimeType) +} + +// IsSTLTextFile detects if data is an STL text format +func (ct SniffedType) IsSTL() bool { + return strings.Contains(ct.contentType, STLMimeType) +} + +// Is3MFFile detects if data is an 3MF image format +func (ct SniffedType) Is3MF() bool { + return strings.Contains(ct.contentType, ThreeMFMimeType) +} + // IsRepresentableAsText returns true if file content can be represented as // plain text or is empty. func (ct SniffedType) IsRepresentableAsText() bool { @@ -75,7 +115,7 @@ func (ct SniffedType) IsRepresentableAsText() bool { // IsBrowsableBinaryType returns whether a non-text type can be displayed in a browser func (ct SniffedType) IsBrowsableBinaryType() bool { - return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio() + return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio() || ct.Is3DModel() } // GetMimeType returns the mime type @@ -135,6 +175,13 @@ func DetectContentType(data []byte) SniffedType { ct = "audio/ogg" // for most cases, it is used as an audio container } } + + // GLTF is unsupported by http.DetectContentType + // hexdump -n 4 -C glTF.glb + if bytes.HasPrefix(data, []byte("glTF")) { + ct = GLBMimeType + } + return SniffedType{ct} } diff --git a/modules/typesniffer/typesniffer_test.go b/modules/typesniffer/typesniffer_test.go index 8d80b4ddb4..176d3658bb 100644 --- a/modules/typesniffer/typesniffer_test.go +++ b/modules/typesniffer/typesniffer_test.go @@ -117,6 +117,14 @@ func TestIsAudio(t *testing.T) { assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2])).IsText()) // test ID3 tag with incomplete UTF8 char } +func TestIsGLB(t *testing.T) { + glb, _ := hex.DecodeString("676c5446") + assert.True(t, DetectContentType(glb).IsGLB()) + assert.True(t, DetectContentType(glb).Is3DModel()) + assert.False(t, DetectContentType([]byte("plain text")).IsGLB()) + assert.False(t, DetectContentType([]byte("plain text")).Is3DModel()) +} + func TestDetectContentTypeFromReader(t *testing.T) { mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") st, err := DetectContentTypeFromReader(bytes.NewReader(mp3)) @@ -145,3 +153,15 @@ func TestDetectContentTypeAvif(t *testing.T) { assert.True(t, st.IsImage()) } + +func TestDetectContentTypeModelGLB(t *testing.T) { + glb, err := hex.DecodeString("676c5446") + require.NoError(t, err) + + st, err := DetectContentTypeFromReader(bytes.NewReader(glb)) + require.NoError(t, err) + + // print st for debugging + assert.Equal(t, "model/gltf-binary", st.GetMimeType()) + assert.True(t, st.IsGLB()) +} diff --git a/modules/updatechecker/update_checker.go b/modules/updatechecker/update_checker.go index 0c93f08d21..b0932ba663 100644 --- a/modules/updatechecker/update_checker.go +++ b/modules/updatechecker/update_checker.go @@ -11,10 +11,10 @@ import ( "net/http" "strings" - "code.gitea.io/gitea/modules/json" - "code.gitea.io/gitea/modules/proxy" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/system" + "forgejo.org/modules/json" + "forgejo.org/modules/proxy" + "forgejo.org/modules/setting" + "forgejo.org/modules/system" "github.com/hashicorp/go-version" ) diff --git a/modules/util/io.go b/modules/util/io.go index 1559b019a0..4c99004c0c 100644 --- a/modules/util/io.go +++ b/modules/util/io.go @@ -4,7 +4,6 @@ package util import ( - "bytes" "errors" "io" ) @@ -20,42 +19,6 @@ func ReadAtMost(r io.Reader, buf []byte) (n int, err error) { return n, err } -// ReadWithLimit reads at most "limit" bytes from r into buf. -// If EOF or ErrUnexpectedEOF occurs while reading, err will be nil. -func ReadWithLimit(r io.Reader, n int) (buf []byte, err error) { - return readWithLimit(r, 1024, n) -} - -func readWithLimit(r io.Reader, batch, limit int) ([]byte, error) { - if limit <= batch { - buf := make([]byte, limit) - n, err := ReadAtMost(r, buf) - if err != nil { - return nil, err - } - return buf[:n], nil - } - res := bytes.NewBuffer(make([]byte, 0, batch)) - bufFix := make([]byte, batch) - eof := false - for res.Len() < limit && !eof { - bufTmp := bufFix - if res.Len()+batch > limit { - bufTmp = bufFix[:limit-res.Len()] - } - n, err := io.ReadFull(r, bufTmp) - if err == io.EOF || err == io.ErrUnexpectedEOF { - eof = true - } else if err != nil { - return nil, err - } - if _, err = res.Write(bufTmp[:n]); err != nil { - return nil, err - } - } - return res.Bytes(), nil -} - // ErrNotEmpty is an error reported when there is a non-empty reader var ErrNotEmpty = errors.New("not-empty") diff --git a/modules/util/io_test.go b/modules/util/io_test.go deleted file mode 100644 index 870e713646..0000000000 --- a/modules/util/io_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package util - -import ( - "bytes" - "errors" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -type readerWithError struct { - buf *bytes.Buffer -} - -func (r *readerWithError) Read(p []byte) (n int, err error) { - if r.buf.Len() < 2 { - return 0, errors.New("test error") - } - return r.buf.Read(p) -} - -func TestReadWithLimit(t *testing.T) { - bs := []byte("0123456789abcdef") - - // normal test - buf, err := readWithLimit(bytes.NewBuffer(bs), 5, 2) - require.NoError(t, err) - assert.Equal(t, []byte("01"), buf) - - buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 5) - require.NoError(t, err) - assert.Equal(t, []byte("01234"), buf) - - buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 6) - require.NoError(t, err) - assert.Equal(t, []byte("012345"), buf) - - buf, err = readWithLimit(bytes.NewBuffer(bs), 5, len(bs)) - require.NoError(t, err) - assert.Equal(t, []byte("0123456789abcdef"), buf) - - buf, err = readWithLimit(bytes.NewBuffer(bs), 5, 100) - require.NoError(t, err) - assert.Equal(t, []byte("0123456789abcdef"), buf) - - // test with error - buf, err = readWithLimit(&readerWithError{bytes.NewBuffer(bs)}, 5, 10) - require.NoError(t, err) - assert.Equal(t, []byte("0123456789"), buf) - - buf, err = readWithLimit(&readerWithError{bytes.NewBuffer(bs)}, 5, 100) - require.ErrorContains(t, err, "test error") - assert.Empty(t, buf) - - // test public function - buf, err = ReadWithLimit(bytes.NewBuffer(bs), 2) - require.NoError(t, err) - assert.Equal(t, []byte("01"), buf) - - buf, err = ReadWithLimit(bytes.NewBuffer(bs), 9999999) - require.NoError(t, err) - assert.Equal(t, []byte("0123456789abcdef"), buf) -} diff --git a/modules/util/paginate_test.go b/modules/util/paginate_test.go index 6e69dd19cc..3dc5095071 100644 --- a/modules/util/paginate_test.go +++ b/modules/util/paginate_test.go @@ -13,23 +13,23 @@ func TestPaginateSlice(t *testing.T) { stringSlice := []string{"a", "b", "c", "d", "e"} result, ok := PaginateSlice(stringSlice, 1, 2).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{"a", "b"}, result) + assert.Equal(t, []string{"a", "b"}, result) result, ok = PaginateSlice(stringSlice, 100, 2).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{}, result) + assert.Equal(t, []string{}, result) result, ok = PaginateSlice(stringSlice, 3, 2).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{"e"}, result) + assert.Equal(t, []string{"e"}, result) result, ok = PaginateSlice(stringSlice, 1, 0).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{"a", "b", "c", "d", "e"}, result) + assert.Equal(t, []string{"a", "b", "c", "d", "e"}, result) result, ok = PaginateSlice(stringSlice, 1, -1).([]string) assert.True(t, ok) - assert.EqualValues(t, []string{"a", "b", "c", "d", "e"}, result) + assert.Equal(t, []string{"a", "b", "c", "d", "e"}, result) type Test struct { Val int @@ -38,9 +38,9 @@ func TestPaginateSlice(t *testing.T) { testVar := []*Test{{Val: 2}, {Val: 3}, {Val: 4}} testVar, ok = PaginateSlice(testVar, 1, 50).([]*Test) assert.True(t, ok) - assert.EqualValues(t, []*Test{{Val: 2}, {Val: 3}, {Val: 4}}, testVar) + assert.Equal(t, []*Test{{Val: 2}, {Val: 3}, {Val: 4}}, testVar) testVar, ok = PaginateSlice(testVar, 2, 2).([]*Test) assert.True(t, ok) - assert.EqualValues(t, []*Test{{Val: 4}}, testVar) + assert.Equal(t, []*Test{{Val: 4}}, testVar) } diff --git a/modules/util/path.go b/modules/util/path.go index 9039f27cbf..3ef3925c49 100644 --- a/modules/util/path.go +++ b/modules/util/path.go @@ -34,9 +34,10 @@ func PathJoinRel(elem ...string) string { elems[i] = path.Clean("/" + e) } p := path.Join(elems...) - if p == "" { + switch p { + case "": return "" - } else if p == "/" { + case "/": return "." } return p[1:] diff --git a/modules/util/remove.go b/modules/util/remove.go index 2a65a6b0aa..b07a48bee4 100644 --- a/modules/util/remove.go +++ b/modules/util/remove.go @@ -4,7 +4,9 @@ package util import ( + "io/fs" "os" + "path/filepath" "syscall" "time" ) @@ -32,10 +34,48 @@ func Remove(name string) error { return err } -// RemoveAll removes the named file or (empty) directory with at most 5 attempts. +// MakeWritable recursively makes the named directory writable. +func MakeWritable(name string) error { + return filepath.WalkDir(name, func(path string, d fs.DirEntry, err error) error { + // NB: this is called WalkDir but it works on a single file too + if err == nil { + info, err := d.Info() + if err != nil { + return err + } + + // Don't try chmod'ing symlinks (will fail with broken symlinks) + if info.Mode()&os.ModeSymlink != os.ModeSymlink { + // 0200 == u+w, in octal unix permission notation + err = os.Chmod(path, info.Mode()|0o200) + if err != nil { + return err + } + } + } + return nil + }) +} + +// RemoveAll removes the named file or directory with at most 5 attempts. func RemoveAll(name string) error { var err error + for i := 0; i < 5; i++ { + // Do chmod -R +w to help ensure the removal succeeds. + // In particular, in the git-annex case, this handles + // https://git-annex.branchable.com/internals/lockdown/ : + // + // > (The only bad consequence of this is that rm -rf .git + // > doesn't work unless you first run chmod -R +w .git) + + err = MakeWritable(name) + if err != nil { + // try again + <-time.After(100 * time.Millisecond) + continue + } + err = os.RemoveAll(name) if err == nil { break diff --git a/modules/util/rotatingfilewriter/writer.go b/modules/util/rotatingfilewriter/writer.go index c595f49c49..ff234eea93 100644 --- a/modules/util/rotatingfilewriter/writer.go +++ b/modules/util/rotatingfilewriter/writer.go @@ -14,8 +14,8 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/graceful/releasereopen" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/graceful/releasereopen" + "forgejo.org/modules/util" ) type Options struct { diff --git a/modules/util/string.go b/modules/util/string.go index cf50f591c6..ca3d43ec6e 100644 --- a/modules/util/string.go +++ b/modules/util/string.go @@ -95,3 +95,25 @@ func UnsafeBytesToString(b []byte) string { func UnsafeStringToBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s)) } + +// AsciiEqualFold is taken from Golang, but reimplemented here, since the original is not exposed to public +// Taken from: https://cs.opensource.google/go/go/+/refs/tags/go1.24.4:src/net/http/internal/ascii/print.go +func ASCIIEqualFold(s, t string) bool { + if len(s) != len(t) { + return false + } + for i := 0; i < len(s); i++ { + if ASCIILower(s[i]) != ASCIILower(t[i]) { + return false + } + } + return true +} + +// AsciiLower returns the ASCII lowercase version of b. +func ASCIILower(b byte) byte { + if 'A' <= b && b <= 'Z' { + return b + ('a' - 'A') + } + return b +} diff --git a/modules/util/string_test.go b/modules/util/string_test.go index 0a4a8bbcfb..1012ab32a4 100644 --- a/modules/util/string_test.go +++ b/modules/util/string_test.go @@ -45,3 +45,29 @@ func TestToSnakeCase(t *testing.T) { assert.Equal(t, expected, ToSnakeCase(input)) } } + +func TestASCIIEqualFold(t *testing.T) { + cases := map[string]struct { + First string + Second string + Expected bool + }{ + "Empty String": {First: "", Second: "", Expected: true}, + "Single Letter Ident": {First: "h", Second: "h", Expected: true}, + "Single Letter Equal": {First: "h", Second: "H", Expected: true}, + "Single Letter Unequal": {First: "h", Second: "g", Expected: false}, + "Simple Match Ident": {First: "someString", Second: "someString", Expected: true}, + "Simple Match Equal": {First: "someString", Second: "someSTRIng", Expected: true}, + "Simple Match Unequal": {First: "someString", Second: "sameString", Expected: false}, + "Different Length": {First: "abcdef", Second: "abcdefg", Expected: false}, + "Unicode Kelvin": {First: "ghijklm", Second: "GHIJ\u212ALM", Expected: false}, + } + + for name := range cases { + c := cases[name] + t.Run(name, func(t *testing.T) { + Actual := ASCIIEqualFold(c.First, c.Second) + assert.Equal(t, c.Expected, Actual) + }) + } +} diff --git a/modules/util/truncate.go b/modules/util/truncate.go index f2edbdc673..7207a89177 100644 --- a/modules/util/truncate.go +++ b/modules/util/truncate.go @@ -54,3 +54,12 @@ func SplitTrimSpace(input, sep string) []string { return stringList } + +// TruncateRunes returns a truncated string with given rune limit, +// it returns input string if its rune length doesn't exceed the limit. +func TruncateRunes(str string, limit int) string { + if utf8.RuneCountInString(str) < limit { + return str + } + return string([]rune(str)[:limit]) +} diff --git a/modules/util/truncate_test.go b/modules/util/truncate_test.go index dfe1230fd4..8187b13eb2 100644 --- a/modules/util/truncate_test.go +++ b/modules/util/truncate_test.go @@ -44,3 +44,18 @@ func TestSplitString(t *testing.T) { } test(tc, SplitStringAtByteN) } + +func TestTruncateRunes(t *testing.T) { + assert.Empty(t, TruncateRunes("", 0)) + assert.Empty(t, TruncateRunes("", 1)) + + assert.Empty(t, TruncateRunes("ab", 0)) + assert.Equal(t, "a", TruncateRunes("ab", 1)) + assert.Equal(t, "ab", TruncateRunes("ab", 2)) + assert.Equal(t, "ab", TruncateRunes("ab", 3)) + + assert.Empty(t, TruncateRunes("测试", 0)) + assert.Equal(t, "测", TruncateRunes("测试", 1)) + assert.Equal(t, "测试", TruncateRunes("测试", 2)) + assert.Equal(t, "测试", TruncateRunes("测试", 3)) +} diff --git a/modules/util/util.go b/modules/util/util.go index 88ac07567b..548fd1e90b 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -14,22 +14,11 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/optional" - "golang.org/x/crypto/ssh" "golang.org/x/text/cases" "golang.org/x/text/language" ) -// OptionalBoolParse get the corresponding optional.Option[bool] of a string using strconv.ParseBool -func OptionalBoolParse(s string) optional.Option[bool] { - v, e := strconv.ParseBool(s) - if e != nil { - return optional.None[bool]() - } - return optional.Some(v) -} - // IsEmptyString checks if the provided string is empty func IsEmptyString(s string) bool { return len(strings.TrimSpace(s)) == 0 @@ -99,10 +88,16 @@ func CryptoRandomString(length int64) (string, error) { // CryptoRandomBytes generates `length` crypto bytes // This differs from CryptoRandomString, as each byte in CryptoRandomString is generated by [0,61] range // This function generates totally random bytes, each byte is generated by [0,255] range -func CryptoRandomBytes(length int64) ([]byte, error) { +func CryptoRandomBytes(length int64) []byte { + // crypto/rand.Read is documented to never return a error. + // https://go.dev/issue/66821 buf := make([]byte, length) - _, err := rand.Read(buf) - return buf, err + n, err := rand.Read(buf) + if err != nil || n != int(length) { + panic(err) + } + + return buf } // ToUpperASCII returns s with all ASCII letters mapped to their upper case. diff --git a/modules/util/util_test.go b/modules/util/util_test.go index 7344c8fbb7..21988fd0f8 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -11,9 +11,8 @@ import ( "strings" "testing" - "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/test" + "forgejo.org/modules/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -164,36 +163,21 @@ func Test_RandomString(t *testing.T) { } func Test_RandomBytes(t *testing.T) { - bytes1, err := util.CryptoRandomBytes(32) - require.NoError(t, err) - - bytes2, err := util.CryptoRandomBytes(32) - require.NoError(t, err) + bytes1 := util.CryptoRandomBytes(32) + bytes2 := util.CryptoRandomBytes(32) + assert.Len(t, bytes1, 32) + assert.Len(t, bytes2, 32) assert.NotEqual(t, bytes1, bytes2) - bytes3, err := util.CryptoRandomBytes(256) - require.NoError(t, err) - - bytes4, err := util.CryptoRandomBytes(256) - require.NoError(t, err) + bytes3 := util.CryptoRandomBytes(256) + bytes4 := util.CryptoRandomBytes(256) + assert.Len(t, bytes3, 256) + assert.Len(t, bytes4, 256) assert.NotEqual(t, bytes3, bytes4) } -func TestOptionalBoolParse(t *testing.T) { - assert.Equal(t, optional.None[bool](), util.OptionalBoolParse("")) - assert.Equal(t, optional.None[bool](), util.OptionalBoolParse("x")) - - assert.Equal(t, optional.Some(false), util.OptionalBoolParse("0")) - assert.Equal(t, optional.Some(false), util.OptionalBoolParse("f")) - assert.Equal(t, optional.Some(false), util.OptionalBoolParse("False")) - - assert.Equal(t, optional.Some(true), util.OptionalBoolParse("1")) - assert.Equal(t, optional.Some(true), util.OptionalBoolParse("t")) - assert.Equal(t, optional.Some(true), util.OptionalBoolParse("True")) -} - // Test case for any function which accepts and returns a single string. type StringTest struct { in, out string @@ -272,8 +256,8 @@ func TestGeneratingEd25519Keypair(t *testing.T) { publicKey, privateKey, err := util.GenerateSSHKeypair() require.NoError(t, err) - assert.EqualValues(t, testPublicKey, string(publicKey)) - assert.EqualValues(t, testPrivateKey, string(privateKey)) + assert.Equal(t, testPublicKey, string(publicKey)) + assert.Equal(t, testPrivateKey, string(privateKey)) } func TestOptionalArg(t *testing.T) { diff --git a/modules/validation/binding.go b/modules/validation/binding.go index 006fbfafc1..f4f82278bd 100644 --- a/modules/validation/binding.go +++ b/modules/validation/binding.go @@ -8,9 +8,9 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/auth" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/auth" + "forgejo.org/modules/git" + "forgejo.org/modules/util" "code.forgejo.org/go-chi/binding" "github.com/gobwas/glob" diff --git a/modules/validation/email.go b/modules/validation/email.go index 326e93378a..8e1ffc203d 100644 --- a/modules/validation/email.go +++ b/modules/validation/email.go @@ -8,11 +8,10 @@ package validation import ( "fmt" "net/mail" - "regexp" "strings" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" + "forgejo.org/modules/setting" + "forgejo.org/modules/util" "github.com/gobwas/glob" ) @@ -20,21 +19,6 @@ import ( // ErrEmailNotActivated e-mail address has not been activated error var ErrEmailNotActivated = util.NewInvalidArgumentErrorf("e-mail address has not been activated") -// ErrEmailCharIsNotSupported e-mail address contains unsupported character -type ErrEmailCharIsNotSupported struct { - Email string -} - -// IsErrEmailCharIsNotSupported checks if an error is an ErrEmailCharIsNotSupported -func IsErrEmailCharIsNotSupported(err error) bool { - _, ok := err.(ErrEmailCharIsNotSupported) - return ok -} - -func (err ErrEmailCharIsNotSupported) Error() string { - return fmt.Sprintf("e-mail address contains unsupported character [email: %s]", err.Email) -} - // ErrEmailInvalid represents an error where the email address does not comply with RFC 5322 // or has a leading '-' character type ErrEmailInvalid struct { @@ -55,8 +39,6 @@ func (err ErrEmailInvalid) Unwrap() error { return util.ErrInvalidArgument } -var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") - // check if email is a valid address with allowed domain func ValidateEmail(email string) error { if err := validateEmailBasic(email); err != nil { @@ -77,15 +59,12 @@ func validateEmailBasic(email string) error { return ErrEmailInvalid{email} } - if !emailRegexp.MatchString(email) { - return ErrEmailCharIsNotSupported{email} - } - - if email[0] == '-' { + parsedAddress, err := mail.ParseAddress(email) + if err != nil { return ErrEmailInvalid{email} } - if _, err := mail.ParseAddress(email); err != nil { + if parsedAddress.Name != "" { return ErrEmailInvalid{email} } diff --git a/modules/validation/email_test.go b/modules/validation/email_test.go index ffdc6fd4ee..b7ee766ddb 100644 --- a/modules/validation/email_test.go +++ b/modules/validation/email_test.go @@ -35,7 +35,7 @@ func TestEmailAddressValidate(t *testing.T) { `first|last@iana.org`: nil, `first}last@iana.org`: nil, `first~last@iana.org`: nil, - `first;last@iana.org`: ErrEmailCharIsNotSupported{`first;last@iana.org`}, + `first;last@iana.org`: ErrEmailInvalid{`first;last@iana.org`}, ".233@qq.com": ErrEmailInvalid{".233@qq.com"}, "!233@qq.com": nil, "#233@qq.com": nil, @@ -45,7 +45,7 @@ func TestEmailAddressValidate(t *testing.T) { "'233@qq.com": nil, "*233@qq.com": nil, "+233@qq.com": nil, - "-233@qq.com": ErrEmailInvalid{"-233@qq.com"}, + "-233@qq.com": nil, "/233@qq.com": nil, "=233@qq.com": nil, "?233@qq.com": nil, @@ -56,13 +56,14 @@ func TestEmailAddressValidate(t *testing.T) { "|233@qq.com": nil, "}233@qq.com": nil, "~233@qq.com": nil, - ";233@qq.com": ErrEmailCharIsNotSupported{";233@qq.com"}, - "Foo ": ErrEmailCharIsNotSupported{"Foo "}, - string([]byte{0xE2, 0x84, 0xAA}): ErrEmailCharIsNotSupported{string([]byte{0xE2, 0x84, 0xAA})}, + "\"~@ \"@famfo.xyz": nil, + "Foo ": ErrEmailInvalid{"Foo "}, + ";233@qq.com": ErrEmailInvalid{";233@qq.com"}, + string([]byte{0xE2, 0x84, 0xAA}): ErrEmailInvalid{string([]byte{0xE2, 0x84, 0xAA})}, } for kase, err := range kases { t.Run(kase, func(t *testing.T) { - assert.EqualValues(t, err, ValidateEmail(kase)) + assert.Equal(t, err, ValidateEmail(kase)) }) } } diff --git a/modules/validation/helpers.go b/modules/validation/helpers.go index 2f88fcbc60..4b28dead03 100644 --- a/modules/validation/helpers.go +++ b/modules/validation/helpers.go @@ -9,7 +9,7 @@ import ( "regexp" "strings" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`) @@ -75,6 +75,11 @@ func IsValidExternalURL(uri string) bool { return true } +// IsValidReleaseAssetURL checks if the URL is valid for external release assets +func IsValidReleaseAssetURL(uri string) bool { + return IsValidURL(uri) +} + // IsValidExternalTrackerURLFormat checks if URL matches required syntax for external trackers func IsValidExternalTrackerURLFormat(uri string) bool { if !IsValidExternalURL(uri) { diff --git a/modules/validation/helpers_test.go b/modules/validation/helpers_test.go index a1bdf2a29c..7e32184691 100644 --- a/modules/validation/helpers_test.go +++ b/modules/validation/helpers_test.go @@ -6,7 +6,8 @@ package validation import ( "testing" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" "github.com/stretchr/testify/assert" ) @@ -47,7 +48,7 @@ func Test_IsValidURL(t *testing.T) { } func Test_IsValidExternalURL(t *testing.T) { - setting.AppURL = "https://try.gitea.io/" + defer test.MockVariableValue(&setting.AppURL, "https://code.forgejo.org/")() cases := []struct { description string @@ -56,7 +57,7 @@ func Test_IsValidExternalURL(t *testing.T) { }{ { description: "Current instance URL", - url: "https://try.gitea.io/test", + url: "https://code.forgejo.org/test", valid: true, }, { @@ -66,7 +67,7 @@ func Test_IsValidExternalURL(t *testing.T) { }, { description: "Current instance API URL", - url: "https://try.gitea.io/api/v1/user/follow", + url: "https://code.forgejo.org/api/v1/user/follow", valid: false, }, { @@ -89,7 +90,7 @@ func Test_IsValidExternalURL(t *testing.T) { } func Test_IsValidExternalTrackerURLFormat(t *testing.T) { - setting.AppURL = "https://try.gitea.io/" + defer test.MockVariableValue(&setting.AppURL, "https://code.forgejo.org/")() cases := []struct { description string @@ -156,7 +157,8 @@ func Test_IsValidExternalTrackerURLFormat(t *testing.T) { } func TestIsValidUsernameAllowDots(t *testing.T) { - setting.Service.AllowDotsInUsernames = true + defer test.MockVariableValue(&setting.Service.AllowDotsInUsernames, true)() + tests := []struct { arg string want bool @@ -188,10 +190,7 @@ func TestIsValidUsernameAllowDots(t *testing.T) { } func TestIsValidUsernameBanDots(t *testing.T) { - setting.Service.AllowDotsInUsernames = false - defer func() { - setting.Service.AllowDotsInUsernames = true - }() + defer test.MockVariableValue(&setting.Service.AllowDotsInUsernames, false)() tests := []struct { arg string diff --git a/modules/validation/validatable.go b/modules/validation/validatable.go index 94b5cc135c..4500f6e53d 100644 --- a/modules/validation/validatable.go +++ b/modules/validation/validatable.go @@ -1,5 +1,4 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// Copyright 2023 The Forgejo Authors. All rights reserved. +// Copyright 2023, 2024, 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package validation @@ -10,7 +9,9 @@ import ( "strings" "unicode/utf8" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/timeutil" + + ap "github.com/go-ap/activitypub" ) // ErrNotValid represents an validation error @@ -33,15 +34,22 @@ type Validateable interface { } func IsValid(v Validateable) (bool, error) { - if err := v.Validate(); len(err) > 0 { + if valdationErrors := v.Validate(); len(valdationErrors) > 0 { typeof := reflect.TypeOf(v) - errString := strings.Join(err, "\n") + errString := strings.Join(valdationErrors, "\n") return false, ErrNotValid{fmt.Sprint(typeof, ": ", errString)} } return true, nil } +func ValidateIDExists(value ap.Item, name string) []string { + if value == nil { + return []string{fmt.Sprintf("%v should not be nil", name)} + } + return ValidateNotEmpty(value.GetID().String(), name) +} + func ValidateNotEmpty(value any, name string) []string { isValid := true switch v := value.(type) { @@ -53,6 +61,10 @@ func ValidateNotEmpty(value any, name string) []string { if v.IsZero() { isValid = false } + case uint16: + if v == 0 { + isValid = false + } case int64: if v == 0 { isValid = false @@ -80,5 +92,5 @@ func ValidateOneOf(value any, allowed []any, name string) []string { return []string{} } } - return []string{fmt.Sprintf("Value %v is not contained in allowed values %v", value, allowed)} + return []string{fmt.Sprintf("Field %s contains the value %v, which is not in allowed subset %v", name, value, allowed)} } diff --git a/modules/validation/validatable_test.go b/modules/validation/validatable_test.go index 919f5a3183..1fe407b343 100644 --- a/modules/validation/validatable_test.go +++ b/modules/validation/validatable_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. +// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package validation @@ -6,7 +6,10 @@ package validation import ( "testing" - "code.gitea.io/gitea/modules/timeutil" + "forgejo.org/modules/timeutil" + + ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" ) type Sut struct { @@ -37,33 +40,50 @@ func Test_IsValid(t *testing.T) { func Test_ValidateNotEmpty_ForString(t *testing.T) { sut := "" - if len(ValidateNotEmpty(sut, "dummyField")) == 0 { - t.Errorf("sut should be invalid") - } + res := ValidateNotEmpty(sut, "dummyField") + assert.Len(t, res, 1) + sut = "not empty" - if res := ValidateNotEmpty(sut, "dummyField"); len(res) > 0 { - t.Errorf("sut should be valid but was %q", res) - } + res = ValidateNotEmpty(sut, "dummyField") + assert.Empty(t, res, 0) } func Test_ValidateNotEmpty_ForTimestamp(t *testing.T) { sut := timeutil.TimeStamp(0) - if res := ValidateNotEmpty(sut, "dummyField"); len(res) == 0 { - t.Errorf("sut should be invalid") - } + res := ValidateNotEmpty(sut, "dummyField") + assert.Len(t, res, 1) + sut = timeutil.TimeStampNow() - if res := ValidateNotEmpty(sut, "dummyField"); len(res) > 0 { - t.Errorf("sut should be valid but was %q", res) + res = ValidateNotEmpty(sut, "dummyField") + assert.Empty(t, res, 0) +} + +func Test_ValidateIDExists_ForItem(t *testing.T) { + sut := ap.Activity{ + Object: nil, } + res := ValidateIDExists(sut.Object, "dummyField") + assert.Len(t, res, 1) + + sut = ap.Activity{ + Object: ap.IRI(""), + } + res = ValidateIDExists(sut.Object, "dummyField") + assert.Len(t, res, 1) + + sut = ap.Activity{ + Object: ap.IRI("https://dummy.link/id"), + } + res = ValidateIDExists(sut.Object, "dummyField") + assert.Empty(t, res, 0) } func Test_ValidateMaxLen(t *testing.T) { sut := "0123456789" - if len(ValidateMaxLen(sut, 9, "dummyField")) == 0 { - t.Errorf("sut should be invalid") - } + res := ValidateMaxLen(sut, 9, "dummyField") + assert.Len(t, res, 1) + sut = "0123456789" - if res := ValidateMaxLen(sut, 11, "dummyField"); len(res) > 0 { - t.Errorf("sut should be valid but was %q", res) - } + res = ValidateMaxLen(sut, 11, "dummyField") + assert.Empty(t, res, 0) } diff --git a/modules/web/handler.go b/modules/web/handler.go index 728cc5a160..4a7f28b1fa 100644 --- a/modules/web/handler.go +++ b/modules/web/handler.go @@ -9,9 +9,9 @@ import ( "net/http" "reflect" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/web/routing" - "code.gitea.io/gitea/modules/web/types" + "forgejo.org/modules/log" + "forgejo.org/modules/web/routing" + "forgejo.org/modules/web/types" ) var responseStatusProviders = map[reflect.Type]func(req *http.Request) types.ResponseStatusProvider{} diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go index 4e43f6d4b3..123eb29015 100644 --- a/modules/web/middleware/binding.go +++ b/modules/web/middleware/binding.go @@ -8,10 +8,10 @@ import ( "reflect" "strings" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/translation" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/validation" + "forgejo.org/modules/setting" + "forgejo.org/modules/translation" + "forgejo.org/modules/util" + "forgejo.org/modules/validation" "code.forgejo.org/go-chi/binding" ) @@ -79,6 +79,11 @@ func GetInclude(field reflect.StructField) string { return getRuleBody(field, "Include(") } +func GetRange(field reflect.StructField) (string, string) { + min, max, _ := strings.Cut(getRuleBody(field, "Range("), ",") + return min, max +} + // Validate populates the data with validation error (if any). func Validate(errs binding.Errors, data map[string]any, f any, l translation.Locale) binding.Errors { if errs.Len() == 0 { @@ -131,6 +136,9 @@ func Validate(errs binding.Errors, data map[string]any, f any, l translation.Loc data["ErrorMsg"] = trName + l.TrString("form.url_error", errs[0].Message) case binding.ERR_INCLUDE: data["ErrorMsg"] = trName + l.TrString("form.include_error", GetInclude(field)) + case binding.ERR_RANGE: + min, max := GetRange(field) + data["ErrorMsg"] = trName + l.TrString("alert.range_error", l.PrettyNumber(min), l.PrettyNumber(max)) case validation.ErrGlobPattern: data["ErrorMsg"] = trName + l.TrString("form.glob_pattern_error", errs[0].Message) case validation.ErrRegexPattern: diff --git a/modules/web/middleware/cookie.go b/modules/web/middleware/cookie.go index f2d25f5b1c..3bfaeabe69 100644 --- a/modules/web/middleware/cookie.go +++ b/modules/web/middleware/cookie.go @@ -9,8 +9,8 @@ import ( "net/url" "strings" - "code.gitea.io/gitea/modules/session" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/session" + "forgejo.org/modules/setting" ) // SetRedirectToCookie convenience function to set the RedirectTo cookie consistently diff --git a/modules/web/middleware/data.go b/modules/web/middleware/data.go index 08d83f94be..4603e64052 100644 --- a/modules/web/middleware/data.go +++ b/modules/web/middleware/data.go @@ -7,7 +7,7 @@ import ( "context" "time" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // ContextDataStore represents a data store diff --git a/modules/web/middleware/locale.go b/modules/web/middleware/locale.go index 9653a6f349..565fb2f502 100644 --- a/modules/web/middleware/locale.go +++ b/modules/web/middleware/locale.go @@ -6,8 +6,8 @@ package middleware import ( "net/http" - "code.gitea.io/gitea/modules/translation" - "code.gitea.io/gitea/modules/translation/i18n" + "forgejo.org/modules/translation" + "forgejo.org/modules/translation/i18n" "golang.org/x/text/language" ) diff --git a/modules/web/route.go b/modules/web/route.go index eda6871d85..046c9f4ba7 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -7,7 +7,7 @@ import ( "net/http" "strings" - "code.gitea.io/gitea/modules/web/middleware" + "forgejo.org/modules/web/middleware" "code.forgejo.org/go-chi/binding" "github.com/go-chi/chi/v5" diff --git a/modules/web/route_test.go b/modules/web/route_test.go index d8015d6e0d..44626ec141 100644 --- a/modules/web/route_test.go +++ b/modules/web/route_test.go @@ -23,17 +23,17 @@ func TestRoute1(t *testing.T) { r := NewRoute() r.Get("/{username}/{reponame}/{type:issues|pulls}", func(resp http.ResponseWriter, req *http.Request) { username := chi.URLParam(req, "username") - assert.EqualValues(t, "gitea", username) + assert.Equal(t, "gitea", username) reponame := chi.URLParam(req, "reponame") - assert.EqualValues(t, "gitea", reponame) + assert.Equal(t, "gitea", reponame) tp := chi.URLParam(req, "type") - assert.EqualValues(t, "issues", tp) + assert.Equal(t, "issues", tp) }) req, err := http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) + assert.Equal(t, http.StatusOK, recorder.Code) } func TestRoute2(t *testing.T) { @@ -48,23 +48,23 @@ func TestRoute2(t *testing.T) { r.Group("", func() { r.Get("/{type:issues|pulls}", func(resp http.ResponseWriter, req *http.Request) { username := chi.URLParam(req, "username") - assert.EqualValues(t, "gitea", username) + assert.Equal(t, "gitea", username) reponame := chi.URLParam(req, "reponame") - assert.EqualValues(t, "gitea", reponame) + assert.Equal(t, "gitea", reponame) tp := chi.URLParam(req, "type") - assert.EqualValues(t, "issues", tp) + assert.Equal(t, "issues", tp) hit = 0 }) r.Get("/{type:issues|pulls}/{index}", func(resp http.ResponseWriter, req *http.Request) { username := chi.URLParam(req, "username") - assert.EqualValues(t, "gitea", username) + assert.Equal(t, "gitea", username) reponame := chi.URLParam(req, "reponame") - assert.EqualValues(t, "gitea", reponame) + assert.Equal(t, "gitea", reponame) tp := chi.URLParam(req, "type") - assert.EqualValues(t, "issues", tp) + assert.Equal(t, "issues", tp) index := chi.URLParam(req, "index") - assert.EqualValues(t, "1", index) + assert.Equal(t, "1", index) hit = 1 }) }, func(resp http.ResponseWriter, req *http.Request) { @@ -77,11 +77,11 @@ func TestRoute2(t *testing.T) { r.Group("/issues/{index}", func() { r.Get("/view", func(resp http.ResponseWriter, req *http.Request) { username := chi.URLParam(req, "username") - assert.EqualValues(t, "gitea", username) + assert.Equal(t, "gitea", username) reponame := chi.URLParam(req, "reponame") - assert.EqualValues(t, "gitea", reponame) + assert.Equal(t, "gitea", reponame) index := chi.URLParam(req, "index") - assert.EqualValues(t, "1", index) + assert.Equal(t, "1", index) hit = 2 }) }) @@ -90,26 +90,26 @@ func TestRoute2(t *testing.T) { req, err := http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 0, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 0, hit) req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 1, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 1, hit) req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1?stop=100", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 100, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 100, hit) req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1/view", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 2, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 2, hit) } func TestRoute3(t *testing.T) { @@ -150,30 +150,30 @@ func TestRoute3(t *testing.T) { req, err := http.NewRequest("GET", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 0, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 0, hit) req, err = http.NewRequest("POST", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code, http.StatusOK) - assert.EqualValues(t, 1, hit) + assert.Equal(t, http.StatusOK, recorder.Code, http.StatusOK) + assert.Equal(t, 1, hit) req, err = http.NewRequest("GET", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 2, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 2, hit) req, err = http.NewRequest("PATCH", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 3, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 3, hit) req, err = http.NewRequest("DELETE", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil) require.NoError(t, err) r.ServeHTTP(recorder, req) - assert.EqualValues(t, http.StatusOK, recorder.Code) - assert.EqualValues(t, 4, hit) + assert.Equal(t, http.StatusOK, recorder.Code) + assert.Equal(t, 4, hit) } diff --git a/modules/web/routemock.go b/modules/web/routemock.go index cb41f63b91..33d2ad06eb 100644 --- a/modules/web/routemock.go +++ b/modules/web/routemock.go @@ -6,7 +6,7 @@ package web import ( "net/http" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" ) // MockAfterMiddlewares is a general mock point, it's between middlewares and the handler diff --git a/modules/web/routemock_test.go b/modules/web/routemock_test.go index cd99b99323..46f0296046 100644 --- a/modules/web/routemock_test.go +++ b/modules/web/routemock_test.go @@ -8,7 +8,7 @@ import ( "net/http/httptest" "testing" - "code.gitea.io/gitea/modules/setting" + "forgejo.org/modules/setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -35,9 +35,9 @@ func TestRouteMock(t *testing.T) { require.NoError(t, err) r.ServeHTTP(recorder, req) assert.Len(t, recorder.Header(), 3) - assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1")) - assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2")) - assert.EqualValues(t, "h", recorder.Header().Get("X-Test-Handler")) + assert.Equal(t, "m1", recorder.Header().Get("X-Test-Middleware1")) + assert.Equal(t, "m2", recorder.Header().Get("X-Test-Middleware2")) + assert.Equal(t, "h", recorder.Header().Get("X-Test-Handler")) RouteMockReset() // mock at "mock-point" @@ -50,8 +50,8 @@ func TestRouteMock(t *testing.T) { require.NoError(t, err) r.ServeHTTP(recorder, req) assert.Len(t, recorder.Header(), 2) - assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1")) - assert.EqualValues(t, "a", recorder.Header().Get("X-Test-MockPoint")) + assert.Equal(t, "m1", recorder.Header().Get("X-Test-Middleware1")) + assert.Equal(t, "a", recorder.Header().Get("X-Test-MockPoint")) RouteMockReset() // mock at MockAfterMiddlewares @@ -64,8 +64,8 @@ func TestRouteMock(t *testing.T) { require.NoError(t, err) r.ServeHTTP(recorder, req) assert.Len(t, recorder.Header(), 3) - assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1")) - assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2")) - assert.EqualValues(t, "b", recorder.Header().Get("X-Test-MockPoint")) + assert.Equal(t, "m1", recorder.Header().Get("X-Test-Middleware1")) + assert.Equal(t, "m2", recorder.Header().Get("X-Test-Middleware2")) + assert.Equal(t, "b", recorder.Header().Get("X-Test-MockPoint")) RouteMockReset() } diff --git a/modules/web/routing/logger.go b/modules/web/routing/logger.go index 5f3a7592af..8fd24c9733 100644 --- a/modules/web/routing/logger.go +++ b/modules/web/routing/logger.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/web/types" + "forgejo.org/modules/log" + "forgejo.org/modules/web/types" ) // NewLoggerHandler is a handler that will log routing to the router log taking account of @@ -90,7 +90,7 @@ func logPrinter(logger log.Logger) func(trigger Event, record *requestRecord) { status = v.WrittenStatus() } logf := logger.Info - if strings.HasPrefix(req.RequestURI, "/assets/") { + if strings.HasPrefix(req.RequestURI, "/assets/") || req.RequestURI == "/api/actions/runner.v1.RunnerService/FetchTask" || req.RequestURI == "/api/actions/runner.v1.RunnerService/UpdateLog" { logf = logger.Trace } message := completedMessage diff --git a/modules/web/routing/logger_manager.go b/modules/web/routing/logger_manager.go index aa25ec3a27..4b12419b44 100644 --- a/modules/web/routing/logger_manager.go +++ b/modules/web/routing/logger_manager.go @@ -9,8 +9,8 @@ import ( "sync" "time" - "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/process" + "forgejo.org/modules/graceful" + "forgejo.org/modules/process" ) // Event indicates when the printer is triggered diff --git a/modules/webhook/structs.go b/modules/webhook/structs.go index 927a91a74c..6c0161bfc2 100644 --- a/modules/webhook/structs.go +++ b/modules/webhook/structs.go @@ -4,6 +4,7 @@ package webhook // HookEvents is a set of web hook events +// update TestCreateWebhook in models/webhook/webhook_test.go when adding or changing values here type HookEvents struct { Create bool `json:"create"` Delete bool `json:"delete"` @@ -26,9 +27,12 @@ type HookEvents struct { Repository bool `json:"repository"` Release bool `json:"release"` Package bool `json:"package"` + ActionRunFailure bool `json:"action_run_failure"` + ActionRunRecover bool `json:"action_run_recover"` + ActionRunSuccess bool `json:"action_run_success"` } -// HookEvent represents events that will delivery hook. +// HookEvent represents events that will deliver a hook. type HookEvent struct { PushOnly bool `json:"push_only"` SendEverything bool `json:"send_everything"` diff --git a/modules/webhook/type.go b/modules/webhook/type.go index 244dc423c1..e833f90f58 100644 --- a/modules/webhook/type.go +++ b/modules/webhook/type.go @@ -7,6 +7,7 @@ package webhook type HookEventType string // Types of hook events +// update TestCreateWebhook in models/webhook/webhook_test.go when adding or changing values here const ( HookEventCreate HookEventType = "create" HookEventDelete HookEventType = "delete" @@ -33,6 +34,9 @@ const ( HookEventPackage HookEventType = "package" HookEventSchedule HookEventType = "schedule" HookEventWorkflowDispatch HookEventType = "workflow_dispatch" + HookEventActionRunFailure HookEventType = "action_run_failure" + HookEventActionRunRecover HookEventType = "action_run_recover" + HookEventActionRunSuccess HookEventType = "action_run_success" ) // Event returns the HookEventType as an event string @@ -65,6 +69,12 @@ func (h HookEventType) Event() string { return "repository" case HookEventRelease: return "release" + case HookEventActionRunFailure: + return "action_run_failure" + case HookEventActionRunRecover: + return "action_run_recover" + case HookEventActionRunSuccess: + return "action_run_success" } return "" } diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index b7b3a0c883..ca74a477ce 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -10,7 +10,7 @@ preview = عاين disabled = معطّل go_back = عُد للوراء licenses = التراخيص -sign_in = سجل الدخول +sign_in = تسجيل الدخول activities = الأنشطة copy_content = انسخ المحتوى collaborative = مشترك @@ -50,7 +50,7 @@ concept_user_organization = المنظمة link_account = ربط الحساب rerun_all = أعِد تشغيل جميع الوظائف your_profile = الملف الشخصي -sign_out = سجل الخروج +sign_out = سجّل الخروج settings = الإعدادات locked = مقفول error = خطأ @@ -87,7 +87,7 @@ add_all = أضف الكل new_fork = اشتقاق جديد لمستودع new_project_column = عمود جديد add = أضف -active_stopwatch = تتبع وقت الإنجاز +active_stopwatch = متتبِّع وقت النشاط organization = منظمة new_migrate = ترحيل جديد save = احفظ @@ -114,7 +114,7 @@ twofa_scratch = الرمز الاحتياطي للمصادقة بعاملين home = الرئيسية email = عنوان البريد الإلكتروني issues = المسائل -error404 = الصفحة التي تحاول الوصول لها إما لا توجد أو أنت لست مأذون لك بعرضها. +error404 = الصفحة التي تحاول الوصول لها إما غير موجودو أو أنك غير مصرح لك بعرضها. powered_by = مدعوم بواسطة %s retry = أعد المحاولة tracked_time_summary = ملخص للتتبع الزمني وفقًا لنتائج تصفية قائمة المسائل @@ -127,8 +127,8 @@ toggle_menu = تبديل القائمة more_items = عناصر اضافية copy_generic = نسخ إلى الحافظة invalid_data = بيانات غير صالحة: %v -filter.clear = مسح المرشحات -filter = مرشح +filter.clear = مسح عوامل التصفية +filter = عامل تصفية filter.is_archived = مؤرشف filter.is_template = قوالب filter.not_mirror = ليست مرايا @@ -137,7 +137,7 @@ filter.is_mirror = مرايا filter.is_fork = الاشتقاقات filter.not_fork = ليست اشتقاقات filter.not_archived = ليس مؤرشف -filter.public = علني +filter.public = عام filter.private = خاص new_repo.title = مستودع جديد new_migrate.title = انتقال جديد @@ -145,6 +145,11 @@ new_org.title = منظمة جديدة new_repo.link = مستودع جديد new_migrate.link = انتقال جديد +new_org.link = منظمة جديدة +test = اختبار +copy_path = نسخ المسار +error413 = لقد استنفدت حصتك. + [install] db_name = اسم قاعدة البيانات user = اسم المستخدم @@ -170,7 +175,7 @@ reinstall_confirm_check_2 = وقد يلزم إعادة تزامن المستود run_user = شغّل عبر مستخدم err_admin_name_is_invalid = اسم مستخدم المدير غير صالح reinstall_confirm_check_3 = أنتِ تؤكد أنكِ متأكد تماماً من أن فورجيو يعمل مع مسار app.ini الصحيح وأنك متأكد من أنه يجب عليك إعادة تثبيته. أنت تُؤكّدُ بأنّك تُقرّ بالمخاطر السالفة الذكر. -repo_path = المسار الجذري للمستودع +repo_path = المسار الجذر للمستودع err_empty_admin_email = عنوان بريد المدير لا يمكن أن يكون فارغ. no_admin_and_disable_registration = لا يمكنك تعطيل التسجيل الذاتي للمستخدمين بدون إنشاء حساب إداري. err_admin_name_pattern_not_allowed = اسم مستخدم المدير غير صالح، هذا الأسم يطابق نمطا محجوز @@ -179,10 +184,10 @@ repo_path_helper = ستُحفظ كلّ مستودعات جِت البعيدة ف general_title = الإعدادات العامة lfs_path_helper = الملفات التي تم تعقبها بواسطة Git LFS ستُخزن في هذا الدليل. اتركه فارغًا لتعطيله. err_empty_db_path = طريق قاعدة بيانات SQLite3 لا يمكن أن يكون فارغا. -lfs_path = مسار جذر جِت LFS -app_name_helper = يمكنك إدخال اسم شركتك هنا. +lfs_path = مسار جذر Git LFS +app_name_helper = أدخل اسم المثيل هنا. سيظهر هذا الاسم في كل الصفحات. err_admin_name_is_reserved = اسم مستخدم المدير غير صالح، هذا الأسم محجوز -app_name = عنوان الموقع +app_name = عنوان المثيل log_root_path = مسار السجل log_root_path_helper = ستُكتب ملفات السجل في هذا الدليل. smtp_addr = مضيف SMTP @@ -190,7 +195,7 @@ smtp_port = منفذ SMTP mailer_password = كلمة مرور SMTP app_url_helper = العنوان الأساسي لاستنساخ عناوين URL HTTP(S) وإشعارات البريد الإلكتروني. mailer_user = اسم مستخدم SMTP -disable_gravatar.description = عطل جرافاتار والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة. +disable_gravatar.description = عطل Gravatar والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة. offline_mode.description = عطل خدمات توصيل المحتوى من الجهات الخارجية، واخدم كل المحتوى محلياً. run_user_helper = اسم مستخدم نظام التشغيل الذي يشغل فورجيو. ملاحظة: هذا المستخدم يجب أن يكون له حق الوصول إلى المسار الجذري للمستودع. domain = نطاق الخادم @@ -199,28 +204,28 @@ smtp_from = أرسل البريد الإلكتروني كـ federated_avatar_lookup = تفعيل الصور الرمزية الاتحادية optional_title = إعدادات اختيارية domain_helper = نطاق أو عنوان المضيف لخادمك. -mail_notify = فعّل التنبيه عبر البريد الإلكتروني -app_url = الرابط الأساس لفورجيو +mail_notify = فعّل التنبيهات عبر البريد الإلكتروني +app_url = الرابط الأساس smtp_from_helper = عنوان البريد الإلكتروني الذي سيستخدمه فورجيو. أدخل عنوان بريد إلكتروني عادي أو استخدم صيغة"Name" . ssh_port_helper = رقم المنفذ الذي يستمع له خادم SSH. اتركه فارغاً لتعطيله. -http_port_helper = المنفذ الذي سيستمع إليه خادم الويب لفورجيو. -http_port = منفذ استماع HTTP لفورجيو +http_port_helper = المنفذ الذي سيستمع إليه خادم ويب Forgejo. +http_port = منفذ استماع HTTP ssh_port = منفذ خادم SSH email_title = إعدادات البريد الإلكتروني offline_mode = فعل الوضع المحلي server_service_title = إعدادات الخادم وخدمات الجهات الخارجية register_confirm = الزم تأكيد البريد الإلكتروني للتسجيل -allow_only_external_registration.description = لا يسمح بالتسجيل إلا من خلال الخدمات الخارجية +allow_only_external_registration.description = لن يتمكن المستخدمون من إنشاء حسابات جديدة إلا باستخدام خدمات خارجية مهيأة. disable_registration = عطّل التسجيل الذاتي -federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام ليبرافاتار. +federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام Libravatar. openid_signup = فعّل التسجيل الذاتي عبر OpenID -disable_registration.description = عطل التسجيل الذاتي. المديرون فقط سيكونون قادرين على إنشاء حسابات جديدة للمستخدمين. +disable_registration.description = سيتمكن مسؤولو المثيل فقط من إنشاء حسابات مستخدمين جديدة. يوصى بشدة بإبقاء التسجيل معطلاً إلا إذا كنت تنوي استضافة مثيل عام للجميع ومستعد للتعامل مع كميات كبيرة من الحسابات غير المرغوب بها. openid_signin = فعّل تسجيل الدخول عبر OpenID openid_signin.description = فعّل تسجيل دخول المستخدمين عبر OpenID. enable_captcha = فعّل كابتشا التسجيل -enable_captcha.description = الزم وجود كابتشا للتسجيل الذاتي للمستخدمين. +enable_captcha.description = مطالبة المستخدمين باجتياز اختبار CAPTCHA من أجل إنشاء حسابات. openid_signup.description = فعّل التسجيل الذاتي للمستخدمين عبر OpenID. -require_sign_in_view = الزم تسجيل الدخول لعرض الصفحات +require_sign_in_view = يتطلب تسجيل الدخول لعرض محتوى المثيل require_sign_in_view.description = مكّن وصول الصفحات للمستخدمين فقط. لن يرى الزائرون سوى صفحات التسجيل والتسجيل. admin_setting.description = إنشاء حساب إداري هو اختياري. أول مستخدم مُسجل سيصبح تلقائيا مديرا. admin_password = كلمة المرور @@ -233,7 +238,7 @@ test_git_failed = يتعذر اختبار أمر جِت: %v confirm_password = أكّد كلمة المرور invalid_admin_setting = إعداد حساب المدير غير صالح: %v invalid_log_root_path = مسار السجل غير صالح: %v -default_enable_timetracking = فعّل تتبع الوقت مبدئيا +default_enable_timetracking = فعّل التتبع الزمني افتراضيًا env_config_keys_prompt = ستطبق المتغيرات البيئية التالية أيضاً على ملف الإعدادات: admin_title = إعدادات حساب المدير no_reply_address_helper = النطاق للمستخدمين بعنوان بريد إلكتروني مخفي. مثلاً، اسم المستخدم "sarah" سوف يسجل في جِت كـ"sarah@noreply.example.org" لو كان نطاق البريد الإلكتروني الخفي مدخل كـ"noreply.example.org". @@ -242,9 +247,9 @@ default_enable_timetracking.description = فعل تتبع الوقت للمست run_user_not_match = مستخدم التشغيل غير مطابق لأسم المستخدم الحالي: %s -> %s invalid_db_setting = إعدادات قاعدة البيانات غير صالحة: %v invalid_db_table = جدول قاعدة البيانات "%s" غير صالح: %v -default_keep_email_private.description = أخفِ عناوين البريد الإلكتروني للحسابات الجديدة مبدئيا. +default_keep_email_private.description = قم بتمكين إخفاء عنوان البريد الإلكتروني للمستخدمين الجدد افتراضيًا حتى لا يتم تسريب هذه المعلومات فور التسجيل. env_config_keys = إعدادات بيئية -default_allow_create_organization = اسمح بإنشاء المنظمات مبدئيا +default_allow_create_organization = اسمح بإنشاء المنظمات بشكل افتراضي invalid_app_data_path = مسار بيانات التطبيق غير صالح: %v enable_update_checker_helper = يفحص لإيجاد اصدارات جديدة عن طريق الإتصال بسيرفرات فورجيو. invalid_repo_path = المسار الجزري للمستودع غير صالح: %v @@ -252,11 +257,17 @@ internal_token_failed = فشل توليد الرمز الداخلي: %v no_reply_address = نطاقات البريد الإلكتروني المخفية default_keep_email_private = أخفِ عناوين البريد الإلكتروني مبدئيا admin_name = اسم مستخدم المدير -default_allow_create_organization.description = اسمح بحسابات المستخدمين الجديدة بإنشاء المنظمات مبدئيا. +default_allow_create_organization.description = السماح للمستخدمين الجدد بإنشاء منتديات المجموعة بشكل افتراضي. عند تعطيل هذا الخيار، سيتعين على المسؤول منح إذن لإنشاء منتديات المجموعة للمستخدمين الجدد. password_algorithm = خوارزمية تجزئة كلمة المرور invalid_password_algorithm = خوارزمية بصمة كلمة المرور غير صالحة password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقوتها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. +app_slogan = شعار المثيل +app_slogan_helper = أدخل شعار المثيل الخاص بك هنا. اتركه فارغاً لتعطيله. +smtp_from_invalid = عنوان "،بريد الإرسال كـ" غير صالح +allow_only_external_registration = السماح بالتسجيل عبر الخدمات الخارجية فقط +config_location_hint = سيتم حفظ خيارات التهيئة هذه في: + [editor] buttons.list.ordered.tooltip = أضف قائمة مرقمة buttons.bold.tooltip = أضف نصًا عريضًا @@ -273,9 +284,22 @@ buttons.italic.tooltip = أضف نصًا مائلًا buttons.link.tooltip = اضف رابط buttons.disable_monospace_font = عطّل الخط الثابت العرض +buttons.indent.tooltip = تداخل العناصر بنفس المستوى +buttons.unindent.tooltip = ‪عناصر غير متساوية من نفس المستوى +buttons.new_table.tooltip = إضافة جدول +table_modal.header = إضافة جدول +table_modal.placeholder.header = الترويسة +table_modal.placeholder.content = المحتوى +table_modal.label.rows = الصفوف +table_modal.label.columns = الأعمدة +link_modal.header = إضافة رابط +link_modal.url = Url +link_modal.description = الوصف +link_modal.paste_reminder = تلميح: باستخدام عنوان URL في حافظتك، يمكنك اللصق مباشرةً في المحرر لإنشاء رابط. + [aria] navbar = شريط التنقل -footer.software = عن البرمجية +footer.software = عن هذه البرمجية footer.links = روابط footer = الذيل @@ -297,7 +321,7 @@ twofa_disabled = عُطِّل الاستيثاق الثنائي. theme_desc = ستكون هذه السمة المبدئية لك عبر الموقع. new_password = كلمة المرور الجديدة twofa_disable_desc = تعطيل الاستيثاق الثنائي سيجعل حسابك أقل أمانًا. أتريد الاستمرار؟ -manage_themes = اختر السمة المبدئية +manage_themes = الموضوع الافتراضي delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عنها بعد ذلك. cancel = ألغ repos_none = ليس لديك أي مستودع. @@ -390,7 +414,7 @@ account = الحساب uploaded_avatar_is_too_big = حجم الملف المرفوع (%d كي‌ب) يتخطى الحجم الأقصى (%d كي‌ب). biography_placeholder = أخبرنا شيئا عن نفسك! (يمكنك استخدام ماركداون) comment_type_group_reference = الإشارات -orgs = إدارة المنظمات +orgs = المنظمات update_profile = حدِّث الملف الشخصي profile = الملف الشخصي comment_type_group_dependency = الاعتماديات @@ -421,7 +445,7 @@ keep_email_private_popup = سيؤدي هذا إلى إخفاء عنوان بري ssh_key_name_used = هناك مفتاح SSH بنفس الاسم موجود بالفعل على حسابك. authorized_oauth2_applications = تطبيقات OAuth2 المأذونة uid = المعرّف الرمزي -manage_openid = إدارة عناوين OpenID +manage_openid = عناوين OpenID webauthn = استيثاق ثنائي (مفاتيح الأمان) comment_type_group_deadline = الموعد النهائي add_key = أضف مفتاح @@ -503,6 +527,8 @@ oauth2_redirect_uris = روابط إعادة التوجيه. نرجو وضع ك remove_account_link = أزل الحساب المربوط remove_account_link_success = أُزيل الحساب المربوط. +quota = كوتا + [org] follow_blocked_user = لا يمكنك إتباع هذه المنظمة لأن هذه المنظمة حظرتك. settings.delete_prompt = ستزال المنظمة إلى الأبد. لا يمكن التراجع عنها بعد ذلك! @@ -699,7 +725,7 @@ issues.filter_milestone_all = كل الأهداف issues.unlock.notice_2 = - يمكنك دوما إقفال هذه المسألة من جديد في المستقبل. issues.num_participants_few = %d متحاور release.title = عنوان الإصدار -issues.closed_at = `أغلق هذه المسألة %[2]s` +issues.closed_at = `أغلق هذه المسألة %s` issues.lock.title = إقفال التحاور في هذه المسألة. issues.new.no_label = بلا تصنيف issues.filter_sort.mostforks = الأعلى اشتقاقا @@ -759,7 +785,7 @@ branch.renamed = غُيّر اسم الفرع %s إلى %s. delete_preexisting = احذف الملفات الموجودة سابقا branch.included_desc = هذا الفرع جزء من الفرع المبدئي trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع -issues.reopened_at = `أعاد فتح هذه المسألة %[2]s` +issues.reopened_at = `أعاد فتح هذه المسألة %s` issues.action_milestone = هدف issues.new.assignees = المكلَّفون release.tag_name_protected = اسم الوسم محمي. @@ -1166,7 +1192,7 @@ pulls.status_checking = في انتظار بعض الفحوص pulls.status_checks_failure = بعض الفحوص فشلت pulls.status_checks_success = جميع الفحوص ناجحة pulls.status_checks_warning = بعض الفحوص تعطي تحذيرات -pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %[2]s` +pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %s` pulls.cmd_instruction_hint = `أظهر شرح استخدام سطر الأوامر.` pulls.cmd_instruction_checkout_title = اسحب pulls.cmd_instruction_checkout_desc = من مستودع مشروعك، اسحب (check out) فرعا جديدا واختبر التغييرات. @@ -1257,8 +1283,8 @@ pulls.status_checks_details = تفاصيل pulls.status_checks_hide_all = أخفِ كل الفحوص pulls.status_checks_show_all = أظهر كل الفحوص pulls.close = أغلق طلب الدمج -pulls.closed_at = `أغلق طلب الدمج %[2]s` -pulls.reopened_at = `أعاد فتح طلب الدمج %[2]s` +pulls.closed_at = `أغلق طلب الدمج %s` +pulls.reopened_at = `أعاد فتح طلب الدمج %s` milestones.title = العنوان milestones.desc = الوصف milestones.edit = عدّل الهدف @@ -1302,11 +1328,11 @@ issues.closed_by_fake = من %[2]s أُغلقت %[1]s issues.num_comments_1 = %d تعليق issues.num_comments = %d تعليقا issues.commented_at = `علّق %s` -issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %[2]s` -issues.ref_issue_from = `أشار إلى هذه المسألة %[4]s %[2]s` -issues.ref_pull_from = `أشار إلى هذا الطلب %[4]s %[2]s` -issues.ref_closing_from = `أشار إلى طلب دمج %[4]s سيغلق هذه المسألة %[2]s` -issues.ref_reopening_from = `أشار إلى طلب دمج %[4]s سيعيد فتح هذه المسألة %[2]s` +issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %s` +issues.ref_issue_from = `أشار إلى هذه المسألة %[3]s %[1]s` +issues.ref_pull_from = `أشار إلى هذا الطلب %[3]s %[1]s` +issues.ref_closing_from = `أشار إلى طلب دمج %[3]s سيغلق هذه المسألة %[1]s` +issues.ref_reopening_from = `أشار إلى طلب دمج %[3]s سيعيد فتح هذه المسألة %[1]s` issues.ref_closed_from = `أغلق هذه المسألة %[4]s %[2]s` issues.ref_reopened_from = `أعاد فتح هذه المسألة %[4]s %[2]s` issues.reference_issue.body = المحتوى @@ -1386,6 +1412,22 @@ issue.action.ready_for_review = @%[1]s علّم هذا الطلب للس issue_assigned.pull = @%[1]s عيّنك إلى طلب سحب %[2]s في مستودع %[3]s. issue.action.review_dismissed = @%[1]s أستبعد آخر مراجعة من %[2]s لهذا الطلب للسحب. +password_change.subject = تم تغيير كلمة مرورك +password_change.text_1 = تم تغيير كلمة مرور حسابك للتو. +primary_mail_change.subject = تم تغيير البريد الأساسي الخاص بك +primary_mail_change.text_1 = تم تغيير البريد الإلكتروني الأساسي لحسابك إلى %[1]s. هذا يعني أن عنوان البريد الإلكتروني هذا لن يتلقى إشعارات البريد لحسابك بعد الآن. +totp_disabled.subject = تم تعطيل TOTP +totp_disabled.text_1 = تم تعطيل كلمة المرور لمرة واحدة المستندة إلى الوقت (TOTP) على حسابك للتو. +totp_disabled.no_2fa = لم تعد هناك طرق أُخرى للمصادقة الثنائية (2FA) قيد التهيئة عد الآن ، أي أنه لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA). +removed_security_key.subject = تمت إزالة مفتاح الأمان +removed_security_key.text_1 = تم إزالة مفتاح الأمان ”%[1] s“ للتو من حسابك. +removed_security_key.no_2fa = لم تعد هناك طرق أخرى للمصادقة الثنائية (2FA) قيد التهيئة بعد الآن، أي لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA). +account_security_caution.text_1 = إذا كان هذا أنت، فيمكنك تجاهل هذا البريد بأمان. +account_security_caution.text_2 = إذا لم تكن أنت، فهذا يعني أن حسابك مخترق. يرجى الاتصال بمسؤولي هذا الموقع. +totp_enrolled.subject = لقد قمت بتشيط TOTP كطريقة 2FA +totp_enrolled.text_1.no_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يجب عليك استخدام TOTP كطريقة للمصادقة الثنائية. +totp_enrolled.text_1.has_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يمكنك استخدام TOTP كطريقة للمصادقة الثنائية ، أو استخدام أي من مفاتيح الأمان الخاصة بك. + [error] not_found = تعذر العثور على الهدف. report_message = إن كنت متيقِّنًا أن هذه علة في فورجيو، رجاءً ابحث في كودبيرج أو افتح مسأله جديدة إذا لزم الأمر. @@ -1420,7 +1462,7 @@ joined_on = انضم في %s user_bio = السيرة الذاتية repositories = المستودعات activity = النشاط العام -projects = مشاريع +projects = المشاريع unfollow = إلغِ المتابعة settings = إعدادات المستخدم following_few = %d يتابع @@ -1428,7 +1470,7 @@ follow = تابع followers_few = %d متابعين form.name_reserved = اسم المستخدم "%s" محجوز. email_visibility.limited = عنوان بريدك الإلكتروني ظاهر لكل المستخدمين المُستَوثَقين -code = البرمجية +code = الكود overview = نظرة عامة watched = المستودعات المشاهدة disabled_public_activity = هذا المستخدم عطّل الظهور العام للنشاط. @@ -1438,6 +1480,18 @@ starred = المستودعات المميّزة بنجمة form.name_chars_not_allowed = اسم المستخدم "%s" يحتوي على رموز غير صالحة. form.name_pattern_not_allowed = النمط "s%" غير مسموح به في إسم المستخدم. +followers.title.one = متابِع +followers.title.few = متابعين +following.title.one = متابعة +following.title.few = متابعة +followers_one = %d متابِع +following_one = %d يُتابع +public_activity.visibility_hint.self_public = نشاطك مرئي للجميع، باستثناء التفاعلات في المساحات الخاصة. اضبط الإعدادات. +public_activity.visibility_hint.admin_public = هذا النشاط مرئي للجميع، ولكن بصفتك مسؤولاً يمكنك أيضًا رؤية التفاعلات في المساحات الخاصة. +public_activity.visibility_hint.self_private = نشاطك مرئي لك ولسُعاة المثيل فقط. تعديل الإعدادات. +public_activity.visibility_hint.admin_private = هذا النشاط مرئي لك لأنك مسؤول، ولكن المستخدم يريد أن يظل خاصاً. +public_activity.visibility_hint.self_private_profile = نشاطك مرئي لك ولسُعاة المثيل فقط لأن ملفك الشخصي خاص. تعديل الإعدادات. + [auth] change_unconfirmed_email_error = تعذر تغيير البريد الإلكتروني: %v change_unconfirmed_email_summary = تغيير البريد الإلكتروني الذي يُرسل التفعيل له. @@ -1457,11 +1511,11 @@ active_your_account = فعّل حسابك register_helper_msg = هل لديك حساب بالفعل؟ سجل الدخول! manual_activation_only = تواصل مع مدير موقعك لإكمال التفعيل. must_change_password = حدّث كلمة المرور الخاصة بك -send_reset_mail = أرسل رسالة استعادة حساب +send_reset_mail = أرسل بريد الاستعادة resend_mail = اضغط هنا لإعادة إرسالة رسالة تفعيل حسابك has_unconfirmed_mail = أهلا يا %s، لديك عنوان بريد إلكتروني غير مؤكَّد (%s). إن لم تستلم رسالة تأكيد أو تريد إرسال واحدة جديدة، فنرجو الضغط على الزر الذي بالأسفل. email_not_associate = عنوان البريد هذا غير مرتبط بأي حساب. -reset_password = استعادة حساب +reset_password = استعادة الحساب oauth_signin_tab = أربط بحساب موجود invalid_password = كلمة المرور الخاصة بك لا تطابق كلمة المرور التي استخدمت لتسجيل الحساب. oauth_signin_title = سجّل الدخول لتأذن للحساب المربوط @@ -1482,13 +1536,13 @@ reset_password_wrong_user = أنت مُسجل كـ %s، لكن رابط أعاد openid_connect_title = اتصل بحساب موجود confirmation_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى %s. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية التسجيل. إذا كان عنوان البريد خاطئ، يمكنك تسجيل الدخول وطلب بريد تأكيد جديد يُرسل إلى عنوان آخر. scratch_code = رمز الخدش -invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهى اضغط هنا للإعادة. +invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهت صلاحيته. اضغط هنا للإعادة. openid_register_title = أنشئ حسابًا جديدًا verify = تحقق twofa_scratch_used = لقد استخدمت رمز الخدش الخاص بك. لقد تم إعادة توجيهك إلى إعدادات المصادقة الثنائية حتى يمكنك إزالة تسجيل جهازك أو توليد رمز خدش جديد. oauth_signup_submit = أكمل الحساب oauth.signin.error = كان هناك خطأ في تجهيز طلب الإذن إذا استمر هذا الخطأ، يرجى الاتصال بالمدير. -invalid_code = رمز تأكيدك غير صحيح أو انتهى. +invalid_code = رمز تأكيدك غير صحيح أو انتهت صلاحيته. oauth_signup_title = أكمل حساب جديد resent_limit_prompt = لقد طلبت بالفعل بريداً إلكترونياً للتفعيل مؤخراً من فضلك انتظر 3 دقائق وحاول مرة أخرى. reset_password_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى %s. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية استعادة الحساب. @@ -1513,6 +1567,14 @@ remember_me = تذكر هذا الجهاز remember_me.compromised = رمز الاحتفاظ بتسجيل الدخول لم يعد صالحا، مما قد يعني اختراق الحساب. نرجو مراجعة حسابك لرؤية أي نشاط غير مألوف. authorization_failed_desc = فشل التفويض لأننا اكتشفنا طلبًا غير صالح. يرجى الاتصال بمشرف التطبيق الذي حاولت ترخيصه. +hint_login = لديك حساب بالفعل؟ سجّل الدخول الآن! +hint_register = يلزمك حساب ؟ سجِّل الآن. +sign_up_button = سجِّل الآن. +unauthorized_credentials = بيانات الاعتماد غير صحيحة أو انتهت صلاحيتها. أعد محاولة تنفيذ الأمر أو راجع %s لمزيد من المعلومات +use_onetime_code = استخدم رمزًا لمرة واحدة +back_to_sign_in = العودة إلى تسجيل الدخول +sign_in_openid = المتابعة باستخدام OpenID + [packages] rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة. rpm.repository.architectures = بنيات @@ -1556,6 +1618,10 @@ number_of_contributions_in_the_last_12_months = %s مساهم في آخر 12 ش contributions_zero = بلا مساهمات more = أكثر +contributions_format = {contributions} مساهمة في {day} {month} {year} +contributions_one = المساهمة +contributions_few = المساهمات + [admin] self_check.database_fix_mysql = لمستخدمين ميسكول/ماريا دي بي، يمكنك استخدام أمر "forgejo doctor convert" لإصلاح مشاكل التجمّع، أو يمكنك أيضاً إصلاح المشكلة عن طريق تعديل السيكول يدوياً. self_check.database_collation_mismatch = توقع قاعدة البيانات لتستعمل تجميع: %s @@ -1706,7 +1772,7 @@ enterred_invalid_org_name = اسم المنظمة التي أدخلته خطأ. lang_select_error = اختر لغة من القائمة. alpha_dash_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_").` alpha_dash_dot_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_") والنقطة (".").` -repo_name_been_taken = اسم المستودع مستعمل بالفعل. +repo_name_been_taken = اسم المستودع مستخدم بالفعل. Email = البريد الإلكتروني auth_failed = فشل الاستيثاق: %v email_error = ` ليس عنوان بريد إلكتروني صالح.` @@ -1726,10 +1792,10 @@ still_has_org = "حسابك عضو في منظمة أو أكثر؛ غادرهم repository_files_already_exist.adopt_or_delete = الملفات موجودة بالفعل لهذا المستودع. إما اعتمادها أو حذفها. repository_files_already_exist.delete = الملفات موجودة بالفعل لهذا المستودع. يجب عليك حذفها. repository_files_already_exist.adopt = الملفات موجودة بالفعل لهذا المستودع ويمكن اعتمادها فقط. -repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. تواصل مع مدير النظام. +repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. اتصل بمدير النظام. TeamName = اسم الفريق username_has_not_been_changed = لم يتم تغيير اسم المستخدم -username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسماؤهم. +username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسمائهم. captcha_incorrect = الكابتشا خاطئة. AdminEmail = عنوان البريد الإلكتروني للمدير team_no_units_error = اسمح بالوصول إلى قسم واحد على الأقل في المستودعات. @@ -1757,6 +1823,24 @@ CommitChoice = إختيار الإداع regex_pattern_error = ` نمط التعبير النمطي غير صالح: %s.` username_error = ` يُمكنه أن يحتوي على حروف إنجليزية وأرقام وشرطة ("-") وشرطة سفلية ("_") و نقطة (".") فقط. ويمكنه ان يبدأ وينتهي بحرف او برقم.` +FullName = الاسم الكامل +Description = الوصف +Pronouns = الضمائر +Biography = النبذة +Website = موقع الويب +Location = الموقع +To = اسم الفرع +AccessToken = رمز الوصول +invalid_group_team_map_error = ` التعيين غير صالح: %s ` +username_claiming_cooldown = لا يمكن المطالبة باسم المستخدم، لأن فترة تباطؤه لم تنتهِ بعد. يمكن المطالبة به عند %[1]s. +repository_force_private = وضع الخاص الإجباري مفعّل: لا يمكن تحويل المستودعات الخاصة إلى عامة. +visit_rate_limit = تناولت الزيارة عن بُعد الحد من معدلها. +email_domain_is_not_allowed = نطاق البريد الإلكتروني للمستخدم %s يتعارض مع قائمة النطاقات المسموحة ، أو الممنوعة. يرجى التأكد من إدخال عنوان البريد الإلكتروني بشكل صحيح. +unset_password = المستخدم المسجل لم يقم بتعيين كلمة مرور. +unsupported_login_type = نوع تسجيل الدخول غير مدعوم لحذف الحساب. +invalid_ssh_principal = أصل غير صالح: %s +required_prefix = المُدخل يجب أن يبدأ مع "%s" + [home] filter = تصفيات أخرى show_archived = مؤرشف @@ -1803,6 +1887,11 @@ relevant_repositories_tooltip = تم أخفاء المستودعات التي ه relevant_repositories = يتم اظهار المستودعات المتعلقة فقط. أظهر النتائج غير المصفاة. code_last_indexed_at = فُهرس آخر مرة %s +stars_one = %d نجمة +stars_few = %d نجوم +forks_one = %d نسخة +forks_few = %d نُسَخ + [actions] variables.none = لا توجد متغيرات بعد. variables.deletion = أزل المتغير @@ -1973,21 +2062,30 @@ component_failed_to_load = حدث خطأ غير متوقع. [search] -org_kind = بحث في المنظمات... +org_kind = بحث في المنظمات… code_search_unavailable = البحث في الكود غير متوفر حاليًا. يرجى الاتصال بمدير الموقع. -search = ابحث... +search = البحث… type_tooltip = نوع البحث fuzzy = أجعد fuzzy_tooltip = قم بتضمين النتائج التي تتطابق أيضًا مع مصطلح البحث بشكل وثيق match = تتناسب match_tooltip = قم بتضمين النتائج التي تطابق مصطلح البحث المحدد فقط -repo_kind = بحث في المستودعات... -user_kind = بحث عن المستخدمين... -team_kind = بحث عن الفرق ... -code_kind = بحث في الكود... -project_kind = البحث ضمن المشاريع... -branch_kind = البحث ضمن الفروع... +repo_kind = بحث في المستودعات… +user_kind = بحث عن المستخدمين… +team_kind = بحث عن الفرق… +code_kind = بحث ضمن الكود… +project_kind = البحث ضمن المشاريع… +branch_kind = البحث ضمن الفروع… no_results = لا توجد نتائج مطابقة. -issue_kind = البحث ضمن الأعطال... -pull_kind = البحث ضمن طلبات السحب... +issue_kind = البحث ضمن الأعطال… +pull_kind = البحث ضمن طلبات السحب… keyword_search_unavailable = البحث من خلال الكلمات المفتاحية ليس متوفر حالياً. رجاءاً تواصل مع مشرف الموقع. +union = مطابقة عامة +union_tooltip = عرض النتائج التي تطابق أي من الكلمات المفتاحية المفصولة بمسافات +exact = مطابق +exact_tooltip = عرض النتائج التي تطابق مصطلح البحث بالضبط فقط +regexp = RegExp +regexp_tooltip = تعامل مع عبارة البحث على أنها تعبير نمطي +package_kind = البحث ضمن الحزم… +commit_kind = البحث ضمن الإيداعات… +runner_kind = البحث ضمن المشغِّلات… diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 6fc4b55eae..35e33f4430 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -11,7 +11,7 @@ copy_content = Копиране на съдържанието user_profile_and_more = Профил и настройки… view = Преглед your_settings = Настройки -mirrors = Огледала +mirrors = Огледални explore = Разглеждане write = Писане twofa = Двуфакторно удостоверяване @@ -36,7 +36,7 @@ dashboard = Табло logo = Лого toc = Съдържание copy_url = Копиране на URL -new_mirror = Ново огледало +new_mirror = Ново огледално re_type = Потвърдете паролата copy = Копиране enabled = Включено @@ -61,7 +61,7 @@ ok = Добре manage_org = Управление на организациите new_repo = Ново хранилище register = Регистрация -mirror = Огледало +mirror = Огледално username = Потребителско име password = Парола template = Шаблон @@ -69,7 +69,7 @@ signed_in_as = Влезли сте като sign_up = Регистриране enable_javascript = Този сайт изисква JavaScript. home = Начало -email = Адрес на ел. поща +email = Адрес за ел. поща issues = Задачи retry = Повторен опит remove = Премахване @@ -93,8 +93,8 @@ filter.not_fork = Не разклонения filter.is_template = Шаблони filter.not_template = Не шаблони filter.private = Частни -filter.is_mirror = Огледала -filter.not_mirror = Не огледала +filter.is_mirror = Огледални +filter.not_mirror = Не огледални copy_hash = Копиране на контролната сума artifacts = Артефакти show_log_seconds = Показване на секундите @@ -102,7 +102,7 @@ remove_all = Премахване на всичко test = Проба remove_label_str = Премахване на елемента „%s“ copy_branch = Копиране на името на клона -error404 = Страницата, която се опитвате да отворите, или не съществува или не сте упълномощени да я видите. +error404 = Страницата, която се опитвате да отворите, или не съществува, или е премахната, или не сте упълномощени да я видите. new_repo.link = Ново хранилище new_migrate.title = Нова миграция new_repo.title = Ново хранилище @@ -112,6 +112,37 @@ new_org.link = Нова организация copy_generic = Копиране в клипборда copy_error = Неуспешно копиране copy_path = Копиране на пътя +toggle_menu = Превключване на менюто +confirm_delete_artifact = Сигурни ли сте, че искате да изтриете артефакта „%s“? +more_items = Още елементи +twofa_scratch = Резервен код за двуфакторно удостоверяване +webauthn_use_twofa = Използвайте двуфакторен код от телефона си +webauthn_error_insecure = WebAuthn поддържа само сигурни връзки. За тестване през HTTP можете да използвате произход „localhost“ или „127.0.0.1“ +error413 = Изчерпали сте квотата си. +go_back = Връщане +invalid_data = Невалидни данни: %v +archived = Архивирано +concept_system_global = Глобално +concept_user_individual = Индивидуално +show_full_screen = Показване на цял екран +show_timestamps = Показване на времеви отпечатъци +rerun = Повторно изпълнение +copy_type_unsupported = Този тип файл не може да бъде копиран +webauthn_error_unknown = Възникна неизвестна грешка. Моля, опитайте отново. +webauthn_error_unable_to_process = Сървърът не можа да обработи заявката ви. +webauthn_error_empty = Трябва да зададете име за този ключ. +webauthn_error_timeout = Времето за изчакване изтече преди ключът ви да бъде прочетен. Моля, презаредете страницата и опитайте отново. +return_to_forgejo = Връщане към Forgejo +unknown = Неизвестно +confirm_delete_selected = Потвърждавате ли изтриването на всички избрани елементи? +webauthn_insert_key = Поставете вашия ключ за сигурност +webauthn_press_button = Моля, натиснете бутона на вашия ключ за сигурност… +webauthn_sign_in = Натиснете бутона на вашия ключ за сигурност. Ако ключът ви за сигурност няма бутон, поставете го отново. +webauthn_error = Неуспешно прочитане на вашия ключ за сигурност. +webauthn_unsupported_browser = Вашият браузър в момента не поддържа WebAuthn. +webauthn_error_duplicated = Ключът за сигурност не е разрешен за тази заявка. Моля, уверете се, че ключът не е вече регистриран. + +tracked_time_summary = Обобщение на проследеното време въз основа на филтрите в списъка със задачи [settings] ui = Тема @@ -157,7 +188,7 @@ account = Акаунт update_avatar = Обновяване на профилната снимка ssh_gpg_keys = SSH / GPG ключове comment_type_group_milestone = Етап -manage_emails = Управление на адресите на ел. поща +manage_emails = Управление на адресите за ел. поща permission_read = Четене update_password = Обновяване на паролата biography_placeholder = Разкажете на другите малко за себе си! (Можете да използвате Маркдаун) @@ -183,7 +214,7 @@ user_block_success = Потребителят е блокиран успешно update_profile_success = Профилът ви е обновен. update_user_avatar_success = Профилната снимка на потребителя е обновена. remove_oauth2_application_success = Приложението е изтрито. -email_deletion_success = Адресът на ел. поща е премахнат. +email_deletion_success = Адресът за ел. поща е премахнат. update_avatar_success = Профилната ви снимка е обновена. change_username = Потребителското ви име е променено. comment_type_group_assignee = Изпълнител @@ -191,22 +222,22 @@ enable_custom_avatar = Използване на персонализирана requires_activation = Изисква активиране activated = Активиран primary = Основен -email_deletion = Премахване на адреса на ел. поща -add_new_email = Добавяне на нов адрес на ел. поща -add_email = Добавяне на адрес на ел. поща +email_deletion = Премахване на адреса за ел. поща +add_new_email = Добавяне на нов адрес за ел. поща +add_email = Добавяне на адрес за ел. поща key_content_gpg_placeholder = Започва с „-----BEGIN PGP PUBLIC KEY BLOCK-----“ comment_type_group_title = Заглавие comment_type_group_label = Етикет -change_username_prompt = Забележка: Промяната на потребителското ви име променя също URL на вашия акаунт. +change_username_prompt = Бележка: Промяната на потребителското ви име променя също URL на вашия акаунт. update_language_not_found = Езикът „%s“ не е наличен. keep_activity_private_popup = Вашата дейност ще бъде видима само за вас и администраторите на сайта uploaded_avatar_not_a_image = Каченият файл не е изображение. uploaded_avatar_is_too_big = Размерът на качения файл (%d KiB) надвишава максималния размер (%d KiB). -change_password_success = Паролата ви е обновена. Влизайте с новата си парола от сега нататък. +change_password_success = Паролата ви е обновена. Отсега нататък използвайте новата си парола, за да влезете. manage_themes = Тема по подразбиране manage_openid = OpenID адреси primary_email = Да е основен -keep_email_private = Скриване на адреса на ел. поща +keep_email_private = Скриване на адреса за ел. поща theme_update_error = Избраната тема не съществува. theme_update_success = Темата ви е обновена. key_content_ssh_placeholder = Започва с „ssh-ed25519“, „ssh-rsa“, „ecdsa-sha2-nistp256“, „ecdsa-sha2-nistp384“, „ecdsa-sha2-nistp521“, „sk-ecdsa-sha2-nistp256@openssh.com“, или „sk-ssh-ed25519@openssh.com“ @@ -227,7 +258,7 @@ saved_successfully = Настройките бяха запазени успеш no_activity = Няма скорошна дейност theme_desc = Тази тема ще се използва за уеб интерфейса, когато сте влезли. keep_activity_private = Скриване на дейността от профилната страница -lookup_avatar_by_mail = Търсене на профилна снимка по адреса на ел. поща +lookup_avatar_by_mail = Търсене на профилна снимка по адреса за ел. поща password_incorrect = Текущата парола е неправилна. change_username_redirect_prompt = Старото потребителско име ще се пренасочва, докато някой не го вземе. principal_content = Съдържание @@ -246,7 +277,7 @@ delete_prompt = Тази операция ще изтрие перманентн email_notifications.disable = Изключване на известията по ел. поща delete_account = Изтриване на акаунта ви confirm_delete_account = Потвърждаване на изтриването -email_notifications.onmention = Ел. поща само при споменаване +email_notifications.onmention = Ел. писмо само при споменаване pronouns_unspecified = Непосочени pronouns = Местоимения gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig @@ -254,8 +285,117 @@ language.title = Език по подразбиране language.localization_project = Помогнете ни да преведем Forgejo на вашия език! Научете повече. language.description = Този език ще бъде запазен във вашия акаунт и ще се използва като език по подразбиране, след като влезете. pronouns_custom = Персонализирани -visibility.limited_tooltip = Видимо само за влезли потребители +visibility.limited_tooltip = Видим само за влезли потребители pronouns_custom_label = Персонализирани местоимения +comment_type_group_review_request = Искане за рецензия +ssh_key_been_used = Този SSH ключ вече е добавен към сървъра. +create_oauth2_application = Създаване на ново OAuth2 приложение +update_oauth2_application_success = Успешно обновихте OAuth2 приложението. +authorized_oauth2_applications = Упълномощени OAuth2 приложения +manage_account_links = Свързани акаунти +revoke_oauth2_grant = Отнемане на достъпа +added_on = Добавен на %s +comment_type_group_dependency = Зависимост +update_hints_success = Подсказките са обновени. +manage_oauth2_applications = Управление на OAuth2 приложения +gpg_key_id_used = Вече съществува публичен GPG ключ със същото ID. +oauth2_applications_desc = OAuth2 приложенията позволяват на вашето приложение от трета страна да удостоверява сигурно потребители в тази инстанция на Forgejo. +blocked_since = Блокиран от %s +hidden_comment_types.ref_tooltip = Коментари, в които тази задача е спомената от друга задача/подаване/… +create_oauth2_application_success = Успешно създадохте ново OAuth2 приложение. +quota.applies_to_org = Следните правила за квота се прилагат за тази организация +keep_activity_private.description = Вашата публична дейност ще бъде видима само за вас и администраторите на инстанцията. +ssh_helper = Нуждаете се от помощ? Разгледайте ръководството за създаване на собствени SSH ключове или за решаване на често срещани проблеми, които може да срещнете при използване на SSH. +twofa_desc = За да защитите акаунта си от кражба на парола, можете да използвате смартфон или друго устройство за получаване на еднократни пароли, базирани на време („TOTP“). +scan_this_image = Сканирайте това изображение с вашето приложение за удостоверяване: +quota.rule.exceeded.helper = Общият размер на обектите за това правило надвиши квотата. +password_change_disabled = Нелокални потребители не могат да обновяват паролата си през уеб интерфейса на Forgejo. +twofa_disable_note = Можете да изключите двуфакторното удостоверяване, ако е необходимо. +hooks.desc = Добавете уеб-куки, които ще се задействат за всички хранилища, които притежавате. +delete_account_desc = Сигурни ли сте, че искате да изтриете перманентно този потребителски акаунт? +last_used = Последно използван на +revoke_oauth2_grant_description = Отнемането на достъпа за това приложение от трета страна ще му попречи да има достъп до вашите данни. Сигурни ли сте? +password_username_disabled = Нелокални потребители не могат да променят потребителското си име. Моля, свържете се с администратора на сайта за повече подробности. +change_username_redirect_prompt.with_cooldown.one = Старото потребителско име ще бъде достъпно за всички след период на изчакване от %[1]d ден. Все още можете да си върнете старото потребителско име по време на периода на изчакване. +change_username_redirect_prompt.with_cooldown.few = Старото потребителско име ще бъде достъпно за всички след период на изчакване от %[1]d дни. Все още можете да си върнете старото потребителско име по време на периода на изчакване. +generate_token_name_duplicate = %s вече е използвано като име на приложение. Моля, използвайте ново. +quota.rule.exceeded = Надвишена +repo_and_org_access = Достъп до хранилища и организации +permissions_public_only = Само публични +permissions_list = Разрешения: +edit_oauth2_application = Редактиране на OAuth2 приложение +remove_oauth2_application = Премахване на OAuth2 приложение +twofa_recovery_tip = Ако загубите устройството си, ще можете да използвате ключ за еднократно възстановяване, за да си върнете достъпа до акаунта. +visibility.private_tooltip = Видим само за участници в организации, в които участвате +quota.applies_to_user = Следните правила за квота се прилагат за вашия акаунт +quota.rule.no_limit = Неограничена +hints = Подсказки +comment_type_group_issue_ref = Препратка към задача +activate_email = Изпращане на активация +ssh_disabled = SSH е изключен +twofa_disable_desc = Изключването на двуфакторното удостоверяване ще направи акаунта ви по-малко сигурен. Продължаване? +keep_pronouns_private = Показване на местоименията само на удостоверени потребители +keep_pronouns_private.description = Това ще скрие вашите местоимения от посетители, които не са влезли в системата. +gpg_helper = Нуждаете се от помощ? Разгледайте ръководството относно GPG. +valid_until_date = Валиден до %s +ssh_externally_managed = Този SSH ключ се управлява външно за този потребител +regenerate_scratch_token_desc = Ако сте загубили ключа си за възстановяване или вече сте го използвали, за да влезете, можете да го нулирате тук. +create_oauth2_application_button = Създаване на приложение +revoke_oauth2_grant_success = Достъпът е отнет успешно. +comment_type_group_deadline = Краен срок +comment_type_group_time_tracking = Проследяване на времето +activations_pending = Чакащи активации +valid_forever = Валиден завинаги +key_state_desc = Този ключ е използван през последните 7 дни +revoke_key = Отнемане +delete_account_title = Изтриване на потребителския акаунт +update_hints = Обновяване на подсказките +permissions_access_all = Всички (публични, частни и ограничени) +oauth2_application_name = Име на приложението +visibility.public_tooltip = Видим за всички +user_block_yourself = Не можете да блокирате себе си. +hidden_comment_types.issue_ref_tooltip = Коментари, в които потребителят променя клона/маркера, свързан със задачата +comment_type_group_reference = Препратка +comment_type_group_branch = Клон +comment_type_group_pull_request_push = Добавени подавания +quota = Квота +webauthn_delete_key = Премахване на ключ за сигурност +webauthn_register_key = Добавяне на ключ за сигурност +webauthn_nickname = Прякор +webauthn_delete_key_desc = Ако премахнете ключ за сигурност, вече няма да можете да влизате с него. Продължаване? +additional_repo_units_hint = Предлагане за включване на допълнителни елементи на хранилището +twofa_is_enrolled = Вашият акаунт в момента е включен в двуфакторно удостоверяване. +twofa_not_enrolled = Вашият акаунт в момента не е включен в двуфакторно удостоверяване. +webauthn_key_loss_warning = Ако загубите ключовете си за сигурност, ще загубите достъп до акаунта си. +email_desc = Вашият основен адрес за ел. поща ще се използва за известия, възстановяване на парола и, при условие че не е скрит, за уеб-базирани Git операции. +email_preference_set_success = Предпочитанията за ел. поща са зададени успешно. +add_email_confirmation_sent = Изпратено е ел. писмо за потвърждение до „%s“. За да потвърдите адреса си за ел. поща, моля, проверете входящата си кутия и последвайте предоставената връзка в рамките на следващите %s. +additional_repo_units_hint_description = Показване на подсказка „Включване на повече“ за хранилища, които нямат включени всички налични елементи. +email_notifications.submit = Задаване на предпочит. за ел. поща +email_notifications.andyourown = И вашите собствени известия +email_deletion_desc = Адресът за ел. поща и свързаната информация ще бъдат премахнати от вашия акаунт. Git подаванията от този адрес за ел. поща ще останат непроменени. Продължаване? +add_email_success = Новият адрес за ел. поща е добавен. +remove_account_link = Премахване на свързан акаунт +webauthn_alternative_tip = Може да искате да конфигурирате допълнителен метод за удостоверяване. +hidden_comment_types_description = Типовете коментари, отметнати тук, няма да се показват в страниците на задачите. Например, отмятането на „Етикет“ премахва всички коментари от типа „<потребител> добави/премахна <етикет>“. +hidden_comment_types = Скрити типове коментари +comment_type_group_lock = Състояние на заключване +can_not_add_email_activations_pending = Има чакаща активация, опитайте отново след няколко минути, ако искате да добавите нова ел. поща. +storage_overview = Преглед на съхранението + +webauthn = Двуфакторно удостоверяване (Ключове за сигурност) +quota.sizes.all = Всички +quota.sizes.repos.all = Хранилища +quota.sizes.repos.public = Публични хранилища +quota.sizes.repos.private = Частни хранилища +quota.sizes.git.all = Git съдържание +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.attachments.all = Прикачени файлове +quota.sizes.assets.attachments.issues = Прикачени файлове към задачи +quota.sizes.assets.attachments.releases = Прикачени файлове към издания +quota.sizes.assets.artifacts = Артефакти +quota.sizes.assets.packages.all = Пакети +quota.sizes.wiki = Уики [packages] container.labels.value = Стойност @@ -287,6 +427,140 @@ generic.download = Изтеглете пакета от командния ре container.details.type = Тип образ alpine.repository = За хранилището container.images.title = Образи +arch.version.description = Описание +search_in_external_registry = Търсене в %s +filter.type = Тип +filter.container.untagged = Без маркер +filter.type.all = Всички +registry.documentation = За повече информация относно регистъра %s, вижте документацията. +filter.no_result = Вашият филтър не даде резултати. +filter.container.tagged = С маркер +arch.pacman.repo.multi = %s има същата версия в различни дистрибуции. +arch.pacman.helper.gpg = Добавете доверителен сертификат за pacman: +alpine.repository.architectures = Архитектури +arch.version.provides = Доставя +arch.version.groups = Група +details.project_site = Уебсайт на проекта +arch.pacman.conf = Добавете сървър със свързаната дистрибуция и архитектура към /etc/pacman.conf : +arch.pacman.sync = Синхронизирайте пакета с pacman: +details.repository_site = Уебсайт на хранилището +arch.version.depends = Зависимости +arch.version.optdepends = Допълнителни зависимости +arch.version.replaces = Заменя +go.install = Инсталирайте пакета от командния ред: +cargo.registry = Настройте този регистър в конфигурационния файл на Cargo (например ~/.cargo/config.toml): +cargo.install = За да инсталирате пакета с Cargo, изпълнете следната команда: +details.documentation_site = Уебсайт на документацията +arch.version.conflicts = В конфликт +alpine.repository.branches = Клонове +arch.pacman.repo.multi.item = Конфигурация за %s + +desc = Управление на пакетите на хранилището. +alpine.registry = Настройте този регистър, като добавите URL адреса във вашия файл /etc/apk/repositories: +alpine.registry.key = Изтеглете публичния RSA ключ на регистъра в папката /etc/apk/keys/, за да проверите подписа на индекса: +alpine.registry.info = Изберете $branch и $repository от списъка по-долу. +alpine.install = За да инсталирате пакета, изпълнете следната команда: +arch.version.properties = Свойства на версията +arch.version.makedepends = Зависимости за изграждането +arch.version.checkdepends = Зависимости за проверката +chef.registry = Настройте този регистър във вашия файл ~/.chef/config.rb: +chef.install = За да инсталирате пакета, изпълнете следната команда: +composer.registry = Настройте този регистър във вашия файл ~/.composer/config.json: +composer.install = За да инсталирате пакета с Composer, изпълнете следната команда: +composer.dependencies = Зависимости +conan.details.repository = Хранилище +conan.registry = Настройте този регистър от командния ред: +conan.install = За да инсталирате пакета с Conan, изпълнете следната команда: +conda.registry = Настройте този регистър като Conda хранилище във вашия файл .condarc: +conda.install = За да инсталирате пакета с Conda, изпълнете следната команда: +container.pull = Издърпайте образа от командния ред: +container.multi_arch = ОС / Архитектура +container.layers = Слоеве на образа +cran.registry = Настройте този регистър във вашия файл Rprofile.site: +cran.install = За да инсталирате пакета, изпълнете следната команда: +debian.registry = Настройте този регистър от командния ред: +debian.registry.info = Изберете $distribution и $component от списъка по-долу. +debian.install = За да инсталирате пакета, изпълнете следната команда: +debian.repository = Информация за хранилището +debian.repository.distributions = Дистрибуции +debian.repository.components = Компоненти +debian.repository.architectures = Архитектури +helm.registry = Настройте този регистър от командния ред: +helm.install = За да инсталирате пакета, изпълнете следната команда: +maven.registry = Настройте този регистър във файла на вашия проект pom.xml: +maven.install = За да използвате пакета, включете следното в блока dependencies във файла pom.xml: +maven.install2 = Изпълнете през командния ред: +maven.download = За да изтеглите зависимостта, изпълнете през командния ред: +nuget.registry = Настройте този регистър от командния ред: +nuget.install = За да инсталирате пакета с NuGet, изпълнете следната команда: +nuget.dependency.framework = Целева платформа +npm.registry = Настройте този регистър във файла на вашия проект .npmrc: +npm.install = За да инсталирате пакета с npm, изпълнете следната команда: +npm.install2 = или го добавете във файла package.json: +npm.dependencies.optional = Опционални зависимости +npm.details.tag = Маркер +pub.install = За да инсталирате пакета с Dart, изпълнете следната команда: +pypi.requires = Изисква Python +pypi.install = За да инсталирате пакета с pip, изпълнете следната команда: +rpm.registry = Настройте този регистър от командния ред: +rpm.distros.redhat = на дистрибуции, базирани на RedHat +rpm.distros.suse = на дистрибуции, базирани на SUSE +rpm.install = За да инсталирате пакета, изпълнете следната команда: +rpm.repository = Информация за хранилището +rpm.repository.architectures = Архитектури +rpm.repository.multiple_groups = Този пакет е наличен в няколко групи. +alt.registry = Настройте този регистър от командния ред: +alt.registry.install = За да инсталирате пакета, изпълнете следната команда: +alt.install = Инсталиране на пакет +alt.setup = Добавете хранилище към списъка със свързани хранилища (изберете необходимата архитектура вместо „_arch_“): +alt.repository = Информация за хранилището +alt.repository.architectures = Архитектури +alt.repository.multiple_groups = Този пакет е наличен в няколко групи. +swift.registry = Настройте този регистър от командния ред: +swift.install = Добавете пакета във вашия файл Package.swift: +swift.install2 = и изпълнете следната команда: +vagrant.install = За да добавите Vagrant box, изпълнете следната команда: +settings.link = Свързване на този пакет с хранилище +settings.link.description = Ако свържете пакет с хранилище, пакетът се изброява в списъка с пакети на хранилището. +settings.link.select = Изберете хранилище +settings.link.button = Обновяване на връзката на хранилището +settings.link.success = Връзката на хранилището беше успешно обновена. +settings.link.error = Неуспешно обновяване на връзката на хранилището. +settings.delete.description = Изтриването на пакет е трайно и не може да бъде отменено. +settings.delete.notice = На път сте да изтриете %s (%s). Тази операция е необратима, сигурни ли сте? +owner.settings.cargo.title = Индекс на регистъра на Cargo +owner.settings.cargo.initialize = Инициализиране на индекс +owner.settings.cargo.initialize.description = Необходимо е специално Git хранилище за индекс, за да се използва регистърът на Cargo. Използването на тази опция ще (пре)създаде хранилището и ще го конфигурира автоматично. +owner.settings.cargo.initialize.error = Неуспешно инициализиране на индекса на Cargo: %v +owner.settings.cargo.initialize.success = Индексът на Cargo беше успешно създаден. +owner.settings.cargo.rebuild = Преизграждане на индекс +owner.settings.cargo.rebuild.description = Преизграждането може да бъде полезно, ако индексът не е синхронизиран със съхранените Cargo пакети. +owner.settings.cargo.rebuild.error = Неуспешно преизграждане на индекса на Cargo: %v +owner.settings.cargo.rebuild.success = Индексът на Cargo беше успешно преизграден. +owner.settings.cargo.rebuild.no_index = Не може да се преизгради, няма инициализиран индекс. +owner.settings.cleanuprules.title = Правила за почистване +owner.settings.cleanuprules.add = Добавяне на правило за почистване +owner.settings.cleanuprules.edit = Редактиране на правилото за почистване +owner.settings.cleanuprules.none = Все още няма правила за почистване. +owner.settings.cleanuprules.preview = Преглед на правило за почистване +owner.settings.cleanuprules.preview.overview = %d пакета са насрочени за премахване. +owner.settings.cleanuprules.preview.none = Правилото за почистване не съвпада с нито един пакет. +owner.settings.cleanuprules.enabled = Включено +owner.settings.cleanuprules.pattern_full_match = Прилагане на шаблона към пълното име на пакета +owner.settings.cleanuprules.keep.title = Версиите, които съответстват на тези правила, се запазват, дори ако съответстват на правило за премахване по-долу. +owner.settings.cleanuprules.keep.count = Запазване на най-новите +owner.settings.cleanuprules.keep.count.1 = 1 версия на пакет +owner.settings.cleanuprules.keep.count.n = %d версии на пакет +owner.settings.cleanuprules.keep.pattern = Запазване на версии, съответстващи на +owner.settings.cleanuprules.keep.pattern.container = Версията latest винаги се запазва за Container пакети. +owner.settings.cleanuprules.remove.title = Версиите, които съответстват на тези правила, се премахват, освен ако правило по-горе не казва да се запазят. +owner.settings.cleanuprules.remove.days = Премахване на версии, по-стари от +owner.settings.cleanuprules.remove.pattern = Премахване на версии, съответстващи на +owner.settings.cleanuprules.success.update = Правилото за почистване е обновено. +owner.settings.cleanuprules.success.delete = Правилото за почистване е изтрито. +owner.settings.chef.title = Регистър на Chef +owner.settings.chef.keypair = Генериране на двойка ключове +owner.settings.chef.keypair.description = Заявките, изпратени до регистъра на Chef, трябва да бъдат криптографски подписани като средство за удостоверяване. При генериране на двойка ключове, само публичният ключ се съхранява във Forgejo. Частният ключ ви се предоставя, за да се използва с knife. Генерирането на нова двойка ключове ще презапише предишната. [tool] hours = %d часа @@ -447,7 +721,7 @@ projects.template.desc = Шаблон projects.card_type.text_only = Само текст projects.card_type.images_and_text = Изображения и текст wiki = Уики -wiki.welcome = Добре дошли в Уикито. +wiki.welcome = Добре дошли в уикито. wiki.create_first_page = Създаване на първата страница editor.upload_file = Качване на файл projects.column.color = Цвят @@ -598,7 +872,7 @@ settings.admin_settings = Администраторски настройки issues.role.owner = Притежател settings.transfer.title = Прехвърляне на притежанието issues.author = Автор -issues.closed_at = `затвори тази задача %[2]s` +issues.closed_at = `затвори тази задача %s` settings.collaborator_deletion_desc = Премахването на сътрудник ще отнеме достъпа му до това хранилище. Продължаване? commits.message = Съобщение issues.due_date_not_set = Няма зададен краен срок. @@ -622,9 +896,9 @@ issues.filter_type.all_issues = Всички задачи issues.filter_poster_no_select = Всички автори issues.opened_by = отворена %[1]s от %[3]s issues.action_open = Отваряне -pulls.closed_at = `затвори тази заявка за сливане %[2]s` -pulls.reopened_at = `отвори наново тази заявка за сливане %[2]s` -issues.reopened_at = `отвори наново тази задача %[2]s` +pulls.closed_at = `затвори тази заявка за сливане %s` +pulls.reopened_at = `отвори наново тази заявка за сливане %s` +issues.reopened_at = `отвори наново тази задача %s` projects.column.edit = Редактиране на колоната issues.close = Затваряне на задачата issues.ref_reopened_from = `отвори наново тази задача %[4]s %[2]s` @@ -644,7 +918,7 @@ milestones.filter_sort.latest_due_date = Най-далечен краен сро diff.view_file = Преглед на файла release.deletion_success = Изданието е изтрито. projects.column.delete = Изтриване на колоната -migrate.migrating = Мигриране от %s ... +migrate.migrating = Мигриране от %s … escape_control_characters = Екраниране issues.label_deletion_success = Етикетът е изтрит. pulls.is_closed = Заявката за сливане е затворена. @@ -789,9 +1063,9 @@ editor.no_changes_to_show = Няма промени за показване. issues.choose.get_started = Първи стъпки issues.change_milestone_at = `промени етапа от %s на %s %s` issues.change_project_at = `промени проекта от %s на %s %s` -issues.self_assign_at = `си само-възложи това %s` +issues.self_assign_at = `си самовъзложи това %s` issues.remove_assignee_at = `е премахнат като изпълнител от %s %s` -issues.remove_self_assignment = `се само-премахна като изпълнител %s` +issues.remove_self_assignment = `се самопремахна като изпълнител %s` issues.add_assignee_at = `му бе възложено това от %s %s` pulls.merged_by = от %[3]s бе слята %[1]s pulls.merged_by_fake = от %[2]s бе слята %[1]s @@ -1025,7 +1299,7 @@ issues.content_history.edited = редактирано pulls.title_desc_one = иска да слее %[1]d подаване от %[2]s в %[3]s pulls.showing_specified_commit_range = Показани са само промените между %[1]s..%[2]s pulls.merged_title_desc_one = сля %[1]d подаване от %[2]s в %[3]s %[4]s -pulls.no_merge_access = Не сте упълномощени за сливане на тази заявка за сливане. +pulls.no_merge_access = Не сте упълномощени да слеете тази заявка за сливане. activity.navbar.code_frequency = Честота на промените activity.git_stats_pushed_1 = е изтласкал activity.git_stats_push_to_branch = към %s и @@ -1054,7 +1328,7 @@ issues.dependency.cancel = Отказ issues.dependency.add_error_dep_exists = Зависимостта вече съществува. issues.dependency.add_error_dep_not_exist = Зависимостта не съществува. issues.remove_ref_at = `премахна препратката %s %s` -issues.ref_pull_from = `спомена тази заявка за сливане %[4]s %[2]s` +issues.ref_pull_from = `спомена тази заявка за сливане %[3]s %[1]s` issues.dependency.pr_no_dependencies = Няма зададени зависимости. issues.dependency.remove_info = Премахване на тази зависимост issues.dependency.removed_dependency = `премахна зависимостта %s` @@ -1079,11 +1353,11 @@ issues.dependency.title = Зависимости issues.dependency.issue_no_dependencies = Няма зададени зависимости. issues.dependency.pr_close_blocked = Трябва да затворите всички задачи, блокиращи тази заявка за сливане, преди да можете да я слеете. issues.dependency.pr_close_blocks = Тази заявка за сливане блокира затварянето на следните задачи -issues.ref_issue_from = `спомена тази задача %[4]s %[2]s` -issues.commit_ref_at = `спомена тази задача в подаване %[2]s` +issues.ref_issue_from = `спомена тази задача %[3]s %[1]s` +issues.commit_ref_at = `спомена тази задача в подаване %s` issues.add_ref_at = `добави препратка %s %s` pulls.merged_info_text = Клонът %s вече може да бъде изтрит. -pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %[2]s` +pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %s` issues.change_ref_at = `промени препратката от %s на %s %s` diff.review.reject = Поискване на промени diff.bin_not_shown = Двоичният файл не е показан. @@ -1121,7 +1395,7 @@ issues.review.show_resolved = Показване на решено issues.review.hide_resolved = Скриване на решено issues.review.resolve_conversation = Решаване на обсъждането diff.comment.markdown_info = Поддържа се стилизиране с Маркдаун. -diff.file_suppressed = Разликите не са показани, защото са твърде много +diff.file_suppressed = Разликите във файла са потиснати, защото са твърде много pulls.reject_count_n = %d поискани промени settings.pulls.default_allow_edits_from_maintainers = Позволяване на редакции от поддържащите по подразбиране fork_branch = Клон за клониране в разклонението @@ -1139,7 +1413,7 @@ issues.review.review = Рецензия issues.review.comment = рецензира %s branch.deleted_by = Изтрит от %s branch.restore = Възстановяване на клона „%s“ -archive.title_date = Това хранилище е архивирано на %s. Можете да преглеждате файлове и да го клонирате, но не можете да изтласквате или отваряте задачи или заявки за сливане. +archive.title_date = Това хранилище е архивирано на %s. Можете да преглеждате файлове и да го клонирате, но не можете да правите промени в състоянието му, като изтласкване и създаване на нови задачи, заявки за сливане или коментари. release.download_count_one = %s изтегляне release.download_count_few = %s изтегляния branch.restore_success = Клонът „%s“ е възстановен. @@ -1148,15 +1422,15 @@ branch.create_new_branch = Създаване на клон от клон: pulls.status_checks_show_all = Показване на всички проверки size_format = %[1]s: %[2]s; %[3]s: %[4]s pulls.filter_changes_by_commit = Филтриране по подаване -issues.ref_closing_from = `спомена тази задача в заявка за сливане %[4]s, която ще я затвори, %[2]s` +issues.ref_closing_from = `спомена тази задача в заявка за сливане %[3]s, която ще я затвори, %[1]s` issues.ref_from = `от %[1]s` -issues.ref_reopening_from = `спомена тази задача в заявка за сливане %[4]s, която ще я отвори наново , %[2]s` +issues.ref_reopening_from = `спомена тази задача в заявка за сливане %[3]s, която ще я отвори наново , %[1]s` issues.draft_title = Чернова pulls.reopen_to_merge = Моля, отворете наново тази заявка за сливане, за да извършите сливане. pulls.cant_reopen_deleted_branch = Тази заявка за сливане не може да бъде отворена наново, защото клонът е изтрит. pulls.status_checks_hide_all = Скриване на всички проверки pulls.status_checks_failure = Някои проверки са неуспешни -issues.review.add_review_request = поиска рецензия от %s %s +issues.review.add_review_request = поиска рецензия от %[1]s %[2]s wiki.no_search_results = Няма резултати wiki.search = Търсене в уикито issues.author.tooltip.pr = Този потребител е авторът на тази заявка за сливане. @@ -1196,7 +1470,7 @@ settings.default_branch_desc = Изберете стандартен клон з settings.transfer.button = Прехвърляне на притежанието settings.transfer.modal.title = Прехвърляне на притежанието ambiguous_runes_line = `Този ред съдържа двусмислени Уникод знаци` -ambiguous_character = `%[1]c [U+%04[1]X] може да бъде объркан с %[2]c [U+%04[2]X]` +ambiguous_character = `%[1]c [U+%04[1]X] може да бъде объркан със %[2]c [U+%04[2]X]` invisible_runes_header = `Този файл съдържа невидими Уникод знаци` issues.all_title = Общо issues.new.assign_to_me = Възлагане на мен @@ -1290,6 +1564,312 @@ issues.reaction.alt_few = %[1]s реагира с %[2]s. issues.reaction.alt_many = %[1]s и още %[2]d реагираха с %[3]s. issues.reaction.alt_add = Добавяне на реакция %[1]s към коментара. issues.reaction.alt_remove = Премахване на реакция %[1]s от коментара. +already_forked = Вече сте разклонили %s +generated_from = генерирано от +clear_ref = `Изчистване на текущата препратка` +file_follow = Последване на символната връзка +commitstatus.failure = Неуспех +issues.filter_label_exclude = Използвайте Alt + Click, за да изключите етикети +migrate.migrating_failed = Мигрирането от %s е неуспешно. +migrate.migrating_issues = Мигриране на задачи +mirror_from = огледално на +fork_from_self = Не можете да разклоните хранилище, което притежавате. +commit_graph.hide_pr_refs = Скриване на заявките за сливане +generated = Генерирано +broken_message = Git данните, лежащи в основата на това хранилище, не могат да бъдат прочетени. Свържете се с администратора на тази инстанция или изтрийте това хранилище. +editor.file_is_a_symlink = `„%s“ е символна връзка. Символните връзки не могат да се редактират в уеб редактора` +commits.browse_further = Разглеждане нататък +commits.older = По-стари +form.reach_limit_of_creation_n = Притежателят вече е достигнал лимита от %d хранилища. +issues.edit.already_changed = Неуспешно запазване на промените в задачата. Изглежда съдържанието вече е променено от друг потребител. Моля, презаредете страницата и опитайте да редактирате отново, за да избегнете презаписването на техните промени +transfer.accept_desc = Прехвърляне към „%s“ +archive.title = Това хранилище е архивирано. Можете да преглеждате файлове и да го клонирате, но не можете да правите промени в състоянието му, като изтласкване и създаване на нови задачи, заявки за сливане или коментари. +form.reach_limit_of_creation_1 = Притежателят вече е достигнал лимита от %d хранилище. +editor.patching = Прилагане на кръпка: +editor.fail_to_apply_patch = Неуспешно прилагане на кръпка „%s“ +commits.no_commits = Няма общи подавания. „%s“ и „%s“ имат напълно различни истории. +migrate.migrating_pulls = Мигриране на заявки за сливане +migrate.migrating_topics = Мигриране на теми +projects.desc = Управлявайте задачи и заявки за сливане в проектни табла. +issues.choose.invalid_templates = %v невалидни шаблона са намерени +pulls.edit.already_changed = Неуспешно запазване на промените в заявката за сливане. Изглежда съдържанието вече е променено от друг потребител. Моля, презаредете страницата и опитайте да редактирате отново, за да избегнете презаписването на техните промени +migrate.gitbucket.description = Мигриране на данни от GitBucket инстанции. +migrate.migrating_git = Мигриране на Git данни +commits.newer = По-нови +issues.choose.blank_about = Създаване на задача от стандартен шаблон. +issues.filter_no_results = Няма резултати +issues.filter_no_results_placeholder = Опитайте да коригирате филтрите си за търсене. +archive.nocomment = Коментирането не е възможно, тъй като хранилището е архивирано. +migrate.gitlab.description = Мигриране на данни от gitlab.com или други GitLab инстанции. +transfer.no_permission_to_accept = Нямате разрешение да приемете това прехвърляне. +transfer.no_permission_to_reject = Нямате разрешение да отхвърлите това прехвърляне. +editor.file_changed_while_editing = Съдържанието на файла е променено, откакто сте го отворили. Щракнете тук, за да го видите, или Подайте промените отново, за да ги презапишете. +sync_fork.button = Синхронизиране +migrate.onedev.description = Мигриране на данни от code.onedev.io или други OneDev инстанции. +migrate.codebase.description = Мигриране на данни от codebasehq.com. +migrate.migrating_labels = Мигриране на етикети +migrate.migrating_releases = Мигриране на издания +editor.push_rejected_no_message = Промяната беше отхвърлена от сървъра без съобщение. Моля, проверете Git куките. +issues.choose.open_external_link = Отваряне +comments.edit.already_changed = Неуспешно запазване на промените в коментара. Изглежда съдържанието вече е променено от друг потребител. Моля, презаредете страницата и опитайте да редактирате отново, за да избегнете презаписването на техните промени +commits.nothing_to_compare = Тези клонове са равни. +transfer.reject_desc = Отказ от прехвърляне към „%s“ +subscribe.pull.guest.tooltip = Влезте, за да се абонирате за тази заявка за сливане. +commit.contained_in_default_branch = Това подаване е част от стандартния клон +normal_view = Нормален изглед +issues.context.menu = Меню за коментара +form.name_reserved = Името на хранилището „%s“ е резервирано. +need_auth = Упълномощаване +subscribe.issue.guest.tooltip = Влезте, за да се абонирате за тази задача. +commitstatus.pending = В очакване +commitstatus.success = Успех +editor.cannot_commit_to_protected_branch = Не може да се подава в защитения клон „%s“. +editor.no_commit_to_branch = Не може да се подава директно в клона, защото: +editor.push_rejected = Промяната беше отхвърлена от сървъра. Моля, проверете Git куките. +cite_this_repo = Цитиране на това хранилище +migrate.gitea.description = Мигриране на данни от gitea.com или други Gitea инстанции. +editor.push_rejected_summary = Пълно съобщение на отхвърлянето: +sync_fork.branch_behind_one = Този клон е %[1]d подаване зад %[2]s +sync_fork.branch_behind_few = Този клон е %[1]d подавания зад %[2]s +form.string_too_long = Даденият низ е по-дълъг от %d знака. +editor.commit_id_not_matching = Файлът е променен, докато сте го редактирали. Подайте в нов клон и след това слейте. +editor.user_no_push_to_branch = Потребителят не може да изтласква в клона +archive.pull.noreview = Това хранилище е архивирано. Не можете да рецензирате заявки за сливане. +migrate.migrating_failed.error = Неуспешно мигриране: %s +migrate.github.description = Мигриране на данни от github.com или GitHub Enterprise сървър. +migrate.forgejo.description = Мигриране на данни от codeberg.org или други Forgejo инстанции. +migrate.gogs.description = Мигриране на данни от notabug.org или други Gogs инстанции. +migrate.migrating_milestones = Мигриране на етапи +migrate.failed = Мигрирането е неуспешно: %v +pulls.nothing_to_compare_and_allow_empty_pr = Тези клонове са равни. Тази заявка за сливане ще бъде празна. +pulls.has_pull_request = `Вече съществува заявка за сливане между тези клонове: %[2]s#%[3]d` +pulls.is_checking = Проверката за конфликти при сливане е в ход. Опитайте отново след няколко минути. +pulls.cannot_merge_work_in_progress = Тази заявка за сливане е отбелязана като в процес на работа. +pulls.blocked_by_approvals = Тази заявка за сливане все още няма достатъчно одобрения. Дадени са %d от %d одобрения. +pulls.blocked_by_rejection = Тази заявка за сливане има поискани промени от официален рецензент. +pulls.waiting_count_1 = %d чакаща рецензия +pulls.status_checks_requested = Задължително +pulls.update_branch_success = Обновяването на клона е успешно +pulls.cannot_auto_merge_helper = Слейте ръчно, за да разрешите конфликтите. +migrate.clone_address_desc = HTTP(S) или Git „clone“ URL на съществуващо хранилище +pulls.add_prefix = Добавете префикс %s +pulls.merge_pull_request = Създаване на подаване със сливане +pulls.waiting_count_n = %d чакащи рецензии +pulls.is_ancestor = Този клон вече е включен в целевия клон. Няма какво да се слива. +pulls.required_status_check_missing = Някои задължителни проверки липсват. +pulls.change_target_branch_at = `промени целевия клон от %s на %s %s` +issues.time_spent_total = Общо изразходвано време +issues.del_time_history = `изтри изразходваното време %s` +pulls.nothing_to_compare_have_tag = Избраните клон/маркер са равни. +pulls.cannot_auto_merge_desc = Тази заявка за сливане не може да бъде слята автоматично поради конфликти. +issues.tracker_auto_close = Таймерът ще бъде спрян автоматично, когато тази задача бъде затворена +issues.force_push_codes = `изтласка принудително %[1]s от %[2]s към %[4]s %[6]s` +pulls.blocked_by_official_review_requests = Тази заявка за сливане е блокирана, защото липсва одобрение от един или повече официални рецензенти. +issues.tracker = Проследяване на времето +issues.add_time_history = `добави изразходвано време %s` +migrate.repo_desc_helper = Оставете празно, за да внесете съществуващото описание +migrate.git.description = Мигриране само на хранилище от всяка Git услуга. +mirror_sync = синхронизирано +migrate_repo = Мигриране на хранилище +migrate_options = Опции за мигрирането +editor.fork_before_edit = Трябва да разклоните това хранилище, за да направите или предложите промени в този файл. +editor.must_have_write_access = Трябва да имате право на запис, за да правите или предлагате промени в този файл. +editor.new_branch_name = Дайте име на новия клон за това подаване +editor.invalid_commit_mail = Невалидна ел. поща за създаване на подаване. +pulls.required_status_check_failed = Някои задължителни проверки не са успешни. +issues.time_spent_from_all_authors = `Общо изразходвано време: %s` +issues.attachment.download = `Щракнете, за да изтеглите „%s“` +issues.attachment.open_tab = `Щракнете, за да видите „%s“ в нов раздел` +pulls.update_branch = Обновяване на клона чрез сливане +migrate_items = Елементи за мигриране +commit.load_referencing_branches_and_tags = Зареждане на клонове и маркери, препращащи към това подаване +pulls.files_conflicted = Тази заявка за сливане има промени, които са в конфликт с целевия клон. +pulls.still_in_progress = Все още е в процес на работа? +pulls.ready_for_review = Готово е за рецензиране? +pulls.is_empty = Промените в този клон вече са в целевия клон. Това ще бъде празно подаване. +issues.start_tracking = Започване на проследяване на времето +migrate_options_mirror_helper = Това хранилище ще бъде огледално +migrate_options_lfs = Мигриране на LFS файлове +editor.upload_file_is_locked = Файлът „%s“ е заключен от %s. +issues.tracking_already_started = `Вече сте започнали проследяване на времето по друга задача!` +pulls.remove_prefix = Премахнете префикса %s +author_search_tooltip = Показва максимум 30 потребители +migrate.migrating_failed_no_addr = Мигрирането е неуспешно. +issues.force_push_compare = Сравняване +pulls.status_checking = Някои проверки са в очакване +pulls.nothing_to_compare = Тези клонове са равни. Не е нужно да създавате заявка за сливане. + +rss.must_be_on_branch = Трябва да сте на клон, за да имате RSS емисия. +admin.manage_flags = Управление на флаговете +admin.enabled_flags = Флагове, включени за хранилището: +admin.update_flags = Обновяване на флаговете +admin.failed_to_replace_flags = Неуспешна замяна на флаговете на хранилището +admin.flags_replaced = Флаговете на хранилището са заменени +fork_to_different_account = Разклоняване в друг акаунт +mirror_interval = Интервал на огледалото (валидни единици за време са „h“, „m“, „s“). 0 за изключване на периодичната синхронизация. (Минимален интервал: %s) +mirror_interval_invalid = Интервалът на огледалото не е валиден. +mirror_use_ssh.text = Използване на SSH удостоверяване +mirror_use_ssh.helper = Forgejo ще създаде огледало на хранилището чрез Git през SSH и ще генерира двойка ключове за вас, когато изберете тази опция. Трябва да се уверите, че генерираният публичен ключ е упълномощен да изтласква към целевото хранилище. Не можете да използвате удостоверяване, базирано на парола, когато избирате това. +mirror_use_ssh.not_available = SSH удостоверяването не е налично. +mirror_denied_combination = Не може да се използва удостоверяване с публичен ключ и парола едновременно. +mirror_sync_on_commit = Синхронизиране при изтласкване на подавания +mirror_address_desc = Поставете всички необходими данни за удостоверяване в секцията „Упълномощаване“. +mirror_address_url_invalid = Предоставеният URL е невалиден. Трябва да екранирате правилно всички компоненти на URL адреса. +mirror_address_protocol_invalid = Предоставеният URL е невалиден. Само http(s):// или git:// адреси могат да се използват за огледални хранилища. +mirror_lfs = Съхранение на големи файлове (LFS) +mirror_password_help = Променете потребителското име, за да изтриете запазена парола. +unit_disabled = Администраторът на сайта е изключил тази секция на хранилището. +summary_card_alt = Карта с обобщение на хранилище %s +template.items = Елементи на шаблона +template.git_content = Git съдържание (стандартен клон) +template.git_hooks = Git куки +template.git_hooks_tooltip = В момента не можете да променяте или премахвате Git куки, след като са добавени. Изберете това само ако се доверявате на шаблонното хранилище. +template.one_item = Трябва да изберете поне един елемент от шаблона +template.invalid = Трябва да изберете шаблонно хранилище +migrate.cancel_migrating_title = Отказ от миграцията +migrate.cancel_migrating_confirm = Искате ли да откажете тази миграция? +invisible_runes_description = `Този файл съдържа невидими Уникод знаци, които са неразличими за хората, но могат да бъдат обработени по различен начин от компютър. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` +ambiguous_runes_header = `Този файл съдържа двусмислени Уникод знаци` +ambiguous_runes_description = `Този файл съдържа Уникод знаци, които могат да бъдат объркани с други знаци. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` +file_copy_permalink = Копиране на постоянна връзка +view_git_blame = Преглед на git blame +video_not_supported_in_browser = Вашият браузър не поддържа HTML5 тага „video“. +audio_not_supported_in_browser = Вашият браузър не поддържа HTML5 тага „audio“. +stored_lfs = Съхранено с Git LFS +commit_graph.select = Изберете клонове +editor.cannot_edit_lfs_files = LFS файлове не могат да се редактират в уеб интерфейса. +editor.filename_help = Добавете директория, като въведете името ѝ, последвано от наклонена черта („/“). Премахнете директория, като натиснете backspace в началото на полето за въвеждане. +editor.commit_signed_changes = Подаване на подписани промени +editor.require_signed_commit = Клонът изисква подписано подаване +editor.commit_email = Ел. поща на подаването +commits.desc = Разглеждане на историята на промените в програмния код. +commits.search.tooltip = Можете да добавите префикс към ключовите думи с „author:“, „committer:“, „after:“ или „before:“, напр. „revert author:Alice before:2019-01-13“. +commits.signed_by = Подписано от +commits.signed_by_untrusted_user = Подписано от недоверен потребител +commits.signed_by_untrusted_user_unmatched = Подписано от недоверен потребител, който не съвпада с подаващия +commits.ssh_key_fingerprint = Отпечатък на SSH ключ +commits.view_single_diff = Преглед на промените в този файл, въведени в това подаване +commit.revert = Връщане +commit.revert-header = Връщане: %s +commit.revert-content = Изберете клон, върху който да се върне: +issues.desc = Организирайте доклади за грешки, задачи и етапи. +issues.choose.ignore_invalid_templates = Невалидните шаблони са игнорирани +issues.choose.invalid_config = Конфигурацията на задачите съдържа грешки: +issues.filter_type.all_pull_requests = Всички заявки за сливане +issues.role.member_helper = Този потребител е участник в организацията, притежаваща това хранилище. +issues.lock.unknown_reason = Не може да се заключи задача с неизвестна причина. +issues.lock_duplicate = Задача не може да бъде заключена два пъти. +issues.unlock_error = Не може да се отключи задача, която не е заключена. +issues.lock.notice_1 = - Други потребители не могат да добавят нови коментари към тази задача. +issues.lock.notice_2 = - Вие и други сътрудници с достъп до това хранилище все още можете да оставяте коментари, които другите да виждат. +issues.lock.notice_3 = - Винаги можете да отключите тази задача отново в бъдеще. +issues.unlock.notice_1 = - Всеки ще може отново да коментира тази задача. +issues.unlock.notice_2 = - Винаги можете да заключите тази задача отново в бъдеще. +issues.lock.title = Заключване на обсъждането по тази задача. +issues.unlock.title = Отключване на обсъждането по тази задача. +issues.comment_on_locked = Не можете да коментирате заключена задача. +issues.delete.text = Наистина ли искате да изтриете тази задача? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) +issues.cancel_tracking_history = `отмени проследяването на времето %s` +issues.add_time_sum_to_small = Не е въведено време. +issues.due_date_form = гггг-мм-дд +issues.due_date_invalid = Крайният срок е невалиден или извън обхвата. Моля, използвайте формата „гггг-мм-дд“. +issues.dependency.no_permission_1 = Нямате разрешение да прочетете %d зависимост +issues.dependency.no_permission_n = Нямате разрешение да прочетете %d зависимости +issues.dependency.no_permission.can_remove = Нямате разрешение да прочетете тази зависимост, но можете да я премахнете +issues.dependency.issue_batch_close_blocked = Не могат да бъдат затворени групово избраните задачи, защото задача #%d все още има отворени зависимости +issues.dependency.blocked_by_short = Зависи от +issues.dependency.setting = Включване на зависимости за задачи и заявки за сливане +issues.dependency.add_error_same_issue = Не можете да направите задача зависима от самата нея. +issues.dependency.add_error_dep_issue_not_exist = Зависимата задача не съществува. +issues.dependency.add_error_cannot_create_circular = Не можете да създадете зависимост с две задачи, които се блокират взаимно. +issues.dependency.add_error_dep_not_same_repo = И двете задачи трябва да са в едно и също хранилище. +issues.review.self.rejection = Не можете да поискате промени в собствената си заявка за сливане. +issues.review.dismissed = отхвърли рецензията на %s %s +issues.review.content.empty = Трябва да оставите коментар, посочващ исканите промени. +issues.review.add_review_requests = поиска рецензии от %[1]s %[2]s +issues.review.remove_review_request = премахна заявката за рецензия за %[1]s %[2]s +issues.review.remove_review_requests = премахна заявките за рецензия за %[1]s %[2]s +issues.review.remove_review_request_self = отказа да рецензира %s +issues.review.pending.tooltip = Този коментар в момента не е видим за други потребители. За да изпратите изчакващите си коментари, изберете „%s“ -> „%s/%s/%s“ в горната част на страницата. +issues.review.outdated = Остарял +issues.review.outdated_description = Съдържанието е променено, след като е направен този коментар +issues.review.show_outdated = Показване на остарели +issues.review.hide_outdated = Скриване на остарели +issues.content_history.options = Опции +issues.blocked_by_user = Не можете да създавате задачи в това хранилище, защото сте блокирани от притежателя на хранилището. +comment.blocked_by_user = Коментирането не е възможно, защото сте блокирани от притежателя на хранилището или от автора. +issues.reopen.blocked_by_user = Не можете да отворите наново тази задача, защото сте блокирани от притежателя на хранилището или от автора на тази задача. +compare.compare_base = основа +compare.compare_head = сравняване +pulls.desc = Включване на заявки за сливане и рецензии на код. +pulls.view = Преглед на заявката за сливане +pulls.allow_edits_from_maintainers_desc = Потребители с право на запис в основния клон могат също да изтласкват към този клон +pulls.allow_edits_from_maintainers_err = Обновяването е неуспешно +pulls.has_changed_since_last_review = Променено след последната ви рецензия +pulls.switch_comparison_type = Превключване на типа сравнение +pulls.filter_branch = Филтриране на клон +pulls.review_only_possible_for_full_diff = Рецензирането е възможно само при преглед на пълните разлики +pulls.wrong_commit_id = ID на подаването трябва да бъде ID на подаване в целевия клон +pulls.blocked_by_user = Не можете да създадете заявка за сливане в това хранилище, защото сте блокирани от притежателя на хранилището. +pulls.no_merge_desc = Тази заявка за сливане не може да бъде слята, защото всички опции за сливане в хранилището са изключени. +pulls.no_merge_helper = Включете опциите за сливане в настройките на хранилището или слейте заявката за сливане ръчно. +pulls.no_merge_wip = Тази заявка за сливане не може да бъде слята, защото е отбелязана като в процес на работа. +pulls.squash_merge_pull_request = Създаване на сплескано подаване +pulls.merge_manually = Ръчно слята +pulls.merge_commit_id = ID на подаването със сливане +pulls.require_signed_wont_sign = Клонът изисква подписани подавания, но това сливане няма да бъде подписано +pulls.merge_conflict = Сливането е неуспешно: Възникна конфликт по време на сливането. Подсказка: Опитайте различна стратегия +pulls.merge_conflict_summary = Съобщение за грешка +pulls.rebase_conflict_summary = Съобщение за грешка +pulls.has_merged = Неуспешно: Заявката за сливане е слята, не можете да слеете отново или да промените целевия клон. +pulls.push_rejected = Изтласкването е неуспешно: Изтласкването е отхвърлено. Прегледайте Git куките за това хранилище. +pulls.push_rejected_no_message = Изтласкването е неуспешно: Изтласкването е отхвърлено, но няма отдалечено съобщение. Прегледайте Git куките за това хранилище +pulls.update_not_allowed = Нямате разрешение да обновявате клона +pulls.outdated_with_base_branch = Този клон е остарял спрямо основния клон +pulls.cmd_instruction_merge_warning = Предупреждение: Настройката „Автоматично откриване на ръчно сливане“ не е включена за това хранилище, ще трябва да отбележите тази заявка за сливане като ръчно слята след това. +pulls.editable_explanation = Тази заявка за сливане позволява редакции от поддържащите. Можете да допринесете директно към нея. +pulls.auto_merge_button_when_succeed = (Когато проверките са успешни) +pulls.auto_merge_when_succeed = Автоматично сливане, когато всички проверки са успешни +pulls.auto_merge_newly_scheduled = Заявката за сливане е насрочена за сливане, когато всички проверки са успешни. +pulls.auto_merge_has_pending_schedule = %[1]s насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[2]s. +pulls.auto_merge_cancel_schedule = Отмяна на автоматичното сливане +pulls.auto_merge_not_scheduled = Тази заявка за сливане не е насрочена за автоматично сливане. +pulls.auto_merge_canceled_schedule = Автоматичното сливане е отменено за тази заявка за сливане. +pulls.auto_merge_newly_scheduled_comment = `насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[1]s` +pulls.auto_merge_canceled_schedule_comment = `отмени автоматичното сливане на тази заявка за сливане, когато всички проверки са успешни %[1]s` +pulls.delete.title = Да се изтрие ли тази заявка за сливане? +pulls.delete.text = Наистина ли искате да изтриете тази заявка за сливане? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) +diff.data_not_available = Съдържанието на разликите не е налично +diff.bin = ДВОИЧЕН +diff.file_suppressed_line_too_long = Разликите във файла са потиснати, защото един или повече редове са твърде дълги +diff.too_many_files = Някои файлове не бяха показани, защото твърде много файлове имат промени в тези разлики +diff.show_more = Показване на още +diff.generated = генериран +diff.comment.add_line_comment = Добавяне на коментар към ред +diff.comment.add_review_comment = Добавяне на коментар +diff.review.self_reject = Авторите на заявки за сливане не могат да поискват промени в собствените си заявки +diff.review.self_approve = Авторите на заявки за сливане не могат да одобряват собствените си заявки +diff.image.side_by_side = Едно до друго +diff.image.swipe = Плъзгане +diff.image.overlay = Наслагване +diff.has_escaped = Този ред има скрити Уникод знаци +release.tag_name_protected = Името на маркера е защитено. +release.add_tag_msg = Използване на заглавието и съдържанието на изданието като съобщение на маркера. +release.hide_archive_links = Скриване на автоматично генерираните архиви +release.hide_archive_links_helper = Скрийте автоматично генерираните архиви с програмен код за това издание. Например, ако качвате свои собствени. +release.asset_external_url = Външен URL адрес +release.summary_card_alt = Карта с обобщение на издание със заглавие „%s“ в хранилище %s +branch.protected_deletion_failed = Клонът „%s“ е защитен. Не може да бъде изтрит. +branch.default_deletion_failed = Клонът „%s“ е стандартният клон. Не може да бъде изтрит. +branch.included_desc = Този клон е част от стандартния клон +branch.included = Включен +branch.warning_rename_default_branch = Преименувате стандартния клон. +topic.count_prompt = Не можете да изберете повече от 25 теми +find_file.no_matching = Не е намерен съвпадащ файл +error.csv.too_large = Не може да се визуализира този файл, защото е твърде голям. +error.csv.unexpected = Не може да се визуализира този файл, защото съдържа неочакван знак на ред %d и колона %d. +error.csv.invalid_field_count = Не може да се визуализира този файл, защото има грешен брой полета на ред %d. +error.broken_git_hook = Git куките на това хранилище изглеждат повредени. Моля, последвайте документацията, за да ги поправите, след което изтласкайте подавания, за да обновите статуса. [modal] confirm = Потвърждаване @@ -1319,6 +1899,11 @@ table_modal.placeholder.content = Съдържание table_modal.placeholder.header = Заглавка buttons.new_table.tooltip = Добавяне на таблица table_modal.header = Добавяне на таблица +link_modal.description = Описание +link_modal.header = Добавяне на връзка +buttons.indent.tooltip = Вмъкване на елементи с едно ниво +buttons.unindent.tooltip = Изваждане на елементи с едно ниво +link_modal.paste_reminder = Подсказка: С URL адрес в клипборда можете да поставите директно в редактора, за да създадете връзка. [org] teams.write_access = Писане @@ -1373,18 +1958,18 @@ follow_blocked_user = Не можете да следвате тази орга settings.delete_prompt = Организацията ще бъде премахната завинаги. Това НЕ МОЖЕ да бъде отменено! settings.labels_desc = Добавете етикети, които могат да се използват за задачи за всички хранилища в тази организация. teams.none_access = Без достъп -teams.members.none = Няма членове в този екип. +teams.members.none = Няма участници в този екип. repo_updated = Обновено %s teams.delete_team_success = Екипът е изтрит. teams.search_repo_placeholder = Потърсете хранилище… teams.delete_team_title = Изтриване на екипа -teams.add_team_member = Добавяне на член на екипа +teams.add_team_member = Добавяне на участник в екипа teams.read_access_helper = Членовете могат да преглеждат и клонират хранилищата на екипа. teams.invite.description = Моля, щракнете върху бутона по-долу, за да се присъедините към екипа. teams.invite.title = Поканени сте да се присъедините към екип %s в организация %s. team_permission_desc = Разрешение members.public_helper = Да е скрит -teams.members = Членове на екипа +teams.members = Участници в екипа teams.delete_team = Изтриване на екипа members.owner = Притежател members.member_role = Роля на участника: @@ -1393,11 +1978,44 @@ members.private_helper = Да е видим teams.no_desc = Този екип няма описание settings.delete_org_desc = Тази организация ще бъде изтрита перманентно. Продължаване? open_dashboard = Отваряне на таблото +settings.change_orgname_prompt = Бележка: Промяната на името на организацията ще промени и URL адреса на вашата организация и ще освободи старото име. + +team_access_desc = Достъп до хранилище +team_unit_desc = Разрешаване на достъп до секции на хранилището +team_unit_disabled = (Изключено) +form.name_reserved = Името на организацията „%s“ е резервирано. +form.name_pattern_not_allowed = Шаблонът „%s“ не е разрешен в име на организация. +form.create_org_not_allowed = Нямате разрешение да създавате организация. +settings.update_setting_success = Настройките на организацията са обновени. +settings.change_orgname_redirect_prompt = Старото име ще се пренасочва, докато не бъде взето. +settings.change_orgname_redirect_prompt.with_cooldown.one = Старото име на организацията ще бъде достъпно за всички след период на изчакване от %[1]d ден. Все още можете да си върнете старото име по време на периода на изчакване. +settings.change_orgname_redirect_prompt.with_cooldown.few = Старото име на организацията ще бъде достъпно за всички след период на изчакване от %[1]d дни. Все още можете да си върнете старото име по време на периода на изчакване. +settings.update_avatar_success = Профилната снимка на организацията е обновена. +settings.hooks_desc = Добавете уеб-куки, които ще се задействат за всички хранилища в тази организация. +members.membership_visibility = Видимост на участничеството: +members.public = Видим +members.private = Скрит +members.invite_desc = Добавяне на нов участник към %s: +members.invite_now = Поканване сега +teams.admin_access = Администраторски достъп +teams.invite_team_member = Поканване в %s +teams.invite_team_member.list = Чакащи покани +teams.delete_team_desc = Изтриването на екип отнема достъпа до хранилището от неговите участници. Продължаване? +teams.remove_all_repos_desc = Това ще премахне всички хранилища от екипа. +teams.add_all_repos_title = Добавяне на всички хранилища +teams.add_all_repos_desc = Това ще добави всички хранилища на организацията към екипа. +teams.add_nonexistent_repo = Хранилището, което се опитвате да добавите, не съществува, моля, първо го създайте. +teams.add_duplicate_users = Потребителят вече е участник в екипа. +teams.repos.none = Няма хранилища, до които този екип да има достъп. +teams.specific_repositories = Конкретни хранилища +teams.specific_repositories_helper = Участниците ще имат достъп само до хранилища, изрично добавени към екипа. Избирането на това няма автоматично да премахне хранилища, вече добавени с Всички хранилища. +teams.all_repositories_helper = Екипът има достъп до всички хранилища. Избирането на това ще добави всички съществуващи хранилища към екипа. +teams.invite.by = Поканен от %s [install] admin_password = Парола user = Потребителско име -admin_email = Адрес на ел. поща +admin_email = Адрес за ел. поща path = Път password = Парола host = Хост @@ -1427,13 +2045,17 @@ admin_title = Настройки на администраторския ака err_empty_admin_password = Администраторската парола не може да бъде празна. docker_helper = Ако стартирате Forgejo в Docker, моля, прочетете документацията преди да промените настройки. sqlite_helper = Път на файла за SQLite3 базата данни.
Въведете абсолютен път, ако стартирате Forgejo като service. -err_empty_admin_email = Администраторският адрес на ел. поща не може да бъде празен. +err_empty_admin_email = Администраторският адрес за ел. поща не може да бъде празен. password_algorithm = Алгоритъм за хеш. на паролите -default_keep_email_private = Скриване на адресите на ел. поща по подразбиране +default_keep_email_private = Скриване на адресите за ел. поща по подразбиране invalid_password_algorithm = Невалиден алгоритъм за хеш. на паролите err_admin_name_is_reserved = Потребителското име на администратора е невалидно, потребителското име е резервирано err_admin_name_pattern_not_allowed = Потребителското име на администратора е невалидно, потребителското име съответства с резервиран шаблон err_admin_name_is_invalid = Потребителското име на администратора е невалидно +db_schema_helper = Оставете празно за схемата по подразбиране на базата данни („public“). +reinstall_error = Опитвате се да инсталирате върху съществуваща Forgejo база данни +reinstall_confirm_message = Преинсталирането със съществуваща Forgejo база данни може да причини множество проблеми. В повечето случаи трябва да използвате съществуващия си „app.ini“, за да стартирате Forgejo. Ако знаете какво правите, потвърдете следното: +app_slogan = Слоган на инстанцията [filter] string.asc = А - Я @@ -1462,8 +2084,8 @@ link_not_working_do_paste = Ако връзката не работи, опит activate_account = Моля, активирайте своя акаунт admin.new_user.subject = Нов потребител %s току-що се регистрира activate_account.text_1 = Здравейте, %[1]s, благодарим ви за регистрацията в %[2]s! -activate_email.text = Моля, щракнете върху следната връзка, за да потвърдите своя адрес на ел. поща в рамките на %s: -activate_email = Потвърдете своя адрес на ел. поща +activate_email.text = Моля, щракнете върху следната връзка, за да потвърдите своя адрес за ел. поща в рамките на %s: +activate_email = Потвърдете своя адрес за ел. поща activate_account.text_2 = Моля, щракнете върху следната връзка, за да активирате своя акаунт в рамките на %s: issue_assigned.issue = @%[1]s ви възложи задача %[2]s в хранилище %[3]s. issue.action.push_n = @%[1]s изтласка %[3]d подавания към %[2]s @@ -1473,6 +2095,28 @@ issue.action.merge = @%[1]s сля #%[2]d в %[3]s. issue_assigned.pull = @%[1]s ви възложи заявката за сливане %[2]s в хранилище %[3]s. issue.action.ready_for_review = @%[1]s отбеляза тази заявка за сливане като готова за рецензиране. repo.transfer.subject_to = %s иска да прехвърли хранилище "%s" към %s +password_change.subject = Вашата парола е променена +admin.new_user.text = Моля, щракнете тук, за да управлявате този потребител от администраторския панел. +password_change.text_1 = Паролата за вашия акаунт току-що беше променена. +reset_password = Възстановете своя акаунт +account_security_caution.text_1 = Ако това сте били вие, можете спокойно да игнорирате това ел. писмо. +issue.action.force_push = %[1]s изтласка принудително %[2]s от %[3]s към %[4]s. +team_invite.text_3 = Бележка: Тази покана е предназначена за %[1]s. Ако не сте очаквали тази покана, можете да игнорирате това ел. писмо. +view_it_on = Вижте го на %s +register_notify.text_1 = това е ел. писмо за потвърждение на вашата регистрация в %s! +register_notify.text_2 = Можете да влезете в акаунта си с потребителско име: %s +register_notify.text_3 = Ако някой друг е създал този акаунт за вас, първо ще трябва да зададете парола. +repo.collaborator.added.subject = %s ви добави към %s като сътрудник +primary_mail_change.text_1 = Основният адрес за ел. поща на вашия акаунт току-що беше променен на %[1]s. Това означава, че този адрес за ел. поща повече няма да получава известия по ел. поща за вашия акаунт. +team_invite.text_2 = Моля, щракнете върху следната връзка, за да се присъедините към екипа: +repo.transfer.body = За да го приемете или отхвърлите, посетете %s или просто го игнорирайте. +repo.collaborator.added.text = Бяхте добавени като сътрудник в хранилище: +team_invite.subject = %[1]s ви покани да се присъедините към организацията %[2]s +team_invite.text_1 = %[1]s ви покани да се присъедините към екип %[2]s в организация %[3]s. +reply = или отговорете директно на това ел. писмо +reset_password.text = Ако това сте вие, моля, щракнете върху следната връзка, за да възстановите акаунта си в рамките на %s: +primary_mail_change.subject = Основният ви адрес за ел. поща е променен +account_security_caution.text_2 = Ако това не сте били вие, акаунтът ви е компрометиран. Моля, свържете се с администраторите на този сайт. [user] joined_on = Присъединени на %s @@ -1493,7 +2137,7 @@ follow = Последване followers_few = %d последователи block_user = Блокиране на потребителя change_avatar = Променете профилната си снимка… -email_visibility.limited = Вашият адрес на ел. поща е видим за всички удостоверени потребители +email_visibility.limited = Вашият адрес за ел. поща е видим за всички удостоверени потребители disabled_public_activity = Този потребител е изключил публичната видимост на дейността. email_visibility.private = Вашият адрес на ел. поща е видим само за вас и администраторите show_on_map = Показване на това място на картата @@ -1507,6 +2151,15 @@ public_activity.visibility_hint.self_public = Вашата дейност е в form.name_pattern_not_allowed = Шаблонът "%s" не е разрешен в потребителско име. form.name_reserved = Потребителското име "%s" е резервирано. public_activity.visibility_hint.self_private_profile = Вашата дейност е видима само за вас и администраторите на инстанцията, тъй като вашият профил е частен. Конфигуриране. +block_user.detail = Моля, имайте предвид, че блокирането на потребител има и други ефекти, като например: +block_user.detail_2 = Този потребител няма да може да взаимодейства с хранилищата, които притежавате, или със задачите и коментарите, които сте създали. +block_user.detail_3 = Няма да можете да се добавяте един друг като сътрудници на хранилище. +public_activity.visibility_hint.self_private = Вашата дейност е видима само за вас и администраторите на инстанцията. Конфигуриране. +form.name_chars_not_allowed = Потребителското име „%s“ съдържа невалидни знаци. +public_activity.visibility_hint.admin_private = Тази дейност е видима за вас, защото сте администратор, но потребителят иска тя да остане частна. +public_activity.visibility_hint.admin_public = Тази дейност е видима за всички, но като администратор можете да виждате и взаимодействия в частни пространства. +follow_blocked_user = Не можете да последвате този потребител, защото сте го блокирали или той ви е блокирал. +block_user.detail_1 = Ще спрете да се следвате един друг и няма да можете да се последвате отново. [home] filter = Други филтри @@ -1530,6 +2183,7 @@ view_home = Преглед на %s collaborative_repos = Съвместни хранилища switch_dashboard_context = Превключване на контекста на таблото show_only_public = Показване само на публични +filter_by_team_repositories = Филтриране по хранилища на екипа [admin] packages.version = Версия @@ -1587,7 +2241,7 @@ config.server_config = Сървърна конфигурация packages.size = Размер settings = Админ. настройки users = Потребителски акаунти -emails.duplicate_active = Този адрес на ел. поща вече е активен за друг потребител. +emails.duplicate_active = Този адрес за ел. поща вече е активен за друг потребител. config.app_ver = Forgejo версия config.custom_conf = Път на конфигурационния файл config.git_version = Git версия @@ -1606,16 +2260,20 @@ users.details = Потребителски данни packages.total_size = Общ размер: %s dashboard.new_version_hint = Forgejo %s вече е наличен, вие изпълнявате %s. Проверете блога за повече подробности. total = Общо: %d +config.db_type = Тип +monitor.queue.type = Тип +notices.type = Тип [error] not_found = Целта не може да бъде намерена. report_message = Ако смятате, че това е грешка на Forgejo, моля, потърсете в задачите на Codeberg или отворете нова задача, ако е необходимо. network_error = Мрежова грешка occurred = Възникна грешка +server_internal = Вътрешна грешка на сървъра [form] UserName = Потребителско име -Email = Адрес на ел. поща +Email = Адрес за ел. поща Password = Парола RepoName = Име на хранилището username_been_taken = Потребителското име вече е заето. @@ -1633,8 +2291,8 @@ url_error = `„%s“ не е валиден URL.` Content = Съдържание team_not_exist = Екипът не съществува. TeamName = Име на екипа -email_error = ` не е валиден адрес на ел. поща.` -email_invalid = Адресът на ел. поща е невалиден. +email_error = ` не е валиден адрес за ел. поща.` +email_invalid = Адресът за ел. поща е невалиден. SSHTitle = Име на SSH ключ repo_name_been_taken = Името на хранилището вече е използвано. team_name_been_taken = Името на екипа вече е заето. @@ -1647,6 +2305,56 @@ Pronouns = Местоимения Biography = Биография Website = Уебсайт Location = Местоположение +cannot_add_org_to_team = Организация не може да бъде добавена като участник в екип. +auth_failed = Неуспешно удостоверяване: %v +team_no_units_error = Разрешете достъп до поне една секция на хранилището. +password_uppercase_one = Поне един голям знак +CommitSummary = Обобщение на подаването +username_error = ` може да съдържа само буквено-цифрови знаци („0-9“, „a-z“, „A-Z“), тире („-“), долна черта („_“) и точка („.“). Не може да започва или завършва с не-буквено-цифрови знаци, като също така са забранени и последователни не-буквено-цифрови знаци.` +username_error_no_dots = ` може да съдържа само буквено-цифрови знаци („0-9“, „a-z“, „A-Z“), тире („-“) и долна черта („_“). Не може да започва или завършва с не-буквено-цифрови знаци, като също така са забранени и последователни не-буквено-цифрови знаци.` +duplicate_invite_to_team = Потребителят вече е поканен като участник в екипа. +must_use_public_key = Ключът, който предоставихте, е частен ключ. Моля, не качвайте частния си ключ никъде. Вместо това използвайте публичния си ключ. +org_still_own_packages = Тази организация все още притежава един или повече пакети, първо ги изтрийте. +admin_cannot_delete_self = Не можете да изтриете себе си, когато сте администратор. Моля, първо премахнете администраторските си привилегии. +To = Име на клон +CommitMessage = Съобщение на подаването +include_error = ` трябва да съдържа подниз „%s“.` +alpha_dash_error = ` трябва да съдържа само буквено-цифрови знаци, тире („-“) и долна черта („_“).` +alpha_dash_dot_error = ` трябва да съдържа само буквено-цифрови знаци, тире („-“), долна черта („_“) и точка („.“).` +size_error = ` трябва да е с размер %s.` +min_size_error = ` трябва да съдържа поне %s знака.` +max_size_error = ` трябва да съдържа най-много %s знака.` +invalid_group_team_map_error = ` съпоставянето е невалидно: %s` +password_complexity = Паролата не отговаря на изискванията за сложност: +password_lowercase_one = Поне един малък знак +password_digit_one = Поне една цифра +password_special_one = Поне един специален знак (препинателни знаци, скоби, кавички и др.) +enterred_invalid_repo_name = Името на хранилището, което въведохте, е неправилно. +enterred_invalid_org_name = Името на организацията, което въведохте, е неправилно. +enterred_invalid_password = Паролата, която въведохте, е неправилна. +organization_leave_success = Успешно напуснахте организацията %s. +still_has_org = Вашият акаунт е участник в една или повече организации, първо ги напуснете. +org_still_own_repo = Тази организация все още притежава едно или повече хранилища, първо ги изтрийте или прехвърлете. +target_branch_not_exist = Целевият клон не съществува. +glob_pattern_error = ` glob шаблонът е невалиден: %s.` +openid_been_used = OpenID адресът „%s“ вече е използван. +unknown_error = Неизвестна грешка: +TreeName = Път до файла +AdminEmail = Администраторски адрес за ел. поща +email_domain_is_not_allowed = Домейнът на адреса за ел. поща на потребителя %s е в конфликт с EMAIL_DOMAIN_ALLOWLIST или EMAIL_DOMAIN_BLOCKLIST. Уверете се, че сте въвели правилно адреса за ел. поща. +email_been_used = Адресът за ел. поща вече се използва. + +NewBranchName = Име на новия клон +git_ref_name_error = ` трябва да е правилно форматирано име на Git препратка.` +regex_pattern_error = ` шаблонът на регулярния израз е невалиден: %s.` +repository_files_already_exist = Вече съществуват файлове за това хранилище. Свържете се със системния администратор. +repository_files_already_exist.delete = Вече съществуват файлове за това хранилище. Трябва да ги изтриете. +enterred_invalid_owner_name = Името на новия притежател не е валидно. +last_org_owner = Не можете да премахнете последния потребител от екипа на „притежателите“. Трябва да има поне един притежател за организация. +invalid_ssh_key = Не може да се потвърди вашият SSH ключ: %s +invalid_gpg_key = Не може да се потвърди вашият GPG ключ: %s +unable_verify_ssh_key = Не може да се потвърди SSH ключът, проверете го отново за грешки. +required_prefix = Въведеният текст трябва да започва с „%s“ [action] close_issue = `затвори задача %[3]s#%[2]s` @@ -1676,6 +2384,12 @@ compare_branch = Сравняване compare_commits_general = Сравняване на подавания compare_commits = Сравнете %d подавания +transfer_repo = прехвърли хранилище %s към %s +mirror_sync_push = синхронизира подавания към %[3]s на %[4]s от огледало +mirror_sync_create = синхронизира нова препратка %[3]s към %[4]s от огледало +mirror_sync_delete = синхронизира и изтри препратка %[2]s на %[3]s от огледало +review_dismissed = `отхвърли рецензия от %[4]s за %[3]s#%[2]s` + [auth] tab_openid = OpenID openid_connect_submit = Свързване @@ -1709,11 +2423,38 @@ sign_up_button = Регистрирайте се. back_to_sign_in = Назад към Вход sign_in_openid = Продължаване с OpenID send_reset_mail = Изпращане на ел. писмо за възстановяване +authorize_application = Упълномощаване на приложение +password_pwned_err = Неуспешно завършване на заявката към HaveIBeenPwned +last_admin = Не можете да премахнете последния администратор. Трябва да има поне един администратор. +allow_password_change = Изискване потребителят да смени паролата си (препоръчително) +authorize_title = Упълномощавате ли „%s“ да има достъп до вашия акаунт? +reset_password_mail_sent_prompt = Изпратено е ел. писмо за потвърждение до %s. За да завършите процеса по възстановяване на акаунта, моля, проверете входящата си поща и последвайте предоставената връзка в рамките на следващите %s. +reset_password_wrong_user = Вие сте влезли като %s, но връзката за възстановяване на акаунта е предназначена за %s +authorize_redirect_notice = Ще бъдете пренасочени към %s, ако упълномощите това приложение. +authorize_application_description = Ако предоставите достъп, то ще може да осъществява достъп и да записва цялата информация за вашия акаунт, включително частни хранилища и организации. +twofa_scratch_used = Използвали сте своя резервен код. Пренасочени сте към страницата с настройки за двуфакторно удостоверяване, за да можете да премахнете регистрацията на устройството си или да генерирате нов резервен код. +reset_password_helper = Възстановяване на акаунт +invalid_password = Вашата парола не съвпада с паролата, използвана за създаване на акаунта. +invalid_code = Вашият код за потвърждение е невалиден или е изтекъл. +invalid_code_forgot_password = Вашият код за потвърждение е невалиден или е изтекъл. Щракнете тук, за да започнете нова сесия. +scratch_code = Резервен код +use_scratch_code = Използвайте резервен код +use_onetime_code = Използвайте еднократен код +twofa_scratch_token_incorrect = Вашият резервен код е неправилен. +authorize_application_created_by = Това приложение е създадено от %s. +authorization_failed = Неуспешно упълномощаване +resent_limit_prompt = Вече сте поискали ел. писмо за активация наскоро. Моля, изчакайте 3 минути и опитайте отново. +has_unconfirmed_mail = Здравейте, %s, имате непотвърден адрес за ел. поща (%s). Ако не сте получили ел. писмо за потвърждение или трябва да изпратите ново, моля, щракнете върху бутона по-долу. +change_unconfirmed_email_error = Неуспешна промяна на адреса за ел. поща: %v +resend_mail = Щракнете тук, за повторно изпращане на ел. писмо за активация +change_unconfirmed_email_summary = Промяна на адреса, на който се изпраща ел. писмо за активация. +change_unconfirmed_email = Ако сте въвели грешен адрес за ел. поща по време на регистрацията, можете да го промените по-долу и потвърждение ще бъде изпратено на новия адрес. [aria] footer.software = Относно този софтуер footer.links = Връзки footer = Долен колонтитул +navbar = Навигационна лента [startpage] install = Лесен за инсталиране @@ -1780,6 +2521,16 @@ runs.no_workflows.help_no_write_access = За да научите повече variables.management = Управление на променливи variables.not_found = Променливата не е открита. variables.id_not_exist = Променлива с идентификатор %d не съществува. +runners.owner_type = Тип + +unit.desc = Управление на интегрирани CI/CD pipelines с Forgejo Actions. +status.unknown = Неизвестно +status.waiting = Изчаква се +status.running = Изпълнява се +status.success = Успешно +status.failure = Неуспешно +status.cancelled = Отменено +status.skipped = Пропуснато [heatmap] less = По-малко @@ -1809,9 +2560,10 @@ invalid_input_type = Не можете да качвате файлове от component_loading_failed = Неуспешно зареждане на %s contributors.what = приноси recent_commits.what = скорошни подавания -component_loading = Зареждане на %s... +component_loading = Зареждане на %s… component_loading_info = Това може да отнеме известно време… code_frequency.what = честота на промените +component_failed_to_load = Възникна неочаквана грешка. [projects] type-1.display_name = Индивидуален проект @@ -1820,20 +2572,29 @@ deleted.display_name = Изтрит проект [search] no_results = Няма намерени съответстващи резултати. -team_kind = Търсене на екипи... -repo_kind = Търсене на хранилища... -org_kind = Търсене на организации... -user_kind = Търсене на потребители... -code_kind = Търсене на код... -commit_kind = Търсене на подавания... -project_kind = Търсене на проекти... -package_kind = Търсене на пакети... -search = Търсене... -branch_kind = Търсене на клонове... -pull_kind = Търсене на заявки за сливане... -issue_kind = Търсене на задачи... +team_kind = Търсене на екипи… +repo_kind = Търсене на хранилища… +org_kind = Търсене на организации… +user_kind = Търсене на потребители… +code_kind = Търсене на код… +commit_kind = Търсене на подавания… +project_kind = Търсене на проекти… +package_kind = Търсене на пакети… +search = Търсене… +branch_kind = Търсене на клонове… +pull_kind = Търсене на заявки за сливане… +issue_kind = Търсене на задачи… fuzzy = Приблизително exact = Прецизно +regexp = Регекс +regexp_tooltip = Третиране на термина за търсене като регулярен израз +fuzzy_tooltip = Включване на резултати, които също съвпадат приблизително с термина за търсене +exact_tooltip = Включване само на резултати, които съвпадат точно с термина за търсене +code_search_unavailable = Търсенето на код в момента не е достъпно. Моля, свържете се с администратора на сайта. +keyword_search_unavailable = Търсенето по ключова дума в момента не е достъпно. Моля, свържете се с администратора на сайта. +union_tooltip = Включване на резултати, които съвпадат с някоя от ключовите думи, разделени с интервал +union = Обединение +type_tooltip = Тип търсене [markup] filepreview.lines = Редове от %[1]d до %[2]d в %[3]s @@ -1851,3 +2612,32 @@ eib = ЕиБ [translation_meta] test = окей + +[repo.permissions] +code.read = Четене: Достъп и клониране на кода на хранилището. +code.write = Писане: Изтласкване към хранилището, създаване на клонове и маркери. +issues.read = Четене: Четене и създаване на задачи и коментари. +issues.write = Писане: Затваряне на задачи и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. +pulls.read = Четене: Четене и създаване на заявки за сливане. +pulls.write = Писане: Затваряне на заявки за сливане и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. +releases.read = Четене: Преглед и изтегляне на издания. +wiki.read = Четене: Четене на интегрираното уики и неговата история. +wiki.write = Писане: Създаване, обновяване и изтриване на страници в интегрираното уики. +projects.read = Четене: Достъп до проектните табла на хранилището. +projects.write = Писане: Създаване и редактиране на проекти и колони. + +[gpg] +default_key = Подписано с ключ по подразбиране +error.extract_sign = Неуспешно извличане на подпис +error.generate_hash = Неуспешно генериране на хеш на подаването +error.no_committer_account = Няма акаунт, свързан с адреса за ел. поща на подаващия +error.no_gpg_keys_found = Не е намерен известен ключ за този подпис в базата данни +error.not_signed_commit = Не е подписано подаване +error.failed_retrieval_gpg_keys = Неуспешно извличане на ключ, свързан с акаунта на подаващия +error.probable_bad_signature = ВНИМАНИЕ! Въпреки че има ключ с това ID в базата данни, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. +error.probable_bad_default_signature = ВНИМАНИЕ! Въпреки че ключът по подразбиране има това ID, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. + +[units] +unit = Елемент +error.no_unit_allowed_repo = Нямате разрешение за достъп до никоя секция на това хранилище. +error.unit_not_allowed = Нямате разрешение за достъп до тази секция на хранилището. diff --git a/options/locale/locale_ca.ini b/options/locale/locale_ca.ini index 9cb7d5e50c..ea2af3b645 100644 --- a/options/locale/locale_ca.ini +++ b/options/locale/locale_ca.ini @@ -153,26 +153,26 @@ fuzzy = Difusa search = Cerca... type_tooltip = Tipus de cerca fuzzy_tooltip = Inclou resultats que s'assemblen al terme de la cerca -repo_kind = Cerca repos... -user_kind = Cerca usuaris... +repo_kind = Cerca repos… +user_kind = Cerca usuaris… code_search_unavailable = La cerca de codi no està disponible actualment. Si us plau concteu amb l'administrador del lloc. code_search_by_git_grep = Els resultats actuals de la cerca de codi són proporcionats per "git grep". Podríen haver-hi millors resultats si l'administrador del lloc habilita l'indexador de codi. -package_kind = Cerca paquets... -project_kind = Cerca projectes... -branch_kind = Cerca branques... -commit_kind = Cerca commits... -runner_kind = Cerca executors... +package_kind = Cerca paquets… +project_kind = Cerca projectes… +branch_kind = Cerca branques… +commit_kind = Cerca commits… +runner_kind = Cerca executors… no_results = Cap resultat coincident trobat. keyword_search_unavailable = La cerca per paraula clau no està disponible ara mateix. Si us plau contacteu amb l'administrador del lloc. union = Paraules clau union_tooltip = Inclou resultats que encaixen amb qualsevol paraula clau separada per espais -org_kind = Cerca organitzacions... -team_kind = Cerca teams... -code_kind = Cerca codi... -pull_kind = Cerca "pulls"... +org_kind = Cerca organitzacions… +team_kind = Cerca teams… +code_kind = Cerca codi… +pull_kind = Cerca "pulls"… exact = Exacte exact_tooltip = Inclou només resultats que són exactament el terme de cerca -issue_kind = Cerca problemes... +issue_kind = Cerca problemes… regexp = RegExp regexp_tooltip = Interpreta el terme de cerca com una expressió regular diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index ae878acc15..5c4bbbd87d 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -118,7 +118,7 @@ go_back=Zpět never=Nikdy unknown=Neznámý -rss_feed=RSS kanál +rss_feed=Kanál RSS pin=Připnout unpin=Odepnout @@ -493,16 +493,16 @@ use_onetime_code = Použít jednorázový kód view_it_on=Zobrazit na %s reply=nebo přímo odpovědět na tento e-mail link_not_working_do_paste=Odkaz nefunguje? Zkuste jej zkopírovat a vložit do adresního řádku svého prohlížeče. -hi_user_x=Ahoj %s, +hi_user_x=Dobrý den, uživateli %s, activate_account=Prosíme, aktivujte si váš účet activate_account.title=%s, prosím aktivujte si váš účet -activate_account.text_1=Ahoj %[1]s, děkujeme za registraci na %[2]s! +activate_account.text_1=Dobrý den, uživateli %[1]s, děkujeme za registraci ve službě %[2]s! activate_account.text_2=Pro aktivaci vašeho účtu klikněte %s na následující odkaz : activate_email=Ověřte vaši e-mailovou adresu activate_email.title=%s, prosím ověřte vaši e-mailovou adresu -activate_email.text=Pro ověření vaší e-mailové adresy klikněte %s na následující odkaz: +activate_email.text=Pro ověření vaší e-mailové adresy klikněte do %s na následující odkaz: register_notify=Vítejte v %s register_notify.title=%[1]s vítejte v %[2]s @@ -932,14 +932,14 @@ generate_new_token=Vygenerovat nový token tokens_desc=Tyto tokeny umožňují přístup k vašemu účtu pomocí Forgejo API. token_name=Název tokenu generate_token=Vygenerovat token -generate_token_success=Nový token byl vygenerován. Zkopírujte jej nyní, jelikož již nebude znovu zobrazen. +generate_token_success=Nový token byl vygenerován. Zkopírujte si jej nyní, jelikož již nebude znovu zobrazen. generate_token_name_duplicate=%s byl již použit jako název aplikace. Použijte prosím nový. delete_token=Smazat access_token_deletion=Odstranit přístupový token access_token_deletion_cancel_action=Zrušit access_token_deletion_confirm_action=Smazat access_token_deletion_desc=Smazání tokenu zruší přístup k vašemu účtu pro aplikace, které jej používají. Tuto akci nelze vrátit. Pokračovat? -delete_token_success=Token byl odstraněn. Aplikace, které jej používají již nemají přístup k vašemu účtu. +delete_token_success=Token byl odstraněn. Aplikace, které jej používají, již nemají přístup k vašemu účtu. repo_and_org_access=Přístup k repozitářům a organizacím permissions_public_only=Pouze veřejné permissions_access_all=Vše (veřejné, soukromé a omezené) @@ -1063,7 +1063,7 @@ language.localization_project = Pomozte nám s překladem Forgejo do vašeho jaz user_block_yourself = Nemůžete zablokovat sami sebe. pronouns_custom_label = Vlastní zájmena change_username_redirect_prompt.with_cooldown.few = Staré uživatelské jméno bude dostupné ostatním po %[1]d dnech. Do té doby budete moci své staré uživatelské jméno znovu získat. -change_username_redirect_prompt.with_cooldown.one = Staré uživatelské jméno bude dostupné ostatním po %[1]d dni. Do té doby budete moci své staré uživatelské jméno znovu získat. +change_username_redirect_prompt.with_cooldown.one = Staré uživatelské jméno bude dostupné ostatním po %[1]d dnu. Do té doby budete moci své staré uživatelské jméno znovu získat. keep_pronouns_private = Zobrazovat zájmena pouze přihlášeným uživatelům keep_pronouns_private.description = Toto nastavení skryje vaše zájmena před návštěvníky, kteří nejsou přihlášeni. quota = Kvóta @@ -1083,7 +1083,7 @@ quota.sizes.assets.artifacts = Artefakty quota.sizes.assets.packages.all = Balíčky quota.sizes.wiki = Wiki storage_overview = Přehled úložiště -quota.applies_to_user = Následující pravidla kvóty se vztahují na váš účet +quota.applies_to_user = Na váš účet se vztahují následující pravidla kvóty quota.rule.exceeded.helper = Celková velikost objektů pro toto pravidlo přesáhla kvótu. quota.sizes.assets.attachments.releases = Přílohy vydání regenerate_token_success = Token byl znovu vygenerován. Aplikace, které jej používají, již nemají přístup k vašemu účtu a je třeba je aktualizovat s novým tokenem. @@ -1213,7 +1213,7 @@ template.issue_labels=Štítky problémů template.one_item=Musíte vybrat alespoň jednu položku šablony template.invalid=Musíte vybrat repositář šablony -archive.title=Tento repozitář je archivovaný. Můžete prohlížet soubory, klonovat, ale nemůžete provádět žádné změny jeho stavu, jako nahrávání a vytváření nových problémů, žádostí nebo komentářů. +archive.title=Tento repozitář byl archivován. Můžete prohlížet soubory, klonovat, ale nemůžete provádět žádné změny jeho stavu, jako nahrávání a vytváření nových problémů, žádostí nebo komentářů. archive.title_date=Tento repozitář byl archivován %s. Můžete prohlížet soubory, klonovat, ale nemůžete provádět žádné změny jeho stavu, jako nahrávání a vytváření nových problémů, žádostí nebo komentářů. archive.issue.nocomment=Tento repozitář je archivovaný. Nemůžete komentovat problémy. archive.pull.nocomment=Tento repozitář je archivovaný. Nemůžete komentovat žádosti o sloučení. @@ -1349,6 +1349,7 @@ view_git_blame=Zobrazit git blame video_not_supported_in_browser=Váš prohlížeč nepodporuje značku HTML5 „video“. audio_not_supported_in_browser=Váš prohlížeč nepodporuje značku HTML5 „audio“. stored_lfs=Uloženo pomocí Git LFS +stored_annex=Uloženo pomocí Git Annex symbolic_link=Symbolický odkaz executable_file=Spustitelný soubor vendored = Vendorováno @@ -1374,6 +1375,7 @@ editor.upload_file=Nahrát soubor editor.edit_file=Upravit soubor editor.preview_changes=Náhled změn editor.cannot_edit_lfs_files=Soubory LFS nemohou být upravovány přes webové rozhraní. +editor.cannot_edit_annex_files=Annex soubory nemohou být upravovány přes webové rozhraní. editor.cannot_edit_non_text_files=Binární soubory nemohou být upravovány přes webové rozhraní. editor.edit_this_file=Upravit soubor editor.this_file_locked=Soubor je uzamčen @@ -1414,7 +1416,7 @@ editor.file_is_a_symlink=`„%s“ je symbolický odkaz. Symbolické odkazy nemo editor.filename_is_a_directory=Jméno souboru „%s“ je již použito jako jméno adresáře v tomto repozitáři. editor.file_editing_no_longer_exists=Upravovaný soubor „%s“ již není součástí tohoto repozitáře. editor.file_deleting_no_longer_exists=Odstraňovaný soubor „%s“ již není součástí tohoto repozitáře. -editor.file_changed_while_editing=Obsah souboru se od zahájení úprav změnil. Klikněte sem pro jeho zobrazení nebo odešlete změny ještě jednou pro jeho přepsání. +editor.file_changed_while_editing=Obsah souboru se od doby, kdy jste ho otevřeli, změnil. Klikněte sem pro jeho zobrazení nebo odešlete změny ještě jednou pro jeho přepsání. editor.file_already_exists=Soubor „%s“ již existuje v tomto repozitáři. editor.commit_empty_file_header=Odeslat prázdný soubor editor.commit_empty_file_text=Soubor, který se chystáte odeslat, je prázdný. Pokračovat? @@ -1556,7 +1558,7 @@ issues.label_templates.info=Zatím nebyly vytvořeny žádné štítky. Vytvořt issues.label_templates.helper=Vyberte přednastavené značky issues.label_templates.use=Použít přednastavené štítky issues.label_templates.fail_to_load_file=Nepodařilo se načíst soubor šablony popisku „%s“: %v -issues.add_label=přidal/a %s štítek %s +issues.add_label=přidal/a štítek %s %s issues.add_labels=přidal/a %s štítky %s issues.remove_label=odstranil/a %s štítek %s issues.remove_labels=odstranil/a %s štítky %s @@ -1579,7 +1581,7 @@ issues.remove_ref_at=`odstranil/a referenci %s %s` issues.add_ref_at=`přidal/a referenci %s %s` issues.delete_branch_at=`odstranil/a větev %s %s` issues.filter_label=Štítek -issues.filter_label_exclude=`Chcete-li vyloučit štítky, použijte alt + click/enter` +issues.filter_label_exclude=Chcete-li vyloučit štítky, použijte Alt + kliknutí issues.filter_label_no_select=Všechny štítky issues.filter_label_select_no_label=Bez štítku issues.filter_milestone=Milník @@ -1633,13 +1635,13 @@ issues.opened_by_fake=otevřeno %[1]s uživatelem %[2]s issues.closed_by_fake=od %[2]s byl uzavřen %[1]s issues.previous=Předchozí issues.next=Další -issues.open_title=Otevřeno -issues.closed_title=Uzavřeno +issues.open_title=Otevřené +issues.closed_title=Uzavřené issues.draft_title=Koncept issues.num_comments_1=%d komentář issues.num_comments=%d komentářů issues.commented_at=`okomentoval/a %s` -issues.delete_comment_confirm=Jste si jist, že chcete smazat tento komentář? +issues.delete_comment_confirm=Opravdu chcete smazat tento komentář? issues.context.copy_link=Kopírovat odkaz issues.context.quote_reply=Citovat odpověď issues.context.reference_issue=Odkázat v novém problému @@ -1652,14 +1654,14 @@ issues.comment_manually_pull_merged_at=ručně sloučena revize %[1]s do %[2]s % issues.close_comment_issue=Zavřít s komentářem issues.reopen_issue=Znovu otevřít issues.reopen_comment_issue=Znovu otevřít s komentářem -issues.create_comment=Okomentovat -issues.closed_at=`uzavřel/a tento problém %[2]s` -issues.reopened_at=`znovu otevřel/a tento problém %[2]s` -issues.commit_ref_at=`odkázal/a na tento problém z revize %[2]s` -issues.ref_issue_from=`odkázal/a na tento problém %[4]s %[2]s` -issues.ref_pull_from=`odkázal/a na tuto žádost o sloučení %[4]s %[2]s` -issues.ref_closing_from=`odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej uzavře, %[2]s` -issues.ref_reopening_from=`odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej znovu otevře, %[2]s` +issues.create_comment=Komentovat +issues.closed_at=`uzavřel/a tento problém %s` +issues.reopened_at=`znovu otevřel/a tento problém %s` +issues.commit_ref_at=`odkázal/a na tento problém z revize %s` +issues.ref_issue_from=`odkázal/a na tento problém %[3]s %[1]s` +issues.ref_pull_from=`odkázal/a na tuto žádost o sloučení %[3]s %[1]s` +issues.ref_closing_from=`odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej uzavře, %[1]s` +issues.ref_reopening_from=`odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej znovu otevře, %[1]s` issues.ref_closed_from=`uzavřel/a tento problém %[4]s %[2]s` issues.ref_reopened_from=`znovu otevřel/a tento problém %[4]s %[2]s` issues.ref_from=`z %[1]s` @@ -1763,7 +1765,7 @@ issues.error_modifying_due_date=Změna termínu dokončení selhala. issues.error_removing_due_date=Odstranění termínu dokončení selhalo. issues.push_commit_1=přidal/a %d revizi %s issues.push_commits_n=přidal/a %d revize %s -issues.force_push_codes=`vynucené nahrání %[1]s od %[2]s do %[4]s %[6]s` +issues.force_push_codes=`vynutil/a nahrání %[1]s od %[2]s do %[4]s %[6]s` issues.force_push_compare=Porovnat issues.due_date_form=rrrr-mm-dd issues.due_date_form_add=Přidat termín dokončení @@ -1813,7 +1815,7 @@ issues.review.approve=schválil/a tyto změny %s issues.review.comment=posoudil/a %s issues.review.dismissed=zamítl/a posouzení uživatele %s %s issues.review.dismissed_label=Zamítnuto -issues.review.left_comment=zanechal komentář +issues.review.left_comment=zanechal/a komentář issues.review.content.empty=Je potřeba zanechat poznámku s uvedením požadované změny (požadovaných změn). issues.review.reject=požádal/a o změny %s issues.review.wait=byl/a požádán/a o posouzení %s @@ -1966,8 +1968,8 @@ pulls.update_branch_success=Aktualizace větve byla úspěšná pulls.update_not_allowed=Nemáte oprávnění aktualizovat větev pulls.outdated_with_base_branch=Tato větev je zastaralá oproti základní větvi pulls.close=Zavřít žádost o sloučení -pulls.closed_at=`uzavřel/a tuto žádost o sloučení %[2]s` -pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení %[2]s` +pulls.closed_at=`uzavřel/a tuto žádost o sloučení %s` +pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení %s` pulls.cmd_instruction_hint=Zobrazit instrukce příkazové řádky pulls.cmd_instruction_checkout_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny. pulls.cmd_instruction_merge_title=Sloučit @@ -2607,7 +2609,7 @@ diff.comment.reply=Odpovědět diff.review=Dokončit posouzení diff.review.header=Odeslat posouzení diff.review.placeholder=Posoudit komentář -diff.review.comment=Okomentovat +diff.review.comment=Komentovat diff.review.approve=Schválit diff.review.self_reject=Autoři požadavků na natažení nemohou požadovat změny na svém vlastním požadavku na natažení diff.review.reject=Požadovat změny @@ -2758,7 +2760,7 @@ settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Tuto ak settings.new_owner_blocked_doer = Nový majitel vás zablokoval. settings.mirror_settings.pushed_repository = Odeslaný repozitář settings.add_collaborator_blocked_our = Nepodařilo se přidat spolupracovníka, jelikož byl zablokován majitelem repozitáře. -pulls.commit_ref_at = `se odkázal/a na tuto žádost o sloučení z revize %[2]s` +pulls.commit_ref_at = `odkázal/a na tuto žádost o sloučení z revize %s` settings.wiki_rename_branch_main = Normalizovat název větve wiki settings.wiki_rename_branch_main_desc = Přejmenovat větev interně používanou pro wiki na „%s“. Tato změna je trvalá a nelze ji vrátit. pulls.fast_forward_only_merge_pull_request = Pouze zrychlené @@ -2909,6 +2911,20 @@ issues.reopen.blocked_by_user = Tento problém nemůžete znovu otevřít, proto pulls.comment.blocked_by_user = Tuto žádost o sloučení nemůžete okomentovat, protože jste byli zablokováni majitelem repozitáře nebo autorem žádosti. issues.filter_no_results_placeholder = Zkuste upravit filtry vyhledávání. issues.filter_no_results = Žádné výsledky +migrate.repo_desc_helper = Ponechte prázdné pro importování existujícího popisu +archive.nocomment = Komentování není možné, protože je repozitář archivován. +comment.blocked_by_user = Komentování není možné, protože jste byli zablokováni majitelem repozitáře nebo autorem. +sync_fork.branch_behind_few = Tato větev je %[1]d revizí pozadu za %[2]s +sync_fork.button = Synchronizovat +sync_fork.branch_behind_one = Tato větev je %[1]d revizi pozadu za %[2]s +settings.event_action_failure = Selhání +settings.event_action_failure_desc = Běh akce selhal. +settings.event_action_recover = Obnovit +settings.event_action_success = Úspěch +settings.event_action_success_desc = Běh akce byl úspěšný. +settings.event_header_action = Události běhu akce +settings.event_action_recover_desc = Běh akce byl úspěšný, předchozí běh akce ve stejném workflow selhal. +issues.filter_type.all_pull_requests = Všechny žádosti o sloučení [graphs] component_loading_info = Tohle může chvíli trvat… @@ -3044,7 +3060,7 @@ teams.invite.by=Pozvání od %s teams.invite.description=Pro připojení k týmu klikněte na tlačítko níže. follow_blocked_user = Tuto organizaci nemůžete sledovat, protože jste v ní zablokováni. open_dashboard = Otevřít nástěnku -settings.change_orgname_redirect_prompt.with_cooldown.one = Starý název organizace bude dostupný ostatním po %[1]d dni. Do té doby budete moci staré jméno znovu získat. +settings.change_orgname_redirect_prompt.with_cooldown.one = Starý název organizace bude dostupný ostatním po %[1]d dnu. Do té doby budete moci staré jméno znovu získat. settings.change_orgname_redirect_prompt.with_cooldown.few = Starý název organizace bude dostupný ostatním po %[1]d dnech. Do té doby budete moci starý název znovu získat. [admin] @@ -3562,7 +3578,7 @@ notices.type=Typ notices.type_1=Repozitář notices.type_2=Úloha notices.desc=Popis -notices.op=Akce +notices.op=Op. notices.delete_success=Systémové upozornění bylo smazáno. dashboard.sync_repo_branches = Synchronizovat vynechané větve z dat Gitu do databáze monitor.queue.activeworkers = Aktivní workery @@ -3843,7 +3859,7 @@ owner.settings.cleanuprules.success.update=Pravidlo pro čištění bylo aktuali owner.settings.cleanuprules.success.delete=Pravidlo pro čištění bylo odstraněno. owner.settings.chef.title=Registr Chef owner.settings.chef.keypair=Generovat pár klíčů -owner.settings.chef.keypair.description=Pro autentizaci do registru Chef je zapotřebí pár klíčů. Pokud jste předtím vytvořili pár klíčů, nově vygenerovaný pár klíčů vyřadí starý pár klíčů. +owner.settings.chef.keypair.description=Žádosti odeslané do registru Chef musí být kryptograficky podepsané jako způsob ověření. Při generování páru klíčů je ve službě Forgejo uložen pouze veřejný klíč. Soukromý klíč je poskytnut vám, abyste jej mohli použít s programem knife. Vygenerováním nového páru klíčů přepíšete ten předchozí. owner.settings.cargo.rebuild.description = Opětovné sestavení může být užitečné, pokud není index synchronizován s uloženými balíčky Cargo. owner.settings.cargo.rebuild.no_index = Opětovné vytvoření selhalo, nebyl inicializován žádný index. npm.dependencies.bundle = Přidružené závislosti @@ -3927,7 +3943,7 @@ runners.delete_runner=Odstranit tento runner runners.delete_runner_success=Runner byl úspěšně odstraněn runners.delete_runner_failed=Odstranění runneru selhalo runners.delete_runner_header=Potvrdit odstranění tohoto runneru -runners.delete_runner_notice=Pokud na tomto runneru běží úloha, bude ukončena a označena jako neúspěšná. Může dojít k přerušení vytváření pracovního postupu. +runners.delete_runner_notice=Pokud na tomto runneru běží úloha, bude ukončena a označena jako neúspěšná. Může dojít k přerušení vytváření workflow. runners.status.unspecified=Neznámý runners.status.idle=Nečinný runners.status.active=Aktivní diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index 87bd4dae2b..99b5789de3 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -1,6 +1,3 @@ - - - [common] home = Hjem dashboard = Instrumentpanel @@ -10,7 +7,7 @@ logo = Logo sign_in = Login sign_in_with_provider = Login med %s sign_in_or = eller -sign_out = Logud +sign_out = Log ud sign_up = Register return_to_forgejo = Vend tilbage til Forgejo new_repo.title = Ny repository @@ -158,7 +155,7 @@ exact_tooltip = Medtag kun resultater, der matcher den nøjagtige søgeterm regexp = RegExp regexp_tooltip = Fortolk søgetermen som et regulært udtryk org_kind = Søg i organisationer… -team_kind = Søg efter hold… +team_kind = Søg efter teams… code_kind = Søg kode… code_search_by_git_grep = Aktuelle kodesøgeresultater leveres af "git grep". Der kan være bedre resultater, hvis webstedsadministratoren aktiverer kodeindeksering. package_kind = Søg pakker… @@ -186,7 +183,7 @@ number_of_contributions_in_the_last_12_months = %s bidrag inden for de sidste 12 contributions_zero = Ingen bidrag contributions_format = {contributions} på {month} {day}, {year} contributions_one = bidrag -contributions_few = bidragene +contributions_few = bidrag less = Mindre more = Mere @@ -200,7 +197,7 @@ buttons.link.tooltip = Tilføj et link buttons.list.unordered.tooltip = Tilføj en punktliste buttons.list.task.tooltip = Tilføj en liste over opgaver buttons.list.ordered.tooltip = Tilføj en nummereret liste -buttons.mention.tooltip = Nævn en bruger eller et hold +buttons.mention.tooltip = Nævn en bruger eller et team buttons.ref.tooltip = Henvis til et problem eller pull-anmodning buttons.enable_monospace_font = Aktiver monospace-skrifttype buttons.disable_monospace_font = Deaktiver monospace-skrifttype @@ -363,7 +360,7 @@ my_repos = Depoter my_orgs = Organisationer view_home = Se %s filter = Andre filtre -filter_by_team_repositories = Filtrer efter holddepoter +filter_by_team_repositories = Filtrer efter team depoter feed_of = Feed af "%s" show_archived = Arkiveret show_both_archived_unarchived = Viser både arkiveret og ikke-arkiveret @@ -530,7 +527,7 @@ repo.collaborator.added.subject = %s føjede dig til %s som samarbejdspartner repo.collaborator.added.text = Du er blevet tilføjet som samarbejdspartner til depotet: team_invite.subject = %[1]s har inviteret dig til at deltage i %[2]s organisationen team_invite.text_1 = %[1]s har inviteret dig til at deltage i teamet %[2]s i organisationen %[3]s. -team_invite.text_2 = Klik venligst på følgende link for at blive medlem af holdet: +team_invite.text_2 = Klik venligst på følgende link for at blive medlem af teamet: team_invite.text_3 = Note: Denne invitation var beregnet til %[1]s. Hvis du ikke forventede denne invitation, kan du ignorere denne e-mail. [modal] @@ -553,7 +550,7 @@ Email = E-mailadresse Password = Adgangskode Retype = Bekræft adgangskode PayloadUrl = Payload URL -TeamName = Holdnavn +TeamName = Team navn AuthName = Autorisationsnavn AdminEmail = Admin email To = Gren navn @@ -978,8 +975,8 @@ delete_with_all_comments = Din konto er yngre end %s. For at undgå spøgelsesko delete_account_title = Slet brugerkonto user_block_yourself = Du kan ikke blokere dig selv. pronouns_custom_label = Brugerdefinerede stedord -change_username_redirect_prompt.with_cooldown.one = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. -change_username_redirect_prompt.with_cooldown.few = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. +change_username_redirect_prompt.with_cooldown.one = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, år. Du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. +change_username_redirect_prompt.with_cooldown.few = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, år. Du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. keep_pronouns_private = Vis kun stedord til godkendte brugere keep_pronouns_private.description = Dette vil skjule dine stedord for besøgende, der ikke er logget ind. quota.applies_to_user = Følgende kvoteregler gælder for din konto @@ -1135,8 +1132,8 @@ mirror_public_key = Offentlig SSH-nøgle blame.ignore_revs = Ignorerer revisioner i .git-blame-ignore-revs. Klik her for at omgå og se den normale skyldvisning. mirror_address_protocol_invalid = Den angivne URL er ugyldig. Kun http(s):// eller git:// placeringer kan bruges til spejling. transfer.reject_desc = Annuller overførsel til "%s" -archive.title = Dette depot er arkiveret. Du kan se filer og klone dem, men du kan ikke pushe eller åbne problemer eller pull-anmodninger. -archive.title_date = Dette depot er blevet arkiveret på %s. Du kan se filer og klone dem, men du kan ikke pushe eller åbne problemer eller pull-anmodninger. +archive.title = Dette depot er arkiveret. Du kan se filer og klone det, men du kan ikke foretage ændringer i dets tilstand, f.eks. pushe og oprette nye problemer, pull-anmodninger eller kommentarer. +archive.title_date = Dette depot er blevet arkiveret på %s. Du kan se filer og klone det, men du kan ikke foretage ændringer i dets tilstand, såsom at pushe og oprette nye problemer, pull-anmodninger eller kommentarer. archive.issue.nocomment = Denne depot er arkiveret. Du kan ikke kommentere på problemer. form.reach_limit_of_creation_1 = Ejeren har allerede nået grænsen på %d depot. form.reach_limit_of_creation_n = Ejeren har allerede nået grænsen på %d depoterne. @@ -1432,7 +1429,7 @@ issues.new.no_items = Ingen elementer issues.new.milestone = Milepæl issues.new.no_milestone = Ingen milepæl issues.filter_assignees = Filter tildelt -issues.filter_milestones = Filter Milepæl +issues.filter_milestones = Filter milepæl issues.filter_projects = Filter projekt issues.filter_labels = Filter etiket issues.filter_reviewers = Filter anmelder @@ -1451,7 +1448,7 @@ issues.filter_sort.leastcomment = Mindst kommenteret issues.filter_sort.nearduedate = Nærmeste forfaldsdato issues.filter_sort.farduedate = Længste forfaldsdato issues.filter_sort.moststars = Flest stjerner -editor.file_changed_while_editing = Filens indhold er ændret, siden du begyndte at redigere. Klik her for at se dem eller Bekræft ændringer igen for at overskrive dem. +editor.file_changed_while_editing = Filens indhold er ændret, siden du begyndte at åbne den. Klik her for at se dem eller Bekræft ændringer igen for at overskrive dem. editor.file_already_exists = En fil med navnet "%s" findes allerede i dette depot. editor.commit_id_not_matching = Filen blev ændret, mens du redigerede den. Forpligt dig til en ny gren og merge derefter. editor.push_out_of_date = Pushet ser ud til at være forældet. @@ -1520,15 +1517,15 @@ issues.add_labels = tilføjede %s etiketterne %s issues.add_remove_labels = tilføjede %s og fjernede %s etiketter %s issues.add_milestone_at = `føjede dette til %s milepælen %s` issues.add_project_at = `føjede dette til %s- projektet %s` -issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[4]s, der vil genåbne den, %[2]s` +issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil genåbne det, %[1]s` issues.ref_closed_from = `lukkede dette problem %[4]s %[2 ]s` issues.ref_reopened_from = `genåbnede dette problem %[4]s %[2 ]s` issues.ref_from = `fra %[1]s` issues.author = Forfatter -issues.commit_ref_at = `henviste til dette problem fra en commit %[2]s` -issues.ref_issue_from = `henviste til dette problem %[4]s %[2 ]s` -issues.ref_pull_from = `henviste til denne pull-anmodning %[4]s %[ 2]s` -issues.ref_closing_from = `henviste til dette problem fra en pull-anmodning %[4]s, der vil lukke det, %[2]s` +issues.commit_ref_at = `henviste til dette problem fra en commit %s` +issues.ref_issue_from = `henviste til dette problem %[3]s %[1]s` +issues.ref_pull_from = `henviste til denne pull-anmodning %[3]s %[1]s` +issues.ref_closing_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil lukke det, %[1]s` issues.author.tooltip.issue = Denne bruger er forfatteren til dette problem. issues.author.tooltip.pr = Denne bruger er forfatteren af denne pull-anmodning. issues.role.owner = Ejer @@ -1564,8 +1561,8 @@ issues.reaction.alt_add = Tilføj %[1]s reaktion til kommentar. issues.context.menu = Kommentar menu issues.reopen_comment_issue = Genåbner med kommentar issues.create_comment = Kommentar -issues.closed_at = `lukkede dette problem %[2]s` -issues.reopened_at = `genåbnede dette problem %[2]s` +issues.closed_at = `lukkede dette problem %s` +issues.reopened_at = `genåbnede dette problem %s` issues.remove_label = fjernede %s etiketten %s issues.remove_labels = fjernede %s etiketterne %s issues.change_project_at = `modificerede projektet fra %s til %s %s` @@ -1582,7 +1579,7 @@ issues.change_ref_at = `ændret reference fra %s til issues.remove_ref_at = `fjernet reference %s %s` issues.add_ref_at = `tilføjet reference %s %s` issues.delete_branch_at = `slettet gren %s %s` -issues.filter_label_exclude = `Brug alt + klik/enter for at ekskludere etiketter` +issues.filter_label_exclude = Brug Alt + klik for at ekskludere etiketter issues.filter_milestone = Milepæl issues.filter_milestone_all = Alle milepæle issues.filter_milestone_none = Ingen milepæle @@ -1911,10 +1908,10 @@ pulls.editable_explanation = Denne pull-anmodning tillader redigeringer fra vedl pulls.auto_merge_button_when_succeed = (Når kontroller lykkes) pulls.status_checks_requested = Påkrævet pulls.close = Luk pull anmodning -pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit %[2]s` +pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit %s` pulls.cmd_instruction_hint = Se instruktionerne på kommandolinjen -pulls.reopened_at = `genåbnede denne pull-anmodning %[2]s` -pulls.closed_at = `lukkede denne pull-anmodning %[2]s` +pulls.reopened_at = `genåbnede denne pull-anmodning %s` +pulls.closed_at = `lukkede denne pull-anmodning %s` pulls.cmd_instruction_checkout_desc = Fra dit projektdepot, tjek en ny gren og test ændringerne. pulls.editable = Redigerbar pulls.made_using_agit = AGit @@ -2018,7 +2015,7 @@ settings.lfs_pointers.inRepo = i depot settings.lfs_pointers.exists = Eksisterer i lager settings.lfs_pointers.accessible = Tilgængeligt for bruger signing.wont_sign.not_signed_in = Du er ikke logget ind. -wiki.welcome = Velkommen til Wikien +wiki.welcome = Velkommen til wikien milestones.modify = Opdater milepæl milestones.edit_success = Milepæl "%s" er blevet opdateret. milestones.filter_sort.least_issues = Mindst problemer @@ -2262,7 +2259,7 @@ settings.wiki_delete_notices_1 = - Dette vil permanent slette og deaktivere depo settings.wiki_branch_rename_failure = Det lykkedes ikke at normalisere depotwikiens filialnavn. settings.add_collaborator_duplicate = Samarbejdspartneren er allerede føjet til dette depot. settings.add_collaborator_owner = Kan ikke tilføje en ejer som samarbejdspartner. -settings.collaborator_deletion = Fjern Samarbejdspartner +settings.collaborator_deletion = Fjern samarbejdspartner settings.collaborator_deletion_desc = Fjernelse af en samarbejdspartner vil tilbagekalde deres adgang til dette depot. Vil du fortsætte? settings.add_team_duplicate = Teamet har allerede depotet settings.add_collaborator_blocked_our = Samarbejdspartneren kan ikke tilføjes, fordi depots ejer har blokeret dem. @@ -2525,13 +2522,264 @@ settings.ignore_stale_approvals_desc = Tæl ikke godkendelser, der er foretaget settings.require_signed_commits = Kræv underskrevne commits settings.remove_protected_branch_success = Grenbeskyttelse for reglen "%s" er blevet fjernet. settings.enforce_on_admins = Håndhæv denne regel for depotadministratorer +settings.matrix.homeserver_url = Hjemmeserver URL +settings.matrix.room_id = Rum ID +branch.branch_name_conflict = Gren navn "%s" er i konflikt med den allerede eksisterende gren "%s". +release.tag_name_protected = Tagnavnet er beskyttet. +branch.name = Gren navn +settings.archive.error = Der opstod en fejl under forsøg på at arkivere depotet. Se loggen for flere detaljer. +settings.archive.error_ismirror = Du kan ikke arkivere et spejlet depot. +settings.unarchive.button = Fjern arkivering af depot +settings.unarchive.header = Fjern arkivering af dette depot +settings.lfs = LFS +settings.lfs_filelist = LFS-filer gemt i dette depot +settings.lfs_no_lfs_files = Ingen LFS-filer gemt i dette depot +settings.lfs_locks = Låse +settings.lfs_findcommits = Find commits +settings.lfs_lfs_file_no_commits = Ingen commits fundet for denne LFS-fil +settings.lfs_noattribute = Denne sti har ikke den låsbare attribut i standardgrenen +settings.lfs_delete = Slet LFS-fil med OID %s +settings.lfs_delete_warning = Sletning af en LFS-fil kan forårsage "objekt findes ikke"-fejl ved checkout. Er du sikker? +settings.lfs_findpointerfiles = Find pointer-filer +diff.download_patch = Download patch-fil +diff.whitespace_ignore_at_eol = Ignorer ændringer i blanke mellemrum på EOL +diff.stats_desc = %d ændrede filer med %d tilføjelser og %d sletninger +diff.stats_desc_file = %d ændringer: %d tilføjelser og %d sletninger +diff.bin = BIN +diff.file_suppressed = Filforskellen er undertrykt, fordi den er for stor +diff.generated = genereret +diff.vendored = forhandlet +diff.comment.add_line_comment = Tilføj linjekommentar +diff.comment.placeholder = Efterlad en kommentar +diff.comment.markdown_info = Styling med Markdown understøttes. +diff.comment.add_single_comment = Tilføj en enkelt kommentar +diff.comment.add_review_comment = Tilføj kommentar +diff.has_escaped = Denne linje har skjulte Unicode-tegn +release.tag_name = Tag navn +release.tag_helper_new = Nyt tag. Dette tag vil blive oprettet fra målet. +release.deletion_tag_desc = Vil slette dette tag fra depotet. Depotindhold og -historik forbliver uændret. Vil du fortsætte? +release.tag_already_exist = Dette tagnavn findes allerede. +release.add_tag_msg = Brug udgivelsens titel og indhold som tagmeddelelse. +release.tags_for = Tags for %s +release.asset_external_url = Ekstern URL +release.add_external_asset = Tilføj eksternt aktiv +branch.already_exists = En gren med navnet "%s" eksisterer allerede. +branch.tag_collision = Gren "%s" kan ikke oprettes, da et tag med samme navn allerede eksisterer i depotet. +releases.desc = Spor projektversioner og downloads. +release.detail = Udgivelsesdetaljer +release.tags = Tags +release.new_release = Ny udgivelse +release.stable = Stabil +release.compare = Sammenlign +release.deletion_desc = Sletning af en udgivelse fjerner den kun fra Forgejo. Det vil ikke påvirke Git-tagget, indholdet af dit depot eller dets historie. Vil du fortsætte? +release.deletion_tag_success = Tagget er blevet slettet. +release.tag_name_already_exist = Der findes allerede en udgivelse med dette tagnavn. +release.tag_name_invalid = Tagnavnet er ikke gyldigt. +release.downloads = Downloads +release.system_generated = Denne vedhæftede fil genereres automatisk. +release.invalid_external_url = Ugyldig ekstern URL: "%s" +branch.deleted_by = Slettet af %s +diff.comment.start_review = Start anmeldelse +diff.comment.reply = Svar +diff.review.comment = Kommentar +diff.review.approve = Godkend +diff.review.self_reject = Forfattere af pull-anmodninger kan ikke anmode om ændringer på deres egen pull-anmodning +diff.review.reject = Anmod om ændringer +diff.review.self_approve = Forfattere af pull-anmodninger kan ikke godkende deres egen pull-anmodning +diff.committed_by = committed af +diff.protected = Beskyttet +diff.image.side_by_side = Side om side +diff.image.swipe = Swipe +diff.image.overlay = Overlejring +release.prerelease = Forhåndsudgivelse +settings.tags.protection.allowed.teams = Tilladte hold +settings.tags.protection.create = Tilføj regel +settings.tags.protection.none = Der er ingen beskyttede tags. +settings.tags.protection.pattern.description = Du kan bruge et enkelt navn eller et globmønster eller regulært udtryk til at matche flere tags. Læs mere i guiden til beskyttede tags. +settings.thread_id = Thread ID +settings.matrix.message_type = Meddelelsestype +settings.matrix.access_token_helper = Det anbefales at opsætte en dedikeret Matrix-konto til dette. Adgangstokenet kan hentes fra Element-webklienten (i en privat/inkognito-fane) > Brugermenu (øverst til venstre) > Alle indstillinger > Hjælp og om > Avanceret > Adgangstoken (lige under hjemmeserverens URL). Luk privat-/inkognitofanen (logging ud ville ugyldiggøre tokenet). +settings.lfs_invalid_locking_path = Ugyldig sti: %s +settings.lfs_invalid_lock_directory = Kan ikke låse mappe: %s +diff.options_button = Diff muligheder +release.hide_archive_links = Skjul automatisk genererede arkiver +release.hide_archive_links_helper = Skjul automatisk genererede kildekodearkiver for denne udgivelse. For eksempel hvis du uploader din egen. +release.add_tag = Opret tag +release.releases_for = Udgivelser for %s +branch.delete_branch_has_new_commits = Gren "%s" kan ikke slettes, fordi nye commits er blevet tilføjet efter fletning. +branch.create_branch = Opret gren %s +branch.create_from = fra "%s" +branch.create_success = Gren "%s" er blevet oprettet. +branch.branch_already_exists = Gren "%s" findes allerede i dette depot. +release.releases = Udgivelser +release.draft = Udkast +release.type_attachment = Vedhæftning +release.type_external_asset = Eksternt aktiv +release.asset_name = Aktivets navn +release.tag_helper_existing = Eksisterende tag. +release.title_empty = Titel må ikke være tom. +release.message = Beskriv denne udgivelse +settings.bot_token = Bot token +settings.chat_id = Chat ID +diff.show_file_tree = Vis filtræ +diff.hide_file_tree = Skjul filtræ +release.ahead.commits = %d commits +release.ahead.target = til %s siden denne udgivelse +release.source_code = Kildekode +release.edit_subheader = Udgivelser organiserer projektversioner. +release.tag_helper = Vælg et eksisterende tag, eller opret et nyt tag. +release.target = Mål +tag.ahead.target = til %s siden dette tag +release.new_subheader = Udgivelser organiserer projektversioner. +release.title = Udgivelsestitel +release.prerelease_desc = Markér som pre-release +release.prerelease_helper = Marker denne udgivelse uegnet til produktionsbrug. +release.deletion_success = Udgivelsen er blevet slettet. +release.summary_card_alt = Oversigtskort for en udgivelse med titlen "%s" i depots %s +settings.unarchive.text = Fjernelse af arkivering af depotet vil genoprette dens evne til at modtage commits og push, såvel som nye problemer og pull-anmodninger. +settings.unarchive.success = Depotet blev fjernet fra arkivet. +settings.unarchive.error = Der opstod en fejl under forsøg på at fjerne depotet fra arkivet. Se loggen for flere detaljer. +settings.update_avatar_success = Depot avataren er blevet opdateret. +branch.restore_success = Gren "%s" er blevet gendannet. +branch.restore_failed = Kunne ikke gendanne grenen "%s". +branch.protected_deletion_failed = Gren "%s" er beskyttet. Det kan ikke slettes. +branch.default_deletion_failed = Gren "%s" er standardgrenen. Det kan ikke slettes. +tag.create_tag_operation = Opret tag +tag.confirm_create_tag = Opret tag +settings.tags.protection.allowed.noone = Ingen +settings.archive.header = Arkivere dette depot +settings.matrix.room_id_helper = Rum-id'et kan hentes fra Element-webklienten > Rumindstillinger > Avanceret > Internt rum-id. Eksempel: %s. +settings.archive.text = Arkivering af depotet vil gøre den fuldstændig skrivebeskyttet. Det vil blive skjult fra dashboardet. Ingen (ikke engang dig!) vil være i stand til at foretage nye commits eller åbne nogen problemer eller pull-anmodninger. +settings.archive.success = Depotet blev arkiveret. +settings.archive.button = Arkivere depot +settings.rename_branch_success = Grenen %s blev omdøbt til %s. +diff.whitespace_ignore_amount_changes = Ignorer ændringer i mængden af blanke mellemrum +diff.download_diff = Download diff fil +diff.show_split_view = Opdelt visning +diff.show_unified_view = Samlet visning +diff.whitespace_button = Blanke mellemrum +diff.whitespace_show_everything = Vis alle ændringer +diff.whitespace_ignore_all_whitespace = Ignorer blanke mellemrum, når du sammenligner linjer +diff.too_many_files = Nogle filer blev ikke vist, fordi der er ændret for mange filer i denne diff +diff.show_more = Vis mere +diff.load = Load diff +settings.archive.branchsettings_unavailable = Indstillinger for gren er ikke tilgængelige i arkiverede depoter. +settings.archive.tagsettings_unavailable = Tag-indstillinger er ikke tilgængelige i arkiverede depoter. +settings.archive.mirrors_unavailable = Spejle er ikke tilgængelige i arkiverede depoter. +settings.lfs_lock_already_exists = Låsen findes allerede: %s +settings.lfs_pointers.associateAccessible = Tilknyt tilgængelige %d OID'er +settings.rename_branch_failed_protected = Kan ikke omdøbe grenen %s, fordi den er en beskyttet gren. +settings.rename_branch_failed_exist = Kan ikke omdøbe grenen, fordi målgrenen %s eksisterer. +settings.rename_branch_failed_not_exist = Kan ikke omdøbe grenen %s, fordi den ikke eksisterer. +settings.rename_branch = Omdøb gren +diff.browse_source = Gennemse kilde +diff.parent = forælder +diff.commit = commit +diff.git-notes = Noter +diff.git-notes.add = Tilføj note +diff.git-notes.remove-header = Fjern note +diff.git-notes.remove-body = Denne note vil blive fjernet. +diff.data_not_available = Diff-indhold er ikke tilgængeligt +diff.bin_not_shown = Binær fil vises ikke. +diff.view_file = Se fil +diff.file_before = Før +diff.file_after = Efter +diff.file_image_width = Bredde +diff.file_image_height = Højde +diff.file_byte_size = Størrelse +diff.file_suppressed_line_too_long = Filforskellen er undertrykt, fordi en eller flere linjer er for lange +diff.review = Afslut gennemgangen +diff.review.header = Send anmeldelse +diff.review.placeholder = Gennemgå kommentar +release.download_count_one = %s download +release.download_count_few = %s downloads +topic.format_prompt = Emner skal starte med et bogstav eller tal, kan indeholde bindestreger ("-") og prikker ("."), kan være op til 35 tegn lange. Bogstaver skal være små. +branch.warning_rename_default_branch = Du omdøber standardgrenen. +topic.done = Færdig +topic.count_prompt = Du kan ikke vælge mere end 25 emner +find_file.go_to_file = Find en fil +find_file.no_matching = Ingen matchende fil fundet +topic.manage_topics = Administrer emner +tag.create_tag_from = Opret nyt tag fra "%s" +tag.create_success = Tag "%s" er blevet oprettet. +branch.restore = Gendan grenen "%s" +branch.new_branch_from = Opret ny gren fra "%s" +branch.create_branch_operation = Opret gren +branch.renamed = Gren %s blev omdøbt til %s. +branch.new_branch = Opret ny gren +tag.create_tag = Opret tag %s +error.csv.too_large = Denne fil kan ikke gengives, fordi den er for stor. +error.csv.unexpected = Denne fil kan ikke gengives, fordi den indeholder et uventet tegn i linje %d og kolonne %d. +error.csv.invalid_field_count = Denne fil kan ikke gengives, fordi den har et forkert antal felter i linje %d. +branch.download = Download gren "%s" +branch.rename = Omdøb grenen "%s" +branch.included_desc = Denne gren er en del af standardgrenen +branch.included = Inkluderet +error.broken_git_hook = Git hooks af dette depot ser ud til at være brudt. Følg venligst dokumentationen for at rette dem, og push derefter på nogle commits for at opdatere statussen. +branch.create_new_branch = Opret en gren fra gren: +branch.confirm_create_branch = Opret gren +branch.rename_branch_to = Omdøb "%s" til: +migrate.repo_desc_helper = Lad være tom for at importere eksisterende beskrivelse +archive.nocomment = Det er ikke muligt at kommentere, fordi depotet er arkiveret. +comment.blocked_by_user = Det er ikke muligt at kommentere, fordi du er blokeret af depots ejer eller forfatteren. +sync_fork.branch_behind_few = Denne gren er %[1]d commits bag %[2]s +sync_fork.button = Sync +sync_fork.branch_behind_one = Denne gren er %[1]d commit bag %[2]s +settings.event_header_action = Handling Run-begivenheder +settings.event_action_failure = Mislykket +settings.event_action_success_desc = Handlingen blev udført. +settings.event_action_success = Success +settings.event_action_recover_desc = Handlingskørsel lykkedes efter at den sidste handlingskørsel i samme arbejdsgang mislykkedes. +settings.event_action_failure_desc = Handlingskørsel sluttede som en fejl. +settings.event_action_recover = Gendan +issues.filter_type.all_pull_requests = Alle pull-anmodninger [notification] watching = Overvåger read = Læs +notifications = Notifikationer +no_unread = Ingen ulæste notifikationer. +unread = Ulæst +mark_as_read = Markér som læst +no_read = Ingen læste notifikationer. +mark_all_as_read = Markér alle som læste +mark_as_unread = Markér som ulæst +subscriptions = Abonnementer +no_subscriptions = Ingen abonnementer +pin = Fastgør notifikation [action] watched_repo = begyndte at overvåge %[2]s +commit_repo = pushed til %[3]s at %[4]s +comment_pull = `kommenterede pull request %[3]s#%[2]s` +mirror_sync_push = synkroniserede commits til %[3]s ved %[4]s fra spejl +create_issue = `åbnet problem %[3]s#%[2]s` +mirror_sync_create = synkroniseret ny reference %[3]s til %[4]s fra spejl +reopen_pull_request = `genåbnet pull request %[3]s#%[2]s` +create_repo = oprettede depot %s +compare_branch = Sammenlign +rename_repo = omdøbt depot fra %[1]s til %[3]s +reopen_issue = `genåbnet problem %[3]s#%[2]s` +create_pull_request = `oprettet pull-anmodning %[3]s#%[2]s` +close_pull_request = `lukket pull request %[3]s#%[2]s` +starred_repo = stjernemarkerede %[2]s +close_issue = `lukket problem %[3]s#%[2]s` +comment_issue = `kommenterede problem %[3]s#%[2]s` +delete_branch = slettede gren %[2]s fra %[3]s +compare_commits = Sammenlign %d commits +compare_commits_general = Sammenlign commits +review_dismissed = `afvist anmeldelse fra %[4]s for %[3]s#%[2]s` +review_dismissed_reason = Årsag: +create_branch = oprettede gren %[3]s i %[4]s +merge_pull_request = `sammenflettet pull request %[3]s#%[2]s` +auto_merge_pull_request = `automatisk flettet pull-anmodning %[3]s#%[2]s` +transfer_repo = overførte lager %s til %s +push_tag = skubbet tag %[3]s til %[4]s +delete_tag = slettede tag %[2]s fra %[3]s +mirror_sync_delete = synkroniseret og slettet reference %[2]s ved %[3]s fra spejl +approve_pull_request = `godkendt %[3]s#%[2]s` +reject_pull_request = `foreslået ændringer for %[3]s#%[2]s` +publish_release = `udgivet %[4]s%[3]s` [org] repo_updated = Opdateret %s @@ -2561,6 +2809,93 @@ settings.location = Lokation settings.permission = Tilladelser settings.visibility = Synlighed members.remove = Slet +org_name_holder = Organisationens navn +create_team = Opret team +org_full_name_holder = Organisationens fulde navn +open_dashboard = Åbn dashboard +lower_repositories = depoter +team_unit_desc = Tillad adgang til depotsektioner +follow_blocked_user = Du kan ikke følge denne organisation, fordi denne organisation har blokeret dig. +org_name_helper = Organisationsnavne skal være korte og mindeværdige. +create_org = Opret organisation +form.name_reserved = Organisationsnavnet "%s" er reserveret. +create_new_team = Nyt team +team_unit_disabled = (Deaktiveret) +code = Kode +team_name = Team navn +team_name_helper = Team navne skal være korte og mindeværdige. +team_desc_helper = Beskriv teamets formål eller rolle. +team_access_desc = Depot adgang +team_permission_desc = Tilladelse +members.member = Medlem +settings.change_orgname_prompt = Bemærk: Ændring af organisationens navn vil også ændre din organisations URL og frigøre det gamle navn. +settings.change_orgname_redirect_prompt = Det gamle navn vil omdirigere, indtil det gøres krav på. +settings.change_orgname_redirect_prompt.with_cooldown.one = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, år. Du kan stadig kræve det gamle navn tilbage i nedkølingsperioden. +settings.change_orgname_redirect_prompt.with_cooldown.few = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, år. Du kan stadig kræve det gamle navn tilbage i. +settings.update_avatar_success = Organisationens avatar er blevet opdateret. +members.public_helper = Gør skjult +members.private = Skjult +members.private_helper = Gør synlig +members.member_role = Medlemsrolle: +members.remove.detail = Vil du fjerne %[1]s fra %[2]s? +members.invite_desc = Tilføj et nyt medlem til %s: +teams.read_permission_desc = Dette team giver læse adgang: medlemmer kan se og klone teamdepoter. +teams.remove_all_repos_title = Slet alle team depoter +members.leave.detail = Er du sikker på, at du vil forlade organisationen "%s"? +settings.email = Kontakt email +settings.delete_account = Slet denne organisation +teams.can_create_org_repo_helper = Medlemmer kan oprette nye arkiver i organisationen. Skaberen får administratoradgang til det nye lager. +teams.remove_all_repos_desc = Dette vil slette alle depoter fra teamet +teams.add_all_repos_title = Tilføj alle depoter +settings.update_settings = Opdater indstillinger +settings.update_setting_success = Organisationsindstillingerne er blevet opdateret. +settings.delete = Slet organisation +members.invite_now = Inviter nu +teams.members = Team medlemmer +teams.owners_permission_desc = Ejere har fuld adgang til alle depoter og har administratoradgang til organisationen. +teams.update_settings = Opdater indstillinger +settings.labels_desc = Tilføj etiketter, som kan bruges til problemer for alle lagre under denne organisation. +members.membership_visibility = Medlemskabs synlighed: +members.public = Synlig +settings.delete_prompt = Organisationen fjernes permanent. Dette KAN IKKE fortrydes! +settings.confirm_delete_account = Bekræft sletning +settings.delete_org_title = Slet organisation +settings.delete_org_desc = Denne organisation vil blive slettet permanent. Vil du fortsætte? +settings.hooks_desc = Tilføj webhooks, som vil blive udløst for alle lagre under denne organisation. +teams.leave.detail = Er du sikker på, at du vil forlade teamet "%s"? +teams.can_create_org_repo = Opret depoter +teams.general_access_helper = Medlemmers tilladelser bestemmes af nedenstående tilladelsestabell. +teams.delete_team = Slet team +teams.add_team_member = Slet team +form.name_pattern_not_allowed = Mønsteret "%s" er ikke tilladt i et organisationsnavn. +form.create_org_not_allowed = Du har ikke tilladelse til at oprette en organisation. +teams.none_access = Ingen adgang +teams.none_access_helper = Muligheden "ingen adgang" har kun effekt på private arkiver. +teams.general_access = Brugerdefineret adgang +members.leave = Forlad +teams.leave = Forlad +teams.join = Deltag +teams.delete_team_success = Teamet er blevet slettet. +teams.write_permission_desc = Dette team giver Skrive adgang: medlemmer kan læse fra og skubbe til teamdepoter. +teams.invite_team_member = Inviter til %s +teams.invite_team_member.list = Afventende invitationer +teams.admin_permission_desc = Dette team giver Administrator adgang: medlemmer kan læse fra, skubbe til og tilføje samarbejdspartnere til teamdepoter. +teams.create_repo_permission_desc = Derudover giver dette team tilladelse til Opret depot: medlemmer kan oprette nye lagre i organisationen. +teams.delete_team_title = Slet team +teams.repositories = Team depoter +teams.delete_team_desc = Sletning af et team tilbagekalder depotadgang fra dets medlemmer. Vil du fortsætte? +teams.add_all_repos_desc = Dette vil tilføje alle organisationens depoter til teamet. +teams.add_nonexistent_repo = Depotet, du forsøger at tilføje, eksisterer ikke, opret det først. +teams.invite.by = Inviteret af %s +teams.all_repositories_helper = Teamet har adgang til alle depoter. Hvis du vælger dette, føjes alle eksisterende depoter til teamet. +teams.invite.title = Du er blevet inviteret til at deltage i teamet %s i organisationen %s. +teams.add_duplicate_users = Brugeren er allerede et teammedlem. +teams.repos.none = Ingen depoter kunne tilgås af dette team. +teams.members.none = Ingen medlemmer på dette team. +teams.specific_repositories = Specifikke depoter +teams.specific_repositories_helper = Medlemmer vil kun have adgang til depoter, der udtrykkeligt er føjet til teamet. Hvis du vælger dette vil det ikke automatisk fjerne depoter, der allerede er tilføjet med Alle depoter. +teams.all_repositories = Alle depoter +teams.invite.description = Klik venligst på knappen nedenfor for at blive medlem af teamet. [admin] orgs.members = Medlemmer @@ -2601,6 +2936,481 @@ config.allow_dots_in_usernames = Tillad brugere at bruge prikker i deres brugern auths.oauth2_icon_url = Icon URL users.edit = Redigere users.auth_source = Godkendelseskilde +monitor.queue.settings.maxnumberworkers.placeholder = I øjeblikket %[1]d +monitor.queue.settings.submit = Opdater indstillinger +monitor.queue.settings.changed = Indstillinger opdateret +monitor.queue.settings.remove_all_items = Slet alle +monitor.queue.settings.remove_all_items_done = Alle varer i køen er blevet fjernet. +notices.system_notice_list = Systemmeddelelser +dashboard.delete_repo_archives = Slet alle depoters arkiver (ZIP, TAR.GZ osv..) +organizations = Organisationer +dashboard.delete_repo_archives.started = Slet alle repository arkiver opgave startet. +dashboard.statistic = Oversigt +dashboard.task.finished = Opgave: %[1]s startet af %[2]s er afsluttet +dashboard.task.unknown = Ukendt opgave: %[1]s +dashboard.cron.error = Fejl i Cron: %s: %[3]s +dashboard.operation_name = Operations navn +dashboard.delete_missing_repos.started = Slet alle depoter, der mangler deres Git-filopgave startet. +dashboard.delete_generated_repository_avatars = Slet genererede depot avatarer +dashboard.sync_repo_branches = Synkroniser mistede grene fra Git-data til databasen +dashboard.sync_repo_tags = Synkroniser tags fra Git-data til database +dashboard.archive_cleanup = Slet gamle depotarkiver +dashboard.deleted_branches_cleanup = Ryd op i slettede grene +dashboard = Instrumentpanel +self_check = Selvtjek +assets = Kode aktiver +repositories = Depoter +authentication = Godkendelseskilder +emails = Bruger e-mails +monitor = Overvågning +settings = Admin indstillinger +dashboard.new_version_hint = Forgejo %s er nu tilgængelig, du kører %s. Tjek bloggen for flere detaljer. +dashboard.operation_switch = Skift +dashboard.operation_run = Kør +dashboard.clean_unbind_oauth = Rens ubundne OAuth-forbindelser +dashboard.delete_inactive_accounts.started = Slet alle uaktiverede konti opgave startet. +dashboard.delete_missing_repos = Slet alle depoter, der mangler deres Git-filer +dashboard.update_migration_poster_id = Opdater migrationsplakat-id'er +dashboard.memory_obtained = Hukommelse opnået +dashboard.pointer_lookup_times = Pointer-opslagstider +hooks = Webhooks +dashboard.cron.finished = Cron: %[1]s er færdig +dashboard.delete_inactive_accounts = Slet alle uaktiverede konti +config = Konfiguration +notices = Systemmeddelelser +config_summary = Oversigt +dashboard.system_status = System status +dashboard.update_mirrors = Opdater spejle +dashboard.server_uptime = Server oppetid +dashboard.current_goroutine = Nuværende goroutiner +dashboard.current_memory_usage = Aktuel hukommelsesbrug +dashboard.total_memory_allocated = Samlet hukommelse tildelt +integrations = Integrationer +dashboard.operations = Vedligeholdelses operationer +dashboard.repo_health_check = Sundhedstjek alle depoter +dashboard.check_repo_stats = Tjek alle depotstatistikker +dashboard.git_gc_repos = Samle alt affald fra alle depoter +dashboard.resync_all_sshkeys = Opdater filen ".ssh/authorized_keys" med Forgejo SSH-nøgler. +dashboard.resync_all_sshprincipals = Opdater ".ssh/authorized_principals" filen med Forgejo SSH principals. +users = Brugerkonti +identity_access = Identitet og adgang +first_page = Først +last_page = Sidst +dashboard.task.process = Opgave: %[1]s +total = Total: %d +dashboard.cron.started = Startede Cron: %[1]s +dashboard.cron.process = Cron: %[1]s +dashboard.cron.cancelled = Cron: %[1]s annulleret: %[3]s +dashboard.clean_unbind_oauth_success = Alle ubundne OAuth-forbindelser er blevet slettet. +dashboard.task.started = Startet opgave: %[1]s +dashboard.task.cancelled = Opgave: %[1]s annulleret: %[3]s +dashboard.task.error = Fejl i Opgave: %[1]s: %[3]s +dashboard.resync_all_hooks = Gensynkroniser pre-receive, update og post-receive hooks for alle depoter +dashboard.reinit_missing_repos = Geninitialiser alle manglende Git-depoter, som der findes poster for +dashboard.sync_external_users = Synkroniser eksterne brugerdata +dashboard.cleanup_hook_task_table = Oprydning hook_task tabel +dashboard.cleanup_packages = Ryd udløbne pakker +dashboard.cleanup_actions = Oprydning af udløbne logfiler og artefakter fra handlinger +users.remote = Remote +users.password_helper = Lad adgangskoden være tom for at bevare den uændret. +dashboard.cancel_abandoned_jobs = Annuller forladte handlingsjob +users.activated.description = Afslutning af e-mailbekræftelse. Ejeren af en uaktiveret konto vil ikke være i stand til at logge ind, før e-mailbekræftelsen er gennemført. +users.admin.description = Giv denne bruger fuld adgang til alle administrative funktioner, der er tilgængelige via web-UI og API. +users.update_profile = Opdater brugerkonto +users.still_has_org = Denne bruger er medlem af en organisation. Fjern først brugeren fra enhver organisation. +users.purge_help = Tvangsslet brugeren og eventuelle depoter, organisationer og pakker, der ejes af brugeren. Alle kommentarer og problemer indsendt af denne bruger vil også blive slettet. +users.is_admin = Administrator konto +dashboard.mspan_structures_obtained = Mspan strukturer opnået +dashboard.mcache_structures_usage = MCache strukturer brug +dashboard.mspan_structures_usage = MSpan strukturer brug +dashboard.mcache_structures_obtained = MCache-strukturer opnået +dashboard.profiling_bucket_hash_table_obtained = Profilering bucket hash tabel opnået +dashboard.gc_metadata_obtained = GC-metadata opnået +dashboard.other_system_allocation_obtained = Anden systemallokering opnået +dashboard.next_gc_recycle = Næste GC genbrug +dashboard.total_gc_pause = Total GC-pause +dashboard.last_gc_time = Tid siden sidste GC +dashboard.delete_old_system_notices = Slet alle gamle systemmeddelelser fra databasen +dashboard.gc_lfs = Affaldssamler LFS-metaobjekter +dashboard.start_schedule_tasks = Start planlæg handlingsopgaver +dashboard.sync_branch.started = Gren synkronisering startede +dashboard.sync_tag.started = Tag-synkronisering er startet +dashboard.rebuild_issue_indexer = Genopbyg problemindekser +users.update_profile_success = Brugerkontoen er blevet opdateret. +users.created = Oprettet +users.max_repo_creation = Maksimalt antal depoter +users.prohibit_login = Suspenderet konto +users.is_restricted = Begrænset konto +dashboard.memory_allocate_times = Hukommelsestildelinger +dashboard.memory_free_times = Hukommelses frigørelse +dashboard.current_heap_usage = Nuværende heap-brug +dashboard.heap_memory_obtained = Heap-hukommelse opnået +dashboard.heap_objects = Heap genstande +dashboard.bootstrap_stack_usage = Brug af bootstrap-stak +dashboard.update_checker = Opdateringskontrol +dashboard.delete_old_actions.started = Slet alle gamle aktiviteter fra den påbegyndte database. +dashboard.stop_zombie_tasks = Stop zombiehandlingsopgaver +dashboard.stop_endless_tasks = Stop endeløse handlingsopgaver +users.2fa = 2FA +users.last_login = Sidst logget ind +users.never_login = Aldrig logget ind +users.new_success = Brugerkontoen "%s" er blevet oprettet. +users.local_import.description = Tillad import af depoter fra serverens lokale filsystem. Dette kan være et sikkerhedsproblem. +users.organization_creation.description = Tillad oprettelse af nye organisationer. +users.delete_account = Slet brugerkonto +users.cannot_delete_self = Du kan ikke slette dig selv +users.still_own_repo = Denne bruger ejer stadig et eller flere arkiver. Slet eller overfør disse depoter først. +users.list_status_filter.is_admin = Admin +users.block.description = Bloker denne bruger i at interagere med denne tjeneste via deres konto, og forbyd at logge ind. +users.restricted.description = Tillad kun interaktion med de depoter og organisationer, hvor denne bruger er tilføjet som en samarbejdspartner. Dette forhindrer adgang til offentlige arkiver i denne instans. +users.list_status_filter.menu_text = Filter +dashboard.heap_memory_idle = Heap hukommelse inaktiv +dashboard.heap_memory_in_use = Heap hukommelse i brug +dashboard.heap_memory_released = Heap-hukommelse frigivet +dashboard.stack_memory_obtained = Stakhukommelse opnået +dashboard.last_gc_pause = Sidste GC-pause +dashboard.gc_times = GC times +dashboard.delete_old_actions = Slet alle gamle aktiviteter fra databasen +users.allow_create_organization = Kan skabe organisationer +users.list_status_filter.not_admin = Ikke admin +users.allow_import_local = Kan importere lokale depoter +users.send_register_notify = Giv besked om tilmelding via e-mail +users.local = Lokal +users.auth_login_name = Godkendelses-loginnavn +users.allow_git_hook = Kan skabe Git hooks +users.allow_git_hook_tooltip = Git hooks udføres som OS-brugeren, der kører Forgejo og vil have samme niveau af værtsadgang. Som et resultat kan brugere med dette specielle Git hook-privilegium få adgang til og ændre alle Forgejo-depoter såvel som databasen, der bruges af Forgejo. Derfor er de også i stand til at opnå Forgejo-administratorrettigheder. +users.repos = Depoter +users.still_own_packages = Denne bruger ejer stadig en eller flere pakker, slet disse pakker først. +users.deletion_success = Brugerkontoen er blevet slettet. +users.reset_2fa = Nulstil 2FA +users.max_repo_creation_desc = (Indtast -1 for at bruge den globale standardgrænse.) +users.is_activated = Aktiveret konto +users.edit_account = Rediger brugerkonto +packages.version = Version +users.list_status_filter.reset = Nulstil +users.list_status_filter.is_active = Aktiv +users.list_status_filter.not_active = Inaktiv +users.purge = Udrens bruger +users.user_manage_panel = Administrer brugerkonti +users.new_account = Opret brugerkonto +users.admin = Admin +users.restricted = Begrænset +users.reserved = Reserveret +users.bot = Bot +emails.primary = Primær +emails.updated = E-mail opdateret +emails.not_updated = Kunne ikke opdatere den anmodede e-mailadresse: %v +packages.package_manage_panel = Administrer pakker +packages.total_size = Samlet størrelse: %s +packages.unreferenced_size = Ikke-referencestørrelse: %s +users.list_status_filter.is_2fa_enabled = 2FA aktiveret +users.list_status_filter.not_2fa_enabled = 2FA deaktiveret +emails.filter_sort.email_reverse = E-mail (omvendt) +emails.filter_sort.name_reverse = Brugernavn (omvendt) +emails.delete = Slet e-mail +emails.delete_desc = Er du sikker på, at du vil slette denne e-mailadresse? +emails.deletion_success = E-mailadressen er blevet slettet. +users.list_status_filter.is_restricted = Begrænset +emails.duplicate_active = Denne e-mailadresse er allerede aktiv for en anden bruger. +emails.change_email_header = Opdater e-mail-egenskaber +emails.change_email_text = Er du sikker på, at du vil opdatere denne e-mailadresse? +repos.issues = Problemer +repos.size = Størrelse +repos.lfs_size = LFS størrelse +users.list_status_filter.not_restricted = Ikke begrænset +users.details = Brugeroplysninger +emails.email_manage_panel = Administrer bruger-e-mails +users.list_status_filter.is_prohibit_login = Forbyd login +users.list_status_filter.not_prohibit_login = Tillad login +emails.delete_primary_email_error = Du kan ikke slette den primære e-mail. +orgs.org_manage_panel = Administrer organisationer +repos.repo_manage_panel = Administrer depoter +repos.unadopted = Ikke-adopterede depoter +repos.unadopted.no_more = Ingen ikke-adopterede depoter fundet. +auths.domain = Domæne +auths.port = Port +auths.host = Host +packages.size = Størrelse +auths.security_protocol = Sikkerhedsprotokol +auths.updated = Opdateret +auths.type = Type +auths.syncenabled = Slå brugersynkronisering til +packages.published = Offentliggjort +packages.type = Type +packages.cleanup = Ryd op i udløbne data +packages.cleanup.success = Der er ryddet op i udløbne data +defaulthooks.add_webhook = Tilføj standard webhook +defaulthooks.update_webhook = Opdater standardwebhook +systemhooks = System webhooks +systemhooks.desc = Webhooks foretager automatisk HTTP POST-anmodninger til en server, når visse Forgejo-hændelser udløses. Webhooks, der er defineret her, vil virke på alle repositories på systemet, så overvej venligst eventuelle præstationsimplikationer, dette kan have. Læs mere i webhooks-guiden. +defaulthooks = Default webhooks +defaulthooks.desc = Webhooks foretager automatisk HTTP POST-anmodninger til en server, når visse Forgejo-hændelser udløses. Webhooks defineret her er standarder og vil blive kopieret til alle nye repositories. Læs mere i webhooks-guiden. +packages.creator = Skaber +systemhooks.update_webhook = Opdater System Webhook +config.enable_openid_signin = Aktiver OpenID-logon +config.cache_test = Test cache +config.cache_item_ttl = Cache element TTL +self_check.database_inconsistent_collation_columns = Databasen bruger sortering %s, men disse kolonner bruger uoverensstemmende sorteringer. Det kan forårsage nogle uventede problemer. +auths.oauth2_group_claim_name = Gør krav på navn med gruppenavne for denne kilde. (Valgfri) +auths.oauth2_map_group_to_team_removal = Fjern brugere fra synkroniserede teams, hvis brugeren ikke tilhører den tilsvarende gruppe. +auths.tips.oauth2.general.tip = Når du registrerer en ny OAuth2-godkendelse, skal URL-adressen til tilbagekald/omdirigering være: +auths.tip.nextcloud = Registrer en ny OAuth-bruger på din instans ved hjælp af følgende menu "Indstillinger -> Sikkerhed -> OAuth 2.0-klient" +auths.tip.dropbox = Opret en ny applikation på %s +auths.tip.github = Registrer en ny OAuth-applikation på %s +auths.tip.gitlab_new = Registrer en ny applikation på %s +auths.tip.yandex = Opret en ny applikation på %s. Vælg følgende tilladelser fra afsnittet "Yandex.Passport API": "Adgang til e-mailadresse", "Adgang til brugeravatar" og "Adgang til brugernavn, fornavn og efternavn, køn" +config.run_mode = Kør tilstand +config.ssh_config = SSH konfiguration +config.allow_only_internal_registration = Tillad kun registrering gennem Forgejo selv +config.ssh_minimum_key_size_check = Minimum nøglestørrelse kontrol +config.enable_captcha = Aktiver CAPTCHA +config.ssh_minimum_key_sizes = Minimum nøglestørrelser +config.default_keep_email_private = Skjul e-mailadresser som standard +config.default_allow_create_organization = Tillad oprettelse af organisationer som standard +config.queue_length = Kø længde +config.default_enable_dependencies = Aktiver problemafhængigheder som standard +config.webhook_config = Webhook-konfiguration +config.deliver_timeout = Lever timeout +config.skip_tls_verify = Spring over TLS-bekræftelse +config.mailer_config = Mailer konfiguration +config.https_only = Kun HTTPS +monitor.next = Næste gang +monitor.processes_count = %d Processer +monitor.duration = Varighed (r) +config.session_life_time = Session levetid +monitor.start = Starttid +notices.deselect_all = Fravælg alle +notices.view_detail_header = Bemærk detaljer +notices.select_all = Vælg alle +self_check.database_collation_case_insensitive = Databasen bruger en sortering %s, som er en ufølsom sortering. Selvom Forgejo kunne arbejde med det, kan der være nogle sjældne tilfælde, som ikke fungerer som forventet. +config.git_max_diff_line_characters = Maks. diff-tegn pr. linje +config.access_log_template = Skabelon til adgangslog +monitor.process.children = Børn +monitor.queues = Køer +monitor.queue.settings.desc = Puljer vokser dynamisk som reaktion på deres blokering af arbejderkø. +auths.auth_manage_panel = Administrer godkendelseskilder +auths.auth_type = Godkendelsestype +auths.map_group_to_team_removal = Fjern brugere fra synkroniserede teams, hvis brugeren ikke tilhører den tilsvarende LDAP-gruppe +auths.attribute_name = Fornavn attribut +auths.map_group_to_team = Tilknyt LDAP-grupper til organisationsteams (lad feltet stå tomt for at springe over) +auths.helo_hostname = HELO værtsnavn +auths.helo_hostname_helper = Værtsnavn sendt med HELO. Lad stå tomt for at sende det nuværende værtsnavn. +auths.disable_helo = Deaktiver HELO +auths.oauth2_authURL = Godkend URL +auths.oauth2_profileURL = Profil-URL +auths.skip_local_two_fa_helper = Hvis du ikke er indstillet, betyder det, at lokale brugere med 2FA indstillet stadig skal bestå 2FA for at logge på +auths.oauth2_required_claim_name_helper = Indstil dette navn for at begrænse login fra denne kilde til brugere med et krav med dette navn +auths.tips.gmail_settings = Gmail-indstillinger: +auths.update_success = Godkendelseskilden er blevet opdateret. +auths.tip.mastodon = Indtast en brugerdefineret instans-URL for den mastodon-instans, du vil godkende med (eller brug standarden) +auths.activated = Denne godkendelseskilde er aktiveret +auths.new_success = Godkendelsen "%s" er blevet tilføjet. +config.enable_openid_signup = Aktiver OpenID-selvregistrering +config.show_registration_button = Vis registreringsknap +config.require_sign_in_view = Kræv at logge ind for at se indhold +config.cache_conn = Cacheforbindelse +config.xorm_log_sql = Log SQL +monitor.stacktrace = Stakspor +monitor.execute_time = Udførelsestid +monitor.last_execution_result = Resultat +monitor.process.cancel_notices = Annuller: %s? +monitor.queue.type = Type +monitor.queue.exemplar = Eksempler type +auths.auth_name = Godkendelsesnavn +auths.attribute_username = Brugernavn attribut +auths.attribute_username_placeholder = Lad stå tomt for at bruge brugernavnet indtastet i Forgejo. +auths.enable_ldap_groups = Aktiver LDAP-grupper +auths.ms_ad_sa = MS AD søgeattributter +auths.smtpport = SMTP port +auths.allowed_domains = Tilladte domæner +auths.skip_tls_verify = Spring over TLS-bekræftelse +auths.force_smtps = Tving SMTPS +auths.force_smtps_helper = SMTPS bruges altid på port 465. Indstil denne til at tvinge SMTPS på andre porte. (Ellers vil STARTTLS blive brugt på andre porte, hvis det understøttes af værten.) +auths.oauth2_emailURL = E-mail URL +auths.oauth2_scopes = Yderligere omfang +auths.oauth2_admin_group = Gruppekravværdi for administratorbrugere. (Valgfrit – kræver kravnavn ovenfor) +auths.oauth2_restricted_group = Gruppekravsværdi for begrænsede brugere. (Valgfrit – kræver kravnavn ovenfor) +auths.oauth2_map_group_to_team = Kortlæg hævdede grupper til organisationsteams. (Valgfrit – kræver kravnavn ovenfor) +auths.tip.google_plus = Hent OAuth2-klientlegitimationsoplysninger fra Google API-konsollen på %s +auths.tip.openid_connect = Brug OpenID Connect Discovery URL (/.well-known/openid-configuration) til at angive slutpunkterne +auths.tip.gitea = Registrer en ny OAuth2-applikation. Guide kan findes på %s +auths.edit = Rediger godkendelseskilden +auths.update = Opdater godkendelseskilden +config.offline_mode = Lokal tilstand +config.disable_router_log = Deaktiver routerlog +config.git_version = Git version +config.app_data_path = Appens datasti +config.repo_root_path = Depotets rodsti +config.log_file_root_path = Logsti +config.script_type = Script type +config.reverse_auth_user = Omvendt proxy-godkendelsesbruger +config.ssh_key_test_path = Nøgleteststi +config.ssh_keygen_path = Keygen ("ssh-keygen") sti +config.active_code_lives = Aktiveringskodens udløbstid +config.no_reply_address = Skjult e-mail-domæne +config.mailer_enable_helo = Aktiver HELO +config.mailer_protocol = Protokol +config.mailer_use_sendmail = Brug Sendmail +config.mailer_sendmail_path = Sendmail sti +config.mailer_sendmail_args = Ekstra argumenter til Sendmail +config.cache_test_failed = Kunne ikke undersøge cachen: %v. +config.cache_test_slow = Cachetest lykkedes, men svaret er langsomt: %s. +config.gc_interval_time = GC interval tid +config.git_max_diff_files = Max diff filer vist +config.git_gc_args = GC argumenter +monitor.queue.settings.maxnumberworkers.error = Max antal arbejdere skal være et tal +notices.inverse_selection = Omvendt valg +notices.delete_selected = Slet valgte +auths.allowed_domains_helper = Lad være tomt for at tillade alle domæner. Adskil flere domæner med et komma (","). +auths.skip_local_two_fa = Spring over lokal 2FA +auths.oauth2_required_claim_value_helper = Indstil denne værdi for at begrænse login fra denne kilde til brugere med et krav med dette navn og denne værdi +monitor.queue.settings.maxnumberworkers = Max antal arbejdere +notices.delete_all = Slet alle meddelelser +auths.smtp_auth = SMTP-godkendelsestype +config.default_visibility_organization = Standardsynlighed for nye organisationer +auths.tip.facebook = Registrer en ny applikation på %s og tilføj produktet "Facebook Login" +auths.tip.twitter = Gå til %s, opret en applikation og sørg for, at "Tillad, at denne applikation bruges til at logge på med Twitter" er aktiveret +self_check.database_fix_mysql = For MySQL/MariaDB-brugere kan du bruge kommandoen "forgejo doctor convert" til at løse sorteringsproblemerne, eller du kan også løse problemet ved at "ALTER ... COLLATE ..." SQLs manuelt. +auths.tips.oauth2.general = OAuth2-godkendelse +systemhooks.add_webhook = Tilføj System Webhook +auths.new = Tilføj godkendelseskilde +auths.unable_to_initialize_openid = Kan ikke initialisere OpenID Connect Provider: %s +auths.tip.oauth2_provider = OAuth2-udbyder +auths.tip.bitbucket = Registrer en ny OAuth-bruger på %s og tilføj tilladelsen "Konto" - "Læs" +auths.login_source_exist = Godkendelseskilden "%s" findes allerede. +auths.invalid_openIdConnectAutoDiscoveryURL = Ugyldig Auto Discovery URL (dette skal være en gyldig URL, der starter med http:// eller https://) +config.lfs_root_path = LFS rodsti +auths.login_source_of_type_exist = Der findes allerede en godkendelseskilde af denne type. +config.lfs_content_path = LFS indholdssti +config.lfs_http_auth_expiry = LFS HTTP-godkendelsesudløbstid +config.disable_register = Deaktiver selvregistrering +config.register_email_confirm = Kræv e-mail-bekræftelse for at tilmelde dig +config.mailer_user = Bruger +auths.openIdConnectAutoDiscoveryURL = OpenID Connect Auto Discovery URL +auths.oauth2_use_custom_url = Brug tilpassede webadresser i stedet for standardwebadresser +auths.pam_service_name = PAM-tjenestenavn +auths.pam_email_domain = PAM e-mail domæne (valgfrit) +auths.oauth2_provider = OAuth2-udbyder +auths.oauth2_clientID = Klient-id (nøgle) +auths.oauth2_clientSecret = Klientens hemmelighed +auths.oauth2_required_claim_value = Påkrævet kravværdi +config.db_config = Database konfiguration +config.db_host = Vært +config.ssh_start_builtin_server = Brug indbygget server +config.lfs_config = LFS-konfiguration +config.service_config = Tjenestekonfiguration +config.mailer_smtp_port = SMTP-port +config.disabled_logger = Deaktiveret +config.access_log_mode = Adgang til logtilstand +config.allow_only_external_registration = Tillad kun registrering via eksterne tjenester +config.mailer_smtp_addr = SMTP vært +config.mailer_sendmail_timeout = Sendmail timeout +auths.oauth2_tokenURL = Token URL +auths.oauth2_tenant = Lejer +config.custom_conf = Konfigurationsfilstien +config.custom_file_root_path = Brugerdefineret fil rodsti +config.app_ver = Forgejo version +config.app_url = Base URL +config.ssh_domain = SSH server domæne +config.ssh_port = Port +config.ssh_listen_port = Lyt port +config.ssh_root_path = Rodsti +config.db_type = Type +config.reset_password_code_lives = Gendannelseskodens udløbstid +config.mailer_use_dummy = Attrapp +config.test_mail_failed = Kunne ikke sende en test-e-mail til "%s": %v +config.cache_adapter = Cache adapter +config.test_mail_sent = En test-e-mail er blevet sendt til "%s". +config.oauth_config = OAuth-konfiguration +config.test_email_placeholder = E-mail (f.eks. test@example.com) +config.send_test_mail = Send test-e-mail +config.send_test_mail_submit = Send +config.cache_config = Cache konfiguration +config.cookie_name = Cookie navn +config.session_config = Sessionskonfiguration +config.session_provider = Session udbyder +config.provider_config = Udbyder konfig +notices.op = Op. +notices.delete_success = Systemmeddelelserne er blevet slettet. +self_check.no_problem_found = Intet problem fundet endnu. +self_check.database_collation_mismatch = Forvent, at databasen bruger sortering: %s +auths.user_dn = Bruger DN +auths.bind_dn = Bind DN +auths.bind_password = Bind adgangskode +auths.user_base = Bruger søgebase +auths.verify_group_membership = Bekræft gruppemedlemskab i LDAP (lad filteret stå tomt for at springe over) +auths.group_search_base = Gruppesøgningsbase DN +auths.smtphost = SMTP vært +auths.oauth2_required_claim_name = Påkrævet kravnavn +auths.tips = Tips +auths.delete = Slet godkendelseskilden +auths.delete_auth_title = Slet godkendelseskilden +auths.delete_auth_desc = Sletning af en godkendelseskilde forhindrer brugere i at bruge den til at logge ind. Vil du fortsætte? +auths.still_in_used = Godkendelseskilden er stadig i brug. Konverter eller slet alle brugere, der bruger denne godkendelseskilde først. +auths.deletion_success = Godkendelseskilden er blevet slettet. +config.run_user = Bruger at køre som +config.mail_notify = Aktiver e-mailmeddelelser +config.git_migrate_timeout = Migration timeout +config.log_config = Log konfiguration +auths.default_domain_name = Standard domænenavn, der bruges til e-mailadressen +auths.attribute_mail = E-mail attribut +auths.allow_deactivate_all = Tillad et tomt søgeresultat for at deaktivere alle brugere +auths.use_paged_search = Brug sidesøgning +auths.search_page_size = Sidestørrelse +auths.attribute_ssh_public_key = Offentlig SSH-nøgleattribut +auths.attribute_avatar = Avatar attribut +auths.attributes_in_bind = Hent attributter i bind DN-kontekst +config.app_name = Instans titel +config.app_slogan = instans slogan +config.cache_interval = Cache interval +monitor.queue.settings.title = Pool indstillinger +notices.type = Type +notices.type_2 = Opgave +config.db_schema = Skematisk +config.db_ssl_mode = SSL +config.db_path = Sti +notices.operations = Operationer +auths.restricted_filter_helper = Lad være tom for ikke at angive nogen brugere som begrænset. Brug en stjerne ("*") for at indstille alle brugere, der ikke matcher Admin-filteret, som begrænset. +auths.group_attribute_list_users = Gruppeattribut, der indeholder en liste over brugere +auths.tip.discord = Registrer en ny applikation på %s +config.server_config = Server konfiguration +config.domain = Server domæne +config.set_setting_failed = Indstilling af %s mislykkedes +monitor.previous = Tidligere tid +monitor.process = Kørende processer +monitor.execute_times = Udførelser +monitor.download_diagnosis_report = Hent diagnoserapport +monitor.process.cancel = Annuller processen +monitor.process.cancel_desc = Annullering af en proces kan medføre tab af data +monitor.queue = Kø: %s +monitor.queue.numberworkers = Antal arbejdere +monitor.queue.activeworkers = Aktive arbejdere +monitor.queue.maxnumberworkers = Max antal arbejdere +monitor.queue.numberinqueue = Nummer i kø +monitor.queue.review_add = Gennemgå / tilføj arbejdere +config.git_pull_timeout = Pull Operation timeout +config.git_clone_timeout = Klone Operation timeout +config.git_gc_timeout = GC Operation timeout +config.logger_name_fmt = Logger: %s +monitor.stats = Statistik +monitor.cron = Cron opgaver +monitor.schedule = Tidsplan +config.cache_test_succeeded = Cachetest gennemført, fik et svar i %s. +config.cookie_life_time = Cookie levetid +config.picture_config = Billede og avatar konfiguration +config.picture_service = Billedservice +config.disable_gravatar = Deaktiver Gravatar +config.enable_federated_avatar = Aktiver fødererede avatarer +config.open_with_editor_app_help = "Åbn med"-editorerne til klonmenuen. Hvis den efterlades tom, vil standarden blive brugt. Udvid for at se standarden. +config.git_config = Git konfiguration +config.git_disable_diff_highlight = Deaktiver diff-syntaksfremhævning +config.git_max_diff_lines = Max diff-linjer pr. fil +config.git_mirror_timeout = Spejlopdateringstimeout +auths.attribute_surname = Efternavn attribut +auths.filter = Bruger filter +auths.admin_filter = Admin filter +auths.restricted_filter = Begrænset filter +auths.user_attribute_in_group = Brugerattribut angivet i gruppen [packages] arch.version.description = Beskrivelse @@ -2646,15 +3456,351 @@ rpm.repository = Depot info rpm.repository.architectures = Arkitekturer alt.registry = Konfigurer dette register fra kommandolinjen: alt.repository = Depot info +alpine.repository.repositories = Depoter +search_in_external_registry = Søg i %s +dependency.version = Version +alpine.registry = Konfigurer dette register ved at tilføje url'en i din /etc/apk/repositories fil: +alpine.registry.key = Download den offentlige RSA-nøgle til registreringsdatabasen i mappen /etc/apk/keys/ for at bekræfte indekssignaturen: +alpine.registry.info = Vælg $branch og $repository fra listen nedenfor. +empty = Der er ingen pakker endnu. +filter.type.all = Alle +filter.container.untagged = Umærket +about = Om denne pakke +filter.no_result = Dit filter gav ingen resultater. +dependencies = Afhængigheder +empty.documentation = For mere information om pakkeregistret, se dokumentationen. +filter.type = Type +registry.documentation = For mere information om %s registreringsdatabasen, se dokumentationen. +title = Pakker +desc = Administrer depotpakker. +empty.repo = Har du uploadet en pakke, men den vises ikke her? Gå til pakkeindstillinger og link den til denne repo. +filter.container.tagged = Tagget +published_by = Udgivet %[1]s af %[3]s +published_by_in = Udgivet %[1]s af %[3]s i %[5]s +installation = Installation +requirements = Krav +cran.registry = Konfigurer dette register i din Rprofile.site fil: +rubygems.required.rubygems = Kræver RubyGem version +owner.settings.chef.title = Kokkeregister +owner.settings.chef.keypair = Generer nøglepar +arch.pacman.repo.multi.item = Konfiguration for %s +arch.pacman.sync = Synkroniser pakke med pacman: +arch.version.properties = Versionsegenskaber +arch.version.provides = Forsyner +arch.version.checkdepends = Check afhænger +arch.version.replaces = Erstatter +conan.install = For at installere pakken ved hjælp af Conan skal du køre følgende kommando: +conda.registry = Konfigurer dette register som et Conda-depot i din .condarc-fil: +conda.install = For at installere pakken ved hjælp af Conda skal du køre følgende kommando: +container.images.title = Billeder +container.details.type = Billedtype +container.details.platform = Platform +container.pull = Træk billedet fra kommandolinjen: +container.digest = Fordøje +alt.setup = Tilføj et depot til listen over tilsluttede arkiver (vælg den nødvendige arkitektur i stedet for "_arch_"): +vagrant.install = For at tilføje en Vagrant-boks skal du køre følgende kommando: +swift.install2 = og kør følgende kommando: +settings.link = Link denne pakke til et depot +settings.link.success = Depotlinket blev opdateret. +owner.settings.cargo.initialize.description = Et særligt indeks Git-depot er nødvendigt for at bruge Cargo-registret. Brug af denne mulighed vil (gen-)oprette depotet og konfigurere det automatisk. +settings.delete.notice = Du er ved at slette %s (%s). Denne operation er uigenkaldeligt, er du sikker? +owner.settings.cargo.title = Lastregisterindeks +owner.settings.cargo.initialize = Initialiser indeks +owner.settings.cleanuprules.preview.none = Oprydningsreglen matcher ikke nogen pakker. +owner.settings.cleanuprules.none = Der er endnu ingen oprydningsregler. +owner.settings.cleanuprules.keep.count.1 = 1 version pr. pakke +owner.settings.cleanuprules.preview.overview = %d pakker er planlagt til at blive fjernet. +owner.settings.cleanuprules.keep.pattern.container = Den seneste version bevares altid for containerpakker. +settings.delete = Slet pakke +settings.delete.description = Sletning af en pakke er permanent og kan ikke fortrydes. +owner.settings.cleanuprules.keep.count.n = %d versioner pr. pakke +arch.version.makedepends = Gør afhænger +alt.install = Installer pakken +composer.registry = Konfigurer dette register i din ~/.composer/config.json fil: +composer.dependencies = Afhængigheder +settings.delete.success = Pakken er blevet slettet. +settings.delete.error = Kunne ikke slette pakken. +owner.settings.cargo.rebuild.error = Kunne ikke genopbygge Cargo-indeks: %v +owner.settings.cargo.rebuild = Genopbyg indeks +owner.settings.cleanuprules.preview = Forhåndsvisning af oprydningsregel +owner.settings.cleanuprules.keep.count = Behold den nyeste +owner.settings.cleanuprules.keep.pattern = Hold versionerne matchende +owner.settings.chef.keypair.description = Anmodninger sendt til Chef-registret skal være kryptografisk signeret som et middel til godkendelse. Når et nøglepar genereres, gemmes kun den offentlige nøgle på Forgejo. Den private nøgle gives til dig til brug med Knife. Generering af et nyt nøglepar vil overskrive det forrige. +maven.install = For at bruge pakken skal du inkludere følgende i blokken afhængigheder i filen pom.xml: +details = Detaljer +cargo.registry = Konfigurer dette register i Cargo-konfigurationsfilen (for eksempel ~/.cargo/config.toml): +cargo.install = For at installere pakken ved hjælp af Cargo skal du køre følgende kommando: +composer.install = For at installere pakken ved hjælp af Composer skal du køre følgende kommando: +container.multi_arch = OS / Arch +rubygems.required.ruby = Kræver Ruby version +swift.install = Tilføj pakken i din Package.swift-fil: +settings.link.select = Vælg depot +settings.link.button = Opdater depot link +settings.link.error = Kunne ikke opdatere depotlinket. +owner.settings.cargo.initialize.success = Cargo-indekset blev oprettet. +owner.settings.cargo.rebuild.description = Genopbygning kan være nyttig, hvis indekset ikke er synkroniseret med de lagrede Cargo-pakker. +owner.settings.cargo.rebuild.success = Cargo-indekset blev genopbygget med succes. +owner.settings.cleanuprules.add = Tilføj oprydningsregel +owner.settings.cleanuprules.edit = Rediger oprydningsregel +owner.settings.cleanuprules.title = Oprydningsregler +maven.registry = Konfigurer denne registreringsdatabasen i din projekt pom.xml fil: +npm.install2 = eller føj det til filen package.json: +nuget.dependency.framework = Mål Framework +npm.registry = Konfigurer denne registreringsdatabase i din projekt-.npmrc-fil: +nuget.install = For at installere pakken ved hjælp af NuGet skal du køre følgende kommando: +npm.dependencies = Afhængigheder +settings.link.description = Hvis du forbinder en pakke med et depot, vises pakken i depotets pakkeliste. +owner.settings.cargo.initialize.error = Kunne ikke initialisere Cargo index: %v +owner.settings.cleanuprules.keep.title = Versioner, der matcher disse regler, bevares, selvom de matcher en fjernelsesregel nedenfor. +generic.download = Download pakken fra kommandolinjen: +go.install = Installer pakken fra kommandolinjen: +container.layers = Billedlag +container.labels.key = Nøgle +container.labels.value = Værdi +debian.registry.info = Vælg $distribution og $component fra listen nedenfor. +maven.download = For at downloade afhængigheden skal du køre via kommandolinjen: +rpm.distros.suse = på SUSE-baserede distributioner +rpm.distros.redhat = på RedHat-baserede distributioner +owner.settings.cleanuprules.pattern_full_match = Anvend mønster på det fulde pakkenavn +details.author = Forfatter +details.repository_site = Depots hjemmeside +details.documentation_site = Dokumentations hjemmeside +details.license = Licens +assets = Aktiver +versions = Versioner +details.project_site = Projektets hjemmeside +versions.view_all = Se alle +dependency.id = ID +alpine.repository.branches = Grene +arch.version.optdepends = Valgfri afhænger +owner.settings.cleanuprules.remove.title = Versioner, der matcher disse regler, fjernes, medmindre en regel ovenfor siger, at de skal beholdes. +owner.settings.cleanuprules.remove.days = Fjern versioner ældre end +owner.settings.cleanuprules.remove.pattern = Fjern matchende versioner +owner.settings.cleanuprules.success.update = Oprydningsreglen er blevet opdateret. +owner.settings.cleanuprules.success.delete = Oprydningsregel er blevet slettet. +arch.version.backup = Backup +chef.registry = Konfigurer dette register i din ~/.chef/config.rb fil: +npm.install = For at installere pakken ved hjælp af npm skal du køre følgende kommando: +owner.settings.cargo.rebuild.no_index = Kan ikke genopbygge, intet indeks er initialiseret. +maven.install2 = Kør via kommandolinje: +keywords = Keywords +arch.pacman.helper.gpg = Tilføj tillidscertifikat til pacman: +arch.pacman.repo.multi = %s har den samme version i forskellige distributioner. +arch.pacman.conf = Tilføj server med relateret distribution og arkitektur til /etc/pacman.conf: +arch.version.groups = Gruppe +arch.version.depends = Afhænger +arch.version.conflicts = Konflikter [actions] runners.description = Beskrivelse runners.labels = Etiketter runners.name = Navn runners.task_list.repository = Depot +runners.status.active = Aktiv +runners.status.offline = Offline +runners.version = Version +runners.owner_type = Type +runners = Runners +unit.desc = Administrer integrerede CI/CD-pipelines med Forgejo Actions. +status.unknown = Ukendt +runners.runner_title = Runner +runners.task_list = Seneste opgaver på denne løber +runners.task_list.run = Kør +runners.task_list.commit = Commit +runners.edit_runner = Rediger Runner +runs.commit = Commit +runs.scheduled = Planlagt +runs.pushed_by = pushed af +status.running = Kører +status.waiting = Venter +runners.new_notice = Hvordan man starter en runner +status.success = Succes +variables.not_found = Variablen kunne ikke findes. +runs.workflow = Arbejdsgang +runners.last_online = Sidste online tid +runners.task_list.done_at = Udført kl +runners.update_runner = Opdater ændringer +runners.update_runner_success = Runner blev opdateret +runners.update_runner_failed = Løberen kunne ikke opdateres +runners.delete_runner_failed = Runner kunne ikke slettes +runners.delete_runner_header = Bekræft for at slette denne runner +runners.status.idle = Tomgang +runs.no_job_without_needs = Arbejdsgangen skal indeholde mindst ét job uden afhængigheder. +runs.no_job = Arbejdsgangen skal indeholde mindst ét job +runs.no_results = Ingen resultater matchede. +runs.no_workflows = Der er endnu ingen arbejdsgange. +workflow.enable = Aktiver arbejdsgang +workflow.enable_success = Arbejdsgangen "%s" blev aktiveret. +variables.none = Der er ingen variabler endnu. +variables.edit = Rediger variabel +variables.deletion.success = Variablen er blevet fjernet. +variables.creation.failed = Kunne ikke tilføje variabel. +runners.delete_runner_notice = Hvis en opgave kører på denne runner, vil den blive afsluttet og markeret som mislykket. Det kan bryde bygningens arbejdsgang. +runs.no_workflows.help_write_access = Ved du ikke, hvordan du starter med Forgejo Actions? Tjek hurtigstarten i brugerdokumentationen for at skrive dit første workflow, og opsæt en Forgejo-løber til at udføre dine opgaver. +runners.delete_runner_success = Runner blev slettet +variables.update.success = Variablen er blevet redigeret. +status.cancelled = Annulleret +status.skipped = Oversprunget +status.blocked = Blokeret +workflow.disable_success = Arbejdsgangen "%s" blev deaktiveret. +workflow.disable = Deaktiver arbejdsgang +workflow.dispatch.use_from = Brug arbejdsgangen fra +workflow.disabled = Arbejdsgangen er deaktiveret. +workflow.dispatch.trigger_found = Denne arbejdsgang har en workflow_dispatch-hændelsestrigger. +workflow.dispatch.warn_input_limit = Viser kun de første %d input. +workflow.dispatch.success = Kørsel af arbejdsgang blev anmodet om. +workflow.dispatch.input_required = Kræv værdi for input "%s". +workflow.dispatch.invalid_input_type = Ugyldig inputtype "%s". +variables.creation = Tilføj variabel +need_approval_desc = Har brug for godkendelse for at køre arbejdsgange for fork pull-anmodning. +runners.delete_runner = Slet denne runner +runners.status.unspecified = Ukendt +runners.reset_registration_token_success = Runner registreringstoken blev nulstillet +runs.all_workflows = Alle arbejdsgange +runners.reset_registration_token = Nulstil registreringstoken +runs.empty_commit_message = (tom commit besked) +runs.expire_log_message = Logfiler er blevet renset, fordi de var for gamle. +variables = Variabler +runs.actor = Aktør +actions = Handlinger +runners.status = Status +runners.task_list.status = Status +runners.id = ID +runners.task_list.no_tasks = Der er ingen opgave endnu. +runs.status = Status +runs.actors_no_select = Alle aktører +runs.status_no_select = Alle status +runners.none = Ingen runners tilgængelige +variables.management = Administrer variabler +variables.creation.success = Variablen "%s" er blevet tilføjet. +variables.update.failed = Variablen kunne ikke redigeres. +runs.no_workflows.help_no_write_access = For at lære om Forgejo Actions, se dokumentationen. +variables.deletion = Fjern variabel +variables.id_not_exist = Variabel med ID %d findes ikke. +variables.deletion.description = Fjernelse af en variabel er permanent og kan ikke fortrydes. Vil du fortsætte? +variables.description = Variabler vil blive videregivet til visse handlinger og kan ikke læses på anden vis. +variables.deletion.failed = Variablen kunne ikke fjernes. +runs.no_matching_online_runner_helper = Ingen matchende online-runner med etiket: %s +runners.runner_manage_panel = Administrer runners +runners.new = Opret ny runner +workflow.dispatch.run = Kør arbejdsgang +runs.invalid_workflow_helper = Workflow-konfigurationsfilen er ugyldig. Tjek venligst din konfigurationsfil: %s +status.failure = Fiasko +runs.no_runs = Workflowet har ingen kørsler endnu. [tool] 1d = 1 dag 1w = 1 uge 1mon = 1 måned -1y = 1 år \ No newline at end of file +1y = 1 år +months = %d måneder +years = %d år +raw_seconds = sekunder +raw_minutes = minutter +future = fremtid +1h = 1 time +seconds = %d sekunder +minutes = %d minutter +hours = %d timer +weeks = %d uger +1s = 1 sekund +1m = 1 minut +days = %d dage +now = nu + +[repo.permissions] +actions.read = Læs: Se integrerede CI/CD-pipelines og deres logfiler. +releases.write = Skriv: Udgiv, rediger og slet udgivelser og deres aktiver. +wiki.read = Læs: Læs den integrerede wiki og dens historie. +wiki.write = Skriv: Opret, opdater og slet sider i den integrerede wiki. +actions.write = Skriv: Udløs, genstart, annuller eller godkend afventende CI/CD-pipelines manuelt. +ext_issues = Få adgang til linket til en ekstern problemsporing. Tilladelserne administreres eksternt. +issues.write = Skriv: Luk problemer og administrer metadata som etiketter, milepæle, modtagere, forfaldsdatoer og afhængigheder. +releases.read = Læs: Se og download udgivelser. +projects.read = Læs: Få adgang til depot-projekttavler. +projects.write = Skriv: Opret projekter og kolonner, og rediger dem. +packages.read = Læs: Se og download pakker, der er tildelt depotet. +packages.write = Skriv: Udgiv og slet pakker, der er tildelt depotet. +ext_wiki = Få adgang til linket til en ekstern wiki. Tilladelserne administreres eksternt. +pulls.read = Læs: Læsning og oprettelse af pull-anmodninger. +pulls.write = Skriv: Luk pull-anmodninger og administrer metadata som etiketter, milepæle, modtagere, forfaldsdatoer og afhængigheder. +code.read = Læs: Få adgang til og klon koden for depotet. +code.write = Skriv: Skub til depotet, opret filialer og tags. +issues.read = Læs: Læs og opret problemer og kommentarer. + +[graphs] +component_failed_to_load = Der skete en uventet fejl. +code_frequency.what = kode frekvens +contributors.what = bidrag +component_loading = Indlæser %s… +component_loading_info = Dette kan tage lidt tid… +recent_commits.what = nylige commits +component_loading_failed = Kunne ikke indlæse %s + +[secrets] +creation.name_placeholder = uafhængig af store og små bogstaver, alfanumeriske tegn eller understregninger, kan ikke starte med GITEA_ eller GITHUB_ +creation.value_placeholder = Indtast ethvert indhold. Mellemrum i starten og slutningen vil blive udeladt. +creation = Tilføj hemmelighed +description = Hemmeligheder vil blive videregivet til visse handlinger og kan ikke læses på anden vis. +secrets = Hemmeligheder +creation.success = Hemmeligheden "%s" er blevet tilføjet. +deletion = Fjern hemmelighed +deletion.success = Hemmeligheden er blevet fjernet. +deletion.failed = Hemmeligheden kunne ikke fjernes. +management = Håndter hemmeligheder +deletion.description = Fjernelse af en hemmelighed er permanent og kan ikke fortrydes. Vil du fortsætte? +none = Der er ingen hemmeligheder endnu. +creation.failed = Kunne ikke tilføje hemmelighed. + +[dropzone] +invalid_input_type = Filer af denne type må ikke uploades. +remove_file = Fjern fil +default_message = Slip filer eller klik her for at uploade. +file_too_big = Filstørrelsen ({{filesize}} MB) overstiger den maksimale størrelse på ({{maxFilesize}} MB). + +[gpg] +default_key = Underskrevet med standardnøglen +error.generate_hash = Kunne ikke generere hash af commit +error.no_committer_account = Ingen konto knyttet til committers e-mailadresse +error.probable_bad_default_signature = ADVARSEL! Selvom standardnøglen har dette ID, bekræfter den ikke denne commit! Denne commit er MISTÆNLIG. +error.no_gpg_keys_found = Ingen kendt nøgle fundet for denne signatur i databasen +error.not_signed_commit = Ikke en underskrevet commit +error.failed_retrieval_gpg_keys = Kunne ikke hente nogen nøgle knyttet til committerens konto +error.probable_bad_signature = ADVARSEL! Selvom der er en nøgle med dette ID i databasen, bekræfter den ikke denne commit! Denne commit er MISTÆNLIG. +error.extract_sign = Kunne ikke udtrække signatur + +[munits.data] +kib = KiB +b = B +pib = PiB +mib = MiB +tib = TiB +eib = EiB +gib = GiB + +[units] +error.no_unit_allowed_repo = Du har ikke tilladelse til at få adgang til nogen sektion af dette depot. +unit = Enhed +error.unit_not_allowed = Du har ikke tilladelse til at få adgang til denne depotsektion. + +[projects] +type-3.display_name = Organisationsprojekt +type-2.display_name = Depotprojekt +deleted.display_name = Slettet projekt +type-1.display_name = Individuelt projekt + +[markup] +filepreview.line = Linje %[1]d i %[2]s +filepreview.lines = Linjer %[1]d til %[2]d i %[3]s +filepreview.truncated = Forhåndsvisningen er blevet afkortet + +[git.filemode] +symbolic_link = Symbolsk link +submodule = Submodule +changed_filemode = %[1]s → %[2]s +directory = Directory +normal_file = Normal fil +executable_file = Eksekverbar fil + +[translation_meta] +test = (OK) DA was translated by Tacaly \ No newline at end of file diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index f4eca32202..a4aa288456 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -110,7 +110,7 @@ preview=Vorschau loading=Laden … error=Fehler -error404=Die Seite, die du versuchst aufzurufen, existiert nicht oder du bist nicht berechtigt, diese anzusehen. +error404=Die Seite, die du versuchst aufzurufen, existiert nicht, wurde entfernt oder du bist nicht berechtigt, diese anzusehen. go_back=Zurück never=Niemals @@ -229,7 +229,7 @@ app_desc=Ein einfacher, selbst gehosteter Git-Service install=Einfach zu installieren install_desc=Starte einfach die Anwendung für deine Plattform oder nutze Docker. Es existieren auch paketierte Versionen. platform=Plattformübergreifend -platform_desc=Forgejo läuft auf freien Betriebssystemen wie Linux und FreeBSD, sowie auf verschiedenen CPU-Architekturen. Wähle das System, das du magst! +platform_desc=Forgejo läuft auf freien Betriebssystemen wie Linux und FreeBSD sowie auf verschiedenen CPU-Architekturen. Wähle das System, das du magst! lightweight=Leichtgewichtig lightweight_desc=Forgejo hat minimale Systemanforderungen und kann selbst auf einem günstigen und stromsparenden Raspberry Pi betrieben werden! license=Quelloffen @@ -251,12 +251,12 @@ db_schema_helper=Leer lassen, um den Datenbank-Standardwert („public“) zu ve ssl_mode=SSL path=Pfad sqlite_helper=Dateipfad zur SQLite3-Datenbank.
Gib einen absoluten Pfad an, wenn Forgejo als Service gestartet wird. -reinstall_error=Du versuchst, in eine bereits existierende Forgejo Datenbank zu installieren +reinstall_error=Du versuchst, in eine bereits existierende Forgejo-Datenbank zu installieren reinstall_confirm_message=Eine Neuinstallation mit einer bestehenden Forgejo-Datenbank kann mehrere Probleme verursachen. In den meisten Fällen solltest du deine vorhandene „app.ini“ verwenden, um Forgejo auszuführen. Wenn du weißt, was du tust, bestätige die folgenden Angaben: reinstall_confirm_check_1=Die von der SECRET_KEY in app.ini verschlüsselten Daten können verloren gehen: Benutzer können sich unter Umständen nicht mit 2FA/OTP einloggen und Spiegel könnten nicht mehr richtig funktionieren. Mit der Ankreuzung dieses Kästchens bestätigst du, dass die aktuelle app.ini-Datei den korrekten SECRET_KEY enthält. reinstall_confirm_check_2=Die Repositorys und Einstellungen müssen eventuell neu synchronisiert werden. Durch das Ankreuzen dieses Kästchens bestätigst du, dass du die Hooks für die Repositorys und die authorized_keys-Datei manuell neu synchronisierst. Du bestätigst, dass du sicherstellst, dass die Repository- und Spiegeleinstellungen korrekt sind. reinstall_confirm_check_3=Du bestätigst, dass du absolut sicher bist, dass diese Forgejo mit der richtigen app.ini läuft, und du sicher bist, dass du neu installieren musst. Du bestätigst, dass du die oben genannten Risiken anerkennst. -err_empty_db_path=Der SQLite3 Datenbankpfad darf nicht leer sein. +err_empty_db_path=Der SQLite3-Datenbankpfad darf nicht leer sein. no_admin_and_disable_registration=Du kannst Selbst-Registrierungen nicht deaktivieren, ohne ein Administratorkonto zu erstellen. err_empty_admin_password=Das Administrator-Passwort darf nicht leer sein. err_empty_admin_email=Die Administrator-E-Mail darf nicht leer sein. @@ -339,7 +339,7 @@ default_enable_timetracking=Zeiterfassung standardmäßig aktivieren default_enable_timetracking.description=Zeiterfassung standardmäßig für neue Repositorys aktivieren. no_reply_address=Versteckte E-Mail-Domain no_reply_address_helper=Domain-Name für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername „Joe“ in Git als „joe@noreply.example.org“ protokolliert, wenn die versteckte E-Mail-Domain „noreply.example.org“ festgelegt ist. -password_algorithm=Passwort Hashing Algorithmus +password_algorithm=Passwort-Hashing-Algorithmus invalid_password_algorithm=Ungültiger Passwort-Hash-Algorithmus password_algorithm_helper=Lege einen Passwort-Hashing-Algorithmus fest. Algorithmen haben unterschiedliche Anforderungen und Stärken. Der argon2-Algorithmus ist ziemlich sicher, aber er verbraucht viel Speicher und kann für kleine Systeme ungeeignet sein. enable_update_checker=Aktualisierungsprüfung aktivieren @@ -347,11 +347,11 @@ env_config_keys=Umgebungskonfiguration env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Konfigurationsdatei angewendet: allow_dots_in_usernames = Erlaubt Benutzern die Verwendung von Punkten in ihren Benutzernamen. Hat keine Auswirkungen auf bestehende Konten. enable_update_checker_helper_forgejo = Prüft regelmäßig auf neue Forgejo-Versionen, indem ein DNS-TXT-Eintrag unter release.forgejo.org überprüft wird. -smtp_from_invalid = Die „Sende E-Mail Als“-Adresse ist ungültig +smtp_from_invalid = Die „Sende E-Mail als“-Adresse ist ungültig config_location_hint = Diese Konfigurationsoptionen werden gespeichert in: allow_only_external_registration = Registrierung nur mittels externer Dienste zulassen app_slogan = Instanz-Slogan -app_slogan_helper = Instanz-Slogan hier eingeben. Leer lassen zum deaktivieren. +app_slogan_helper = Instanz-Slogan hier eingeben. Leer lassen zum Deaktivieren. [home] uname_holder=Benutzername oder E-Mail-Adresse @@ -418,7 +418,7 @@ forgot_password_title=Passwort vergessen forgot_password=Passwort vergessen? sign_up_now=Noch kein Konto? Jetzt registrieren. sign_up_successful=Konto wurde erfolgreich erstellt. Willkommen! -confirmation_mail_sent_prompt=Eine neue Bestätigungs-E-Mail wurde an %s gesendet. Um den Registrierungsprozess abzuschließen, überprüf bitte deinen Posteingang und folg dem angegebenen Link innerhalb von: %s. Falls die E-Mail inkorrekt sein sollte, kannst du dich einloggen und anfragen, eine weitere Bestätigungs-E-Mail an eine andere Adresse zu senden. +confirmation_mail_sent_prompt=Eine neue Bestätigungs-E-Mail wurde an %s gesendet. Um den Registrierungsprozess abzuschließen, überprüfe bitte deinen Posteingang und folge dem angegebenen Link innerhalb von: %s. Falls die E-Mail inkorrekt sein sollte, kannst du dich einloggen und anfragen, eine weitere Bestätigungs-E-Mail an eine andere Adresse zu senden. must_change_password=Aktualisiere dein Passwort allow_password_change=Verlange vom Benutzer das Passwort zu ändern (empfohlen) reset_password_mail_sent_prompt=Eine Bestätigungs-E-Mail wurde an %s gesendet. Um den Kontowiederherstellungsprozess abzuschließen, überprüfe bitte deinen Posteingang und folge dem angegebenen Link innerhalb von %s. @@ -451,7 +451,7 @@ oauth_signup_tab=Neues Konto registrieren oauth_signup_title=Neues Konto fertigstellen oauth_signup_submit=Konto vervollständigen oauth_signin_tab=Mit einem existierenden Konto verbinden -oauth_signin_title=Anmelden um verbundenes Konto zu autorisieren +oauth_signin_title=Anmelden, um verbundenes Konto zu autorisieren oauth_signin_submit=Konto verbinden oauth.signin.error=Beim Verarbeiten der Autorisierungsanfrage ist ein Fehler aufgetreten. Wenn dieser Fehler weiterhin besteht, wende dich bitte an deinen Administrator. oauth.signin.error.access_denied=Die Autorisierungsanfrage wurde abgelehnt. @@ -463,7 +463,7 @@ openid_register_title=Neues Konto einrichten openid_register_desc=Die gewählte OpenID-URI ist unbekannt. Ordne sie hier einem neuen Account zu. openid_signin_desc=Gib deine OpenID-URI ein, zum Beispiel alice.openid.example.org oder https://openid.example.org/alice. disable_forgot_password_mail=Die Kontowiederherstellung ist deaktiviert, da keine E-Mail eingerichtet ist. Bitte kontaktiere den zuständigen Administrator. -disable_forgot_password_mail_admin=Die Kontowiederherstellung ist nur verfügbar, wenn eine E-Mail eingerichtet wurde. Bitte richte eine E-Mail Adresse ein, um die Kontowiederherstellung freizuschalten. +disable_forgot_password_mail_admin=Die Kontowiederherstellung ist nur verfügbar, wenn eine E-Mail eingerichtet wurde. Bitte richte eine E-Mail-Adresse ein, um die Kontowiederherstellung freizuschalten. email_domain_blacklisted=Du kannst dich nicht mit deiner E-Mail-Adresse registrieren. authorize_application=Anwendung autorisieren authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung autorisierst. @@ -530,8 +530,8 @@ issue.action.merge=@%[1]s hat #%[2]d in %[3]s zusammengeführt. issue.action.approve=@%[1]s hat diesen Pull-Request genehmigt. issue.action.reject=@%[1]s hat Änderungen auf diesem Pull-Request angefordert. issue.action.review=@%[1]s hat diesen Pull-Request kommentiert. -issue.action.review_dismissed=@%[1]s hat das letzte Review von %[2]s für diesen Pull-Request verworfen. -issue.action.ready_for_review=@%[1]s hat diesen Pull-Request zum Review freigegeben. +issue.action.review_dismissed=@%[1]s hat die letzte Sichtung von %[2]s für diesen Pull-Request verworfen. +issue.action.ready_for_review=@%[1]s hat diesen Pull-Request für die Sichtung freigegeben. issue.action.new=@%[1]s hat #%[2]d geöffnet. issue.in_tree_path=In %s: @@ -540,8 +540,8 @@ release.new.text=@%[1]s hat %[2]s in %[3]s released release.title=Titel: %s release.note=Anmerkung: release.downloads=Downloads: -release.download.zip=Quellcode (ZIP Datei) -release.download.targz=Quellcode (TAR.GZ Datei) +release.download.zip=Quellcode (ZIP) +release.download.targz=Quellcode (TAR.GZ) repo.transfer.subject_to=%s möchte „%s“ an %s übertragen repo.transfer.subject_to_you=%s möchte dir „%s“ übertragen @@ -562,7 +562,7 @@ password_change.subject = Dein Passwort wurde geändert password_change.text_1 = Das Passwort für deinen Account wurde soeben geändert. primary_mail_change.subject = Deine primäre E-Mail-Adresse wurde geändert totp_disabled.subject = TOTP wurde deaktiviert -totp_disabled.text_1 = TOTP (Time-based one-time password [Zeitbasiertes Einmalpasswort]) wurde auf deinem Account soeben deaktiviert. +totp_disabled.text_1 = TOTP (Time-based one-time password [zeitbasiertes Einmalpasswort]) wurde auf deinem Account soeben deaktiviert. totp_disabled.no_2fa = Es sind keine anderen 2FA-Methoden mehr konfiguriert, was bedeutet, dass es nicht mehr nötig ist, sich in deinen Account mit 2FA einzuloggen. removed_security_key.subject = Ein Sicherheitsschlüssel wurde entfernt removed_security_key.no_2fa = Es sind keine anderen 2FA-Methoden mehr konfiguriert, was bedeutet, dass es nicht mehr nötig ist, sich in deinen Account mit 2FA einzuloggen. @@ -572,7 +572,7 @@ reset_password.text_1 = Das Passwort für deinen Account wurde soeben geändert. primary_mail_change.text_1 = Die primäre E-Mail-Adresse deines Account wurde soeben zu %[1]s geändert. Das bedeutet, dass diese E-Mail-Adresse keine E-Mail-Benachrichtigungen für deinen Account erhalten wird. account_security_caution.text_2 = Wenn du das nicht warst, wurde dein Account kompromittiert. Bitte kontaktiere die Admins dieser Webseite. totp_enrolled.subject = Du hast TOTP als 2FA-Methode aktiviert -totp_enrolled.text_1.has_webauthn = Du hast gerade eben TOTP für deinen Account aktiviert. Das bedeutet, dass du in Zukunft für alle Logins in deinen Account TOTP als 2FA-Methode benutzen könntest, oder einen deiner Sicherheitsschlüssel. +totp_enrolled.text_1.has_webauthn = Du hast gerade eben TOTP für deinen Account aktiviert. Das bedeutet, dass du in Zukunft für alle Logins in deinen Account TOTP als 2FA-Methode oder einen deiner Sicherheitsschlüssel benutzen könntest. totp_enrolled.text_1.no_webauthn = Du hast gerade eben TOTP für deinen Account aktiviert. Das bedeutet, dass du in Zukunft für alle Logins in deinen Account TOTP als 2FA-Methode benutzen musst. [modal] @@ -661,8 +661,8 @@ organization_leave_success=Du hast die Organisation %s erfolgreich verlassen. invalid_ssh_key=Dein SSH-Key kann nicht überprüft werden: %s invalid_gpg_key=Dein GPG-Key kann nicht überprüft werden: %s invalid_ssh_principal=Ungültige Identität: %s -must_use_public_key=Der von dir bereitgestellte Key ist ein privater Key. Bitte lade deinen privaten Key nirgendwo hoch. Verwende stattdessen deinen öffentlichen Key. -unable_verify_ssh_key=Der SSH-Key kann nicht verifiziert werden, überprüfe ihn auf Fehler. +must_use_public_key=Der von dir bereitgestellte Schlüssel ist ein privater. Bitte lade deinen privaten Schlüssel nirgendwo hoch, sondern verwende stattdessen deinen öffentlichen. +unable_verify_ssh_key=Der SSH-Schlüssel kann nicht verifiziert werden, überprüfe ihn auf Fehler. auth_failed=Authentifizierung fehlgeschlagen: %v still_own_repo=Dein Konto besitzt ein oder mehrere Repositorys. Diese müssen erst gelöscht oder übertragen werden. @@ -700,7 +700,7 @@ watched=Beobachtete Repositorys code=Quelltext projects=Projekte overview=Übersicht -following_few=%d Folge ich +following_few=%d folge ich follow=Folgen unfollow=Nicht mehr folgen user_bio=Biografie @@ -722,7 +722,7 @@ follow_blocked_user = Du kannst diesen Benutzer nicht folgen, weil du ihn blocki block_user.detail_3 = Ihr werdet nicht mehr in der Lage sein, euch gegenseitig als Repository-Mitarbeiter hinzuzufügen. unblock = Nicht mehr blockieren followers_one = %d Follower -following_one = %d Folge ich +following_one = %d folge ich followers.title.few = Follower following.title.one = Folgt following.title.few = Folgt @@ -745,14 +745,14 @@ social=Soziale Konten applications=Anwendungen orgs=Organisationen repos=Repositorys -delete=Konto löschen +delete=Account löschen twofa=Zwei-Faktor-Authentifizierung (TOTP) account_link=Verknüpfte Benutzerkonten organization=Organisationen uid=UID webauthn=Hardware-Sicherheitsschlüssel -public_profile=Öffentliches Profil +public_profile=Öffentliches profil biography_placeholder=Erzähle anderen ein wenig über dich selbst! (Markdown wird unterstützt) location_placeholder=Teile deinen ungefähren Standort mit anderen profile_desc=Über dich @@ -774,7 +774,7 @@ cancel=Abbrechen language=Sprache ui=Theme hidden_comment_types=Ausgeblendete Kommentartypen -hidden_comment_types_description=Die hier markierten Kommentartypen werden nicht innerhalb der Issue-Seiten angezeigt. Die Markierung von „Label“ zum Beispiel entfernt alle Kommentare der Form „ hat