diff --git a/.deadcode-out b/.deadcode-out index 61c5bcb055..24facdf12e 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -13,13 +13,6 @@ forgejo.org/models IsErrSHANotFound IsErrMergeDivergingFastForwardOnly -forgejo.org/models/activities - GetActivityByID - NewFederatedUserActivity - CreateUserActivity - GetFollowingFeeds - FederatedUserActivity.loadActor - forgejo.org/models/auth WebAuthnCredentials @@ -27,15 +20,13 @@ forgejo.org/models/db TruncateBeans InTransaction DumpTables + GetTableNames forgejo.org/models/dbfs file.renameTo Create Rename -forgejo.org/models/forgefed - GetFederationHost - forgejo.org/models/forgejo/semver GetVersion SetVersionString @@ -61,17 +52,10 @@ forgejo.org/models/user IsErrExternalLoginUserAlreadyExist IsErrExternalLoginUserNotExist NewFederatedUser - NewFederatedUserFollower IsErrUserSettingIsNotExist GetUserAllSettings DeleteUserSetting GetFederatedUser - GetFederatedUserByUserID - UpdateFederatedUser - GetFollowersForUser - AddFollower - RemoveFollower - IsFollowingAp forgejo.org/modules/activitypub NewContext @@ -102,24 +86,14 @@ forgejo.org/modules/eventsource Event.String 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 @@ -231,7 +205,6 @@ forgejo.org/modules/util/filebuffer forgejo.org/modules/validation IsErrNotValid - ValidateIDExists forgejo.org/modules/web RouteMock @@ -248,6 +221,9 @@ forgejo.org/routers/web/org forgejo.org/services/context GetPrivateContext +forgejo.org/services/federation + FollowRemoteActor + forgejo.org/services/repository IsErrForkAlreadyExist diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 28fa9e4555..3f250e5682 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,7 @@ "ghcr.io/devcontainers/features/node:1": { "version": "22" }, - "ghcr.io/devcontainers/features/git-lfs:1.2.4": {}, + "ghcr.io/devcontainers/features/git-lfs:1.2.5": {}, "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} }, "customizations": { diff --git a/.forgejo/workflows-composite/install-minimum-git-version/action.yaml b/.forgejo/workflows-composite/install-minimum-git-version/action.yaml new file mode 100644 index 0000000000..d4e6e3f2a7 --- /dev/null +++ b/.forgejo/workflows-composite/install-minimum-git-version/action.yaml @@ -0,0 +1,22 @@ +# +# Install the minimal version of Git supported by Forgejo +# +runs: + using: "composite" + steps: + - name: install git and git-lfs + run: | + set -x + + export DEBIAN_FRONTEND=noninteractive + + apt-get update -qq + apt-get -q install -y -qq curl ca-certificates + + curl -sS -o /tmp/git-man.deb http://archive.ubuntu.com/ubuntu/pool/main/g/git/git-man_2.34.1-1ubuntu1_all.deb + curl -sS -o /tmp/git.deb https://archive.ubuntu.com/ubuntu/pool/main/g/git/git_2.34.1-1ubuntu1_amd64.deb + curl -sS -o /tmp/git-lfs.deb https://archive.ubuntu.com/ubuntu/pool/universe/g/git-lfs/git-lfs_3.0.2-1_amd64.deb + + apt-get -q install --allow-downgrades -y -qq /tmp/git-man.deb + apt-get -q install --allow-downgrades -y -qq /tmp/git.deb + apt-get -q install --allow-downgrades -y -qq /tmp/git-lfs.deb diff --git a/.forgejo/workflows/build-release-integration.yml b/.forgejo/workflows/build-release-integration.yml index 1af6d567dd..a525a39eeb 100644 --- a/.forgejo/workflows/build-release-integration.yml +++ b/.forgejo/workflows/build-release-integration.yml @@ -28,7 +28,7 @@ jobs: - uses: https://data.forgejo.org/actions/checkout@v4 - id: forgejo - uses: https://data.forgejo.org/actions/setup-forgejo@v2.0.4 + uses: https://data.forgejo.org/actions/setup-forgejo@v3.0.2 with: user: root password: admin1234 diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index 3ab63b0589..c0948781c1 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.5 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.4.1 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.5 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.4.1 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" @@ -201,7 +201,7 @@ jobs: - name: end-to-end tests if: ${{ secrets.TOKEN != '' && vars.ROLE == 'forgejo-integration' && vars.SKIP_END_TO_END != 'true' }} - uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0 + uses: https://data.forgejo.org/actions/cascading-pr@v2.2.1 with: origin-url: ${{ env.GITHUB_SERVER_URL }} origin-repo: ${{ github.repository }} diff --git a/.forgejo/workflows/cascade-setup-end-to-end.yml b/.forgejo/workflows/cascade-setup-end-to-end.yml index 7c8c56de13..9d613189a4 100644 --- a/.forgejo/workflows/cascade-setup-end-to-end.yml +++ b/.forgejo/workflows/cascade-setup-end-to-end.yml @@ -41,7 +41,7 @@ jobs: with: fetch-depth: '0' show-progress: 'false' - - uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0 + - uses: https://data.forgejo.org/actions/cascading-pr@v2.2.1 with: origin-url: ${{ env.GITHUB_SERVER_URL }} origin-repo: ${{ github.repository }} diff --git a/.forgejo/workflows/merge-requirements.yml b/.forgejo/workflows/merge-requirements.yml index 9aaf2af68d..9bed4b8797 100644 --- a/.forgejo/workflows/merge-requirements.yml +++ b/.forgejo/workflows/merge-requirements.yml @@ -1,4 +1,4 @@ -# Copyright 2024 The Forgejo Authors +# Copyright 2025 The Forgejo Authors # SPDX-License-Identifier: MIT name: requirements @@ -13,7 +13,8 @@ on: jobs: merge-conditions: - if: vars.ROLE == 'forgejo-coding' + if: > + vars.ROLE == 'forgejo-coding' && forge.event.pull_request.head.repo.full_name != 'forgejo-cascading-pr/forgejo' runs-on: docker container: image: 'data.forgejo.org/oci/node:22-bookworm' @@ -26,9 +27,9 @@ jobs: - name: Missing test label if: > !( - contains(toJSON(github.event.pull_request.labels), 'test/present') - || contains(toJSON(github.event.pull_request.labels), 'test/not-needed') - || contains(toJSON(github.event.pull_request.labels), 'test/manual') + contains(toJSON(forge.event.pull_request.labels), 'test/present') + || contains(toJSON(forge.event.pull_request.labels), 'test/not-needed') + || contains(toJSON(forge.event.pull_request.labels), 'test/manual') ) run: | echo "A team member must set the label to either 'present', 'not-needed' or 'manual'." @@ -36,8 +37,8 @@ jobs: - name: Missing manual test instructions if: > ( - contains(toJSON(github.event.pull_request.labels), 'test/manual') - && !contains(toJSON(github.event.pull_request.body), '# Test') + contains(toJSON(forge.event.pull_request.labels), 'test/manual') + && !contains(toJSON(forge.event.pull_request.body), '# Test') ) run: | echo "Manual test label is set. The PR description needs to contain test steps introduced by a heading like:" diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 3aec46fb03..91fa03ab8e 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -44,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.5 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.4.1 with: from-forgejo: ${{ vars.FORGEJO }} to-forgejo: ${{ vars.FORGEJO }} diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index 7f77098357..ff2afe7303 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -5,32 +5,34 @@ on: - cron: '@daily' env: - RNA_VERSION: v1.2.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_WORKDIR: /srv/rna + RNA_VERSION: v1.3.6 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: if: vars.ROLE == 'forgejo-coding' runs-on: docker container: - image: 'data.forgejo.org/oci/node:22-bookworm' + image: 'data.forgejo.org/oci/ci:1' steps: - uses: https://data.forgejo.org/actions/checkout@v4 - - uses: https://data.forgejo.org/actions/setup-go@v5 + - uses: https://data.forgejo.org/actions/cache@v4 with: - go-version-file: "go.mod" - cache: false + key: rna-${{ env.RNA_VERSION }} + path: ${{ env.RNA_WORKDIR }} - - name: apt install jq + - name: install release-notes-assistant run: | - export DEBIAN_FRONTEND=noninteractive - apt-get update -qq - apt-get -q install -y -qq jq + set -x + wget -O /usr/local/bin/rna https://code.forgejo.org/forgejo/release-notes-assistant/releases/download/${{ env.RNA_VERSION}}/release-notes-assistant + chmod +x /usr/local/bin/rna - name: update open milestones run: | set -x - curl -sS $GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/milestones?state=open | jq -r '.[] | .title' | while read forgejo version ; do + mkdir -p ${{ env.RNA_WORKDIR }} + curl -sS $FORGEJO_SERVER_URL/api/v1/repos/$FORGEJO_REPOSITORY/milestones?state=open | jq -r '.[] | .title' | while read forgejo version ; do milestone="$forgejo $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 + rna --workdir ${{ env.RNA_WORKDIR }} --config .release-notes-assistant.yaml --storage milestone --storage-location "$milestone" --forgejo-url $FORGEJO_SERVER_URL --repository $FORGEJO_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 cdcd2e6fe4..f47fc59422 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -8,7 +8,7 @@ on: - labeled env: - RNA_VERSION: v1.2.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.6 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index 5aa6c8cd98..7e6371ad87 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:41.1.4 + image: data.forgejo.org/renovate/renovate:41.76.0 steps: - name: Load renovate repo cache @@ -49,7 +49,7 @@ jobs: LOG_LEVEL: debug RENOVATE_BASE_DIR: ${{ github.workspace }}/.tmp RENOVATE_ENDPOINT: ${{ github.server_url }} - RENOVATE_PLATFORM: gitea + RENOVATE_PLATFORM: forgejo RENOVATE_REPOSITORY_CACHE: 'enabled' RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }} RENOVATE_GIT_AUTHOR: 'Renovate Bot ' diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml index 9e5cfb92ed..6822c45f39 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -1,7 +1,8 @@ # # 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. +# and send a notification via email to the contact email of the +# organization should they fail. # # For debug purposes: # @@ -22,6 +23,8 @@ on: - 'forgejo' - 'v*/forgejo' +enable-email-notifications: true + jobs: test-unit: # if: vars.ROLE == 'forgejo-coding' @@ -33,11 +36,8 @@ jobs: 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 + - name: install git 2.34.1 and git-lfs 3.0.2 + uses: ./.forgejo/workflows-composite/install-minimum-git-version - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-backend test-check' @@ -55,11 +55,8 @@ jobs: 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 + - name: install git 2.34.1 and git-lfs 3.0.2 + uses: ./.forgejo/workflows-composite/install-minimum-git-version - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-sqlite-migration test-sqlite' @@ -69,3 +66,34 @@ jobs: RACE_ENABLED: true TEST_TAGS: sqlite sqlite_unlock_notify USE_REPO_TEST_DIR: 1 + test-mariadb: +# if: vars.ROLE == 'forgejo-coding' + if: vars.ROLE == 'forgejo-integration' + runs-on: docker + name: ${{ format('test-mariadb (v{0})', matrix.version) }} + strategy: + matrix: + version: ['10.6', '11.8'] + container: + image: 'data.forgejo.org/oci/node:22-bookworm' + options: --tmpfs /tmp:exec,noatime + services: + mysql: + image: ${{ format('data.forgejo.org/oci/mariadb:{0}', matrix.version) }} + env: + MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes + MARIADB_DATABASE: testgitea + options: --tmpfs /var/lib/mysql:noatime + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/workflows-composite/setup-env + - name: install dependencies & git >= 2.42 + uses: ./.forgejo/workflows-composite/apt-install-from + with: + packages: git 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 diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index 7a93bb66a8..4c5e18a5e9 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -37,7 +37,11 @@ jobs: - run: make deps-frontend - run: make lint-frontend - run: make checks-frontend - - run: make test-frontend-coverage + - run: | + # Usage of `dayjs` can be impacted by local system timezone and can be sensitive to DST differences; since + # frontend tests are very short they're run twice with varying DST rules to reduce regression risk. + TZ=Europe/Berlin make test-frontend-coverage + TZ=America/Edmonton make test-frontend-coverage - run: make frontend - name: Install zstd for cache saving # works around https://github.com/actions/cache/issues/1169, because the diff --git a/.gitignore b/.gitignore index 744e24a09a..e1200ce4e8 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,8 @@ _testmain.go *coverage.out coverage.all +coverage.html +coverage.html.gz coverage/ cpu.out @@ -129,3 +131,4 @@ prime/ # Manpage /man +tests/integration/api_activitypub_person_inbox_useractivity_test.go diff --git a/.golangci.yml b/.golangci.yml index 532132838d..17d39c1456 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -42,6 +42,8 @@ linters: 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 + - pkg: github.com/go-git/go-git + desc: use forgejo.org/modules/git instead, see https://codeberg.org/forgejo/forgejo/pulls/4941 gocritic: disabled-checks: - ifElseChain @@ -79,6 +81,10 @@ linters: - name: unreachable-code - name: var-declaration - name: var-naming + arguments: + - [] + - [] + - - skip-package-name-checks: true - name: redefines-builtin-id disabled: true staticcheck: diff --git a/Makefile b/Makefile index e770f2a989..81b1cdfc1e 100644 --- a/Makefile +++ b/Makefile @@ -39,15 +39,15 @@ 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.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 +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.3.1 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # 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.34.0 # renovate: datasource=go +DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.36.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 +RENOVATE_NPM_PACKAGE ?= renovate@41.76.0 # 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: ... @@ -638,6 +638,7 @@ generate-ini-pgsql: -e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \ -e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \ -e 's|{{TEST_STORAGE_TYPE}}|$(or $(TEST_STORAGE_TYPE),minio)|g' \ + -e 's|{{TEST_S3_HOST}}|$(or $(TEST_S3_HOST),minio:9000)|g' \ tests/pgsql.ini.tmpl > tests/pgsql.ini .PHONY: test-pgsql @@ -940,6 +941,7 @@ fomantic: cd $(FOMANTIC_WORK_DIR) && npm install --no-save cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/ + rm -rf $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/themes/default/modules/dropdown.overrides $(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build # fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 32f7b8c264..c688045d1c 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,7 +4,7 @@ A minor or major Forgejo release is published every [three months](https://forge A [patch or minor release](https://semver.org/spec/v2.0.0.html) (e.g. upgrading from v7.0.0 to v7.0.1 or v7.1.0) does not require manual intervention. But [major releases](https://semver.org/spec/v2.0.0.html#spec-item-8) where the first version number changes (e.g. upgrading from v1.21 to v7.0) contain breaking changes and the release notes explain how to deal with them. -The release notes of each release [are available in the release-notes-published directory of this repository](release-notes-published), starting with [Forgejo 7.0.7](release-notes-published/7.0.7.md) and [Forgejo 8.0.1](release-notes-published/8.0.1.md). +The release notes of each release [are available in the release-notes-published directory of this repository](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/release-notes-published), starting with [Forgejo 7.0.7](release-notes-published/7.0.7.md) and [Forgejo 8.0.1](release-notes-published/8.0.1.md). ## 9.0.2 diff --git a/assets/go-licenses.json b/assets/go-licenses.json index fb6c201a5e..11fd237360 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -34,6 +34,11 @@ "path": "code.forgejo.org/forgejo/reply/LICENSE", "licenseText": "MIT License\n\nCopyright (c) The Forgejo Authors\nCopyright (c) Discourse\nCopyright (c) Claudemiro\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": "code.forgejo.org/forgejo/runner/v9/act", + "path": "code.forgejo.org/forgejo/runner/v9/act/LICENSE", + "licenseText": "Copyright (c) 2023-2025 The Forgejo Authors\nCopyright (c) 2022 The Gitea Authors\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/go-chi/binding", "path": "code.forgejo.org/go-chi/binding/LICENSE", @@ -114,6 +119,11 @@ "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" }, + { + "name": "github.com/STARRY-S/zip", + "path": "github.com/STARRY-S/zip/LICENSE", + "licenseText": "BSD 3-Clause License\n\nCopyright (c) 2023, Starry\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n2. 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\n3. Neither the name of the copyright holder 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 \"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/SaveTheRbtz/zstd-seekable-format-go/pkg", "path": "github.com/SaveTheRbtz/zstd-seekable-format-go/pkg/LICENSE", @@ -244,6 +254,26 @@ "path": "github.com/blevesearch/zapx/v16/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." }, + { + "name": "github.com/bmatcuk/doublestar/v4", + "path": "github.com/bmatcuk/doublestar/v4/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Bob Matcuk\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\n" + }, + { + "name": "github.com/bodgit/plumbing", + "path": "github.com/bodgit/plumbing/LICENSE", + "licenseText": "BSD 3-Clause License\n\nCopyright (c) 2019, Matt Dainty\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* Neither the name of the copyright holder 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 \"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\n" + }, + { + "name": "github.com/bodgit/sevenzip", + "path": "github.com/bodgit/sevenzip/LICENSE", + "licenseText": "BSD 3-Clause License\n\nCopyright (c) 2020, Matt Dainty\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* Neither the name of the copyright holder 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 \"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/bodgit/windows", + "path": "github.com/bodgit/windows/LICENSE", + "licenseText": "BSD 3-Clause License\n\nCopyright (c) 2020, Matt Dainty\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* Neither the name of the copyright holder 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 \"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\n" + }, { "name": "github.com/boombuler/barcode", "path": "github.com/boombuler/barcode/LICENSE", @@ -564,11 +594,21 @@ "path": "github.com/gorilla/sessions/LICENSE", "licenseText": "Copyright (c) 2024 The Gorilla 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\t * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\t * 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\t * 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/hashicorp/errwrap", + "path": "github.com/hashicorp/errwrap/LICENSE", + "licenseText": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. “Contributor”\n\n means each individual or legal entity that creates, contributes to the\n creation of, or owns Covered Software.\n\n1.2. “Contributor Version”\n\n means the combination of the Contributions of others (if any) used by a\n Contributor and that particular Contributor’s Contribution.\n\n1.3. “Contribution”\n\n means Covered Software of a particular Contributor.\n\n1.4. “Covered Software”\n\n means Source Code Form to which the initial Contributor has attached the\n notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions\n thereof.\n\n1.5. “Incompatible With Secondary Licenses”\n means\n\n a. that the initial Contributor has attached the notice described in\n Exhibit B to the Covered Software; or\n\n b. that the Covered Software was made available under the terms of version\n 1.1 or earlier of the License, but not also under the terms of a\n Secondary License.\n\n1.6. “Executable Form”\n\n means any form of the work other than Source Code Form.\n\n1.7. “Larger Work”\n\n means a work that combines Covered Software with other material, in a separate\n file or files, that is not Covered Software.\n\n1.8. “License”\n\n means this document.\n\n1.9. “Licensable”\n\n means having the right to grant, to the maximum extent possible, whether at the\n time of the initial grant or subsequently, any and all of the rights conveyed by\n this License.\n\n1.10. “Modifications”\n\n means any of the following:\n\n a. any file in Source Code Form that results from an addition to, deletion\n from, or modification of the contents of Covered Software; or\n\n b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. “Patent Claims” of a Contributor\n\n means any patent claim(s), including without limitation, method, process,\n and apparatus claims, in any patent Licensable by such Contributor that\n would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of\n either its Contributions or its Contributor Version.\n\n1.12. “Secondary License”\n\n means either the GNU General Public License, Version 2.0, the GNU Lesser\n General Public License, Version 2.1, the GNU Affero General Public\n License, Version 3.0, or any later versions of those licenses.\n\n1.13. “Source Code Form”\n\n means the form of the work preferred for making modifications.\n\n1.14. “You” (or “Your”)\n\n means an individual or a legal entity exercising rights under this\n License. For legal entities, “You” includes any entity that controls, is\n controlled by, or is under common control with You. For purposes of this\n definition, “control” means (a) the power, direct or indirect, to cause\n the direction or management of such entity, whether by contract or\n otherwise, or (b) ownership of more than fifty percent (50%) of the\n outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n a. under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or as\n part of a Larger Work; and\n\n b. under Patent Claims of such Contributor to make, use, sell, offer for\n sale, have made, import, and otherwise transfer either its Contributions\n or its Contributor Version.\n\n2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become\n effective for each Contribution on the date the Contributor first distributes\n such Contribution.\n\n2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this\n License. No additional rights or licenses will be implied from the distribution\n or licensing of Covered Software under this License. Notwithstanding Section\n 2.1(b) above, no patent license is granted by a Contributor:\n\n a. for any code that a Contributor has removed from Covered Software; or\n\n b. for infringements caused by: (i) Your and any other third party’s\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n c. under Patent Claims infringed by Covered Software in the absence of its\n Contributions.\n\n This License does not grant any rights in the trademarks, service marks, or\n logos of any Contributor (except as may be necessary to comply with the\n notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to\n distribute the Covered Software under a subsequent version of this License\n (see Section 10.2) or under the terms of a Secondary License (if permitted\n under the terms of Section 3.3).\n\n2.5. Representation\n\n Each Contributor represents that the Contributor believes its Contributions\n are its original creation(s) or it has sufficient rights to grant the\n rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\n This License is not intended to limit any rights You have under applicable\n copyright doctrines of fair use, fair dealing, or other equivalents.\n\n2.7. Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in\n Section 2.1.\n\n\n3. Responsibilities\n\n3.1. Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any\n Modifications that You create or to which You contribute, must be under the\n terms of this License. You must inform recipients that the Source Code Form\n of the Covered Software is governed by the terms of this License, and how\n they can obtain a copy of this License. You may not attempt to alter or\n restrict the recipients’ rights in the Source Code Form.\n\n3.2. Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n a. such Covered Software must also be made available in Source Code Form,\n as described in Section 3.1, and You must inform recipients of the\n Executable Form how they can obtain a copy of such Source Code Form by\n reasonable means in a timely manner, at a charge no more than the cost\n of distribution to the recipient; and\n\n b. You may distribute such Executable Form under the terms of this License,\n or sublicense it under different terms, provided that the license for\n the Executable Form does not attempt to limit or alter the recipients’\n rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice,\n provided that You also comply with the requirements of this License for the\n Covered Software. If the Larger Work is a combination of Covered Software\n with a work governed by one or more Secondary Licenses, and the Covered\n Software is not Incompatible With Secondary Licenses, this License permits\n You to additionally distribute such Covered Software under the terms of\n such Secondary License(s), so that the recipient of the Larger Work may, at\n their option, further distribute the Covered Software under the terms of\n either this License or such Secondary License(s).\n\n3.4. Notices\n\n You may not remove or alter the substance of any license notices (including\n copyright notices, patent notices, disclaimers of warranty, or limitations\n of liability) contained within the Source Code Form of the Covered\n Software, except that You may alter any license notices to the extent\n required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support,\n indemnity or liability obligations to one or more recipients of Covered\n Software. However, You may do so only on Your own behalf, and not on behalf\n of any Contributor. You must make it absolutely clear that any such\n warranty, support, indemnity, or liability obligation is offered by You\n alone, and You hereby agree to indemnify every Contributor for any\n liability incurred by such Contributor as a result of warranty, support,\n indemnity or liability terms You offer. You may include additional\n disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n\n If it is impossible for You to comply with any of the terms of this License\n with respect to some or all of the Covered Software due to statute, judicial\n order, or regulation then You must: (a) comply with the terms of this License\n to the maximum extent possible; and (b) describe the limitations and the code\n they affect. Such description must be placed in a text file included with all\n distributions of the Covered Software under this License. Except to the\n extent prohibited by statute or regulation, such description must be\n sufficiently detailed for a recipient of ordinary skill to be able to\n understand it.\n\n5. Termination\n\n5.1. The rights granted under this License will terminate automatically if You\n fail to comply with any of its terms. However, if You become compliant,\n then the rights granted under this License from a particular Contributor\n are reinstated (a) provisionally, unless and until such Contributor\n explicitly and finally terminates Your grants, and (b) on an ongoing basis,\n if such Contributor fails to notify You of the non-compliance by some\n reasonable means prior to 60 days after You have come back into compliance.\n Moreover, Your grants from a particular Contributor are reinstated on an\n ongoing basis if such Contributor notifies You of the non-compliance by\n some reasonable means, this is the first time You have received notice of\n non-compliance with this License from such Contributor, and You become\n compliant prior to 30 days after Your receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\n infringement claim (excluding declaratory judgment actions, counter-claims,\n and cross-claims) alleging that a Contributor Version directly or\n indirectly infringes any patent, then the rights granted to You by any and\n all Contributors for the Covered Software under Section 2.1 of this License\n shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user\n license agreements (excluding distributors and resellers) which have been\n validly granted by You or Your distributors under this License prior to\n termination shall survive termination.\n\n6. Disclaimer of Warranty\n\n Covered Software is provided under this License on an “as is” basis, without\n warranty of any kind, either expressed, implied, or statutory, including,\n without limitation, warranties that the Covered Software is free of defects,\n merchantable, fit for a particular purpose or non-infringing. The entire\n risk as to the quality and performance of the Covered Software is with You.\n Should any Covered Software prove defective in any respect, You (not any\n Contributor) assume the cost of any necessary servicing, repair, or\n correction. This disclaimer of warranty constitutes an essential part of this\n License. No use of any Covered Software is authorized under this License\n except under this disclaimer.\n\n7. Limitation of Liability\n\n Under no circumstances and under no legal theory, whether tort (including\n negligence), contract, or otherwise, shall any Contributor, or anyone who\n distributes Covered Software as permitted above, be liable to You for any\n direct, indirect, special, incidental, or consequential damages of any\n character including, without limitation, damages for lost profits, loss of\n goodwill, work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses, even if such party shall have been\n informed of the possibility of such damages. This limitation of liability\n shall not apply to liability for death or personal injury resulting from such\n party’s negligence to the extent applicable law prohibits such limitation.\n Some jurisdictions do not allow the exclusion or limitation of incidental or\n consequential damages, so this exclusion and limitation may not apply to You.\n\n8. Litigation\n\n Any litigation relating to this License may be brought only in the courts of\n a jurisdiction where the defendant maintains its principal place of business\n and such litigation shall be governed by laws of that jurisdiction, without\n reference to its conflict-of-law provisions. Nothing in this Section shall\n prevent a party’s ability to bring cross-claims or counter-claims.\n\n9. Miscellaneous\n\n This License represents the complete agreement concerning the subject matter\n hereof. If any provision of this License is held to be unenforceable, such\n provision shall be reformed only to the extent necessary to make it\n enforceable. Any law or regulation which provides that the language of a\n contract shall be construed against the drafter shall not be used to construe\n this License against a Contributor.\n\n\n10. Versions of the License\n\n10.1. New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section\n 10.3, no one other than the license steward has the right to modify or\n publish new versions of this License. Each version will be given a\n distinguishing version number.\n\n10.2. Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version of\n the License under which You originally received the Covered Software, or\n under the terms of any subsequent version published by the license\n steward.\n\n10.3. Modified Versions\n\n If you create software not governed by this License, and you want to\n create a new license for such software, you may create and use a modified\n version of this License if you rename the license and remove any\n references to the name of the license steward (except to note that such\n modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses\n If You choose to distribute Source Code Form that is Incompatible With\n Secondary Licenses under the terms of this version of the License, the\n notice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n\n This Source Code Form is subject to the\n terms of the Mozilla Public License, v.\n 2.0. If a copy of the MPL was not\n distributed with this file, You can\n obtain one at\n http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file, then\nYou may include the notice in a location (such as a LICENSE file in a relevant\ndirectory) where a recipient would be likely to look for such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - “Incompatible With Secondary Licenses” Notice\n\n This Source Code Form is “Incompatible\n With Secondary Licenses”, as defined by\n the Mozilla Public License, v. 2.0.\n\n" + }, { "name": "github.com/hashicorp/go-cleanhttp", "path": "github.com/hashicorp/go-cleanhttp/LICENSE", "licenseText": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. \"Contributor\"\n\n means each individual or legal entity that creates, contributes to the\n creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n\n means the combination of the Contributions of others (if any) used by a\n Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n\n means Source Code Form to which the initial Contributor has attached the\n notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions\n thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n a. that the initial Contributor has attached the notice described in\n Exhibit B to the Covered Software; or\n\n b. that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the terms of\n a Secondary License.\n\n1.6. \"Executable Form\"\n\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n\n means a work that combines Covered Software with other material, in a\n separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n\n means this document.\n\n1.9. \"Licensable\"\n\n means having the right to grant, to the maximum extent possible, whether\n at the time of the initial grant or subsequently, any and all of the\n rights conveyed by this License.\n\n1.10. \"Modifications\"\n\n means any of the following:\n\n a. any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered Software; or\n\n b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. \"Patent Claims\" of a Contributor\n\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the License,\n by the making, using, selling, offering for sale, having made, import,\n or transfer of either its Contributions or its Contributor Version.\n\n1.12. \"Secondary License\"\n\n means either the GNU General Public License, Version 2.0, the GNU Lesser\n General Public License, Version 2.1, the GNU Affero General Public\n License, Version 3.0, or any later versions of those licenses.\n\n1.13. \"Source Code Form\"\n\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that controls, is\n controlled by, or is under common control with You. For purposes of this\n definition, \"control\" means (a) the power, direct or indirect, to cause\n the direction or management of such entity, whether by contract or\n otherwise, or (b) ownership of more than fifty percent (50%) of the\n outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n a. under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n b. under Patent Claims of such Contributor to make, use, sell, offer for\n sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution\n become effective for each Contribution on the date the Contributor first\n distributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under\n this License. No additional rights or licenses will be implied from the\n distribution or licensing of Covered Software under this License.\n Notwithstanding Section 2.1(b) above, no patent license is granted by a\n Contributor:\n\n a. for any code that a Contributor has removed from Covered Software; or\n\n b. for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n c. under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\n This License does not grant any rights in the trademarks, service marks,\n or logos of any Contributor (except as may be necessary to comply with\n the notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to\n distribute the Covered Software under a subsequent version of this\n License (see Section 10.2) or under the terms of a Secondary License (if\n permitted under the terms of Section 3.3).\n\n2.5. Representation\n\n Each Contributor represents that the Contributor believes its\n Contributions are its original creation(s) or it has sufficient rights to\n grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\n This License is not intended to limit any rights You have under\n applicable copyright doctrines of fair use, fair dealing, or other\n equivalents.\n\n2.7. Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in\n Section 2.1.\n\n\n3. Responsibilities\n\n3.1. Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any\n Modifications that You create or to which You contribute, must be under\n the terms of this License. You must inform recipients that the Source\n Code Form of the Covered Software is governed by the terms of this\n License, and how they can obtain a copy of this License. You may not\n attempt to alter or restrict the recipients' rights in the Source Code\n Form.\n\n3.2. Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n a. such Covered Software must also be made available in Source Code Form,\n as described in Section 3.1, and You must inform recipients of the\n Executable Form how they can obtain a copy of such Source Code Form by\n reasonable means in a timely manner, at a charge no more than the cost\n of distribution to the recipient; and\n\n b. You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter the\n recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice,\n provided that You also comply with the requirements of this License for\n the Covered Software. If the Larger Work is a combination of Covered\n Software with a work governed by one or more Secondary Licenses, and the\n Covered Software is not Incompatible With Secondary Licenses, this\n License permits You to additionally distribute such Covered Software\n under the terms of such Secondary License(s), so that the recipient of\n the Larger Work may, at their option, further distribute the Covered\n Software under the terms of either this License or such Secondary\n License(s).\n\n3.4. Notices\n\n You may not remove or alter the substance of any license notices\n (including copyright notices, patent notices, disclaimers of warranty, or\n limitations of liability) contained within the Source Code Form of the\n Covered Software, except that You may alter any license notices to the\n extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support,\n indemnity or liability obligations to one or more recipients of Covered\n Software. However, You may do so only on Your own behalf, and not on\n behalf of any Contributor. You must make it absolutely clear that any\n such warranty, support, indemnity, or liability obligation is offered by\n You alone, and You hereby agree to indemnify every Contributor for any\n liability incurred by such Contributor as a result of warranty, support,\n indemnity or liability terms You offer. You may include additional\n disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n\n If it is impossible for You to comply with any of the terms of this License\n with respect to some or all of the Covered Software due to statute,\n judicial order, or regulation then You must: (a) comply with the terms of\n this License to the maximum extent possible; and (b) describe the\n limitations and the code they affect. Such description must be placed in a\n text file included with all distributions of the Covered Software under\n this License. Except to the extent prohibited by statute or regulation,\n such description must be sufficiently detailed for a recipient of ordinary\n skill to be able to understand it.\n\n5. Termination\n\n5.1. The rights granted under this License will terminate automatically if You\n fail to comply with any of its terms. However, if You become compliant,\n then the rights granted under this License from a particular Contributor\n are reinstated (a) provisionally, unless and until such Contributor\n explicitly and finally terminates Your grants, and (b) on an ongoing\n basis, if such Contributor fails to notify You of the non-compliance by\n some reasonable means prior to 60 days after You have come back into\n compliance. Moreover, Your grants from a particular Contributor are\n reinstated on an ongoing basis if such Contributor notifies You of the\n non-compliance by some reasonable means, this is the first time You have\n received notice of non-compliance with this License from such\n Contributor, and You become compliant prior to 30 days after Your receipt\n of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\n infringement claim (excluding declaratory judgment actions,\n counter-claims, and cross-claims) alleging that a Contributor Version\n directly or indirectly infringes any patent, then the rights granted to\n You by any and all Contributors for the Covered Software under Section\n 2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user\n license agreements (excluding distributors and resellers) which have been\n validly granted by You or Your distributors under this License prior to\n termination shall survive termination.\n\n6. Disclaimer of Warranty\n\n Covered Software is provided under this License on an \"as is\" basis,\n without warranty of any kind, either expressed, implied, or statutory,\n including, without limitation, warranties that the Covered Software is free\n of defects, merchantable, fit for a particular purpose or non-infringing.\n The entire risk as to the quality and performance of the Covered Software\n is with You. Should any Covered Software prove defective in any respect,\n You (not any Contributor) assume the cost of any necessary servicing,\n repair, or correction. This disclaimer of warranty constitutes an essential\n part of this License. No use of any Covered Software is authorized under\n this License except under this disclaimer.\n\n7. Limitation of Liability\n\n Under no circumstances and under no legal theory, whether tort (including\n negligence), contract, or otherwise, shall any Contributor, or anyone who\n distributes Covered Software as permitted above, be liable to You for any\n direct, indirect, special, incidental, or consequential damages of any\n character including, without limitation, damages for lost profits, loss of\n goodwill, work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses, even if such party shall have been\n informed of the possibility of such damages. This limitation of liability\n shall not apply to liability for death or personal injury resulting from\n such party's negligence to the extent applicable law prohibits such\n limitation. Some jurisdictions do not allow the exclusion or limitation of\n incidental or consequential damages, so this exclusion and limitation may\n not apply to You.\n\n8. Litigation\n\n Any litigation relating to this License may be brought only in the courts\n of a jurisdiction where the defendant maintains its principal place of\n business and such litigation shall be governed by laws of that\n jurisdiction, without reference to its conflict-of-law provisions. Nothing\n in this Section shall prevent a party's ability to bring cross-claims or\n counter-claims.\n\n9. Miscellaneous\n\n This License represents the complete agreement concerning the subject\n matter hereof. If any provision of this License is held to be\n unenforceable, such provision shall be reformed only to the extent\n necessary to make it enforceable. Any law or regulation which provides that\n the language of a contract shall be construed against the drafter shall not\n be used to construe this License against a Contributor.\n\n\n10. Versions of the License\n\n10.1. New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section\n 10.3, no one other than the license steward has the right to modify or\n publish new versions of this License. Each version will be given a\n distinguishing version number.\n\n10.2. Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version\n of the License under which You originally received the Covered Software,\n or under the terms of any subsequent version published by the license\n steward.\n\n10.3. Modified Versions\n\n If you create software not governed by this License, and you want to\n create a new license for such software, you may create and use a\n modified version of this License if you rename the license and remove\n any references to the name of the license steward (except to note that\n such modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\n Licenses If You choose to distribute Source Code Form that is\n Incompatible With Secondary Licenses under the terms of this version of\n the License, the notice described in Exhibit B of this License must be\n attached.\n\nExhibit A - Source Code Form License Notice\n\n This Source Code Form is subject to the\n terms of the Mozilla Public License, v.\n 2.0. If a copy of the MPL was not\n distributed with this file, You can\n obtain one at\n http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file,\nthen You may include the notice in a location (such as a LICENSE file in a\nrelevant directory) where a recipient would be likely to look for such a\nnotice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\n This Source Code Form is \"Incompatible\n With Secondary Licenses\", as defined by\n the Mozilla Public License, v. 2.0.\n\n" }, + { + "name": "github.com/hashicorp/go-multierror", + "path": "github.com/hashicorp/go-multierror/LICENSE", + "licenseText": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. “Contributor”\n\n means each individual or legal entity that creates, contributes to the\n creation of, or owns Covered Software.\n\n1.2. “Contributor Version”\n\n means the combination of the Contributions of others (if any) used by a\n Contributor and that particular Contributor’s Contribution.\n\n1.3. “Contribution”\n\n means Covered Software of a particular Contributor.\n\n1.4. “Covered Software”\n\n means Source Code Form to which the initial Contributor has attached the\n notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions\n thereof.\n\n1.5. “Incompatible With Secondary Licenses”\n means\n\n a. that the initial Contributor has attached the notice described in\n Exhibit B to the Covered Software; or\n\n b. that the Covered Software was made available under the terms of version\n 1.1 or earlier of the License, but not also under the terms of a\n Secondary License.\n\n1.6. “Executable Form”\n\n means any form of the work other than Source Code Form.\n\n1.7. “Larger Work”\n\n means a work that combines Covered Software with other material, in a separate\n file or files, that is not Covered Software.\n\n1.8. “License”\n\n means this document.\n\n1.9. “Licensable”\n\n means having the right to grant, to the maximum extent possible, whether at the\n time of the initial grant or subsequently, any and all of the rights conveyed by\n this License.\n\n1.10. “Modifications”\n\n means any of the following:\n\n a. any file in Source Code Form that results from an addition to, deletion\n from, or modification of the contents of Covered Software; or\n\n b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. “Patent Claims” of a Contributor\n\n means any patent claim(s), including without limitation, method, process,\n and apparatus claims, in any patent Licensable by such Contributor that\n would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of\n either its Contributions or its Contributor Version.\n\n1.12. “Secondary License”\n\n means either the GNU General Public License, Version 2.0, the GNU Lesser\n General Public License, Version 2.1, the GNU Affero General Public\n License, Version 3.0, or any later versions of those licenses.\n\n1.13. “Source Code Form”\n\n means the form of the work preferred for making modifications.\n\n1.14. “You” (or “Your”)\n\n means an individual or a legal entity exercising rights under this\n License. For legal entities, “You” includes any entity that controls, is\n controlled by, or is under common control with You. For purposes of this\n definition, “control” means (a) the power, direct or indirect, to cause\n the direction or management of such entity, whether by contract or\n otherwise, or (b) ownership of more than fifty percent (50%) of the\n outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n a. under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or as\n part of a Larger Work; and\n\n b. under Patent Claims of such Contributor to make, use, sell, offer for\n sale, have made, import, and otherwise transfer either its Contributions\n or its Contributor Version.\n\n2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become\n effective for each Contribution on the date the Contributor first distributes\n such Contribution.\n\n2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this\n License. No additional rights or licenses will be implied from the distribution\n or licensing of Covered Software under this License. Notwithstanding Section\n 2.1(b) above, no patent license is granted by a Contributor:\n\n a. for any code that a Contributor has removed from Covered Software; or\n\n b. for infringements caused by: (i) Your and any other third party’s\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n c. under Patent Claims infringed by Covered Software in the absence of its\n Contributions.\n\n This License does not grant any rights in the trademarks, service marks, or\n logos of any Contributor (except as may be necessary to comply with the\n notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to\n distribute the Covered Software under a subsequent version of this License\n (see Section 10.2) or under the terms of a Secondary License (if permitted\n under the terms of Section 3.3).\n\n2.5. Representation\n\n Each Contributor represents that the Contributor believes its Contributions\n are its original creation(s) or it has sufficient rights to grant the\n rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\n This License is not intended to limit any rights You have under applicable\n copyright doctrines of fair use, fair dealing, or other equivalents.\n\n2.7. Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in\n Section 2.1.\n\n\n3. Responsibilities\n\n3.1. Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any\n Modifications that You create or to which You contribute, must be under the\n terms of this License. You must inform recipients that the Source Code Form\n of the Covered Software is governed by the terms of this License, and how\n they can obtain a copy of this License. You may not attempt to alter or\n restrict the recipients’ rights in the Source Code Form.\n\n3.2. Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n a. such Covered Software must also be made available in Source Code Form,\n as described in Section 3.1, and You must inform recipients of the\n Executable Form how they can obtain a copy of such Source Code Form by\n reasonable means in a timely manner, at a charge no more than the cost\n of distribution to the recipient; and\n\n b. You may distribute such Executable Form under the terms of this License,\n or sublicense it under different terms, provided that the license for\n the Executable Form does not attempt to limit or alter the recipients’\n rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice,\n provided that You also comply with the requirements of this License for the\n Covered Software. If the Larger Work is a combination of Covered Software\n with a work governed by one or more Secondary Licenses, and the Covered\n Software is not Incompatible With Secondary Licenses, this License permits\n You to additionally distribute such Covered Software under the terms of\n such Secondary License(s), so that the recipient of the Larger Work may, at\n their option, further distribute the Covered Software under the terms of\n either this License or such Secondary License(s).\n\n3.4. Notices\n\n You may not remove or alter the substance of any license notices (including\n copyright notices, patent notices, disclaimers of warranty, or limitations\n of liability) contained within the Source Code Form of the Covered\n Software, except that You may alter any license notices to the extent\n required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support,\n indemnity or liability obligations to one or more recipients of Covered\n Software. However, You may do so only on Your own behalf, and not on behalf\n of any Contributor. You must make it absolutely clear that any such\n warranty, support, indemnity, or liability obligation is offered by You\n alone, and You hereby agree to indemnify every Contributor for any\n liability incurred by such Contributor as a result of warranty, support,\n indemnity or liability terms You offer. You may include additional\n disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n\n If it is impossible for You to comply with any of the terms of this License\n with respect to some or all of the Covered Software due to statute, judicial\n order, or regulation then You must: (a) comply with the terms of this License\n to the maximum extent possible; and (b) describe the limitations and the code\n they affect. Such description must be placed in a text file included with all\n distributions of the Covered Software under this License. Except to the\n extent prohibited by statute or regulation, such description must be\n sufficiently detailed for a recipient of ordinary skill to be able to\n understand it.\n\n5. Termination\n\n5.1. The rights granted under this License will terminate automatically if You\n fail to comply with any of its terms. However, if You become compliant,\n then the rights granted under this License from a particular Contributor\n are reinstated (a) provisionally, unless and until such Contributor\n explicitly and finally terminates Your grants, and (b) on an ongoing basis,\n if such Contributor fails to notify You of the non-compliance by some\n reasonable means prior to 60 days after You have come back into compliance.\n Moreover, Your grants from a particular Contributor are reinstated on an\n ongoing basis if such Contributor notifies You of the non-compliance by\n some reasonable means, this is the first time You have received notice of\n non-compliance with this License from such Contributor, and You become\n compliant prior to 30 days after Your receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\n infringement claim (excluding declaratory judgment actions, counter-claims,\n and cross-claims) alleging that a Contributor Version directly or\n indirectly infringes any patent, then the rights granted to You by any and\n all Contributors for the Covered Software under Section 2.1 of this License\n shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user\n license agreements (excluding distributors and resellers) which have been\n validly granted by You or Your distributors under this License prior to\n termination shall survive termination.\n\n6. Disclaimer of Warranty\n\n Covered Software is provided under this License on an “as is” basis, without\n warranty of any kind, either expressed, implied, or statutory, including,\n without limitation, warranties that the Covered Software is free of defects,\n merchantable, fit for a particular purpose or non-infringing. The entire\n risk as to the quality and performance of the Covered Software is with You.\n Should any Covered Software prove defective in any respect, You (not any\n Contributor) assume the cost of any necessary servicing, repair, or\n correction. This disclaimer of warranty constitutes an essential part of this\n License. No use of any Covered Software is authorized under this License\n except under this disclaimer.\n\n7. Limitation of Liability\n\n Under no circumstances and under no legal theory, whether tort (including\n negligence), contract, or otherwise, shall any Contributor, or anyone who\n distributes Covered Software as permitted above, be liable to You for any\n direct, indirect, special, incidental, or consequential damages of any\n character including, without limitation, damages for lost profits, loss of\n goodwill, work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses, even if such party shall have been\n informed of the possibility of such damages. This limitation of liability\n shall not apply to liability for death or personal injury resulting from such\n party’s negligence to the extent applicable law prohibits such limitation.\n Some jurisdictions do not allow the exclusion or limitation of incidental or\n consequential damages, so this exclusion and limitation may not apply to You.\n\n8. Litigation\n\n Any litigation relating to this License may be brought only in the courts of\n a jurisdiction where the defendant maintains its principal place of business\n and such litigation shall be governed by laws of that jurisdiction, without\n reference to its conflict-of-law provisions. Nothing in this Section shall\n prevent a party’s ability to bring cross-claims or counter-claims.\n\n9. Miscellaneous\n\n This License represents the complete agreement concerning the subject matter\n hereof. If any provision of this License is held to be unenforceable, such\n provision shall be reformed only to the extent necessary to make it\n enforceable. Any law or regulation which provides that the language of a\n contract shall be construed against the drafter shall not be used to construe\n this License against a Contributor.\n\n\n10. Versions of the License\n\n10.1. New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section\n 10.3, no one other than the license steward has the right to modify or\n publish new versions of this License. Each version will be given a\n distinguishing version number.\n\n10.2. Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version of\n the License under which You originally received the Covered Software, or\n under the terms of any subsequent version published by the license\n steward.\n\n10.3. Modified Versions\n\n If you create software not governed by this License, and you want to\n create a new license for such software, you may create and use a modified\n version of this License if you rename the license and remove any\n references to the name of the license steward (except to note that such\n modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses\n If You choose to distribute Source Code Form that is Incompatible With\n Secondary Licenses under the terms of this version of the License, the\n notice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n\n This Source Code Form is subject to the\n terms of the Mozilla Public License, v.\n 2.0. If a copy of the MPL was not\n distributed with this file, You can\n obtain one at\n http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file, then\nYou may include the notice in a location (such as a LICENSE file in a relevant\ndirectory) where a recipient would be likely to look for such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - “Incompatible With Secondary Licenses” Notice\n\n This Source Code Form is “Incompatible\n With Secondary Licenses”, as defined by\n the Mozilla Public License, v. 2.0.\n" + }, { "name": "github.com/hashicorp/go-retryablehttp", "path": "github.com/hashicorp/go-retryablehttp/LICENSE", @@ -595,8 +635,8 @@ "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Huan Du\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\n" }, { - "name": "github.com/jaytaylor/html2text", - "path": "github.com/jaytaylor/html2text/LICENSE", + "name": "github.com/inbucket/html2text", + "path": "github.com/inbucket/html2text/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Jay Taylor\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\n" }, { @@ -704,6 +744,11 @@ "path": "github.com/mattn/go-runewidth/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Yasuhiro Matsumoto\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/mattn/go-shellwords", + "path": "github.com/mattn/go-shellwords/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2017 Yasuhiro Matsumoto\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/meilisearch/meilisearch-go", "path": "github.com/meilisearch/meilisearch-go/LICENSE", @@ -715,8 +760,8 @@ "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" }, { - "name": "github.com/mholt/archiver/v3", - "path": "github.com/mholt/archiver/v3/LICENSE", + "name": "github.com/mholt/archives", + "path": "github.com/mholt/archives/LICENSE", "licenseText": "MIT License\n\nCopyright (c) 2016 Matthew Holt\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." }, { @@ -729,6 +774,11 @@ "path": "github.com/miekg/dns/LICENSE", "licenseText": "BSD 3-Clause License\n\nCopyright (c) 2009, The Go Authors. Extensions copyright (c) 2011, Miek Gieben. \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\n1. Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n2. 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\n3. Neither the name of the copyright holder 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 \"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/mikelolasagasti/xz", + "path": "github.com/mikelolasagasti/xz/LICENSE", + "licenseText": "Copyright (C) 2015-2017 Michael Cross \u003chttps://github.com/xi2\u003e\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n" + }, { "name": "github.com/minio/crc64nvme", "path": "github.com/minio/crc64nvme/LICENSE", @@ -744,6 +794,11 @@ "path": "github.com/minio/minio-go/v7/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/minio/minlz", + "path": "github.com/minio/minlz/LICENSE", + "licenseText": "\r\n Apache License\r\n Version 2.0, January 2004\r\n http://www.apache.org/licenses/\r\n\r\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r\n\r\n 1. Definitions.\r\n\r\n \"License\" shall mean the terms and conditions for use, reproduction,\r\n and distribution as defined by Sections 1 through 9 of this document.\r\n\r\n \"Licensor\" shall mean the copyright owner or entity authorized by\r\n the copyright owner that is granting the License.\r\n\r\n \"Legal Entity\" shall mean the union of the acting entity and all\r\n other entities that control, are controlled by, or are under common\r\n control with that entity. For the purposes of this definition,\r\n \"control\" means (i) the power, direct or indirect, to cause the\r\n direction or management of such entity, whether by contract or\r\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\r\n outstanding shares, or (iii) beneficial ownership of such entity.\r\n\r\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\r\n exercising permissions granted by this License.\r\n\r\n \"Source\" form shall mean the preferred form for making modifications,\r\n including but not limited to software source code, documentation\r\n source, and configuration files.\r\n\r\n \"Object\" form shall mean any form resulting from mechanical\r\n transformation or translation of a Source form, including but\r\n not limited to compiled object code, generated documentation,\r\n and conversions to other media types.\r\n\r\n \"Work\" shall mean the work of authorship, whether in Source or\r\n Object form, made available under the License, as indicated by a\r\n copyright notice that is included in or attached to the work\r\n (an example is provided in the Appendix below).\r\n\r\n \"Derivative Works\" shall mean any work, whether in Source or Object\r\n form, that is based on (or derived from) the Work and for which the\r\n editorial revisions, annotations, elaborations, or other modifications\r\n represent, as a whole, an original work of authorship. For the purposes\r\n of this License, Derivative Works shall not include works that remain\r\n separable from, or merely link (or bind by name) to the interfaces of,\r\n the Work and Derivative Works thereof.\r\n\r\n \"Contribution\" shall mean any work of authorship, including\r\n the original version of the Work and any modifications or additions\r\n to that Work or Derivative Works thereof, that is intentionally\r\n submitted to Licensor for inclusion in the Work by the copyright owner\r\n or by an individual or Legal Entity authorized to submit on behalf of\r\n the copyright owner. For the purposes of this definition, \"submitted\"\r\n means any form of electronic, verbal, or written communication sent\r\n to the Licensor or its representatives, including but not limited to\r\n communication on electronic mailing lists, source code control systems,\r\n and issue tracking systems that are managed by, or on behalf of, the\r\n Licensor for the purpose of discussing and improving the Work, but\r\n excluding communication that is conspicuously marked or otherwise\r\n designated in writing by the copyright owner as \"Not a Contribution.\"\r\n\r\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\r\n on behalf of whom a Contribution has been received by Licensor and\r\n subsequently incorporated within the Work.\r\n\r\n 2. Grant of Copyright License. Subject to the terms and conditions of\r\n this License, each Contributor hereby grants to You a perpetual,\r\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n copyright license to reproduce, prepare Derivative Works of,\r\n publicly display, publicly perform, sublicense, and distribute the\r\n Work and such Derivative Works in Source or Object form.\r\n\r\n 3. Grant of Patent License. Subject to the terms and conditions of\r\n this License, each Contributor hereby grants to You a perpetual,\r\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n (except as stated in this section) patent license to make, have made,\r\n use, offer to sell, sell, import, and otherwise transfer the Work,\r\n where such license applies only to those patent claims licensable\r\n by such Contributor that are necessarily infringed by their\r\n Contribution(s) alone or by combination of their Contribution(s)\r\n with the Work to which such Contribution(s) was submitted. If You\r\n institute patent litigation against any entity (including a\r\n cross-claim or counterclaim in a lawsuit) alleging that the Work\r\n or a Contribution incorporated within the Work constitutes direct\r\n or contributory patent infringement, then any patent licenses\r\n granted to You under this License for that Work shall terminate\r\n as of the date such litigation is filed.\r\n\r\n 4. Redistribution. You may reproduce and distribute copies of the\r\n Work or Derivative Works thereof in any medium, with or without\r\n modifications, and in Source or Object form, provided that You\r\n meet the following conditions:\r\n\r\n (a) You must give any other recipients of the Work or\r\n Derivative Works a copy of this License; and\r\n\r\n (b) You must cause any modified files to carry prominent notices\r\n stating that You changed the files; and\r\n\r\n (c) You must retain, in the Source form of any Derivative Works\r\n that You distribute, all copyright, patent, trademark, and\r\n attribution notices from the Source form of the Work,\r\n excluding those notices that do not pertain to any part of\r\n the Derivative Works; and\r\n\r\n (d) If the Work includes a \"NOTICE\" text file as part of its\r\n distribution, then any Derivative Works that You distribute must\r\n include a readable copy of the attribution notices contained\r\n within such NOTICE file, excluding those notices that do not\r\n pertain to any part of the Derivative Works, in at least one\r\n of the following places: within a NOTICE text file distributed\r\n as part of the Derivative Works; within the Source form or\r\n documentation, if provided along with the Derivative Works; or,\r\n within a display generated by the Derivative Works, if and\r\n wherever such third-party notices normally appear. The contents\r\n of the NOTICE file are for informational purposes only and\r\n do not modify the License. You may add Your own attribution\r\n notices within Derivative Works that You distribute, alongside\r\n or as an addendum to the NOTICE text from the Work, provided\r\n that such additional attribution notices cannot be construed\r\n as modifying the License.\r\n\r\n You may add Your own copyright statement to Your modifications and\r\n may provide additional or different license terms and conditions\r\n for use, reproduction, or distribution of Your modifications, or\r\n for any such Derivative Works as a whole, provided Your use,\r\n reproduction, and distribution of the Work otherwise complies with\r\n the conditions stated in this License.\r\n\r\n 5. Submission of Contributions. Unless You explicitly state otherwise,\r\n any Contribution intentionally submitted for inclusion in the Work\r\n by You to the Licensor shall be under the terms and conditions of\r\n this License, without any additional terms or conditions.\r\n Notwithstanding the above, nothing herein shall supersede or modify\r\n the terms of any separate license agreement you may have executed\r\n with Licensor regarding such Contributions.\r\n\r\n 6. Trademarks. This License does not grant permission to use the trade\r\n names, trademarks, service marks, or product names of the Licensor,\r\n except as required for reasonable and customary use in describing the\r\n origin of the Work and reproducing the content of the NOTICE file.\r\n\r\n 7. Disclaimer of Warranty. Unless required by applicable law or\r\n agreed to in writing, Licensor provides the Work (and each\r\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\r\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r\n implied, including, without limitation, any warranties or conditions\r\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r\n PARTICULAR PURPOSE. You are solely responsible for determining the\r\n appropriateness of using or redistributing the Work and assume any\r\n risks associated with Your exercise of permissions under this License.\r\n\r\n 8. Limitation of Liability. In no event and under no legal theory,\r\n whether in tort (including negligence), contract, or otherwise,\r\n unless required by applicable law (such as deliberate and grossly\r\n negligent acts) or agreed to in writing, shall any Contributor be\r\n liable to You for damages, including any direct, indirect, special,\r\n incidental, or consequential damages of any character arising as a\r\n result of this License or out of the use or inability to use the\r\n Work (including but not limited to damages for loss of goodwill,\r\n work stoppage, computer failure or malfunction, or any and all\r\n other commercial damages or losses), even if such Contributor\r\n has been advised of the possibility of such damages.\r\n\r\n 9. Accepting Warranty or Additional Liability. While redistributing\r\n the Work or Derivative Works thereof, You may choose to offer,\r\n and charge a fee for, acceptance of support, warranty, indemnity,\r\n or other liability obligations and/or rights consistent with this\r\n License. However, in accepting such obligations, You may act only\r\n on Your own behalf and on Your sole responsibility, not on behalf\r\n of any other Contributor, and only if You agree to indemnify,\r\n defend, and hold each Contributor harmless for any liability\r\n incurred by, or claims asserted against, such Contributor by reason\r\n of your accepting any such warranty or additional liability.\r\n\r\nEND OF TERMS AND CONDITIONS" + }, { "name": "github.com/mitchellh/mapstructure", "path": "github.com/mitchellh/mapstructure/LICENSE", @@ -764,21 +819,26 @@ "path": "github.com/munnerz/goautoneg/LICENSE", "licenseText": "Copyright (c) 2011, Open Knowledge Foundation Ltd.\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\n notice, this list of conditions and the following disclaimer.\n\n Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n Neither the name of the Open Knowledge Foundation Ltd. nor the\n names of its contributors may be used to endorse or promote\n products derived from this software without specific prior written\n 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\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/nektos/act/pkg", - "path": "github.com/nektos/act/pkg/LICENSE", - "licenseText": "MIT License\n\nCopyright (c) 2022 The Gitea Authors\nCopyright (c) 2019\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/niklasfasching/go-org/org", "path": "github.com/niklasfasching/go-org/org/LICENSE", "licenseText": "MIT License\n\nCopyright (c) 2018 Niklas Fasching\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/nwaples/rardecode", - "path": "github.com/nwaples/rardecode/LICENSE", + "name": "github.com/nwaples/rardecode/v2", + "path": "github.com/nwaples/rardecode/v2/LICENSE", "licenseText": "Copyright (c) 2015, Nicholas Waples\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\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/olekukonko/errors", + "path": "github.com/olekukonko/errors/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\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/olekukonko/ll", + "path": "github.com/olekukonko/ll/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\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/olekukonko/tablewriter", "path": "github.com/olekukonko/tablewriter/LICENSE.md", @@ -904,6 +964,11 @@ "path": "github.com/skeema/knownhosts/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" }, + { + "name": "github.com/sorairolake/lzip-go", + "path": "github.com/sorairolake/lzip-go/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---\n\nMIT License\n\nCopyright (c) 2024 Shun Sakai\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/ssor/bom", "path": "github.com/ssor/bom/LICENSE", @@ -999,6 +1064,11 @@ "path": "go.uber.org/zap/exp/zapslog/LICENSE", "licenseText": "Copyright (c) 2016-2024 Uber Technologies, Inc.\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": "go4.org", + "path": "go4.org/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": "golang.org/x/crypto", "path": "golang.org/x/crypto/LICENSE", diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index cb95b3b3c8..91b344b1e9 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -17,6 +17,15 @@ import ( "github.com/urfave/cli/v3" ) +type ( + authService struct { + initDB func(ctx context.Context) error + createAuthSource func(context.Context, *auth_model.Source) error + updateAuthSource func(context.Context, *auth_model.Source) error + getAuthSourceByID func(ctx context.Context, id int64) (*auth_model.Source, error) + } +) + func microcmdAuthDelete() *cli.Command { return &cli.Command{ Name: "delete", @@ -60,6 +69,16 @@ func microcmdAuthList() *cli.Command { } } +// newAuthService creates a service with default functions. +func newAuthService() *authService { + return &authService{ + initDB: initDB, + createAuthSource: auth_model.CreateSource, + updateAuthSource: auth_model.UpdateSource, + getAuthSourceByID: auth_model.GetSourceByID, + } +} + func runListAuth(ctx context.Context, c *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go index 997d6b3a16..9af6c331d3 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -14,15 +14,6 @@ import ( "github.com/urfave/cli/v3" ) -type ( - authService struct { - initDB func(ctx context.Context) error - createAuthSource func(context.Context, *auth.Source) error - updateAuthSource func(context.Context, *auth.Source) error - getAuthSourceByID func(ctx context.Context, id int64) (*auth.Source, error) - } -) - func commonLdapCLIFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ @@ -184,16 +175,6 @@ func microcmdAuthUpdateLdapSimpleAuth() *cli.Command { } } -// newAuthService creates a service with default functions. -func newAuthService() *authService { - return &authService{ - initDB: initDB, - createAuthSource: auth.CreateSource, - updateAuthSource: auth.UpdateSource, - getAuthSourceByID: auth.GetSourceByID, - } -} - // parseAuthSource assigns values on authSource according to command line flags. func parseAuthSource(c *cli.Command, authSource *auth.Source) { if c.IsSet("name") { diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go index abdcd5d48a..ef5d9116e3 100644 --- a/cmd/admin_auth_oauth.go +++ b/cmd/admin_auth_oauth.go @@ -86,6 +86,11 @@ func oauthCLIFlags() []cli.Flag { Value: nil, Usage: "Scopes to request when to authenticate against this OAuth2 source", }, + &cli.StringFlag{ + Name: "attribute-ssh-public-key", + Value: "", + Usage: "Claim name providing SSH public keys for this source", + }, &cli.StringFlag{ Name: "required-claim-name", Value: "", @@ -120,6 +125,10 @@ func oauthCLIFlags() []cli.Flag { Name: "group-team-map-removal", Usage: "Activate automatic team membership removal depending on groups", }, + &cli.BoolFlag{ + Name: "allow-username-change", + Usage: "Allow users to change their username", + }, } } @@ -127,7 +136,7 @@ func microcmdAuthAddOauth() *cli.Command { return &cli.Command{ Name: "add-oauth", Usage: "Add new Oauth authentication source", - Action: runAddOauth, + Action: newAuthService().addOauth, Flags: oauthCLIFlags(), } } @@ -136,7 +145,7 @@ func microcmdAuthUpdateOauth() *cli.Command { return &cli.Command{ Name: "update-oauth", Usage: "Update existing Oauth authentication source", - Action: runUpdateOauth, + Action: newAuthService().updateOauth, Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{idFlag()}, oauthCLIFlags()[1:]...)...), } } @@ -163,6 +172,7 @@ func parseOAuth2Config(_ context.Context, c *cli.Command) *oauth2.Source { IconURL: c.String("icon-url"), SkipLocalTwoFA: c.Bool("skip-local-2fa"), Scopes: c.StringSlice("scopes"), + AttributeSSHPublicKey: c.String("attribute-ssh-public-key"), RequiredClaimName: c.String("required-claim-name"), RequiredClaimValue: c.String("required-claim-value"), GroupClaimName: c.String("group-claim-name"), @@ -170,14 +180,15 @@ func parseOAuth2Config(_ context.Context, c *cli.Command) *oauth2.Source { RestrictedGroup: c.String("restricted-group"), GroupTeamMap: c.String("group-team-map"), GroupTeamMapRemoval: c.Bool("group-team-map-removal"), + AllowUsernameChange: c.Bool("allow-username-change"), } } -func runAddOauth(ctx context.Context, c *cli.Command) error { +func (a *authService) addOauth(ctx context.Context, c *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() - if err := initDB(ctx); err != nil { + if err := a.initDB(ctx); err != nil { return err } @@ -189,7 +200,7 @@ func runAddOauth(ctx context.Context, c *cli.Command) error { } } - return auth_model.CreateSource(ctx, &auth_model.Source{ + return a.createAuthSource(ctx, &auth_model.Source{ Type: auth_model.OAuth2, Name: c.String("name"), IsActive: true, @@ -197,7 +208,7 @@ func runAddOauth(ctx context.Context, c *cli.Command) error { }) } -func runUpdateOauth(ctx context.Context, c *cli.Command) error { +func (a *authService) updateOauth(ctx context.Context, c *cli.Command) error { if !c.IsSet("id") { return errors.New("--id flag is missing") } @@ -205,11 +216,11 @@ func runUpdateOauth(ctx context.Context, c *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() - if err := initDB(ctx); err != nil { + if err := a.initDB(ctx); err != nil { return err } - source, err := auth_model.GetSourceByID(ctx, c.Int64("id")) + source, err := a.getAuthSourceByID(ctx, c.Int64("id")) if err != nil { return err } @@ -244,6 +255,10 @@ func runUpdateOauth(ctx context.Context, c *cli.Command) error { oAuth2Config.Scopes = c.StringSlice("scopes") } + if c.IsSet("attribute-ssh-public-key") { + oAuth2Config.AttributeSSHPublicKey = c.String("attribute-ssh-public-key") + } + if c.IsSet("required-claim-name") { oAuth2Config.RequiredClaimName = c.String("required-claim-name") } @@ -267,6 +282,10 @@ func runUpdateOauth(ctx context.Context, c *cli.Command) error { oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal") } + if c.IsSet("allow-username-change") { + oAuth2Config.AllowUsernameChange = c.Bool("allow-username-change") + } + // update custom URL mapping customURLMapping := &oauth2.CustomURLMapping{} @@ -300,5 +319,5 @@ func runUpdateOauth(ctx context.Context, c *cli.Command) error { oAuth2Config.CustomURLMapping = customURLMapping source.Cfg = oAuth2Config - return auth_model.UpdateSource(ctx, source) + return a.updateAuthSource(ctx, source) } diff --git a/cmd/admin_auth_oauth_test.go b/cmd/admin_auth_oauth_test.go new file mode 100644 index 0000000000..3430ad1f56 --- /dev/null +++ b/cmd/admin_auth_oauth_test.go @@ -0,0 +1,706 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package cmd + +import ( + "context" + "testing" + + "forgejo.org/models/auth" + "forgejo.org/modules/test" + "forgejo.org/services/auth/source/oauth2" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v3" +) + +func TestAddOauth(t *testing.T) { + // Mock cli functions to do not exit on error + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() + + // Test cases + cases := []struct { + args []string + source *auth.Source + errMsg string + }{ + // case 0 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source full", + "--provider", "openidConnect", + "--key", "client id", + "--secret", "client secret", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + "--use-custom-urls", "", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + "--icon-url", "https://example.com/icon.svg", + "--skip-local-2fa", + "--scopes", "address", + "--scopes", "email", + "--scopes", "phone", + "--scopes", "profile", + "--attribute-ssh-public-key", "ssh_public_key", + "--required-claim-name", "can_access", + "--required-claim-value", "yes", + "--group-claim-name", "groups", + "--admin-group", "admin", + "--restricted-group", "restricted", + "--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + "--group-team-map-removal", + "--allow-username-change", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source full", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + ClientID: "client id", + ClientSecret: "client secret", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + CustomURLMapping: &oauth2.CustomURLMapping{ + AuthURL: "https://example.com/auth", + TokenURL: "https://example.com/token", + ProfileURL: "https://example.com/profile", + EmailURL: "https://example.com/email", + Tenant: "tenant id", + }, + IconURL: "https://example.com/icon.svg", + Scopes: []string{"address", "email", "phone", "profile"}, + AttributeSSHPublicKey: "ssh_public_key", + RequiredClaimName: "can_access", + RequiredClaimValue: "yes", + GroupClaimName: "groups", + AdminGroup: "admin", + GroupTeamMap: `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + GroupTeamMapRemoval: true, + RestrictedGroup: "restricted", + SkipLocalTwoFA: true, + AllowUsernameChange: true, + }, + }, + }, + // case 1 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source min", + "--provider", "openidConnect", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source min", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + Scopes: []string{}, + }, + }, + }, + // case 2 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags", + IsActive: true, + Cfg: &oauth2.Source{ + Scopes: []string{}, + }, + }, + }, + // case 3 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses", + "--provider", "openidConnect", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + "--scopes", "address", + "--scopes", "email", + "--scopes", "phone", + "--scopes", "profile", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + Scopes: []string{"address", "email", "phone", "profile"}, + }, + }, + }, + // case 4 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source `--scopes` supports commas as separators", + "--provider", "openidConnect", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + "--scopes", "address,email,phone,profile", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--scopes` supports commas as separators", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + Scopes: []string{"address", "email", "phone", "profile"}, + }, + }, + }, + // case 5 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source", + "--provider", "openidConnect", + }, + errMsg: "invalid Auto Discovery URL: (this must be a valid URL starting with http:// or https://)", + }, + // case 6 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source", + "--provider", "openidConnect", + "--auto-discover-url", "example.com", + }, + errMsg: "invalid Auto Discovery URL: example.com (this must be a valid URL starting with http:// or https://)", + }, + } + + for n, c := range cases { + // Mock functions. + var createdAuthSource *auth.Source + service := &authService{ + initDB: func(context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, authSource *auth.Source) error { + createdAuthSource = authSource + return nil + }, + updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { + assert.FailNow(t, "should not call updateAuthSource", "case: %d", n) + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { + assert.FailNow(t, "should not call getAuthSourceByID", "case: %d", n) + return nil, nil + }, + } + + // Create a copy of command to test + app := cli.Command{} + app.Flags = microcmdAuthAddOauth().Flags + app.Action = service.addOauth + + // Run it + err := app.Run(t.Context(), c.args) + if c.errMsg != "" { + assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) + } else { + require.NoError(t, err, "case %d: should have no errors", n) + assert.Equal(t, c.source, createdAuthSource, "case %d: wrong authSource", n) + } + } +} + +func TestUpdateOauth(t *testing.T) { + // Mock cli functions to do not exit on error + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() + + // Test cases + cases := []struct { + args []string + id int64 + existingAuthSource *auth.Source + authSource *auth.Source + errMsg string + }{ + // case 0 + { + args: []string{ + "oauth-test", + "--id", "23", + "--name", "oauth2 (via openidConnect) source full", + "--provider", "openidConnect", + "--key", "client id", + "--secret", "client secret", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + "--use-custom-urls", "", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + "--icon-url", "https://example.com/icon.svg", + "--skip-local-2fa", + "--scopes", "address", + "--scopes", "email", + "--scopes", "phone", + "--scopes", "profile", + "--attribute-ssh-public-key", "ssh_public_key", + "--required-claim-name", "can_access", + "--required-claim-value", "yes", + "--group-claim-name", "groups", + "--admin-group", "admin", + "--restricted-group", "restricted", + "--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + "--group-team-map-removal", + }, + id: 23, + existingAuthSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{}, + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source full", + Cfg: &oauth2.Source{ + Provider: "openidConnect", + ClientID: "client id", + ClientSecret: "client secret", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + CustomURLMapping: &oauth2.CustomURLMapping{ + AuthURL: "https://example.com/auth", + TokenURL: "https://example.com/token", + ProfileURL: "https://example.com/profile", + EmailURL: "https://example.com/email", + Tenant: "tenant id", + }, + IconURL: "https://example.com/icon.svg", + Scopes: []string{"address", "email", "phone", "profile"}, + AttributeSSHPublicKey: "ssh_public_key", + RequiredClaimName: "can_access", + RequiredClaimValue: "yes", + GroupClaimName: "groups", + AdminGroup: "admin", + GroupTeamMap: `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + GroupTeamMapRemoval: true, + RestrictedGroup: "restricted", + // `--skip-local-2fa` is currently ignored. + // SkipLocalTwoFA: true, + }, + }, + }, + // case 1 + { + args: []string{ + "oauth-test", + "--id", "1", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 2 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source full", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source full", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 3 + { + args: []string{ + "oauth-test", + "--id", "1", + "--provider", "openidConnect", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 4 + { + args: []string{ + "oauth-test", + "--id", "1", + "--key", "client id", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + ClientID: "client id", + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 5 + { + args: []string{ + "oauth-test", + "--id", "1", + "--secret", "client secret", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + ClientSecret: "client secret", + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 6 + { + args: []string{ + "oauth-test", + "--id", "1", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 7 + { + args: []string{ + "oauth-test", + "--id", "1", + "--use-custom-urls", "", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{ + AuthURL: "https://example.com/auth", + TokenURL: "https://example.com/token", + ProfileURL: "https://example.com/profile", + EmailURL: "https://example.com/email", + Tenant: "tenant id", + }, + }, + }, + }, + // case 8 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 9 + { + args: []string{ + "oauth-test", + "--id", "1", + "--icon-url", "https://example.com/icon.svg", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + IconURL: "https://example.com/icon.svg", + }, + }, + }, + // case 10 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source `--skip-local-2fa` is currently ignored", + "--skip-local-2fa", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--skip-local-2fa` is currently ignored", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + // `--skip-local-2fa` is currently ignored. + // SkipLocalTwoFA: true, + }, + }, + }, + // case 11 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses", + "--scopes", "address", + "--scopes", "email", + "--scopes", "phone", + "--scopes", "profile", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + Scopes: []string{"address", "email", "phone", "profile"}, + }, + }, + }, + // case 12 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source `--scopes` supports commas as separators", + "--scopes", "address,email,phone,profile", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--scopes` supports commas as separators", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + Scopes: []string{"address", "email", "phone", "profile"}, + }, + }, + }, + // case 13 + { + args: []string{ + "oauth-test", + "--id", "1", + "--attribute-ssh-public-key", "ssh_public_key", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + AttributeSSHPublicKey: "ssh_public_key", + }, + }, + }, + // case 14 + { + args: []string{ + "oauth-test", + "--id", "1", + "--required-claim-name", "can_access", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + RequiredClaimName: "can_access", + }, + }, + }, + // case 15 + { + args: []string{ + "oauth-test", + "--id", "1", + "--required-claim-value", "yes", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + RequiredClaimValue: "yes", + }, + }, + }, + // case 16 + { + args: []string{ + "oauth-test", + "--id", "1", + "--group-claim-name", "groups", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + GroupClaimName: "groups", + }, + }, + }, + // case 17 + { + args: []string{ + "oauth-test", + "--id", "1", + "--admin-group", "admin", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + AdminGroup: "admin", + }, + }, + }, + // case 18 + { + args: []string{ + "oauth-test", + "--id", "1", + "--restricted-group", "restricted", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + RestrictedGroup: "restricted", + }, + }, + }, + // case 19 + { + args: []string{ + "oauth-test", + "--id", "1", + "--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + GroupTeamMap: `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + }, + }, + }, + // case 20 + { + args: []string{ + "oauth-test", + "--id", "1", + "--group-team-map-removal", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + GroupTeamMapRemoval: true, + }, + }, + }, + // case 21 + { + args: []string{ + "oauth-test", + "--id", "23", + "--group-team-map-removal=false", + }, + id: 23, + existingAuthSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + GroupTeamMapRemoval: true, + }, + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + GroupTeamMapRemoval: false, + }, + }, + }, + // case 22 + { + args: []string{ + "oauth-test", + }, + errMsg: "--id flag is missing", + }, + } + + for n, c := range cases { + // Mock functions. + var updatedAuthSource *auth.Source + service := &authService{ + initDB: func(context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, authSource *auth.Source) error { + assert.FailNow(t, "should not call createAuthSource", "case: %d", n) + return nil + }, + updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { + updatedAuthSource = authSource + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { + if c.id != 0 { + assert.Equal(t, c.id, id, "case %d: wrong id", n) + } + if c.existingAuthSource != nil { + return c.existingAuthSource, nil + } + return &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{}, + }, nil + }, + } + + // Create a copy of command to test + app := cli.Command{} + app.Flags = microcmdAuthUpdateOauth().Flags + app.Action = service.updateOauth + + // Run it + err := app.Run(t.Context(), c.args) + if c.errMsg != "" { + assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) + } else { + require.NoError(t, err, "case %d: should have no errors", n) + assert.Equal(t, c.authSource, updatedAuthSource, "case %d: wrong authSource", n) + } + } +} diff --git a/cmd/dump.go b/cmd/dump.go index cb01e74196..ea141291f5 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -8,11 +8,12 @@ import ( "context" "errors" "fmt" - "io" + "io/fs" "os" "path" "path/filepath" "strings" + "sync" "time" "forgejo.org/models/db" @@ -23,36 +24,43 @@ import ( "forgejo.org/modules/util" "code.forgejo.org/go-chi/session" - "github.com/mholt/archiver/v3" + "github.com/mholt/archives" "github.com/urfave/cli/v3" ) -func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error { +func addObject(archiveJobs chan archives.ArchiveAsyncJob, object fs.File, customName string, verbose bool) error { if verbose { log.Info("Adding file %s", customName) } - return w.Write(archiver.File{ - FileInfo: archiver.FileInfo{ - FileInfo: info, - CustomName: customName, + info, err := object.Stat() + if err != nil { + return err + } + + ch := make(chan error) + + archiveJobs <- archives.ArchiveAsyncJob{ + File: archives.FileInfo{ + FileInfo: info, + NameInArchive: customName, + Open: func() (fs.File, error) { + return object, nil + }, }, - ReadCloser: r, - }) + Result: ch, + } + + return <-ch } -func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error { - file, err := os.Open(absPath) - if err != nil { - return err - } - defer file.Close() - fileInfo, err := file.Stat() +func addFile(archiveJobs chan archives.ArchiveAsyncJob, filePath, absPath string, verbose bool) error { + file, err := os.Open(absPath) // Closed by archiver if err != nil { return err } - return addReader(w, file, fileInfo, filePath, verbose) + return addObject(archiveJobs, file, filePath, verbose) } func isSubdir(upper, lower string) (bool, error) { @@ -101,6 +109,54 @@ var outputTypeEnum = &outputType{ Default: "zip", } +func getArchiverByType(outType string) (archives.ArchiverAsync, error) { + var archiver archives.ArchiverAsync + switch outType { + case "zip": + archiver = archives.Zip{} + case "tar": + archiver = archives.Tar{} + case "tar.sz": + archiver = archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Sz{}, + } + case "tar.gz": + archiver = archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Gz{}, + } + case "tar.xz": + archiver = archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Xz{}, + } + case "tar.bz2": + archiver = archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Bz2{}, + } + case "tar.br": + archiver = archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Brotli{}, + } + case "tar.lz4": + archiver = archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Lz4{}, + } + case "tar.zst": + archiver = archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Zstd{}, + } + default: + return nil, fmt.Errorf("unsupported output type: %s", outType) + } + return archiver, nil +} + // CmdDump represents the available dump sub-command. func cmdDump() *cli.Command { return &cli.Command{ @@ -254,46 +310,185 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error { return err } - var iface any - if fileName == "-" { - iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType)) - } else { - iface, err = archiver.ByExtension(fileName) - } + archiveJobs := make(chan archives.ArchiveAsyncJob) + wg := sync.WaitGroup{} + archiver, err := getArchiverByType(outType) if err != nil { fatal("Failed to get archiver for extension: %v", err) } - w, _ := iface.(archiver.Writer) - if err := w.Create(file); err != nil { - fatal("Creating archiver.Writer failed: %v", err) - } - defer w.Close() - if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") { log.Info("Skipping local repositories") } else { log.Info("Dumping local repositories... %s", setting.RepoRootPath) - if err := addRecursiveExclude(w, "repos", setting.RepoRootPath, []string{absFileName}, verbose); err != nil { - fatal("Failed to include repositories: %v", err) - } + wg.Add(1) + go dumpRepos(ctx, archiveJobs, &wg, absFileName, verbose) + } - if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") { - log.Info("Skipping LFS data") - } else if !setting.LFS.StartServer { - log.Info("LFS not enabled - skipping") - } else if err := storage.LFS.IterateObjects("", func(objPath string, object storage.Object) error { - info, err := object.Stat() - if err != nil { - return err + wg.Add(1) + go dumpDatabase(ctx, archiveJobs, &wg, verbose) + + if len(setting.CustomConf) > 0 { + wg.Add(1) + go func() { + defer wg.Done() + log.Info("Adding custom configuration file from %s", setting.CustomConf) + if err := addFile(archiveJobs, "app.ini", setting.CustomConf, verbose); err != nil { + fatal("Failed to include specified app.ini: %v", err) } + }() + } - return addReader(w, object, info, path.Join("data", "lfs", objPath), verbose) - }); err != nil { - fatal("Failed to dump LFS objects: %v", err) + if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") { + log.Info("Skipping custom directory") + } else { + wg.Add(1) + go dumpCustom(archiveJobs, &wg, absFileName, verbose) + } + + isExist, err := util.IsExist(setting.AppDataPath) + if err != nil { + log.Error("Failed to check if %s exists: %v", setting.AppDataPath, err) + } + if isExist { + log.Info("Packing data directory...%s", setting.AppDataPath) + + wg.Add(1) + go dumpData(ctx, archiveJobs, &wg, absFileName, verbose) + } + + if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") { + log.Info("Skipping attachment data") + } else { + wg.Add(1) + go func() { + defer wg.Done() + if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error { + return addObject(archiveJobs, object, path.Join("data", "attachments", objPath), verbose) + }); err != nil { + fatal("Failed to dump attachments: %v", err) + } + }() + } + + if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") { + log.Info("Skipping package data") + } else if !setting.Packages.Enabled { + log.Info("Package registry not enabled - skipping") + } else { + wg.Add(1) + go func() { + defer wg.Done() + if err := storage.Packages.IterateObjects("", func(objPath string, object storage.Object) error { + return addObject(archiveJobs, object, path.Join("data", "packages", objPath), verbose) + }); err != nil { + fatal("Failed to dump packages: %v", err) + } + }() + } + + // Doesn't check if LogRootPath exists before processing --skip-log intentionally, + // ensuring that it's clear the dump is skipped whether the directory's initialized + // yet or not. + if ctx.IsSet("skip-log") && ctx.Bool("skip-log") { + log.Info("Skipping log files") + } else { + isExist, err := util.IsExist(setting.Log.RootPath) + if err != nil { + log.Error("Failed to check if %s exists: %v", setting.Log.RootPath, err) + } + if isExist { + wg.Add(1) + go func() { + defer wg.Done() + if err := addRecursiveExclude(archiveJobs, "log", setting.Log.RootPath, []string{absFileName}, verbose); err != nil { + fatal("Failed to include log: %v", err) + } + }() } } + // Wait for all jobs to finish before closing the channel + // ArchiveAsync will only return after the channel is closed + go func() { + wg.Wait() + close(archiveJobs) + }() + + if err := archiver.ArchiveAsync(stdCtx, file, archiveJobs); err != nil { + _ = util.Remove(fileName) + + fatal("Archiving failed: %v", err) + } + + if fileName != "-" { + if err := os.Chmod(fileName, 0o600); err != nil { + log.Info("Can't change file access permissions mask to 0600: %v", err) + } + + log.Info("Finished dumping in file %s", fileName) + } else { + log.Info("Finished dumping to stdout") + } + + return nil +} + +func dumpData(ctx *cli.Command, archiveJobs chan archives.ArchiveAsyncJob, wg *sync.WaitGroup, absFileName string, verbose bool) { + defer wg.Done() + + var excludes []string + if setting.SessionConfig.OriginalProvider == "file" { + var opts session.Options + if err := json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil { + fatal("Failed to parse session config: %v", err) + } + excludes = append(excludes, opts.ProviderConfig) + } + + if ctx.IsSet("skip-index") && ctx.Bool("skip-index") { + log.Info("Skipping bleve index data") + excludes = append(excludes, setting.Indexer.RepoPath) + excludes = append(excludes, setting.Indexer.IssuePath) + } + + if ctx.IsSet("skip-repo-archives") && ctx.Bool("skip-repo-archives") { + log.Info("Skipping repository archives data") + excludes = append(excludes, setting.RepoArchive.Storage.Path) + } + + excludes = append(excludes, setting.RepoRootPath) + excludes = append(excludes, setting.LFS.Storage.Path) + excludes = append(excludes, setting.Attachment.Storage.Path) + excludes = append(excludes, setting.Packages.Storage.Path) + excludes = append(excludes, setting.Log.RootPath) + excludes = append(excludes, absFileName) + if err := addRecursiveExclude(archiveJobs, "data", setting.AppDataPath, excludes, verbose); err != nil { + fatal("Failed to include data directory: %v", err) + } +} + +func dumpCustom(archiveJobs chan archives.ArchiveAsyncJob, wg *sync.WaitGroup, absFileName string, verbose bool) { + defer wg.Done() + + customDir, err := os.Stat(setting.CustomPath) + if err == nil && customDir.IsDir() { + if is, _ := isSubdir(setting.AppDataPath, setting.CustomPath); !is { + if err := addRecursiveExclude(archiveJobs, "custom", setting.CustomPath, []string{absFileName}, verbose); err != nil { + fatal("Failed to include custom: %v", err) + } + } else { + log.Info("Custom dir %s is inside data dir %s, skipping", setting.CustomPath, setting.AppDataPath) + } + } else { + log.Info("Custom dir %s does not exist, skipping", setting.CustomPath) + } +} + +func dumpDatabase(ctx *cli.Command, archiveJobs chan archives.ArchiveAsyncJob, wg *sync.WaitGroup, verbose bool) { + defer wg.Done() + + var err error tmpDir := ctx.String("tempdir") if tmpDir == "" { tmpDir, err = os.MkdirTemp("", "forgejo-dump-*") @@ -334,139 +529,32 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error { fatal("Failed to dump database: %v", err) } - if err := addFile(w, "forgejo-db.sql", dbDump.Name(), verbose); err != nil { + if err := addFile(archiveJobs, "forgejo-db.sql", dbDump.Name(), verbose); err != nil { fatal("Failed to include forgejo-db.sql: %v", err) } +} - if len(setting.CustomConf) > 0 { - log.Info("Adding custom configuration file from %s", setting.CustomConf) - if err := addFile(w, "app.ini", setting.CustomConf, verbose); err != nil { - fatal("Failed to include specified app.ini: %v", err) - } +func dumpRepos(ctx *cli.Command, archiveJobs chan archives.ArchiveAsyncJob, wg *sync.WaitGroup, absFileName string, verbose bool) { + defer wg.Done() + + if err := addRecursiveExclude(archiveJobs, "repos", setting.RepoRootPath, []string{absFileName}, verbose); err != nil { + fatal("Failed to include repositories: %v", err) } - if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") { - log.Info("Skipping custom directory") - } else { - customDir, err := os.Stat(setting.CustomPath) - if err == nil && customDir.IsDir() { - if is, _ := isSubdir(setting.AppDataPath, setting.CustomPath); !is { - if err := addRecursiveExclude(w, "custom", setting.CustomPath, []string{absFileName}, verbose); err != nil { - fatal("Failed to include custom: %v", err) - } - } else { - log.Info("Custom dir %s is inside data dir %s, skipping", setting.CustomPath, setting.AppDataPath) - } - } else { - log.Info("Custom dir %s does not exist, skipping", setting.CustomPath) - } - } - - isExist, err := util.IsExist(setting.AppDataPath) - if err != nil { - log.Error("Failed to check if %s exists: %v", setting.AppDataPath, err) - } - if isExist { - log.Info("Packing data directory...%s", setting.AppDataPath) - - var excludes []string - if setting.SessionConfig.OriginalProvider == "file" { - var opts session.Options - if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil { - return err - } - excludes = append(excludes, opts.ProviderConfig) - } - - if ctx.IsSet("skip-index") && ctx.Bool("skip-index") { - log.Info("Skipping bleve index data") - excludes = append(excludes, setting.Indexer.RepoPath) - excludes = append(excludes, setting.Indexer.IssuePath) - } - - if ctx.IsSet("skip-repo-archives") && ctx.Bool("skip-repo-archives") { - log.Info("Skipping repository archives data") - excludes = append(excludes, setting.RepoArchive.Storage.Path) - } - - excludes = append(excludes, setting.RepoRootPath) - excludes = append(excludes, setting.LFS.Storage.Path) - excludes = append(excludes, setting.Attachment.Storage.Path) - excludes = append(excludes, setting.Packages.Storage.Path) - excludes = append(excludes, setting.Log.RootPath) - excludes = append(excludes, absFileName) - if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil { - fatal("Failed to include data directory: %v", err) - } - } - - if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") { - log.Info("Skipping attachment data") - } else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error { - info, err := object.Stat() - if err != nil { - return err - } - - return addReader(w, object, info, path.Join("data", "attachments", objPath), verbose) + if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") { + log.Info("Skipping LFS data") + } else if !setting.LFS.StartServer { + log.Info("LFS not enabled - skipping") + } else if err := storage.LFS.IterateObjects("", func(objPath string, object storage.Object) error { + return addObject(archiveJobs, object, path.Join("data", "lfs", objPath), verbose) }); err != nil { - fatal("Failed to dump attachments: %v", err) + fatal("Failed to dump LFS objects: %v", err) } - - if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") { - log.Info("Skipping package data") - } else if !setting.Packages.Enabled { - log.Info("Package registry not enabled - skipping") - } else if err := storage.Packages.IterateObjects("", func(objPath string, object storage.Object) error { - info, err := object.Stat() - if err != nil { - return err - } - - return addReader(w, object, info, path.Join("data", "packages", objPath), verbose) - }); err != nil { - fatal("Failed to dump packages: %v", err) - } - - // Doesn't check if LogRootPath exists before processing --skip-log intentionally, - // ensuring that it's clear the dump is skipped whether the directory's initialized - // yet or not. - if ctx.IsSet("skip-log") && ctx.Bool("skip-log") { - log.Info("Skipping log files") - } else { - isExist, err := util.IsExist(setting.Log.RootPath) - if err != nil { - log.Error("Failed to check if %s exists: %v", setting.Log.RootPath, err) - } - if isExist { - if err := addRecursiveExclude(w, "log", setting.Log.RootPath, []string{absFileName}, verbose); err != nil { - fatal("Failed to include log: %v", err) - } - } - } - - if fileName != "-" { - if err = w.Close(); err != nil { - _ = util.Remove(fileName) - fatal("Failed to save %s: %v", fileName, err) - } - - if err := os.Chmod(fileName, 0o600); err != nil { - log.Info("Can't change file access permissions mask to 0600: %v", err) - } - } - - if fileName != "-" { - log.Info("Finish dumping in file %s", fileName) - } else { - log.Info("Finish dumping to stdout") - } - - return nil } // addRecursiveExclude zips absPath to specified insidePath inside writer excluding excludeAbsPath -func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeAbsPath []string, verbose bool) error { +// archives.FilesFromDisk doesn't support excluding files, so we have to do it manually +func addRecursiveExclude(archiveJobs chan archives.ArchiveAsyncJob, insidePath, absPath string, excludeAbsPath []string, verbose bool) error { absPath, err := filepath.Abs(absPath) if err != nil { return err @@ -491,10 +579,11 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA } if file.IsDir() { - if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil { + if err := addFile(archiveJobs, currentInsidePath, currentAbsPath, false); err != nil { return err } - if err = addRecursiveExclude(w, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil { + + if err := addRecursiveExclude(archiveJobs, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil { return err } } else { @@ -512,7 +601,7 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA shouldAdd = targetStat.Mode().IsRegular() } if shouldAdd { - if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil { + if err := addFile(archiveJobs, currentInsidePath, currentAbsPath, verbose); err != nil { return err } } diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go index eb89273e7f..7159d55e99 100644 --- a/cmd/dump_repo.go +++ b/cmd/dump_repo.go @@ -82,6 +82,11 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme } func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error { + setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr) + + // setting.DisableLoggerInit() + setting.LoadSettings() // cannot access skip_tls_verify settings otherwise + stdCtx, cancel := installSignals(stdCtx) defer cancel() diff --git a/cmd/dump_test.go b/cmd/dump_test.go index 459386318f..3bdd9d68f8 100644 --- a/cmd/dump_test.go +++ b/cmd/dump_test.go @@ -4,40 +4,32 @@ package cmd import ( - "io" "os" "testing" - "github.com/mholt/archiver/v3" + "github.com/mholt/archives" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -type mockArchiver struct { - addedFiles []string -} - -func (mockArchiver) Create(out io.Writer) error { - return nil -} - -func (m *mockArchiver) Write(f archiver.File) error { - m.addedFiles = append(m.addedFiles, f.Name()) - return nil -} - -func (mockArchiver) Close() error { - return nil +func mockArchiverAsync(ch chan archives.ArchiveAsyncJob, files *[]string) { + for job := range ch { + *files = append(*files, job.File.NameInArchive) + job.Result <- nil + } } func TestAddRecursiveExclude(t *testing.T) { t.Run("Empty", func(t *testing.T) { - dir := t.TempDir() - archiver := &mockArchiver{} + ch := make(chan archives.ArchiveAsyncJob) + var files []string + go mockArchiverAsync(ch, &files) - err := addRecursiveExclude(archiver, "", dir, []string{}, false) + dir := t.TempDir() + + err := addRecursiveExclude(ch, "", dir, []string{}, false) require.NoError(t, err) - assert.Empty(t, archiver.addedFiles) + assert.Empty(t, files) }) t.Run("Single file", func(t *testing.T) { @@ -46,20 +38,25 @@ func TestAddRecursiveExclude(t *testing.T) { require.NoError(t, err) t.Run("No exclude", func(t *testing.T) { - archiver := &mockArchiver{} + ch := make(chan archives.ArchiveAsyncJob) + var files []string + go mockArchiverAsync(ch, &files) - err = addRecursiveExclude(archiver, "", dir, nil, false) + err := addRecursiveExclude(ch, "", dir, nil, false) require.NoError(t, err) - assert.Len(t, archiver.addedFiles, 1) - assert.Contains(t, archiver.addedFiles, "example") + + assert.Len(t, files, 1) + assert.Contains(t, files, "example") }) t.Run("With exclude", func(t *testing.T) { - archiver := &mockArchiver{} + ch := make(chan archives.ArchiveAsyncJob) + var files []string + go mockArchiverAsync(ch, &files) - err = addRecursiveExclude(archiver, "", dir, []string{dir + "/example"}, false) + err := addRecursiveExclude(ch, "", dir, []string{dir + "/example"}, false) require.NoError(t, err) - assert.Empty(t, archiver.addedFiles) + assert.Empty(t, files) }) }) @@ -73,46 +70,57 @@ func TestAddRecursiveExclude(t *testing.T) { require.NoError(t, err) t.Run("No exclude", func(t *testing.T) { - archiver := &mockArchiver{} + ch := make(chan archives.ArchiveAsyncJob) + var files []string + go mockArchiverAsync(ch, &files) - err = addRecursiveExclude(archiver, "", dir, nil, false) + err := addRecursiveExclude(ch, "", dir, nil, false) require.NoError(t, err) - assert.Len(t, archiver.addedFiles, 5) - assert.Contains(t, archiver.addedFiles, "deep") - assert.Contains(t, archiver.addedFiles, "deep/nested") - assert.Contains(t, archiver.addedFiles, "deep/nested/folder") - assert.Contains(t, archiver.addedFiles, "deep/nested/folder/example") - assert.Contains(t, archiver.addedFiles, "deep/nested/folder/another-file") + assert.Len(t, files, 5) + + assert.Contains(t, files, "deep") + assert.Contains(t, files, "deep/nested") + assert.Contains(t, files, "deep/nested/folder") + assert.Contains(t, files, "deep/nested/folder/example") + assert.Contains(t, files, "deep/nested/folder/another-file") }) t.Run("Exclude first directory", func(t *testing.T) { - archiver := &mockArchiver{} + ch := make(chan archives.ArchiveAsyncJob) + var files []string + go mockArchiverAsync(ch, &files) - err = addRecursiveExclude(archiver, "", dir, []string{dir + "/deep"}, false) + err := addRecursiveExclude(ch, "", dir, []string{dir + "/deep"}, false) require.NoError(t, err) - assert.Empty(t, archiver.addedFiles) + assert.Empty(t, files) }) t.Run("Exclude nested directory", func(t *testing.T) { - archiver := &mockArchiver{} + ch := make(chan archives.ArchiveAsyncJob) + var files []string + go mockArchiverAsync(ch, &files) - err = addRecursiveExclude(archiver, "", dir, []string{dir + "/deep/nested/folder"}, false) + err := addRecursiveExclude(ch, "", dir, []string{dir + "/deep/nested/folder"}, false) require.NoError(t, err) - assert.Len(t, archiver.addedFiles, 2) - assert.Contains(t, archiver.addedFiles, "deep") - assert.Contains(t, archiver.addedFiles, "deep/nested") + assert.Len(t, files, 2) + + assert.Contains(t, files, "deep") + assert.Contains(t, files, "deep/nested") }) t.Run("Exclude file", func(t *testing.T) { - archiver := &mockArchiver{} + ch := make(chan archives.ArchiveAsyncJob) + var files []string + go mockArchiverAsync(ch, &files) - err = addRecursiveExclude(archiver, "", dir, []string{dir + "/deep/nested/folder/example"}, false) + err := addRecursiveExclude(ch, "", dir, []string{dir + "/deep/nested/folder/example"}, false) require.NoError(t, err) - assert.Len(t, archiver.addedFiles, 4) - assert.Contains(t, archiver.addedFiles, "deep") - assert.Contains(t, archiver.addedFiles, "deep/nested") - assert.Contains(t, archiver.addedFiles, "deep/nested/folder") - assert.Contains(t, archiver.addedFiles, "deep/nested/folder/another-file") + assert.Len(t, files, 4) + + assert.Contains(t, files, "deep") + assert.Contains(t, files, "deep/nested") + assert.Contains(t, files, "deep/nested/folder") + assert.Contains(t, files, "deep/nested/folder/another-file") }) }) } diff --git a/cmd/hook.go b/cmd/hook.go index 909cdfdf84..7378dc21ad 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -231,8 +231,6 @@ Forgejo or set your environment appropriately.`, "") } } - supportProcReceive := git.CheckGitVersionAtLeast("2.29") == nil - for scanner.Scan() { // TODO: support news feeds for wiki if isWiki { @@ -250,31 +248,25 @@ Forgejo or set your environment appropriately.`, "") total++ lastline++ - // If the ref is a branch or tag, check if it's protected - // if supportProcReceive all ref should be checked because - // permission check was delayed - if supportProcReceive || refFullName.IsBranch() || refFullName.IsTag() { - oldCommitIDs[count] = oldCommitID - newCommitIDs[count] = newCommitID - refFullNames[count] = refFullName - count++ - fmt.Fprint(out, "*") + // All references should be checked because permission check was delayed. + oldCommitIDs[count] = oldCommitID + newCommitIDs[count] = newCommitID + refFullNames[count] = refFullName + count++ + fmt.Fprint(out, "*") - if count >= hookBatchSize { - fmt.Fprintf(out, " Checking %d references\n", count) + if count >= hookBatchSize { + fmt.Fprintf(out, " Checking %d references\n", count) - hookOptions.OldCommitIDs = oldCommitIDs - hookOptions.NewCommitIDs = newCommitIDs - hookOptions.RefFullNames = refFullNames - extra := private.HookPreReceive(ctx, username, reponame, hookOptions) - if extra.HasError() { - return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error) - } - count = 0 - lastline = 0 + hookOptions.OldCommitIDs = oldCommitIDs + hookOptions.NewCommitIDs = newCommitIDs + hookOptions.RefFullNames = refFullNames + extra := private.HookPreReceive(ctx, username, reponame, hookOptions) + if extra.HasError() { + return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error) } - } else { - fmt.Fprint(out, ".") + count = 0 + lastline = 0 } if lastline >= hookBatchSize { fmt.Fprint(out, "\n") @@ -513,10 +505,6 @@ Forgejo or set your environment appropriately.`, "") return nil } - if git.CheckGitVersionAtLeast("2.29") != nil { - return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.") - } - reader := bufio.NewReader(os.Stdin) repoUser := os.Getenv(repo_module.EnvRepoUsername) repoName := os.Getenv(repo_module.EnvRepoName) diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go index c543afe872..c18bfa919b 100644 --- a/cmd/manager_logging.go +++ b/cmd/manager_logging.go @@ -44,6 +44,11 @@ func defaultLoggingFlags() []cli.Flag { Aliases: []string{"e"}, Usage: "Matching expression for the logger", }, + &cli.StringFlag{ + Name: "exclusion", + Aliases: []string{"x"}, + Usage: "Exclusion for the logger", + }, &cli.StringFlag{ Name: "prefix", Aliases: []string{"p"}, @@ -286,6 +291,9 @@ func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[ if len(c.String("expression")) > 0 { vals["expression"] = c.String("expression") } + if len(c.String("exclusion")) > 0 { + vals["exclusion"] = c.String("exclusion") + } if len(c.String("prefix")) > 0 { vals["prefix"] = c.String("prefix") } diff --git a/cmd/serv.go b/cmd/serv.go index 1fac2d13f5..b0571a276c 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -193,12 +193,10 @@ func runServ(ctx context.Context, c *cli.Command) error { } if len(words) < 2 { - if git.CheckGitVersionAtLeast("2.29") == nil { - // for AGit Flow - if cmd == "ssh_info" { - fmt.Print(`{"type":"agit","version":1}`) - return nil - } + // for AGit Flow + if cmd == "ssh_info" { + fmt.Print(`{"type":"agit","version":1}`) + return nil } return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd) } diff --git a/cmd/web_acme.go b/cmd/web_acme.go index 03b3b9f0da..be6314addb 100644 --- a/cmd/web_acme.go +++ b/cmd/web_acme.go @@ -15,6 +15,7 @@ import ( "forgejo.org/modules/graceful" "forgejo.org/modules/log" "forgejo.org/modules/process" + "forgejo.org/modules/proxy" "forgejo.org/modules/setting" "github.com/caddyserver/certmagic" @@ -76,6 +77,12 @@ 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() diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 1b8d4c6697..267696872d 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -449,6 +449,9 @@ INTERNAL_TOKEN = ;; How long to remember that a user is logged in before requiring relogin (in days) ;LOGIN_REMEMBER_DAYS = 31 ;; +;; Require 2FA globally for none|all|admin. +;GLOBAL_TWO_FACTOR_REQUIREMENT = none +;; ;; Name of cookie used to store authentication information. ;COOKIE_REMEMBER_NAME = gitea_incredible ;; @@ -592,9 +595,9 @@ LEVEL = Info ;BUFFER_LEN = 10000 ;; ;; Sub logger modes, a single comma means use default MODE above, empty means disable it -;logger.access.MODE= -;logger.router.MODE=, -;logger.xorm.MODE=, +;LOGGER_ACCESS_MODE= +;LOGGER_ROUTER_MODE=, +;LOGGER_XORM_MODE=, ;; ;; Collect SSH logs (Creates log from ssh git request) ;; @@ -631,6 +634,7 @@ LEVEL = Info ;LEVEL= ;FLAGS = stdflags or journald ;EXPRESSION = +;EXCLUSION = ;PREFIX = ;COLORIZE = false ;; @@ -1585,6 +1589,11 @@ LEVEL = Info ;; 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 +;; How long to keep resolved abuse reports for. +;; Applies to reports that have been marked as ignored or handled +;; Can be 1 hour, 7 days etc +;KEEP_RESOLVED_REPORTS_FOR = 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[openid] @@ -1767,6 +1776,9 @@ LEVEL = Info ;; Use PASSWD = `your password` for quoting if you use special characters in the password. ;PASSWD = ;; +;; Alternative location to specify mailer password. You cannot specify both this and PASSWD, and must pick one +;PASSWD_URI = file:/etc/forgejo/mailer_passwd +;; ;; Send mails only in plain text, without HTML alternative ;SEND_AS_PLAIN_TEXT = false ;; @@ -1819,6 +1831,9 @@ LEVEL = Info ;; Password of the receiving account ;PASSWORD = ;; +;; Alternative location to specify password of the receiving account. You cannot specify both this and PASSWORD, and must pick one +;PASSWORD_URI = file:/etc/forgejo/email_incoming_password +;; ;; Whether the IMAP server uses TLS. ;USE_TLS = false ;; diff --git a/go.mod b/go.mod index bb2be827eb..40f43061c7 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module forgejo.org go 1.24 -toolchain go1.24.4 +toolchain go1.24.6 require ( code.forgejo.org/f3/gof3/v3 v3.11.0 @@ -10,6 +10,7 @@ require ( 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/forgejo/runner/v9 v9.0.3 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 @@ -24,11 +25,11 @@ require ( 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.18.0 + github.com/alecthomas/chroma/v2 v2.20.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.5.2 github.com/buildkite/terminal-to-html/v3 v3.16.8 - github.com/caddyserver/certmagic v0.23.0 + github.com/caddyserver/certmagic v0.24.0 github.com/chi-middleware/proxy v1.1.1 github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 @@ -42,44 +43,42 @@ require ( 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.2 - github.com/go-chi/cors v1.2.1 + github.com/go-chi/cors v1.2.2 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.21.0 github.com/go-sql-driver/mysql v1.9.3 - github.com/go-webauthn/webauthn v0.13.0 + github.com/go-webauthn/webauthn v0.13.4 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-jwt/jwt/v5 v5.3.0 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-20250317173921-a4b03ec1a45e + github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.2.0 github.com/gorilla/sessions v1.4.0 github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/huandu/xstrings v1.5.0 - github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 - github.com/jhillyerd/enmime/v2 v2.1.0 + github.com/inbucket/html2text v0.9.0 + github.com/jhillyerd/enmime/v2 v2.2.0 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/klauspost/compress v1.18.0 - github.com/klauspost/cpuid/v2 v2.2.10 + github.com/klauspost/cpuid/v2 v2.2.11 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.28 - github.com/meilisearch/meilisearch-go v0.31.0 - github.com/mholt/archiver/v3 v3.5.1 + github.com/mattn/go-sqlite3 v1.14.32 + github.com/meilisearch/meilisearch-go v0.33.0 + github.com/mholt/archives v0.1.3 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/minio/minio-go/v7 v7.0.94 + github.com/minio/minio-go/v7 v7.0.95 github.com/msteinert/pam/v2 v2.1.0 - github.com/nektos/act v0.2.52 - github.com/niklasfasching/go-org v1.8.0 + github.com/niklasfasching/go-org v1.9.1 github.com/olivere/elastic/v7 v7.0.32 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 @@ -92,21 +91,21 @@ require ( 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/v3 v3.3.3 + github.com/urfave/cli/v3 v3.4.1 github.com/valyala/fastjson v1.6.4 github.com/yohcop/openid-go v1.0.1 - github.com/yuin/goldmark v1.7.12 + github.com/yuin/goldmark v1.7.13 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc 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/crypto v0.41.0 + golang.org/x/image v0.30.0 + golang.org/x/net v0.43.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 + golang.org/x/sync v0.16.0 + golang.org/x/sys v0.35.0 + golang.org/x/text v0.28.0 + google.golang.org/protobuf v1.36.7 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 gopkg.in/yaml.v3 v3.0.1 @@ -122,7 +121,8 @@ require ( git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect - github.com/andybalholm/brotli v1.1.1 // indirect + github.com/STARRY-S/zip v0.2.1 // indirect + github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // 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 @@ -145,35 +145,40 @@ require ( 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/bmatcuk/doublestar/v4 v4.8.0 // indirect + github.com/bodgit/plumbing v1.3.0 // indirect + github.com/bodgit/sevenzip v1.6.0 // indirect + github.com/bodgit/windows v1.0.1 // indirect github.com/boombuler/barcode v1.0.1 // 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.6.1 // indirect - github.com/cyphar/filepath-securejoin v0.3.6 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // 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.5 // indirect github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/fatih/color v1.16.0 // indirect - github.com/fxamacker/cbor/v2 v2.8.0 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.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 github.com/go-fed/httpsig v1.1.0 // indirect 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-git/go-git/v5 v5.16.2 // indirect github.com/go-ini/ini v1.67.0 // 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/go-webauthn/x v0.1.23 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // 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 @@ -183,31 +188,38 @@ require ( github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 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 v1.0.0-beta.1 // indirect + github.com/libdns/libdns v1.0.0 // 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-colorable v0.1.14 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-shellwords v1.0.12 // 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/mikelolasagasti/xz v1.0.1 // indirect + github.com/minio/crc64nvme v1.0.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/minlz v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nwaples/rardecode v1.1.3 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/nwaples/rardecode/v2 v2.1.0 // indirect + github.com/olekukonko/errors v1.1.0 // indirect + github.com/olekukonko/ll v0.0.9 // indirect + github.com/olekukonko/tablewriter v1.0.7 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect + github.com/philhofer/fwd v1.2.0 // 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 @@ -215,35 +227,33 @@ require ( 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/rhysd/actionlint v1.7.7 // 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/sirupsen/logrus v1.9.3 // indirect - github.com/skeema/knownhosts v1.3.0 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect + github.com/sorairolake/lzip-go v0.3.5 // 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/zeebo/assert v1.3.0 // indirect github.com/zeebo/blake3 v0.2.4 // indirect - go.etcd.io/bbolt v1.4.0 // indirect + go.etcd.io/bbolt v1.4.2 // 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.25.0 // indirect - golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.34.0 // indirect + go4.org v0.0.0-20230225012048-214862532bf5 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/time v0.12.0 // indirect + golang.org/x/tools v0.36.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/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 replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 diff --git a/go.sum b/go.sum index 639880e2ce..b7153121df 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,25 @@ +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.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.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +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/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +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/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 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.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= @@ -16,6 +28,8 @@ code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/Uf code.forgejo.org/forgejo/levelqueue v1.0.0/go.mod h1:fmG6zhVuqim2rxSFOoasgXO8V2W/k9U31VVYqLIRLhQ= code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ= code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= +code.forgejo.org/forgejo/runner/v9 v9.0.3 h1:BijUSIuPCDNKE3xfq7Ilj0JvK/IcFmP8oarg9dX/QPE= +code.forgejo.org/forgejo/runner/v9 v9.0.3/go.mod h1:0/xEl3Fndpb7g28/4xmmInsugp2V6sXBePJuERPT+S8= 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.1 h1:coKNI+X1NzRN7X85LlrpvBRqk0TXpJ+ja28vusQWEuY= @@ -36,6 +50,7 @@ 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= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= @@ -48,6 +63,8 @@ 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/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= @@ -57,21 +74,22 @@ github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiU 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/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg= +github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4= 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/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.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4= -github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= +github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= +github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= 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/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= +github.com/alecthomas/repr v0.5.1/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.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= -github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ= +github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/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= @@ -123,6 +141,14 @@ github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFx 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/bmatcuk/doublestar/v4 v4.8.0 h1:DSXtrypQddoug1459viM9X9D3dp1Z7993fw36I2kNcQ= +github.com/bmatcuk/doublestar/v4 v4.8.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= +github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= +github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A= +github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc= +github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= +github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= 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= @@ -134,10 +160,11 @@ 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.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= -github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4= +github.com/caddyserver/certmagic v0.24.0 h1:EfXTWpxHAUKgDfOj6MHImJN8Jm4AMFfMT6ITuKhrDF0= +github.com/caddyserver/certmagic v0.24.0/go.mod h1:xPT7dC1DuHHnS2yuEQCEyks+b89sUkMENh8dJF+InLE= 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/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/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -147,14 +174,18 @@ 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.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= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -173,7 +204,6 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 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= @@ -181,8 +211,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp 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= github.com/editorconfig/editorconfig-core-go/v2 v2.6.3/go.mod h1:ThHVc+hqbUsmE1wmK/MASpQEhCleWu1JDJDNhUOMy0c= -github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= -github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA= github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY= github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4= @@ -192,8 +222,10 @@ 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/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 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/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -202,8 +234,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 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/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.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= @@ -215,8 +247,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro github.com/go-chi/chi/v5 v5.0.1/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-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= +github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY= @@ -231,8 +263,10 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= 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-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= +github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +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-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= @@ -250,10 +284,10 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6 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-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/go-webauthn/webauthn v0.13.4 h1:q68qusWPcqHbg9STSxBLBHnsKaLxNO0RnVKaAqMuAuQ= +github.com/go-webauthn/webauthn v0.13.4/go.mod h1:MglN6OH9ECxvhDqoq1wMoF6P6JRYDiQpC9nc5OomQmI= +github.com/go-webauthn/x v0.1.23 h1:9lEO0s+g8iTyz5Vszlg/rXTGrx3CjcD0RZQ1GPZCaxI= +github.com/go-webauthn/x v0.1.23/go.mod h1:AJd3hI7NfEp/4fI6T4CHD753u91l510lglU7/NMN6+E= 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= @@ -265,16 +299,26 @@ github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7w 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/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-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= 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/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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +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-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +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/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.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= @@ -284,11 +328,13 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw 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.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/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= @@ -306,13 +352,20 @@ github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u 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/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-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -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/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ= +github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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/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/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= @@ -327,12 +380,19 @@ 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/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 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-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 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= @@ -340,30 +400,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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -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/inbucket/html2text v0.9.0 h1:ULJmVcBEMAcmLE+/rN815KG1Fx6+a4HhbUxiDiN+qks= +github.com/inbucket/html2text v0.9.0/go.mod h1:QDaumzl+/OzlSVbNohhmg+yAy5pKjUjzCKW2BMvztKE= 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/jhillyerd/enmime/v2 v2.2.0 h1:Pe35MB96eZK5Q0XjlvPftOgWypQpd1gcbfJKAt7rsB8= +github.com/jhillyerd/enmime/v2 v2.2.0/go.mod h1:SOBXlCemjhiV2DvHhAKnJiWrtJGS/Ffuw4Iy7NjBTaI= 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/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/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.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.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/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= +github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -380,8 +442,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 v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ= -github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= +github.com/libdns/libdns v1.0.0 h1:IvYaz07JNz6jUQ4h/fv2R4sVnRnm77J/aOuC9B+TQTA= +github.com/libdns/libdns v1.0.0/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= @@ -389,30 +451,36 @@ github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o= 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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 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.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/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= +github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/meilisearch/meilisearch-go v0.33.0 h1:vDJtB5Ba5X0XaDmrazDiX89QEpi3zuee5Lvt7FJu7Q0= +github.com/meilisearch/meilisearch-go v0.33.0/go.mod h1:dY4nxhVc0Ext8Kn7u2YohJCsEjirg80DdcOmfNezUYg= 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/mholt/archives v0.1.3 h1:aEAaOtNra78G+TvV5ohmXrJOAzf++dIlYeDW3N9q458= +github.com/mholt/archives v0.1.3/go.mod h1:LUCGp++/IbV/I0Xq4SzcIR6uwgeh2yjnQWamjRQfLTU= 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/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY= -github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= +github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0= +github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc= +github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg= +github.com/minio/crc64nvme v1.0.2/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.94 h1:1ZoksIKPyaSt64AVOyaQvhDOgVC3MfZsWM6mZXRUGtM= -github.com/minio/minio-go/v7 v7.0.94/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc= +github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU= +github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo= +github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ= +github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= 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= @@ -428,16 +496,19 @@ github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidG 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.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= +github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0= +github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48= +github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U= +github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI= +github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= +github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw= +github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs= github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E= github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -455,9 +526,8 @@ 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/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/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= 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= @@ -472,6 +542,7 @@ 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.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= @@ -482,19 +553,21 @@ github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhi 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= -github.com/rhysd/actionlint v1.6.27/go.mod h1:m2nFUjAnOrxCMXuOMz9evYBRCLUsMnKY2IJl/N5umbk= +github.com/rhysd/actionlint v1.7.7 h1:0KgkoNTrYY7vmOCs9BW2AHxLvvpoY9nEUzgBHiPUr0k= +github.com/rhysd/actionlint v1.7.7/go.mod h1:AE6I6vJEkNaIfWqC2GNE5spIJNhxf8NCtLEKU4NnUXg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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/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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= 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= @@ -503,8 +576,10 @@ github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepq 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/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg= +github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk= 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= @@ -526,19 +601,16 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP 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/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I= -github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= +github.com/urfave/cli/v3 v3.4.1 h1:1M9UOCy5bLmGnuu1yn3t3CB4rG79Rtoxuv1sPhnm6qM= +github.com/urfave/cli/v3 v3.4.1/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= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= 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/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= @@ -546,8 +618,8 @@ github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBz github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= -github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= +github.com/yuin/goldmark v1.7.13/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= @@ -558,8 +630,12 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= 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.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I= +go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= +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.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= @@ -573,7 +649,11 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= 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= +go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= +go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= 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-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-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= @@ -583,23 +663,59 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY 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.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/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-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-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 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.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= -golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= +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.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4= +golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c= +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/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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.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.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +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/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-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-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -612,12 +728,21 @@ 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.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +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.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-20201020160332-67f06af15bc9/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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -625,15 +750,25 @@ 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.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-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-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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= @@ -645,7 +780,6 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220811171246-fbc7d0a398ab/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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -653,8 +787,8 @@ 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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.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= @@ -664,9 +798,12 @@ 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.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -676,31 +813,88 @@ 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.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/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +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.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.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-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-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-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-20200130002326-2f3ba24bd6e7/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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 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.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= 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= +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/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/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-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +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.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/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.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -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= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -728,6 +922,11 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C 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= 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.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= @@ -750,6 +949,9 @@ modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= 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/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/models/actions/artifact.go b/models/actions/artifact.go index 10cd3868a1..95c4bead8f 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -132,6 +132,13 @@ func (opts FindArtifactsOptions) ToConds() builder.Cond { return cond } +var _ db.FindOptionsOrder = FindArtifactsOptions{} + +// ToOrders implements db.FindOptionsOrder, to have a stable order +func (opts FindArtifactsOptions) ToOrders() string { + return "id" +} + // ActionArtifactMeta is the meta data of an artifact type ActionArtifactMeta struct { ArtifactName string diff --git a/models/actions/run.go b/models/actions/run.go index 69592120e9..6e0c70f8cf 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -21,7 +21,7 @@ import ( "forgejo.org/modules/util" webhook_module "forgejo.org/modules/webhook" - "github.com/nektos/act/pkg/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" "xorm.io/builder" ) diff --git a/models/actions/task.go b/models/actions/task.go index 93369db7e8..88b30196e3 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -17,8 +17,8 @@ import ( "forgejo.org/modules/timeutil" "forgejo.org/modules/util" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" lru "github.com/hashicorp/golang-lru/v2" - "github.com/nektos/act/pkg/jobparser" "xorm.io/builder" ) @@ -275,7 +275,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask } var workflowJob *jobparser.Job - if gots, err := jobparser.Parse(job.WorkflowPayload); err != nil { + if gots, err := jobparser.Parse(job.WorkflowPayload, false); err != nil { return nil, false, fmt.Errorf("parse workflow of job %d: %w", job.ID, err) } else if len(gots) != 1 { return nil, false, fmt.Errorf("workflow of job %d: not single workflow", job.ID) diff --git a/models/activities/action.go b/models/activities/action.go index 8592f81414..5067109545 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -26,6 +26,7 @@ import ( "forgejo.org/modules/base" "forgejo.org/modules/container" "forgejo.org/modules/git" + "forgejo.org/modules/json" "forgejo.org/modules/log" "forgejo.org/modules/setting" "forgejo.org/modules/structs" @@ -386,8 +387,21 @@ func (a *Action) IsIssueEvent() bool { // GetIssueInfos returns a list of associated information with the action. func (a *Action) GetIssueInfos() []string { + // Previously multiple pieces of data used to be encoded into a.Content by pipe-separating them, but this doesn't + // work well if some of the user-entered pieces of content (issue titles, comments, etc.) contain pipes. The newer + // storage format is to json-encode a string array, which we check for and prefer... then fallback to assuming old. + var ret []string + if strings.HasPrefix(a.Content, "[") && strings.HasSuffix(a.Content, "]") { + ret = make([]string, 0, 3) + err := json.Unmarshal([]byte(a.Content), &ret) + if err != nil { + log.Error("GetIssueInfos json decoding error: %v", err) + } + } else { + ret = strings.SplitN(a.Content, "|", 3) + } + // make sure it always returns 3 elements, because there are some access to the a[1] and a[2] without checking the length - ret := strings.SplitN(a.Content, "|", 3) for len(ret) < 3 { ret = append(ret, "") } @@ -473,8 +487,11 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err return nil, 0, err } + sess := db.GetEngine(ctx).Where(cond). + Select("`action`.*"). // this line will avoid select other joined table's columns + Join("INNER", "repository", "`repository`.id = `action`.repo_id") + opts.SetDefaultValues() - sess := db.GetEngine(ctx).Where(cond) sess = db.SetSessionPagination(sess, &opts) actions := make([]*Action, 0, opts.PageSize) @@ -767,7 +784,9 @@ func DeleteIssueActions(ctx context.Context, repoID, issueID, issueIndex int64) _, err := e.Where("repo_id = ?", repoID). In("op_type", ActionCreateIssue, ActionCreatePullRequest). - Where("content LIKE ?", strconv.FormatInt(issueIndex, 10)+"|%"). // "IssueIndex|content..." + Where(builder.Or( + builder.Like{"content", strconv.FormatInt(issueIndex, 10) + "|%"}, // "IssueIndex|content..." + builder.Like{"content", "[\"" + strconv.FormatInt(issueIndex, 10) + "\"%"})). // JSON, ["IssueIndex"... Delete(&Action{}) return err } diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 47dbd8ac2d..6911733a09 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -227,6 +227,24 @@ func TestNotifyWatchers(t *testing.T) { }) } +func TestGetFeedsCorrupted(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ + ID: 8, + RepoID: 1700, + }) + + actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + RequestedUser: user, + Actor: user, + IncludePrivate: true, + }) + require.NoError(t, err) + assert.Empty(t, actions) + assert.Equal(t, int64(0), count) +} + func TestConsistencyUpdateAction(t *testing.T) { if !setting.Database.Type.IsSQLite3() { t.Skip("Test is only for SQLite database.") @@ -290,14 +308,69 @@ func TestDeleteIssueActions(t *testing.T) { }) require.NoError(t, err) err = db.Insert(db.DefaultContext, &activities_model.Action{ - OpType: activities_model.ActionCreateIssue, - RepoID: issue.RepoID, + OpType: activities_model.ActionCreateIssue, + RepoID: issue.RepoID, + // Older Content format... Content: fmt.Sprintf("%d|content...", issue.Index), }) require.NoError(t, err) + err = db.Insert(db.DefaultContext, &activities_model.Action{ + OpType: activities_model.ActionCreateIssue, + RepoID: issue.RepoID, + // JSON-encoded Content format... + Content: fmt.Sprintf("[\"%d\",\"content...\"]", issue.Index), + }) + require.NoError(t, err) // assert that the actions exist, then delete them - unittest.AssertCount(t, &activities_model.Action{}, 2) + unittest.AssertCount(t, &activities_model.Action{}, 3) require.NoError(t, activities_model.DeleteIssueActions(db.DefaultContext, issue.RepoID, issue.ID, issue.Index)) unittest.AssertCount(t, &activities_model.Action{}, 0) } + +func TestGetIssueInfos(t *testing.T) { + tt := []struct { + content string + field1 string + field2 string + field3 string + }{ + { + content: "4|", + field1: "4", + }, + { + content: "2|docs: Add README w/ template sections", + field1: "2", + field2: "docs: Add README w/ template sections", + }, + { + content: "2|docs: Add README w/ template sections|Some comment...", + field1: "2", + field2: "docs: Add README w/ template sections", + field3: "Some comment...", + }, + { + content: "[\"4\"]", + field1: "4", + }, + { + content: "[\"2\", \"docs: Add README w/ | template sections\"]", + field1: "2", + field2: "docs: Add README w/ | template sections", + }, + { + content: "[\"2\", \"docs: Add README w/ | template sections\", \"Some | comment...\"]", + field1: "2", + field2: "docs: Add README w/ | template sections", + field3: "Some | comment...", + }, + } + for _, test := range tt { + action := &activities_model.Action{Content: test.content} + issueInfos := action.GetIssueInfos() + assert.Equal(t, test.field1, issueInfos[0]) + assert.Equal(t, test.field2, issueInfos[1]) + assert.Equal(t, test.field3, issueInfos[2]) + } +} diff --git a/models/asymkey/main_test.go b/models/asymkey/main_test.go index 316e8f1d54..aa13a93f0a 100644 --- a/models/asymkey/main_test.go +++ b/models/asymkey/main_test.go @@ -15,8 +15,6 @@ func TestMain(m *testing.M) { "gpg_key.yml", "public_key.yml", "TestParseCommitWithSSHSignature/public_key.yml", - "deploy_key.yml", - "gpg_key_import.yml", "user.yml", "email_address.yml", }, diff --git a/models/db/engine.go b/models/db/engine.go index 05a119b08d..42b9150696 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "forgejo.org/modules/container" "forgejo.org/modules/log" "forgejo.org/modules/setting" @@ -385,6 +386,15 @@ func (TracingHook) BeforeProcess(c *contexts.ContextHook) (context.Context, erro } func (TracingHook) AfterProcess(c *contexts.ContextHook) error { + if c.Result != nil { + if rowsAffected, err := c.Result.RowsAffected(); err == nil { + trace.Logf(c.Ctx, "rows affected", "%d", rowsAffected) + } + if lastID, err := c.Result.LastInsertId(); err == nil { + trace.Logf(c.Ctx, "last insert id", "%d", lastID) + } + } + c.Ctx.Value(sqlTask{}).(*trace.Task).End() return nil } @@ -438,3 +448,12 @@ func GetMasterEngine(x Engine) (*xorm.Engine, error) { return engine, nil } + +// GetTableNames returns the table name of all registered models. +func GetTableNames() container.Set[string] { + names := make(container.Set[string]) + for _, table := range tables { + names.Add(x.TableName(table)) + } + return names +} diff --git a/models/db/index.go b/models/db/index.go index 4c15dbe8a1..c069f0febd 100644 --- a/models/db/index.go +++ b/models/db/index.go @@ -72,14 +72,19 @@ func postgresGetNextResourceIndex(ctx context.Context, tableName string, groupID } func mysqlGetNextResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) { - if _, err := GetEngine(ctx).Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ - "VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", - tableName), groupID); err != nil { + res, err := GetEngine(ctx).Query(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ + "VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1 /*M!100500 RETURNING max_index */", + tableName), groupID) + if err != nil { return 0, err } + if len(res) > 0 { + return strconv.ParseInt(string(res[0]["max_index"]), 10, 64) + } + var idx int64 - _, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&idx) + _, err = GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&idx) if err != nil { return 0, err } diff --git a/models/db/table_names_test.go b/models/db/table_names_test.go new file mode 100644 index 0000000000..176ce9905d --- /dev/null +++ b/models/db/table_names_test.go @@ -0,0 +1,40 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package db + +import ( + "slices" + "testing" + + "forgejo.org/modules/test" + + "github.com/stretchr/testify/assert" +) + +func TestGetTableNames(t *testing.T) { + t.Run("Simple", func(t *testing.T) { + defer test.MockVariableValue(&tables, []any{new(GPGKey)})() + + assert.Equal(t, []string{"gpg_key"}, GetTableNames().Values()) + }) + + t.Run("Multiple tables", func(t *testing.T) { + defer test.MockVariableValue(&tables, []any{new(GPGKey), new(User), new(BlockedUser)})() + + tableNames := GetTableNames().Values() + slices.Sort(tableNames) + + assert.Equal(t, []string{"forgejo_blocked_user", "gpg_key", "user"}, tableNames) + }) +} + +type GPGKey struct{} + +type User struct{} + +type BlockedUser struct{} + +func (*BlockedUser) TableName() string { + return "forgejo_blocked_user" +} 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/action.yml b/models/fixtures/action.yml index a97e94fbf4..f1592d4569 100644 --- a/models/fixtures/action.yml +++ b/models/fixtures/action.yml @@ -59,6 +59,14 @@ created_unix: 1603011540 # grouped with id:7 - id: 8 + user_id: 1 + op_type: 12 # close issue + act_user_id: 1 + repo_id: 1700 # dangling intentional + is_private: false + created_unix: 1603011541 + +- id: 9 user_id: 34 op_type: 12 # close issue act_user_id: 34 diff --git a/models/fixtures/action_variable.yml b/models/fixtures/action_variable.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/action_variable.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/deploy_key.yml b/models/fixtures/deploy_key.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/deploy_key.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/external_login_user.yml b/models/fixtures/external_login_user.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/external_login_user.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/federated_user.yml b/models/fixtures/federated_user.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/federated_user.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/federation_host.yml b/models/fixtures/federation_host.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/federation_host.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/gpg_key_import.yml b/models/fixtures/gpg_key_import.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/gpg_key_import.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/label.yml b/models/fixtures/label.yml index acfac74968..84c2a7f418 100644 --- a/models/fixtures/label.yml +++ b/models/fixtures/label.yml @@ -3,6 +3,7 @@ repo_id: 1 org_id: 0 name: label1 + description: 'First label' color: '#abcdef' exclusive: false num_issues: 2 @@ -107,3 +108,26 @@ num_issues: 0 num_closed_issues: 0 archived_unix: 0 + +- + id: 11 + repo_id: 3 + org_id: 0 + name: " /'?&" + description: "Malicious label ' " + color: '#000000' + exclusive: true + num_issues: 0 + num_closed_issues: 0 + archived_unix: 0 + +- + id: 12 + repo_id: 3 + org_id: 0 + name: 'archived label<>' + color: '#000000' + exclusive: false + num_issues: 0 + num_closed_issues: 0 + archived_unix: 2991092130 diff --git a/models/fixtures/login_source.yml b/models/fixtures/login_source.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/login_source.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/protected_branch.yml b/models/fixtures/protected_branch.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/protected_branch.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/pull_auto_merge.yml b/models/fixtures/pull_auto_merge.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/pull_auto_merge.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/push_mirror.yml b/models/fixtures/push_mirror.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/push_mirror.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/repo_archiver.yml b/models/fixtures/repo_archiver.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/repo_archiver.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/repo_indexer_status.yml b/models/fixtures/repo_indexer_status.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/repo_indexer_status.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index c383fa43ac..2f104eed65 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -32,7 +32,7 @@ created_unix: 1731254961 updated_unix: 1731254961 topics: '[]' - + - id: 2 owner_id: 2 diff --git a/models/fixtures/secret.yml b/models/fixtures/secret.yml deleted file mode 100644 index ca780a73aa..0000000000 --- a/models/fixtures/secret.yml +++ /dev/null @@ -1 +0,0 @@ -[] # empty diff --git a/models/forgejo_migrations/main_test.go b/models/forgejo_migrations/main_test.go index 031fe8090d..2246e327f0 100644 --- a/models/forgejo_migrations/main_test.go +++ b/models/forgejo_migrations/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 94469b7371..71fcf16e7a 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "context" @@ -111,6 +111,14 @@ var migrations = []*Migration{ NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), // v35 -> v36 NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission), + // v36 -> v37 + NewMigration("Add `branch_filter` to `push_mirror` table", AddPushMirrorBranchFilter), + // v37 -> v38 + NewMigration("Add `resolved_unix` column to `abuse_report` table", AddResolvedUnixToAbuseReport), + // v38 -> v39 + NewMigration("Migrate `data` column of `secret` table to store keying material", MigrateActionSecretsToKeying), + // v39 -> v40 + NewMigration("Add index for release sha1", AddIndexForReleaseSha1), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/migrate_test.go b/models/forgejo_migrations/migrate_test.go index 20653929a3..9d16c9fe1c 100644 --- a/models/forgejo_migrations/migrate_test.go +++ b/models/forgejo_migrations/migrate_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v13.go b/models/forgejo_migrations/v13.go index 614f68249d..ba4183885e 100644 --- a/models/forgejo_migrations/v13.go +++ b/models/forgejo_migrations/v13.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v14.go b/models/forgejo_migrations/v14.go index 53f1ef2223..65b857d343 100644 --- a/models/forgejo_migrations/v14.go +++ b/models/forgejo_migrations/v14.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "forgejo.org/models/migrations/base" diff --git a/models/forgejo_migrations/v15.go b/models/forgejo_migrations/v15.go index 5e5588dd05..a63199ab19 100644 --- a/models/forgejo_migrations/v15.go +++ b/models/forgejo_migrations/v15.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "time" diff --git a/models/forgejo_migrations/v16.go b/models/forgejo_migrations/v16.go index f80bfc5268..a7d4d5d590 100644 --- a/models/forgejo_migrations/v16.go +++ b/models/forgejo_migrations/v16.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v17.go b/models/forgejo_migrations/v17.go index d6e2983d00..8ef6f2c681 100644 --- a/models/forgejo_migrations/v17.go +++ b/models/forgejo_migrations/v17.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v18.go b/models/forgejo_migrations/v18.go index e6c1493f0e..e39b0cbf10 100644 --- a/models/forgejo_migrations/v18.go +++ b/models/forgejo_migrations/v18.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v19.go b/models/forgejo_migrations/v19.go index 69b7746eb1..43d279dcb0 100644 --- a/models/forgejo_migrations/v19.go +++ b/models/forgejo_migrations/v19.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_20/v1.go b/models/forgejo_migrations/v1_20/v1.go index 72beaf23de..f0cb125557 100644 --- a/models/forgejo_migrations/v1_20/v1.go +++ b/models/forgejo_migrations/v1_20/v1.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_v1_20 //nolint:revive +package forgejo_v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v1_20/v2.go b/models/forgejo_migrations/v1_20/v2.go index 39f3b58924..3f79ac3801 100644 --- a/models/forgejo_migrations/v1_20/v2.go +++ b/models/forgejo_migrations/v1_20/v2.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -package forgejo_v1_20 //nolint:revive +package forgejo_v1_20 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_20/v3.go b/models/forgejo_migrations/v1_20/v3.go index cce227e6eb..49530df556 100644 --- a/models/forgejo_migrations/v1_20/v3.go +++ b/models/forgejo_migrations/v1_20/v3.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_v1_20 //nolint:revive +package forgejo_v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v1_22/main_test.go b/models/forgejo_migrations/v1_22/main_test.go index 03c4c5272c..d6a5bdacee 100644 --- a/models/forgejo_migrations/v1_22/main_test.go +++ b/models/forgejo_migrations/v1_22/main_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/forgejo_migrations/v1_22/v10.go b/models/forgejo_migrations/v1_22/v10.go index 819800ae71..cf45abdd24 100644 --- a/models/forgejo_migrations/v1_22/v10.go +++ b/models/forgejo_migrations/v1_22/v10.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v11.go b/models/forgejo_migrations/v1_22/v11.go index 17bb592379..f0f92bd04c 100644 --- a/models/forgejo_migrations/v1_22/v11.go +++ b/models/forgejo_migrations/v1_22/v11.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v1_22/v12.go b/models/forgejo_migrations/v1_22/v12.go index 6822524705..51354bd3c2 100644 --- a/models/forgejo_migrations/v1_22/v12.go +++ b/models/forgejo_migrations/v1_22/v12.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v4.go b/models/forgejo_migrations/v1_22/v4.go index f1195f5f66..499d377bb4 100644 --- a/models/forgejo_migrations/v1_22/v4.go +++ b/models/forgejo_migrations/v1_22/v4.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v5.go b/models/forgejo_migrations/v1_22/v5.go index 55f9fe1338..1671d3eed2 100644 --- a/models/forgejo_migrations/v1_22/v5.go +++ b/models/forgejo_migrations/v1_22/v5.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v6.go b/models/forgejo_migrations/v1_22/v6.go index 1a4874872c..072f8e6a15 100644 --- a/models/forgejo_migrations/v1_22/v6.go +++ b/models/forgejo_migrations/v1_22/v6.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v7.go b/models/forgejo_migrations/v1_22/v7.go index b42dd1af67..e7f6eb412b 100644 --- a/models/forgejo_migrations/v1_22/v7.go +++ b/models/forgejo_migrations/v1_22/v7.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v8.go b/models/forgejo_migrations/v1_22/v8.go index 2d3c0c594b..f23b00d2ad 100644 --- a/models/forgejo_migrations/v1_22/v8.go +++ b/models/forgejo_migrations/v1_22/v8.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "strings" diff --git a/models/forgejo_migrations/v1_22/v8_test.go b/models/forgejo_migrations/v1_22/v8_test.go index baaba7290f..5117dd2dfb 100644 --- a/models/forgejo_migrations/v1_22/v8_test.go +++ b/models/forgejo_migrations/v1_22/v8_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/forgejo_migrations/v1_22/v9.go b/models/forgejo_migrations/v1_22/v9.go index 34c2844c39..e3cdea97f2 100644 --- a/models/forgejo_migrations/v1_22/v9.go +++ b/models/forgejo_migrations/v1_22/v9.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v20.go b/models/forgejo_migrations/v20.go index 8ca9e91f73..91c7b8e911 100644 --- a/models/forgejo_migrations/v20.go +++ b/models/forgejo_migrations/v20.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v21.go b/models/forgejo_migrations/v21.go index 53f141b2ab..61d7950c5a 100644 --- a/models/forgejo_migrations/v21.go +++ b/models/forgejo_migrations/v21.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v22.go b/models/forgejo_migrations/v22.go index eeb738799c..8078591da6 100644 --- a/models/forgejo_migrations/v22.go +++ b/models/forgejo_migrations/v22.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v23.go b/models/forgejo_migrations/v23.go index 20a916a716..a79a4f3d6e 100644 --- a/models/forgejo_migrations/v23.go +++ b/models/forgejo_migrations/v23.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v24.go b/models/forgejo_migrations/v24.go index ebfb5fc1c4..084a57e1ce 100644 --- a/models/forgejo_migrations/v24.go +++ b/models/forgejo_migrations/v24.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v25.go b/models/forgejo_migrations/v25.go index 8e3032a40c..56cde499a3 100644 --- a/models/forgejo_migrations/v25.go +++ b/models/forgejo_migrations/v25.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "context" diff --git a/models/forgejo_migrations/v25_test.go b/models/forgejo_migrations/v25_test.go index e7402fd021..68e71da012 100644 --- a/models/forgejo_migrations/v25_test.go +++ b/models/forgejo_migrations/v25_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v26.go b/models/forgejo_migrations/v26.go index 3292d93ffd..a0c47799c2 100644 --- a/models/forgejo_migrations/v26.go +++ b/models/forgejo_migrations/v26.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v27.go b/models/forgejo_migrations/v27.go index 2efa3485a8..9cfbc64370 100644 --- a/models/forgejo_migrations/v27.go +++ b/models/forgejo_migrations/v27.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v28.go b/models/forgejo_migrations/v28.go index cba888d2ec..19f0dcd862 100644 --- a/models/forgejo_migrations/v28.go +++ b/models/forgejo_migrations/v28.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v29.go b/models/forgejo_migrations/v29.go index d0c2f723ae..92eb05e8b3 100644 --- a/models/forgejo_migrations/v29.go +++ b/models/forgejo_migrations/v29.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "database/sql" diff --git a/models/forgejo_migrations/v30.go b/models/forgejo_migrations/v30.go index 6c41a55316..05a1dff898 100644 --- a/models/forgejo_migrations/v30.go +++ b/models/forgejo_migrations/v30.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "time" diff --git a/models/forgejo_migrations/v30_test.go b/models/forgejo_migrations/v30_test.go index f826dab815..152fddeb47 100644 --- a/models/forgejo_migrations/v30_test.go +++ b/models/forgejo_migrations/v30_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v31.go b/models/forgejo_migrations/v31.go index fdcab21b1a..23397c7c13 100644 --- a/models/forgejo_migrations/v31.go +++ b/models/forgejo_migrations/v31.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v31_test.go b/models/forgejo_migrations/v31_test.go index 5b4aac2a60..6d1690aae0 100644 --- a/models/forgejo_migrations/v31_test.go +++ b/models/forgejo_migrations/v31_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v32.go b/models/forgejo_migrations/v32.go index bed335ab6b..ce3f855694 100644 --- a/models/forgejo_migrations/v32.go +++ b/models/forgejo_migrations/v32.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "encoding/xml" @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "forgejo.org/models/db" "forgejo.org/models/packages" "forgejo.org/modules/json" "forgejo.org/modules/log" @@ -52,55 +53,50 @@ type mavenPackageResult struct { // 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 { - sess := x.NewSession() - defer sess.Close() - - if err := sess.Begin(); err != nil { - return err - } - - // get unique owner IDs of Maven packages - var ownerIDs []*int64 - if err := sess. - 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(sess, id); err != nil { - log.Error("owner %d migration failed: %v", id, err) - return err // rollback all + 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 } - } - return sess.Commit() + 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(sess *xorm.Session, ownerID *int64) error { - results, err := getMavenPackageResultsToUpdate(sess, ownerID) +func fixMavenArtifactPerOwner(ctx context.Context, ownerID *int64) error { + results, err := getMavenPackageResultsToUpdate(ctx, ownerID) if err != nil { return err } - if err = resolvePackageCollisions(results, sess); err != nil { + if err = resolvePackageCollisions(ctx, results); err != nil { return err } - if err = processPackageVersions(results, sess); err != nil { + if err = processPackageVersions(ctx, results); err != nil { return err } - return processPackageFiles(results, sess) + 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(results []*mavenPackageResult, sess *xorm.Session) error { +func processPackageFiles(ctx context.Context, results []*mavenPackageResult) error { processedVersion := make(map[string][]*mavenPackageResult) for _, r := range results { @@ -113,7 +109,7 @@ func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) erro 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 := sess.Exec("UPDATE package_file SET version_id = ? WHERE version_id = ? and name like ?", r.PackageVersion.ID, r.PackageFile.VersionID, pattern); err != nil { + 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 } } @@ -128,14 +124,14 @@ func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) erro rs := packageResults[0] - pf, md, err := parseMetadata(sess, rs) + 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 := sess.ID(pf.ID).Cols("version_id").Update(pf); err != nil { + if _, err := db.GetEngine(ctx).ID(pf.ID).Cols("version_id").Update(pf); err != nil { return err } } @@ -150,11 +146,9 @@ func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) erro // 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(sess *xorm.Session, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { - ctx := context.Background() - +func parseMetadata(ctx context.Context, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { var pf packages.PackageFile - found, err := sess.Table(pf). + 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) @@ -183,7 +177,7 @@ func parseMetadata(sess *xorm.Session, snapshot *mavenPackageResult) (*packages. // 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(results []*mavenPackageResult, sess *xorm.Session) error { +func processPackageVersions(ctx context.Context, results []*mavenPackageResult) error { processedVersion := make(map[string]int64) for _, r := range results { @@ -196,14 +190,14 @@ func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) e // for non collisions, just update the metadata if r.PackageVersion.PackageID == r.Package.ID { - if _, err := sess.ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil { + 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 := sess.Insert(r.PackageVersion); err != nil { + if _, err := db.GetEngine(ctx).Insert(r.PackageVersion); err != nil { return err } } @@ -216,10 +210,9 @@ func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) e // 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(sess *xorm.Session, ownerID *int64) ([]*mavenPackageResult, error) { - ctx := context.Background() +func getMavenPackageResultsToUpdate(ctx context.Context, ownerID *int64) ([]*mavenPackageResult, error) { var candidates []*mavenPackageResult - if err := sess. + 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"). @@ -265,7 +258,7 @@ func getMavenPackageResultsToUpdate(sess *xorm.Session, ownerID *int64) ([]*mave // 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(results []*mavenPackageResult, sess *xorm.Session) error { +func resolvePackageCollisions(ctx context.Context, results []*mavenPackageResult) error { // Group new names by lowerName collisions := make(map[string][]string) for _, r := range results { @@ -292,7 +285,7 @@ func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) } else if list[0] == r.PackageName { pkgIDByName[r.PackageName] = r.Package.ID - if _, err = sess.ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { + if _, err = db.GetEngine(ctx).ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { return err } // create a new entry @@ -300,7 +293,7 @@ func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) log.Info("Create new maven package for %s", r.Package.Name) r.Package.ID = 0 - if _, err = sess.Insert(r.Package); err != nil { + if _, err = db.GetEngine(ctx).Insert(r.Package); err != nil { return err } diff --git a/models/forgejo_migrations/v32_test.go b/models/forgejo_migrations/v32_test.go index cd33de2608..24cda891bc 100644 --- a/models/forgejo_migrations/v32_test.go +++ b/models/forgejo_migrations/v32_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "bytes" diff --git a/models/forgejo_migrations/v33.go b/models/forgejo_migrations/v33.go index 272035fc23..b9ea8efe47 100644 --- a/models/forgejo_migrations/v33.go +++ b/models/forgejo_migrations/v33.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "fmt" diff --git a/models/forgejo_migrations/v33_test.go b/models/forgejo_migrations/v33_test.go index 664c704bbc..1d3298da15 100644 --- a/models/forgejo_migrations/v33_test.go +++ b/models/forgejo_migrations/v33_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v34.go b/models/forgejo_migrations/v34.go index 9e958b934f..d193d799e7 100644 --- a/models/forgejo_migrations/v34.go +++ b/models/forgejo_migrations/v34.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v35.go b/models/forgejo_migrations/v35.go index ca412d7951..9b389fcc12 100644 --- a/models/forgejo_migrations/v35.go +++ b/models/forgejo_migrations/v35.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v36.go b/models/forgejo_migrations/v36.go index 019c177a08..1a798147cf 100644 --- a/models/forgejo_migrations/v36.go +++ b/models/forgejo_migrations/v36.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v37.go b/models/forgejo_migrations/v37.go new file mode 100644 index 0000000000..89358991af --- /dev/null +++ b/models/forgejo_migrations/v37.go @@ -0,0 +1,16 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations + +import ( + "xorm.io/xorm" +) + +func AddPushMirrorBranchFilter(x *xorm.Engine) error { + type PushMirror struct { + ID int64 `xorm:"pk autoincr"` + BranchFilter string `xorm:"VARCHAR(255)"` + } + return x.Sync2(new(PushMirror)) +} diff --git a/models/forgejo_migrations/v38.go b/models/forgejo_migrations/v38.go new file mode 100644 index 0000000000..24240f15a0 --- /dev/null +++ b/models/forgejo_migrations/v38.go @@ -0,0 +1,19 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations + +import ( + "forgejo.org/modules/timeutil" + + "xorm.io/xorm" +) + +func AddResolvedUnixToAbuseReport(x *xorm.Engine) error { + type AbuseReport struct { + ID int64 `xorm:"pk autoincr"` + ResolvedUnix timeutil.TimeStamp `xorm:"DEFAULT NULL"` + } + + return x.Sync(&AbuseReport{}) +} diff --git a/models/forgejo_migrations/v39.go b/models/forgejo_migrations/v39.go new file mode 100644 index 0000000000..9af1c250b3 --- /dev/null +++ b/models/forgejo_migrations/v39.go @@ -0,0 +1,78 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations + +import ( + "context" + "fmt" + + "forgejo.org/models/db" + secret_model "forgejo.org/models/secret" + "forgejo.org/modules/log" + "forgejo.org/modules/secret" + "forgejo.org/modules/setting" + + "xorm.io/xorm" + "xorm.io/xorm/schemas" +) + +func MigrateActionSecretsToKeying(x *xorm.Engine) error { + return db.WithTx(db.DefaultContext, func(ctx context.Context) error { + sess := db.GetEngine(ctx) + + switch x.Dialect().URI().DBType { + case schemas.MYSQL: + if _, err := sess.Exec("ALTER TABLE `secret` MODIFY `data` BLOB"); err != nil { + return err + } + case schemas.SQLITE: + if _, err := sess.Exec("ALTER TABLE `secret` RENAME COLUMN `data` TO `data_backup`"); err != nil { + return err + } + if _, err := sess.Exec("ALTER TABLE `secret` ADD COLUMN `data` BLOB"); err != nil { + return err + } + if _, err := sess.Exec("UPDATE `secret` SET `data` = `data_backup`"); err != nil { + return err + } + if _, err := sess.Exec("ALTER TABLE `secret` DROP COLUMN `data_backup`"); err != nil { + return err + } + case schemas.POSTGRES: + if _, err := sess.Exec("ALTER TABLE `secret` ALTER COLUMN `data` SET DATA TYPE bytea USING data::text::bytea"); err != nil { + return err + } + } + + oldEncryptionKey := setting.SecretKey + + messages := make([]string, 0, 100) + ids := make([]int64, 0, 100) + + err := db.Iterate(ctx, nil, func(ctx context.Context, bean *secret_model.Secret) error { + secretBytes, err := secret.DecryptSecret(oldEncryptionKey, string(bean.Data)) + if err != nil { + messages = append(messages, fmt.Sprintf("secret.id=%d, secret.name=%q, secret.repo_id=%d, secret.owner_id=%d: secret.DecryptSecret(): %v", bean.ID, bean.Name, bean.RepoID, bean.OwnerID, err)) + ids = append(ids, bean.ID) + return nil + } + + bean.SetSecret(secretBytes) + _, err = sess.Cols("data").ID(bean.ID).Update(bean) + return err + }) + + if err == nil { + if len(ids) > 0 { + log.Error("Forgejo migration[37]: The following action secrets were found to be corrupted and removed from the database.") + for _, message := range messages { + log.Error("Forgejo migration[37]: %s", message) + } + + _, err = sess.In("id", ids).NoAutoCondition().NoAutoTime().Delete(&secret_model.Secret{}) + } + } + return err + }) +} diff --git a/models/forgejo_migrations/v39_test.go b/models/forgejo_migrations/v39_test.go new file mode 100644 index 0000000000..42934d912f --- /dev/null +++ b/models/forgejo_migrations/v39_test.go @@ -0,0 +1,52 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations + +import ( + "testing" + + migration_tests "forgejo.org/models/migrations/test" + "forgejo.org/models/secret" + "forgejo.org/modules/keying" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_MigrateActionSecretToKeying(t *testing.T) { + type Secret struct { + ID int64 + OwnerID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL"` + RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL DEFAULT 0"` + Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"` + Data string `xorm:"LONGTEXT"` // encrypted data + CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` + } + + // Prepare and load the testing database + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Secret)) + defer deferable() + if x == nil || t.Failed() { + return + } + + cnt, err := x.Table("secret").Count() + require.NoError(t, err) + assert.EqualValues(t, 2, cnt) + + require.NoError(t, MigrateActionSecretsToKeying(x)) + + cnt, err = x.Table("secret").Count() + require.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var secret secret.Secret + _, err = x.Table("secret").ID(1).Get(&secret) + require.NoError(t, err) + + secretBytes, err := keying.DeriveKey(keying.ContextActionSecret).Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) + require.NoError(t, err) + assert.Equal(t, []byte("A deep dark secret"), secretBytes) +} diff --git a/models/forgejo_migrations/v40.go b/models/forgejo_migrations/v40.go new file mode 100644 index 0000000000..11e8fbd85e --- /dev/null +++ b/models/forgejo_migrations/v40.go @@ -0,0 +1,13 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forgejo_migrations + +import "xorm.io/xorm" + +func AddIndexForReleaseSha1(x *xorm.Engine) error { + type Release struct { + Sha1 string `xorm:"INDEX VARCHAR(64)"` + } + return x.Sync(new(Release)) +} diff --git a/models/issues/action_aggregator.go b/models/issues/action_aggregator.go index d3643adeef..c4632fd4dd 100644 --- a/models/issues/action_aggregator.go +++ b/models/issues/action_aggregator.go @@ -4,6 +4,7 @@ package issues import ( + "context" "slices" "forgejo.org/models/organization" @@ -374,3 +375,10 @@ func (t *RequestReviewTarget) Type() string { } return "team" } + +func (t *RequestReviewTarget) Link(ctx context.Context) string { + if t.User != nil { + return t.User.HomeLink() + } + return t.Team.Link(ctx) +} diff --git a/models/issues/action_aggregator_test.go b/models/issues/action_aggregator_test.go new file mode 100644 index 0000000000..1962596d2d --- /dev/null +++ b/models/issues/action_aggregator_test.go @@ -0,0 +1,37 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package issues + +import ( + "testing" + + "forgejo.org/models/db" + org_model "forgejo.org/models/organization" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + + "github.com/stretchr/testify/assert" +) + +func TestRequestReviewTarget(t *testing.T) { + unittest.PrepareTestEnv(t) + + target := RequestReviewTarget{User: &user_model.User{ID: 1, Name: "user1"}} + assert.Equal(t, int64(1), target.ID()) + assert.Equal(t, "user1", target.Name()) + assert.Equal(t, "user", target.Type()) + assert.Equal(t, "/user1", target.Link(db.DefaultContext)) + + target = RequestReviewTarget{Team: &org_model.Team{ID: 2, Name: "Collaborators", OrgID: 3}} + assert.Equal(t, int64(2), target.ID()) + assert.Equal(t, "Collaborators", target.Name()) + assert.Equal(t, "team", target.Type()) + assert.Equal(t, "/org/org3/teams/Collaborators", target.Link(db.DefaultContext)) + + target = RequestReviewTarget{Team: org_model.NewGhostTeam()} + assert.Equal(t, int64(-1), target.ID()) + assert.Equal(t, "Ghost team", target.Name()) + assert.Equal(t, "team", target.Type()) + assert.Empty(t, target.Link(db.DefaultContext)) +} diff --git a/models/issues/comment.go b/models/issues/comment.go index a81221caf4..a84bbffd08 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -795,17 +795,16 @@ func (c *Comment) LoadPushCommits(ctx context.Context) (err error) { } c.OldCommit = data.CommitIDs[0] c.NewCommit = data.CommitIDs[1] - } else { - gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, c.Issue.Repo) - if err != nil { - return err - } - defer closer.Close() - - c.Commits = git_model.ParseCommitsWithStatus(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo) - c.CommitsNum = int64(len(c.Commits)) } + gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, c.Issue.Repo) + if err != nil { + return err + } + defer closer.Close() + c.Commits = git_model.ParseCommitsWithStatus(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo) + c.CommitsNum = int64(len(c.Commits)) + return err } @@ -1156,7 +1155,7 @@ func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *us defer committer.Close() // If the comment was reported as abusive, a shadow copy should be created before first update. - if err := IfNeededCreateShadowCopyForComment(ctx, c); err != nil { + if err := IfNeededCreateShadowCopyForComment(ctx, c, true); err != nil { return err } @@ -1197,7 +1196,7 @@ func DeleteComment(ctx context.Context, comment *Comment) error { e := db.GetEngine(ctx) // If the comment was reported as abusive, a shadow copy should be created before deletion. - if err := IfNeededCreateShadowCopyForComment(ctx, comment); err != nil { + if err := IfNeededCreateShadowCopyForComment(ctx, comment, false); err != nil { return err } diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 91a69c26a7..529f0c15d4 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -48,7 +48,9 @@ type IssuesOptions struct { //nolint UpdatedBeforeUnix int64 // prioritize issues from this repo PriorityRepoID int64 - IsArchived optional.Option[bool] + // if this issue index (not ID) exists and matches the filters, *and* priorityrepo sort is used, show it first + PriorityIssueIndex int64 + IsArchived optional.Option[bool] // If combined with AllPublic, then private as well as public issues // that matches the criteria will be returned, if AllPublic is false @@ -60,7 +62,7 @@ type IssuesOptions struct { //nolint // applySorts sort an issues-related session based on the provided // sortType string -func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { +func applySorts(sess *xorm.Session, sortType string, priorityRepoID, priorityIssueIndex int64) { switch sortType { case "oldest": sess.Asc("issue.created_unix").Asc("issue.id") @@ -97,8 +99,11 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { case "priorityrepo": sess.OrderBy("CASE "+ "WHEN issue.repo_id = ? THEN 1 "+ - "ELSE 2 END ASC", priorityRepoID). - Desc("issue.created_unix"). + "ELSE 2 END ASC", priorityRepoID) + if priorityIssueIndex != 0 { + sess.OrderBy("issue.index = ? DESC", priorityIssueIndex) + } + sess.Desc("issue.created_unix"). Desc("issue.id") case "project-column-sorting": sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id") @@ -470,7 +475,7 @@ func Issues(ctx context.Context, opts *IssuesOptions) (IssueList, error) { Join("INNER", "repository", "`issue`.repo_id = `repository`.id") applyLimit(sess, opts) applyConditions(sess, opts) - applySorts(sess, opts.SortType, opts.PriorityRepoID) + applySorts(sess, opts.SortType, opts.PriorityRepoID, opts.PriorityIssueIndex) issues := IssueList{} if err := sess.Find(&issues); err != nil { @@ -494,7 +499,7 @@ func IssueIDs(ctx context.Context, opts *IssuesOptions, otherConds ...builder.Co } applyLimit(sess, opts) - applySorts(sess, opts.SortType, opts.PriorityRepoID) + applySorts(sess, opts.SortType, opts.PriorityRepoID, opts.PriorityIssueIndex) var res []int64 total, err := sess.Select("`issue`.id").Table(&Issue{}).FindAndCount(&res) diff --git a/models/issues/moderation.go b/models/issues/moderation.go index 635d295db0..9afb711d65 100644 --- a/models/issues/moderation.go +++ b/models/issues/moderation.go @@ -5,6 +5,7 @@ package issues import ( "context" + "strconv" "forgejo.org/models/moderation" "forgejo.org/modules/json" @@ -24,6 +25,21 @@ type IssueData struct { UpdatedUnix timeutil.TimeStamp } +// Implements GetFieldsMap() from ShadowCopyData interface, returning a list of pairs +// to be used when rendering the shadow copy for admins reviewing the corresponding abuse report(s). +func (cd IssueData) GetFieldsMap() []moderation.ShadowCopyField { + return []moderation.ShadowCopyField{ + {Key: "RepoID", Value: strconv.FormatInt(cd.RepoID, 10)}, + {Key: "Index", Value: strconv.FormatInt(cd.Index, 10)}, + {Key: "PosterID", Value: strconv.FormatInt(cd.PosterID, 10)}, + {Key: "Title", Value: cd.Title}, + {Key: "Content", Value: cd.Content}, + {Key: "ContentVersion", Value: strconv.Itoa(cd.ContentVersion)}, + {Key: "CreatedUnix", Value: cd.CreatedUnix.AsLocalTime().String()}, + {Key: "UpdatedUnix", Value: cd.UpdatedUnix.AsLocalTime().String()}, + } +} + // newIssueData creates a trimmed down issue to be used just to create a JSON structure // (keeping only the fields relevant for moderation purposes) func newIssueData(issue *Issue) IssueData { @@ -31,8 +47,8 @@ func newIssueData(issue *Issue) IssueData { RepoID: issue.RepoID, Index: issue.Index, PosterID: issue.PosterID, - Content: issue.Content, Title: issue.Title, + Content: issue.Content, ContentVersion: issue.ContentVersion, CreatedUnix: issue.CreatedUnix, UpdatedUnix: issue.UpdatedUnix, @@ -50,6 +66,19 @@ type CommentData struct { UpdatedUnix timeutil.TimeStamp } +// Implements GetFieldsMap() from ShadowCopyData interface, returning a list of pairs +// to be used when rendering the shadow copy for admins reviewing the corresponding abuse report(s). +func (cd CommentData) GetFieldsMap() []moderation.ShadowCopyField { + return []moderation.ShadowCopyField{ + {Key: "PosterID", Value: strconv.FormatInt(cd.PosterID, 10)}, + {Key: "IssueID", Value: strconv.FormatInt(cd.IssueID, 10)}, + {Key: "Content", Value: cd.Content}, + {Key: "ContentVersion", Value: strconv.Itoa(cd.ContentVersion)}, + {Key: "CreatedUnix", Value: cd.CreatedUnix.AsLocalTime().String()}, + {Key: "UpdatedUnix", Value: cd.UpdatedUnix.AsLocalTime().String()}, + } +} + // newCommentData creates a trimmed down comment to be used just to create a JSON structure // (keeping only the fields relevant for moderation purposes) func newCommentData(comment *Comment) CommentData { @@ -87,13 +116,19 @@ func IfNeededCreateShadowCopyForIssue(ctx context.Context, issue *Issue) error { // IfNeededCreateShadowCopyForComment checks if for the given comment there are any reports of abusive content submitted // and if found a shadow copy of relevant comment fields will be stored into DB and linked to the above report(s). // This function should be called before a comment is deleted or updated. -func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment) error { +func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment, forUpdates bool) error { shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeComment, comment.ID) if err != nil { return err } if shadowCopyNeeded { + if forUpdates { + // get the unaltered comment fields (for updates the provided variable is already altered but not yet saved) + if comment, err = GetCommentByID(ctx, comment.ID); err != nil { + return err + } + } commentData := newCommentData(comment) content, err := json.Marshal(commentData) if err != nil { diff --git a/models/issues/moderation_test.go b/models/issues/moderation_test.go new file mode 100644 index 0000000000..adb07bd63a --- /dev/null +++ b/models/issues/moderation_test.go @@ -0,0 +1,70 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package issues_test + +import ( + "testing" + + "forgejo.org/models/issues" + "forgejo.org/models/moderation" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +const ( + tsCreated timeutil.TimeStamp = timeutil.TimeStamp(1753093500) // 2025-07-21 10:25:00 UTC + tsUpdated timeutil.TimeStamp = timeutil.TimeStamp(1753093525) // 2025-07-21 10:25:25 UTC +) + +func testShadowCopyField(t *testing.T, scField moderation.ShadowCopyField, key, value string) { + assert.Equal(t, key, scField.Key) + assert.Equal(t, value, scField.Value) +} + +func TestIssueDataGetFieldsMap(t *testing.T) { + id := issues.IssueData{ + RepoID: 2001, + Index: 2, + PosterID: 1002, + Title: "Professional marketing services", + Content: "Visit my website at promote-your-business.biz for a list of available services.", + ContentVersion: 0, + CreatedUnix: tsCreated, + UpdatedUnix: tsUpdated, + } + scFields := id.GetFieldsMap() + + if assert.Len(t, scFields, 8) { + testShadowCopyField(t, scFields[0], "RepoID", "2001") + testShadowCopyField(t, scFields[1], "Index", "2") + testShadowCopyField(t, scFields[2], "PosterID", "1002") + testShadowCopyField(t, scFields[3], "Title", "Professional marketing services") + testShadowCopyField(t, scFields[4], "Content", "Visit my website at promote-your-business.biz for a list of available services.") + testShadowCopyField(t, scFields[5], "ContentVersion", "0") + testShadowCopyField(t, scFields[6], "CreatedUnix", tsCreated.AsLocalTime().String()) + testShadowCopyField(t, scFields[7], "UpdatedUnix", tsUpdated.AsLocalTime().String()) + } +} + +func TestCommentDataGetFieldsMap(t *testing.T) { + cd := issues.CommentData{ + PosterID: 1002, + IssueID: 3001, + Content: "Check out [alexsmith/website](/alexsmith/website)", + ContentVersion: 0, + CreatedUnix: tsCreated, + UpdatedUnix: tsUpdated, + } + scFields := cd.GetFieldsMap() + + if assert.Len(t, scFields, 6) { + testShadowCopyField(t, scFields[0], "PosterID", "1002") + testShadowCopyField(t, scFields[1], "IssueID", "3001") + testShadowCopyField(t, scFields[2], "Content", "Check out [alexsmith/website](/alexsmith/website)") + testShadowCopyField(t, scFields[3], "ContentVersion", "0") + testShadowCopyField(t, scFields[4], "CreatedUnix", tsCreated.AsLocalTime().String()) + testShadowCopyField(t, scFields[5], "UpdatedUnix", tsUpdated.AsLocalTime().String()) + } +} diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index 8fc0491026..ddb813cf44 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -149,7 +149,7 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio } findSession := listPullRequestStatement(ctx, baseRepoID, opts) - applySorts(findSession, opts.SortType, 0) + applySorts(findSession, opts.SortType, 0, 0) findSession = db.SetSessionPagination(findSession, opts) prs := make([]*PullRequest, 0, opts.PageSize) found := findSession.Find(&prs) diff --git a/models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml b/models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml new file mode 100644 index 0000000000..908b428321 --- /dev/null +++ b/models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml @@ -0,0 +1,14 @@ +- + id: 1 + owner_id: 2 + repo_id: 1 + name: SECRET_1 + data: 02458e5f341b2d5081a31283559843b6b7543ab98ed213d2b15b5cef94385fa348afa7e0875122e9 + created_unix: 1753556968 +- + id: 2 + owner_id: 2 + repo_id: 1 + name: BADBAD + data: badbad + created_unix: 1753556968 diff --git a/models/migrations/test/tests.go b/models/migrations/test/tests.go index c1f0caf19b..6be3b3c2fc 100644 --- a/models/migrations/test/tests.go +++ b/models/migrations/test/tests.go @@ -95,7 +95,8 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu t.Logf("initializing fixtures from: %s", fixturesDir) if err := unittest.InitFixtures( unittest.FixturesOptions{ - Dir: fixturesDir, + Dir: fixturesDir, + SkipCleanRegistedModels: true, }, x); err != nil { t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err) return x, deferFn diff --git a/models/migrations/v1_10/v100.go b/models/migrations/v1_10/v100.go index 5d2fd8e244..1742bea296 100644 --- a/models/migrations/v1_10/v100.go +++ b/models/migrations/v1_10/v100.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "net/url" diff --git a/models/migrations/v1_10/v101.go b/models/migrations/v1_10/v101.go index f023a2a0e7..6c8dfe2486 100644 --- a/models/migrations/v1_10/v101.go +++ b/models/migrations/v1_10/v101.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_10/v88.go b/models/migrations/v1_10/v88.go index 7e86ac364f..eb8e81c19e 100644 --- a/models/migrations/v1_10/v88.go +++ b/models/migrations/v1_10/v88.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "crypto/sha1" diff --git a/models/migrations/v1_10/v89.go b/models/migrations/v1_10/v89.go index d5f27ffdc6..0df2a6e17b 100644 --- a/models/migrations/v1_10/v89.go +++ b/models/migrations/v1_10/v89.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v90.go b/models/migrations/v1_10/v90.go index 295d4b1c1b..5521a97e32 100644 --- a/models/migrations/v1_10/v90.go +++ b/models/migrations/v1_10/v90.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v91.go b/models/migrations/v1_10/v91.go index 48cac2de70..08db6c2742 100644 --- a/models/migrations/v1_10/v91.go +++ b/models/migrations/v1_10/v91.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v92.go b/models/migrations/v1_10/v92.go index 9080108594..b6c04a9234 100644 --- a/models/migrations/v1_10/v92.go +++ b/models/migrations/v1_10/v92.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "xorm.io/builder" diff --git a/models/migrations/v1_10/v93.go b/models/migrations/v1_10/v93.go index ee59a8db39..c131be9a8d 100644 --- a/models/migrations/v1_10/v93.go +++ b/models/migrations/v1_10/v93.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v94.go b/models/migrations/v1_10/v94.go index c131af162b..13b7d7b303 100644 --- a/models/migrations/v1_10/v94.go +++ b/models/migrations/v1_10/v94.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v95.go b/models/migrations/v1_10/v95.go index 3b1f67fd9c..86b52026bf 100644 --- a/models/migrations/v1_10/v95.go +++ b/models/migrations/v1_10/v95.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v96.go b/models/migrations/v1_10/v96.go index 3bfb770f24..bcbd618b49 100644 --- a/models/migrations/v1_10/v96.go +++ b/models/migrations/v1_10/v96.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "path/filepath" diff --git a/models/migrations/v1_10/v97.go b/models/migrations/v1_10/v97.go index dee45b32e3..5872bb63e5 100644 --- a/models/migrations/v1_10/v97.go +++ b/models/migrations/v1_10/v97.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v98.go b/models/migrations/v1_10/v98.go index bdd9aed089..d21c326459 100644 --- a/models/migrations/v1_10/v98.go +++ b/models/migrations/v1_10/v98.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v99.go b/models/migrations/v1_10/v99.go index 7f287b77aa..addae66be9 100644 --- a/models/migrations/v1_10/v99.go +++ b/models/migrations/v1_10/v99.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_11/v102.go b/models/migrations/v1_11/v102.go index a585d9c423..15f0c83c36 100644 --- a/models/migrations/v1_11/v102.go +++ b/models/migrations/v1_11/v102.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_11/v103.go b/models/migrations/v1_11/v103.go index 53527dac58..a515710160 100644 --- a/models/migrations/v1_11/v103.go +++ b/models/migrations/v1_11/v103.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v104.go b/models/migrations/v1_11/v104.go index af3578ca4a..7461f0cda3 100644 --- a/models/migrations/v1_11/v104.go +++ b/models/migrations/v1_11/v104.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_11/v105.go b/models/migrations/v1_11/v105.go index b91340c30a..d86973a0f6 100644 --- a/models/migrations/v1_11/v105.go +++ b/models/migrations/v1_11/v105.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v106.go b/models/migrations/v1_11/v106.go index ecb11cdd1e..edffe18683 100644 --- a/models/migrations/v1_11/v106.go +++ b/models/migrations/v1_11/v106.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v107.go b/models/migrations/v1_11/v107.go index f0bfe5862c..a158e3bb50 100644 --- a/models/migrations/v1_11/v107.go +++ b/models/migrations/v1_11/v107.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v108.go b/models/migrations/v1_11/v108.go index a85096234d..8f14504ceb 100644 --- a/models/migrations/v1_11/v108.go +++ b/models/migrations/v1_11/v108.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v109.go b/models/migrations/v1_11/v109.go index ea565ccda3..f7616aec7b 100644 --- a/models/migrations/v1_11/v109.go +++ b/models/migrations/v1_11/v109.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v110.go b/models/migrations/v1_11/v110.go index fce9be847e..e94a738f67 100644 --- a/models/migrations/v1_11/v110.go +++ b/models/migrations/v1_11/v110.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go index cc3dc0d545..6f531e4858 100644 --- a/models/migrations/v1_11/v111.go +++ b/models/migrations/v1_11/v111.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "fmt" diff --git a/models/migrations/v1_11/v112.go b/models/migrations/v1_11/v112.go index 6112ab51a5..22054e6f68 100644 --- a/models/migrations/v1_11/v112.go +++ b/models/migrations/v1_11/v112.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "fmt" diff --git a/models/migrations/v1_11/v113.go b/models/migrations/v1_11/v113.go index dea344a44f..a4d54f66fb 100644 --- a/models/migrations/v1_11/v113.go +++ b/models/migrations/v1_11/v113.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "fmt" diff --git a/models/migrations/v1_11/v114.go b/models/migrations/v1_11/v114.go index 95adcee989..9467a8a90c 100644 --- a/models/migrations/v1_11/v114.go +++ b/models/migrations/v1_11/v114.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "net/url" diff --git a/models/migrations/v1_11/v115.go b/models/migrations/v1_11/v115.go index 3d4b41017b..65094df93d 100644 --- a/models/migrations/v1_11/v115.go +++ b/models/migrations/v1_11/v115.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "crypto/md5" diff --git a/models/migrations/v1_11/v116.go b/models/migrations/v1_11/v116.go index 85aa76c1e0..729fbad18b 100644 --- a/models/migrations/v1_11/v116.go +++ b/models/migrations/v1_11/v116.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v117.go b/models/migrations/v1_12/v117.go index 8eadcdef2b..73b58ca34b 100644 --- a/models/migrations/v1_12/v117.go +++ b/models/migrations/v1_12/v117.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v118.go b/models/migrations/v1_12/v118.go index eb022dc5e4..e8b4249743 100644 --- a/models/migrations/v1_12/v118.go +++ b/models/migrations/v1_12/v118.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v119.go b/models/migrations/v1_12/v119.go index 60bfe6a57d..b4bf29a935 100644 --- a/models/migrations/v1_12/v119.go +++ b/models/migrations/v1_12/v119.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v120.go b/models/migrations/v1_12/v120.go index 3f7ed8d373..14d515f5a7 100644 --- a/models/migrations/v1_12/v120.go +++ b/models/migrations/v1_12/v120.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v121.go b/models/migrations/v1_12/v121.go index 175ec9164d..a28ae4e1c9 100644 --- a/models/migrations/v1_12/v121.go +++ b/models/migrations/v1_12/v121.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import "xorm.io/xorm" diff --git a/models/migrations/v1_12/v122.go b/models/migrations/v1_12/v122.go index 6e31d863a1..bc1b175f6a 100644 --- a/models/migrations/v1_12/v122.go +++ b/models/migrations/v1_12/v122.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v123.go b/models/migrations/v1_12/v123.go index b0c3af07a3..52b10bb850 100644 --- a/models/migrations/v1_12/v123.go +++ b/models/migrations/v1_12/v123.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v124.go b/models/migrations/v1_12/v124.go index d2ba03ffe0..9a93f436d4 100644 --- a/models/migrations/v1_12/v124.go +++ b/models/migrations/v1_12/v124.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v125.go b/models/migrations/v1_12/v125.go index ec4ffaab25..7f582ecff5 100644 --- a/models/migrations/v1_12/v125.go +++ b/models/migrations/v1_12/v125.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v126.go b/models/migrations/v1_12/v126.go index ca9ec3aa3f..64fd7f7478 100644 --- a/models/migrations/v1_12/v126.go +++ b/models/migrations/v1_12/v126.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/builder" diff --git a/models/migrations/v1_12/v127.go b/models/migrations/v1_12/v127.go index 11a4042973..f686fa617c 100644 --- a/models/migrations/v1_12/v127.go +++ b/models/migrations/v1_12/v127.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go index 6d7307f470..8fca974616 100644 --- a/models/migrations/v1_12/v128.go +++ b/models/migrations/v1_12/v128.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v129.go b/models/migrations/v1_12/v129.go index cf228242b9..3e4d3aca68 100644 --- a/models/migrations/v1_12/v129.go +++ b/models/migrations/v1_12/v129.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v130.go b/models/migrations/v1_12/v130.go index bfa856796a..383ef47492 100644 --- a/models/migrations/v1_12/v130.go +++ b/models/migrations/v1_12/v130.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "forgejo.org/modules/json" diff --git a/models/migrations/v1_12/v131.go b/models/migrations/v1_12/v131.go index 5184bc3590..1266c2f185 100644 --- a/models/migrations/v1_12/v131.go +++ b/models/migrations/v1_12/v131.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v132.go b/models/migrations/v1_12/v132.go index 3b2b28f7ab..8b1ae6db93 100644 --- a/models/migrations/v1_12/v132.go +++ b/models/migrations/v1_12/v132.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v133.go b/models/migrations/v1_12/v133.go index c9087fc8c1..69e20597d8 100644 --- a/models/migrations/v1_12/v133.go +++ b/models/migrations/v1_12/v133.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import "xorm.io/xorm" diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go index bba996fd40..1fabdcae96 100644 --- a/models/migrations/v1_12/v134.go +++ b/models/migrations/v1_12/v134.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v135.go b/models/migrations/v1_12/v135.go index 8898011df5..5df0ad7fc4 100644 --- a/models/migrations/v1_12/v135.go +++ b/models/migrations/v1_12/v135.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v136.go b/models/migrations/v1_12/v136.go index e2557ae002..7d246a82be 100644 --- a/models/migrations/v1_12/v136.go +++ b/models/migrations/v1_12/v136.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v137.go b/models/migrations/v1_12/v137.go index 0d86b72010..9d38483488 100644 --- a/models/migrations/v1_12/v137.go +++ b/models/migrations/v1_12/v137.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v138.go b/models/migrations/v1_12/v138.go index 8c8d353f40..4485adeb2d 100644 --- a/models/migrations/v1_12/v138.go +++ b/models/migrations/v1_12/v138.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go index cd7963524e..51e57b984a 100644 --- a/models/migrations/v1_12/v139.go +++ b/models/migrations/v1_12/v139.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_13/v140.go b/models/migrations/v1_13/v140.go index d74f808e9f..5bb612c098 100644 --- a/models/migrations/v1_13/v140.go +++ b/models/migrations/v1_13/v140.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v141.go b/models/migrations/v1_13/v141.go index ae211e0e44..b54bc1727c 100644 --- a/models/migrations/v1_13/v141.go +++ b/models/migrations/v1_13/v141.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v142.go b/models/migrations/v1_13/v142.go index 7490e0f3b4..8939f6f2f8 100644 --- a/models/migrations/v1_13/v142.go +++ b/models/migrations/v1_13/v142.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_13/v143.go b/models/migrations/v1_13/v143.go index 1f9120e2ba..6a8da8b06d 100644 --- a/models/migrations/v1_13/v143.go +++ b/models/migrations/v1_13/v143.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_13/v144.go b/models/migrations/v1_13/v144.go index 7e801eab8a..f138338514 100644 --- a/models/migrations/v1_13/v144.go +++ b/models/migrations/v1_13/v144.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go index a01f577ed1..f7d3895c84 100644 --- a/models/migrations/v1_13/v145.go +++ b/models/migrations/v1_13/v145.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v146.go b/models/migrations/v1_13/v146.go index a1b54ee3aa..e6a476a288 100644 --- a/models/migrations/v1_13/v146.go +++ b/models/migrations/v1_13/v146.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_13/v147.go b/models/migrations/v1_13/v147.go index cc57504c74..831ef5842a 100644 --- a/models/migrations/v1_13/v147.go +++ b/models/migrations/v1_13/v147.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_13/v148.go b/models/migrations/v1_13/v148.go index 7bb8ab700b..d276db3d61 100644 --- a/models/migrations/v1_13/v148.go +++ b/models/migrations/v1_13/v148.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_13/v149.go b/models/migrations/v1_13/v149.go index 3a0c5909d5..c1bfe8b09e 100644 --- a/models/migrations/v1_13/v149.go +++ b/models/migrations/v1_13/v149.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v150.go b/models/migrations/v1_13/v150.go index be14fd130c..471a531024 100644 --- a/models/migrations/v1_13/v150.go +++ b/models/migrations/v1_13/v150.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go index ff584fff67..691b86062d 100644 --- a/models/migrations/v1_13/v151.go +++ b/models/migrations/v1_13/v151.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "context" diff --git a/models/migrations/v1_13/v152.go b/models/migrations/v1_13/v152.go index 502c82a40d..648e26446f 100644 --- a/models/migrations/v1_13/v152.go +++ b/models/migrations/v1_13/v152.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import "xorm.io/xorm" diff --git a/models/migrations/v1_13/v153.go b/models/migrations/v1_13/v153.go index 0b2dd3eb62..e5462fc162 100644 --- a/models/migrations/v1_13/v153.go +++ b/models/migrations/v1_13/v153.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_13/v154.go b/models/migrations/v1_13/v154.go index cf31190781..89dc7821b2 100644 --- a/models/migrations/v1_13/v154.go +++ b/models/migrations/v1_13/v154.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_14/main_test.go b/models/migrations/v1_14/main_test.go index c01faedc35..57cf995be1 100644 --- a/models/migrations/v1_14/main_test.go +++ b/models/migrations/v1_14/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "testing" diff --git a/models/migrations/v1_14/v155.go b/models/migrations/v1_14/v155.go index e814f59938..505a9ae033 100644 --- a/models/migrations/v1_14/v155.go +++ b/models/migrations/v1_14/v155.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v156.go b/models/migrations/v1_14/v156.go index b6dc91a054..7bbd9f4c85 100644 --- a/models/migrations/v1_14/v156.go +++ b/models/migrations/v1_14/v156.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v157.go b/models/migrations/v1_14/v157.go index 7187278d29..ba69f71130 100644 --- a/models/migrations/v1_14/v157.go +++ b/models/migrations/v1_14/v157.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go index 3fa27cfecd..2ab3c8a1f0 100644 --- a/models/migrations/v1_14/v158.go +++ b/models/migrations/v1_14/v158.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "errors" diff --git a/models/migrations/v1_14/v159.go b/models/migrations/v1_14/v159.go index fdd7e12449..4e921ea1c6 100644 --- a/models/migrations/v1_14/v159.go +++ b/models/migrations/v1_14/v159.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v160.go b/models/migrations/v1_14/v160.go index 4dea91b514..73f3798954 100644 --- a/models/migrations/v1_14/v160.go +++ b/models/migrations/v1_14/v160.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v161.go b/models/migrations/v1_14/v161.go index 6e904cfab6..9c850ad0c2 100644 --- a/models/migrations/v1_14/v161.go +++ b/models/migrations/v1_14/v161.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "context" diff --git a/models/migrations/v1_14/v162.go b/models/migrations/v1_14/v162.go index 5d6d7c2e3f..ead63f16f4 100644 --- a/models/migrations/v1_14/v162.go +++ b/models/migrations/v1_14/v162.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v163.go b/models/migrations/v1_14/v163.go index 60fc98c0a4..06ac36cbc7 100644 --- a/models/migrations/v1_14/v163.go +++ b/models/migrations/v1_14/v163.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v164.go b/models/migrations/v1_14/v164.go index 54f6951427..d2fd9b8464 100644 --- a/models/migrations/v1_14/v164.go +++ b/models/migrations/v1_14/v164.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v165.go b/models/migrations/v1_14/v165.go index 9315e44197..90fd2b1e46 100644 --- a/models/migrations/v1_14/v165.go +++ b/models/migrations/v1_14/v165.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go index e5731582fd..4c106bd7da 100644 --- a/models/migrations/v1_14/v166.go +++ b/models/migrations/v1_14/v166.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "crypto/sha256" diff --git a/models/migrations/v1_14/v167.go b/models/migrations/v1_14/v167.go index 9d416f6a32..d77bbc401e 100644 --- a/models/migrations/v1_14/v167.go +++ b/models/migrations/v1_14/v167.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v168.go b/models/migrations/v1_14/v168.go index a30a8859f7..aa93eec19b 100644 --- a/models/migrations/v1_14/v168.go +++ b/models/migrations/v1_14/v168.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import "xorm.io/xorm" diff --git a/models/migrations/v1_14/v169.go b/models/migrations/v1_14/v169.go index 5b81bb58b1..4f9df0d96f 100644 --- a/models/migrations/v1_14/v169.go +++ b/models/migrations/v1_14/v169.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v170.go b/models/migrations/v1_14/v170.go index 7b6498a3e9..a2ff4623e1 100644 --- a/models/migrations/v1_14/v170.go +++ b/models/migrations/v1_14/v170.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v171.go b/models/migrations/v1_14/v171.go index 51a35a02ad..7b200e960a 100644 --- a/models/migrations/v1_14/v171.go +++ b/models/migrations/v1_14/v171.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v172.go b/models/migrations/v1_14/v172.go index d49b70f5ad..c410d393f1 100644 --- a/models/migrations/v1_14/v172.go +++ b/models/migrations/v1_14/v172.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_14/v173.go b/models/migrations/v1_14/v173.go index 2d9eee9197..7752fbe966 100644 --- a/models/migrations/v1_14/v173.go +++ b/models/migrations/v1_14/v173.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v174.go b/models/migrations/v1_14/v174.go index c839e15db8..4049e43070 100644 --- a/models/migrations/v1_14/v174.go +++ b/models/migrations/v1_14/v174.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v175.go b/models/migrations/v1_14/v175.go index 3cda5772a0..49fa17d046 100644 --- a/models/migrations/v1_14/v175.go +++ b/models/migrations/v1_14/v175.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v176.go b/models/migrations/v1_14/v176.go index 1ed49f75fa..ef5dce9a02 100644 --- a/models/migrations/v1_14/v176.go +++ b/models/migrations/v1_14/v176.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v176_test.go b/models/migrations/v1_14/v176_test.go index d88ff207e7..d56b3e0470 100644 --- a/models/migrations/v1_14/v176_test.go +++ b/models/migrations/v1_14/v176_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "testing" diff --git a/models/migrations/v1_14/v177.go b/models/migrations/v1_14/v177.go index 6e1838f369..96676bf8d9 100644 --- a/models/migrations/v1_14/v177.go +++ b/models/migrations/v1_14/v177.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v177_test.go b/models/migrations/v1_14/v177_test.go index bffc6f92e3..0e0a67fd33 100644 --- a/models/migrations/v1_14/v177_test.go +++ b/models/migrations/v1_14/v177_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "testing" diff --git a/models/migrations/v1_15/main_test.go b/models/migrations/v1_15/main_test.go index 6c04d3f5ee..4cf6d6f695 100644 --- a/models/migrations/v1_15/main_test.go +++ b/models/migrations/v1_15/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "testing" diff --git a/models/migrations/v1_15/v178.go b/models/migrations/v1_15/v178.go index 6d236eb049..ca3a5c262e 100644 --- a/models/migrations/v1_15/v178.go +++ b/models/migrations/v1_15/v178.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v179.go b/models/migrations/v1_15/v179.go index b990583303..ce514cc4a9 100644 --- a/models/migrations/v1_15/v179.go +++ b/models/migrations/v1_15/v179.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_15/v180.go b/models/migrations/v1_15/v180.go index 02fbd57cdb..0b68c3ceb7 100644 --- a/models/migrations/v1_15/v180.go +++ b/models/migrations/v1_15/v180.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "forgejo.org/modules/json" diff --git a/models/migrations/v1_15/v181.go b/models/migrations/v1_15/v181.go index 2185ed0213..fb1d3d7a75 100644 --- a/models/migrations/v1_15/v181.go +++ b/models/migrations/v1_15/v181.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "strings" diff --git a/models/migrations/v1_15/v181_test.go b/models/migrations/v1_15/v181_test.go index 4154e0b1e9..8196f751e5 100644 --- a/models/migrations/v1_15/v181_test.go +++ b/models/migrations/v1_15/v181_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "strings" diff --git a/models/migrations/v1_15/v182.go b/models/migrations/v1_15/v182.go index 9ca500c0f9..f53ff11df9 100644 --- a/models/migrations/v1_15/v182.go +++ b/models/migrations/v1_15/v182.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v182_test.go b/models/migrations/v1_15/v182_test.go index 6865cafac4..2baf90d06a 100644 --- a/models/migrations/v1_15/v182_test.go +++ b/models/migrations/v1_15/v182_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "testing" diff --git a/models/migrations/v1_15/v183.go b/models/migrations/v1_15/v183.go index aaad64c220..5684e35699 100644 --- a/models/migrations/v1_15/v183.go +++ b/models/migrations/v1_15/v183.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "fmt" diff --git a/models/migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go index 41b64d4743..fbe0dcd780 100644 --- a/models/migrations/v1_15/v184.go +++ b/models/migrations/v1_15/v184.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "context" diff --git a/models/migrations/v1_15/v185.go b/models/migrations/v1_15/v185.go index e5878ec193..60af59edca 100644 --- a/models/migrations/v1_15/v185.go +++ b/models/migrations/v1_15/v185.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v186.go b/models/migrations/v1_15/v186.go index ad75822de5..55d3199335 100644 --- a/models/migrations/v1_15/v186.go +++ b/models/migrations/v1_15/v186.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_15/v187.go b/models/migrations/v1_15/v187.go index b573fc52ef..fabef14779 100644 --- a/models/migrations/v1_15/v187.go +++ b/models/migrations/v1_15/v187.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_15/v188.go b/models/migrations/v1_15/v188.go index 71e45cab0e..4494e6ff05 100644 --- a/models/migrations/v1_15/v188.go +++ b/models/migrations/v1_15/v188.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import "xorm.io/xorm" diff --git a/models/migrations/v1_16/main_test.go b/models/migrations/v1_16/main_test.go index 6f891f3e94..8c0a043be6 100644 --- a/models/migrations/v1_16/main_test.go +++ b/models/migrations/v1_16/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v189.go b/models/migrations/v1_16/v189.go index 1ee72d9c39..19bfcb2423 100644 --- a/models/migrations/v1_16/v189.go +++ b/models/migrations/v1_16/v189.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "encoding/binary" diff --git a/models/migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go index 90b721d5f1..9d74462a92 100644 --- a/models/migrations/v1_16/v189_test.go +++ b/models/migrations/v1_16/v189_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v190.go b/models/migrations/v1_16/v190.go index 5953802849..1eb6b6ddb4 100644 --- a/models/migrations/v1_16/v190.go +++ b/models/migrations/v1_16/v190.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v191.go b/models/migrations/v1_16/v191.go index 567f88d6d1..427476b70b 100644 --- a/models/migrations/v1_16/v191.go +++ b/models/migrations/v1_16/v191.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_16/v192.go b/models/migrations/v1_16/v192.go index 731b9fb43a..31e8c36346 100644 --- a/models/migrations/v1_16/v192.go +++ b/models/migrations/v1_16/v192.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_16/v193.go b/models/migrations/v1_16/v193.go index 8d3ce7a558..a5af2de380 100644 --- a/models/migrations/v1_16/v193.go +++ b/models/migrations/v1_16/v193.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go index 8260acf32d..bf8d8a7dc6 100644 --- a/models/migrations/v1_16/v193_test.go +++ b/models/migrations/v1_16/v193_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v194.go b/models/migrations/v1_16/v194.go index 6aa13c50cf..2e4ed8340e 100644 --- a/models/migrations/v1_16/v194.go +++ b/models/migrations/v1_16/v194.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v195.go b/models/migrations/v1_16/v195.go index 6d7e94141e..4fd42b7bd2 100644 --- a/models/migrations/v1_16/v195.go +++ b/models/migrations/v1_16/v195.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v195_test.go b/models/migrations/v1_16/v195_test.go index 71234a6fb3..1fc7b51f3c 100644 --- a/models/migrations/v1_16/v195_test.go +++ b/models/migrations/v1_16/v195_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v196.go b/models/migrations/v1_16/v196.go index 7cbafc61e5..6c9caa100f 100644 --- a/models/migrations/v1_16/v196.go +++ b/models/migrations/v1_16/v196.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v197.go b/models/migrations/v1_16/v197.go index 97888b2847..862bdfdcbd 100644 --- a/models/migrations/v1_16/v197.go +++ b/models/migrations/v1_16/v197.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v198.go b/models/migrations/v1_16/v198.go index 8b3c73addc..5d3043eb46 100644 --- a/models/migrations/v1_16/v198.go +++ b/models/migrations/v1_16/v198.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v199.go b/models/migrations/v1_16/v199.go index 6adcf890af..4020352f2b 100644 --- a/models/migrations/v1_16/v199.go +++ b/models/migrations/v1_16/v199.go @@ -1,6 +1,6 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 // We used to use a table `remote_version` to store information for updater, now we use `AppState`, so this migration task is a no-op now. diff --git a/models/migrations/v1_16/v200.go b/models/migrations/v1_16/v200.go index c08c20e51d..de57fad8fe 100644 --- a/models/migrations/v1_16/v200.go +++ b/models/migrations/v1_16/v200.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v201.go b/models/migrations/v1_16/v201.go index 35e0c9f2fb..2c43698b0c 100644 --- a/models/migrations/v1_16/v201.go +++ b/models/migrations/v1_16/v201.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v202.go b/models/migrations/v1_16/v202.go index 6ba36152f1..d8c8fdcadc 100644 --- a/models/migrations/v1_16/v202.go +++ b/models/migrations/v1_16/v202.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v203.go b/models/migrations/v1_16/v203.go index e8e6b52453..c3241cba57 100644 --- a/models/migrations/v1_16/v203.go +++ b/models/migrations/v1_16/v203.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v204.go b/models/migrations/v1_16/v204.go index ece03e1305..4d375307e7 100644 --- a/models/migrations/v1_16/v204.go +++ b/models/migrations/v1_16/v204.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import "xorm.io/xorm" diff --git a/models/migrations/v1_16/v205.go b/models/migrations/v1_16/v205.go index a064b9830d..cb452dfd7f 100644 --- a/models/migrations/v1_16/v205.go +++ b/models/migrations/v1_16/v205.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_16/v206.go b/models/migrations/v1_16/v206.go index 581a7d76e9..01a9c386eb 100644 --- a/models/migrations/v1_16/v206.go +++ b/models/migrations/v1_16/v206.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v207.go b/models/migrations/v1_16/v207.go index 91208f066c..19126ead1f 100644 --- a/models/migrations/v1_16/v207.go +++ b/models/migrations/v1_16/v207.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v208.go b/models/migrations/v1_16/v208.go index 1a11ef096a..fb643324f4 100644 --- a/models/migrations/v1_16/v208.go +++ b/models/migrations/v1_16/v208.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v209.go b/models/migrations/v1_16/v209.go index be3100e02a..230838647b 100644 --- a/models/migrations/v1_16/v209.go +++ b/models/migrations/v1_16/v209.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go index 375a008e18..f48ab11db6 100644 --- a/models/migrations/v1_16/v210.go +++ b/models/migrations/v1_16/v210.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "crypto/ecdh" diff --git a/models/migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go index f6423a5821..8454920aa0 100644 --- a/models/migrations/v1_16/v210_test.go +++ b/models/migrations/v1_16/v210_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "encoding/hex" diff --git a/models/migrations/v1_17/main_test.go b/models/migrations/v1_17/main_test.go index 0a8e05ab5f..166860b3b1 100644 --- a/models/migrations/v1_17/main_test.go +++ b/models/migrations/v1_17/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "testing" diff --git a/models/migrations/v1_17/v211.go b/models/migrations/v1_17/v211.go index 9b72c8610b..517cf19388 100644 --- a/models/migrations/v1_17/v211.go +++ b/models/migrations/v1_17/v211.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v212.go b/models/migrations/v1_17/v212.go index 2337adcc80..23868c0bb2 100644 --- a/models/migrations/v1_17/v212.go +++ b/models/migrations/v1_17/v212.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_17/v213.go b/models/migrations/v1_17/v213.go index bb3f466e52..b2bbdf7279 100644 --- a/models/migrations/v1_17/v213.go +++ b/models/migrations/v1_17/v213.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v214.go b/models/migrations/v1_17/v214.go index 2268164919..1925324f0f 100644 --- a/models/migrations/v1_17/v214.go +++ b/models/migrations/v1_17/v214.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v215.go b/models/migrations/v1_17/v215.go index 5aae798562..431103c98e 100644 --- a/models/migrations/v1_17/v215.go +++ b/models/migrations/v1_17/v215.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "forgejo.org/models/pull" diff --git a/models/migrations/v1_17/v216.go b/models/migrations/v1_17/v216.go index 268f472a42..37aeacb6fc 100644 --- a/models/migrations/v1_17/v216.go +++ b/models/migrations/v1_17/v216.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 // This migration added non-ideal indices to the action table which on larger datasets slowed things down // it has been superseded by v218.go diff --git a/models/migrations/v1_17/v217.go b/models/migrations/v1_17/v217.go index 5f096d4824..fef48b7a5b 100644 --- a/models/migrations/v1_17/v217.go +++ b/models/migrations/v1_17/v217.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_17/v218.go b/models/migrations/v1_17/v218.go index 5e3dcd0841..412d124286 100644 --- a/models/migrations/v1_17/v218.go +++ b/models/migrations/v1_17/v218.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_17/v219.go b/models/migrations/v1_17/v219.go index e90656090f..7ca6a26be6 100644 --- a/models/migrations/v1_17/v219.go +++ b/models/migrations/v1_17/v219.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "time" diff --git a/models/migrations/v1_17/v220.go b/models/migrations/v1_17/v220.go index 61bbf19725..4e010e5b76 100644 --- a/models/migrations/v1_17/v220.go +++ b/models/migrations/v1_17/v220.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( packages_model "forgejo.org/models/packages" diff --git a/models/migrations/v1_17/v221.go b/models/migrations/v1_17/v221.go index 84e9a238af..3ef34e3f06 100644 --- a/models/migrations/v1_17/v221.go +++ b/models/migrations/v1_17/v221.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "encoding/base32" diff --git a/models/migrations/v1_17/v221_test.go b/models/migrations/v1_17/v221_test.go index 02607d6b32..a9c47136b2 100644 --- a/models/migrations/v1_17/v221_test.go +++ b/models/migrations/v1_17/v221_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "encoding/base32" diff --git a/models/migrations/v1_17/v222.go b/models/migrations/v1_17/v222.go index ae910cbcb6..873769881e 100644 --- a/models/migrations/v1_17/v222.go +++ b/models/migrations/v1_17/v222.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "context" diff --git a/models/migrations/v1_17/v223.go b/models/migrations/v1_17/v223.go index 7d92dcf5ae..4f5d34d841 100644 --- a/models/migrations/v1_17/v223.go +++ b/models/migrations/v1_17/v223.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "context" diff --git a/models/migrations/v1_18/main_test.go b/models/migrations/v1_18/main_test.go index 33f5c51222..0c20934cea 100644 --- a/models/migrations/v1_18/main_test.go +++ b/models/migrations/v1_18/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "testing" diff --git a/models/migrations/v1_18/v224.go b/models/migrations/v1_18/v224.go index f3d522b91a..6dc12020ea 100644 --- a/models/migrations/v1_18/v224.go +++ b/models/migrations/v1_18/v224.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_18/v225.go b/models/migrations/v1_18/v225.go index 86bcb1323d..266eccfff8 100644 --- a/models/migrations/v1_18/v225.go +++ b/models/migrations/v1_18/v225.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_18/v226.go b/models/migrations/v1_18/v226.go index f87e24b11d..8ed9761476 100644 --- a/models/migrations/v1_18/v226.go +++ b/models/migrations/v1_18/v226.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "xorm.io/builder" diff --git a/models/migrations/v1_18/v227.go b/models/migrations/v1_18/v227.go index b6250fb76c..d39a010159 100644 --- a/models/migrations/v1_18/v227.go +++ b/models/migrations/v1_18/v227.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_18/v228.go b/models/migrations/v1_18/v228.go index 1161c8a4c9..3f5b69734d 100644 --- a/models/migrations/v1_18/v228.go +++ b/models/migrations/v1_18/v228.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_18/v229.go b/models/migrations/v1_18/v229.go index f96dde9840..00d794725f 100644 --- a/models/migrations/v1_18/v229.go +++ b/models/migrations/v1_18/v229.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "fmt" diff --git a/models/migrations/v1_18/v229_test.go b/models/migrations/v1_18/v229_test.go index ac5e726a79..903a60c851 100644 --- a/models/migrations/v1_18/v229_test.go +++ b/models/migrations/v1_18/v229_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "testing" diff --git a/models/migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go index ea5b4d02e1..078fce7643 100644 --- a/models/migrations/v1_18/v230.go +++ b/models/migrations/v1_18/v230.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go index 7dd6675673..da31b0dc9b 100644 --- a/models/migrations/v1_18/v230_test.go +++ b/models/migrations/v1_18/v230_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "testing" diff --git a/models/migrations/v1_19/main_test.go b/models/migrations/v1_19/main_test.go index 7c56926f4c..9d1c3a57ea 100644 --- a/models/migrations/v1_19/main_test.go +++ b/models/migrations/v1_19/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "testing" diff --git a/models/migrations/v1_19/v231.go b/models/migrations/v1_19/v231.go index 79e46132f0..8ef1e4e743 100644 --- a/models/migrations/v1_19/v231.go +++ b/models/migrations/v1_19/v231.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v232.go b/models/migrations/v1_19/v232.go index 7fb4a5ac8d..2aab2cf830 100644 --- a/models/migrations/v1_19/v232.go +++ b/models/migrations/v1_19/v232.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_19/v233.go b/models/migrations/v1_19/v233.go index 191afd4868..e62e8a9356 100644 --- a/models/migrations/v1_19/v233.go +++ b/models/migrations/v1_19/v233.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "fmt" diff --git a/models/migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go index 4dc35d1e27..3d5eac9887 100644 --- a/models/migrations/v1_19/v233_test.go +++ b/models/migrations/v1_19/v233_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "testing" diff --git a/models/migrations/v1_19/v234.go b/models/migrations/v1_19/v234.go index c610a423dd..e00b1cc2b6 100644 --- a/models/migrations/v1_19/v234.go +++ b/models/migrations/v1_19/v234.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_19/v235.go b/models/migrations/v1_19/v235.go index 3715de3920..297d90f65a 100644 --- a/models/migrations/v1_19/v235.go +++ b/models/migrations/v1_19/v235.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go index fa01a6ab80..c453f95e04 100644 --- a/models/migrations/v1_19/v236.go +++ b/models/migrations/v1_19/v236.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_19/v237.go b/models/migrations/v1_19/v237.go index b23c765aa5..cf30226ccd 100644 --- a/models/migrations/v1_19/v237.go +++ b/models/migrations/v1_19/v237.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v238.go b/models/migrations/v1_19/v238.go index 7c912a8341..b257315319 100644 --- a/models/migrations/v1_19/v238.go +++ b/models/migrations/v1_19/v238.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go index 10076f2401..8f4a65be95 100644 --- a/models/migrations/v1_19/v239.go +++ b/models/migrations/v1_19/v239.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v240.go b/models/migrations/v1_19/v240.go index 4ca5becede..c49ce2f49a 100644 --- a/models/migrations/v1_19/v240.go +++ b/models/migrations/v1_19/v240.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/models/db" diff --git a/models/migrations/v1_19/v241.go b/models/migrations/v1_19/v241.go index a617d6fd2f..e35801a057 100644 --- a/models/migrations/v1_19/v241.go +++ b/models/migrations/v1_19/v241.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v242.go b/models/migrations/v1_19/v242.go index bbf227ef77..87ca9cf214 100644 --- a/models/migrations/v1_19/v242.go +++ b/models/migrations/v1_19/v242.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_19/v243.go b/models/migrations/v1_19/v243.go index 55bbfafb2f..9c3f372594 100644 --- a/models/migrations/v1_19/v243.go +++ b/models/migrations/v1_19/v243.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/main_test.go b/models/migrations/v1_20/main_test.go index f870dca429..ee5eec5ef6 100644 --- a/models/migrations/v1_20/main_test.go +++ b/models/migrations/v1_20/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "testing" diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go index 977566ad7d..76cdccaca5 100644 --- a/models/migrations/v1_20/v244.go +++ b/models/migrations/v1_20/v244.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go index 7e6585388b..5e034568c4 100644 --- a/models/migrations/v1_20/v245.go +++ b/models/migrations/v1_20/v245.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "context" diff --git a/models/migrations/v1_20/v246.go b/models/migrations/v1_20/v246.go index e6340ef079..22bf723404 100644 --- a/models/migrations/v1_20/v246.go +++ b/models/migrations/v1_20/v246.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v247.go b/models/migrations/v1_20/v247.go index 9ed810a623..056699d744 100644 --- a/models/migrations/v1_20/v247.go +++ b/models/migrations/v1_20/v247.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v248.go b/models/migrations/v1_20/v248.go index 40555210e7..4f2091e4bc 100644 --- a/models/migrations/v1_20/v248.go +++ b/models/migrations/v1_20/v248.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import "xorm.io/xorm" diff --git a/models/migrations/v1_20/v249.go b/models/migrations/v1_20/v249.go index d2b096bf58..0aebb2a343 100644 --- a/models/migrations/v1_20/v249.go +++ b/models/migrations/v1_20/v249.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go index cfcde2fc9b..e12223691f 100644 --- a/models/migrations/v1_20/v250.go +++ b/models/migrations/v1_20/v250.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "strings" diff --git a/models/migrations/v1_20/v251.go b/models/migrations/v1_20/v251.go index c8665ba7eb..7d2d259df6 100644 --- a/models/migrations/v1_20/v251.go +++ b/models/migrations/v1_20/v251.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v252.go b/models/migrations/v1_20/v252.go index bb85c78309..435cce7ebe 100644 --- a/models/migrations/v1_20/v252.go +++ b/models/migrations/v1_20/v252.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v253.go b/models/migrations/v1_20/v253.go index 5f4057e9d9..73354fd485 100644 --- a/models/migrations/v1_20/v253.go +++ b/models/migrations/v1_20/v253.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v254.go b/models/migrations/v1_20/v254.go index 1e26979a5b..9cdbfb3916 100644 --- a/models/migrations/v1_20/v254.go +++ b/models/migrations/v1_20/v254.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v255.go b/models/migrations/v1_20/v255.go index 49b0ecf220..baa3c4b6d8 100644 --- a/models/migrations/v1_20/v255.go +++ b/models/migrations/v1_20/v255.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_20/v256.go b/models/migrations/v1_20/v256.go index 822153b93e..7b84c1e154 100644 --- a/models/migrations/v1_20/v256.go +++ b/models/migrations/v1_20/v256.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go index 70f229d73f..8045909dba 100644 --- a/models/migrations/v1_20/v257.go +++ b/models/migrations/v1_20/v257.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_20/v258.go b/models/migrations/v1_20/v258.go index 47174ce805..1d3faffdae 100644 --- a/models/migrations/v1_20/v258.go +++ b/models/migrations/v1_20/v258.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v259.go b/models/migrations/v1_20/v259.go index f10b94fa9c..9b2b68263e 100644 --- a/models/migrations/v1_20/v259.go +++ b/models/migrations/v1_20/v259.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "fmt" diff --git a/models/migrations/v1_20/v259_test.go b/models/migrations/v1_20/v259_test.go index 32e4aa3050..b41b6c7995 100644 --- a/models/migrations/v1_20/v259_test.go +++ b/models/migrations/v1_20/v259_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "sort" diff --git a/models/migrations/v1_21/main_test.go b/models/migrations/v1_21/main_test.go index 7104887afb..3f10a39a94 100644 --- a/models/migrations/v1_21/main_test.go +++ b/models/migrations/v1_21/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "testing" diff --git a/models/migrations/v1_21/v260.go b/models/migrations/v1_21/v260.go index 245f3011ab..b73b53bd61 100644 --- a/models/migrations/v1_21/v260.go +++ b/models/migrations/v1_21/v260.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_21/v261.go b/models/migrations/v1_21/v261.go index 743bef152d..83a4927704 100644 --- a/models/migrations/v1_21/v261.go +++ b/models/migrations/v1_21/v261.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v262.go b/models/migrations/v1_21/v262.go index 23e900572a..6e88e29b9d 100644 --- a/models/migrations/v1_21/v262.go +++ b/models/migrations/v1_21/v262.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v263.go b/models/migrations/v1_21/v263.go index 2c7cbadf0d..55c418bde0 100644 --- a/models/migrations/v1_21/v263.go +++ b/models/migrations/v1_21/v263.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "fmt" diff --git a/models/migrations/v1_21/v264.go b/models/migrations/v1_21/v264.go index 5615600072..acd2c9bb48 100644 --- a/models/migrations/v1_21/v264.go +++ b/models/migrations/v1_21/v264.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "context" diff --git a/models/migrations/v1_21/v265.go b/models/migrations/v1_21/v265.go index 800eb95f72..b6892acc27 100644 --- a/models/migrations/v1_21/v265.go +++ b/models/migrations/v1_21/v265.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v266.go b/models/migrations/v1_21/v266.go index 79a5f5e14c..440549e868 100644 --- a/models/migrations/v1_21/v266.go +++ b/models/migrations/v1_21/v266.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v267.go b/models/migrations/v1_21/v267.go index f94696a22b..13992d8776 100644 --- a/models/migrations/v1_21/v267.go +++ b/models/migrations/v1_21/v267.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v268.go b/models/migrations/v1_21/v268.go index 332793ff07..b677d2383e 100644 --- a/models/migrations/v1_21/v268.go +++ b/models/migrations/v1_21/v268.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v269.go b/models/migrations/v1_21/v269.go index 475ec02380..042040927d 100644 --- a/models/migrations/v1_21/v269.go +++ b/models/migrations/v1_21/v269.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v270.go b/models/migrations/v1_21/v270.go index b9cc84d3ac..ab7c5660ba 100644 --- a/models/migrations/v1_21/v270.go +++ b/models/migrations/v1_21/v270.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v271.go b/models/migrations/v1_21/v271.go index f45c113c1f..e3ce2d4b74 100644 --- a/models/migrations/v1_21/v271.go +++ b/models/migrations/v1_21/v271.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v272.go b/models/migrations/v1_21/v272.go index a729c49f1b..14c1e0c4b0 100644 --- a/models/migrations/v1_21/v272.go +++ b/models/migrations/v1_21/v272.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "xorm.io/xorm" ) diff --git a/models/migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go index 1ec6ade566..d6ec80d3d5 100644 --- a/models/migrations/v1_21/v273.go +++ b/models/migrations/v1_21/v273.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v274.go b/models/migrations/v1_21/v274.go index b74e5fed51..a1211d1fdd 100644 --- a/models/migrations/v1_21/v274.go +++ b/models/migrations/v1_21/v274.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "time" diff --git a/models/migrations/v1_21/v275.go b/models/migrations/v1_21/v275.go index 78804a59d6..2bfe5c72fa 100644 --- a/models/migrations/v1_21/v275.go +++ b/models/migrations/v1_21/v275.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go index 0830c3bd92..3b0bc23da7 100644 --- a/models/migrations/v1_21/v276.go +++ b/models/migrations/v1_21/v276.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( repo_model "forgejo.org/models/repo" diff --git a/models/migrations/v1_21/v277.go b/models/migrations/v1_21/v277.go index 12529160b7..0c102eddde 100644 --- a/models/migrations/v1_21/v277.go +++ b/models/migrations/v1_21/v277.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v278.go b/models/migrations/v1_21/v278.go index d6a462d1e7..846f228678 100644 --- a/models/migrations/v1_21/v278.go +++ b/models/migrations/v1_21/v278.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v279.go b/models/migrations/v1_21/v279.go index 2abd1bbe84..beb39effe1 100644 --- a/models/migrations/v1_21/v279.go +++ b/models/migrations/v1_21/v279.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/main_test.go b/models/migrations/v1_22/main_test.go index dc991b78fe..7b05993e09 100644 --- a/models/migrations/v1_22/main_test.go +++ b/models/migrations/v1_22/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v280.go b/models/migrations/v1_22/v280.go index a8ee4a3bf7..2271cb6089 100644 --- a/models/migrations/v1_22/v280.go +++ b/models/migrations/v1_22/v280.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v281.go b/models/migrations/v1_22/v281.go index 5271c786be..2eeca9be82 100644 --- a/models/migrations/v1_22/v281.go +++ b/models/migrations/v1_22/v281.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_22/v282.go b/models/migrations/v1_22/v282.go index baad9e0916..eed64c30f7 100644 --- a/models/migrations/v1_22/v282.go +++ b/models/migrations/v1_22/v282.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go index 86946d1c39..33a2513069 100644 --- a/models/migrations/v1_22/v283.go +++ b/models/migrations/v1_22/v283.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v283_test.go b/models/migrations/v1_22/v283_test.go index d8e147a131..652d96ac16 100644 --- a/models/migrations/v1_22/v283_test.go +++ b/models/migrations/v1_22/v283_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v284.go b/models/migrations/v1_22/v284.go index 2b95078980..31b38f6aed 100644 --- a/models/migrations/v1_22/v284.go +++ b/models/migrations/v1_22/v284.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 + import ( "xorm.io/xorm" ) diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go index a55cc17c04..fed89f670e 100644 --- a/models/migrations/v1_22/v285.go +++ b/models/migrations/v1_22/v285.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "time" diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go index d0489e7aeb..05247bb436 100644 --- a/models/migrations/v1_22/v286.go +++ b/models/migrations/v1_22/v286.go @@ -1,6 +1,6 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "fmt" diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go index c63deef495..5bb3334df2 100644 --- a/models/migrations/v1_22/v286_test.go +++ b/models/migrations/v1_22/v286_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go index c8b1593286..5fd901f9de 100644 --- a/models/migrations/v1_22/v287.go +++ b/models/migrations/v1_22/v287.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v288.go b/models/migrations/v1_22/v288.go index 44e4991851..78be3b6ef2 100644 --- a/models/migrations/v1_22/v288.go +++ b/models/migrations/v1_22/v288.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go index b9941aadd9..78689a4ffa 100644 --- a/models/migrations/v1_22/v289.go +++ b/models/migrations/v1_22/v289.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go index 594e417644..ebafab6567 100644 --- a/models/migrations/v1_22/v290.go +++ b/models/migrations/v1_22/v290.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_22/v290_test.go b/models/migrations/v1_22/v290_test.go index 569d77bc16..a1907cf4d6 100644 --- a/models/migrations/v1_22/v290_test.go +++ b/models/migrations/v1_22/v290_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "strconv" diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go index 74726fae96..823a644a95 100644 --- a/models/migrations/v1_22/v291.go +++ b/models/migrations/v1_22/v291.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go index beca556aee..440f48ce80 100644 --- a/models/migrations/v1_22/v292.go +++ b/models/migrations/v1_22/v292.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 // NOTE: noop the original migration has bug which some projects will be skip, so // these projects will have no default board. diff --git a/models/migrations/v1_22/v293.go b/models/migrations/v1_22/v293.go index 9f38c3db56..e9c9746b26 100644 --- a/models/migrations/v1_22/v293.go +++ b/models/migrations/v1_22/v293.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go index 444146737d..6b1931b761 100644 --- a/models/migrations/v1_22/v293_test.go +++ b/models/migrations/v1_22/v293_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go index 314b4519f1..6c52372306 100644 --- a/models/migrations/v1_22/v294.go +++ b/models/migrations/v1_22/v294.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go index ef7b67ca5b..e87a4bc85f 100644 --- a/models/migrations/v1_22/v294_test.go +++ b/models/migrations/v1_22/v294_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "slices" @@ -45,7 +45,8 @@ func Test_AddUniqueIndexForProjectIssue(t *testing.T) { for _, index := range tables[0].Indexes { if index.Type == schemas.UniqueType { found = true - slices.Equal(index.Cols, []string{"project_id", "issue_id"}) + slices.Sort(index.Cols) + assert.Equal(t, []string{"issue_id", "project_id"}, index.Cols) break } } diff --git a/models/migrations/v1_22/v295.go b/models/migrations/v1_22/v295.go index 17bdadb4ad..319b1a399b 100644 --- a/models/migrations/v1_22/v295.go +++ b/models/migrations/v1_22/v295.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go index 1ecacab95f..75350f9f65 100644 --- a/models/migrations/v1_22/v296.go +++ b/models/migrations/v1_22/v296.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go index b9f3b95ade..7700173a00 100644 --- a/models/migrations/v1_22/v298.go +++ b/models/migrations/v1_22/v298.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go index 0fd90a4a67..5fb4fec999 100644 --- a/models/migrations/v1_23/main_test.go +++ b/models/migrations/v1_23/main_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "testing" diff --git a/models/migrations/v1_23/v299.go b/models/migrations/v1_23/v299.go index f6db960c3b..73ce19c875 100644 --- a/models/migrations/v1_23/v299.go +++ b/models/migrations/v1_23/v299.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v300.go b/models/migrations/v1_23/v300.go index f1f1cccdbf..404d8dbea8 100644 --- a/models/migrations/v1_23/v300.go +++ b/models/migrations/v1_23/v300.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v301.go b/models/migrations/v1_23/v301.go index b7797f6c6b..f2a4d8c559 100644 --- a/models/migrations/v1_23/v301.go +++ b/models/migrations/v1_23/v301.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v302.go b/models/migrations/v1_23/v302.go index c8ed786d63..1b056993bd 100644 --- a/models/migrations/v1_23/v302.go +++ b/models/migrations/v1_23/v302.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_23/v303.go b/models/migrations/v1_23/v303.go index fae0131bdd..03197d2857 100644 --- a/models/migrations/v1_23/v303.go +++ b/models/migrations/v1_23/v303.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package v1_23 //nolint +package v1_23 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_23/v303_test.go b/models/migrations/v1_23/v303_test.go index f105d11830..f2c764bae3 100644 --- a/models/migrations/v1_23/v303_test.go +++ b/models/migrations/v1_23/v303_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package v1_23 //nolint +package v1_23 import ( "testing" diff --git a/models/migrations/v1_6/v70.go b/models/migrations/v1_6/v70.go index ec6bd09bb5..eb669f57b6 100644 --- a/models/migrations/v1_6/v70.go +++ b/models/migrations/v1_6/v70.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 //nolint +package v1_6 import ( "fmt" diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go index 3706ad4406..42fe8cd1ba 100644 --- a/models/migrations/v1_6/v71.go +++ b/models/migrations/v1_6/v71.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 //nolint +package v1_6 import ( "fmt" diff --git a/models/migrations/v1_6/v72.go b/models/migrations/v1_6/v72.go index 4df2a0f6e9..7cd2331376 100644 --- a/models/migrations/v1_6/v72.go +++ b/models/migrations/v1_6/v72.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 //nolint +package v1_6 import ( "fmt" diff --git a/models/migrations/v1_7/v73.go b/models/migrations/v1_7/v73.go index b5a748aae3..e0b7a28537 100644 --- a/models/migrations/v1_7/v73.go +++ b/models/migrations/v1_7/v73.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_7 //nolint +package v1_7 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_7/v74.go b/models/migrations/v1_7/v74.go index f0567e3c9b..376be37a24 100644 --- a/models/migrations/v1_7/v74.go +++ b/models/migrations/v1_7/v74.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_7 //nolint +package v1_7 import "xorm.io/xorm" diff --git a/models/migrations/v1_7/v75.go b/models/migrations/v1_7/v75.go index fa7430970c..ef11575466 100644 --- a/models/migrations/v1_7/v75.go +++ b/models/migrations/v1_7/v75.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_7 //nolint +package v1_7 import ( "xorm.io/builder" diff --git a/models/migrations/v1_8/v76.go b/models/migrations/v1_8/v76.go index 61ad006a47..8d47280b41 100644 --- a/models/migrations/v1_8/v76.go +++ b/models/migrations/v1_8/v76.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "fmt" diff --git a/models/migrations/v1_8/v77.go b/models/migrations/v1_8/v77.go index 8b19993924..4fe5ebe635 100644 --- a/models/migrations/v1_8/v77.go +++ b/models/migrations/v1_8/v77.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_8/v78.go b/models/migrations/v1_8/v78.go index 8102b19335..840fc20d96 100644 --- a/models/migrations/v1_8/v78.go +++ b/models/migrations/v1_8/v78.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_8/v79.go b/models/migrations/v1_8/v79.go index f7d2d68f96..c8e0db531f 100644 --- a/models/migrations/v1_8/v79.go +++ b/models/migrations/v1_8/v79.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_8/v80.go b/models/migrations/v1_8/v80.go index cebbbead28..6f9df47a93 100644 --- a/models/migrations/v1_8/v80.go +++ b/models/migrations/v1_8/v80.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import "xorm.io/xorm" diff --git a/models/migrations/v1_8/v81.go b/models/migrations/v1_8/v81.go index 734fc24641..8152a47ad7 100644 --- a/models/migrations/v1_8/v81.go +++ b/models/migrations/v1_8/v81.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "fmt" diff --git a/models/migrations/v1_9/v82.go b/models/migrations/v1_9/v82.go index 78a90bdde9..235c73c504 100644 --- a/models/migrations/v1_9/v82.go +++ b/models/migrations/v1_9/v82.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "fmt" diff --git a/models/migrations/v1_9/v83.go b/models/migrations/v1_9/v83.go index fa24a92d28..9640564a44 100644 --- a/models/migrations/v1_9/v83.go +++ b/models/migrations/v1_9/v83.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_9/v84.go b/models/migrations/v1_9/v84.go index c7155fe9cf..423915ae57 100644 --- a/models/migrations/v1_9/v84.go +++ b/models/migrations/v1_9/v84.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go index d8e9d91840..9d5adc82dd 100644 --- a/models/migrations/v1_9/v85.go +++ b/models/migrations/v1_9/v85.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "fmt" diff --git a/models/migrations/v1_9/v86.go b/models/migrations/v1_9/v86.go index cf2725d158..9464ff0cf6 100644 --- a/models/migrations/v1_9/v86.go +++ b/models/migrations/v1_9/v86.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_9/v87.go b/models/migrations/v1_9/v87.go index fa01b6e5e3..81a4ebf80d 100644 --- a/models/migrations/v1_9/v87.go +++ b/models/migrations/v1_9/v87.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "xorm.io/xorm" diff --git a/models/moderation/abuse_report.go b/models/moderation/abuse_report.go index 3a6244ef4c..0bf8aab174 100644 --- a/models/moderation/abuse_report.go +++ b/models/moderation/abuse_report.go @@ -8,6 +8,7 @@ import ( "database/sql" "errors" "slices" + "time" "forgejo.org/models/db" "forgejo.org/modules/log" @@ -47,14 +48,21 @@ const ( AbuseCategoryTypeIllegalContent // 4 ) +var AbuseCategoriesTranslationKeys = map[AbuseCategoryType]string{ + AbuseCategoryTypeSpam: "moderation.abuse_category.spam", + AbuseCategoryTypeMalware: "moderation.abuse_category.malware", + AbuseCategoryTypeIllegalContent: "moderation.abuse_category.illegal_content", + AbuseCategoryTypeOther: "moderation.abuse_category.other_violations", +} + // GetAbuseCategoriesList returns a list of pairs with the available abuse category types // and their corresponding translation keys func GetAbuseCategoriesList() []AbuseCategoryItem { return []AbuseCategoryItem{ - {AbuseCategoryTypeSpam, "moderation.abuse_category.spam"}, - {AbuseCategoryTypeMalware, "moderation.abuse_category.malware"}, - {AbuseCategoryTypeIllegalContent, "moderation.abuse_category.illegal_content"}, - {AbuseCategoryTypeOther, "moderation.abuse_category.other_violations"}, + {AbuseCategoryTypeSpam, AbuseCategoriesTranslationKeys[AbuseCategoryTypeSpam]}, + {AbuseCategoryTypeMalware, AbuseCategoriesTranslationKeys[AbuseCategoryTypeMalware]}, + {AbuseCategoryTypeIllegalContent, AbuseCategoriesTranslationKeys[AbuseCategoryTypeIllegalContent]}, + {AbuseCategoryTypeOther, AbuseCategoriesTranslationKeys[AbuseCategoryTypeOther]}, } } @@ -104,6 +112,7 @@ type AbuseReport struct { // The ID of the corresponding shadow-copied content when exists; otherwise null. ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` + ResolvedUnix timeutil.TimeStamp `xorm:"DEFAULT NULL"` } var ErrSelfReporting = errors.New("reporting yourself is not allowed") @@ -154,6 +163,25 @@ func ReportAbuse(ctx context.Context, report *AbuseReport) error { return err } +// GetResolvedReports gets all resolved reports +func GetResolvedReports(ctx context.Context, keepReportsFor time.Duration) ([]*AbuseReport, error) { + cond := builder.And( + builder.Or( + builder.Eq{"`status`": ReportStatusTypeHandled}, + builder.Eq{"`status`": ReportStatusTypeIgnored}, + ), + ) + + if keepReportsFor > 0 { + cond = cond.And(builder.Lt{"resolved_unix": time.Now().Add(-keepReportsFor).Unix()}) + } + + abuseReports := make([]*AbuseReport, 0, 30) + return abuseReports, db.GetEngine(ctx). + Where(cond). + Find(&abuseReports) +} + /* // MarkAsHandled will change the status to 'Handled' for all reports linked to the same item (user, repository, issue or comment). func MarkAsHandled(ctx context.Context, contentType ReportedContentType, contentID int64) error { diff --git a/models/moderation/abuse_report_detailed.go b/models/moderation/abuse_report_detailed.go new file mode 100644 index 0000000000..265d143709 --- /dev/null +++ b/models/moderation/abuse_report_detailed.go @@ -0,0 +1,135 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "context" + "fmt" + "strings" + + "forgejo.org/models/db" + "forgejo.org/modules/setting" + "forgejo.org/modules/timeutil" + + "xorm.io/builder" +) + +type AbuseReportDetailed struct { + AbuseReport `xorm:"extends"` + ReportedTimes int // only for overview + ReporterName string + ContentReference string + ShadowCopyDate timeutil.TimeStamp // only for details + ShadowCopyRawValue string // only for details +} + +func (ard AbuseReportDetailed) ContentTypeIconName() string { + switch ard.ContentType { + case ReportedContentTypeUser: + return "octicon-person" + case ReportedContentTypeRepository: + return "octicon-repo" + case ReportedContentTypeIssue: + return "octicon-issue-opened" + case ReportedContentTypeComment: + return "octicon-comment" + default: + return "octicon-question" + } +} + +func (ard AbuseReportDetailed) ContentURL() string { + switch ard.ContentType { + case ReportedContentTypeUser: + return strings.TrimLeft(ard.ContentReference, "@") + case ReportedContentTypeIssue: + return strings.ReplaceAll(ard.ContentReference, "#", "/issues/") + default: + return ard.ContentReference + } +} + +func GetOpenReports(ctx context.Context) ([]*AbuseReportDetailed, error) { + var reports []*AbuseReportDetailed + + // - For PostgreSQL user table name should be escaped. + // - Escaping can be done with double quotes (") but this doesn't work for MariaDB. + // - For SQLite index column name should be escaped. + // - Escaping can be done with double quotes (") or backticks (`). + // - For MariaDB/MySQL there is no need to escape the above. + // - Therefore we will use double quotes (") but only for PostgreSQL and SQLite. + identifierEscapeChar := `` + if setting.Database.Type.IsPostgreSQL() || setting.Database.Type.IsSQLite3() { + identifierEscapeChar = `"` + } + + err := db.GetEngine(ctx).SQL(fmt.Sprintf(`SELECT AR.*, ARD.reported_times, U.name AS reporter_name, REFS.ref AS content_reference + FROM abuse_report AR + INNER JOIN ( + SELECT min(id) AS id, count(id) AS reported_times + FROM abuse_report + WHERE status = %[2]d + GROUP BY content_type, content_id + ) ARD ON ARD.id = AR.id + LEFT JOIN %[1]suser%[1]s U ON U.id = AR.reporter_id + LEFT JOIN ( + SELECT %[3]d AS type, id, concat('@', name) AS "ref" + FROM %[1]suser%[1]s WHERE id IN ( + SELECT content_id FROM abuse_report WHERE status = %[2]d AND content_type = %[3]d + ) + UNION + SELECT %[4]d AS "type", id, concat(owner_name, '/', name) AS "ref" + FROM repository WHERE id IN ( + SELECT content_id FROM abuse_report WHERE status = %[2]d AND content_type = %[4]d + ) + UNION + SELECT %[5]d AS "type", I.id, concat(IR.owner_name, '/', IR.name, '#', I.%[1]sindex%[1]s) AS "ref" + FROM issue I + LEFT JOIN repository IR ON IR.id = I.repo_id + WHERE I.id IN ( + SELECT content_id FROM abuse_report WHERE status = %[2]d AND content_type = %[5]d + ) + UNION + SELECT %[6]d AS "type", C.id, concat(CIR.owner_name, '/', CIR.name, '/issues/', CI.%[1]sindex%[1]s, '#issuecomment-', C.id) AS "ref" + FROM comment C + LEFT JOIN issue CI ON CI.id = C.issue_id + LEFT JOIN repository CIR ON CIR.id = CI.repo_id + WHERE C.id IN ( + SELECT content_id FROM abuse_report WHERE status = %[2]d AND content_type = %[6]d + ) + ) REFS ON REFS.type = AR.content_type AND REFS.id = AR.content_id + ORDER BY AR.created_unix ASC`, identifierEscapeChar, ReportStatusTypeOpen, + ReportedContentTypeUser, ReportedContentTypeRepository, ReportedContentTypeIssue, ReportedContentTypeComment)). + Find(&reports) + if err != nil { + return nil, err + } + return reports, nil +} + +func GetOpenReportsByTypeAndContentID(ctx context.Context, contentType ReportedContentType, contentID int64) ([]*AbuseReportDetailed, error) { + var reports []*AbuseReportDetailed + + // Some remarks concerning PostgreSQL: + // - user table should be escaped (e.g. `user`); + // - tried to use aliases for table names but errors like 'invalid reference to FROM-clause entry' + // or 'missing FROM-clause entry' were returned; + err := db.GetEngine(ctx). + Select("abuse_report.*, `user`.name AS reporter_name, abuse_report_shadow_copy.created_unix AS shadow_copy_date, abuse_report_shadow_copy.raw_value AS shadow_copy_raw_value"). + Table("abuse_report"). + Join("LEFT", "user", "`user`.id = abuse_report.reporter_id"). + Join("LEFT", "abuse_report_shadow_copy", "abuse_report_shadow_copy.id = abuse_report.shadow_copy_id"). + Where(builder.Eq{ + "content_type": contentType, + "content_id": contentID, + "status": ReportStatusTypeOpen, + }). + Asc("abuse_report.created_unix"). + Find(&reports) + if err != nil { + return nil, err + } + + return reports, nil +} diff --git a/models/moderation/shadow_copy.go b/models/moderation/shadow_copy.go index d363610a48..8abb32e8ec 100644 --- a/models/moderation/shadow_copy.go +++ b/models/moderation/shadow_copy.go @@ -26,6 +26,22 @@ func (sc AbuseReportShadowCopy) NullableID() sql.NullInt64 { return sql.NullInt64{Int64: sc.ID, Valid: sc.ID > 0} } +// ShadowCopyField defines a pair of a value stored within the shadow copy +// (of some content reported as abusive) and a corresponding key (caption). +// A list of such pairs is used when rendering shadow copies for admins reviewing abuse reports. +type ShadowCopyField struct { + Key string + Value string +} + +// ShadowCopyData interface should be implemented by the type structs used for marshaling/unmarshaling the fields +// preserved as shadow copies for abusive content reports (i.e. UserData, RepositoryData, IssueData, CommentData). +type ShadowCopyData interface { + // GetFieldsMap returns a list of pairs with the fields stored within shadow copies + // of content reported as abusive, to be used when rendering a shadow copy in the admin UI. + GetFieldsMap() []ShadowCopyField +} + func init() { // RegisterModel will create the table if does not already exist // or any missing columns if the table was previously created. diff --git a/models/organization/org.go b/models/organization/org.go index ff95261051..c4df5d4fe1 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -186,6 +186,11 @@ func (org *Organization) CanCreateRepo() bool { return org.AsUser().CanCreateRepo() } +// IsGhost returns if the organization is a ghost +func (org *Organization) IsGhost() bool { + return org.AsUser().IsGhost() +} + // FindOrgMembersOpts represensts find org members conditions type FindOrgMembersOpts struct { db.ListOptions diff --git a/models/organization/org_list.go b/models/organization/org_list.go index e387936473..371993cdee 100644 --- a/models/organization/org_list.go +++ b/models/organization/org_list.go @@ -71,11 +71,8 @@ func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organiz Find(&orgs) } -// MinimalOrg represents a simple organization with only the needed columns -type MinimalOrg = Organization - // GetUserOrgsList returns all organizations the given user has access to -func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) { +func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*Organization, error) { schema, err := db.TableInfo(new(user_model.User)) if err != nil { return nil, err @@ -100,7 +97,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, } columnsStr := selectColumns.String() - var orgs []*MinimalOrg + var orgs []*Organization if err := db.GetEngine(ctx).Select(columnsStr). Table("user"). Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))). @@ -138,6 +135,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, for _, org := range orgs { org.NumRepos = orgCountMap[org.ID] + org.Type = user_model.UserTypeOrganization } return orgs, nil diff --git a/models/organization/org_list_test.go b/models/organization/org_list_test.go index 170e2bf131..6e8c0bac26 100644 --- a/models/organization/org_list_test.go +++ b/models/organization/org_list_test.go @@ -85,11 +85,11 @@ func TestGetUserOrgsList(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4}) require.NoError(t, err) - if assert.Len(t, orgs, 1) { - assert.EqualValues(t, 3, orgs[0].ID) - // repo_id: 3 is in the team, 32 is public, 5 is private with no team - assert.Equal(t, 2, orgs[0].NumRepos) - } + assert.Len(t, orgs, 1) + assert.EqualValues(t, 3, orgs[0].ID) + // repo_id: 3 is in the team, 32 is public, 5 is private with no team + assert.Equal(t, 2, orgs[0].NumRepos) + assert.Equal(t, user_model.UserTypeOrganization, orgs[0].Type) } func TestGetUserOrgsListSorting(t *testing.T) { @@ -97,7 +97,7 @@ func TestGetUserOrgsListSorting(t *testing.T) { orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 1}) require.NoError(t, err) - isSorted := slices.IsSortedFunc(orgs, func(a, b *organization.MinimalOrg) int { + isSorted := slices.IsSortedFunc(orgs, func(a, b *organization.Organization) int { return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name)) }) diff --git a/models/organization/team.go b/models/organization/team.go index c78eff39fb..209471e013 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -1,5 +1,6 @@ -// Copyright 2018 The Gitea Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package organization @@ -7,6 +8,7 @@ package organization import ( "context" "fmt" + "net/url" "strings" "forgejo.org/models/db" @@ -20,13 +22,6 @@ import ( "xorm.io/builder" ) -// ___________ -// \__ ___/___ _____ _____ -// | |_/ __ \\__ \ / \ -// | |\ ___/ / __ \| Y Y \ -// |____| \___ >____ /__|_| / -// \/ \/ \/ - // ErrTeamAlreadyExist represents a "TeamAlreadyExist" kind of error. type ErrTeamAlreadyExist struct { OrgID int64 @@ -193,6 +188,27 @@ func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode return perm.AccessModeNone } +// GetOrg returns the team's organization +func (t *Team) GetOrg(ctx context.Context) *Organization { + org, err := GetOrgByID(ctx, t.OrgID) + if err != nil { + return OrgFromUser(user_model.NewGhostUser()) + } + return org +} + +// Link returns the team's page link +func (t *Team) Link(ctx context.Context) string { + if t.IsGhost() { + return "" + } + org := t.GetOrg(ctx) + if org.IsGhost() { + return "" + } + return org.OrganisationLink() + "/teams/" + url.PathEscape(t.Name) +} + // IsUsableTeamName tests if a name could be as team name func IsUsableTeamName(name string) error { switch name { @@ -293,10 +309,22 @@ func FixInconsistentOwnerTeams(ctx context.Context) (int64, error) { return int64(len(teamIDs)), nil } +const ( + GhostTeamID = -1 + GhostTeamName = "Ghost team" + GhostTeamLowerName = "ghost team" +) + +// NewGhostTeam creates ghost team (for deleted team) func NewGhostTeam() *Team { return &Team{ - ID: -1, - Name: "Ghost team", - LowerName: "ghost team", + ID: GhostTeamID, + Name: GhostTeamName, + LowerName: GhostTeamLowerName, } } + +// IsGhost returns if a team is a ghost team +func (t *Team) IsGhost() bool { + return t.ID == GhostTeamID +} diff --git a/models/organization/team_test.go b/models/organization/team_test.go index 60c500e7ec..768ccdf5be 100644 --- a/models/organization/team_test.go +++ b/models/organization/team_test.go @@ -1,4 +1,5 @@ // Copyright 2017 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package organization_test @@ -15,14 +16,33 @@ import ( "github.com/stretchr/testify/require" ) -func TestTeam_IsOwnerTeam(t *testing.T) { +func TestTeam(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}) - assert.True(t, team.IsOwnerTeam()) + owners := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}) + assert.Equal(t, int64(3), owners.GetOrg(db.DefaultContext).ID) + assert.Equal(t, "/org/org3/teams/Owners", owners.Link(db.DefaultContext)) + assert.False(t, owners.IsGhost()) + assert.True(t, owners.IsOwnerTeam()) - team = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}) - assert.False(t, team.IsOwnerTeam()) + team1 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}) + assert.Equal(t, int64(3), team1.GetOrg(db.DefaultContext).ID) + assert.Equal(t, "/org/org3/teams/team1", team1.Link(db.DefaultContext)) + assert.False(t, team1.IsGhost()) + assert.False(t, team1.IsOwnerTeam()) + + ghost := organization.NewGhostTeam() + assert.Equal(t, int64(-1), ghost.ID) + assert.Equal(t, int64(-1), ghost.GetOrg(db.DefaultContext).ID) + assert.Empty(t, ghost.Link(db.DefaultContext)) + assert.True(t, ghost.IsGhost()) + assert.False(t, ghost.IsOwnerTeam()) + + ghosted := organization.Team{ID: 10, Name: "Ghosted"} + assert.Equal(t, int64(-1), ghosted.GetOrg(db.DefaultContext).ID) + assert.Empty(t, ghosted.Link(db.DefaultContext)) + assert.False(t, ghosted.IsGhost()) + assert.False(t, ghosted.IsOwnerTeam()) } func TestTeam_IsMember(t *testing.T) { diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index ce9963b83a..f7daf38e5c 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -7,6 +7,7 @@ import ( "context" "fmt" + actions_model "forgejo.org/models/actions" "forgejo.org/models/db" "forgejo.org/models/organization" perm_model "forgejo.org/models/perm" @@ -136,6 +137,33 @@ func (p *Permission) LogString() string { return fmt.Sprintf(format, args...) } +func GetActionRepoPermission(ctx context.Context, repo *repo_model.Repository, task *actions_model.ActionTask) (Permission, error) { + // straight forward case: an actions task is attempting to access its own repo + if task.RepoID == repo.ID { + var mode perm_model.AccessMode + + // determine default access mode for repo: + if task.IsForkPullRequest { + mode = perm_model.AccessModeRead + } else { + mode = perm_model.AccessModeWrite + } + + if err := repo.LoadUnits(ctx); err != nil { + return Permission{}, err + } + + perm := Permission{ + AccessMode: mode, + Units: repo.Units, + } + + return perm, nil + } + + return GetUserRepoPermission(ctx, repo, user_model.NewActionsUser()) +} + // GetUserRepoPermission returns the user permissions to the repository func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (Permission, error) { var perm Permission diff --git a/models/perm/access/repo_permission_test.go b/models/perm/access/repo_permission_test.go new file mode 100644 index 0000000000..55bc975421 --- /dev/null +++ b/models/perm/access/repo_permission_test.go @@ -0,0 +1,78 @@ +package access_test + +import ( + "testing" + + actions_model "forgejo.org/models/actions" + "forgejo.org/models/db" + perm_model "forgejo.org/models/perm" + "forgejo.org/models/perm/access" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func assertAccess(t *testing.T, expectedMode perm_model.AccessMode, perm *access.Permission) { + assert.Equal(t, expectedMode, perm.AccessMode) + + for _, unit := range perm.Units { + assert.Equal(t, expectedMode, perm.UnitAccessMode(unit.Type)) + } +} + +func TestActionTaskCanAccessOwnRepo(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: actionTask.RepoID}) + + perm, err := access.GetActionRepoPermission(db.DefaultContext, repo, actionTask) + require.NoError(t, err) + assertAccess(t, perm_model.AccessModeWrite, &perm) +} + +func TestActionTaskCanAccessPublicRepo(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + + perm, err := access.GetActionRepoPermission(db.DefaultContext, repo, actionTask) + require.NoError(t, err) + assertAccess(t, perm_model.AccessModeRead, &perm) +} + +func TestActionTaskCanAccessPublicRepoOfLimitedOrg(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 38}) + + perm, err := access.GetActionRepoPermission(db.DefaultContext, repo, actionTask) + require.NoError(t, err) + assertAccess(t, perm_model.AccessModeRead, &perm) +} + +func TestActionTaskNoAccessPublicRepoOfPrivateOrg(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 40}) + + perm, err := access.GetActionRepoPermission(db.DefaultContext, repo, actionTask) + require.NoError(t, err) + assertAccess(t, perm_model.AccessModeNone, &perm) +} + +func TestActionTaskNoAccessPrivateRepo(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + + perm, err := access.GetActionRepoPermission(db.DefaultContext, repo, actionTask) + require.NoError(t, err) + assertAccess(t, perm_model.AccessModeNone, &perm) +} diff --git a/models/repo/git.go b/models/repo/git.go index 692176c8f6..11f6452be5 100644 --- a/models/repo/git.go +++ b/models/repo/git.go @@ -29,6 +29,8 @@ const ( MergeStyleRebaseUpdate MergeStyle = "rebase-update-only" ) +var MergeStyles = []MergeStyle{MergeStyleMerge, MergeStyleRebase, MergeStyleRebaseMerge, MergeStyleSquash, MergeStyleFastForwardOnly, MergeStyleManuallyMerged, MergeStyleRebaseUpdate} + type UpdateStyle string const ( diff --git a/models/repo/moderation.go b/models/repo/moderation.go index d7b87dffa0..0d2672227b 100644 --- a/models/repo/moderation.go +++ b/models/repo/moderation.go @@ -5,6 +5,8 @@ package repo import ( "context" + "strconv" + "strings" "forgejo.org/models/moderation" "forgejo.org/modules/json" @@ -25,6 +27,22 @@ type RepositoryData struct { UpdatedUnix timeutil.TimeStamp } +// Implements GetFieldsMap() from ShadowCopyData interface, returning a list of pairs +// to be used when rendering the shadow copy for admins reviewing the corresponding abuse report(s). +func (rd RepositoryData) GetFieldsMap() []moderation.ShadowCopyField { + return []moderation.ShadowCopyField{ + {Key: "OwnerID", Value: strconv.FormatInt(rd.OwnerID, 10)}, + {Key: "OwnerName", Value: rd.OwnerName}, + {Key: "Name", Value: rd.Name}, + {Key: "Description", Value: rd.Description}, + {Key: "Website", Value: rd.Website}, + {Key: "Topics", Value: strings.Join(rd.Topics, ", ")}, + {Key: "Avatar", Value: rd.Avatar}, + {Key: "CreatedUnix", Value: rd.CreatedUnix.AsLocalTime().String()}, + {Key: "UpdatedUnix", Value: rd.UpdatedUnix.AsLocalTime().String()}, + } +} + // newRepositoryData creates a trimmed down repository to be used just to create a JSON structure // (keeping only the fields relevant for moderation purposes) func newRepositoryData(repo *Repository) RepositoryData { diff --git a/models/repo/moderation_test.go b/models/repo/moderation_test.go new file mode 100644 index 0000000000..9852db1b51 --- /dev/null +++ b/models/repo/moderation_test.go @@ -0,0 +1,51 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package repo_test + +import ( + "testing" + + "forgejo.org/models/moderation" + "forgejo.org/models/repo" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +const ( + tsCreated timeutil.TimeStamp = timeutil.TimeStamp(1753093500) // 2025-07-21 10:25:00 UTC + tsUpdated timeutil.TimeStamp = timeutil.TimeStamp(1753093525) // 2025-07-21 10:25:25 UTC +) + +func testShadowCopyField(t *testing.T, scField moderation.ShadowCopyField, key, value string) { + assert.Equal(t, key, scField.Key) + assert.Equal(t, value, scField.Value) +} + +func TestRepositoryDataGetFieldsMap(t *testing.T) { + rd := repo.RepositoryData{ + OwnerID: 1002, + OwnerName: "alexsmith", + Name: "website", + Description: "My static website.", + Website: "http://promote-your-business.biz", + Topics: []string{"bulk-email", "email-services"}, + Avatar: "avatar-hash-repo-2002", + CreatedUnix: tsCreated, + UpdatedUnix: tsUpdated, + } + scFields := rd.GetFieldsMap() + + if assert.Len(t, scFields, 9) { + testShadowCopyField(t, scFields[0], "OwnerID", "1002") + testShadowCopyField(t, scFields[1], "OwnerName", "alexsmith") + testShadowCopyField(t, scFields[2], "Name", "website") + testShadowCopyField(t, scFields[3], "Description", "My static website.") + testShadowCopyField(t, scFields[4], "Website", "http://promote-your-business.biz") + testShadowCopyField(t, scFields[5], "Topics", "bulk-email, email-services") + testShadowCopyField(t, scFields[6], "Avatar", "avatar-hash-repo-2002") + testShadowCopyField(t, scFields[7], "CreatedUnix", tsCreated.AsLocalTime().String()) + testShadowCopyField(t, scFields[8], "UpdatedUnix", tsUpdated.AsLocalTime().String()) + } +} diff --git a/models/repo/pushmirror.go b/models/repo/pushmirror.go index d6d0d1135a..e57897fb7e 100644 --- a/models/repo/pushmirror.go +++ b/models/repo/pushmirror.go @@ -32,6 +32,7 @@ type PushMirror struct { Repo *Repository `xorm:"-"` RemoteName string RemoteAddress string `xorm:"VARCHAR(2048)"` + BranchFilter string `xorm:"VARCHAR(2048)"` // A keypair formatted in OpenSSH format. PublicKey string `xorm:"VARCHAR(100)"` @@ -122,6 +123,11 @@ func UpdatePushMirrorInterval(ctx context.Context, m *PushMirror) error { return err } +func UpdatePushMirrorBranchFilter(ctx context.Context, m *PushMirror) error { + _, err := db.GetEngine(ctx).ID(m.ID).Cols("branch_filter").Update(m) + return err +} + var DeletePushMirrors = deletePushMirrors func deletePushMirrors(ctx context.Context, opts PushMirrorOptions) error { diff --git a/models/repo/pushmirror_test.go b/models/repo/pushmirror_test.go index fbef835372..a7e063ff71 100644 --- a/models/repo/pushmirror_test.go +++ b/models/repo/pushmirror_test.go @@ -75,3 +75,139 @@ func TestPushMirrorPrivatekey(t *testing.T) { assert.Empty(t, actualPrivateKey) }) } + +func TestPushMirrorBranchFilter(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("Create push mirror with branch filter", func(t *testing.T) { + m := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-branch-filter", + BranchFilter: "main,develop", + } + unittest.AssertSuccessfulInsert(t, m) + assert.NotZero(t, m.ID) + assert.Equal(t, "main,develop", m.BranchFilter) + }) + + t.Run("Create push mirror with empty branch filter", func(t *testing.T) { + m := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-empty-filter", + BranchFilter: "", + } + unittest.AssertSuccessfulInsert(t, m) + assert.NotZero(t, m.ID) + assert.Empty(t, m.BranchFilter) + }) + + t.Run("Create push mirror without branch filter", func(t *testing.T) { + m := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-no-filter", + // BranchFilter: "", + } + unittest.AssertSuccessfulInsert(t, m) + assert.NotZero(t, m.ID) + assert.Empty(t, m.BranchFilter) + }) + + t.Run("Update branch filter", func(t *testing.T) { + m := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-update", + BranchFilter: "main", + } + unittest.AssertSuccessfulInsert(t, m) + + m.BranchFilter = "main,develop" + require.NoError(t, repo_model.UpdatePushMirrorBranchFilter(db.DefaultContext, m)) + + updated := unittest.AssertExistsAndLoadBean(t, &repo_model.PushMirror{ID: m.ID}) + assert.Equal(t, "main,develop", updated.BranchFilter) + }) + + t.Run("Retrieve push mirror with branch filter", func(t *testing.T) { + original := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-retrieve", + BranchFilter: "main,develop", + } + unittest.AssertSuccessfulInsert(t, original) + + retrieved := unittest.AssertExistsAndLoadBean(t, &repo_model.PushMirror{ID: original.ID}) + assert.Equal(t, original.BranchFilter, retrieved.BranchFilter) + assert.Equal(t, "main,develop", retrieved.BranchFilter) + }) + + t.Run("GetPushMirrorsByRepoID includes branch filter", func(t *testing.T) { + mirrors := []*repo_model.PushMirror{ + { + RepoID: 2, + RemoteName: "mirror-1", + BranchFilter: "main", + }, + { + RepoID: 2, + RemoteName: "mirror-2", + BranchFilter: "develop,feature-*", + }, + { + RepoID: 2, + RemoteName: "mirror-3", + BranchFilter: "", + }, + } + + for _, mirror := range mirrors { + unittest.AssertSuccessfulInsert(t, mirror) + } + + retrieved, count, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, 2, db.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, int64(3), count) + assert.Len(t, retrieved, 3) + + filterMap := make(map[string]string) + for _, mirror := range retrieved { + filterMap[mirror.RemoteName] = mirror.BranchFilter + } + + assert.Equal(t, "main", filterMap["mirror-1"]) + assert.Equal(t, "develop,feature-*", filterMap["mirror-2"]) + assert.Empty(t, filterMap["mirror-3"]) + }) + + t.Run("GetPushMirrorsSyncedOnCommit includes branch filter", func(t *testing.T) { + mirrors := []*repo_model.PushMirror{ + { + RepoID: 3, + RemoteName: "sync-mirror-1", + BranchFilter: "main,develop", + SyncOnCommit: true, + }, + { + RepoID: 3, + RemoteName: "sync-mirror-2", + BranchFilter: "feature-*", + SyncOnCommit: true, + }, + } + + for _, mirror := range mirrors { + unittest.AssertSuccessfulInsert(t, mirror) + } + + retrieved, err := repo_model.GetPushMirrorsSyncedOnCommit(db.DefaultContext, 3) + require.NoError(t, err) + assert.Len(t, retrieved, 2) + + filterMap := make(map[string]string) + for _, mirror := range retrieved { + filterMap[mirror.RemoteName] = mirror.BranchFilter + } + + assert.Equal(t, "main,develop", filterMap["sync-mirror-1"]) + assert.Equal(t, "feature-*", filterMap["sync-mirror-2"]) + }) +} diff --git a/models/repo/release.go b/models/repo/release.go index 10e9bb259f..b39a1de971 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -77,7 +77,7 @@ type Release struct { Target string TargetBehind string `xorm:"-"` // to handle non-existing or empty target Title string - Sha1 string `xorm:"VARCHAR(64)"` + Sha1 string `xorm:"INDEX VARCHAR(64)"` HideArchiveLinks bool `xorm:"NOT NULL DEFAULT false"` NumCommits int64 NumCommitsBehind int64 `xorm:"-"` @@ -618,3 +618,17 @@ func InsertReleases(ctx context.Context, rels ...*Release) error { return committer.Commit() } + +func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string) (map[string][]*Release, error) { + releases := make([]*Release, 0, len(commitIDs)) + if err := db.GetEngine(ctx).Where("repo_id=?", repoID). + In("sha1", commitIDs). + Find(&releases); err != nil { + return nil, err + } + res := make(map[string][]*Release, len(releases)) + for _, r := range releases { + res[r.Sha1] = append(res[r.Sha1], r) + } + return res, nil +} diff --git a/models/repo/release_test.go b/models/repo/release_test.go index 94dbd6d9d5..69f9333589 100644 --- a/models/repo/release_test.go +++ b/models/repo/release_test.go @@ -49,3 +49,16 @@ func TestReleaseDisplayName(t *testing.T) { release.Title = "Title" assert.Equal(t, "Title", release.DisplayName()) } + +func Test_FindTagsByCommitIDs(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + sha1Rels, err := FindTagsByCommitIDs(db.DefaultContext, 1, "65f1bf27bc3bf70f64657658635e66094edbcb4d") + require.NoError(t, err) + assert.Len(t, sha1Rels, 1) + rels := sha1Rels["65f1bf27bc3bf70f64657658635e66094edbcb4d"] + assert.Len(t, rels, 3) + assert.Equal(t, "v1.1", rels[0].TagName) + assert.Equal(t, "delete-tag", rels[1].TagName) + assert.Equal(t, "v1.0", rels[2].TagName) +} diff --git a/models/repo/repo_repository.go b/models/repo/repo_repository.go index 0ba50e6614..9d586b8345 100644 --- a/models/repo/repo_repository.go +++ b/models/repo/repo_repository.go @@ -38,18 +38,18 @@ func StoreFollowingRepos(ctx context.Context, localRepoID int64, followingRepoLi } // Begin transaction - ctx, committer, err := db.TxContext((ctx)) + dbCtx, committer, err := db.TxContext((ctx)) if err != nil { return err } defer committer.Close() - _, err = db.GetEngine(ctx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) + _, err = db.GetEngine(dbCtx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) if err != nil { return err } for _, followingRepo := range followingRepoList { - _, err = db.GetEngine(ctx).Insert(followingRepo) + _, err = db.GetEngine(dbCtx).Insert(followingRepo) if err != nil { return err } diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index e50f79e945..aa6f2fa0ae 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -336,5 +336,8 @@ func getUnitsByRepoID(ctx context.Context, repoID int64) (units []*RepoUnit, err // UpdateRepoUnit updates the provided repo unit func UpdateRepoUnit(ctx context.Context, unit *RepoUnit) error { _, err := db.GetEngine(ctx).ID(unit.ID).Update(unit) - return err + if err != nil { + return fmt.Errorf("UpdateRepoUnit: %v", err) + } + return nil } diff --git a/models/repo/topic.go b/models/repo/topic.go index 4a3bdc7d8c..9086f17627 100644 --- a/models/repo/topic.go +++ b/models/repo/topic.go @@ -164,7 +164,7 @@ func FindTopics(ctx context.Context, opts *FindTopicOptions) ([]*Topic, int64, e orderBy := "topic.repo_count DESC" if opts.RepoID > 0 { sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") - orderBy = "topic.name" // when render topics for a repo, it's better to sort them by name, to get consistent result + orderBy = "topic.name" // When rendering topics for a repo, it's better to sort them by name to get consistent results } if opts.PageSize > 0 { sess = db.SetSessionPagination(sess, opts) diff --git a/models/secret/main_test.go b/models/secret/main_test.go new file mode 100644 index 0000000000..85bfec0c4f --- /dev/null +++ b/models/secret/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package secret_test + +import ( + "testing" + + "forgejo.org/models/unittest" + + _ "forgejo.org/models" + _ "forgejo.org/models/activities" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} diff --git a/models/secret/secret.go b/models/secret/secret.go index 7be7f454a1..6f6867db52 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -11,9 +11,8 @@ import ( actions_model "forgejo.org/models/actions" "forgejo.org/models/db" actions_module "forgejo.org/modules/actions" + "forgejo.org/modules/keying" "forgejo.org/modules/log" - secret_module "forgejo.org/modules/secret" - "forgejo.org/modules/setting" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" @@ -39,7 +38,7 @@ type Secret struct { OwnerID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL"` RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL DEFAULT 0"` Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"` - Data string `xorm:"LONGTEXT"` // encrypted data + Data []byte `xorm:"BLOB"` // encrypted data CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` } @@ -67,17 +66,21 @@ func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, dat return nil, fmt.Errorf("%w: ownerID and repoID cannot be both zero, global secrets are not supported", util.ErrInvalidArgument) } - encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) - if err != nil { - return nil, err - } secret := &Secret{ OwnerID: ownerID, RepoID: repoID, Name: strings.ToUpper(name), - Data: encrypted, } - return secret, db.Insert(ctx, secret) + + return secret, db.WithTx(ctx, func(ctx context.Context) error { + if err := db.Insert(ctx, secret); err != nil { + return err + } + + secret.SetSecret(data) + _, err := db.GetEngine(ctx).ID(secret.ID).Cols("data").Update(secret) + return err + }) } func init() { @@ -113,21 +116,9 @@ func (opts FindSecretsOptions) ToConds() builder.Cond { return cond } -// UpdateSecret changes org or user reop secret. -func UpdateSecret(ctx context.Context, secretID int64, data string) error { - encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) - if err != nil { - return err - } - - s := &Secret{ - Data: encrypted, - } - affected, err := db.GetEngine(ctx).ID(secretID).Cols("data").Update(s) - if affected != 1 { - return ErrSecretNotFound{} - } - return err +func (s *Secret) SetSecret(data string) { + key := keying.DeriveKey(keying.ContextActionSecret) + s.Data = key.Encrypt([]byte(data), keying.ColumnAndID("data", s.ID)) } func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) { @@ -155,13 +146,14 @@ func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[ return nil, err } + key := keying.DeriveKey(keying.ContextActionSecret) for _, secret := range append(ownerSecrets, repoSecrets...) { - v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data) + v, err := key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) if err != nil { - log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err) + log.Error("unable to decrypt secret[id=%d,name=%q]: %v", secret.ID, secret.Name, err) return nil, err } - secrets[secret.Name] = v + secrets[secret.Name] = string(v) } return secrets, nil diff --git a/models/secret/secret_test.go b/models/secret/secret_test.go new file mode 100644 index 0000000000..15142d207b --- /dev/null +++ b/models/secret/secret_test.go @@ -0,0 +1,103 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package secret + +import ( + "testing" + + "forgejo.org/models/actions" + "forgejo.org/models/repo" + "forgejo.org/models/unittest" + "forgejo.org/modules/keying" + "forgejo.org/modules/util" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInsertEncryptedSecret(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("Global secret", func(t *testing.T) { + secret, err := InsertEncryptedSecret(t.Context(), 0, 0, "GLOBAL_SECRET", "some common secret") + require.ErrorIs(t, err, util.ErrInvalidArgument) + assert.Nil(t, secret) + }) + + key := keying.DeriveKey(keying.ContextActionSecret) + + t.Run("Insert repository secret", func(t *testing.T) { + secret, err := InsertEncryptedSecret(t.Context(), 0, 1, "REPO_SECRET", "some repository secret") + require.NoError(t, err) + assert.NotNil(t, secret) + assert.Equal(t, "REPO_SECRET", secret.Name) + assert.EqualValues(t, 1, secret.RepoID) + assert.NotEmpty(t, secret.Data) + + // Assert the secret is stored in the database. + unittest.AssertExistsAndLoadBean(t, &Secret{RepoID: 1, Name: "REPO_SECRET", Data: secret.Data}) + + t.Run("Keying", func(t *testing.T) { + // Cannot decrypt with different ID. + plainText, err := key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID+1)) + require.Error(t, err) + assert.Nil(t, plainText) + + // Cannot decrypt with different column. + plainText, err = key.Decrypt(secret.Data, keying.ColumnAndID("metadata", secret.ID)) + require.Error(t, err) + assert.Nil(t, plainText) + + // Can decrypt with correct column and ID. + plainText, err = key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) + require.NoError(t, err) + assert.EqualValues(t, "some repository secret", plainText) + }) + }) + + t.Run("Insert owner secret", func(t *testing.T) { + secret, err := InsertEncryptedSecret(t.Context(), 2, 0, "OWNER_SECRET", "some owner secret") + require.NoError(t, err) + assert.NotNil(t, secret) + assert.Equal(t, "OWNER_SECRET", secret.Name) + assert.EqualValues(t, 2, secret.OwnerID) + assert.NotEmpty(t, secret.Data) + + // Assert the secret is stored in the database. + unittest.AssertExistsAndLoadBean(t, &Secret{OwnerID: 2, Name: "OWNER_SECRET", Data: secret.Data}) + + t.Run("Keying", func(t *testing.T) { + // Cannot decrypt with different ID. + plainText, err := key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID+1)) + require.Error(t, err) + assert.Nil(t, plainText) + + // Cannot decrypt with different column. + plainText, err = key.Decrypt(secret.Data, keying.ColumnAndID("metadata", secret.ID)) + require.Error(t, err) + assert.Nil(t, plainText) + + // Can decrypt with correct column and ID. + plainText, err = key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) + require.NoError(t, err) + assert.EqualValues(t, "some owner secret", plainText) + }) + }) + + t.Run("Get secrets", func(t *testing.T) { + secrets, err := GetSecretsOfTask(t.Context(), &actions.ActionTask{ + Job: &actions.ActionRunJob{ + Run: &actions.ActionRun{ + RepoID: 1, + Repo: &repo.Repository{ + OwnerID: 2, + }, + }, + }, + }) + require.NoError(t, err) + assert.Equal(t, "some owner secret", secrets["OWNER_SECRET"]) + assert.Equal(t, "some repository secret", secrets["REPO_SECRET"]) + }) +} diff --git a/models/unittest/fixture_loader.go b/models/unittest/fixture_loader.go index 67ef1b28df..dda5c48d35 100644 --- a/models/unittest/fixture_loader.go +++ b/models/unittest/fixture_loader.go @@ -12,6 +12,8 @@ import ( "path/filepath" "strings" + "forgejo.org/modules/container" + "gopkg.in/yaml.v3" ) @@ -32,13 +34,15 @@ type loader struct { fixtureFiles []*fixtureFile } -func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string) (*loader, error) { +func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTableNames container.Set[string]) (*loader, error) { l := &loader{ db: db, dialect: dialect, fixtureFiles: []*fixtureFile{}, } + tablesWithoutFixture := allTableNames + // Load fixtures for _, fixturePath := range fixturePaths { stat, err := os.Stat(fixturePath) @@ -60,6 +64,7 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string) (*loade return nil, err } l.fixtureFiles = append(l.fixtureFiles, fixtureFile) + tablesWithoutFixture.Remove(fixtureFile.name) } } } else { @@ -71,6 +76,14 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string) (*loade } } + // Even though these tables have no fixtures, they can still be used and ensure + // they are cleaned. + for table := range tablesWithoutFixture.Seq() { + l.fixtureFiles = append(l.fixtureFiles, &fixtureFile{ + name: table, + }) + } + return l, nil } @@ -178,13 +191,13 @@ func (l *loader) Load() error { }() // Clean the table and re-insert the fixtures. - tableDeleted := map[string]struct{}{} + tableDeleted := make(container.Set[string]) for _, fixture := range l.fixtureFiles { - if _, ok := tableDeleted[fixture.name]; !ok { + if !tableDeleted.Contains(fixture.name) { if _, err := tx.Exec(fmt.Sprintf("DELETE FROM %s", l.quoteKeyword(fixture.name))); err != nil { return fmt.Errorf("cannot delete table %s: %w", fixture.name, err) } - tableDeleted[fixture.name] = struct{}{} + tableDeleted.Add(fixture.name) } for _, insertSQL := range fixture.insertSQLs { diff --git a/models/unittest/fixtures.go b/models/unittest/fixtures.go index 6dc5c8412d..829cc16466 100644 --- a/models/unittest/fixtures.go +++ b/models/unittest/fixtures.go @@ -7,10 +7,12 @@ package unittest import ( "fmt" "path/filepath" + "sync" "time" "forgejo.org/models/db" "forgejo.org/modules/auth/password/hash" + "forgejo.org/modules/container" "forgejo.org/modules/setting" "xorm.io/xorm" @@ -44,6 +46,8 @@ func OverrideFixtures(dir string) func() { } } +var allTableNames = sync.OnceValue(db.GetTableNames) + // InitFixtures initialize test fixtures for a test database func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { e, err := GetXORMEngine(engine...) @@ -75,7 +79,12 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { panic("Unsupported RDBMS for test") } - fixturesLoader, err = newFixtureLoader(e.DB().DB, dialect, fixturePaths) + var allTables container.Set[string] + if !opts.SkipCleanRegistedModels { + allTables = allTableNames().Clone() + } + + fixturesLoader, err = newFixtureLoader(e.DB().DB, dialect, fixturePaths, allTables) if err != nil { return err } diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index d34c9e9a0a..29ec82c55f 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -217,6 +217,10 @@ type FixturesOptions struct { Files []string Dirs []string Base string + // By default all registered models are cleaned, even if they do not have + // fixture. Enabling this will skip that and only models with fixtures are + // considered. + SkipCleanRegistedModels bool } // CreateTestEngine creates a memory database and loads the fixture data from fixturesDir diff --git a/models/user/activitypub.go b/models/user/activitypub.go index 816fd8a098..aabf2336fc 100644 --- a/models/user/activitypub.go +++ b/models/user/activitypub.go @@ -19,7 +19,7 @@ func (u *User) APActorID() string { return fmt.Sprintf("%sapi/v1/activitypub/user-id/%s", setting.AppURL, url.PathEscape(fmt.Sprintf("%d", u.ID))) } -// APActorKeyID returns the ID of the user's public key -func (u *User) APActorKeyID() string { +// KeyID returns the ID of the user's public key +func (u *User) KeyID() string { return u.APActorID() + "#main-key" } diff --git a/models/user/fixtures/login_source.yml b/models/user/fixtures/login_source.yml new file mode 100644 index 0000000000..3950f85964 --- /dev/null +++ b/models/user/fixtures/login_source.yml @@ -0,0 +1,8 @@ +- + id: 1001 + type: 6 # OAuth2 + name: OAuth2 authentication source + is_active: 1 + cfg: '{"Provider":"invalid","ClientID":"invalid","ClientSecret":"invalid","AllowUsernameChange":true}' + created_unix: 1753740851 + updated_unix: 1753740851 diff --git a/models/user/fixtures/user.yml b/models/user/fixtures/user.yml index b1892f331b..137064a368 100644 --- a/models/user/fixtures/user.yml +++ b/models/user/fixtures/user.yml @@ -11,6 +11,7 @@ must_change_password: false login_source: 1001 login_name: 123 + login_type: 6 type: 5 salt: ZogKvWdyEx max_repo_creation: -1 diff --git a/models/user/follow.go b/models/user/follow.go index e32c226385..8663b2a943 100644 --- a/models/user/follow.go +++ b/models/user/follow.go @@ -39,21 +39,21 @@ func FollowUser(ctx context.Context, userID, followID int64) (err error) { return ErrBlockedByUser } - ctx, committer, err := db.TxContext(ctx) + dbCtx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if err = db.Insert(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if err = db.Insert(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { return err } return committer.Commit() @@ -65,21 +65,21 @@ func UnfollowUser(ctx context.Context, userID, followID int64) (err error) { return nil } - ctx, committer, err := db.TxContext(ctx) + dbCtx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if _, err = db.DeleteByBean(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { return err } return committer.Commit() diff --git a/models/user/moderation.go b/models/user/moderation.go index afda497f02..17901f84ec 100644 --- a/models/user/moderation.go +++ b/models/user/moderation.go @@ -37,6 +37,26 @@ type UserData struct { //revive:disable-line:exported AvatarEmail string } +// Implements GetFieldsMap() from ShadowCopyData interface, returning a list of pairs +// to be used when rendering the shadow copy for admins reviewing the corresponding abuse report(s). +func (ud UserData) GetFieldsMap() []moderation.ShadowCopyField { + return []moderation.ShadowCopyField{ + {Key: "Name", Value: ud.Name}, + {Key: "FullName", Value: ud.FullName}, + {Key: "Email", Value: ud.Email}, + {Key: "LoginName", Value: ud.LoginName}, + {Key: "Location", Value: ud.Location}, + {Key: "Website", Value: ud.Website}, + {Key: "Pronouns", Value: ud.Pronouns}, + {Key: "Description", Value: ud.Description}, + {Key: "CreatedUnix", Value: ud.CreatedUnix.AsLocalTime().String()}, + {Key: "UpdatedUnix", Value: ud.UpdatedUnix.AsLocalTime().String()}, + {Key: "LastLogin", Value: ud.LastLogin.AsLocalTime().String()}, + {Key: "Avatar", Value: ud.Avatar}, + {Key: "AvatarEmail", Value: ud.AvatarEmail}, + } +} + // newUserData creates a trimmed down user to be used just to create a JSON structure // (keeping only the fields relevant for moderation purposes) func newUserData(user *User) UserData { @@ -73,16 +93,20 @@ var userDataColumnNames = sync.OnceValue(func() []string { // and if found a shadow copy of relevant user fields will be stored into DB and linked to the above report(s). // This function should be called before a user is deleted or updated. // +// In case the User object was already altered before calling this method, just provide the userID and +// nil for unalteredUser; when it is decided that a shadow copy should be created and unalteredUser is nil, +// the user will be retrieved from DB based on the provided userID. +// // For deletions alteredCols argument must be omitted. // // In case of updates it will first checks whether any of the columns being updated (alteredCols argument) // is relevant for moderation purposes (i.e. included in the UserData struct). -func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCols ...string) error { +func IfNeededCreateShadowCopyForUser(ctx context.Context, userID int64, unalteredUser *User, alteredCols ...string) error { // TODO: this can be triggered quite often (e.g. by routers/web/repo/middlewares.go SetDiffViewStyle()) shouldCheckIfNeeded := len(alteredCols) == 0 // no columns being updated, therefore a deletion if !shouldCheckIfNeeded { - // for updates we need to go further only if certain column are being changed + // for updates we need to go further only if certain columns are being changed for _, colName := range userDataColumnNames() { if shouldCheckIfNeeded = slices.Contains(alteredCols, colName); shouldCheckIfNeeded { break @@ -94,18 +118,23 @@ func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCol return nil } - shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, user.ID) + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, userID) if err != nil { return err } if shadowCopyNeeded { - userData := newUserData(user) + if unalteredUser == nil { + if unalteredUser, err = GetUserByID(ctx, userID); err != nil { + return err + } + } + userData := newUserData(unalteredUser) content, err := json.Marshal(userData) if err != nil { return err } - return moderation.CreateShadowCopyForUser(ctx, user.ID, string(content)) + return moderation.CreateShadowCopyForUser(ctx, userID, string(content)) } return nil diff --git a/models/user/moderation_test.go b/models/user/moderation_test.go new file mode 100644 index 0000000000..f951e41e11 --- /dev/null +++ b/models/user/moderation_test.go @@ -0,0 +1,60 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package user_test + +import ( + "testing" + + "forgejo.org/models/moderation" + "forgejo.org/models/user" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +const ( + tsCreated timeutil.TimeStamp = timeutil.TimeStamp(1753093200) // 2025-07-21 10:20:00 UTC + tsUpdated timeutil.TimeStamp = timeutil.TimeStamp(1753093320) // 2025-07-21 10:22:00 UTC + tsLastLogin timeutil.TimeStamp = timeutil.TimeStamp(1753093800) // 2025-07-21 10:30:00 UTC +) + +func testShadowCopyField(t *testing.T, scField moderation.ShadowCopyField, key, value string) { + assert.Equal(t, key, scField.Key) + assert.Equal(t, value, scField.Value) +} + +func TestUserDataGetFieldsMap(t *testing.T) { + ud := user.UserData{ + Name: "alexsmith", + FullName: "Alex Smith", + Email: "alexsmith@example.org", + LoginName: "", + Location: "@master@seo.net", + Website: "http://promote-your-business.biz", + Pronouns: "SEO", + Description: "I can help you promote your business online using SEO.", + CreatedUnix: tsCreated, + UpdatedUnix: tsUpdated, + LastLogin: tsLastLogin, + Avatar: "avatar-hash-user-1002", + AvatarEmail: "alexsmith@example.org", + } + scFields := ud.GetFieldsMap() + + if assert.Len(t, scFields, 13) { + testShadowCopyField(t, scFields[0], "Name", "alexsmith") + testShadowCopyField(t, scFields[1], "FullName", "Alex Smith") + testShadowCopyField(t, scFields[2], "Email", "alexsmith@example.org") + testShadowCopyField(t, scFields[3], "LoginName", "") + testShadowCopyField(t, scFields[4], "Location", "@master@seo.net") + testShadowCopyField(t, scFields[5], "Website", "http://promote-your-business.biz") + testShadowCopyField(t, scFields[6], "Pronouns", "SEO") + testShadowCopyField(t, scFields[7], "Description", "I can help you promote your business online using SEO.") + testShadowCopyField(t, scFields[8], "CreatedUnix", tsCreated.AsLocalTime().String()) + testShadowCopyField(t, scFields[9], "UpdatedUnix", tsUpdated.AsLocalTime().String()) + testShadowCopyField(t, scFields[10], "LastLogin", tsLastLogin.AsLocalTime().String()) + testShadowCopyField(t, scFields[11], "Avatar", "avatar-hash-user-1002") + testShadowCopyField(t, scFields[12], "AvatarEmail", "alexsmith@example.org") + } +} diff --git a/models/user/user.go b/models/user/user.go index b124572bb6..5f413ba136 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -234,6 +234,33 @@ func GetAllAdmins(ctx context.Context) ([]*User, error) { return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).And("is_admin = ?", true).Find(&users) } +// MustHaveTwoFactor returns true if the user is a individual and requires 2fa +func (u *User) MustHaveTwoFactor() bool { + if !u.IsIndividual() || setting.GlobalTwoFactorRequirement.IsNone() { + return false + } + + return setting.GlobalTwoFactorRequirement.IsAll() || (u.IsAdmin && setting.GlobalTwoFactorRequirement.IsAdmin()) +} + +// IsAccessAllowed determines whether the user is permitted to log in based on +// their activation status, login prohibition, 2FA requirement and 2FA enrollment status. +func (u *User) IsAccessAllowed(ctx context.Context) bool { + if !u.IsActive || u.ProhibitLogin { + return false + } + if !u.MustHaveTwoFactor() { + return true + } + + hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, u.ID) + if err != nil { + log.Error("Error getting 2fa: %s", err) + return false + } + return hasTwoFactor +} + // IsLocal returns true if user login type is LoginPlain. func (u *User) IsLocal() bool { return u.LoginType <= auth.Plain @@ -296,6 +323,9 @@ func (u *User) CanImportLocal() bool { // DashboardLink returns the user dashboard page link. func (u *User) DashboardLink() string { + if u.IsGhost() { + return "" + } if u.IsOrganization() { return u.OrganisationLink() + "/dashboard" } @@ -304,16 +334,25 @@ func (u *User) DashboardLink() string { // HomeLink returns the user or organization home page link. func (u *User) HomeLink() string { + if u.IsGhost() { + return "" + } return setting.AppSubURL + "/" + url.PathEscape(u.Name) } // HTMLURL returns the user or organization's full link. func (u *User) HTMLURL() string { + if u.IsGhost() { + return "" + } return setting.AppURL + url.PathEscape(u.Name) } // OrganisationLink returns the organization sub page link. func (u *User) OrganisationLink() string { + if u.IsGhost() || !u.IsOrganization() { + return "" + } return setting.AppSubURL + "/org/" + url.PathEscape(u.Name) } @@ -927,7 +966,9 @@ func UpdateUserCols(ctx context.Context, u *User, cols ...string) error { // If the user was reported as abusive and any of the columns being updated is relevant // for moderation purposes a shadow copy should be created before first update. - if err := IfNeededCreateShadowCopyForUser(ctx, u, cols...); err != nil { + // Since u is already altered at this point we are sending nil instead as an argument + // so that the unaltered version will be retrieved from DB. + if err := IfNeededCreateShadowCopyForUser(ctx, u.ID, nil, cols...); err != nil { return err } diff --git a/models/user/user_repository.go b/models/user/user_repository.go index 3f24efb1fb..df864746e8 100644 --- a/models/user/user_repository.go +++ b/models/user/user_repository.go @@ -28,7 +28,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat } // Begin transaction - ctx, committer, err := db.TxContext((ctx)) + txCtx, committer, err := db.TxContext(ctx) if err != nil { return err } @@ -39,7 +39,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat } }() - if err := CreateUser(ctx, user, &overwrite); err != nil { + if err := CreateUser(txCtx, user, &overwrite); err != nil { return err } @@ -48,7 +48,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat return err } - _, err = db.GetEngine(ctx).Insert(federatedUser) + _, err = db.GetEngine(txCtx).Insert(federatedUser) if err != nil { return err } @@ -57,14 +57,6 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat return committer.Commit() } -func (federatedUser *FederatedUser) UpdateFederatedUser(ctx context.Context) error { - if _, err := validation.IsValid(federatedUser); err != nil { - return err - } - _, err := db.GetEngine(ctx).ID(federatedUser.ID).Cols("inbox_path").Update(federatedUser) - return err -} - func FindFederatedUser(ctx context.Context, externalID string, federationHostID int64) (*User, *FederatedUser, error) { federatedUser := new(FederatedUser) user := new(User) @@ -78,7 +70,7 @@ func FindFederatedUser(ctx context.Context, externalID string, federationHostID if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -95,7 +87,7 @@ func GetFederatedUser(ctx context.Context, externalID string, federationHostID i if err != nil { return nil, nil, err } else if federatedUser == nil { - return nil, nil, fmt.Errorf("FederatedUser for externalId = %v and federationHostId = %v does not exist", externalID, federationHostID) + return nil, nil, fmt.Errorf("FederatedUser not found (given externalId: %v, federationHostId: %v)", externalID, federationHostID) } return user, federatedUser, nil } @@ -107,13 +99,13 @@ func GetFederatedUserByUserID(ctx context.Context, userID int64) (*User, *Federa if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("Federated user %v does not exist", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table does not contain entry for user ID: %v", federatedUser.UserID) } has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user) if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -138,7 +130,7 @@ func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *Federa if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -219,7 +211,6 @@ func RemoveFollower(ctx context.Context, followedUser *User, followingUser *Fede return err } -// TODO: We should unify Activity-pub-following and classical following (see models/user/follow.go) func IsFollowingAp(ctx context.Context, followedUser *User, followingUser *FederatedUser) (bool, error) { if res, err := validation.IsValid(followedUser); !res { return false, err diff --git a/models/user/user_test.go b/models/user/user_test.go index fd9d05653f..5b0c9676de 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -46,6 +46,28 @@ func TestIsValidUserID(t *testing.T) { assert.True(t, user_model.IsValidUserID(200)) } +func TestUserLinks(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + assert.Equal(t, "/", user1.DashboardLink()) + assert.Equal(t, "/user1", user1.HomeLink()) + assert.Equal(t, "https://try.gitea.io/user1", user1.HTMLURL()) + assert.Empty(t, user1.OrganisationLink()) + + org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) + assert.Equal(t, "/org/org3/dashboard", org3.DashboardLink()) + assert.Equal(t, "/org3", org3.HomeLink()) + assert.Equal(t, "https://try.gitea.io/org3", org3.HTMLURL()) + assert.Equal(t, "/org/org3", org3.OrganisationLink()) + + ghost := user_model.NewGhostUser() + assert.Empty(t, ghost.DashboardLink()) + assert.Empty(t, ghost.HomeLink()) + assert.Empty(t, ghost.HTMLURL()) + assert.Empty(t, ghost.OrganisationLink()) +} + func TestGetUserFromMap(t *testing.T) { id := int64(200) idMap := map[int64]*user_model.User{ @@ -150,7 +172,7 @@ func TestAPActorID_APActorID(t *testing.T) { func TestKeyID(t *testing.T) { user := user_model.User{ID: 1} - url := user.APActorKeyID() + url := user.KeyID() expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key" assert.Equal(t, expected, url) } @@ -615,6 +637,145 @@ func TestGetAllAdmins(t *testing.T) { assert.Equal(t, int64(1), admins[0].ID) } +func TestMustHaveTwoFactor(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + normalUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17}) + restrictedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}) + ghostUser := user_model.NewGhostUser() + + t.Run("NoneTwoFactorRequirement", func(t *testing.T) { + // this should be the default, so don't have to set the variable + assert.False(t, adminUser.MustHaveTwoFactor()) + assert.False(t, normalUser.MustHaveTwoFactor()) + assert.False(t, restrictedUser.MustHaveTwoFactor()) + assert.False(t, org.MustHaveTwoFactor()) + assert.False(t, ghostUser.MustHaveTwoFactor()) + }) + + t.Run("AllTwoFactorRequirement", func(t *testing.T) { + defer test.MockVariableValue(&setting.GlobalTwoFactorRequirement, setting.AllTwoFactorRequirement)() + + assert.True(t, adminUser.MustHaveTwoFactor()) + assert.True(t, normalUser.MustHaveTwoFactor()) + assert.True(t, restrictedUser.MustHaveTwoFactor()) + assert.False(t, org.MustHaveTwoFactor()) + assert.True(t, ghostUser.MustHaveTwoFactor()) + }) + + t.Run("AdminTwoFactorRequirement", func(t *testing.T) { + defer test.MockVariableValue(&setting.GlobalTwoFactorRequirement, setting.AdminTwoFactorRequirement)() + + assert.True(t, adminUser.MustHaveTwoFactor()) + assert.False(t, normalUser.MustHaveTwoFactor()) + assert.False(t, restrictedUser.MustHaveTwoFactor()) + assert.False(t, org.MustHaveTwoFactor()) + assert.False(t, ghostUser.MustHaveTwoFactor()) + }) +} + +func TestIsAccessAllowed(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + runTest := func(t *testing.T, user *user_model.User, useTOTP, accessAllowed bool) { + t.Helper() + if useTOTP { + unittest.AssertSuccessfulInsert(t, &auth.TwoFactor{UID: user.ID}) + defer unittest.AssertSuccessfulDelete(t, &auth.TwoFactor{UID: user.ID}) + } + + assert.Equal(t, accessAllowed, user.IsAccessAllowed(t.Context())) + } + + adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + normalUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + inactiveUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 9}) + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17}) + restrictedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}) + prohibitLoginUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 37}) + ghostUser := user_model.NewGhostUser() + + // users with enabled WebAuthn + normalWebAuthnUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 32}) + + t.Run("NoneTwoFactorRequirement", func(t *testing.T) { + // this should be the default, so don't have to set the variable + + t.Run("no 2fa", func(t *testing.T) { + runTest(t, adminUser, false, true) + runTest(t, normalUser, false, true) + runTest(t, inactiveUser, false, false) + runTest(t, org, false, true) + runTest(t, restrictedUser, false, true) + runTest(t, prohibitLoginUser, false, false) + runTest(t, ghostUser, false, false) + }) + + t.Run("enabled 2fa", func(t *testing.T) { + runTest(t, normalWebAuthnUser, false, true) + + runTest(t, adminUser, true, true) + runTest(t, normalUser, true, true) + runTest(t, inactiveUser, true, false) + runTest(t, org, true, true) + runTest(t, restrictedUser, true, true) + runTest(t, prohibitLoginUser, true, false) + }) + }) + + t.Run("AllTwoFactorRequirement", func(t *testing.T) { + defer test.MockVariableValue(&setting.GlobalTwoFactorRequirement, setting.AllTwoFactorRequirement)() + + t.Run("no 2fa", func(t *testing.T) { + runTest(t, adminUser, false, false) + runTest(t, normalUser, false, false) + runTest(t, inactiveUser, false, false) + runTest(t, org, false, true) + runTest(t, restrictedUser, false, false) + runTest(t, prohibitLoginUser, false, false) + runTest(t, ghostUser, false, false) + }) + + t.Run("enabled 2fa", func(t *testing.T) { + runTest(t, normalWebAuthnUser, false, true) + + runTest(t, adminUser, true, true) + runTest(t, normalUser, true, true) + runTest(t, inactiveUser, true, false) + runTest(t, org, true, true) + runTest(t, restrictedUser, true, true) + runTest(t, prohibitLoginUser, true, false) + }) + }) + + t.Run("AdminTwoFactorRequirement", func(t *testing.T) { + defer test.MockVariableValue(&setting.GlobalTwoFactorRequirement, setting.AdminTwoFactorRequirement)() + + t.Run("no 2fa", func(t *testing.T) { + runTest(t, adminUser, false, false) + runTest(t, normalUser, false, true) + runTest(t, inactiveUser, false, false) + runTest(t, org, false, true) + runTest(t, restrictedUser, false, true) + runTest(t, prohibitLoginUser, false, false) + runTest(t, ghostUser, false, false) + }) + + t.Run("enabled 2fa", func(t *testing.T) { + runTest(t, normalWebAuthnUser, false, true) + + runTest(t, adminUser, true, true) + runTest(t, normalUser, true, true) + runTest(t, inactiveUser, true, false) + runTest(t, org, true, true) + runTest(t, restrictedUser, true, true) + runTest(t, prohibitLoginUser, true, false) + }) + }) +} + func Test_ValidateUser(t *testing.T) { defer test.MockVariableValue(&setting.Service.AllowedUserVisibilityModesSlice, []bool{true, false, true})() diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index 7ae4557ed6..26d862ac4a 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -13,10 +13,10 @@ import ( api "forgejo.org/modules/structs" webhook_module "forgejo.org/modules/webhook" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/model" + "code.forgejo.org/forgejo/runner/v9/act/workflowpattern" "github.com/gobwas/glob" - "github.com/nektos/act/pkg/jobparser" - "github.com/nektos/act/pkg/model" - "github.com/nektos/act/pkg/workflowpattern" "gopkg.in/yaml.v3" ) @@ -86,7 +86,7 @@ func GetContentFromEntry(entry *git.TreeEntry) ([]byte, error) { } func GetEventsFromContent(content []byte) ([]*jobparser.Event, error) { - workflow, err := model.ReadWorkflow(bytes.NewReader(content)) + workflow, err := model.ReadWorkflow(bytes.NewReader(content), false) if err != nil { return nil, err } diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go index d015fb7bec..11a2fd94c3 100644 --- a/modules/activitypub/client.go +++ b/modules/activitypub/client.go @@ -66,6 +66,11 @@ type ClientFactory struct { // NewClient function func NewClientFactory() (c *ClientFactory, err error) { + return NewClientFactoryWithTimeout(5 * time.Second) +} + +// NewClient function +func NewClientFactoryWithTimeout(timeout time.Duration) (c *ClientFactory, err error) { if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil { return nil, err } else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil { @@ -77,7 +82,7 @@ func NewClientFactory() (c *ClientFactory, err error) { Transport: &http.Transport{ Proxy: proxy.Proxy(), }, - Timeout: 5 * time.Second, + Timeout: timeout, }, algs: setting.HttpsigAlgs, digestAlg: httpsig.DigestAlgorithm(setting.Federation.DigestAlgorithm), @@ -89,6 +94,7 @@ func NewClientFactory() (c *ClientFactory, err error) { type APClientFactory interface { WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) + WithKeysDirect(ctx context.Context, privateKey, pubID string) (APClient, error) } // Client struct @@ -103,12 +109,8 @@ type Client struct { } // NewRequest function -func (cf *ClientFactory) WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) { - priv, err := GetPrivateKey(ctx, user) - if err != nil { - return nil, err - } - privPem, _ := pem.Decode([]byte(priv)) +func (cf *ClientFactory) WithKeysDirect(ctx context.Context, privateKey, pubID string) (APClient, error) { + privPem, _ := pem.Decode([]byte(privateKey)) privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes) if err != nil { return nil, err @@ -126,6 +128,14 @@ func (cf *ClientFactory) WithKeys(ctx context.Context, user *user_model.User, pu return &c, nil } +func (cf *ClientFactory) WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) { + priv, err := GetPrivateKey(ctx, user) + if err != nil { + return nil, err + } + return cf.WithKeysDirect(ctx, priv, pubID) +} + // NewRequest function func (c *Client) newRequest(method string, b []byte, to string) (req *http.Request, err error) { buf := bytes.NewBuffer(b) @@ -149,12 +159,14 @@ func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) { return nil, err } - signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime) - if err != nil { - return nil, err - } - if err := signer.SignRequest(c.priv, c.pubID, req, b); err != nil { - return nil, err + if c.pubID != "" { + signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime) + if err != nil { + return nil, err + } + if err := signer.SignRequest(c.priv, c.pubID, req, b); err != nil { + return nil, err + } } resp, err = c.client.Do(req) @@ -167,12 +179,15 @@ func (c *Client) Get(to string) (resp *http.Response, err error) { if req, err = c.newRequest(http.MethodGet, nil, to); err != nil { return nil, err } - signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.getHeaders, httpsig.Signature, httpsigExpirationTime) - if err != nil { - return nil, err - } - if err := signer.SignRequest(c.priv, c.pubID, req, nil); err != nil { - return nil, err + + if c.pubID != "" { + signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.getHeaders, httpsig.Signature, httpsigExpirationTime) + if err != nil { + return nil, err + } + if err := signer.SignRequest(c.priv, c.pubID, req, nil); err != nil { + return nil, err + } } resp, err = c.client.Do(req) diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index 48c6728f43..2041f28bb1 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -56,14 +56,7 @@ func Local(name, base string, sub ...string) *Layer { panic(fmt.Sprintf("Unable to get absolute path for %q: %v", base, err)) } root := util.FilePathJoinAbs(base, sub...) - fsRoot, err := os.OpenRoot(root) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - return nil - } - panic(fmt.Sprintf("Unable to open layer %q", err)) - } - return &Layer{name: name, fs: fsRoot.FS(), localPath: root} + return &Layer{name: name, fs: os.DirFS(root), localPath: root} } // Bindata returns a new Layer with the given name, it serves files from the given bindata asset. @@ -80,7 +73,7 @@ type LayeredFS struct { // Layered returns a new LayeredFS with the given layers. The first layer is the top layer. func Layered(layers ...*Layer) *LayeredFS { - return &LayeredFS{layers: slices.DeleteFunc(layers, func(layer *Layer) bool { return layer == nil })} + return &LayeredFS{layers: layers} } // Open opens the named file. The caller is responsible for closing the file. diff --git a/modules/assetfs/layered_test.go b/modules/assetfs/layered_test.go index 87d1f92b00..76eeb61d83 100644 --- a/modules/assetfs/layered_test.go +++ b/modules/assetfs/layered_test.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 assetfs @@ -108,3 +109,30 @@ func TestLayered(t *testing.T) { assert.Equal(t, "l1", assets.GetFileLayerName("f1")) assert.Equal(t, "l2", assets.GetFileLayerName("f2")) } + +// Allow layers to read symlink outside the layer root. +func TestLayeredSymlink(t *testing.T) { + dir := t.TempDir() + dirl1 := filepath.Join(dir, "l1") + require.NoError(t, os.MkdirAll(dirl1, 0o755)) + + // Open layer in dir/l1 + layer := Local("l1", dirl1) + + // Create a file in dir/outside + fileContents := []byte("I am outside the layer") + require.NoError(t, os.WriteFile(filepath.Join(dir, "outside"), fileContents, 0o600)) + // Symlink dir/l1/outside to dir/outside + require.NoError(t, os.Symlink(filepath.Join(dir, "outside"), filepath.Join(dirl1, "outside"))) + + // Open dir/l1/outside. + f, err := layer.Open("outside") + require.NoError(t, err) + defer f.Close() + + // Confirm it contains the output of dir/outside + contents, err := io.ReadAll(f) + require.NoError(t, err) + + assert.Equal(t, fileContents, contents) +} diff --git a/modules/base/tool.go b/modules/base/tool.go index fd6a7c2b77..e3a3ff4a23 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -114,7 +114,7 @@ func EntryIcon(entry *git.TreeEntry) string { return "file-symlink-file" case entry.IsDir(): return "file-directory-fill" - case entry.IsSubModule(): + case entry.IsSubmodule(): return "file-submodule" } diff --git a/modules/container/set.go b/modules/container/set.go index 70f837bc66..d3719dc552 100644 --- a/modules/container/set.go +++ b/modules/container/set.go @@ -74,3 +74,8 @@ func (s Set[T]) Values() []T { func (s Set[T]) Seq() iter.Seq[T] { return maps.Keys(s) } + +// Clone returns a identical shallow copy of this set. +func (s Set[T]) Clone() Set[T] { + return maps.Clone(s) +} diff --git a/modules/container/set_test.go b/modules/container/set_test.go index af5e9126ab..44e4847f6b 100644 --- a/modules/container/set_test.go +++ b/modules/container/set_test.go @@ -47,4 +47,11 @@ func TestSet(t *testing.T) { assert.False(t, s.IsSubset([]string{"key1"})) assert.True(t, s.IsSubset([]string{})) + + t.Run("Clone", func(t *testing.T) { + clonedSet := s.Clone() + clonedSet.Remove("key6") + assert.False(t, clonedSet.Contains("key6")) + assert.True(t, s.Contains("key6")) + }) } diff --git a/modules/forgefed/activity_follow_test.go b/modules/forgefed/activity_follow_test.go index bb0c1de2f7..8ba31d5f6f 100644 --- a/modules/forgefed/activity_follow_test.go +++ b/modules/forgefed/activity_follow_test.go @@ -9,6 +9,7 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" ) func Test_NewForgeFollowValidation(t *testing.T) { @@ -17,15 +18,13 @@ func Test_NewForgeFollowValidation(t *testing.T) { sut.Actor = ap.IRI("example.org/alice") sut.Object = ap.IRI("example.org/bob") - if err, _ := validation.IsValid(sut); !err { - t.Errorf("sut is invalid: %v\n", err) - } + valid, err := validation.IsValid(sut) + assert.True(t, valid, "sut is invalid: %v\n", err) sut = ForgeFollow{} sut.Actor = ap.IRI("example.org/alice") sut.Object = ap.IRI("example.org/bob") - if err, _ := validation.IsValid(sut); err { - t.Errorf("sut is valid: %v\n", err) - } + valid, err = validation.IsValid(sut) + assert.False(t, valid, "sut is valid: %v\n", err) } diff --git a/modules/forgefed/activity_like_test.go b/modules/forgefed/activity_like_test.go index 6b252d5960..eef5563d8b 100644 --- a/modules/forgefed/activity_like_test.go +++ b/modules/forgefed/activity_like_test.go @@ -13,6 +13,8 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_NewForgeLike(t *testing.T) { @@ -22,21 +24,14 @@ func Test_NewForgeLike(t *testing.T) { objectIRI := "https://codeberg.org/api/v1/activitypub/repository-id/1" startTime, _ := time.Parse("2006-Jan-02", "2024-Mar-07") sut, err := NewForgeLike(actorIRI, objectIRI, startTime) - if err != nil { - t.Errorf("unexpected error: %v\n", err) - } - if valid, _ := validation.IsValid(sut); !valid { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + require.NoError(t, err, "unexpected error: %v\n", err) + + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) got, err := sut.MarshalJSON() - if err != nil { - t.Errorf("MarshalJSON() error = \"%v\"", err) - return - } - if !reflect.DeepEqual(got, want) { - t.Errorf("MarshalJSON() got = %q, want %q", got, want) - } + require.NoError(t, err, "MarshalJSON() error = %q", err) + assert.True(t, reflect.DeepEqual(got, want), "MarshalJSON()\n got: %q,\n want: %q", got, want) } func Test_LikeMarshalJSON(t *testing.T) { @@ -66,13 +61,8 @@ func Test_LikeMarshalJSON(t *testing.T) { for name, tt := range tests { t.Run(name, func(t *testing.T) { got, err := tt.item.MarshalJSON() - if (err != nil || tt.wantErr != nil) && tt.wantErr.Error() != err.Error() { - t.Errorf("MarshalJSON() error = \"%v\", wantErr \"%v\"", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MarshalJSON() got = %q, want %q", got, tt.want) - } + assert.False(t, (err != nil || tt.wantErr != nil) && tt.wantErr.Error() != err.Error(), "MarshalJSON()\n got: %v,\n want: %v", err, tt.wantErr) + assert.True(t, reflect.DeepEqual(got, tt.want), "MarshalJSON()\n got: %q\n want: %q", got, tt.want) }) } } @@ -89,8 +79,8 @@ func Test_LikeUnmarshalJSON(t *testing.T) { item: []byte(`{"type":"Like","actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1","object":"https://codeberg.org/api/activitypub/repository-id/1"}`), want: &ForgeLike{ Activity: ap.Activity{ - Actor: ap.IRI("https://repo.prod.meissa.de/api/activitypub/user-id/1"), Type: "Like", + Actor: ap.IRI("https://repo.prod.meissa.de/api/activitypub/user-id/1"), Object: ap.IRI("https://codeberg.org/api/activitypub/repository-id/1"), }, }, @@ -107,12 +97,10 @@ func Test_LikeUnmarshalJSON(t *testing.T) { t.Run(name, func(t *testing.T) { got := new(ForgeLike) err := got.UnmarshalJSON(test.item) - if (err != nil || test.wantErr != nil) && !strings.Contains(err.Error(), test.wantErr.Error()) { - t.Errorf("UnmarshalJSON() error = \"%v\", wantErr \"%v\"", err, test.wantErr) - return - } + assert.False(t, (err != nil || test.wantErr != nil) && !strings.Contains(err.Error(), test.wantErr.Error()), "UnmarshalJSON()\n error: %v\n wantErr: %v", err, test.wantErr) + if !reflect.DeepEqual(got, test.want) { - t.Errorf("UnmarshalJSON() got = %q, want %q, err %q", got, test.want, err.Error()) + assert.Errorf(t, err, "UnmarshalJSON() got = %q, want %q, err %q", got, test.want, err.Error()) } }) } @@ -120,46 +108,47 @@ func Test_LikeUnmarshalJSON(t *testing.T) { func Test_ForgeLikeValidation(t *testing.T) { // Successful - sut := new(ForgeLike) sut.UnmarshalJSON([]byte(`{"type":"Like", "actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", "object":"https://codeberg.org/api/activitypub/repository-id/1", "startTime": "2014-12-31T23:00:00-08:00"}`)) - if res, _ := validation.IsValid(sut); !res { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) // Errors - sut.UnmarshalJSON([]byte(`{"actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", "object":"https://codeberg.org/api/activitypub/repository-id/1", "startTime": "2014-12-31T23:00:00-08:00"}`)) - if err := validateAndCheckError(sut, "type should not be empty"); err != nil { - t.Error(err) - } + validate := sut.Validate() + assert.Len(t, validate, 2) + assert.Equal(t, + "Field type contains the value , which is not in allowed subset [Like]", + validate[1]) sut.UnmarshalJSON([]byte(`{"type":"bad-type", "actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", "object":"https://codeberg.org/api/activitypub/repository-id/1", "startTime": "2014-12-31T23:00:00-08:00"}`)) - if err := validateAndCheckError(sut, "Field type contains the value bad-type, which is not in allowed subset [Like]"); err != nil { - t.Error(err) - } + validate = sut.Validate() + assert.Len(t, validate, 1) + assert.Equal(t, + "Field type contains the value bad-type, which is not in allowed subset [Like]", + validate[0]) sut.UnmarshalJSON([]byte(`{"type":"Like", "actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", "object":"https://codeberg.org/api/activitypub/repository-id/1", "startTime": "not a date"}`)) - if err := validateAndCheckError(sut, "StartTime was invalid."); err != nil { - t.Error(err) - } + validate = sut.Validate() + assert.Len(t, validate, 1) + assert.Equal(t, + "StartTime was invalid.", + validate[0]) } func TestActivityValidation_Attack(t *testing.T) { sut := new(ForgeLike) sut.UnmarshalJSON([]byte(`{rubbish}`)) - if len(sut.Validate()) != 5 { - t.Errorf("5 validation errors expected but was: %v\n", len(sut.Validate())) - } + assert.Len(t, sut.Validate(), 5) } diff --git a/modules/forgefed/activity_undo_like_test.go b/modules/forgefed/activity_undo_like_test.go index 5867a84e7b..76358b1669 100644 --- a/modules/forgefed/activity_undo_like_test.go +++ b/modules/forgefed/activity_undo_like_test.go @@ -173,7 +173,7 @@ func TestActivityValidationUndo(t *testing.T) { "startTime":"2024-03-27T00:00:00Z", "actor":"https://repo.prod.meissa.de/api/v1/activitypub/user-id/1", "object":"https://codeberg.org/api/v1/activitypub/repository-id/1"}}`)) - if err := validateAndCheckError(sut, "type should not be empty"); err != nil { + if err := validateAndCheckError(sut, "Value type should not be empty"); err != nil { t.Error(*err) } diff --git a/modules/forgefed/activity_user_activity_test.go b/modules/forgefed/activity_user_activity_test.go index 9cb9f133b9..107ae51204 100644 --- a/modules/forgefed/activity_user_activity_test.go +++ b/modules/forgefed/activity_user_activity_test.go @@ -9,6 +9,7 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" ) func Test_ForgeUserActivityValidation(t *testing.T) { @@ -34,7 +35,6 @@ func Test_ForgeUserActivityValidation(t *testing.T) { sut.Note = note - if res, _ := validation.IsValid(sut); !res { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) } diff --git a/modules/forgefed/actor_person_test.go b/modules/forgefed/actor_person_test.go index f466ddb964..e4f1734a9d 100644 --- a/modules/forgefed/actor_person_test.go +++ b/modules/forgefed/actor_person_test.go @@ -115,7 +115,7 @@ func TestPersonIdValidation(t *testing.T) { result, err := validation.IsValid(sut) assert.False(t, result) - require.EqualError(t, err, "Validation Error: forgefed.PersonID: path should not be empty\npath: \"\" has to be a person specific api path") + require.EqualError(t, err, "Validation Error: forgefed.PersonID: Value path should not be empty\npath: \"\" has to be a person specific api path") sut = PersonID{} sut.ID = "1" @@ -166,38 +166,28 @@ func TestWebfingerId(t *testing.T) { } func TestShouldThrowErrorOnInvalidInput(t *testing.T) { - var err any - _, err = NewPersonID("", "forgejo") - if err == nil { - t.Errorf("empty input should be invalid.") + tests := []struct { + input string + username string + expectErr bool + }{ + {"", "forgejo", true}, + {"http://localhost:3000/api/v1/something", "forgejo", true}, + {"./api/v1/something", "forgejo", true}, + {"http://1.2.3.4/api/v1/something", "forgejo", true}, + {"http:///[fe80::1ff:fe23:4567:890a%25eth0]/api/v1/something", "forgejo", true}, + {"https://codeberg.org/api/v1/activitypub/../activitypub/user-id/12345", "forgejo", true}, + {"https://myuser@an.other.host/api/v1/activitypub/user-id/1", "forgejo", true}, + {"https://an.other.host/api/v1/activitypub/user-id/1", "forgejo", false}, } - _, err = NewPersonID("http://localhost:3000/api/v1/something", "forgejo") - if err == nil { - t.Errorf("localhost uris are not external") - } - _, err = NewPersonID("./api/v1/something", "forgejo") - if err == nil { - t.Errorf("relative uris are not allowed") - } - _, err = NewPersonID("http://1.2.3.4/api/v1/something", "forgejo") - if err == nil { - t.Errorf("uri may not be ip-4 based") - } - _, err = NewPersonID("http:///[fe80::1ff:fe23:4567:890a%25eth0]/api/v1/something", "forgejo") - if err == nil { - t.Errorf("uri may not be ip-6 based") - } - _, err = NewPersonID("https://codeberg.org/api/v1/activitypub/../activitypub/user-id/12345", "forgejo") - if err == nil { - t.Errorf("uri may not contain relative path elements") - } - _, err = NewPersonID("https://myuser@an.other.host/api/v1/activitypub/user-id/1", "forgejo") - if err == nil { - t.Errorf("uri may not contain unparsed elements") - } - _, err = NewPersonID("https://an.other.host/api/v1/activitypub/user-id/1", "forgejo") - if err != nil { - t.Errorf("this uri should be valid but was: %v", err) + + for _, tt := range tests { + _, err := NewPersonID(tt.input, tt.username) + if tt.expectErr { + assert.Error(t, err, "Expected an error for input: %s", tt.input) + } else { + assert.NoError(t, err, "Expected no error for input: %s, but got: %v", tt.input, err) + } } } @@ -221,14 +211,11 @@ func Test_PersonUnmarshalJSON(t *testing.T) { } sut := new(ForgePerson) err := sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`)) - if err != nil { - t.Errorf("UnmarshalJSON() unexpected error: %v", err) - } + require.NoError(t, err, "UnmarshalJSON() unexpected error: %q", err) + x, _ := expected.MarshalJSON() y, _ := sut.MarshalJSON() - if !reflect.DeepEqual(x, y) { - t.Errorf("UnmarshalJSON() expected: %q got: %q", x, y) - } + assert.True(t, reflect.DeepEqual(x, y), "UnmarshalJSON()\n got: %q,\n want: %q", x, y) expectedStr := strings.ReplaceAll(strings.ReplaceAll(`{ "id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10", @@ -244,9 +231,7 @@ func Test_PersonUnmarshalJSON(t *testing.T) { "\n", ""), "\t", "") err = sut.UnmarshalJSON([]byte(expectedStr)) - if err != nil { - t.Errorf("UnmarshalJSON() unexpected error: %v", err) - } + require.NoError(t, err, "UnmarshalJSON() unexpected error: %q", err) result, _ := sut.MarshalJSON() assert.JSONEq(t, expectedStr, string(result), "Expected string is not equal") } @@ -254,9 +239,8 @@ func Test_PersonUnmarshalJSON(t *testing.T) { func TestForgePersonValidation(t *testing.T) { sut := new(ForgePerson) sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`)) - if res, _ := validation.IsValid(sut); !res { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) } func TestAsloginName(t *testing.T) { diff --git a/modules/forgefed/actor_test.go b/modules/forgefed/actor_test.go index 48d773c5b9..a32114616c 100644 --- a/modules/forgefed/actor_test.go +++ b/modules/forgefed/actor_test.go @@ -58,7 +58,7 @@ func TestActorIdValidation(t *testing.T) { sut.UnvalidatedInput = "https://an.other.host/api/v1/activitypub/user-id/" result := sut.Validate() assert.Len(t, result, 1) - assert.Equal(t, "ID should not be empty", result[0]) + assert.Equal(t, "Value ID should not be empty", result[0]) sut = ActorID{} sut.ID = "1" diff --git a/modules/forgefed/object_user_activity_note_test.go b/modules/forgefed/object_user_activity_note_test.go index 20c3666bb1..02aebd58d3 100644 --- a/modules/forgefed/object_user_activity_note_test.go +++ b/modules/forgefed/object_user_activity_note_test.go @@ -9,6 +9,7 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" ) func Test_UserActivityNoteValidation(t *testing.T) { @@ -22,7 +23,6 @@ func Test_UserActivityNoteValidation(t *testing.T) { } sut.URL = ap.IRI("example.org/user-id/57") - if res, _ := validation.IsValid(sut); !res { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) } diff --git a/modules/git/blame.go b/modules/git/blame.go index 4ff347e31b..868edab2b8 100644 --- a/modules/git/blame.go +++ b/modules/git/blame.go @@ -132,7 +132,7 @@ func (r *BlameReader) Close() error { // CreateBlameReader creates reader for given repository, commit and file func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) { var ignoreRevsFile *string - if CheckGitVersionAtLeast("2.23") == nil && !bypassBlameIgnore { + if !bypassBlameIgnore { ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit) } diff --git a/modules/git/blob.go b/modules/git/blob.go index 8c5c275146..4eef5f0e2a 100644 --- a/modules/git/blob.go +++ b/modules/git/blob.go @@ -8,6 +8,7 @@ import ( "bufio" "bytes" "encoding/base64" + "fmt" "io" "forgejo.org/modules/log" @@ -172,60 +173,43 @@ func (b *Blob) GetBlobContent(limit int64) (string, error) { return string(buf), err } -// GetBlobLineCount gets line count of the blob -func (b *Blob) GetBlobLineCount() (int, error) { - reader, err := b.DataAsync() - if err != nil { - return 0, err - } - defer reader.Close() - buf := make([]byte, 32*1024) - count := 1 - lineSep := []byte{'\n'} - - c, err := reader.Read(buf) - if c == 0 && err == io.EOF { - return 0, nil - } - for { - count += bytes.Count(buf[:c], lineSep) - switch { - case err == io.EOF: - return count, nil - case err != nil: - return count, err - } - c, err = reader.Read(buf) - } +type BlobTooLargeError struct { + Size, Limit int64 } -// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string -func (b *Blob) GetBlobContentBase64() (string, error) { - dataRc, err := b.DataAsync() - if err != nil { - return "", err - } - defer dataRc.Close() +func (b BlobTooLargeError) Error() string { + return fmt.Sprintf("blob: content larger than limit (%d > %d)", b.Size, b.Limit) +} - pr, pw := io.Pipe() - encoder := base64.NewEncoder(base64.StdEncoding, pw) - - go func() { - _, err := io.Copy(encoder, dataRc) - _ = encoder.Close() - - if err != nil { - _ = pw.CloseWithError(err) - } else { - _ = pw.Close() +// GetContentBase64 Reads the content of the blob and returns it as base64 encoded string. +// Returns [BlobTooLargeError] if the (unencoded) content is larger than the limit. +func (b *Blob) GetContentBase64(limit int64) (string, error) { + if b.Size() > limit { + return "", BlobTooLargeError{ + Size: b.Size(), + Limit: limit, } - }() + } - out, err := io.ReadAll(pr) + rc, size, err := b.NewTruncatedReader(limit) if err != nil { return "", err } - return string(out), nil + defer rc.Close() + + encoding := base64.StdEncoding + buf := bytes.NewBuffer(make([]byte, 0, encoding.EncodedLen(int(size)))) + + encoder := base64.NewEncoder(encoding, buf) + + if _, err := io.Copy(encoder, rc); err != nil { + return "", err + } + if err := encoder.Close(); err != nil { + return "", err + } + + return buf.String(), nil } // GuessContentType guesses the content type of the blob. @@ -236,7 +220,7 @@ func (b *Blob) GuessContentType() (typesniffer.SniffedType, error) { } defer r.Close() - return typesniffer.DetectContentTypeFromReader(r) + return typesniffer.DetectContentTypeFromReader(r, b.Name()) } // GetBlob finds the blob object in the repository. diff --git a/modules/git/blob_test.go b/modules/git/blob_test.go index 54115013d3..a4b8033941 100644 --- a/modules/git/blob_test.go +++ b/modules/git/blob_test.go @@ -63,6 +63,24 @@ func TestBlob(t *testing.T) { require.Equal(t, "file2\n", r) }) + t.Run("GetContentBase64", func(t *testing.T) { + r, err := testBlob.GetContentBase64(100) + require.NoError(t, err) + require.Equal(t, "ZmlsZTIK", r) + + r, err = testBlob.GetContentBase64(-1) + require.ErrorAs(t, err, &BlobTooLargeError{}) + require.Empty(t, r) + + r, err = testBlob.GetContentBase64(4) + require.ErrorAs(t, err, &BlobTooLargeError{}) + require.Empty(t, r) + + r, err = testBlob.GetContentBase64(6) + require.NoError(t, err) + require.Equal(t, "ZmlsZTIK", r) + }) + t.Run("NewTruncatedReader", func(t *testing.T) { // read fewer than available rc, size, err := testBlob.NewTruncatedReader(100) diff --git a/modules/git/commit.go b/modules/git/commit.go index 96831e3ae4..4fb13ecd4f 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -16,8 +16,6 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/util" - - "github.com/go-git/go-git/v5/config" ) // Commit represents a git commit. @@ -29,8 +27,8 @@ type Commit struct { CommitMessage string Signature *ObjectSignature - Parents []ObjectID // ID strings - submoduleCache *ObjectCache + Parents []ObjectID // ID strings + submodules map[string]Submodule // submodule indexed by path } // Message returns the commit message. Same as retrieving CommitMessage directly. @@ -352,71 +350,9 @@ func (c *Commit) GetFileContent(filename string, limit int) (string, error) { return string(bytes), nil } -// GetSubModules get all the sub modules of current revision git tree -func (c *Commit) GetSubModules() (*ObjectCache, error) { - if c.submoduleCache != nil { - return c.submoduleCache, nil - } - - entry, err := c.GetTreeEntryByPath(".gitmodules") - if err != nil { - if _, ok := err.(ErrNotExist); ok { - return nil, nil - } - return nil, err - } - - content, err := entry.Blob().GetBlobContent(10 * 1024) - if err != nil { - return nil, err - } - - c.submoduleCache, err = parseSubmoduleContent([]byte(content)) - if err != nil { - return nil, err - } - return c.submoduleCache, nil -} - -func parseSubmoduleContent(bs []byte) (*ObjectCache, error) { - cfg := config.NewModules() - if err := cfg.Unmarshal(bs); err != nil { - return nil, err - } - submoduleCache := newObjectCache() - if len(cfg.Submodules) == 0 { - return nil, errors.New("no submodules found") - } - for _, subModule := range cfg.Submodules { - submoduleCache.Set(subModule.Path, subModule.URL) - } - - return submoduleCache, nil -} - -// GetSubModule returns the URL to the submodule according entryname -func (c *Commit) GetSubModule(entryname string) (string, error) { - modules, err := c.GetSubModules() - if err != nil { - return "", err - } - - if modules != nil { - module, has := modules.Get(entryname) - if has { - return module.(string), nil - } - } - return "", nil -} - // GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only') func (c *Commit) GetBranchName() (string, error) { - cmd := NewCommand(c.repo.Ctx, "name-rev") - if CheckGitVersionAtLeast("2.13.0") == nil { - cmd.AddArguments("--exclude", "refs/tags/*") - } - cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) + cmd := NewCommand(c.repo.Ctx, "name-rev", "--exclude", "refs/tags/*", "--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) data, _, err := cmd.RunStdString(&RunOpts{Dir: c.repo.Path}) if err != nil { // handle special case where git can not describe commit diff --git a/modules/git/commit_info.go b/modules/git/commit_info.go index 8d9142d362..6511a1689a 100644 --- a/modules/git/commit_info.go +++ b/modules/git/commit_info.go @@ -15,9 +15,9 @@ import ( // CommitInfo describes the first commit with the provided entry type CommitInfo struct { - Entry *TreeEntry - Commit *Commit - SubModuleFile *SubModuleFile + Entry *TreeEntry + Commit *Commit + Submodule Submodule } // GetCommitsInfo gets information of all commits that are corresponding to these entries @@ -71,19 +71,18 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath } // If the entry if a submodule add a submodule file for this - if entry.IsSubModule() { + if entry.IsSubmodule() { var fullPath string if len(treePath) > 0 { fullPath = treePath + "/" + entry.Name() } else { fullPath = entry.Name() } - subModuleURL, err := commit.GetSubModule(fullPath) + submodule, err := commit.GetSubmodule(fullPath, entry) if err != nil { return nil, nil, err } - subModuleFile := NewSubModuleFile(commitsInfo[i].Commit, subModuleURL, entry.ID.String()) - commitsInfo[i].SubModuleFile = subModuleFile + commitsInfo[i].Submodule = submodule } } diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index 484827149c..ee57a735e6 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -436,33 +436,3 @@ func TestGetAllBranches(t *testing.T) { assert.Equal(t, []string{"branch1", "branch2", "master"}, branches) } - -func Test_parseSubmoduleContent(t *testing.T) { - submoduleFiles := []struct { - fileContent string - expectedPath string - expectedURL string - }{ - { - fileContent: `[submodule "jakarta-servlet"] -url = ../../ALP-pool/jakarta-servlet -path = jakarta-servlet`, - expectedPath: "jakarta-servlet", - expectedURL: "../../ALP-pool/jakarta-servlet", - }, - { - fileContent: `[submodule "jakarta-servlet"] -path = jakarta-servlet -url = ../../ALP-pool/jakarta-servlet`, - expectedPath: "jakarta-servlet", - expectedURL: "../../ALP-pool/jakarta-servlet", - }, - } - for _, kase := range submoduleFiles { - submodule, err := parseSubmoduleContent([]byte(kase.fileContent)) - require.NoError(t, err) - v, ok := submodule.Get(kase.expectedPath) - assert.True(t, ok) - assert.Equal(t, kase.expectedURL, v) - } -} diff --git a/modules/git/diff_compare.go b/modules/git/diff_compare.go new file mode 100644 index 0000000000..0eba8cb541 --- /dev/null +++ b/modules/git/diff_compare.go @@ -0,0 +1,56 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later +package git + +import ( + "bytes" + "context" + "fmt" + "os" + + "forgejo.org/modules/log" + "forgejo.org/modules/util" +) + +// CheckIfDiffDiffers returns if the diff of the newCommitID and +// oldCommitID with the merge base of the base branch has changed. +// +// Informally it checks if the following two diffs are exactly the same in their +// contents, thus ignoring different commit IDs, headers and messages: +// 1. git diff --merge-base baseReference newCommitID +// 2. git diff --merge-base baseReference oldCommitID +func (repo *Repository) CheckIfDiffDiffers(base, oldCommitID, newCommitID string, env []string) (hasChanged bool, err error) { + cmd := NewCommand(repo.Ctx, "diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base) + stdoutReader, stdoutWriter, err := os.Pipe() + if err != nil { + return false, fmt.Errorf("unable to open pipe for to run diff: %w", err) + } + + stderr := new(bytes.Buffer) + if err := cmd.Run(&RunOpts{ + Dir: repo.Path, + Stdout: stdoutWriter, + Stderr: stderr, + PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { + _ = stdoutWriter.Close() + defer func() { + _ = stdoutReader.Close() + }() + return util.IsEmptyReader(stdoutReader) + }, + }); err != nil { + if err == util.ErrNotEmpty { + return true, nil + } + err = ConcatenateError(err, stderr.String()) + + log.Error("Unable to run git diff on %s %s %s in %q: Error: %v", + newCommitID, oldCommitID, base, + repo.Path, + err) + + return false, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, base, err) + } + + return false, nil +} diff --git a/modules/git/diff_compare_test.go b/modules/git/diff_compare_test.go new file mode 100644 index 0000000000..433497b5c4 --- /dev/null +++ b/modules/git/diff_compare_test.go @@ -0,0 +1,421 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later +package git + +import ( + "bytes" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCheckIfDiffDiffers(t *testing.T) { + tmpDir := t.TempDir() + + err := InitRepository(t.Context(), tmpDir, false, Sha1ObjectFormat.Name()) + require.NoError(t, err) + + gitRepo, err := openRepositoryWithDefaultContext(tmpDir) + require.NoError(t, err) + defer gitRepo.Close() + + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "main").Run(&RunOpts{Dir: tmpDir})) + + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("aaa"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "initial commit").Run(&RunOpts{Dir: tmpDir})) + + t.Run("Simple fast-forward", func(t *testing.T) { + // Check that A--B--C, where A is the base branch. + + t.Run("Different diff", func(t *testing.T) { + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "a-1", "main").Run(&RunOpts{Dir: tmpDir})) + + // B commit + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "a-2").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("ccc"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main", "a-1", "a-2", nil) + require.NoError(t, err) + assert.True(t, changed) + }) + + t.Run("Same diff", func(t *testing.T) { + // Because C is a empty commit, the diff does not differ relative to the + // base branch. + + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "a-3", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "a-4").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "--allow-empty", "-m", "No changes to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main", "a-3", "a-4", nil) + require.NoError(t, err) + assert.False(t, changed) + }) + }) + + t.Run("Merge-base is base reference", func(t *testing.T) { + // B + // / + // A + // \ + // C + t.Run("Different diff", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "b-1", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "b-2", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("ccc"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main", "b-1", "b-2", nil) + require.NoError(t, err) + assert.True(t, changed) + }) + + t.Run("Same diff", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "b-3", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "b-4", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main", "b-3", "b-4", nil) + require.NoError(t, err) + assert.False(t, changed) + }) + }) + + t.Run("Merge-base is different", func(t *testing.T) { + // B + // / + // A--D + // \ + // C + // Where D is the base reference. + + // D commit, where A is `main`. + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "main-D", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "FUNFACT"), []byte("Smithy was the runner up to be Forgejo's name"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "FUNFACT").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Forgejo history").Run(&RunOpts{Dir: tmpDir})) + + t.Run("Different diff", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "c-1", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "c-2", "main-D").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "FUNFACT"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "FUNFACT").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the funfact").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main-D", "c-1", "c-2", nil) + require.NoError(t, err) + assert.True(t, changed) + }) + + t.Run("Same diff", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "c-3", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "c-4", "main-D").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main-D", "c-3", "c-4", nil) + require.NoError(t, err) + assert.False(t, changed) + }) + }) + + t.Run("Merge commit", func(t *testing.T) { + // B + // / + // A - D + // \ + // C + // + // From B, it merges D where E is the merge commit : + // B---E + // / / + // A---D + // \ + // C + + t.Run("Different diff", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "d-1", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + // E commit + require.NoError(t, NewCommand(t.Context(), "merge", "--no-ff", "main-D").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "d-2", "main-D").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "FUNFACT"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "FUNFACT").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the funfact").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main-D", "d-1", "d-2", nil) + require.NoError(t, err) + assert.True(t, changed) + }) + + t.Run("Same diff", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "d-3", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + // Merges D. + require.NoError(t, NewCommand(t.Context(), "merge", "--no-ff", "main-D").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "d-4", "main-D").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main-D", "d-3", "d-4", nil) + require.NoError(t, err) + assert.False(t, changed) + }) + }) + + t.Run("Non-typical rebase", func(t *testing.T) { + // B + // / + // A--D + // \ + // C + // Where D is the base reference. + // B was rebased onto D, which produced C. + // B and D made the same change to same file. + + // D commit. + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "main-D-2", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "FUNFACT"), []byte("Smithy was the runner up to be Forgejo's name"), 0o600)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("ccc"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "FUNFACT", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Forgejo history").Run(&RunOpts{Dir: tmpDir})) + + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "e-1", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "CONTACT"), []byte("@example.com"), 0o600)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("ccc"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README", "CONTACT").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the contact and README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "e-2").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "rebase", "main-D-2").Run(&RunOpts{Dir: tmpDir})) + + // The diff changed, because it no longers shows the change made to `README`. + changed, err := gitRepo.CheckIfDiffDiffers("main-D-2", "e-1", "e-2", nil) + require.NoError(t, err) + assert.False(t, changed) // This should be true. + }) + + t.Run("Directory", func(t *testing.T) { + // B + // / + // A + // \ + // C + t.Run("Same directory", func(t *testing.T) { + t.Run("Different diff", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-1", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-2", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("ccc"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main", "f-1", "f-2", nil) + require.NoError(t, err) + assert.True(t, changed) + }) + + t.Run("Same diff", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-3", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-4", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main", "f-3", "f-4", nil) + require.NoError(t, err) + assert.False(t, changed) + }) + }) + + t.Run("Different directory", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-5", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-6", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "documentation"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "documentation", "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "documentation/README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main", "f-5", "f-6", nil) + require.NoError(t, err) + assert.True(t, changed) + }) + + t.Run("Directory and file", func(t *testing.T) { + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-7", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-8", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("main", "f-7", "f-8", nil) + require.NoError(t, err) + assert.True(t, changed) + }) + }) + + t.Run("Rebase", func(t *testing.T) { + // B + // / + // A--D + // \ + // C + // Where D is the base reference. + // B was rebased onto D, which produced C. + // B and D made different (non conflicting) changes to same file. + + // A commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "main-3", "main").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "REBASE"), bytes.Repeat([]byte{'b', 'b', 'b', '\n'}, 100), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "REBASE").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Rebasing is fun").Run(&RunOpts{Dir: tmpDir})) + + // B commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "g-1", "main-3").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "REBASE"), append(bytes.Repeat([]byte{'b', 'b', 'b', '\n'}, 100), 'a', 'a', 'a'), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "REBASE").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Rebasing is fun").Run(&RunOpts{Dir: tmpDir})) + + // D commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "g-2", "main-3").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "REBASE"), append([]byte{'a', 'a', 'a'}, bytes.Repeat([]byte{'b', 'b', 'b', '\n'}, 99)...), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "REBASE").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Rebasing is fun").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "g-3", "g-1").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "rebase", "g-2").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("g-2", "g-1", "g-3", nil) + require.NoError(t, err) + assert.True(t, changed) + }) + + t.Run("Rebasing change not shown", func(t *testing.T) { + // B + // / + // A--D + // \ + // C + // Where D is the base reference. + // B was rebased onto D, which produced C. + // B and D made different (non conflicting) changes to same file. + + // A commit + require.NoError(t, NewCommand(t.Context(), "switch", "--orphan", "main-4").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "A"), 0o700)) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "A", "a"), bytes.Repeat([]byte{'A', 'A', 'A', '\n'}, 100), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "A/a").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Just wondering").Run(&RunOpts{Dir: tmpDir})) + + // B commit + // Changes last line. + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "h-1").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "A", "a"), append(bytes.Repeat([]byte{'A', 'A', 'A', '\n'}, 99), 'B', 'B', 'B', '\n'), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "A/a").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Just wondering").Run(&RunOpts{Dir: tmpDir})) + + // D commit + // Changes first line. + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "h-2", "main-4").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "A", "a"), append([]byte{'B', 'B', 'B', '\n'}, bytes.Repeat([]byte{'A', 'A', 'A', '\n'}, 99)...), 0o600)) + require.NoError(t, NewCommand(t.Context(), "add", "A/a").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Just wondering").Run(&RunOpts{Dir: tmpDir})) + + // C commit + require.NoError(t, NewCommand(t.Context(), "switch", "-c", "h-3").Run(&RunOpts{Dir: tmpDir})) + require.NoError(t, NewCommand(t.Context(), "rebase", "h-2").Run(&RunOpts{Dir: tmpDir})) + + changed, err := gitRepo.CheckIfDiffDiffers("h-2", "h-1", "h-3", nil) + require.NoError(t, err) + + assert.False(t, changed) + }) +} diff --git a/modules/git/git.go b/modules/git/git.go index 1dfd0b5134..851b090b53 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -23,7 +23,7 @@ import ( ) // RequiredVersion is the minimum Git version required -const RequiredVersion = "2.0.0" +const RequiredVersion = "2.34.1" var ( // GitExecutable is the command name of git @@ -33,7 +33,6 @@ var ( // DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx DefaultContext context.Context - SupportProcReceive bool // >= 2.29 SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’ InvertedGitFlushEnv bool // 2.43.1 SupportCheckAttrOnBare bool // >= 2.40 @@ -113,7 +112,7 @@ func VersionInfo() string { format := "%s" args := []any{GitVersion.Original()} // Since git wire protocol has been released from git v2.18 - if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { + if setting.Git.EnableAutoGitWireProtocol { format += ", Wire Protocol %s Enabled" args = append(args, "Version 2") // for focus color } @@ -172,16 +171,13 @@ func InitFull(ctx context.Context) (err error) { _ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg")) } - // Since git wire protocol has been released from git v2.18 - if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { + if setting.Git.EnableAutoGitWireProtocol { globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2") } // Explicitly disable credential helper, otherwise Git credentials might leak - if CheckGitVersionAtLeast("2.9") == nil { - globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") - } - SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil + globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") + SupportHashSha256 = CheckGitVersionAtLeast("2.42") == nil SupportCheckAttrOnBare = CheckGitVersionAtLeast("2.40") == nil if SupportHashSha256 { @@ -195,9 +191,6 @@ func InitFull(ctx context.Context) (err error) { SupportGrepMaxCount = CheckGitVersionAtLeast("2.38") == nil if setting.LFS.StartServer { - if CheckGitVersionAtLeast("2.1.2") != nil { - return errors.New("LFS server support requires Git >= 2.1.2") - } globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=", "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=") } @@ -234,38 +227,28 @@ func syncGitConfig() (err error) { } } - // Set git some configurations - these must be set to these values for gitea to work correctly + // Set git some configurations - these must be set to these values for forgejo to work correctly if err := configSet("core.quotePath", "false"); err != nil { return err } - if CheckGitVersionAtLeast("2.10") == nil { - if err := configSet("receive.advertisePushOptions", "true"); err != nil { - return err - } + if err := configSet("receive.advertisePushOptions", "true"); err != nil { + return err } - if CheckGitVersionAtLeast("2.18") == nil { - if err := configSet("core.commitGraph", "true"); err != nil { - return err - } - if err := configSet("gc.writeCommitGraph", "true"); err != nil { - return err - } - if err := configSet("fetch.writeCommitGraph", "true"); err != nil { - return err - } + if err := configSet("core.commitGraph", "true"); err != nil { + return err + } + if err := configSet("gc.writeCommitGraph", "true"); err != nil { + return err + } + if err := configSet("fetch.writeCommitGraph", "true"); err != nil { + return err } - if SupportProcReceive { - // set support for AGit flow - if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil { - return err - } - } else { - if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil { - return err - } + // set support for AGit flow + if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil { + return err } // Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user @@ -284,11 +267,6 @@ func syncGitConfig() (err error) { switch setting.Repository.Signing.Format { case "ssh": - // First do a git version check. - if CheckGitVersionAtLeast("2.34.0") != nil { - return errors.New("ssh signing requires Git >= 2.34.0") - } - // Get the ssh-keygen binary that Git will use. // This can be overridden in app.ini in [git.config] section, so we must // query this information. @@ -325,8 +303,7 @@ func syncGitConfig() (err error) { } } - // By default partial clones are disabled, enable them from git v2.22 - if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil { + if !setting.Git.DisablePartialClone { if err = configSet("uploadpack.allowfilter", "true"); err != nil { return err } diff --git a/modules/git/git_test.go b/modules/git/git_test.go index 01200dba68..38d4db169c 100644 --- a/modules/git/git_test.go +++ b/modules/git/git_test.go @@ -14,7 +14,6 @@ import ( "forgejo.org/modules/test" "forgejo.org/modules/util" - "github.com/hashicorp/go-version" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -105,10 +104,6 @@ func TestSyncConfigGPGFormat(t *testing.T) { }) t.Run("SSH format", func(t *testing.T) { - if CheckGitVersionAtLeast("2.34.0") != nil { - t.SkipNow() - } - r, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) f, err := r.OpenFile("ssh-keygen", os.O_CREATE|os.O_TRUNC, 0o700) @@ -121,13 +116,6 @@ func TestSyncConfigGPGFormat(t *testing.T) { assert.True(t, gitConfigContains("[gpg]")) assert.True(t, gitConfigContains("format = ssh")) - t.Run("Old version", func(t *testing.T) { - oldVersion, err := version.NewVersion("2.33.0") - require.NoError(t, err) - defer test.MockVariableValue(&GitVersion, oldVersion)() - require.ErrorContains(t, syncGitConfig(), "ssh signing requires Git >= 2.34.0") - }) - t.Run("No ssh-keygen binary", func(t *testing.T) { require.NoError(t, r.Remove("ssh-keygen")) require.ErrorContains(t, syncGitConfig(), "git signing requires a ssh-keygen binary") diff --git a/modules/git/pipeline/revlist.go b/modules/git/pipeline/revlist.go index f39b7113bb..1ee8921854 100644 --- a/modules/git/pipeline/revlist.go +++ b/modules/git/pipeline/revlist.go @@ -16,26 +16,6 @@ import ( "forgejo.org/modules/log" ) -// RevListAllObjects runs rev-list --objects --all and writes to a pipewriter -func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, basePath string, errChan chan<- error) { - defer wg.Done() - defer revListWriter.Close() - - stderr := new(bytes.Buffer) - var errbuf strings.Builder - cmd := git.NewCommand(ctx, "rev-list", "--objects", "--all") - if err := cmd.Run(&git.RunOpts{ - Dir: basePath, - Stdout: revListWriter, - Stderr: stderr, - }); err != nil { - log.Error("git rev-list --objects --all [%s]: %v - %s", basePath, err, errbuf.String()) - err = fmt.Errorf("git rev-list --objects --all [%s]: %w - %s", basePath, err, errbuf.String()) - _ = revListWriter.CloseWithError(err) - errChan <- err - } -} - // RevListObjects run rev-list --objects from headSHA to baseSHA func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, tmpBasePath, headSHA, baseSHA string, errChan chan<- error) { defer wg.Done() diff --git a/modules/git/pushoptions/pushoptions.go b/modules/git/pushoptions/pushoptions.go index 9709a8be79..e96ba0a339 100644 --- a/modules/git/pushoptions/pushoptions.go +++ b/modules/git/pushoptions/pushoptions.go @@ -4,6 +4,7 @@ package pushoptions import ( + "encoding/base64" "fmt" "os" "strconv" @@ -109,5 +110,22 @@ func (o gitPushOptions) GetBool(key Key, def bool) bool { func (o gitPushOptions) GetString(key Key) (string, bool) { val, ok := o[string(key)] - return val, ok + if !ok { + return "", false + } + + // If the value is prefixed with `{base64}` then everything after that is very + // likely to be encoded via base64. + base64Value, found := strings.CutPrefix(val, "{base64}") + if !found { + return val, true + } + + value, err := base64.StdEncoding.DecodeString(base64Value) + if err != nil { + // Not valid base64? Return the original value. + return val, true + } + + return string(value), true } diff --git a/modules/git/pushoptions/pushoptions_test.go b/modules/git/pushoptions/pushoptions_test.go index 1cb36d9d1e..d7c50649d0 100644 --- a/modules/git/pushoptions/pushoptions_test.go +++ b/modules/git/pushoptions/pushoptions_test.go @@ -4,6 +4,7 @@ package pushoptions import ( + "encoding/base64" "fmt" "testing" @@ -92,6 +93,23 @@ func TestParse(t *testing.T) { assert.False(t, options.Parse("unknown=value")) assert.True(t, options.Empty()) }) + + t.Run("Base64 values", func(t *testing.T) { + options := New() + + description := `I contain +a +line` + assert.True(t, options.Parse(fmt.Sprintf("%s={base64}%s", AgitDescription, base64.StdEncoding.EncodeToString([]byte(description))))) + val, ok := options.GetString(AgitDescription) + assert.True(t, ok) + assert.Equal(t, description, val) + + assert.True(t, options.Parse(fmt.Sprintf("%s={base64}fooled you", AgitTitle))) + val, ok = options.GetString(AgitTitle) + assert.True(t, ok) + assert.Equal(t, "{base64}fooled you", val) + }) } func TestReadEnv(t *testing.T) { diff --git a/modules/git/remote.go b/modules/git/remote.go index fb66d76ff0..83a02fe2be 100644 --- a/modules/git/remote.go +++ b/modules/git/remote.go @@ -12,14 +12,7 @@ import ( // GetRemoteAddress returns remote url of git repository in the repoPath with special remote name func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) { - var cmd *Command - if CheckGitVersionAtLeast("2.7") == nil { - cmd = NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName) - } else { - cmd = NewCommand(ctx, "config", "--get").AddDynamicArguments("remote." + remoteName + ".url") - } - - result, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) + result, _, err := NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName).RunStdString(&RunOpts{Dir: repoPath}) if err != nil { return "", err } diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go index c69382e245..3d2c845fa0 100644 --- a/modules/git/repo_attribute_test.go +++ b/modules/git/repo_attribute_test.go @@ -5,7 +5,6 @@ package git import ( "context" - "fmt" "io" "io/fs" "os" @@ -197,7 +196,7 @@ func TestGitAttributeCheckerError(t *testing.T) { path := t.TempDir() // we can't use unittest.CopyDir because of an import cycle (git.Init in unittest) - require.NoError(t, CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo")))) + require.NoError(t, os.CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo")))) gitRepo, err := openRepositoryWithDefaultContext(path) require.NoError(t, err) @@ -324,32 +323,3 @@ func TestGitAttributeCheckerError(t *testing.T) { require.ErrorIs(t, err, fs.ErrClosed) }) } - -// CopyFS is adapted from https://github.com/golang/go/issues/62484 -// which should be available with go1.23 -func CopyFS(dir string, fsys fs.FS) error { - return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, _ error) error { - targ := filepath.Join(dir, filepath.FromSlash(path)) - if d.IsDir() { - return os.MkdirAll(targ, 0o777) - } - r, err := fsys.Open(path) - if err != nil { - return err - } - defer r.Close() - info, err := r.Stat() - if err != nil { - return err - } - w, err := os.OpenFile(targ, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o666|info.Mode()&0o777) - if err != nil { - return err - } - if _, err := io.Copy(w, r); err != nil { - w.Close() - return fmt.Errorf("copying %s: %v", path, err) - } - return w.Close() - }) -} diff --git a/modules/git/repo_blame.go b/modules/git/repo_blame.go index 139cdd7be9..d812354af5 100644 --- a/modules/git/repo_blame.go +++ b/modules/git/repo_blame.go @@ -4,20 +4,46 @@ package git import ( + "errors" "fmt" + "regexp" +) + +var ( + ErrBlameFileDoesNotExist = errors.New("the blamed file does not exist") + ErrBlameFileNotEnoughLines = errors.New("the blamed file has not enough lines") + + notEnoughLinesRe = regexp.MustCompile(`^fatal: file .+ has only \d+ lines?\n$`) ) // LineBlame returns the latest commit at the given line -func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) { - res, _, err := NewCommand(repo.Ctx, "blame"). +func (repo *Repository) LineBlame(revision, file string, line uint64) (*Commit, error) { + res, _, gitErr := NewCommand(repo.Ctx, "blame"). AddOptionFormat("-L %d,%d", line, line). AddOptionValues("-p", revision). - AddDashesAndList(file).RunStdString(&RunOpts{Dir: path}) + AddDashesAndList(file).RunStdString(&RunOpts{Dir: repo.Path}) + if gitErr != nil { + stdErr := gitErr.Stderr() + + if stdErr == fmt.Sprintf("fatal: no such path %s in %s\n", file, revision) { + return nil, ErrBlameFileDoesNotExist + } + if notEnoughLinesRe.MatchString(stdErr) { + return nil, ErrBlameFileNotEnoughLines + } + + return nil, gitErr + } + + objectFormat, err := repo.GetObjectFormat() if err != nil { return nil, err } - if len(res) < 40 { - return nil, fmt.Errorf("invalid result of blame: %s", res) + + objectIDLen := objectFormat.FullLength() + if len(res) < objectIDLen { + return nil, fmt.Errorf("output of blame is invalid, cannot contain commit ID: %s", res) } - return repo.GetCommit(res[:40]) + + return repo.GetCommit(res[:objectIDLen]) } diff --git a/modules/git/repo_blame_test.go b/modules/git/repo_blame_test.go new file mode 100644 index 0000000000..126b95386d --- /dev/null +++ b/modules/git/repo_blame_test.go @@ -0,0 +1,52 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package git + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLineBlame(t *testing.T) { + t.Run("SHA1", func(t *testing.T) { + repo, err := OpenRepository(t.Context(), filepath.Join(testReposDir, "repo1_bare")) + require.NoError(t, err) + defer repo.Close() + + commit, err := repo.LineBlame("HEAD", "foo/link_short", 1) + require.NoError(t, err) + assert.Equal(t, "37991dec2c8e592043f47155ce4808d4580f9123", commit.ID.String()) + + commit, err = repo.LineBlame("HEAD", "foo/link_short", 512) + require.ErrorIs(t, err, ErrBlameFileNotEnoughLines) + assert.Nil(t, commit) + + commit, err = repo.LineBlame("HEAD", "non-existent/path", 512) + require.ErrorIs(t, err, ErrBlameFileDoesNotExist) + assert.Nil(t, commit) + }) + + t.Run("SHA256", func(t *testing.T) { + skipIfSHA256NotSupported(t) + + repo, err := OpenRepository(t.Context(), filepath.Join(testReposDir, "repo1_bare_sha256")) + require.NoError(t, err) + defer repo.Close() + + commit, err := repo.LineBlame("HEAD", "foo/link_short", 1) + require.NoError(t, err) + assert.Equal(t, "6aae864a3d1d0d6a5be0cc64028c1e7021e2632b031fd8eb82afc5a283d1c3d1", commit.ID.String()) + + commit, err = repo.LineBlame("HEAD", "foo/link_short", 512) + require.ErrorIs(t, err, ErrBlameFileNotEnoughLines) + assert.Nil(t, commit) + + commit, err = repo.LineBlame("HEAD", "non-existent/path", 512) + require.ErrorIs(t, err, ErrBlameFileDoesNotExist) + assert.Nil(t, commit) + }) +} diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index 3a9aa3e4e6..1e38bf2946 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -19,15 +19,9 @@ import ( // BranchPrefix base dir of the branch information file store on git const BranchPrefix = "refs/heads/" -// IsReferenceExist returns true if given reference exists in the repository. -func IsReferenceExist(ctx context.Context, repoPath, name string) bool { - _, _, err := NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repoPath}) - return err == nil -} - // IsBranchExist returns true if given branch exists in the repository. func IsBranchExist(ctx context.Context, repoPath, name string) bool { - return IsReferenceExist(ctx, repoPath, BranchPrefix+name) + return NewCommand(ctx, "show-ref", "--verify", "--quiet").AddDashesAndList(BranchPrefix+name).Run(&RunOpts{Dir: repoPath}) == nil } // Branch represents a Git branch. diff --git a/modules/git/repo_branch_test.go b/modules/git/repo_branch_test.go index 1e0fea7cd4..e61ea6f5d7 100644 --- a/modules/git/repo_branch_test.go +++ b/modules/git/repo_branch_test.go @@ -1,4 +1,5 @@ // Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package git @@ -195,3 +196,17 @@ func TestRepository_IsReferenceExist(t *testing.T) { }) } } + +func TestIsBranchExist(t *testing.T) { + repo1Path := filepath.Join(testReposDir, "repo1_bare") + + assert.True(t, IsBranchExist(t.Context(), repo1Path, "branch1")) + assert.True(t, IsBranchExist(t.Context(), repo1Path, "branch2")) + assert.True(t, IsBranchExist(t.Context(), repo1Path, "master")) + + assert.False(t, IsBranchExist(t.Context(), repo1Path, "HEAD")) + assert.False(t, IsBranchExist(t.Context(), repo1Path, "153f451b9ee7fa1da317ab17a127e9fd9d384310")) + assert.False(t, IsBranchExist(t.Context(), repo1Path, "153f451b9ee7fa1da317ab17a127e9fd9d384310")) + assert.False(t, IsBranchExist(t.Context(), repo1Path, "signed-tag")) + assert.False(t, IsBranchExist(t.Context(), repo1Path, "test")) +} diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 4c8516f828..41ca0e39b1 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -443,42 +443,18 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit, } func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) { - if CheckGitVersionAtLeast("2.7.0") == nil { - command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix) + command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix) - if limit != -1 { - command = command.AddOptionFormat("--count=%d", limit) - } - - stdout, _, err := command.RunStdString(&RunOpts{Dir: repo.Path}) - if err != nil { - return nil, err - } - - branches := strings.Fields(stdout) - return branches, nil + if limit != -1 { + command = command.AddOptionFormat("--count=%d", limit) } - stdout, _, err := NewCommand(repo.Ctx, "branch").AddOptionValues("--contains", commit.ID.String()).RunStdString(&RunOpts{Dir: repo.Path}) + stdout, _, err := command.RunStdString(&RunOpts{Dir: repo.Path}) if err != nil { return nil, err } - refs := strings.Split(stdout, "\n") - - var max int - if len(refs) > limit { - max = limit - } else { - max = len(refs) - 1 - } - - branches := make([]string, max) - for i, ref := range refs[:max] { - parts := strings.Fields(ref) - - branches[i] = parts[len(parts)-1] - } + branches := strings.Fields(stdout) return branches, nil } diff --git a/modules/git/repo_commitgraph.go b/modules/git/repo_commitgraph.go index 492438be37..c3647bd894 100644 --- a/modules/git/repo_commitgraph.go +++ b/modules/git/repo_commitgraph.go @@ -11,10 +11,8 @@ import ( // WriteCommitGraph write commit graph to speed up repo access // this requires git v2.18 to be installed func WriteCommitGraph(ctx context.Context, repoPath string) error { - if CheckGitVersionAtLeast("2.18") == nil { - if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil { - return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err) - } + if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil { + return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err) } return nil } diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go index 373b5befb5..94f1911c4a 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -183,6 +183,17 @@ func (repo *Repository) GetDiffShortStat(base, head string) (numFiles, totalAddi return numFiles, totalAdditions, totalDeletions, err } +// GetCommitStat returns the number of files, total additions and total deletions the commit has. +func (repo *Repository) GetCommitShortStat(commitID string) (numFiles, totalAdditions, totalDeletions int, err error) { + cmd := NewCommand(repo.Ctx, "diff-tree", "--shortstat", "--no-commit-id", "--root").AddDynamicArguments(commitID) + stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) + if err != nil { + return 0, 0, 0, err + } + + return parseDiffStat(stdout) +} + // GetDiffShortStat counts number of changed files, number of additions and deletions func GetDiffShortStat(ctx context.Context, repoPath string, trustedArgs TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) { // Now if we call: diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go index 86bd6855a7..b1ebdf6177 100644 --- a/modules/git/repo_compare_test.go +++ b/modules/git/repo_compare_test.go @@ -1,4 +1,5 @@ // Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package git @@ -162,3 +163,83 @@ func TestGetCommitFilesChanged(t *testing.T) { assert.ElementsMatch(t, tc.files, changedFiles) } } + +func TestGetCommitShortStat(t *testing.T) { + t.Run("repo1_bare", func(t *testing.T) { + repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare")) + if err != nil { + require.NoError(t, err) + return + } + defer repo.Close() + + numFiles, totalAddition, totalDeletions, err := repo.GetCommitShortStat("ce064814f4a0d337b333e646ece456cd39fab612") + require.NoError(t, err) + assert.Equal(t, 0, numFiles) + assert.Equal(t, 0, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("feaf4ba6bc635fec442f46ddd4512416ec43c2c2") + require.NoError(t, err) + assert.Equal(t, 0, numFiles) + assert.Equal(t, 0, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("37991dec2c8e592043f47155ce4808d4580f9123") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("6fbd69e9823458e6c4a2fc5c0f6bc022b2f2acd1") + require.NoError(t, err) + assert.Equal(t, 2, numFiles) + assert.Equal(t, 2, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("8006ff9adbf0cb94da7dad9e537e53817f9fa5c0") + require.NoError(t, err) + assert.Equal(t, 2, numFiles) + assert.Equal(t, 2, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("95bb4d39648ee7e325106df01a621c530863a653") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + }) + + t.Run("repo6_blame_sha256", func(t *testing.T) { + repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo6_blame_sha256")) + if err != nil { + require.NoError(t, err) + return + } + defer repo.Close() + + numFiles, totalAddition, totalDeletions, err := repo.GetCommitShortStat("e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 1, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 6, totalAddition) + assert.Equal(t, 0, totalDeletions) + }) +} diff --git a/modules/git/submodule.go b/modules/git/submodule.go index b99c81582b..4ea97d66eb 100644 --- a/modules/git/submodule.go +++ b/modules/git/submodule.go @@ -6,38 +6,124 @@ package git import ( "fmt" + "io" "net" "net/url" "path" "regexp" "strings" + + "forgejo.org/modules/setting" + "forgejo.org/modules/util" + + "gopkg.in/ini.v1" //nolint:depguard // used to read .gitmodules ) -var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`) - -// SubModule submodule is a reference on git repository -type SubModule struct { - Name string - URL string -} - -// SubModuleFile represents a file with submodule type. -type SubModuleFile struct { - *Commit - - refURL string - refID string -} - -// NewSubModuleFile create a new submodule file -func NewSubModuleFile(c *Commit, refURL, refID string) *SubModuleFile { - return &SubModuleFile{ - Commit: c, - refURL: refURL, - refID: refID, +// GetSubmodule returns the Submodule of a given path +func (c *Commit) GetSubmodule(path string, entry *TreeEntry) (Submodule, error) { + err := c.readSubmodules() + if err != nil { + // the .gitmodules file exists but could not be read or parsed + return Submodule{}, err } + + sm, ok := c.submodules[path] + if !ok { + // no info found in .gitmodules: fallback to what we can provide + return Submodule{ + Path: path, + Commit: entry.ID, + }, nil + } + + sm.Commit = entry.ID + return sm, nil } +// readSubmodules populates the submodules field by reading the .gitmodules file +func (c *Commit) readSubmodules() error { + if c.submodules != nil { + return nil + } + + entry, err := c.GetTreeEntryByPath(".gitmodules") + if err != nil { + if IsErrNotExist(err) { + c.submodules = make(map[string]Submodule) + return nil + } + return err + } + + rc, _, err := entry.Blob().NewTruncatedReader(10 * 1024) + if err != nil { + return err + } + defer rc.Close() + + c.submodules, err = parseSubmoduleContent(rc) + return err +} + +func parseSubmoduleContent(r io.Reader) (map[string]Submodule, error) { + // https://git-scm.com/docs/gitmodules#_description + // The .gitmodules file, located in the top-level directory of a Git working tree + // is a text file with a syntax matching the requirements of git-config[1]. + // https://git-scm.com/docs/git-config#_configuration_file + + cfg := ini.Empty(ini.LoadOptions{ + InsensitiveKeys: true, // "The variable names are case-insensitive", but "Subsection names are case sensitive" + }) + err := cfg.Append(r) + if err != nil { + return nil, err + } + + sections := cfg.Sections() + submodule := make(map[string]Submodule, len(sections)) + + for _, s := range sections { + sm := parseSubmoduleSection(s) + if sm.Path == "" || sm.URL == "" { + continue + } + submodule[sm.Path] = sm + } + return submodule, nil +} + +func parseSubmoduleSection(s *ini.Section) Submodule { + section, name, _ := strings.Cut(s.Name(), " ") + if !util.ASCIIEqualFold("submodule", section) { // See https://codeberg.org/forgejo/forgejo/pulls/8438#issuecomment-5805251 + return Submodule{} + } + _ = name + + sm := Submodule{} + if key, _ := s.GetKey("path"); key != nil { + sm.Path = key.Value() + } + if key, _ := s.GetKey("url"); key != nil { + sm.URL = key.Value() + } + return sm +} + +// Submodule represents a parsed git submodule reference. +type Submodule struct { + Path string // path property + URL string // upstream URL + Commit ObjectID // upstream Commit-ID +} + +// ResolveUpstreamURL resolves the upstream URL relative to the repo URL. +func (sm Submodule) ResolveUpstreamURL(repoURL string) string { + repoFullName := strings.TrimPrefix(repoURL, setting.AppURL) // currently hacky, but can be dropped when refactoring getRefURL + return getRefURL(sm.URL, setting.AppURL, repoFullName, setting.SSH.Domain) +} + +var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`) + func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { if refURL == "" { return "" @@ -53,7 +139,7 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { urlPrefix = strings.TrimSuffix(urlPrefix, "/") - // FIXME: Need to consider branch - which will require changes in modules/git/commit.go:GetSubModules + // FIXME: Need to consider branch - which will require changes in parseSubmoduleSection // Relative url prefix check (according to git submodule documentation) if strings.HasPrefix(refURI, "./") || strings.HasPrefix(refURI, "../") { return urlPrefix + path.Clean(path.Join("/", repoFullName, refURI)) @@ -107,13 +193,3 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { return "" } - -// RefURL guesses and returns reference URL. -func (sf *SubModuleFile) RefURL(urlPrefix, repoFullName, sshDomain string) string { - return getRefURL(sf.refURL, urlPrefix, repoFullName, sshDomain) -} - -// RefID returns reference ID. -func (sf *SubModuleFile) RefID() string { - return sf.refID -} diff --git a/modules/git/submodule_test.go b/modules/git/submodule_test.go index a396e4ea0d..2d27f47456 100644 --- a/modules/git/submodule_test.go +++ b/modules/git/submodule_test.go @@ -4,9 +4,11 @@ package git import ( + "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestGetRefURL(t *testing.T) { @@ -40,3 +42,74 @@ func TestGetRefURL(t *testing.T) { assert.Equal(t, kase.expect, getRefURL(kase.refURL, kase.prefixURL, kase.parentPath, kase.SSHDomain)) } } + +func Test_parseSubmoduleContent(t *testing.T) { + submoduleFiles := []struct { + fileContent string + expectedPath string + expected Submodule + }{ + { + fileContent: `[submodule "jakarta-servlet"] +url = ../../ALP-pool/jakarta-servlet +path = jakarta-servlet`, + expectedPath: "jakarta-servlet", + expected: Submodule{ + Path: "jakarta-servlet", + URL: "../../ALP-pool/jakarta-servlet", + }, + }, + { + fileContent: `[submodule "jakarta-servlet"] +path = jakarta-servlet +url = ../../ALP-pool/jakarta-servlet`, + expectedPath: "jakarta-servlet", + expected: Submodule{ + Path: "jakarta-servlet", + URL: "../../ALP-pool/jakarta-servlet", + }, + }, + { + fileContent: `[submodule "about/documents"] + path = about/documents + url = git@github.com:example/documents.git + branch = gh-pages +[submodule "custom-name"] + path = manifesto + url = https://github.com/example/manifesto.git +[submodule] + path = relative/url + url = ../such-relative.git +`, + expectedPath: "relative/url", + expected: Submodule{ + Path: "relative/url", + URL: "../such-relative.git", + }, + }, + { + fileContent: `# .gitmodules +# Subsection names are case sensitive +[submodule "Seanpm2001/Degoogle-your-life"] + path = Its-time-to-cut-WideVine-DRM/DeGoogle-Your-Life/submodule.gitmodules + url = https://github.com/seanpm2001/Degoogle-your-life/ + +[submodule "seanpm2001/degoogle-your-life"] + url = https://github.com/seanpm2001/degoogle-your-life/ +# This second section should not be merged with the first, because of casing +`, + expectedPath: "Its-time-to-cut-WideVine-DRM/DeGoogle-Your-Life/submodule.gitmodules", + expected: Submodule{ + Path: "Its-time-to-cut-WideVine-DRM/DeGoogle-Your-Life/submodule.gitmodules", + URL: "https://github.com/seanpm2001/Degoogle-your-life/", + }, + }, + } + for _, kase := range submoduleFiles { + submodule, err := parseSubmoduleContent(strings.NewReader(kase.fileContent)) + require.NoError(t, err) + v, ok := submodule[kase.expectedPath] + assert.True(t, ok) + assert.Equal(t, kase.expected, v) + } +} diff --git a/modules/git/tests/repos/templates_repo/COMMIT_EDITMSG b/modules/git/tests/repos/templates_repo/COMMIT_EDITMSG new file mode 100644 index 0000000000..a77fa514de --- /dev/null +++ b/modules/git/tests/repos/templates_repo/COMMIT_EDITMSG @@ -0,0 +1 @@ +Initial diff --git a/modules/git/tests/repos/templates_repo/HEAD b/modules/git/tests/repos/templates_repo/HEAD new file mode 100644 index 0000000000..cb089cd89a --- /dev/null +++ b/modules/git/tests/repos/templates_repo/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/modules/git/tests/repos/templates_repo/config b/modules/git/tests/repos/templates_repo/config new file mode 100644 index 0000000000..a4ef456cbc --- /dev/null +++ b/modules/git/tests/repos/templates_repo/config @@ -0,0 +1,5 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true + logallrefupdates = true diff --git a/modules/git/tests/repos/templates_repo/description b/modules/git/tests/repos/templates_repo/description new file mode 100644 index 0000000000..498b267a8c --- /dev/null +++ b/modules/git/tests/repos/templates_repo/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/modules/git/tests/repos/templates_repo/index b/modules/git/tests/repos/templates_repo/index new file mode 100644 index 0000000000..2b3f95a155 Binary files /dev/null and b/modules/git/tests/repos/templates_repo/index differ diff --git a/modules/git/tests/repos/templates_repo/info/exclude b/modules/git/tests/repos/templates_repo/info/exclude new file mode 100644 index 0000000000..a5196d1be8 --- /dev/null +++ b/modules/git/tests/repos/templates_repo/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/modules/git/tests/repos/templates_repo/info/refs b/modules/git/tests/repos/templates_repo/info/refs new file mode 100644 index 0000000000..8ee4ab3ff8 --- /dev/null +++ b/modules/git/tests/repos/templates_repo/info/refs @@ -0,0 +1 @@ +45697427ce0595075c5c8efa42567f050208510d refs/heads/master diff --git a/modules/git/tests/repos/templates_repo/objects/info/commit-graph b/modules/git/tests/repos/templates_repo/objects/info/commit-graph new file mode 100644 index 0000000000..8904092586 Binary files /dev/null and b/modules/git/tests/repos/templates_repo/objects/info/commit-graph differ diff --git a/modules/git/tests/repos/templates_repo/objects/info/packs b/modules/git/tests/repos/templates_repo/objects/info/packs new file mode 100644 index 0000000000..5833126b59 --- /dev/null +++ b/modules/git/tests/repos/templates_repo/objects/info/packs @@ -0,0 +1,2 @@ +P pack-abb44544ae19d590e95822e963f78d069d27ba9e.pack + diff --git a/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.bitmap b/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.bitmap new file mode 100644 index 0000000000..6bd87bf13c Binary files /dev/null and b/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.bitmap differ diff --git a/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.idx b/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.idx new file mode 100644 index 0000000000..b530533e1b Binary files /dev/null and b/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.idx differ diff --git a/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.pack b/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.pack new file mode 100644 index 0000000000..6bbcbb6546 Binary files /dev/null and b/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.pack differ diff --git a/modules/git/tests/repos/templates_repo/packed-refs b/modules/git/tests/repos/templates_repo/packed-refs new file mode 100644 index 0000000000..eb6e7a8add --- /dev/null +++ b/modules/git/tests/repos/templates_repo/packed-refs @@ -0,0 +1,2 @@ +# pack-refs with: peeled fully-peeled sorted +45697427ce0595075c5c8efa42567f050208510d refs/heads/master diff --git a/modules/git/tests/repos/templates_repo/refs/heads/.gitkeep b/modules/git/tests/repos/templates_repo/refs/heads/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/modules/git/tests/repos/templates_repo/refs/tags/.gitkeep b/modules/git/tests/repos/templates_repo/refs/tags/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/modules/git/tree_blob.go b/modules/git/tree_blob.go index df339f64b1..5c1aa7753d 100644 --- a/modules/git/tree_blob.go +++ b/modules/git/tree_blob.go @@ -17,7 +17,6 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) { ptree: t, ID: t.ID, name: "", - fullName: "", entryMode: EntryModeTree, }, nil } @@ -55,7 +54,7 @@ func (t *Tree) GetBlobByPath(relpath string) (*Blob, error) { return nil, err } - if !entry.IsDir() && !entry.IsSubModule() { + if !entry.IsDir() && !entry.IsSubmodule() { return entry.Blob(), nil } diff --git a/modules/git/tree_entry.go b/modules/git/tree_entry.go index d51b7992fe..8b6c4c467c 100644 --- a/modules/git/tree_entry.go +++ b/modules/git/tree_entry.go @@ -21,16 +21,12 @@ type TreeEntry struct { entryMode EntryMode name string - size int64 - sized bool - fullName string + size int64 + sized bool } // Name returns the name of the entry func (te *TreeEntry) Name() string { - if te.fullName != "" { - return te.fullName - } return te.name } @@ -68,8 +64,8 @@ func (te *TreeEntry) Size() int64 { return te.size } -// IsSubModule if the entry is a sub module -func (te *TreeEntry) IsSubModule() bool { +// IsSubmodule if the entry is a submodule +func (te *TreeEntry) IsSubmodule() bool { return te.entryMode == EntryModeCommit } @@ -116,32 +112,37 @@ func (te *TreeEntry) Type() string { } } +// LinkTarget returns the target of the symlink as string. +func (te *TreeEntry) LinkTarget() (string, error) { + if !te.IsLink() { + return "", ErrBadLink{te.Name(), "not a symlink"} + } + + const symlinkLimit = 4096 // according to git config core.longpaths https://stackoverflow.com/a/22575737 + blob := te.Blob() + if blob.Size() > symlinkLimit { + return "", ErrBadLink{te.Name(), "symlink too large"} + } + + rc, size, err := blob.NewTruncatedReader(symlinkLimit) + if err != nil { + return "", err + } + defer rc.Close() + + buf := make([]byte, int(size)) + _, err = io.ReadFull(rc, buf) + return string(buf), err +} + // FollowLink returns the entry pointed to by a symlink func (te *TreeEntry) FollowLink() (*TreeEntry, string, error) { - if !te.IsLink() { - return nil, "", ErrBadLink{te.Name(), "not a symlink"} - } - // read the link - r, err := te.Blob().DataAsync() + lnk, err := te.LinkTarget() if err != nil { return nil, "", err } - closed := false - defer func() { - if !closed { - _ = r.Close() - } - }() - buf := make([]byte, te.Size()) - _, err = io.ReadFull(r, buf) - if err != nil { - return nil, "", err - } - _ = r.Close() - closed = true - lnk := string(buf) t := te.ptree // traverse up directories @@ -209,7 +210,7 @@ func (te *TreeEntry) Tree() *Tree { // GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory ) func (te *TreeEntry) GetSubJumpablePathName() string { - if te.IsSubModule() || !te.IsDir() { + if te.IsSubmodule() || !te.IsDir() { return "" } tree, err := te.ptree.SubTree(te.Name()) @@ -236,7 +237,7 @@ type customSortableEntries struct { var sorter = []func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool{ func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool { - return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule() + return (t1.IsDir() || t1.IsSubmodule()) && !t2.IsDir() && !t2.IsSubmodule() }, func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool { return cmp(t1.Name(), t2.Name()) diff --git a/modules/graceful/server_http.go b/modules/graceful/server_http.go index 7c855ac64e..0b2984a953 100644 --- a/modules/graceful/server_http.go +++ b/modules/graceful/server_http.go @@ -16,6 +16,11 @@ func newHTTPServer(network, address, name string, handler http.Handler) (*Server Handler: handler, BaseContext: func(net.Listener) context.Context { return GetManager().HammerContext() }, } + // Enable H2C for HTTP server + httpServer.Protocols = new(http.Protocols) + httpServer.Protocols.SetHTTP1(true) + httpServer.Protocols.SetHTTP2(true) + httpServer.Protocols.SetUnencryptedHTTP2(true) server.OnShutdown = func() { httpServer.SetKeepAlivesEnabled(false) } diff --git a/modules/httplib/serve.go b/modules/httplib/serve.go index c5f0658d4e..d385ac21c9 100644 --- a/modules/httplib/serve.go +++ b/modules/httplib/serve.go @@ -99,7 +99,7 @@ func setServeHeadersByFile(r *http.Request, w http.ResponseWriter, filePath stri Filename: path.Base(filePath), } - sniffedType := typesniffer.DetectContentType(mineBuf) + sniffedType := typesniffer.DetectContentType(mineBuf, opts.Filename) // the "render" parameter came from year 2016: 638dd24c, it doesn't have clear meaning, so I think it could be removed later isPlain := sniffedType.IsText() || r.FormValue("render") != "" diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index c53b7a2e6d..4c8b5f2a86 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -177,7 +177,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro fileContents, err := io.ReadAll(io.LimitReader(batchReader, size)) if err != nil { return err - } else if !typesniffer.DetectContentType(fileContents).IsText() { + } else if !typesniffer.DetectContentType(fileContents, update.Filename).IsText() { // FIXME: UTF-16 files will probably fail here // Even if the file is not recognized as a "text file", we could still put its name into the indexers to make the filename become searchable, while leave the content to empty. fileContents = nil diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go index 3903d77fe0..9b11f56fb7 100644 --- a/modules/indexer/code/elasticsearch/elasticsearch.go +++ b/modules/indexer/code/elasticsearch/elasticsearch.go @@ -144,7 +144,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro fileContents, err := io.ReadAll(io.LimitReader(batchReader, size)) if err != nil { return nil, err - } else if !typesniffer.DetectContentType(fileContents).IsText() { + } else if !typesniffer.DetectContentType(fileContents, update.Filename).IsText() { // FIXME: UTF-16 files will probably fail here return nil, nil } diff --git a/modules/indexer/code/search_test.go b/modules/indexer/code/search_test.go index e542b38c24..2413ddec0b 100644 --- a/modules/indexer/code/search_test.go +++ b/modules/indexer/code/search_test.go @@ -86,9 +86,9 @@ func TestHighlightSearchResultCode(t *testing.T) { Range: [][3]int{{1, 14, 23}}, Code: "func main() {\n\tfmt.Println(\"mark this\")\n}", Result: []template.HTML{ - "func main() {", - "\tfmt.Println("mark this")", - "}", + "func main() {", + "\tfmt.Println("mark this")", + "}", }, }, { @@ -97,9 +97,9 @@ func TestHighlightSearchResultCode(t *testing.T) { Range: [][3]int{{1, 14, 28}}, Code: "func main() {\n\tfmt.Println(\"mark this 😊\")\n}", Result: []template.HTML{ - "func main() {", - "\tfmt.Println("mark this 😊")", - "}", + "func main() {", + "\tfmt.Println("mark this 😊")", + "}", }, }, } diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index 80af7bac45..cb98f722c5 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -171,7 +171,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if issueID, err := token.ParseIssueReference(); err == nil { idQuery := inner_bleve.NumericEqualityQuery(issueID, "index") - idQuery.SetBoost(5.0) + idQuery.SetBoost(20.0) innerQ.AddQuery(idQuery) } @@ -198,6 +198,15 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( queries = append(queries, bleve.NewDisjunctionQuery(repoQueries...)) } + if options.PriorityRepoID.Has() { + eq := inner_bleve.NumericEqualityQuery(options.PriorityRepoID.Value(), "repo_id") + eq.SetBoost(10.0) + meh := bleve.NewMatchAllQuery() + meh.SetBoost(0) + should := bleve.NewDisjunctionQuery(eq, meh) + queries = append(queries, should) + } + if options.IsPull.Has() { queries = append(queries, inner_bleve.BoolFieldQuery(options.IsPull.Value(), "is_pull")) } diff --git a/modules/indexer/issues/db/db.go b/modules/indexer/issues/db/db.go index 397daa3265..5f42bce9a1 100644 --- a/modules/indexer/issues/db/db.go +++ b/modules/indexer/issues/db/db.go @@ -53,6 +53,7 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( cond := builder.NewCond() + var priorityIssueIndex int64 if options.Keyword != "" { repoCond := builder.In("repo_id", options.RepoIDs) if len(options.RepoIDs) == 1 { @@ -82,6 +83,7 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( builder.Eq{"`index`": issueID}, cond, ) + priorityIssueIndex = issueID } } @@ -89,6 +91,7 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if err != nil { return nil, err } + opt.PriorityIssueIndex = priorityIssueIndex // If pagesize == 0, return total count only. It's a special case for search count. if options.Paginator != nil && options.Paginator.PageSize == 0 { diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index 4411cc1c37..55a471fc8e 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -78,6 +78,11 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m User: nil, } + if options.PriorityRepoID.Has() { + opts.SortType = "priorityrepo" + opts.PriorityRepoID = options.PriorityRepoID.Value() + } + if len(options.MilestoneIDs) == 1 && options.MilestoneIDs[0] == 0 { opts.MilestoneIDs = []int64{db.NoConditionID} } else { diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index 0c4e5fae02..311e92730e 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -166,7 +166,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( } var eitherQ elastic.Query = innerQ if issueID, err := token.ParseIssueReference(); err == nil { - indexQ := elastic.NewTermQuery("index", issueID).Boost(15.0) + indexQ := elastic.NewTermQuery("index", issueID).Boost(20) eitherQ = elastic.NewDisMaxQuery().Query(indexQ).Query(innerQ).TieBreaker(0.5) } switch token.Kind { @@ -189,6 +189,10 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( } query.Must(q) } + if options.PriorityRepoID.Has() { + q := elastic.NewTermQuery("repo_id", options.PriorityRepoID.Value()).Boost(10) + query.Should(q) + } if options.IsPull.Has() { query.Must(elastic.NewTermQuery("is_pull", options.IsPull.Value())) diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index 6c55405179..cdd113212d 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -75,8 +75,9 @@ type SearchResult struct { type SearchOptions struct { Keyword string // keyword to search - RepoIDs []int64 // repository IDs which the issues belong to - AllPublic bool // if include all public repositories + RepoIDs []int64 // repository IDs which the issues belong to + AllPublic bool // if include all public repositories + PriorityRepoID optional.Option[int64] // issues from this repository will be prioritized when SortByScore IsPull optional.Option[bool] // if the issues is a pull request IsClosed optional.Option[bool] // if the issues is closed diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index e7723ea69d..46014994a0 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -772,6 +772,25 @@ var cases = []*testIndexerCase{ } }, }, + { + Name: "PriorityRepoID", + SearchOptions: &internal.SearchOptions{ + IsPull: optional.Some(false), + IsClosed: optional.Some(false), + PriorityRepoID: optional.Some(int64(3)), + Paginator: &db.ListOptionsAll, + SortBy: internal.SortByScore, + }, + Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { + for i, v := range result.Hits { + if i < 7 { + assert.Equal(t, int64(3), data[v.ID].RepoID) + } else { + assert.NotEqual(t, int64(3), data[v.ID].RepoID) + } + } + }, + }, } type testIndexerCase struct { diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index 17a8ba2452..8c83a7f417 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -13,6 +13,7 @@ import ( indexer_internal "forgejo.org/modules/indexer/internal" inner_meilisearch "forgejo.org/modules/indexer/internal/meilisearch" "forgejo.org/modules/indexer/issues/internal" + "forgejo.org/modules/json" "github.com/meilisearch/meilisearch-go" ) @@ -100,7 +101,7 @@ func (b *Indexer) Index(_ context.Context, issues ...*internal.IndexerData) erro return nil } for _, issue := range issues { - _, err := b.inner.Client.Index(b.inner.VersionedIndexName()).AddDocuments(issue) + _, err := b.inner.Client.Index(b.inner.VersionedIndexName()).AddDocuments(issue, nil) if err != nil { return err } @@ -305,18 +306,13 @@ func doubleQuoteKeyword(k string) string { func convertHits(searchRes *meilisearch.SearchResponse) ([]internal.Match, error) { hits := make([]internal.Match, 0, len(searchRes.Hits)) for _, hit := range searchRes.Hits { - hit, ok := hit.(map[string]any) - if !ok { - return nil, ErrMalformedResponse - } - - issueID, ok := hit["id"].(float64) - if !ok { + var issueID int64 + if err := json.Unmarshal(hit["id"], &issueID); err != nil { return nil, ErrMalformedResponse } hits = append(hits, internal.Match{ - ID: int64(issueID), + ID: issueID, }) } return hits, nil diff --git a/modules/indexer/issues/meilisearch/meilisearch_test.go b/modules/indexer/issues/meilisearch/meilisearch_test.go index 7637e8d6b4..810de77046 100644 --- a/modules/indexer/issues/meilisearch/meilisearch_test.go +++ b/modules/indexer/issues/meilisearch/meilisearch_test.go @@ -46,30 +46,34 @@ func TestMeilisearchIndexer(t *testing.T) { } func TestConvertHits(t *testing.T) { - _, err := convertHits(&meilisearch.SearchResponse{ - Hits: []any{"aa", "bb", "cc", "dd"}, - }) - require.ErrorIs(t, err, ErrMalformedResponse) + for _, invalidID := range []string{"\"aa\"", "{\"aa\":\"123\"}", "[\"aa\"]"} { + _, err := convertHits(&meilisearch.SearchResponse{ + Hits: meilisearch.Hits{ + meilisearch.Hit{"id": []byte(invalidID)}, + }, + }) + require.ErrorIs(t, err, ErrMalformedResponse) + } validResponse := &meilisearch.SearchResponse{ - Hits: []any{ - map[string]any{ - "id": float64(11), - "title": "a title", - "content": "issue body with no match", - "comments": []any{"hey what's up?", "I'm currently bowling", "nice"}, + Hits: meilisearch.Hits{ + meilisearch.Hit{ + "id": []byte("11"), + "title": []byte("\"a title\""), + "content": []byte("\"issue body with no match\""), + "comments": []byte("[\"hey what's up?\", \"I'm currently bowling\", \"nice\"]"), }, - map[string]any{ - "id": float64(22), - "title": "Bowling as title", - "content": "", - "comments": []any{}, + meilisearch.Hit{ + "id": []byte("22"), + "title": []byte("\"Bowling as title\""), + "content": []byte("\"\""), + "comments": []byte("[]"), }, - map[string]any{ - "id": float64(33), - "title": "Bowl-ing as fuzzy match", - "content": "", - "comments": []any{}, + meilisearch.Hit{ + "id": []byte("33"), + "title": []byte("\"Bowl-ing as fuzzy match\""), + "content": []byte("\"\""), + "comments": []byte("[]"), }, }, } diff --git a/modules/keying/keying.go b/modules/keying/keying.go index c859e30e9f..f39e16aeed 100644 --- a/modules/keying/keying.go +++ b/modules/keying/keying.go @@ -58,6 +58,8 @@ var ( ContextPushMirror Context = "pushmirror" // Used for the `two_factor` table. ContextTOTP Context = "totp" + // Used for the `secret` table. + ContextActionSecret Context = "action_secret" ) // Derive *the* key for a given context, this is a deterministic function. diff --git a/modules/lfs/pointer_scanner.go b/modules/lfs/pointer_scanner.go index 632ecd19ae..80da8e5222 100644 --- a/modules/lfs/pointer_scanner.go +++ b/modules/lfs/pointer_scanner.go @@ -39,16 +39,7 @@ func SearchPointerBlobs(ctx context.Context, repo *git.Repository, pointerChan c go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg) // 1. Run batch-check on all objects in the repository - if git.CheckGitVersionAtLeast("2.6.0") != nil { - revListReader, revListWriter := io.Pipe() - shasToCheckReader, shasToCheckWriter := io.Pipe() - wg.Add(2) - go pipeline.CatFileBatchCheck(ctx, shasToCheckReader, catFileCheckWriter, &wg, basePath) - go pipeline.BlobsFromRevListObjects(revListReader, shasToCheckWriter, &wg) - go pipeline.RevListAllObjects(ctx, revListWriter, &wg, basePath, errChan) - } else { - go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan) - } + go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan) wg.Wait() close(pointerChan) diff --git a/modules/log/event_format.go b/modules/log/event_format.go index df6b083a92..6835a4ca5b 100644 --- a/modules/log/event_format.go +++ b/modules/log/event_format.go @@ -13,10 +13,9 @@ import ( type Event struct { Time time.Time - GoroutinePid string - Caller string - Filename string - Line int + Caller string + Filename string + Line int Level Level @@ -225,19 +224,6 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms msg = msg[:len(msg)-1] } - if flags&Lgopid == Lgopid { - if event.GoroutinePid != "" { - buf = append(buf, '[') - if mode.Colorize { - buf = append(buf, ColorBytes(FgHiYellow)...) - } - buf = append(buf, event.GoroutinePid...) - if mode.Colorize { - buf = append(buf, resetBytes...) - } - buf = append(buf, ']', ' ') - } - } buf = append(buf, msg...) if event.Stacktrace != "" && mode.StacktraceLevel <= event.Level { diff --git a/modules/log/event_format_test.go b/modules/log/event_format_test.go index 0c6061eaea..f6f9754300 100644 --- a/modules/log/event_format_test.go +++ b/modules/log/event_format_test.go @@ -24,48 +24,45 @@ func TestItoa(t *testing.T) { func TestEventFormatTextMessage(t *testing.T) { res := EventFormatTextMessage(&WriterMode{Prefix: "[PREFIX] ", Colorize: false, Flags: Flags{defined: true, flags: 0xffffffff}}, &Event{ - Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), - Caller: "caller", - Filename: "filename", - Line: 123, - GoroutinePid: "pid", - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + Level: ERROR, + Stacktrace: "stacktrace", }, "msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue), ) - assert.Equal(t, `<3>[PREFIX] 2020/01/02 03:04:05.000000 filename:123:caller [E] [pid] msg format: arg0 arg1 + assert.Equal(t, `<3>[PREFIX] 2020/01/02 03:04:05.000000 filename:123:caller [E] msg format: arg0 arg1 stacktrace `, string(res)) res = EventFormatTextMessage(&WriterMode{Prefix: "[PREFIX] ", Colorize: true, Flags: Flags{defined: true, flags: 0xffffffff}}, &Event{ - Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), - Caller: "caller", - Filename: "filename", - Line: 123, - GoroutinePid: "pid", - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + Level: ERROR, + Stacktrace: "stacktrace", }, "msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue), ) - assert.Equal(t, "<3>[PREFIX] \x1b[36m2020/01/02 03:04:05.000000 \x1b[0m\x1b[32mfilename:123:\x1b[32mcaller\x1b[0m \x1b[1;31m[E]\x1b[0m [\x1b[93mpid\x1b[0m] msg format: arg0 \x1b[34marg1\x1b[0m\n\tstacktrace\n\n", string(res)) + assert.Equal(t, "<3>[PREFIX] \x1b[36m2020/01/02 03:04:05.000000 \x1b[0m\x1b[32mfilename:123:\x1b[32mcaller\x1b[0m \x1b[1;31m[E]\x1b[0m msg format: arg0 \x1b[34marg1\x1b[0m\n\tstacktrace\n\n", string(res)) } func TestEventFormatTextMessageStd(t *testing.T) { res := EventFormatTextMessage(&WriterMode{Prefix: "[PREFIX] ", Colorize: false, Flags: Flags{defined: true, flags: LstdFlags}}, &Event{ - Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), - Caller: "caller", - Filename: "filename", - Line: 123, - GoroutinePid: "pid", - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + Level: ERROR, + Stacktrace: "stacktrace", }, "msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue), ) @@ -77,13 +74,12 @@ func TestEventFormatTextMessageStd(t *testing.T) { res = EventFormatTextMessage(&WriterMode{Prefix: "[PREFIX] ", Colorize: true, Flags: Flags{defined: true, flags: LstdFlags}}, &Event{ - Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), - Caller: "caller", - Filename: "filename", - Line: 123, - GoroutinePid: "pid", - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + Level: ERROR, + Stacktrace: "stacktrace", }, "msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue), ) @@ -96,13 +92,12 @@ func TestEventFormatTextMessageJournal(t *testing.T) { // the proper way here is to attach the backtrace as structured metadata, but we can't do that via stderr res := EventFormatTextMessage(&WriterMode{Prefix: "[PREFIX] ", Colorize: false, Flags: Flags{defined: true, flags: LjournaldFlags}}, &Event{ - Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), - Caller: "caller", - Filename: "filename", - Line: 123, - GoroutinePid: "pid", - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + Level: ERROR, + Stacktrace: "stacktrace", }, "msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue), ) diff --git a/modules/log/event_writer.go b/modules/log/event_writer.go index 4b77e488de..32b5b582c5 100644 --- a/modules/log/event_writer.go +++ b/modules/log/event_writer.go @@ -26,6 +26,7 @@ type WriterMode struct { Flags Flags Expression string + Exclusion string StacktraceLevel Level diff --git a/modules/log/event_writer_base.go b/modules/log/event_writer_base.go index 9189ca4e90..4de2b953c7 100644 --- a/modules/log/event_writer_base.go +++ b/modules/log/event_writer_base.go @@ -68,6 +68,14 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) { } } + var exclusionRegexp *regexp.Regexp + if b.Mode.Exclusion != "" { + var err error + if exclusionRegexp, err = regexp.Compile(b.Mode.Exclusion); err != nil { + FallbackErrorf("unable to compile exclusion %q for writer %q: %v", b.Mode.Exclusion, b.Name, err) + } + } + handlePaused := func() { if pause := b.GetPauseChan(); pause != nil { select { @@ -95,6 +103,13 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) { continue } } + if exclusionRegexp != nil { + fileLineCaller := fmt.Sprintf("%s:%d:%s", event.Origin.Filename, event.Origin.Line, event.Origin.Caller) + matched := exclusionRegexp.MatchString(fileLineCaller) || exclusionRegexp.MatchString(event.Origin.MsgSimpleText) + if matched { + continue + } + } var err error switch msg := event.Msg.(type) { diff --git a/modules/log/event_writer_buffer_test.go b/modules/log/event_writer_buffer_test.go index ba9455ba69..d1e37c3673 100644 --- a/modules/log/event_writer_buffer_test.go +++ b/modules/log/event_writer_buffer_test.go @@ -31,3 +31,49 @@ func TestBufferLogger(t *testing.T) { logger.Close() assert.Contains(t, bufferWriter.Buffer.String(), expected) } + +func TestBufferLoggerWithExclusion(t *testing.T) { + prefix := "ExclusionPrefix " + level := log.INFO + message := "something" + + bufferWriter := log.NewEventWriterBuffer("test-buffer", log.WriterMode{ + Level: level, + Prefix: prefix, + Exclusion: message, + }) + + logger := log.NewLoggerWithWriters(t.Context(), "test", bufferWriter) + + logger.SendLogEvent(&log.Event{ + Level: log.INFO, + MsgSimpleText: message, + }) + logger.Close() + assert.NotContains(t, bufferWriter.Buffer.String(), message) +} + +func TestBufferLoggerWithExpressionAndExclusion(t *testing.T) { + prefix := "BothPrefix " + level := log.INFO + expression := ".*foo.*" + exclusion := ".*bar.*" + + bufferWriter := log.NewEventWriterBuffer("test-buffer", log.WriterMode{ + Level: level, + Prefix: prefix, + Expression: expression, + Exclusion: exclusion, + }) + + logger := log.NewLoggerWithWriters(t.Context(), "test", bufferWriter) + + logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "foo expression"}) + logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "bar exclusion"}) + logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "foo bar both"}) + logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "none"}) + logger.Close() + + assert.Contains(t, bufferWriter.Buffer.String(), "foo expression") + assert.NotContains(t, bufferWriter.Buffer.String(), "bar") +} diff --git a/modules/log/flags.go b/modules/log/flags.go index afce30680d..1e4fe830c1 100644 --- a/modules/log/flags.go +++ b/modules/log/flags.go @@ -30,7 +30,6 @@ const ( LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone Llevelinitial // Initial character of the provided level in brackets, eg. [I] for info Llevel // Provided level in brackets [INFO] - Lgopid // the Goroutine-PID of the context Llevelprefix // printk-style logging prefixes as documented in sd-daemon(3), used by journald Lmedfile = Lshortfile | Llongfile // last 20 characters of the filename @@ -57,7 +56,6 @@ var flagFromString = map[string]uint32{ "levelinitial": Llevelinitial, "level": Llevel, "levelprefix": Llevelprefix, - "gopid": Lgopid, "medfile": Lmedfile, "stdflags": LstdFlags, @@ -82,7 +80,6 @@ var flagComboToString = []struct { {LUTC, "utc"}, {Llevelinitial, "levelinitial"}, {Llevel, "level"}, - {Lgopid, "gopid"}, } func (f Flags) Bits() uint32 { diff --git a/modules/log/flags_test.go b/modules/log/flags_test.go index 1af9a58ed6..f39fe440e2 100644 --- a/modules/log/flags_test.go +++ b/modules/log/flags_test.go @@ -15,9 +15,7 @@ import ( func TestFlags(t *testing.T) { assert.Equal(t, Ldefault, Flags{}.Bits()) assert.EqualValues(t, 0, FlagsFromString("").Bits()) - assert.Equal(t, Lgopid, FlagsFromString("", Lgopid).Bits()) - assert.EqualValues(t, 0, FlagsFromString("none", Lgopid).Bits()) - assert.Equal(t, Ldate|Ltime, FlagsFromString("date,time", Lgopid).Bits()) + assert.Equal(t, Ldate|Ltime, FlagsFromString("date,time").Bits()) assert.Equal(t, "stdflags", FlagsFromString("stdflags").String()) assert.Equal(t, "medfile", FlagsFromString("medfile").String()) diff --git a/modules/log/groutinelabel.go b/modules/log/groutinelabel.go deleted file mode 100644 index cd5fb96d52..0000000000 --- a/modules/log/groutinelabel.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build !go1.24 - -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package log - -import "unsafe" - -//go:linkname runtime_getProfLabel runtime/pprof.runtime_getProfLabel -func runtime_getProfLabel() unsafe.Pointer //nolint - -type labelMap map[string]string - -func getGoroutineLabels() map[string]string { - l := (*labelMap)(runtime_getProfLabel()) - if l == nil { - return nil - } - return *l -} diff --git a/modules/log/groutinelabel_go1.24.go b/modules/log/groutinelabel_go1.24.go deleted file mode 100644 index e849811bc2..0000000000 --- a/modules/log/groutinelabel_go1.24.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build go1.24 - -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package log - -import "unsafe" - -//go:linkname runtime_getProfLabel runtime/pprof.runtime_getProfLabel -func runtime_getProfLabel() unsafe.Pointer //nolint - -// Struct definitions copied from: https://github.com/golang/go/blob/ca63101df47a4467bc80faa654fc19d68e583952/src/runtime/pprof/label.go -type label struct { - key string - value string -} - -type LabelSet struct { - list []label -} - -type labelMap struct { - LabelSet -} - -func getGoroutineLabels() map[string]string { - l := (*labelMap)(runtime_getProfLabel()) - if l == nil { - return nil - } - - m := make(map[string]string, len(l.list)) - for _, label := range l.list { - m[label.key] = label.value - } - return m -} diff --git a/modules/log/groutinelabel_test.go b/modules/log/groutinelabel_test.go deleted file mode 100644 index 98d7b4e129..0000000000 --- a/modules/log/groutinelabel_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package log - -import ( - "context" - "runtime/pprof" - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_getGoroutineLabels(t *testing.T) { - pprof.Do(t.Context(), pprof.Labels(), func(ctx context.Context) { - currentLabels := getGoroutineLabels() - pprof.ForLabels(ctx, func(key, value string) bool { - assert.Equal(t, value, currentLabels[key]) - return true - }) - - pprof.Do(ctx, pprof.Labels("Test_getGoroutineLabels", "Test_getGoroutineLabels_child1"), func(ctx context.Context) { - currentLabels := getGoroutineLabels() - pprof.ForLabels(ctx, func(key, value string) bool { - assert.Equal(t, value, currentLabels[key]) - return true - }) - if assert.NotNil(t, currentLabels) { - assert.Equal(t, "Test_getGoroutineLabels_child1", currentLabels["Test_getGoroutineLabels"]) - } - }) - }) -} diff --git a/modules/log/logger_impl.go b/modules/log/logger_impl.go index b21e800f52..76d7e6a821 100644 --- a/modules/log/logger_impl.go +++ b/modules/log/logger_impl.go @@ -200,11 +200,6 @@ func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) { event.Stacktrace = Stack(skip + 1) } - labels := getGoroutineLabels() - if labels != nil { - event.GoroutinePid = labels["pid"] - } - // get a simple text message without color msgArgs := make([]any, len(logArgs)) copy(msgArgs, logArgs) diff --git a/modules/log/logger_test.go b/modules/log/logger_test.go index 6d6ceb69d7..99045b0f4f 100644 --- a/modules/log/logger_test.go +++ b/modules/log/logger_test.go @@ -143,3 +143,19 @@ func TestLoggerExpressionFilter(t *testing.T) { assert.Equal(t, []string{"foo\n", "foo bar\n", "by filename\n"}, w1.GetLogs()) } + +func TestLoggerExclusionFilter(t *testing.T) { + logger := NewLoggerWithWriters(t.Context(), "test") + + w1 := newDummyWriter("dummy-1", DEBUG, 0) + w1.Mode.Exclusion = "foo.*" + logger.AddWriters(w1) + + logger.Info("foo") + logger.Info("bar") + logger.Info("foo bar") + logger.SendLogEvent(&Event{Level: INFO, Filename: "foo.go", MsgSimpleText: "by filename"}) + logger.Close() + + assert.Equal(t, []string{"bar\n"}, w1.GetLogs()) +} diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 2bc929bb04..6bc0f7e56c 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -743,11 +743,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -777,11 +777,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -856,11 +856,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -887,11 +887,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -920,11 +920,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -945,11 +945,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -976,11 +976,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1001,11 +1001,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1026,11 +1026,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1129,11 +1129,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index d229afa8e3..67d81488fd 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -9,6 +9,7 @@ import ( "strings" "forgejo.org/modules/markup" + markdownutil "forgejo.org/modules/markup/markdown/util" "forgejo.org/modules/setting" "github.com/yuin/goldmark/ast" @@ -35,8 +36,8 @@ func (g *ASTTransformer) applyElementDir(n ast.Node) { func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { firstChild := node.FirstChild() tocMode := "" - ctx := pc.Get(renderContextKey).(*markup.RenderContext) - rc := pc.Get(renderConfigKey).(*RenderConfig) + ctx := pc.Get(markdownutil.RenderContextKey).(*markup.RenderContext) + rc := pc.Get(markdownutil.RenderConfigKey).(*RenderConfig) tocList := make([]markup.Header, 0, 20) if rc.yamlNode != nil { diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index e811d29994..2b19e0f1c9 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -16,6 +16,7 @@ import ( "forgejo.org/modules/markup/common" "forgejo.org/modules/markup/markdown/callout" "forgejo.org/modules/markup/markdown/math" + markdownutil "forgejo.org/modules/markup/markdown/util" "forgejo.org/modules/setting" giteautil "forgejo.org/modules/util" @@ -34,11 +35,6 @@ var ( specMarkdownOnce sync.Once ) -var ( - renderContextKey = parser.NewContextKey() - renderConfigKey = parser.NewContextKey() -) - type limitWriter struct { w io.Writer sum int64 @@ -64,7 +60,7 @@ func (l *limitWriter) Write(data []byte) (int, error) { // newParserContext creates a parser.Context with the render context set func newParserContext(ctx *markup.RenderContext) parser.Context { pc := parser.NewContext(parser.WithIDs(newPrefixedIDs())) - pc.Set(renderContextKey, ctx) + pc.Set(markdownutil.RenderContextKey, ctx) return pc } @@ -192,7 +188,7 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) } rc.metaLength = metaLength - pc.Set(renderConfigKey, rc) + pc.Set(markdownutil.RenderConfigKey, rc) if err := converter.Convert(buf, lw, parser.WithContext(pc)); err != nil { log.Error("Unable to render: %v", err) diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index f7955115e0..c854861031 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -561,6 +561,14 @@ func TestMathBlock(t *testing.T) { "test $$a$$", `

test a

` + nl, }, + { + `\[ +[\triangle ABC] = \sqrt{s(s-a)(s-b)(s-c)} +\]`, + `

[
+[\triangle ABC] = \sqrt{s(s-a)(s-b)(s-c)}
+]

` + nl, + }, } for _, test := range testcases { @@ -568,6 +576,32 @@ func TestMathBlock(t *testing.T) { require.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) } + + t.Run("Wiki context", func(t *testing.T) { + testcases := []struct { + testcase string + expected string + }{ + { + "$a$", + `

a

` + nl, + }, + { + `\[ +[\triangle ABC] = \sqrt{s(s-a)(s-b)(s-c)} +\]`, + `

+[\triangle ABC] = \sqrt{s(s-a)(s-b)(s-c)}
+
` + nl, + }, + } + + for _, test := range testcases { + res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext, IsWiki: true}, test.testcase) + require.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) + assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) + } + }) } func TestFootnote(t *testing.T) { diff --git a/modules/markup/markdown/math/block_parser.go b/modules/markup/markdown/math/block_parser.go index 527df84975..b0fe1d588a 100644 --- a/modules/markup/markdown/math/block_parser.go +++ b/modules/markup/markdown/math/block_parser.go @@ -6,6 +6,9 @@ package math import ( "bytes" + "forgejo.org/modules/markup" + markdownutil "forgejo.org/modules/markup/markdown/util" + "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/text" @@ -61,6 +64,13 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex return node, parser.Close | parser.NoChildren } + ctx := pc.Get(markdownutil.RenderContextKey).(*markup.RenderContext) + if ctx.IsWiki { + reader.Advance(segment.Len() - 1) + segment.Start += 2 + node.Lines().Append(segment) + return node, parser.NoChildren + } return nil, parser.NoChildren } diff --git a/modules/markup/markdown/util/text.go b/modules/markup/markdown/util/text.go index 8a42e5835b..db6e432e79 100644 --- a/modules/markup/markdown/util/text.go +++ b/modules/markup/markdown/util/text.go @@ -7,6 +7,7 @@ import ( "bytes" "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/parser" ) func textOfChildren(n ast.Node, src []byte, b *bytes.Buffer) { @@ -24,3 +25,8 @@ func Text(n ast.Node, src []byte) []byte { textOfChildren(n, src, &b) return b.Bytes() } + +var ( + RenderContextKey = parser.NewContextKey() + RenderConfigKey = parser.NewContextKey() +) diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go index cdaa9f18ce..71157dc7c7 100644 --- a/modules/markup/orgmode/orgmode_test.go +++ b/modules/markup/orgmode/orgmode_test.go @@ -152,9 +152,9 @@ func HelloWorld() { } #+end_src `, `
-
// HelloWorld prints "Hello World"
-func HelloWorld() {
-	fmt.Println("Hello World")
-}
+
// HelloWorld prints "Hello World"
+func HelloWorld() {
+	fmt.Println("Hello World")
+}
`) } diff --git a/modules/packages/arch/metadata.go b/modules/packages/arch/metadata.go index f967bd25a0..ca352c828c 100644 --- a/modules/packages/arch/metadata.go +++ b/modules/packages/arch/metadata.go @@ -7,6 +7,7 @@ import ( "archive/tar" "bufio" "bytes" + "context" "encoding/hex" "errors" "fmt" @@ -19,7 +20,7 @@ import ( "forgejo.org/modules/util" "forgejo.org/modules/validation" - "github.com/mholt/archiver/v3" + "github.com/mholt/archives" ) // Arch Linux Packages @@ -109,55 +110,61 @@ func ParsePackage(r *packages.HashedBuffer) (*Package, error) { return nil, err } - var tarball archiver.Reader + var tarball archives.Extractor var tarballType string if bytes.Equal(header[:len(magicZSTD)], magicZSTD) { tarballType = "zst" - tarball = archiver.NewTarZstd() + tarball = archives.CompressedArchive{ + Compression: archives.Zstd{}, + Extraction: archives.Tar{}, + } } else if bytes.Equal(header[:len(magicXZ)], magicXZ) { tarballType = "xz" - tarball = archiver.NewTarXz() + tarball = archives.CompressedArchive{ + Compression: archives.Xz{}, + Extraction: archives.Tar{}, + } } else if bytes.Equal(header[:len(magicGZ)], magicGZ) { tarballType = "gz" - tarball = archiver.NewTarGz() + tarball = archives.CompressedArchive{ + Compression: archives.Gz{}, + Extraction: archives.Tar{}, + } } else { return nil, errors.New("not supported compression") } - err = tarball.Open(r, 0) - if err != nil { - return nil, err - } - defer tarball.Close() var pkg *Package var mTree bool files := make([]string, 0) - for { - f, err := tarball.Read() - if err == io.EOF { - break - } - if err != nil { - return nil, err - } + err = tarball.Extract(context.TODO(), r, func(ctx context.Context, file archives.FileInfo) error { // ref:https://gitlab.archlinux.org/pacman/pacman/-/blob/91546004903eea5d5267d59898a6029ba1d64031/lib/libalpm/add.c#L529-L533 - if !strings.HasPrefix(f.Name(), ".") { - files = append(files, (f.Header.(*tar.Header)).Name) + if !strings.HasPrefix(file.Name(), ".") { + files = append(files, (file.Header.(*tar.Header)).Name) } - switch f.Name() { + switch file.Name() { case ".PKGINFO": + f, err := file.Open() + if err != nil { + return err + } + defer f.Close() + pkg, err = ParsePackageInfo(tarballType, f) if err != nil { - _ = f.Close() - return nil, err + return err } case ".MTREE": mTree = true } - _ = f.Close() + + return nil + }) + if err != nil { + return nil, err } if pkg == nil { diff --git a/modules/packages/arch/metadata_test.go b/modules/packages/arch/metadata_test.go index 16c1c1637d..fbdc9e5ddc 100644 --- a/modules/packages/arch/metadata_test.go +++ b/modules/packages/arch/metadata_test.go @@ -5,7 +5,7 @@ package arch import ( "bytes" - "errors" + "io/fs" "os" "strings" "testing" @@ -14,7 +14,7 @@ import ( "forgejo.org/modules/packages" - "github.com/mholt/archiver/v3" + "github.com/mholt/archives" "github.com/stretchr/testify/require" ) @@ -25,7 +25,7 @@ pkgbase = b pkgver = 1-2 arch = x86_64 ` - fs := fstest.MapFS{ + mapfs := fstest.MapFS{ "pkginfo": &fstest.MapFile{ Data: []byte(PKGINFO), Mode: os.ModePerm, @@ -39,65 +39,111 @@ arch = x86_64 } // Test .PKGINFO file - pinf, err := fs.Stat("pkginfo") - require.NoError(t, err) - - pfile, err := fs.Open("pkginfo") - require.NoError(t, err) - - parcname, err := archiver.NameInArchive(pinf, ".PKGINFO", ".PKGINFO") + pinf, err := mapfs.Stat("pkginfo") require.NoError(t, err) // Test .MTREE file - minf, err := fs.Stat("mtree") + minf, err := mapfs.Stat("mtree") require.NoError(t, err) - mfile, err := fs.Open("mtree") - require.NoError(t, err) + files := []archives.FileInfo{ + { + FileInfo: pinf, + NameInArchive: ".PKGINFO", + Open: func() (fs.File, error) { + return mapfs.Open("pkginfo") + }, + }, + { + FileInfo: minf, + NameInArchive: ".MTREE", + Open: func() (fs.File, error) { + return mapfs.Open("mtree") + }, + }, + } - marcname, err := archiver.NameInArchive(minf, ".MTREE", ".MTREE") - require.NoError(t, err) - - t.Run("normal archive", func(t *testing.T) { + t.Run("normal zst archive", func(t *testing.T) { var buf bytes.Buffer - - archive := archiver.NewTarZstd() - archive.Create(&buf) - - err = archive.Write(archiver.File{ - FileInfo: archiver.FileInfo{ - FileInfo: pinf, - CustomName: parcname, - }, - ReadCloser: pfile, - }) - require.NoError(t, errors.Join(pfile.Close(), err)) - - err = archive.Write(archiver.File{ - FileInfo: archiver.FileInfo{ - FileInfo: minf, - CustomName: marcname, - }, - ReadCloser: mfile, - }) - require.NoError(t, errors.Join(mfile.Close(), archive.Close(), err)) + archive := archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Zstd{}, + } + archive.Archive(t.Context(), &buf, files) reader, err := packages.CreateHashedBufferFromReader(&buf) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer reader.Close() - _, err = ParsePackage(reader) + pkg, err := ParsePackage(reader) + + require.Equal(t, "zst", pkg.CompressType) + require.Equal(t, "a", pkg.Name) + require.Equal(t, "b", pkg.VersionMetadata.Base) + require.Equal(t, "x86_64", pkg.FileMetadata.Arch) + require.Equal(t, "1-2", pkg.Version) + + require.NoError(t, err) + }) + + t.Run("normal xz archive", func(t *testing.T) { + var buf bytes.Buffer + archive := archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Xz{}, + } + archive.Archive(t.Context(), &buf, files) + + reader, err := packages.CreateHashedBufferFromReader(&buf) + require.NoError(t, err) + defer reader.Close() + pkg, err := ParsePackage(reader) + + require.Equal(t, "xz", pkg.CompressType) + require.Equal(t, "a", pkg.Name) + require.Equal(t, "b", pkg.VersionMetadata.Base) + require.Equal(t, "x86_64", pkg.FileMetadata.Arch) + require.Equal(t, "1-2", pkg.Version) + + require.NoError(t, err) + }) + + t.Run("normal gz archive", func(t *testing.T) { + var buf bytes.Buffer + archive := archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Gz{}, + } + archive.Archive(t.Context(), &buf, files) + + reader, err := packages.CreateHashedBufferFromReader(&buf) + require.NoError(t, err) + defer reader.Close() + pkg, err := ParsePackage(reader) + + require.Equal(t, "gz", pkg.CompressType) + require.Equal(t, "a", pkg.Name) + require.Equal(t, "b", pkg.VersionMetadata.Base) + require.Equal(t, "x86_64", pkg.FileMetadata.Arch) + require.Equal(t, "1-2", pkg.Version) require.NoError(t, err) }) t.Run("missing .PKGINFO", func(t *testing.T) { var buf bytes.Buffer - - archive := archiver.NewTarZstd() - archive.Create(&buf) - require.NoError(t, archive.Close()) + archive := archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Zstd{}, + } + archive.Archive(t.Context(), &buf, []archives.FileInfo{ + { + FileInfo: minf, + NameInArchive: ".MTREE", + Open: func() (fs.File, error) { + return mapfs.Open("mtree") + }, + }, + }) reader, err := packages.CreateHashedBufferFromReader(&buf) require.NoError(t, err) @@ -111,21 +157,20 @@ arch = x86_64 t.Run("missing .MTREE", func(t *testing.T) { var buf bytes.Buffer - - pfile, err := fs.Open("pkginfo") - require.NoError(t, err) - - archive := archiver.NewTarZstd() - archive.Create(&buf) - - err = archive.Write(archiver.File{ - FileInfo: archiver.FileInfo{ - FileInfo: pinf, - CustomName: parcname, + archive := archives.CompressedArchive{ + Archival: archives.Tar{}, + Compression: archives.Zstd{}, + } + archive.Archive(t.Context(), &buf, []archives.FileInfo{ + { + FileInfo: pinf, + NameInArchive: ".PKGINFO", + Open: func() (fs.File, error) { + return mapfs.Open("pkginfo") + }, }, - ReadCloser: pfile, }) - require.NoError(t, errors.Join(pfile.Close(), archive.Close(), err)) + reader, err := packages.CreateHashedBufferFromReader(&buf) require.NoError(t, err) diff --git a/modules/packages/debian/metadata.go b/modules/packages/debian/metadata.go index e44801654b..1729a2206e 100644 --- a/modules/packages/debian/metadata.go +++ b/modules/packages/debian/metadata.go @@ -46,7 +46,7 @@ var ( // https://www.debian.org/doc/debian-policy/ch-controlfields.html#source namePattern = regexp.MustCompile(`\A[a-z0-9][a-z0-9+-.]+\z`) // https://www.debian.org/doc/debian-policy/ch-controlfields.html#version - versionPattern = regexp.MustCompile(`\A(?:[0-9]:)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?\z`) + versionPattern = regexp.MustCompile(`\A(?:[1-9]?[0-9]:)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?\z`) ) type Package struct { diff --git a/modules/packages/debian/metadata_test.go b/modules/packages/debian/metadata_test.go index cfcbc57ee0..079b9c19c8 100644 --- a/modules/packages/debian/metadata_test.go +++ b/modules/packages/debian/metadata_test.go @@ -167,6 +167,14 @@ func TestParseControlFile(t *testing.T) { require.ErrorIs(t, err, ErrInvalidArchitecture) }) + t.Run("ValidVersionEpoch", func(t *testing.T) { + for _, version := range []string{"0:1.2.3-test", "1:1.2.3-test", "9:1.2.3-test", "10:1.2.3-test", "37:1.2.3-test", "99:1.2.3-test"} { + p, err := ParseControlFile(buildContent(packageName, version, packageArchitecture)) + require.NoError(t, err) + assert.NotNil(t, p) + } + }) + t.Run("Valid", func(t *testing.T) { content := buildContent(packageName, packageVersion, packageArchitecture) full := content.String() diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go index 19f3b9008a..a34859de56 100644 --- a/modules/setting/config_provider.go +++ b/modules/setting/config_provider.go @@ -331,6 +331,14 @@ func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, n } } +func deprecatedSettingWarning(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey string) { //nolint:unparam + if rootCfg.Section(oldSection).HasKey(oldKey) { + msg := fmt.Sprintf("Deprecated config option `[%s]` `%s` present. Use `[%s]` `%s` instead.", oldSection, oldKey, newSection, newKey) + log.Error("%v", msg) + DeprecatedWarnings = append(DeprecatedWarnings, msg) + } +} + // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) { if rootCfg.Section(oldSection).HasKey(oldKey) { diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index e592220de6..a890a4a328 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -44,9 +44,14 @@ func loadIncomingEmailFrom(rootCfg ConfigProvider) { if sec.HasKey("USER") && !sec.HasKey("USERNAME") { IncomingEmail.Username = sec.Key("USER").String() } + if sec.HasKey("PASSWD") && !sec.HasKey("PASSWORD") { - IncomingEmail.Password = sec.Key("PASSWD").String() + sec.Key("PASSWORD").SetValue(sec.Key("PASSWD").String()) } + if sec.HasKey("PASSWD_URI") && !sec.HasKey("PASSWORD_URI") { + sec.Key("PASSWORD_URI").SetValue(sec.Key("PASSWD_URI").String()) + } + IncomingEmail.Password = loadSecret(sec, "PASSWORD_URI", "PASSWORD") // Infer Port if not set if IncomingEmail.Port == 0 { diff --git a/modules/setting/incoming_email_test.go b/modules/setting/incoming_email_test.go index 6d181cae3c..4ea740bafd 100644 --- a/modules/setting/incoming_email_test.go +++ b/modules/setting/incoming_email_test.go @@ -4,6 +4,8 @@ package setting import ( + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -35,6 +37,22 @@ func Test_loadIncomingEmailFrom(t *testing.T) { assert.Equal(t, "y0u'll n3v3r gUess th1S!!1", IncomingEmail.Password) }) + t.Run("Secrets", func(t *testing.T) { + uri := filepath.Join(t.TempDir(), "email_incoming_password") + + if err := os.WriteFile(uri, []byte("th1S gUess n3v3r y0u'll!!1"), 0o644); err != nil { + t.Fatal(err) + } + + cfg, sec := makeBaseConfig() + sec.NewKey("PASSWORD_URI", "file:"+uri) + + IncomingEmail.Password = "" + loadIncomingEmailFrom(cfg) + + assert.Equal(t, "th1S gUess n3v3r y0u'll!!1", IncomingEmail.Password) + }) + t.Run("Port settings", func(t *testing.T) { t.Run("no port, no tls", func(t *testing.T) { defer resetIncomingEmailPort()() diff --git a/modules/setting/log.go b/modules/setting/log.go index 0747ac4dac..a23662239e 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -56,41 +56,73 @@ func loadLogGlobalFrom(rootCfg ConfigProvider) { func prepareLoggerConfig(rootCfg ConfigProvider) { sec := rootCfg.Section("log") - if !sec.HasKey("logger.default.MODE") { - sec.Key("logger.default.MODE").MustString(",") + // Priority: `LOGGER_DEFAULT_MODE` -> `logger.default.MODE` + deprecatedSettingWarning(rootCfg, "log", "logger.default.MODE", "log", "LOGGER_DEFAULT_MODE") + hasNoValue := !sec.HasKey("LOGGER_DEFAULT_MODE") + if hasNoValue && sec.HasKey("logger.default.MODE") { + sec.Key("LOGGER_DEFAULT_MODE").SetValue(sec.Key("logger.default.MODE").String()) + hasNoValue = false + } + if hasNoValue { + sec.Key("LOGGER_DEFAULT_MODE").SetValue(",") // use default logger } - deprecatedSetting(rootCfg, "log", "ACCESS", "log", "logger.access.MODE", "1.21") - deprecatedSetting(rootCfg, "log", "ENABLE_ACCESS_LOG", "log", "logger.access.MODE", "1.21") - if val := sec.Key("ACCESS").String(); val != "" { - sec.Key("logger.access.MODE").MustString(val) + // Priority: `ENABLE_ACCESS_LOG` -> `LOGGER_ACCESS_MODE` -> `logger.access.MODE` -> `ACCESS` + deprecatedSettingWarning(rootCfg, "log", "ACCESS", "log", "LOGGER_ACCESS_MODE") + deprecatedSettingWarning(rootCfg, "log", "ENABLE_ACCESS_LOG", "log", "LOGGER_ACCESS_MODE") + deprecatedSettingWarning(rootCfg, "log", "logger.access.MODE", "log", "LOGGER_ACCESS_MODE") + hasNoValue = !sec.HasKey("LOGGER_ACCESS_MODE") + if hasNoValue && sec.HasKey("logger.access.MODE") { + sec.Key("LOGGER_ACCESS_MODE").SetValue(sec.Key("logger.access.MODE").String()) + hasNoValue = false + } + if val := sec.Key("ACCESS").String(); hasNoValue && val != "" { + sec.Key("LOGGER_ACCESS_MODE").SetValue(val) } if sec.HasKey("ENABLE_ACCESS_LOG") && !sec.Key("ENABLE_ACCESS_LOG").MustBool() { - sec.Key("logger.access.MODE").SetValue("") + sec.Key("LOGGER_ACCESS_MODE").SetValue("") } - deprecatedSetting(rootCfg, "log", "ROUTER", "log", "logger.router.MODE", "1.21") - deprecatedSetting(rootCfg, "log", "DISABLE_ROUTER_LOG", "log", "logger.router.MODE", "1.21") - if val := sec.Key("ROUTER").String(); val != "" { - sec.Key("logger.router.MODE").MustString(val) + // Priority: `DISABLE_ROUTER_LOG` -> `LOGGER_ROUTER_MODE` -> `logger.router.MODE` -> `ROUTER` + deprecatedSettingWarning(rootCfg, "log", "ROUTER", "log", "LOGGER_ROUTER_MODE") + deprecatedSettingWarning(rootCfg, "log", "DISABLE_ROUTER_LOG", "log", "LOGGER_ROUTER_MODE") + deprecatedSettingWarning(rootCfg, "log", "logger.router.MODE", "log", "LOGGER_ROUTER_MODE") + hasNoValue = !sec.HasKey("LOGGER_ROUTER_MODE") + if hasNoValue && sec.HasKey("logger.router.MODE") { + sec.Key("LOGGER_ROUTER_MODE").SetValue(sec.Key("logger.router.MODE").String()) + hasNoValue = false } - if !sec.HasKey("logger.router.MODE") { - sec.Key("logger.router.MODE").MustString(",") // use default logger + if val := sec.Key("ROUTER").String(); hasNoValue && val != "" { + sec.Key("LOGGER_ROUTER_MODE").SetValue(val) + hasNoValue = false } if sec.HasKey("DISABLE_ROUTER_LOG") && sec.Key("DISABLE_ROUTER_LOG").MustBool() { - sec.Key("logger.router.MODE").SetValue("") + sec.Key("LOGGER_ROUTER_MODE").SetValue("") + hasNoValue = false + } + if hasNoValue { + sec.Key("LOGGER_ROUTER_MODE").SetValue(",") // use default logger } - deprecatedSetting(rootCfg, "log", "XORM", "log", "logger.xorm.MODE", "1.21") - deprecatedSetting(rootCfg, "log", "ENABLE_XORM_LOG", "log", "logger.xorm.MODE", "1.21") - if val := sec.Key("XORM").String(); val != "" { - sec.Key("logger.xorm.MODE").MustString(val) + // Priority: `ENABLE_XORM_LOG` -> `LOGGER_XORM_MODE` -> `logger.xorm.MODE` -> `XORM` + deprecatedSettingWarning(rootCfg, "log", "XORM", "log", "LOGGER_XORM_MODE") + deprecatedSettingWarning(rootCfg, "log", "ENABLE_XORM_LOG", "log", "LOGGER_XORM_MODE") + deprecatedSettingWarning(rootCfg, "log", "logger.xorm.MODE", "log", "LOGGER_XORM_MODE") + hasNoValue = !sec.HasKey("LOGGER_XORM_MODE") + if hasNoValue && sec.HasKey("logger.xorm.MODE") { + sec.Key("LOGGER_XORM_MODE").SetValue(sec.Key("logger.xorm.MODE").String()) + hasNoValue = false } - if !sec.HasKey("logger.xorm.MODE") { - sec.Key("logger.xorm.MODE").MustString(",") // use default logger + if val := sec.Key("XORM").String(); hasNoValue && val != "" { + sec.Key("LOGGER_XORM_MODE").SetValue(val) + hasNoValue = false } if sec.HasKey("ENABLE_XORM_LOG") && !sec.Key("ENABLE_XORM_LOG").MustBool() { - sec.Key("logger.xorm.MODE").SetValue("") + sec.Key("LOGGER_XORM_MODE").SetValue("") + hasNoValue = false + } + if hasNoValue { + sec.Key("LOGGER_XORM_MODE").SetValue(",") // use default logger } } @@ -133,6 +165,7 @@ func loadLogModeByName(rootCfg ConfigProvider, loggerName, modeName string) (wri writerMode.StacktraceLevel = log.LevelFromString(ConfigInheritedKeyString(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel.String())) writerMode.Prefix = ConfigInheritedKeyString(sec, "PREFIX") writerMode.Expression = ConfigInheritedKeyString(sec, "EXPRESSION") + writerMode.Exclusion = ConfigInheritedKeyString(sec, "EXCLUSION") // flags are updated and set below switch writerType { @@ -216,14 +249,14 @@ func initManagedLoggers(manager *log.LoggerManager, cfg ConfigProvider) { func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, loggerName string) { sec := rootCfg.Section("log") - keyPrefix := "logger." + loggerName + key := "LOGGER_" + strings.ToUpper(loggerName) + "_MODE" - disabled := sec.HasKey(keyPrefix+".MODE") && sec.Key(keyPrefix+".MODE").String() == "" + disabled := sec.HasKey(key) && sec.Key(key).String() == "" if disabled { return } - modeVal := sec.Key(keyPrefix + ".MODE").String() + modeVal := sec.Key(key).String() if modeVal == "," { modeVal = Log.Mode } diff --git a/modules/setting/log_test.go b/modules/setting/log_test.go index eda6dc36af..a37c8f07a9 100644 --- a/modules/setting/log_test.go +++ b/modules/setting/log_test.go @@ -10,16 +10,13 @@ import ( "forgejo.org/modules/json" "forgejo.org/modules/log" + "forgejo.org/modules/test" "github.com/stretchr/testify/require" ) func initLoggersByConfig(t *testing.T, config string) (*log.LoggerManager, func()) { - oldLogConfig := Log - Log = LogGlobalConfig{} - defer func() { - Log = oldLogConfig - }() + defer test.MockVariableValue(&Log, LogGlobalConfig{})() cfg, err := NewConfigProviderFromData(config) require.NoError(t, err) @@ -29,6 +26,17 @@ func initLoggersByConfig(t *testing.T, config string) (*log.LoggerManager, func( return manager, manager.Close } +func initLoggerConfig(t *testing.T, config string) ConfigProvider { + defer test.MockVariableValue(&Log, LogGlobalConfig{})() + + cfg, err := NewConfigProviderFromData(config) + require.NoError(t, err) + + prepareLoggerConfig(cfg) + + return cfg +} + func toJSON(v any) string { b, _ := json.MarshalIndent(v, "", "\t") return string(b) @@ -44,6 +52,7 @@ func TestLogConfigDefault(t *testing.T) { "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -83,6 +92,7 @@ logger.xorm.MODE = "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -121,6 +131,7 @@ MODE = console "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -168,6 +179,7 @@ ACCESS = file "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -191,6 +203,7 @@ ACCESS = file "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "none", "Level": "info", "Prefix": "", @@ -238,8 +251,8 @@ ENABLE_ACCESS_LOG = false func TestLogConfigNewConfig(t *testing.T) { manager, managerClose := initLoggersByConfig(t, ` [log] -logger.access.MODE = console -logger.xorm.MODE = console, console-1 +LOGGER_ACCESS_MODE = console +LOGGER_XORM_MODE = console, console-1 [log.console] LEVEL = warn @@ -257,6 +270,7 @@ STDERR = true "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "warn", "Prefix": "", @@ -270,6 +284,7 @@ STDERR = true "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "error", "Prefix": "", @@ -287,6 +302,7 @@ STDERR = true "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "none", "Level": "warn", "Prefix": "", @@ -323,6 +339,7 @@ MODE = file LEVEL = error STACKTRACE_LEVEL = fatal EXPRESSION = filter +EXCLUSION = not FLAGS = medfile PREFIX = "[Prefix] " FILE_NAME = file-xxx.log @@ -341,6 +358,7 @@ COMPRESSION_LEVEL = 4 "BufferLen": 10, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -360,6 +378,7 @@ COMPRESSION_LEVEL = 4 "BufferLen": 10, "Colorize": false, "Expression": "filter", + "Exclusion": "not", "Flags": "medfile", "Level": "error", "Prefix": "[Prefix] ", @@ -384,3 +403,191 @@ COMPRESSION_LEVEL = 4 expected = strings.ReplaceAll(expected, "$FILENAME-1", tempPath("file-xxx.log")) require.JSONEq(t, expected, toJSON(dump)) } + +func TestLegacyLoggerMigrations(t *testing.T) { + type Cases = []struct { + name string + cfg string + exp string + } + + runCases := func(t *testing.T, key string, cases Cases) { + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + cfg := initLoggerConfig(t, c.cfg) + require.Equal(t, c.exp, cfg.Section("log").Key(key).String()) + }) + } + } + + t.Run("default", func(t *testing.T) { + runCases(t, "LOGGER_DEFAULT_MODE", Cases{ + { + "uses default value for default logger", + "", + ",", + }, + { + "uses logger.default.MODE for default logger", + `[log] +logger.default.MODE = file +`, + "file", + }, + }) + }) + + t.Run("access", func(t *testing.T) { + runCases(t, "LOGGER_ACCESS_MODE", Cases{ + { + "uses default value for access logger", + "", + "", + }, + { + "uses ACCESS for access logger", + `[log] +ACCESS = file +`, + "file", + }, + { + "ENABLE_ACCESS_LOG=true doesn't change access logger", + `[log] +ENABLE_ACCESS_LOG = true +logger.access.MODE = console +`, + "console", + }, + { + "ENABLE_ACCESS_LOG=false disables access logger", + `[log] +ENABLE_ACCESS_LOG = false +logger.access.MODE = console +`, + "", + }, + { + "logger.access.MODE has precedence over ACCESS for access logger", + `[log] +ACCESS = file +logger.access.MODE = console +`, + "console", + }, + { + "LOGGER_ACCESS_MODE has precedence over logger.access.MODE for access logger", + `[log] +LOGGER_ACCESS_MODE = file +logger.access.MODE = console +`, + "file", + }, + { + "ENABLE_ACCESS_LOG doesn't enable access logger", + `[log] +ENABLE_ACCESS_LOG = true +`, + "", // should be `,` + }, + }) + }) + + t.Run("router", func(t *testing.T) { + runCases(t, "LOGGER_ROUTER_MODE", Cases{ + { + "uses default value for router logger", + "", + ",", + }, + { + "uses ROUTER for router logger", + `[log] +ROUTER = file +`, + "file", + }, + { + "DISABLE_ROUTER_LOG=false doesn't change router logger", + `[log] +ROUTER = file +DISABLE_ROUTER_LOG = false +`, + "file", + }, + { + "DISABLE_ROUTER_LOG=true disables router logger", + `[log] +DISABLE_ROUTER_LOG = true +logger.router.MODE = console +`, + "", + }, + { + "logger.router.MODE as precedence over ROUTER for router logger", + `[log] +ROUTER = file +logger.router.MODE = console +`, + "console", + }, + { + "LOGGER_ROUTER_MODE has precedence over logger.router.MODE for router logger", + `[log] +LOGGER_ROUTER_MODE = file +logger.router.MODE = console +`, + "file", + }, + }) + }) + + t.Run("xorm", func(t *testing.T) { + runCases(t, "LOGGER_XORM_MODE", Cases{ + { + "uses default value for xorm logger", + "", + ",", + }, + { + "uses XORM for xorm logger", + `[log] +XORM = file +`, + "file", + }, + { + "ENABLE_XORM_LOG=true doesn't change xorm logger", + `[log] +ENABLE_XORM_LOG = true +logger.xorm.MODE = console +`, + "console", + }, + { + "ENABLE_XORM_LOG=false disables xorm logger", + `[log] +ENABLE_XORM_LOG = false +logger.xorm.MODE = console +`, + "", + }, + { + "logger.xorm.MODE has precedence over XORM for xorm logger", + `[log] +XORM = file +logger.xorm.MODE = console +`, + "console", + }, + { + "LOGGER_XORM_MODE has precedence over logger.xorm.MODE for xorm logger", + `[log] +LOGGER_XORM_MODE = file +logger.xorm.MODE = console +`, + "file", + }, + }) + }) +} diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 9c004c6ce0..b43484a90f 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -147,6 +147,10 @@ func loadMailerFrom(rootCfg ConfigProvider) { if sec.HasKey("PASSWORD") && !sec.HasKey("PASSWD") { sec.Key("PASSWD").SetValue(sec.Key("PASSWORD").String()) } + if sec.HasKey("PASSWORD_URI") && !sec.HasKey("PASSWD_URI") { + sec.Key("PASSWD_URI").SetValue(sec.Key("PASSWORD_URI").String()) + } + sec.Key("PASSWD").SetValue(loadSecret(sec, "PASSWD_URI", "PASSWD")) // Set default values & validate sec.Key("NAME").MustString(AppName) diff --git a/modules/setting/mailer_test.go b/modules/setting/mailer_test.go index 4523cc91dd..47eaf3ffbb 100644 --- a/modules/setting/mailer_test.go +++ b/modules/setting/mailer_test.go @@ -4,6 +4,8 @@ package setting import ( + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -52,6 +54,24 @@ func Test_loadMailerFrom(t *testing.T) { assert.Equal(t, "y0u'll n3v3r gUess th1S!!1", MailService.Passwd) }) + t.Run("Secrets", func(t *testing.T) { + uri := filepath.Join(t.TempDir(), "mailer_passwd") + + if err := os.WriteFile(uri, []byte("th1S gUess n3v3r y0u'll!!1"), 0o644); err != nil { + t.Fatal(err) + } + + cfg, _ := NewConfigProviderFromData("") + sec := cfg.Section("mailer") + sec.NewKey("ENABLED", "true") + sec.NewKey("PASSWD_URI", "file:"+uri) + + MailService.Passwd = "" + loadMailerFrom(cfg) + + assert.Equal(t, "th1S gUess n3v3r y0u'll!!1", MailService.Passwd) + }) + t.Run("sendmail argument sanitization", func(t *testing.T) { cfg, _ := NewConfigProviderFromData("") sec := cfg.Section("mailer") diff --git a/modules/setting/moderation.go b/modules/setting/moderation.go index 5f35a284d6..799efed761 100644 --- a/modules/setting/moderation.go +++ b/modules/setting/moderation.go @@ -3,13 +3,28 @@ package setting +import ( + "fmt" + "time" +) + // Moderation settings var Moderation = struct { - Enabled bool `ini:"ENABLED"` + Enabled bool `ini:"ENABLED"` + KeepResolvedReportsFor time.Duration `ini:"KEEP_RESOLVED_REPORTS_FOR"` }{ Enabled: false, } -func loadModerationFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "moderation", &Moderation) +func loadModerationFrom(rootCfg ConfigProvider) error { + sec := rootCfg.Section("moderation") + err := sec.MapTo(&Moderation) + if err != nil { + return fmt.Errorf("failed to map Moderation settings: %v", err) + } + + // keep reports for one week by default. Since time.Duration stops at the unit of an hour + // we are using the value of 24 (hours) * 7 (days) which gives us the value of 168 + Moderation.KeepResolvedReportsFor = sec.Key("KEEP_RESOLVED_REPORTS_FOR").MustDuration(168 * time.Hour) + return nil } diff --git a/modules/setting/security.go b/modules/setting/security.go index c38d8dae79..c591a7c90a 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -20,6 +20,7 @@ var ( SecretKey string InternalToken string // internal access token LogInRememberDays int + GlobalTwoFactorRequirement TwoFactorRequirementType CookieRememberName string ReverseProxyAuthUser string ReverseProxyAuthEmail string @@ -35,6 +36,7 @@ var ( PasswordHashAlgo string PasswordCheckPwn bool SuccessfulTokensCacheSize int + DisableQueryAuthToken bool CSRFCookieName = "_csrf" CSRFCookieHTTPOnly = true ) @@ -112,6 +114,8 @@ func loadSecurityFrom(rootCfg ConfigProvider) { } keying.Init([]byte(SecretKey)) + GlobalTwoFactorRequirement = NewTwoFactorRequirementType(sec.Key("GLOBAL_TWO_FACTOR_REQUIREMENT").String()) + CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible") ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER") @@ -159,4 +163,50 @@ func loadSecurityFrom(rootCfg ConfigProvider) { PasswordComplexity = append(PasswordComplexity, name) } } + + sectionHasDisableQueryAuthToken := sec.HasKey("DISABLE_QUERY_AUTH_TOKEN") + + // TODO: default value should be true in future releases + DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false) + + // warn if the setting is set to false explicitly + if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken { + log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will be removed in Forgejo v13.0.0.") + } +} + +type TwoFactorRequirementType string + +// llu:TrKeysSuffix admin.config.global_2fa_requirement. +const ( + NoneTwoFactorRequirement TwoFactorRequirementType = "none" + AllTwoFactorRequirement TwoFactorRequirementType = "all" + AdminTwoFactorRequirement TwoFactorRequirementType = "admin" +) + +func NewTwoFactorRequirementType(twoFactorRequirement string) TwoFactorRequirementType { + switch twoFactorRequirement { + case AllTwoFactorRequirement.String(): + return AllTwoFactorRequirement + case AdminTwoFactorRequirement.String(): + return AdminTwoFactorRequirement + default: + return NoneTwoFactorRequirement + } +} + +func (r TwoFactorRequirementType) String() string { + return string(r) +} + +func (r TwoFactorRequirementType) IsNone() bool { + return r == NoneTwoFactorRequirement +} + +func (r TwoFactorRequirementType) IsAll() bool { + return r == AllTwoFactorRequirement +} + +func (r TwoFactorRequirementType) IsAdmin() bool { + return r == AdminTwoFactorRequirement } diff --git a/modules/setting/server.go b/modules/setting/server.go index bff51f787d..3ff91d2cde 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -16,6 +16,8 @@ import ( "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/setting.go b/modules/setting/setting.go index 75c24580b2..9644d9b83b 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -140,6 +140,10 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error { if err := loadActionsFrom(cfg); err != nil { return err } + if err := loadModerationFrom(cfg); err != nil { + return err + } + loadUIFrom(cfg) loadAdminFrom(cfg) loadAPIFrom(cfg) @@ -221,7 +225,6 @@ func LoadSettings() { loadProjectFrom(CfgProvider) loadMimeTypeMapFrom(CfgProvider) loadF3From(CfgProvider) - loadModerationFrom(CfgProvider) } // LoadSettingsForInstall initializes the settings for install diff --git a/modules/storage/minio.go b/modules/storage/minio.go index bf51a1642a..8d4f9d6627 100644 --- a/modules/storage/minio.go +++ b/modules/storage/minio.go @@ -76,8 +76,13 @@ var getBucketVersioning = func(ctx context.Context, minioClient *minio.Client, b return err } +var initializationTimeout = 30 * time.Second + // NewMinioStorage returns a minio storage func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error) { + initCtx, cancel := context.WithTimeout(ctx, initializationTimeout) + defer cancel() + config := cfg.MinioConfig if config.ChecksumAlgorithm != "" && config.ChecksumAlgorithm != "default" && config.ChecksumAlgorithm != "md5" { return nil, fmt.Errorf("invalid minio checksum algorithm: %s", config.ChecksumAlgorithm) @@ -112,7 +117,7 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, // Otherwise even if the request itself fails (403, 404, etc), the code should still continue because the parameters seem "good" enough. // Keep in mind that GetBucketVersioning requires "owner" to really succeed, so it can't be used to check the existence. // Not using "BucketExists (HeadBucket)" because it doesn't include detailed failure reasons. - err = getBucketVersioning(ctx, minioClient, config.Bucket) + err = getBucketVersioning(initCtx, minioClient, config.Bucket) if err != nil { errResp, ok := err.(minio.ErrorResponse) if !ok { @@ -125,13 +130,13 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, } // Check to see if we already own this bucket - exists, err := minioClient.BucketExists(ctx, config.Bucket) + exists, err := minioClient.BucketExists(initCtx, config.Bucket) if err != nil { return nil, convertMinioErr(err) } if !exists { - if err := minioClient.MakeBucket(ctx, config.Bucket, minio.MakeBucketOptions{ + if err := minioClient.MakeBucket(initCtx, config.Bucket, minio.MakeBucketOptions{ Region: config.Location, }); err != nil { return nil, convertMinioErr(err) diff --git a/modules/storage/minio_test.go b/modules/storage/minio_test.go index ec1b2fc77a..18fe91edfb 100644 --- a/modules/storage/minio_test.go +++ b/modules/storage/minio_test.go @@ -9,8 +9,10 @@ import ( "net/http/httptest" "os" "testing" + "time" "forgejo.org/modules/setting" + "forgejo.org/modules/test" "github.com/minio/minio-go/v7" "github.com/stretchr/testify/assert" @@ -217,3 +219,41 @@ func TestMinioCredentials(t *testing.T) { }) }) } + +func TestNewMinioStorageInitializationTimeout(t *testing.T) { + defer test.MockVariableValue(&getBucketVersioning, func(ctx context.Context, minioClient *minio.Client, bucket string) error { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(1 * time.Millisecond): + return minio.ErrorResponse{ + StatusCode: http.StatusBadRequest, + Code: "TestError", + Message: "Mocked error for testing", + } + } + })() + + settings := &setting.Storage{ + MinioConfig: setting.MinioStorageConfig{ + Endpoint: "localhost", + AccessKeyID: "123456", + SecretAccessKey: "12345678", + Bucket: "bucket", + Location: "us-east-1", + }, + } + + // Verify that we reach `getBucketVersioning` and return the error from our mock. + storage, err := NewMinioStorage(t.Context(), settings) + require.ErrorContains(t, err, "Mocked error for testing") + assert.Nil(t, storage) + + defer test.MockVariableValue(&initializationTimeout, 1*time.Nanosecond)() + + // Now that the timeout is super low, verify that we get a context deadline exceeded error from our mock. + storage, err = NewMinioStorage(t.Context(), settings) + require.Error(t, err) + require.ErrorIs(t, err, context.DeadlineExceeded, "err must be a context deadline exceeded error, but was %v", err) + assert.Nil(t, storage) +} diff --git a/modules/storage/storage_test.go b/modules/storage/storage_test.go index af3dd9520e..76589d941a 100644 --- a/modules/storage/storage_test.go +++ b/modules/storage/storage_test.go @@ -5,6 +5,7 @@ package storage import ( "bytes" + "io" "testing" "forgejo.org/modules/setting" @@ -13,22 +14,39 @@ import ( "github.com/stretchr/testify/require" ) +type spyCloser struct { + io.Reader + closed int +} + +func (s *spyCloser) Close() error { + s.closed++ + return nil +} + +var _ io.ReadCloser = &spyCloser{} + func testStorageIterator(t *testing.T, typStr Type, cfg *setting.Storage) { l, err := NewStorage(typStr, cfg) require.NoError(t, err) - testFiles := [][]string{ - {"a/1.txt", "a1"}, - {"/a/1.txt", "aa1"}, // same as above, but with leading slash that will be trim - {"ab/1.txt", "ab1"}, - {"b/1.txt", "b1"}, - {"b/2.txt", "b2"}, - {"b/3.txt", "b3"}, - {"b/x 4.txt", "bx4"}, + testFiles := []struct { + path, content string + size int64 + }{ + {"a/1.txt", "a1", -1}, + {"/a/1.txt", "aa1", -1}, // same as above, but with leading slash that will be trim + {"ab/1.txt", "ab1", 3}, + {"b/1.txt", "b1", 2}, // minio closes when the size is set + {"b/2.txt", "b2", -1}, + {"b/3.txt", "b3", -1}, + {"b/x 4.txt", "bx4", -1}, } for _, f := range testFiles { - _, err = l.Save(f[0], bytes.NewBufferString(f[1]), -1) + sc := &spyCloser{bytes.NewBufferString(f.content), 0} + _, err = l.Save(f.path, sc, f.size) require.NoError(t, err) + assert.Equal(t, 0, sc.closed) } expectedList := map[string][]string{ diff --git a/modules/structs/activitypub.go b/modules/structs/activitypub.go index 117eb0bed2..0cc257ff95 100644 --- a/modules/structs/activitypub.go +++ b/modules/structs/activitypub.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 structs @@ -7,3 +8,15 @@ package structs type ActivityPub struct { Context string `json:"@context"` } + +type APRemoteFollowOption struct { + Target string `json:"target"` +} + +type APPersonFollowItem struct { + ActorID string `json:"actor_id"` + Note string `json:"note"` + + OriginalURL string `json:"original_url"` + OriginalItem string `json:"original_item"` +} diff --git a/modules/structs/mirror.go b/modules/structs/mirror.go index 1b6566803a..4909ae20ca 100644 --- a/modules/structs/mirror.go +++ b/modules/structs/mirror.go @@ -13,6 +13,7 @@ type CreatePushMirrorOption struct { Interval string `json:"interval"` SyncOnCommit bool `json:"sync_on_commit"` UseSSH bool `json:"use_ssh"` + BranchFilter string `json:"branch_filter"` } // PushMirror represents information of a push mirror @@ -29,4 +30,6 @@ type PushMirror struct { Interval string `json:"interval"` SyncOnCommit bool `json:"sync_on_commit"` PublicKey string `json:"public_key"` + + BranchFilter string `json:"branch_filter"` } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index c9cd729cf3..01cbf26f61 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -327,6 +327,7 @@ const ( GitBucketService // 7 gitbucket service CodebaseService // 8 codebase service ForgejoService // 9 forgejo service + PagureService // 10 pagure service ) // Name represents the service type's name @@ -354,6 +355,8 @@ func (gt GitServiceType) Title() string { return "Codebase" case ForgejoService: return "Forgejo" + case PagureService: + return "Pagure" case PlainGitService: return "Git" } @@ -412,6 +415,7 @@ var SupportedFullGitService = []GitServiceType{ OneDevService, GitBucketService, CodebaseService, + PagureService, } // RepoTransfer represents a pending repo transfer diff --git a/modules/templates/context.go b/modules/templates/context.go new file mode 100644 index 0000000000..d2b896391b --- /dev/null +++ b/modules/templates/context.go @@ -0,0 +1,23 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package templates + +import ( + "context" + + "forgejo.org/modules/translation" +) + +type Context struct { + context.Context + Locale translation.Locale + AvatarUtils *AvatarUtils + Data map[string]any +} + +var _ context.Context = Context{} + +func NewContext(ctx context.Context) *Context { + return &Context{Context: ctx} +} diff --git a/modules/templates/context_test.go b/modules/templates/context_test.go new file mode 100644 index 0000000000..d854fbf0ff --- /dev/null +++ b/modules/templates/context_test.go @@ -0,0 +1,18 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later +package templates + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestContext(t *testing.T) { + type ctxKey struct{} + + // Test that the original context is used for its context functions. + ctx := NewContext(context.WithValue(t.Context(), ctxKey{}, "there")) + assert.Equal(t, "there", ctx.Value(ctxKey{})) +} diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 02b175e6f6..2e33e51e60 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -6,6 +6,8 @@ package templates import ( + "bytes" + "context" "fmt" "html" "html/template" @@ -29,6 +31,23 @@ func NewFuncMap() template.FuncMap { return map[string]any{ "ctx": func() any { return nil }, // template context function + "ExecuteTemplate": func(ctx context.Context, tmplName string, args any) template.HTML { + h := HTMLRenderer() + tmpl, err := h.TemplateLookup(tmplName, ctx) + if err != nil { + panic("Template not found: " + tmplName) + } + + buf := bytes.Buffer{} + if err := tmpl.Execute(&buf, args); err != nil { + panic("Error while executing template") + } + + // We can safely return this as `template.HTML` as html/template will + // already make sure it's sanitized. + return template.HTML(buf.String()) + }, + "DumpVar": dumpVar, // ----------------------------------------------------------------- @@ -188,6 +207,7 @@ func NewFuncMap() template.FuncMap { "RenderMarkdownToHtml": RenderMarkdownToHtml, "RenderLabel": RenderLabel, "RenderLabels": RenderLabels, + "RenderUser": RenderUser, "RenderReviewRequest": RenderReviewRequest, // ----------------------------------------------------------------- diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index a4d7a82eea..bec8d5f5e3 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -7,6 +7,7 @@ import ( "context" "encoding/hex" "fmt" + "html" "html/template" "math" "net/url" @@ -15,18 +16,18 @@ import ( "unicode" issues_model "forgejo.org/models/issues" + user_model "forgejo.org/models/user" "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. func RenderCommitMessage(ctx context.Context, msg string, metas map[string]string) template.HTML { - cleanMsg := template.HTMLEscapeString(msg) + cleanMsg := html.EscapeString(msg) // we can safely assume that it will not return any error, since there // shouldn't be any special HTML. fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{ @@ -63,7 +64,7 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlDefault string, Ctx: ctx, DefaultLink: urlDefault, Metas: metas, - }, template.HTMLEscapeString(msgLine)) + }, html.EscapeString(msgLine)) if err != nil { log.Error("RenderCommitMessageSubject: %v", err) return template.HTML("") @@ -88,7 +89,7 @@ func RenderCommitBody(ctx context.Context, msg string, metas map[string]string) renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{ Ctx: ctx, Metas: metas, - }, template.HTMLEscapeString(msgLine)) + }, html.EscapeString(msgLine)) if err != nil { log.Error("RenderCommitMessage: %v", err) return "" @@ -122,7 +123,7 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{ Ctx: ctx, Metas: metas, - }, template.HTMLEscapeString(text)) + }, html.EscapeString(text)) if err != nil { log.Error("RenderIssueTitle: %v", err) return template.HTML("") @@ -132,7 +133,7 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) // RenderRefIssueTitle renders referenced issue/pull title with defined post processors func RenderRefIssueTitle(ctx context.Context, text string) template.HTML { - renderedText, err := markup.RenderRefIssueTitle(&markup.RenderContext{Ctx: ctx}, template.HTMLEscapeString(text)) + renderedText, err := markup.RenderRefIssueTitle(&markup.RenderContext{Ctx: ctx}, html.EscapeString(text)) if err != nil { log.Error("RenderRefIssueTitle: %v", err) return "" @@ -143,18 +144,18 @@ func RenderRefIssueTitle(ctx context.Context, text string) template.HTML { // RenderLabel renders a label // locale is needed due to an import cycle with our context providing the `Tr` function -func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML { +func RenderLabel(ctx *Context, label *issues_model.Label) template.HTML { var ( archivedCSSClass string textColor = util.ContrastColor(label.Color) labelScope = label.ExclusiveScope() ) - description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) + description := emoji.ReplaceAliases(html.EscapeString(label.Description)) if label.IsArchived() { archivedCSSClass = "archived-label" - description = locale.TrString("repo.issues.archived_label_description", description) + description = ctx.Locale.TrString("repo.issues.archived_label_description", description) } if labelScope == "" { @@ -212,7 +213,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m // RenderEmoji renders html text with emoji post processors func RenderEmoji(ctx context.Context, text string) template.HTML { renderedText, err := markup.RenderEmoji(&markup.RenderContext{Ctx: ctx}, - template.HTMLEscapeString(text)) + html.EscapeString(text)) if err != nil { log.Error("RenderEmoji: %v", err) return template.HTML("") @@ -244,7 +245,7 @@ func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //n return output } -func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string, isPull bool) template.HTML { +func RenderLabels(ctx *Context, labels []*issues_model.Label, repoLink string, isPull bool) template.HTML { htmlCode := `` for _, label := range labels { // Protect against nil value in labels - shouldn't happen but would cause a panic if so @@ -257,16 +258,34 @@ func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issu issuesOrPull = "pulls" } htmlCode += fmt.Sprintf("%s ", - repoLink, issuesOrPull, label.ID, RenderLabel(ctx, locale, label)) + repoLink, issuesOrPull, label.ID, RenderLabel(ctx, label)) } htmlCode += "" return template.HTML(htmlCode) } -func RenderReviewRequest(users []issues_model.RequestReviewTarget) template.HTML { +func RenderUser(ctx context.Context, user user_model.User) template.HTML { + if user.ID > 0 { + return template.HTML(fmt.Sprintf( + "%s", + user.HomeLink(), html.EscapeString(user.GetDisplayName()))) + } + return template.HTML(fmt.Sprintf("%s", + html.EscapeString(user.GetDisplayName()))) +} + +func RenderReviewRequest(ctx context.Context, users []issues_model.RequestReviewTarget) template.HTML { usernames := make([]string, 0, len(users)) for _, user := range users { - usernames = append(usernames, template.HTMLEscapeString(user.Name())) + if user.ID() > 0 { + usernames = append(usernames, fmt.Sprintf( + "%s", + user.Link(ctx), html.EscapeString(user.Name()))) + } else { + usernames = append(usernames, fmt.Sprintf( + "%s", + html.EscapeString(user.Name()))) + } } htmlCode := `` diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 62e063213c..3cfd572491 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -10,7 +10,11 @@ import ( "forgejo.org/models/db" issues_model "forgejo.org/models/issues" + org_model "forgejo.org/models/organization" "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" "forgejo.org/modules/translation" "github.com/stretchr/testify/assert" @@ -215,9 +219,70 @@ func TestRenderLabels(t *testing.T) { tr := &translation.MockLocale{} label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) + labelScoped := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 7}) + labelMalicious := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 11}) + labelArchived := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 12}) - assert.Contains(t, RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", false), - "user2/repo1/issues?labels=1") - assert.Contains(t, RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", true), - "user2/repo1/pulls?labels=1") + ctx := NewContext(t.Context()) + ctx.Locale = tr + + rendered := RenderLabels(ctx, []*issues_model.Label{label}, "user2/repo1", false) + assert.Contains(t, rendered, "user2/repo1/issues?labels=1") + assert.Contains(t, rendered, ">label1<") + assert.Contains(t, rendered, "title='First label'") + rendered = RenderLabels(ctx, []*issues_model.Label{label}, "user2/repo1", true) + assert.Contains(t, rendered, "user2/repo1/pulls?labels=1") + assert.Contains(t, rendered, ">label1<") + rendered = RenderLabels(ctx, []*issues_model.Label{labelScoped}, "user2/repo1", false) + assert.Contains(t, rendered, "user2/repo1/issues?labels=7") + assert.Contains(t, rendered, ">scope<") + assert.Contains(t, rendered, ">label1<") + rendered = RenderLabels(ctx, []*issues_model.Label{labelMalicious}, "user2/repo1", false) + assert.Contains(t, rendered, "user2/repo1/issues?labels=11") + assert.Contains(t, rendered, "> <script>malicious</script> <") + assert.Contains(t, rendered, ">'?&<") + assert.Contains(t, rendered, "title='Malicious label ' <script>malicious</script>'") + rendered = RenderLabels(ctx, []*issues_model.Label{labelArchived}, "user2/repo1", false) + assert.Contains(t, rendered, "user2/repo1/issues?labels=12") + assert.Contains(t, rendered, ">archived label<><") + assert.Contains(t, rendered, "title='repo.issues.archived_label_description'") +} + +func TestRenderUser(t *testing.T) { + unittest.PrepareTestEnv(t) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) + ghost := user_model.NewGhostUser() + + assert.Contains(t, RenderUser(db.DefaultContext, *user), + "user2") + assert.Contains(t, RenderUser(db.DefaultContext, *org), + "org3") + assert.Contains(t, RenderUser(db.DefaultContext, *ghost), + "Ghost") + + defer test.MockVariableValue(&setting.UI.DefaultShowFullName, true)() + assert.Contains(t, RenderUser(db.DefaultContext, *user), + "< U<se>r Tw<o > ><") + assert.Contains(t, RenderUser(db.DefaultContext, *org), + "<<<< >> >> > >> > >>> >>") + assert.Contains(t, RenderUser(db.DefaultContext, *ghost), + "Ghost") +} + +func TestRenderReviewRequest(t *testing.T) { + unittest.PrepareTestEnv(t) + + target1 := issues_model.RequestReviewTarget{User: &user_model.User{ID: 1, Name: "user1", FullName: "User "}} + target2 := issues_model.RequestReviewTarget{Team: &org_model.Team{ID: 2, Name: "Team2", OrgID: 3}} + target3 := issues_model.RequestReviewTarget{Team: org_model.NewGhostTeam()} + assert.Contains(t, RenderReviewRequest(db.DefaultContext, []issues_model.RequestReviewTarget{target1, target2, target3}), + "user1, "+ + "Team2, "+ + "Ghost team") + + defer test.MockVariableValue(&setting.UI.DefaultShowFullName, true)() + assert.Contains(t, RenderReviewRequest(db.DefaultContext, []issues_model.RequestReviewTarget{target1}), + "User <One>") } diff --git a/modules/test/distant_federation_server_mock.go b/modules/test/distant_federation_server_mock.go index 9bd908e2b9..ea8a69e9b4 100644 --- a/modules/test/distant_federation_server_mock.go +++ b/modules/test/distant_federation_server_mock.go @@ -10,56 +10,79 @@ import ( "net/http/httptest" "strings" "testing" + + "forgejo.org/modules/util" ) type FederationServerMockPerson struct { - ID int64 - Name string - PubKey string + ID int64 + Name string + PubKey string + PrivKey string } type FederationServerMockRepository struct { ID int64 } +type ApActorMock struct { + PrivKey string + PubKey string +} type FederationServerMock struct { + ApActor ApActorMock Persons []FederationServerMockPerson Repositories []FederationServerMockRepository LastPost string } func NewFederationServerMockPerson(id int64, name string) FederationServerMockPerson { + priv, pub, _ := util.GenerateKeyPair(3072) return FederationServerMockPerson{ - ID: id, - Name: name, - PubKey: `"-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA18H5s7N6ItZUAh9tneII\nIuZdTTa3cZlLa/9ejWAHTkcp3WLW+/zbsumlMrWYfBy2/yTm56qasWt38iY4D6ul\n` + - `CPiwhAqX3REvVq8tM79a2CEqZn9ka6vuXoDgBg/sBf/BUWqf7orkjUXwk/U0Egjf\nk5jcurF4vqf1u+rlAHH37dvSBaDjNj6Qnj4OP12bjfaY/yvs7+jue/eNXFHjzN4E\n` + - `T2H4B/yeKTJ4UuAwTlLaNbZJul2baLlHelJPAsxiYaziVuV5P+IGWckY6RSerRaZ\nAkc4mmGGtjAyfN9aewe+lNVfwS7ElFx546PlLgdQgjmeSwLX8FWxbPE5A/PmaXCs\n` + - `nx+nou+3dD7NluULLtdd7K+2x02trObKXCAzmi5/Dc+yKTzpFqEz+hLNCz7TImP/\ncK//NV9Q+X67J9O27baH9R9ZF4zMw8rv2Pg0WLSw1z7lLXwlgIsDapeMCsrxkVO4\n` + - `LXX5AQ1xQNtlssnVoUBqBrvZsX2jUUKUocvZqMGuE4hfAgMBAAE=\n-----END PUBLIC KEY-----\n"`, + ID: id, + Name: name, + PubKey: pub, + PrivKey: priv, } } +func (p *FederationServerMockPerson) KeyID(host string) string { + return fmt.Sprintf("%[1]v/api/v1/activitypub/user-id/%[2]v#main-key", host, p.ID) +} + func NewFederationServerMockRepository(id int64) FederationServerMockRepository { return FederationServerMockRepository{ ID: id, } } +func NewApActorMock() ApActorMock { + priv, pub, _ := util.GenerateKeyPair(1024) + return ApActorMock{ + PrivKey: priv, + PubKey: pub, + } +} + +func (u *ApActorMock) KeyID(host string) string { + return fmt.Sprintf("%[1]v/api/v1/activitypub/actor#main-key", host) +} + func (p FederationServerMockPerson) marshal(host string) string { return fmt.Sprintf(`{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],`+ - `"id":"http://%[1]v/api/activitypub/user-id/%[2]v",`+ + `"id":"http://%[1]v/api/v1/activitypub/user-id/%[2]v",`+ `"type":"Person",`+ `"icon":{"type":"Image","mediaType":"image/png","url":"http://%[1]v/avatars/1bb05d9a5f6675ed0272af9ea193063c"},`+ `"url":"http://%[1]v/%[2]v",`+ - `"inbox":"http://%[1]v/api/activitypub/user-id/%[2]v/inbox",`+ - `"outbox":"http://%[1]v/api/activitypub/user-id/%[2]v/outbox",`+ + `"inbox":"http://%[1]v/api/v1/activitypub/user-id/%[2]v/inbox",`+ + `"outbox":"http://%[1]v/api/v1/activitypub/user-id/%[2]v/outbox",`+ `"preferredUsername":"%[3]v",`+ - `"publicKey":{"id":"http://%[1]v/api/activitypub/user-id/%[2]v#main-key",`+ - `"owner":"http://%[1]v/api/activitypub/user-id/%[2]v",`+ - `"publicKeyPem":%[4]v}}`, host, p.ID, p.Name, p.PubKey) + `"publicKey":{"id":"http://%[1]v/api/v1/activitypub/user-id/%[2]v#main-key",`+ + `"owner":"http://%[1]v/api/v1/activitypub/user-id/%[2]v",`+ + `"publicKeyPem":%[4]q}}`, host, p.ID, p.Name, p.PubKey) } func NewFederationServerMock() *FederationServerMock { return &FederationServerMock{ + ApActor: NewApActorMock(), Persons: []FederationServerMockPerson{ NewFederationServerMockPerson(15, "stargoose1"), NewFederationServerMockPerson(30, "stargoose2"), @@ -71,8 +94,18 @@ func NewFederationServerMock() *FederationServerMock { } } +func (mock *FederationServerMock) recordLastPost(t *testing.T, req *http.Request) { + buf := new(strings.Builder) + _, err := io.Copy(buf, req.Body) + if err != nil { + t.Errorf("Error reading body: %q", err) + } + mock.LastPost = strings.ReplaceAll(buf.String(), req.Host, "DISTANT_FEDERATION_HOST") +} + func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server { federatedRoutes := http.NewServeMux() + federatedRoutes.HandleFunc("/.well-known/nodeinfo", func(res http.ResponseWriter, req *http.Request) { // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/.well-known/nodeinfo @@ -87,30 +120,28 @@ func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server { `"protocols":["activitypub"],"services":{"inbound":[],"outbound":["rss2.0"]},`+ `"openRegistrations":true,"usage":{"users":{"total":14,"activeHalfyear":2}},"metadata":{}}`) }) + for _, person := range mock.Persons { federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/user-id/%v", person.ID), func(res http.ResponseWriter, req *http.Request) { // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/2 fmt.Fprint(res, person.marshal(req.Host)) }) - } - for _, repository := range mock.Repositories { - federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox", repository.ID), + federatedRoutes.HandleFunc(fmt.Sprintf("POST /api/v1/activitypub/user-id/%v/inbox", person.ID), func(res http.ResponseWriter, req *http.Request) { - if req.Method != "POST" { - t.Errorf("POST expected at: %q", req.URL.EscapedPath()) - } - buf := new(strings.Builder) - _, err := io.Copy(buf, req.Body) - if err != nil { - t.Errorf("Error reading body: %q", err) - } - mock.LastPost = buf.String() + mock.recordLastPost(t, req) + }) + } + + for _, repository := range mock.Repositories { + federatedRoutes.HandleFunc(fmt.Sprintf("POST /api/v1/activitypub/repository-id/%v/inbox", repository.ID), + func(res http.ResponseWriter, req *http.Request) { + mock.recordLastPost(t, req) }) } federatedRoutes.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { - t.Errorf("Unhandled request: %q", req.URL.EscapedPath()) + t.Errorf("Unhandled %v request: %q", req.Method, req.URL.EscapedPath()) }) federatedSrv := httptest.NewServer(federatedRoutes) return federatedSrv diff --git a/modules/translation/plural_rules.go b/modules/translation/plural_rules.go index 59665da255..587ee48850 100644 --- a/modules/translation/plural_rules.go +++ b/modules/translation/plural_rules.go @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT // Some useful links: +// https://codeberg.org/forgejo/forgejo/src/branch/forgejo/web_src/js/webcomponents/relative-time.js // https://www.unicode.org/cldr/charts/46/supplemental/language_plural_rules.html // https://translate.codeberg.org/languages/$LANGUAGE_CODE/#information // https://github.com/WeblateOrg/language-data/blob/main/languages.csv @@ -16,7 +17,7 @@ import ( "forgejo.org/modules/translation/i18n" ) -// The constants refer to indices below in `PluralRules` and also in i18n.js, keep them in sync! +// The constants refer to indices below in `PluralRules` and also in web_src/js/webcomponents/relative-time.js, keep them in sync! const ( PluralRuleDefault = 0 PluralRuleBengali = 1 diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go index 262feb2b05..8cb1513a88 100644 --- a/modules/typesniffer/typesniffer.go +++ b/modules/typesniffer/typesniffer.go @@ -124,7 +124,7 @@ func (ct SniffedType) GetMimeType() string { } // DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty. -func DetectContentType(data []byte) SniffedType { +func DetectContentType(data []byte, filename string) SniffedType { if len(data) == 0 { return SniffedType{"text/unknown"} } @@ -176,6 +176,13 @@ func DetectContentType(data []byte) SniffedType { } } + if ct == "application/octet-stream" && + filename != "" && + !strings.HasSuffix(strings.ToUpper(filename), ".LCOM") && + bytes.Contains(data, []byte("(DEFINE-FILE-INFO ")) { + ct = "text/vnd.interlisp" + } + // GLTF is unsupported by http.DetectContentType // hexdump -n 4 -C glTF.glb if bytes.HasPrefix(data, []byte("glTF")) { @@ -186,7 +193,7 @@ func DetectContentType(data []byte) SniffedType { } // DetectContentTypeFromReader guesses the content type contained in the reader. -func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) { +func DetectContentTypeFromReader(r io.Reader, filename string) (SniffedType, error) { buf := make([]byte, sniffLen) n, err := util.ReadAtMost(r, buf) if err != nil { @@ -194,5 +201,5 @@ func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) { } buf = buf[:n] - return DetectContentType(buf), nil + return DetectContentType(buf, filename), nil } diff --git a/modules/typesniffer/typesniffer_test.go b/modules/typesniffer/typesniffer_test.go index 176d3658bb..d2b7ed4f21 100644 --- a/modules/typesniffer/typesniffer_test.go +++ b/modules/typesniffer/typesniffer_test.go @@ -16,63 +16,63 @@ import ( func TestDetectContentTypeLongerThanSniffLen(t *testing.T) { // Pre-condition: Shorter than sniffLen detects SVG. - assert.Equal(t, "image/svg+xml", DetectContentType([]byte(``)).contentType) + assert.Equal(t, "image/svg+xml", DetectContentType([]byte(``), "").contentType) // Longer than sniffLen detects something else. - assert.NotEqual(t, "image/svg+xml", DetectContentType([]byte(``)).contentType) + assert.NotEqual(t, "image/svg+xml", DetectContentType([]byte(``), "").contentType) } func TestIsTextFile(t *testing.T) { - assert.True(t, DetectContentType([]byte{}).IsText()) - assert.True(t, DetectContentType([]byte("lorem ipsum")).IsText()) + assert.True(t, DetectContentType([]byte{}, "").IsText()) + assert.True(t, DetectContentType([]byte("lorem ipsum"), "").IsText()) } func TestIsSvgImage(t *testing.T) { - assert.True(t, DetectContentType([]byte("")).IsSvgImage()) - assert.True(t, DetectContentType([]byte(" ")).IsSvgImage()) - assert.True(t, DetectContentType([]byte(``)).IsSvgImage()) - assert.True(t, DetectContentType([]byte(``)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(""), "").IsSvgImage()) + assert.True(t, DetectContentType([]byte(" "), "").IsSvgImage()) + assert.True(t, DetectContentType([]byte(``), "").IsSvgImage()) + assert.True(t, DetectContentType([]byte(``), "").IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) // the DetectContentType should work for incomplete data, because only beginning bytes are used for detection - assert.True(t, DetectContentType([]byte(`....`)).IsSvgImage()) + assert.True(t, DetectContentType([]byte(`....`), "").IsSvgImage()) - assert.False(t, DetectContentType([]byte{}).IsSvgImage()) - assert.False(t, DetectContentType([]byte("svg")).IsSvgImage()) - assert.False(t, DetectContentType([]byte("")).IsSvgImage()) - assert.False(t, DetectContentType([]byte("text")).IsSvgImage()) - assert.False(t, DetectContentType([]byte("")).IsSvgImage()) - assert.False(t, DetectContentType([]byte(``)).IsSvgImage()) + assert.False(t, DetectContentType([]byte{}, "").IsSvgImage()) + assert.False(t, DetectContentType([]byte("svg"), "").IsSvgImage()) + assert.False(t, DetectContentType([]byte(""), "").IsSvgImage()) + assert.False(t, DetectContentType([]byte("text"), "").IsSvgImage()) + assert.False(t, DetectContentType([]byte(""), "").IsSvgImage()) + assert.False(t, DetectContentType([]byte(``), "").IsSvgImage()) assert.False(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) assert.False(t, DetectContentType([]byte(` - `)).IsSvgImage()) + `), "").IsSvgImage()) assert.False(t, DetectContentType([]byte(` @@ -80,7 +80,7 @@ func TestIsSvgImage(t *testing.T) { -`)).IsSvgImage()) +`), "").IsSvgImage()) assert.False(t, DetectContentType([]byte(` -`)).IsSvgImage()) - assert.False(t, DetectContentType([]byte(``)).IsSvgImage()) - assert.False(t, DetectContentType([]byte(``)).IsSvgImage()) +`), "").IsSvgImage()) + assert.False(t, DetectContentType([]byte(``), "").IsSvgImage()) + assert.False(t, DetectContentType([]byte(``), "").IsSvgImage()) } func TestIsPDF(t *testing.T) { pdf, _ := base64.StdEncoding.DecodeString("JVBERi0xLjYKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nF3NPwsCMQwF8D2f4s2CNYk1baF0EHRwOwg4iJt/NsFb/PpevUE4Mjwe") - assert.True(t, DetectContentType(pdf).IsPDF()) - assert.False(t, DetectContentType([]byte("plain text")).IsPDF()) + assert.True(t, DetectContentType(pdf, "").IsPDF()) + assert.False(t, DetectContentType([]byte("plain text"), "").IsPDF()) } func TestIsVideo(t *testing.T) { mp4, _ := base64.StdEncoding.DecodeString("AAAAGGZ0eXBtcDQyAAAAAGlzb21tcDQyAAEI721vb3YAAABsbXZoZAAAAADaBlwX2gZcFwAAA+gA") - assert.True(t, DetectContentType(mp4).IsVideo()) - assert.False(t, DetectContentType([]byte("plain text")).IsVideo()) + assert.True(t, DetectContentType(mp4, "").IsVideo()) + assert.False(t, DetectContentType([]byte("plain text"), "").IsVideo()) } func TestIsAudio(t *testing.T) { mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") - assert.True(t, DetectContentType(mp3).IsAudio()) - assert.False(t, DetectContentType([]byte("plain text")).IsAudio()) + assert.True(t, DetectContentType(mp3, "").IsAudio()) + assert.False(t, DetectContentType([]byte("plain text"), "").IsAudio()) - assert.True(t, DetectContentType([]byte("ID3Toy\000")).IsAudio()) - assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ...")).IsText()) // test ID3 tag for plain text - assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2])).IsText()) // test ID3 tag with incomplete UTF8 char + assert.True(t, DetectContentType([]byte("ID3Toy\000"), "").IsAudio()) + assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."), "").IsText()) // test ID3 tag for plain text + 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()) + 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)) + st, err := DetectContentTypeFromReader(bytes.NewReader(mp3), "") require.NoError(t, err) assert.True(t, st.IsAudio()) } func TestDetectContentTypeOgg(t *testing.T) { oggAudio, _ := hex.DecodeString("4f67675300020000000000000000352f0000000000007dc39163011e01766f72626973000000000244ac0000000000000071020000000000b8014f6767530000") - st, err := DetectContentTypeFromReader(bytes.NewReader(oggAudio)) + st, err := DetectContentTypeFromReader(bytes.NewReader(oggAudio), "") require.NoError(t, err) assert.True(t, st.IsAudio()) oggVideo, _ := hex.DecodeString("4f676753000200000000000000007d9747ef000000009b59daf3012a807468656f7261030201001e00110001e000010e00020000001e00000001000001000001") - st, err = DetectContentTypeFromReader(bytes.NewReader(oggVideo)) + st, err = DetectContentTypeFromReader(bytes.NewReader(oggVideo), "") require.NoError(t, err) assert.True(t, st.IsVideo()) } @@ -148,7 +148,7 @@ func TestDetectContentTypeAvif(t *testing.T) { avifImage, err := hex.DecodeString("000000206674797061766966") require.NoError(t, err) - st, err := DetectContentTypeFromReader(bytes.NewReader(avifImage)) + st, err := DetectContentTypeFromReader(bytes.NewReader(avifImage), "") require.NoError(t, err) assert.True(t, st.IsImage()) @@ -158,10 +158,24 @@ func TestDetectContentTypeModelGLB(t *testing.T) { glb, err := hex.DecodeString("676c5446") require.NoError(t, err) - st, err := DetectContentTypeFromReader(bytes.NewReader(glb)) + 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()) } + +func TestDetectInterlisp(t *testing.T) { + interlisp, err := base64.StdEncoding.DecodeString("ICAKKERFRklORS1GSUxFLUlORk8gHlBBQ0tBR0UgIklOVEVSTElTUCIgHlJFQURUQUJMRSAiSU5URVJMSVNQIiAeQkFTRSAxMCkKCgYB") + require.NoError(t, err) + st, err := DetectContentTypeFromReader(bytes.NewReader(interlisp), "test") + require.NoError(t, err) + assert.True(t, st.IsText()) + st, err = DetectContentTypeFromReader(bytes.NewReader(interlisp), "") + require.NoError(t, err) + assert.False(t, st.IsText()) + st, err = DetectContentTypeFromReader(bytes.NewReader(interlisp), "test.lcom") + require.NoError(t, err) + assert.False(t, st.IsText()) +} diff --git a/modules/validation/validatable.go b/modules/validation/validatable.go index 4500f6e53d..7bcca03bf8 100644 --- a/modules/validation/validatable.go +++ b/modules/validation/validatable.go @@ -45,7 +45,7 @@ func IsValid(v Validateable) (bool, error) { func ValidateIDExists(value ap.Item, name string) []string { if value == nil { - return []string{fmt.Sprintf("%v should not be nil", name)} + return []string{fmt.Sprintf("Field %v must not be nil", name)} } return ValidateNotEmpty(value.GetID().String(), name) } @@ -76,12 +76,12 @@ func ValidateNotEmpty(value any, name string) []string { if isValid { return []string{} } - return []string{fmt.Sprintf("%v should not be empty", name)} + return []string{fmt.Sprintf("Value %v should not be empty", name)} } func ValidateMaxLen(value string, maxLen int, name string) []string { if utf8.RuneCountInString(value) > maxLen { - return []string{fmt.Sprintf("Value %v was longer than %v", name, maxLen)} + return []string{fmt.Sprintf("Value %v is longer than expected length %v", name, maxLen)} } return []string{} } diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index ca74a477ce..d41677d540 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -144,10 +144,9 @@ new_migrate.title = انتقال جديد new_org.title = منظمة جديدة new_repo.link = مستودع جديد new_migrate.link = انتقال جديد - -new_org.link = منظمة جديدة -test = اختبار copy_path = نسخ المسار +test = اختبار +new_org.link = منظمة جديدة error413 = لقد استنفدت حصتك. [install] @@ -261,12 +260,11 @@ default_allow_create_organization.description = السماح للمستخدمي password_algorithm = خوارزمية تجزئة كلمة المرور invalid_password_algorithm = خوارزمية بصمة كلمة المرور غير صالحة password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقوتها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. - -app_slogan = شعار المثيل app_slogan_helper = أدخل شعار المثيل الخاص بك هنا. اتركه فارغاً لتعطيله. -smtp_from_invalid = عنوان "،بريد الإرسال كـ" غير صالح +app_slogan = شعار المثيل allow_only_external_registration = السماح بالتسجيل عبر الخدمات الخارجية فقط config_location_hint = سيتم حفظ خيارات التهيئة هذه في: +smtp_from_invalid = عنوان "،بريد الإرسال كـ" غير صالح [editor] buttons.list.ordered.tooltip = أضف قائمة مرقمة @@ -283,18 +281,17 @@ buttons.mention.tooltip = اذكر مستخدمًا أو فريقًا buttons.italic.tooltip = أضف نصًا مائلًا buttons.link.tooltip = اضف رابط buttons.disable_monospace_font = عطّل الخط الثابت العرض - -buttons.indent.tooltip = تداخل العناصر بنفس المستوى buttons.unindent.tooltip = ‪عناصر غير متساوية من نفس المستوى -buttons.new_table.tooltip = إضافة جدول +buttons.indent.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 = الوصف +buttons.new_table.tooltip = إضافة جدول +link_modal.header = إضافة رابط link_modal.paste_reminder = تلميح: باستخدام عنوان URL في حافظتك، يمكنك اللصق مباشرةً في المحرر لإنشاء رابط. [aria] @@ -312,31 +309,31 @@ blocked_since = محظور منذ %s comment_type_group_milestone = الأهداف ui = السمة email_notifications.disable = عطّل إشعارات البريد الإلكتروني -passcode_invalid = رمز الدخول خطأ. حاول مرة أخرى. +passcode_invalid = رمز الدخول خطأ. حاول مجدداً. openid_deletion = أزل عنوان OpenID activate_email = أرسل التفعيل uploaded_avatar_not_a_image = الملف المرفوع ليس صورة. theme_update_error = السمة المختارة غير موجودة. -twofa_disabled = عُطِّل الاستيثاق الثنائي. +twofa_disabled = تم تعطيل المصادقة الثنائية. theme_desc = ستكون هذه السمة المبدئية لك عبر الموقع. new_password = كلمة المرور الجديدة -twofa_disable_desc = تعطيل الاستيثاق الثنائي سيجعل حسابك أقل أمانًا. أتريد الاستمرار؟ +twofa_disable_desc = تعطيل المصادقة الثنائية سيجعل حسابك أقل أماناً. الاستمرار؟ manage_themes = الموضوع الافتراضي -delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عنها بعد ذلك. -cancel = ألغ -repos_none = ليس لديك أي مستودع. +delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عن ذلك. +cancel = إلغاء +repos_none = لا تملك أية مستودع. twofa_desc = لحماية حسابك من سرقة كلمة المرور، يمكنك استخدام هاتف ذكي أو جهاز آخر لاستلام كلمة مرور مؤقتة ذات استخدام واحد ("TOTP"). -email_notifications.submit = اضبط تفضيلات البريد الإلكتروني +email_notifications.submit = ضبط تفضيلات البريد الإلكتروني update_user_avatar_success = حُدِّثت صورة المستخدم الرمزية. activations_pending = في انتظار التفعيل -language = اللغة +language = اللّغة primary = الأساسي update_avatar_success = حُدِّثت صورتك الرمزية. keep_email_private = أخفِ عنوان البريد delete_current_avatar = احذف الصورة الرمزية الحالية avatar = صورة رمزية email_preference_set_success = حُدِّثت تفضيلات البريد الإلكتروني. -scan_this_image = امسح هذه الصورة بتطبيق الاستيثاق الذي تستخدمه: +scan_this_image = امسح هذه الصورة بتطبيق المصادقة الذي تستخدمه: orgs_none = لست عضوًا في أي منظمة. delete_email = أزله theme_update_success = حُدِّثت السمة. @@ -363,14 +360,14 @@ openid_deletion_success = أزيل عنوان OpenID. add_email_success = أضيف عنوان البريد الجديد. enable_custom_avatar = استخدم صورة رمزية مخصصة update_avatar = حدّث الصورة الرمزية -twofa_disable = تعطيل الاستيثاق الثنائي +twofa_disable = تعطيل المصادقة الثنائية retype_new_password = تأكيد كلمة المرور الجديدة manage_emails = أدر عناوين البريد الإلكتروني then_enter_passcode = وأدخل رمز الدخول الظاهر في التطبيق: update_password = حدّث كلمة المرور continue = استمر emails = عناوين البريد الإلكتروني -confirm_delete_account = أكُد الحذف +confirm_delete_account = تأكيد الحذف change_password_success = حُدِّثت كلمة مرورك. سجّل الدخول بكلمة مرورك الجديدة من الآن فصاعدا. email_deletion_desc = سيُزال عنوان البريد هذا مع كل المعلومات المرتطبة به من حسابك. لكن ستبقى إيداعات Git المودعة به بلا تغيير. أتريد الاستمرار؟ or_enter_secret = أو أدخل السر: %s @@ -389,14 +386,14 @@ saved_successfully = حُدِّثت إعداداتك بنجاح. update_theme = حدِّث السمة access_token_deletion_confirm_action = احذف website = الموقع الإلكتروني -delete_token = احذف +delete_token = حذف hidden_comment_types.ref_tooltip = التعليقات التي تقول أن هذه المسألة قد أشير إليها في مسألة أخرى أو إيداع أو غير ذلك… update_language_success = تم تحديث اللغة. privacy = الخصوصية comment_type_group_label = التصنيفات account_link = الحسابات المرتبطة comment_type_group_assignee = المكلفون -update_language = حدِّث اللغة +update_language = تغيير اللغة organization = المنظمات update_language_not_found = اللغة "%s" غير متاحة. update_profile_success = تم تحديث ملفك الشخصي. @@ -424,7 +421,7 @@ can_write_info = كتابة delete = احذف الحساب oauth2_application_name = اسم التطبيق key_state_desc = هذا المفتاح أستُعمل خلال آخر 7 أيام -webauthn_delete_key = أزِل مفتاح الأمان +webauthn_delete_key = إزالة مفتاح الأمان valid_forever = صالح للأبد can_read_info = قراءة create_oauth2_application_button = أنشئ تطبيقا @@ -436,14 +433,14 @@ select_permissions = أختر التصاريح added_on = مُضاف في %s show_openid = أظهر على الملف الشخصي hide_openid = أخفي من الملف الشخصي -webauthn_delete_key_desc = إذا أزلت مفتاح الأمان، فلن تتمكن من تسجيل الدخول باستخدامه. اكمل؟ +webauthn_delete_key_desc = إذا أزلت مفتاح الأمان، فلن تتمكن من تسجيل الدخول باستخدامه. المتابعة؟ permissions_list = التصاريح: webauthn_key_loss_warning = إذا فقدت مفاتيح الأمان الخاصة بك، فسوف تفقد الوصول إلى حسابك. -hooks.desc = أضف خطاطيف ويب تُطلق لكل مستودعاتك. +hooks.desc = إضافة خطاطيف ويب سيتم تشغيلها لـ جميع المستودعات التي تمتلكها. keep_activity_private_popup = يجعل النشاط مرأياً لك وللمديرين فقط keep_email_private_popup = سيؤدي هذا إلى إخفاء عنوان بريدك الإلكتروني من ملفك الشخصي، وكذلك عند تقديم طلب سحب أو تحرير ملف باستخدام واجهة الويب. لن يتم تعديل الالتزامات المدفوعة. استخدم %s في الإيداعات لربطها بحسابك. ssh_key_name_used = هناك مفتاح SSH بنفس الاسم موجود بالفعل على حسابك. -authorized_oauth2_applications = تطبيقات OAuth2 المأذونة +authorized_oauth2_applications = تطبيقات OAuth2 المأذون لها uid = المعرّف الرمزي manage_openid = عناوين OpenID webauthn = استيثاق ثنائي (مفاتيح الأمان) @@ -468,11 +465,11 @@ key_content_gpg_placeholder = يبدأ بـ '-----BEGIN PGP PUBLIC KEY BLOCK---- add_email_confirmation_sent = بريد تفعيل جديد تم إرساله إلى "%s". يُرجى التحقق من البريد الوارد خلال %s لتأكيد عنوان البريد الإلكتروني. ssh_desc = مفاتيح SSH العمومية هذه مرتبطة بحسابك. وتسمح المفاتيح الخصوصية المرافقة بالوصول الكامل إلى مستودعاتك. ويمكن استعمال مفاتيح SSH الموثَّقة لتوثيق إيداعات جت الموقَّعة بمفاتيح SSH. ssh_gpg_keys = مفاتيح SSH / GPG -authorized_oauth2_applications_description = لقد منحتَ إمكانية الوصول إلى حسابك الشخصي على فورجيو لهذه التطبيقات من تطبيقات خارجية. الرجاء إلغاء وصول التطبيقات التي لم تعد بحاجة إليها. +authorized_oauth2_applications_description = لقد منحت حق الوصول إلى حسابك الشخصي في Forgejo لهذه التطبيقات الخارجية. يرجى إلغاء الوصول للتطبيقات التي لم تعد قيد الاستخدام. ssh_key_been_used = هذا المفتاح الـSSH تم إضافته بالفعل إلى هذا الخادم. password_change_disabled = المستخدمين غير المحليين لا يمكنهم تغيير كلمة مرورهم عن طريق واجهة ويب فورجيو. token_state_desc = هذا الرمز استخدم خلال آخر 7 أيام -delete_key = أزله +delete_key = إزالة ssh_invalid_token_signature = مفتاح SSH المزود، والتوقيع والرمز لا يتطابقوا أو الرمز قديم. ssh_token_help = يمكنك توليد توقيع باستخدام: gpg_key_verify = تحقق @@ -486,10 +483,10 @@ unbind = الغ الربط verify_ssh_key_success = تم التحقق من مفتاح SSH "%s". gpg_token_required = يجب أن تقدم توقيعاً للرمز التالي ssh_key_verified_long = تم التحقق من المفتاح مع رمز ويمكن استخدامه للتحقق من الإيداعات المتطابقة مع أي عناوين البريد المفعلة لهذا المستخدم. -gpg_key_deletion = أزل مفتاح GPG +gpg_key_deletion = إزالة مفتاح GPG gpg_token_help = يمكنك توليد توقيع باستخدام: -ssh_key_deletion = أزل مفتاح SSH -ssh_token = رمز +ssh_key_deletion = إزالة مفتاح SSH +ssh_token = رمز فريد ssh_disabled = SSH مُعطل gpg_key_verified_long = تم التحقق من المفتاح بواسطة رمز ويمكن استخدامه للتحقق من إيداعات متطابقة لعناوين البريد المفعلة لهذا المستخدم بالإضافة إلى أي هويات متطابقة لهذا المفتاح. change_username_redirect_prompt = اسم المستخدم القديم سوف يعاد توجيهه حتى يطالب به شخص آخر. @@ -497,7 +494,7 @@ add_key_success = تم إضافة مفتاح SSH "%s". key_name = اسم المفتاح comment_type_group_time_tracking = تتبع الوقت gpg_invalid_token_signature = مفتاح GPG المزود، والتوقيع والرمز لا يتطابقوا أو الرمز قديم. -ssh_key_verified = مفتاح مُتحقق منه +ssh_key_verified = مفتاح تم التحقق منه ssh_key_deletion_success = تم إزالة مفتاح SSH. key_signature_ssh_placeholder = يبدأ بـ'-----BEGIN SSH SIGNATURE-----' gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig @@ -511,7 +508,7 @@ gpg_key_deletion_desc = إزالة مفتاح GPG يُلغي تحقق الإيد permission_read = القراءة ssh_token_signature = توقيع SSH مصفح ssh_key_deletion_desc = إزالة مفتاح SSH يلغي وصوله إلى حسابك. أكمل؟ -revoke_key = أسحب +revoke_key = سحب gpg_token = رمز gpg_key_matched_identities_long = الهويات المدمجة في هذا المفتاح تطابق عناوين البريد الإلكتروني المفعلة التالية لهذا المستخدم. ويمكن التحقق من صحة الإيداعات المطابقة لهذه العناوين البريدية مع هذا المفتاح. key_content = المحتوى @@ -525,9 +522,120 @@ remove_oauth2_application_success = أُزيل التطبيق. update_oauth2_application_success = لقد حدّثت بنجاح تطبيق OAuth2. oauth2_redirect_uris = روابط إعادة التوجيه. نرجو وضع كل رابط في سطر وحده. remove_account_link = أزل الحساب المربوط -remove_account_link_success = أُزيل الحساب المربوط. - +remove_account_link_success = أُزيل الحساب المرتبط. quota = كوتا +update_hints = حدِّث التلميحات +keep_activity_private.description = سيكون نشاطك العام مرئيًا لك ولمشرفي المثيل فقط. +manage_ssh_principals = إدارة مدراء شهادات SSH الرئيسية +pronouns = الضمائر +pronouns_unspecified = غير محدد +ssh_principal_been_used = تمت إضافة هذه الهوية الرئيسية إلى الخادم مسبقاً. +principal_desc = هذه الهُويات الرئيسية لشهادات SSH مرتبطة بحسابك وتتيح وصولاً كاملاً إلى مستودعاتك. +gpg_helper = تحتاج لمساعدة؟ اطّلع على الدليل حول GPG. +ssh_helper = هل تحتاج مساعدة؟ اطلع على الدليل لـ إنشاء مفاتيح SSH الخاصة بك أو لحل المشكلات الشائعة التي قد تواجهها عند استخدام SSH. +add_new_principal = إضافة هوية رئيسية +keep_pronouns_private.description = سيؤدي ذلك إلى إخفاء ضمائرك عن الزوار الذين لم يقوموا بتسجيل الدخول. +language.description = سيتم حفظ هذه اللغة في حسابك واستخدامها كلغة افتراضية بعد تسجيل الدخول. +storage_overview = نظرة عامة على التخزين +hints = تلميحات +language.title = اللغة الافتراضية +update_hints_success = تم تحديث التلميحات. +language.localization_project = "ساعدنا في ترجمة Forgejo إلى لغتك! المزيد من المعلومات. +change_username_redirect_prompt.with_cooldown.one = سيصبح اسم المستخدم القديم متاحًا للجميع بعد فترة تباطؤ تبلغ %[1]d يومًا. لا يزال بإمكانك استعادة اسم المستخدم القديم خلال فترة التهدئة. +additional_repo_units_hint = اقتراح تفعيل وحدات المستودعات الإضافية +additional_repo_units_hint_description = اعرض تلميح "تفعيل المزيد" للمستودعات التي لم يتم تفعيل جميع الوحدات المتاحة بها. +change_password = غيّر كلمة المرور +keep_pronouns_private = إظهار الضمائر للمستخدمين الذين تمت مصادقتهم فقط +change_username_redirect_prompt.with_cooldown.few = سيصبح اسم المستخدم القديم متاحًا للجميع بعد فترة تباطؤ تبلغ يومًا. لا يزال بإمكانك استعادة اسم المستخدم القديم خلال فترة التهدئة. +no_activity = لا يوجد نشاط حديث +generate_token_success = تم إنشاء الرمز الفريد الجديد الخاص بك. انسخه الآن لأنه لن يظهر مرة أخرى. +manage_access_token = رموز الوصول الفريدة +token_name = اسم الرمز الفريد +generate_token_name_duplicate = اسم التطبيق %s مُستخدم مسبقًا. يُرجى استخدام اسم جديد. +regenerate_token = إعادة التوليد +access_token_regeneration = إعادة توليد رمز وصول فريد +permission_write = قراءة وكتابة +delete_token_success = تم حذف الرمز الفريد. لم يعد بإمكان التطبيقات التي تستخدمه الوصول إلى حسابك. +gpg_key_matched_identities = الهويات المتطابقة: +tokens_desc = تمنح هذه الرموز الفريدة إمكانية الوصول إلى حسابك باستخدام واجهة برمجة تطبيقات Forgejo. +generate_token = توليد رمز فريد +access_token_deletion = حذف رمز الوصول الفريد +generate_new_token = توليد رمز جديد +access_token_deletion_desc = حذف الرمز الفريد سيسحب صلاحية الوصول إلى حسابك من التطبيقات التي تستخدمه. لا يمكن التراجع عن هذا الإجراء. تريد المتابعة؟ +access_token_desc = تقتصر صلاحيات الرمز المحددة على مسارات واجهة البرمجة (API) المقابلة فقط. اطلع على الوثائق لمزيد من المعلومات. +oauth2_application_remove_description = ستؤدي إزالة تطبيق OAuth2 إلى منعه من الوصول إلى حسابات المستخدمين المصرح لهم على هذا المثيل. المتابعة؟ +ssh_principal_deletion = إزالة الهوية الرئيسية لشهادة SSH +at_least_one_permission = يجب عليك تحديد صلاحية واحدة على الأقل لإنشاء رمز فريد +subkeys = المفاتيح الفرعية +ssh_principal_deletion_desc = إزالة هوية رئيسية لشهادة SSH ستسحب صلاحية وصولها إلى حسابك. تريد المتابعة؟ +principal_state_desc = استخدمت هذه الهوية في آخر 7 أيام +ssh_signonly = SSH معطّل حاليًا، لذا تُستخدم هذه المفاتيح فقط للتحقق من توقيع الإيداع. +ssh_externally_managed = يتم إدارة مفتاح SSH هذا خارجيًا لهذا المستخدم +access_token_regeneration_desc = سيؤدي إعادة إنشاء رمز فريد إلى إبطال الوصول إلى حسابك للتطبيقات التي تستخدمه. لا يمكن التراجع عن ذلك. المتابعة؟ +regenerate_token_success = تم إعادة إنشاء الرمز الغريد. لم يعد بإمكان التطبيقات التي تستخدمه الوصول إلى حسابك ويجب تحديثها بالرمز الجديد. +repo_and_org_access = الوصول إلى المستودع والمنظمة +add_principal_success = تمت إضافة الهوية الرئيسية لشهادة "SSH "%s. +ssh_principal_deletion_success = تم إزالة الهوية. +oauth2_applications_desc = تمكّن تطبيقات OAuth2 تطبيقات الطرف الثالث من مصادقة المستخدمين بأمان في مثيل Forgejo هذا. +oauth2_client_secret = سر العميل +oauth2_regenerate_secret = تجديد السر +oauth2_regenerate_secret_hint = فقدت سرك؟ +oauth2_client_id = معرف العميل +permission_no_access = لا وصول +remove_oauth2_application_desc = ستؤدي إزالة تطبيق OAuth2 إلى إبطال الوصول إلى جميع رموز الوصول الفريدة الموقعة. المتابعة؟ +oauth2_confidential_client = العميل السري. حدد للتطبيقات التي تحافظ على السرية، مثل تطبيقات الويب. لا تحدد للتطبيقات الأصلية بما في ذلك تطبيقات سطح المكتب وتطبيقات الأجهزة المحمولة. +oauth2_client_secret_hint = لن يظهر السر مرة أخرى بعد مغادرة هذه الصفحة أو تحديثها. يرجى التأكد من أنك قمت بحفظه. +oauth2_application_create_description = ‪تمنح تطبيقات OAuth2 تطبيقات الطرف الثالث حق الوصول إلى حسابات المستخدمين على هذا المثيل. +oauth2_application_locked = يقوم Forgejo بالتسجيل المسبق لبعض تطبيقات OAuth2 عند بدء التشغيل إذا تم تمكينها في التكوين. لمنع السلوك غير المتوقع، لا يمكن تحريرها أو إزالتها. يرجى الرجوع إلى وثائق OAuth2 لمزيد من المعلومات. +twofa_recovery_tip = إذا فقدت جهازك، ستتمكن من استخدام مفتاح الاسترداد للاستخدام مرة واحدة لاستعادة الوصول إلى حسابك. +twofa_not_enrolled = حسابك غير مسجّل حالياً في المصادقة الثنائية. +twofa_scratch_token_regenerate = إعادة إنشاء مفتاح الاسترداد للاستخدام مرة واحدة +regenerate_scratch_token_desc = إذا فقدت مفتاح الاسترداد الخاص بك في غير محله أو استخدمته بالفعل لتسجيل الدخول، يمكنك إعادة تعيينه هنا. +twofa_failed_get_secret = إخفاق في الحصول على سر. +manage_account_links_desc = هذه الحسابات الخارجية مرتبطة بحسابك في Forgejo. +revoke_oauth2_grant_description = سيؤدي إبطال الوصول لهذا التطبيق التابع لجهة خارجية إلى منع هذا التطبيق من الوصول إلى بياناتك. أنت متأكد؟ +revoke_oauth2_grant_success = تم سحب صلاحية الوصول بنجاح. +webauthn_alternative_tip = قد ترغب في تكوين أسلوب مصادقة إضافي. +webauthn_register_key = إضافة مفتاح تشفير +webauthn_nickname = الاسم المستعار +manage_account_links = الحسابات المرتبطة +revoke_oauth2_grant = سحب صلاحية الوصول +twofa_enroll = التسجيل في المصادقة الثنائية +twofa_is_enrolled = حسابك مسجّل حاليًا في المصادقة الثنائية. +twofa_scratch_token_regenerated = مفتاح الاسترداد للاستخدام مرة واحدة هو %s الآن. قم بتخزينه في مكان آمن، فلن يتم عرضه مجدداً. +twofa_enrolled = تم تسجيل حسابك بنجاح. قم بتخزين مفتاح الاسترداد للاستخدام لمرة واحدة (%s) في مكان آمن، فلن يتم عرضه مجدداً. +webauthn_desc = مفاتيح الأمان هي أجهزة فعلية تحوي على مفاتيح تشفير. يمكن استخدامها للتحقق بخطوتين. يجب أن تدعم مفاتيح الأمان معيار WebAuthn Authenticator. +remove_account_link_desc = ستؤدي إزالة حساب مرتبط إلى إلغاء وصوله إلى حساب Forgejo الخاص بك. المتابعة؟ +email_notifications.onmention = البريد الإلكتروني فقط عند الإشارة +email_notifications.andyourown = والإشعارات الخاصة بك +visibility = رؤية المستخدم +visibility.public_tooltip = مرئي للجميع +visibility.private_tooltip = مرئي فقط لأعضاء المؤسسات التي انضممت إليها +visibility.public = عام +delete_account_desc = هل أنت متأكد من رغبتك في حذف حساب المستخدم هذا نهائيًا؟ +user_block_yourself = لا يمكنك حظر نفسك. +delete_with_all_comments = حسابك أصغر من %s. لتجنب التعليقات الوهمية، سيتم حذف جميع تعليقات المشكلة/المسؤولية الشخصية معها. +visibility.limited_tooltip = مرئية فقط للمستخدمين الذين قاموا بتسجيل الدخول +visibility.limited = محدود +visibility.private = خاص +quota.applies_to_org = تنطبق قواعد الحصص التالية على هذه المنظمة +quota.rule.no_limit = غير محدود +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.all = الأصول +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.rule.exceeded.helper = لقد تجاوز الحجم الإجمالي للكائنات لهذه القاعدة الحصة النسبية. +quota.rule.exceeded = تم تجاوزه +quota.applies_to_user = تنطبق قواعد الحصص التالية على حسابك +quota.sizes.wiki = الموسوعة [org] follow_blocked_user = لا يمكنك إتباع هذه المنظمة لأن هذه المنظمة حظرتك. @@ -636,7 +744,7 @@ pulls.blocked_by_user = لا يمكنك أن ترسل طلب سحب في هذا migrate.migrating_milestones = ترحيل الأهداف migrate_items_milestones = أهداف repo_size = حجم المستودع -object_format = صيغة الكائنات +object_format = تنسيق الكائنات use_template = استخدم هذا القالب migrate_items_merge_requests = طلبات الدمج repo_name = اسم المستودع @@ -644,11 +752,11 @@ template = القالب projects.modify = عدّل المشروع tree_path_not_found_commit = المسار %[1]s غير موجود في الإيداع %[2]s repo_lang = اللغة -fork_repo = اشتق المستودع +fork_repo = اشتقاق المستودع fork_no_valid_owners = لا يمكن اشتقاق هذا المستودع لعدم وجود مالك صالح. license = الترخيص fork_branch = الفرع الذي سيُستنسخ إلى الاشتقاق -template_helper = اجعل المستودع قالبا +template_helper = اجعل المستودع قالباً owner = المالك projects.deletion_success = تم حذف المشروع. projects.deletion = احذف المشروع @@ -658,14 +766,14 @@ projects.edit = عدّل المشروع template.avatar = الصورة الرمزية migrate_items_wiki = الموسوعة repo_desc = الوصف -template_select = اختر قالبا. +template_select = اختر قالبا repo_name_helper = الأسماء الحسنة للمستودعات تستخدم كلمات مفتاحية قصيرة وسهلة التذكر وفريدة. -default_branch = الفرع المبدئي +default_branch = الفرع الافتراضي all_branches = كل الفروع -migrate_items_issues = المسائل +migrate_items_issues = البلاغات projects.deletion_desc = حذف مشروع يحذف كل المسائل المرتبطة به. أتريد الاستمرار؟ -repo_desc_helper = أدخل وصفا قصيرا (اختياريا) -create_repo = أنشئ مستودعا +repo_desc_helper = أدخل وصفاً قصيراً (اختياري) +create_repo = إنشاء مستودع migrate_items_releases = الإصدارات already_forked = لقد اشتققت %s بالفعل license_helper = اختر ملف ترخيص. @@ -686,7 +794,7 @@ issues.remove_milestone_at = `أزال هذه المسألة من الهدف issues.filter_assginee_no_assignee = بلا مكلف issues.new.no_milestone = بلا هدف issues.new.projects = المشروعات -delete_preexisting_label = احذف +delete_preexisting_label = حذف issues.context.edit = عدّل branch.rename = غيّر اسم الفرع "%s" issue_labels = تصنيفات المسائل @@ -782,7 +890,7 @@ milestones.deletion_desc = حذف هدف يحذفه من كل المسائل ا issues.desc = نظّم إبلاغات العلل، والمهام، والأهداف. issues.choose.ignore_invalid_templates = أُهمِلت القوالب التالفة branch.renamed = غُيّر اسم الفرع %s إلى %s. -delete_preexisting = احذف الملفات الموجودة سابقا +delete_preexisting = حذف الملفات الموجودة مسبقاً branch.included_desc = هذا الفرع جزء من الفرع المبدئي trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع issues.reopened_at = `أعاد فتح هذه المسألة %s` @@ -856,7 +964,7 @@ issues.reopen_comment_issue = علّق وأعد فتحها issues.dependency.add = أضف اعتمادية… issues.label_deletion_desc = حذف تصنيف يحذفه من كل المسائل، أتريد الاستمرار؟ labels = التصنيفات -delete_preexisting_content = احذف الملفات في %s +delete_preexisting_content = حذف الملفات في %s milestones.deletion = احذف الهدف issues.comment_pull_merged_at = دمج الإيداع %[1]s إلى %[2]s %[3]s issues.new.closed_milestone = الأهداف التامة @@ -898,7 +1006,7 @@ issues.dependency.issue_no_dependencies = بلا اعتمادية. issues.filter_sort.mostcomment = الأكثر تعليقات issues.label.filter_sort.reverse_by_size = الأكبر حجما release.message = صِف هذا الإصدار -editor.cancel_lower = ألغِ +editor.cancel_lower = إلغاء issues.label.filter_sort.reverse_alphabetically = أبجديا معكوسا trust_model_helper = اختر نموذج الثقة للتحقق من التوقيعات. الخيارات المتاحة هي: issues.unlock_error = لا يمكن فك قفل مسألة غير مقفلة. @@ -932,7 +1040,7 @@ issues.label_archive = أرشف التصنيف issues.choose.blank_about = أنشئ مسألة من القالب المبدئي. issues.filter_poster = منشئ release.delete_release = احذف الإصدار -editor.cancel = ألغِ +editor.cancel = إلغاء issues.change_title_at = `غيّر العنوان من %s إلى %s %s` branch.new_branch_from = أنشئ فرعًا جديدًا من "%s" issues.due_date_form_edit = "عدّل" @@ -944,7 +1052,7 @@ issues.filter_assignee = مكلف issues.open_title = حالية download_file = نزّل الملف issues.attachment.download = `انقر لتنزيل "%s"` -download_archive = نزّل المستودع +download_archive = تنزيل المستودع download_tar = نزّل TAR.GZ download_zip = نزّل ZIP releases.desc = تتبع إصدارات المشروع وتنزيلاته. @@ -957,15 +1065,15 @@ projects.desc = أدر المسائل وطلبات الدمج في لوحات م ambiguous_runes_header = `يحتوي هذا الملف على محارف يونيكود غامضة` executable_file = ملف تنفيذي settings.webhook.response = الاستجابة -editor.delete = احذف %s -file_view_raw = أظهر المجرد +editor.delete = حذف %s +file_view_raw = أظهر الخام settings.add_webhook = أضف خطاف ويب settings.slack_channel = قناة commits = إيداعات commit = إيداع -editor.upload_file = ارفع ملفا +editor.upload_file = رفع ملف settings.webhook.headers = الترويسات -org_labels_desc_manage = أدرها +org_labels_desc_manage = إدارة editor.patch = طبّق الرقعة editor.must_have_write_access = يجب أن يكون لديك إمكانية الكتابة حتى تعدّل هذا الملف أو تقترح تعديلات. template.webhooks = خطاطيف الويب @@ -989,12 +1097,12 @@ settings.webhook.payload = المحتوى invisible_runes_header = `يحتوي هذا الملف على محارف يونيكود غير مرئية` editor.filename_help = أضف مجلدا بكتابة اسمه ثم شرطة مائلة ('/'). احذف مجلدا بضغط زر Backspace أول شيء في خانة الاسم. editor.commit_changes = أودع التعديلات -editor.add_file = أضف ملفا +editor.add_file = إضافة ملف settings.githook_name = اسم الخطاف editor.fork_before_edit = يجب أن تشتق هذا المستودع حتى تعدّل هذا الملف أو تقترح تعديلات. projects.description_placeholder = الوصف tag = وسم -file_raw = مجرد +file_raw = خام projects.create = أنشئ مشروعا settings.update_webhook = حدّث خطاف الويب editor.push_rejected_no_message = لقد رفض الخادوم التعديل بغير رسالة. نرجو مراجعة خطاطيف جت. @@ -1013,7 +1121,7 @@ editor.no_changes_to_show = لا توجد تعديلات لعرضها. settings.webhook.test_delivery = اختبار التوصيل commit_graph.hide_pr_refs = أخفِ طلبات الدمج editor.new_file = ملف جديد -file_view_source = أظهر المصدر +file_view_source = عرض المصدر settings.webhook_deletion_success = أزيل خطاف الويب. projects.title = العنوان settings.slack_domain = النطاق @@ -1042,11 +1150,11 @@ settings.add_hook_success = أضيف خطاف الويب. commit.revert-header = إرجاع: %s editor.file_already_exists = يوجد فعلا في هذا المستودع ملف باسم "%s". settings.web_hook_name_matrix = متركس -editor.filename_cannot_be_empty = لا يمكن ترك اسم الملف فارغا. +editor.filename_cannot_be_empty = لا يمكن ترك اسم الملف فارغاً. editor.add_tmpl = أضف '<%s>' editor.new_branch_name_desc = اسم الفرع الجديد… release = إصدار -editor.delete_this_file = احذف الملف +editor.delete_this_file = حذف الملف editor.or = أو editor.push_rejected_summary = رسالة الرفض الكاملة: settings.webhook_deletion = أزل خطاف الويب @@ -1353,6 +1461,210 @@ pulls.fast_forward_only_merge_pull_request = تسريع وحسب pulls.merge_conflict = تعذر الدمج: حدث نزاع خلال الدمج. مساعدة: جرب طريقة أخرى pulls.rebase_conflict = تعذر الدمج: حدث نزاع خلال إعادة تأسيس الإيداع: %[1]s. مساعدة: جرب طريقة أخرى pulls.has_merged = فشل: لقد تم دمج هذا الطلب، فلا يمكنك دمجه مجددا أو تغيير الفرع الهدف. +new_repo_helper = يحتوي المستودع على جميع ملفات المشروع، بما في ذلك سجل التعديلات. هل تستضيف واحدًا بالفعل على منصة أخرى؟ نقل المستودع. +new_from_template = استخدم قالباً +new_advanced = إعدادات مقتدمة +new_advanced_expand = انقر للتوسعة +new_from_template_description = يمكنك تحديد قالب مستودع موجود على هذا المثيل وتطبيق إعداداته. +owner_helper = قد لا تظهر بعض منتديات المجموعة في القائمة المنسدلة بسبب الحد الأقصى لعدد المستودعات. +fork_from = اشتق من +fork_visibility_helper = لا يمكن تغيير ظهور المستودع المشتّق. +generate_repo = توليد لمستودع +readme_helper_desc = هذا هو المكان الذي يمكنك فيه كتابة وصف كامل لمشروعك. +repo_gitignore_helper = حدد قوالب .gitignore +auto_init = تهيئة المستودع +default_branch_label = افتراضي +auto_init_description = ابدأ سجل Git بملف README، مع إمكانية إضافة ملفات الرخصة و.gitignore اختيارياً. +mirror_use_ssh.text = استخدم مصادقة SSH +mirror_address = استنساخ عبر URL +mirror_prune_desc = إزالة مراجع التتبع عن بُعد القديمة +mirror_interval_invalid = الفاصل الزمني للمرآة غير صالح. +readme_helper = حدد قالب ملف README +generate_from = التوليد من +mirror_use_ssh.not_available = المصادقة عبر SSH غير متاحة. +open_with_editor = افتح بـ %s +mirror_prune = تنقية +visibility_fork_helper = (سيؤثر تغيير ذلك على رؤية جميع المشتقات.) +clone_helper = تحتاج مساعدة في الاستنساخ؟ زُر المساعدة. +mirror_public_key = مفتاح SSH عام +size_format = %[1]s: %[2]s, %[3]s: %[4]s +visibility_description = فقط المالك أو أعضاء المؤسسة إذا كان لديهم حقوق، سيتمكنون من رؤيته. +license_helper_desc = تحدد الرخصة ما يمكن للآخرين فعله أو عدم فعله بشيفرة برمجيتك. لست متأكدًا من الرخصة المناسبة لمشروعك؟ طالع اختيار الرخصة. +mirror_denied_combination = لا يمكن استخدام المصادقة المستندة إلى المفتاح العام وكلمة المرور معاً. +repo_gitignore_helper_desc = اختر الملفات التي لا تريد تتبعها من قائمة القوالب الخاصة باللغات الشائعة. يتم تضمين القطع الأثرية النموذجية التي تم إنشاؤها بواسطة أدوات البناء الخاصة بكل لغة في .gitignore بشكل افتراضي. +stars = النجوم +default_branch_helper = الفرع الافتراضي هو الفرع الأساسي لطلبات السحب ،وعمليات إلإيداع. +mirror_use_ssh.helper = سيقوم Forgejo بعكس المستودع عبر Git عبر SSH وإنشاء زوج مفاتيح لك عند تحديد هذا الخيار. يجب عليك التأكد من أن المفتاح العام الذي تم إنشاؤه مخول للدفع إلى المستودع الوجهة. لا يمكنك استخدام التخويل المستند إلى كلمة المرور عند تحديد هذا الخيار. +desc.private = خاص +readme = README +mirror_interval = الفاصل الزمني للمرآة (وحدات الوقت الصحيحة هي 'h' ،'m' ،'s'). 0 لتعطيل المزامنة الدورية. (الحد الأدنى: %s) +desc.template = قالب +desc.public = عام +visibility = الرؤية +migrate_options_mirror_helper = سيكون هذا المستودع مرآة +adopt_preexisting_success = الملفات المعتمدة والمستودع الذي تم إنشاؤه من %s +archive.title_date = تمت أرشفة هذا المستودع على %s. يمكنك عرض الملفات واستنساخه، لكن لا يمكنك إجراء أي تغييرات على حالته، مثل دفع وإنشاء بلاغات أو طلبات سحب أو تعليقات جديدة. +migrate_options_lfs_endpoint.label = نقطة نهاية LFS +transfer.reject_desc = إلغاء النقل إلى ”%s“ +archive.title = تمت أرشفة هذا المستودع. يمكنك عرض الملفات واستنساخه، لكن لا يمكنك إجراء أي تغييرات على حالته، مثل دفع وإنشاء بلاغات أو طلبات سحب أو تعليقات جديدة. +stargazers = المميِّزون بنجمة +form.reach_limit_of_creation_n = لقد وصل المالك بالفعل إلى الحد الأقصى للمستودعات %d. +mirror_sync_on_commit = المزامنة عند دفع الإيداعات +mirror_lfs_desc = تنشيط النسخ المتطابق لبيانات LFS. +author_search_tooltip = عرض كحد أقصى 30 مستخدمًا +template.git_content = محتوى Git (الفرع الافتراضي) +need_auth = المصادقة +migrate_options_lfs_endpoint.description.local = يتم دعم مسار الخادم المحلي أيضاً. +template.one_item = يجب على الأقل تحديد عنصر قالب واحد +archive.pull.noreview = هذا المستودع مؤرشف. لا يمكنك مراجعة طلبات السحب. +migrate.clone_address = ترحيل / استنساخ من عنوان URL +migrate.repo_desc_helper = اتركه فارغاً لاستيراد الوصف الموجود +archive.nocomment = التعليق غير ممكن لأن المستودع تمت أرشفته. +mirror_address_protocol_invalid = عنوان URL المقدم غير صالح. يمكن استخدام مواقع http(s):// أو git:// فقط للنسخ المتطابق. +mirror_lfs_endpoint = نقطة نهاية LFS +mirror_lfs_endpoint_desc = ستحاول المزامنة استخدام عنوان url المستنسخ إلى تحديد خادم LFS. يمكنك أيضًا تحديد نقطة نهاية مخصصة إذا كانت بيانات LFS المستودع مخزنة في مكان آخر. +mirror_password_blank_placeholder = (بلا تعيين) +delete_preexisting_success = الملفات المحذوفة غير المعتمدة في %s +blame_prior = عرض النّقد قبل هذا التغيير +blame.ignore_revs = جاري تجاهل المراجعات في .git-blame-ignore-revs. انقر هنا لتجاوز وعرض واجهة blame العادية. +desc.archived = مؤرشف +template.issue_labels = وسوم الإبلاغات +sync_fork.branch_behind_one = هذا الفرع هو %[1]d إيداع خلف %[2]s +sync_fork.button = مزامنة +transfer.no_permission_to_reject = لا تملك الصلاحية لرفض هذا النقل. +form.string_too_long = السلسلة المحددة أطول من d حرفاً. +migrate_options = خيارات الترحيل +migrate_options_lfs = ترحيل ملفات LFS +migrate.clone_local_path = أو مسار خادم محلي +mirror_password_help = تغيير اسم المستخدم لمسح كلمة المرور المخزنة. +watchers = المراقبون +template.items = عناصر القالب +template.topics = المواضيع +migrate_options_lfs_endpoint.placeholder = إذا تُركت فارغة، سيتم اشتقاق نقطة النهاية من عنوان URL المستنسخ +migrate.clone_address_desc = رابط HTTP(S) أو Git لاستنساخ مستودع موجود +migrate.github_token_desc = يمكنك وضع رمز فريد أو أكثر هنا مفصول بفواصل لجعل الترحيل أسرع من خلال التحايل على حد معدل GitHub API. تحذير: قد يؤدي إساءة استخدام هذه الميزة إلى انتهاك سياسة مزود الخدمة وقد يؤدي إلى حظر حسابك (حساباتك). +transfer.no_permission_to_accept = لا تملك الصلاحية لقبول هذا النقل. +transfer.reject = رفض النقل +transfer.accept_desc = النقل إلى ”%s“ +desc.internal = داخلي +summary_card_alt = بطاقة ملخص المستودع %s +transfer.accept = قبول النقل +blame.ignore_revs.failed = فشل تجاهل المراجعات في .git-blame-ignore-revs. +form.name_pattern_not_allowed = النمط ”%s“ غير مسموح به في اسم المستودع. +migrate_options_lfs_endpoint.description = سيحاول الترحيل استخدام مسار Git البعيد (remote) لـ تحديد خادم LFS. يمكنك أيضًا تحديد نقطة نهاية مخصصة إذا كانت بيانات LFS للمستودع مخزنة في مكان آخر. +migrate_items = عناصر الترحيل +adopt_preexisting_content = إنشاء مستودع من %s +migrate_repo = ترحيل المستودع +mirror_password_placeholder = (لم يتم تعديله) +sync_fork.branch_behind_few = هذا الفرع هو %[1]d إيداعات خلف %[2]s +mirror_address_desc = ضع أي بيانات اعتماد مطلوبة في قسم المصادقة. +mirror_last_synced = آخر مزامنة +mirror_lfs = تخزين الملفات الكبيرة (LFS) +stars_remove_warning = سيؤدي ذلك إلى إزالة جميع النجوم من هذا المستودع. +reactions_more = و %d أكثر +template.invalid = يجب تحديد مستودع القوالب +adopt_search = أدخل اسم المستخدم للبحث عن مستودعات غير معتمدة... (اتركه فارغاً للعثور على الكل) +adopt_preexisting = اعتماد الملفات الموجودة مسبقاً +language_other = أُخرى +adopt_preexisting_label = اعتماد الملفات +form.reach_limit_of_creation_1 = لقد وصل المالك بالفعل إلى الحد الأقصى للمستودع %d. +form.name_reserved = تم حجز اسم المستودع ”%s“. +mirror_address_url_invalid = عنوان URL المقدم غير صالح. تأكد من تحرير مكونات عنوان URL بشكل صحيح. +migrate.migrate = الترحيل من %s +migrate.gogs.description = ترحيل البيانات من notabug.org أو مثيلات Gogs الأخرى. +migrate.onedev.description = ترحيل البيانات من code.onedev.io أو مثيلات OneDev الأخرى. +migrate.migrating_topics = ترحيل المواضيع +migrate.cancel_migrating_title = إلغاء الترحيل +generated_from = تم توليده من +star_guest_user = قم بتسجيل الدخول لإعطاء نجمة لهذا المستودع. +subscribe.pull.guest.tooltip = سجّل الدخول للاشتراك في طلب السحب هذا. +unwatch = إلغاء المشاهدة +quick_guide = دليل سريع +empty_message = لا يحتوي هذا المستودع على أي محتوى. +migrate.migrating_labels = ترحيل الوسوم +migrate.migrating_releases = ترحيل الإصدارات +watch_guest_user = سجّل الدخول لمشاهدة هذا المستودع. +star = نجمة +cite_this_repo = الاستشهاد بهذا المستودع +no_desc = لا يوجد وصف +migrate.github.description = ترحيل البيانات من github.com أو خادم GitHub Enterprise. +migrated_from_fake =تم الترحيل من %[1]s +clone_this_repo = استنسخ هذا المستودع +fork_from_self = لا يمكنك اشتقاق مستودع تملكه. +fork_guest_user = سجّل الدخول لاشتقاق هذا المستودع. +subscribe.issue.guest.tooltip = سجّل الدخول للاشتراك في هذا البلاغ. +more_operations = المزيد من العمليات +push_exist_repo = دفع مستودع موجود من موجّه الأوامر +broken_message = لا يمكن قراءة بيانات Git التي يستند إليها هذا المستودع. اتصل بمسؤول هذا المثيل أو احذف هذا المستودع. +migrate.git.description = ترحيل مستودع فقط من أي خدمة Git. +watch = شاهد +migrate.migrating_git = ترحيل بيانات Git +migrate.gitea.description = ترحيل البيانات من gitea.com أو مثيلات Gitea الأخرى. +migrate.gitbucket.description = ترحيل البيانات من مثيلات GitBucket. +migrate.cancel_migrating_confirm = تريد إلغاء عملية الترحيل هذه؟ +migrate.permission_denied_blocked = لا يمكنك الاستيراد من مضيفين غير مسموح بهم، يُرجى الطلب من المسؤول التحقق من إعدادات ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. +migrate.gitlab.description = ترحيل البيانات من gitlab.com أو مثيلات GitLab الأخرى. +migrate.migrating = الترحيل من %s … +migrate.migrating_failed.error = أخفق الترحيل: %s +migrate.codebase.description = ترحيل البيانات من codebasehq.com. +migrate.invalid_local_path = المسار المحلي غير صالح. فهو غير موجود أو ليس مجلداً. +migrate.permission_denied = لا يُسمح لك باستيراد المستودعات المحلية. +migrate.failed = أخفق الترحيل: %v +migrate.migrate_items_options = رمز الوصول الفريد مطلوب لترحيل العناصر الإضافية +migrated_from = تم الترحيل من %[2]s +mirror_from = مرآة لـ +forked_from = مشتقّ من +create_new_repo_command = إنشاء مستودع جديد على موجّه الأوامر +fork = اشتقاق +migrate.migrating_failed_no_addr = أخفق الترحيل. +unstar = إزالة النجمة +migrate.migrating_issues = ترحيل البلاغات +migrate.migrating_pulls = ترحيل طلبات السحب +code = الكود +migrate.invalid_lfs_endpoint = نقطة نهاية LFS غير صالحة. +migrate.migrating_failed = فشل الترحيل من %s. +branch = فرع +template.git_hooks_tooltip = يتعذر عليك حاليًا تعديل أو إزالة خطافات Git بمجرد إضافتها. حدد هذا فقط إذا كنت تثق بمستودع القالب. +code.desc = الوصول إلى الشيفرة المصدرية والملفات والالتزامات والفروع. +tree = شجرة +clear_ref = 'مسح المرجع الحالي' +project = المشارييع +filter_branch_and_tag = تصفية الفرع أو العلامة +find_tag = البحث عن علامة +commit_graph = الرسم البياني للإيداع +editor.edit_this_file = عدل الملف +editor.signoff_desc = أضف مقطورة موقّعة من قِبل المُجرّد في نهاية رسالة سجل الدخول. +n_release_one = %s إصدار +symbolic_link = رابط رمزي +editor.cannot_edit_lfs_files = لا يمكن تحرير ملفات LFS في واجهة الويب. +editor.this_file_locked = الملف مقفل +n_commit_few = %s إيداعات +editor.file_delete_success = تم حذف الملف "%s". +editor.edit_file = عدّل الملف +commit.contained_in_default_branch = هذا الإيداع جزء من الفرع الافتراضي +line = سطر +lines = أسطر +normal_view = عرض عادي +n_branch_one = %s فرع +n_commit_one = %s إيداع +view_git_blame = عرض مسؤول تعديل git +editor.add_tmpl.filename = اسم الملف +editor.name_your_file = اسم ملفك… +editor.propose_file_change = اقتراح تغيير الملف +editor.new_branch_name = تسمية الفرع الجديد لهذا الإيداع +from_comment = (تعليق) +no_eol.text = لا EOL +no_eol.tooltip = هذا الملف لا يحتوي على نهاية لخط الشخصية. +stored_lfs = مخزن مع Git LFS +commit.contained_in = هذا الإيداع موجود في: +file_view_rendered = عرض المُخرج النهائي +n_branch_few = %s فروع +n_tag_one = %s علامة +n_tag_few = ‪%s علامات +n_release_few = %s إصدارات +file.title = %s عند %s +vendored = مضمن +file_follow = متابعة الروابط الرمزية [mail] admin.new_user.text = من فضلك اضغط هنا لإدارة هذا المستخدم من لوحة الإدارة. @@ -1411,22 +1723,21 @@ repo.transfer.subject_to = %s يود نقل ملكية "%s" إلى %s 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). +totp_enrolled.text_1.has_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يمكنك استخدام TOTP كطريقة للمصادقة الثنائية ، أو استخدام أي من مفاتيح الأمان الخاصة بك. +totp_enrolled.subject = لقد قمت بتشيط TOTP كطريقة 2FA removed_security_key.subject = تمت إزالة مفتاح الأمان removed_security_key.text_1 = تم إزالة مفتاح الأمان ”%[1] s“ للتو من حسابك. -removed_security_key.no_2fa = لم تعد هناك طرق أخرى للمصادقة الثنائية (2FA) قيد التهيئة بعد الآن، أي لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA). account_security_caution.text_1 = إذا كان هذا أنت، فيمكنك تجاهل هذا البريد بأمان. +totp_disabled.no_2fa = لم تعد هناك طرق أُخرى للمصادقة الثنائية (2FA) قيد التهيئة عد الآن ، أي أنه لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA). +removed_security_key.no_2fa = لم تعد هناك طرق أخرى للمصادقة الثنائية (2FA) قيد التهيئة بعد الآن، أي لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA). +primary_mail_change.subject = تم تغيير البريد الأساسي الخاص بك +password_change.text_1 = تم تغيير كلمة مرور حسابك للتو. +primary_mail_change.text_1 = تم تغيير البريد الإلكتروني الأساسي لحسابك إلى %[1]s. هذا يعني أن عنوان البريد الإلكتروني هذا لن يتلقى إشعارات البريد لحسابك بعد الآن. 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 = تعذر العثور على الهدف. @@ -1479,17 +1790,16 @@ email_visibility.private = عنوان بريدك الإلكتروني ظاهر 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 = متابعة +public_activity.visibility_hint.admin_private = هذا النشاط مرئي لك لأنك مسؤول، ولكن المستخدم يريد أن يظل خاصاً. +public_activity.visibility_hint.self_private = نشاطك مرئي لك ولسُعاة المثيل فقط. تعديل الإعدادات. followers_one = %d متابِع +following.title.one = متابعة +followers.title.few = متابعين following_one = %d يُتابع +following.title.few = متابعة 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] @@ -1566,14 +1876,13 @@ openid_register_desc = مسار الـOpenID المختار مجهول. اربط remember_me = تذكر هذا الجهاز remember_me.compromised = رمز الاحتفاظ بتسجيل الدخول لم يعد صالحا، مما قد يعني اختراق الحساب. نرجو مراجعة حسابك لرؤية أي نشاط غير مألوف. authorization_failed_desc = فشل التفويض لأننا اكتشفنا طلبًا غير صالح. يرجى الاتصال بمشرف التطبيق الذي حاولت ترخيصه. - +sign_in_openid = المتابعة باستخدام OpenID hint_login = لديك حساب بالفعل؟ سجّل الدخول الآن! hint_register = يلزمك حساب ؟ سجِّل الآن. sign_up_button = سجِّل الآن. -unauthorized_credentials = بيانات الاعتماد غير صحيحة أو انتهت صلاحيتها. أعد محاولة تنفيذ الأمر أو راجع %s لمزيد من المعلومات -use_onetime_code = استخدم رمزًا لمرة واحدة back_to_sign_in = العودة إلى تسجيل الدخول -sign_in_openid = المتابعة باستخدام OpenID +use_onetime_code = استخدم رمزًا لمرة واحدة +unauthorized_credentials = بيانات الاعتماد غير صحيحة أو انتهت صلاحيتها. أعد محاولة تنفيذ الأمر أو راجع %s لمزيد من المعلومات [packages] rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة. @@ -1617,7 +1926,6 @@ less = أقل number_of_contributions_in_the_last_12_months = %s مساهم في آخر 12 شهر contributions_zero = بلا مساهمات more = أكثر - contributions_format = {contributions} مساهمة في {day} {month} {year} contributions_one = المساهمة contributions_few = المساهمات @@ -1822,18 +2130,17 @@ glob_pattern_error = `النمط الشامل غير صالح: %s.` CommitChoice = إختيار الإداع regex_pattern_error = ` نمط التعبير النمطي غير صالح: %s.` username_error = ` يُمكنه أن يحتوي على حروف إنجليزية وأرقام وشرطة ("-") وشرطة سفلية ("_") و نقطة (".") فقط. ويمكنه ان يبدأ وينتهي بحرف او برقم.` - +Biography = النبذة +Website = موقع الويب +To = اسم الفرع +AccessToken = رمز الوصول +repository_force_private = وضع الخاص الإجباري مفعّل: لا يمكن تحويل المستودعات الخاصة إلى عامة. FullName = الاسم الكامل Description = الوصف Pronouns = الضمائر -Biography = النبذة -Website = موقع الويب -Location = الموقع -To = اسم الفرع -AccessToken = رمز الوصول -invalid_group_team_map_error = ` التعيين غير صالح: %s ` username_claiming_cooldown = لا يمكن المطالبة باسم المستخدم، لأن فترة تباطؤه لم تنتهِ بعد. يمكن المطالبة به عند %[1]s. -repository_force_private = وضع الخاص الإجباري مفعّل: لا يمكن تحويل المستودعات الخاصة إلى عامة. +Location = الموقع +invalid_group_team_map_error = ` التعيين غير صالح: %s ` visit_rate_limit = تناولت الزيارة عن بُعد الحد من معدلها. email_domain_is_not_allowed = نطاق البريد الإلكتروني للمستخدم %s يتعارض مع قائمة النطاقات المسموحة ، أو الممنوعة. يرجى التأكد من إدخال عنوان البريد الإلكتروني بشكل صحيح. unset_password = المستخدم المسجل لم يقم بتعيين كلمة مرور. @@ -1886,11 +2193,10 @@ code_no_results = لم يتم العثور على برمجية تطابق الب relevant_repositories_tooltip = تم أخفاء المستودعات التي هي مشتقات وأيضاً التي ليس لها موضوع، ولا أيقونة، ولا يوجد وصف. relevant_repositories = يتم اظهار المستودعات المتعلقة فقط. أظهر النتائج غير المصفاة. code_last_indexed_at = فُهرس آخر مرة %s - -stars_one = %d نجمة stars_few = %d نجوم forks_one = %d نسخة forks_few = %d نُسَخ +stars_one = %d نجمة [actions] variables.none = لا توجد متغيرات بعد. @@ -2080,12 +2386,12 @@ no_results = لا توجد نتائج مطابقة. issue_kind = البحث ضمن الأعطال… pull_kind = البحث ضمن طلبات السحب… keyword_search_unavailable = البحث من خلال الكلمات المفتاحية ليس متوفر حالياً. رجاءاً تواصل مع مشرف الموقع. +package_kind = البحث ضمن الحزم… +regexp_tooltip = تعامل مع عبارة البحث على أنها تعبير نمطي +commit_kind = البحث ضمن الإيداعات… union = مطابقة عامة -union_tooltip = عرض النتائج التي تطابق أي من الكلمات المفتاحية المفصولة بمسافات +runner_kind = البحث ضمن المشغِّلات… exact = مطابق exact_tooltip = عرض النتائج التي تطابق مصطلح البحث بالضبط فقط regexp = RegExp -regexp_tooltip = تعامل مع عبارة البحث على أنها تعبير نمطي -package_kind = البحث ضمن الحزم… -commit_kind = البحث ضمن الإيداعات… -runner_kind = البحث ضمن المشغِّلات… +union_tooltip = عرض النتائج التي تطابق أي من الكلمات المفتاحية المفصولة بمسافات diff --git a/options/locale/locale_be.ini b/options/locale/locale_be.ini index fe04dadc3e..25aff3019c 100644 --- a/options/locale/locale_be.ini +++ b/options/locale/locale_be.ini @@ -7,10 +7,20 @@ sign_in = Увайсці sign_in_or = або sign_out = Выйсці sign_up = Зарэгістравацца -link_account = Звязаць Уліковы запіс +link_account = Звязаць уліковы запіс register = Рэгістрацыя version = Версія powered_by = Працуе на ℅s page = Старонка home = Галоўная Старонка -sign_in_with_provider = Увайсці з %s \ No newline at end of file +sign_in_with_provider = Увайсці з %s +template = Шаблон +language = Мова +notifications = Апавяшчэнні +create_new = Стварыць… +user_profile_and_more = Профіль і налады… +signed_in_as = Увайшоў як +enable_javascript = Гэты вэб-сайт патрабуе JavaScript. +toc = Змест +licenses = Ліцэнзіі +return_to_forgejo = Вярнуцца да Forgejo \ No newline at end of file diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 35e33f4430..a1e9aa4aba 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -141,7 +141,6 @@ webauthn_sign_in = Натиснете бутона на вашия ключ за webauthn_error = Неуспешно прочитане на вашия ключ за сигурност. webauthn_unsupported_browser = Вашият браузър в момента не поддържа WebAuthn. webauthn_error_duplicated = Ключът за сигурност не е разрешен за тази заявка. Моля, уверете се, че ключът не е вече регистриран. - tracked_time_summary = Обобщение на проследеното време въз основа на филтрите в списъка със задачи [settings] @@ -382,20 +381,19 @@ 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 = Уики +quota.sizes.all = Всички +quota.sizes.repos.all = Хранилища +quota.sizes.assets.attachments.issues = Прикачени файлове към задачи [packages] container.labels.value = Стойност @@ -454,113 +452,112 @@ 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 = Този пакет е наличен в няколко групи. +container.pull = Издърпайте образа от командния ред: +helm.registry = Настройте този регистър от командния ред: +debian.repository.distributions = Дистрибуции +npm.dependencies.optional = Опционални зависимости +owner.settings.cargo.title = Индекс на регистъра на Cargo +owner.settings.cleanuprules.keep.pattern.container = Версията latest винаги се запазва за Container пакети. +owner.settings.cleanuprules.remove.pattern = Премахване на версии, съответстващи на +rpm.distros.suse = на дистрибуции, базирани на SUSE +owner.settings.cleanuprules.preview.overview = %d пакета са насрочени за премахване. +owner.settings.cleanuprules.preview = Преглед на правило за почистване +arch.version.properties = Свойства на версията +conan.registry = Настройте този регистър от командния ред: +conan.details.repository = Хранилище +composer.install = За да инсталирате пакета с Composer, изпълнете следната команда: +chef.install = За да инсталирате пакета, изпълнете следната команда: +chef.registry = Настройте този регистър във вашия файл ~/.chef/config.rb: +pub.install = За да инсталирате пакета с Dart, изпълнете следната команда: +npm.details.tag = Маркер +npm.install = За да инсталирате пакета с npm, изпълнете следната команда: +maven.registry = Настройте този регистър във файла на вашия проект pom.xml: +debian.repository.components = Компоненти +debian.install = За да инсталирате пакета, изпълнете следната команда: +cran.install = За да инсталирате пакета, изпълнете следната команда: +cran.registry = Настройте този регистър във вашия файл Rprofile.site: +rpm.distros.redhat = на дистрибуции, базирани на RedHat alt.registry = Настройте този регистър от командния ред: +rpm.repository.architectures = Архитектури alt.registry.install = За да инсталирате пакета, изпълнете следната команда: -alt.install = Инсталиране на пакет alt.setup = Добавете хранилище към списъка със свързани хранилища (изберете необходимата архитектура вместо „_arch_“): alt.repository = Информация за хранилището -alt.repository.architectures = Архитектури +owner.settings.cargo.initialize.error = Неуспешно инициализиране на индекса на Cargo: %v +owner.settings.cargo.initialize = Инициализиране на индекс +settings.delete.description = Изтриването на пакет е трайно и не може да бъде отменено. alt.repository.multiple_groups = Този пакет е наличен в няколко групи. -swift.registry = Настройте този регистър от командния ред: +alt.repository.architectures = Архитектури +owner.settings.chef.title = Регистър на Chef +owner.settings.cleanuprules.remove.days = Премахване на версии, по-стари от +owner.settings.cleanuprules.keep.pattern = Запазване на версии, съответстващи на +owner.settings.cleanuprules.keep.count.n = %d версии на пакет +owner.settings.cleanuprules.keep.count.1 = 1 версия на пакет +owner.settings.cleanuprules.keep.count = Запазване на най-новите +owner.settings.cleanuprules.enabled = Включено +owner.settings.cleanuprules.preview.none = Правилото за почистване не съвпада с нито един пакет. +owner.settings.cleanuprules.none = Все още няма правила за почистване. +owner.settings.cleanuprules.add = Добавяне на правило за почистване +owner.settings.cleanuprules.title = Правила за почистване +owner.settings.cargo.rebuild.success = Индексът на Cargo беше успешно преизграден. +alpine.registry.key = Изтеглете публичния RSA ключ на регистъра в папката /etc/apk/keys/, за да проверите подписа на индекса: +alpine.registry.info = Изберете $branch и $repository от списъка по-долу. +arch.version.checkdepends = Зависимости за проверката +composer.dependencies = Зависимости swift.install = Добавете пакета във вашия файл Package.swift: +settings.link.error = Неуспешно обновяване на връзката на хранилището. swift.install2 = и изпълнете следната команда: -vagrant.install = За да добавите Vagrant box, изпълнете следната команда: +rpm.repository.multiple_groups = Този пакет е наличен в няколко групи. +conda.registry = Настройте този регистър като Conda хранилище във вашия файл .condarc: +conda.install = За да инсталирате пакета с Conda, изпълнете следната команда: +owner.settings.cargo.rebuild.error = Неуспешно преизграждане на индекса на Cargo: %v +owner.settings.cargo.rebuild = Преизграждане на индекс +settings.link.button = Обновяване на връзката на хранилището +settings.link.select = Изберете хранилище +debian.repository.architectures = Архитектури +rpm.registry = Настройте този регистър от командния ред: +debian.registry = Настройте този регистър от командния ред: +helm.install = За да инсталирате пакета, изпълнете следната команда: +swift.registry = Настройте този регистър от командния ред: 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 +debian.repository = Информация за хранилището +maven.install = За да използвате пакета, включете следното в блока dependencies във файла pom.xml: +nuget.install = За да инсталирате пакета с NuGet, изпълнете следната команда: +alt.install = Инсталиране на пакет +owner.settings.cleanuprules.edit = Редактиране на правилото за почистване +rpm.install = За да инсталирате пакета, изпълнете следната команда: +pypi.install = За да инсталирате пакета с pip, изпълнете следната команда: +arch.version.makedepends = Зависимости за изграждането +alpine.install = За да инсталирате пакета, изпълнете следната команда: +desc = Управление на пакетите на хранилището. +owner.settings.cargo.rebuild.no_index = Не може да се преизгради, няма инициализиран индекс. +owner.settings.cargo.rebuild.description = Преизграждането може да бъде полезно, ако индексът не е синхронизиран със съхранените Cargo пакети. +owner.settings.cargo.initialize.description = Необходимо е специално Git хранилище за индекс, за да се използва регистърът на Cargo. Използването на тази опция ще (пре)създаде хранилището и ще го конфигурира автоматично. +pypi.requires = Изисква Python +debian.registry.info = Изберете $distribution и $component от списъка по-долу. +alpine.registry = Настройте този регистър, като добавите URL адреса във вашия файл /etc/apk/repositories: +owner.settings.cargo.initialize.success = Индексът на Cargo беше успешно създаден. +npm.registry = Настройте този регистър във файла на вашия проект .npmrc: owner.settings.chef.keypair = Генериране на двойка ключове owner.settings.chef.keypair.description = Заявките, изпратени до регистъра на Chef, трябва да бъдат криптографски подписани като средство за удостоверяване. При генериране на двойка ключове, само публичният ключ се съхранява във Forgejo. Частният ключ ви се предоставя, за да се използва с knife. Генерирането на нова двойка ключове ще презапише предишната. +owner.settings.cleanuprules.remove.title = Версиите, които съответстват на тези правила, се премахват, освен ако правило по-горе не казва да се запазят. +nuget.registry = Настройте този регистър от командния ред: +owner.settings.cleanuprules.success.update = Правилото за почистване е обновено. +settings.delete.notice = На път сте да изтриете %s (%s). Тази операция е необратима, сигурни ли сте? +npm.install2 = или го добавете във файла package.json: +owner.settings.cleanuprules.success.delete = Правилото за почистване е изтрито. +vagrant.install = За да добавите Vagrant box, изпълнете следната команда: +nuget.dependency.framework = Целева платформа +maven.install2 = Изпълнете през командния ред: +maven.download = За да изтеглите зависимостта, изпълнете през командния ред: +container.layers = Слоеве на образа +conan.install = За да инсталирате пакета с Conan, изпълнете следната команда: +composer.registry = Настройте този регистър във вашия файл ~/.composer/config.json: [tool] hours = %d часа @@ -1663,7 +1660,7 @@ 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` +issues.force_push_codes = `изтласка принудително %[1]s от %[2]s %[8]s към %[4]s %[9]s %[6]s` pulls.blocked_by_official_review_requests = Тази заявка за сливане е блокирана, защото липсва одобрение от един или повече официални рецензенти. issues.tracker = Проследяване на времето issues.add_time_history = `добави изразходвано време %s` @@ -1698,178 +1695,177 @@ 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 = Трябва да изберете шаблонно хранилище +editor.cannot_edit_lfs_files = LFS файлове не могат да се редактират в уеб интерфейса. +commits.ssh_key_fingerprint = Отпечатък на SSH ключ +issues.comment_on_locked = Не можете да коментирате заключена задача. +commit.revert = Връщане 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“. +issues.choose.invalid_config = Конфигурацията на задачите съдържа грешки: +unit_disabled = Администраторът на сайта е изключил тази секция на хранилището. +issues.blocked_by_user = Не можете да създавате задачи в това хранилище, защото сте блокирани от притежателя на хранилището. 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 = сравняване +template.one_item = Трябва да изберете поне един елемент от шаблона +admin.failed_to_replace_flags = Неуспешна замяна на флаговете на хранилището +mirror_interval_invalid = Интервалът на огледалото не е валиден. +mirror_use_ssh.not_available = SSH удостоверяването не е налично. +mirror_address_desc = Поставете всички необходими данни за удостоверяване в секцията „Упълномощаване“. +template.git_hooks = Git куки +template.invalid = Трябва да изберете шаблонно хранилище +issues.review.outdated = Остарял +issues.dependency.add_error_dep_issue_not_exist = Зависимата задача не съществува. +template.items = Елементи на шаблона +issues.review.dismissed = отхвърли рецензията на %s %s +audio_not_supported_in_browser = Вашият браузър не поддържа HTML5 тага „audio“. +stored_lfs = Съхранено с Git LFS +commit_graph.select = Изберете клонове +issues.content_history.options = Опции +editor.commit_email = Ел. поща на подаването +commit.revert-header = Връщане: %s +commits.desc = Разглеждане на историята на промените в програмния код. +commits.search.tooltip = Можете да добавите префикс към ключовите думи с „author:“, „committer:“, „after:“ или „before:“, напр. „revert author:Alice before:2019-01-13“. +issues.unlock_error = Не може да се отключи задача, която не е заключена. +issues.lock.unknown_reason = Не може да се заключи задача с неизвестна причина. +issues.cancel_tracking_history = `отмени проследяването на времето %s` +issues.dependency.add_error_dep_not_same_repo = И двете задачи трябва да са в едно и също хранилище. +issues.review.remove_review_requests = премахна заявките за рецензия за %[1]s %[2]s +issues.review.content.empty = Трябва да оставите коментар, посочващ исканите промени. +issues.review.hide_outdated = Скриване на остарели pulls.desc = Включване на заявки за сливане и рецензии на код. +issues.review.show_outdated = Показване на остарели +ambiguous_runes_header = `Този файл съдържа двусмислени Уникод знаци` +admin.update_flags = Обновяване на флаговете +issues.dependency.blocked_by_short = Зависи от +mirror_lfs = Съхранение на големи файлове (LFS) +mirror_use_ssh.text = Използване на SSH удостоверяване +mirror_denied_combination = Не може да се използва удостоверяване с публичен ключ и парола едновременно. +rss.must_be_on_branch = Трябва да сте на клон, за да имате RSS емисия. +admin.manage_flags = Управление на флаговете +admin.enabled_flags = Флагове, включени за хранилището: +mirror_password_help = Променете потребителското име, за да изтриете запазена парола. +issues.review.remove_review_request = премахна заявката за рецензия за %[1]s %[2]s +issues.review.outdated_description = Съдържанието е променено, след като е направен този коментар +issues.dependency.setting = Включване на зависимости за задачи и заявки за сливане +issues.dependency.add_error_same_issue = Не можете да направите задача зависима от самата нея. +issues.review.self.rejection = Не можете да поискате промени в собствената си заявка за сливане. +issues.filter_type.all_pull_requests = Всички заявки за сливане +fork_to_different_account = Разклоняване в друг акаунт +mirror_sync_on_commit = Синхронизиране при изтласкване на подавания +mirror_address_protocol_invalid = Предоставеният URL е невалиден. Само http(s):// или git:// адреси могат да се използват за огледални хранилища. +template.git_hooks_tooltip = В момента не можете да променяте или премахвате Git куки, след като са добавени. Изберете това само ако се доверявате на шаблонното хранилище. +editor.commit_signed_changes = Подаване на подписани промени +editor.require_signed_commit = Клонът изисква подписано подаване +issues.desc = Организирайте доклади за грешки, задачи и етапи. +issues.lock_duplicate = Задача не може да бъде заключена два пъти. +issues.lock.notice_2 = - Вие и други сътрудници с достъп до това хранилище все още можете да оставяте коментари, които другите да виждат. +issues.due_date_invalid = Крайният срок е невалиден или извън обхвата. Моля, използвайте формата „гггг-мм-дд“. +mirror_interval = Интервал на огледалото (валидни единици за време са „h“, „m“, „s“). 0 за изключване на периодичната синхронизация. (Минимален интервал: %s) +summary_card_alt = Карта с обобщение на хранилище %s +file_copy_permalink = Копиране на постоянна връзка +view_git_blame = Преглед на git blame +commit.revert-content = Изберете клон, върху който да се върне: +issues.unlock.notice_1 = - Всеки ще може отново да коментира тази задача. +issues.delete.text = Наистина ли искате да изтриете тази задача? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) +issues.add_time_sum_to_small = Не е въведено време. +issues.dependency.no_permission_n = Нямате разрешение да прочетете %d зависимости +issues.review.pending.tooltip = Този коментар в момента не е видим за други потребители. За да изпратите изчакващите си коментари, изберете „%s“ -> „%s/%s/%s“ в горната част на страницата. +invisible_runes_description = `Този файл съдържа невидими Уникод знаци, които са неразличими за хората, но могат да бъдат обработени по различен начин от компютър. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` +video_not_supported_in_browser = Вашият браузър не поддържа HTML5 тага „video“. +editor.filename_help = Добавете директория, като въведете името ѝ, последвано от наклонена черта („/“). Премахнете директория, като натиснете backspace в началото на полето за въвеждане. +commits.view_single_diff = Преглед на промените в този файл, въведени в това подаване +issues.choose.ignore_invalid_templates = Невалидните шаблони са игнорирани +issues.due_date_form = гггг-мм-дд +issues.dependency.no_permission.can_remove = Нямате разрешение да прочетете тази зависимост, но можете да я премахнете +issues.review.remove_review_request_self = отказа да рецензира %s +mirror_use_ssh.helper = Forgejo ще създаде огледало на хранилището чрез Git през SSH и ще генерира двойка ключове за вас, когато изберете тази опция. Трябва да се уверите, че генерираният публичен ключ е упълномощен да изтласква към целевото хранилище. Не можете да използвате удостоверяване, базирано на парола, когато избирате това. +mirror_address_url_invalid = Предоставеният URL е невалиден. Трябва да екранирате правилно всички компоненти на URL адреса. +template.git_content = Git съдържание (стандартен клон) +ambiguous_runes_description = `Този файл съдържа Уникод знаци, които могат да бъдат объркани с други знаци. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` +issues.lock.notice_3 = - Винаги можете да отключите тази задача отново в бъдеще. +issues.lock.title = Заключване на обсъждането по тази задача. +issues.dependency.issue_batch_close_blocked = Не могат да бъдат затворени групово избраните задачи, защото задача #%d все още има отворени зависимости +issues.dependency.add_error_cannot_create_circular = Не можете да създадете зависимост с две задачи, които се блокират взаимно. +issues.review.add_review_requests = поиска рецензии от %[1]s %[2]s +comment.blocked_by_user = Коментирането не е възможно, защото сте блокирани от притежателя на хранилището или от автора. 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.switch_comparison_type = Превключване на типа сравнение +pulls.has_changed_since_last_review = Променено след последната ви рецензия +pulls.filter_branch = Филтриране на клон pulls.squash_merge_pull_request = Създаване на сплескано подаване +pulls.rebase_conflict_summary = Съобщение за грешка +pulls.auto_merge_button_when_succeed = (Когато проверките са успешни) +pulls.auto_merge_newly_scheduled_comment = `насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[1]s` +pulls.auto_merge_canceled_schedule_comment = `отмени автоматичното сливане на тази заявка за сливане, когато всички проверки са успешни %[1]s` 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.no_merge_helper = Включете опциите за сливане в настройките на хранилището или слейте заявката за сливане ръчно. +pulls.review_only_possible_for_full_diff = Рецензирането е възможно само при преглед на пълните разлики 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_canceled_schedule = Автоматичното сливане е отменено за тази заявка за сливане. +pulls.allow_edits_from_maintainers_err = Обновяването е неуспешно 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.outdated_with_base_branch = Този клон е остарял спрямо основния клон +pulls.update_not_allowed = Нямате разрешение да обновявате клона +pulls.wrong_commit_id = ID на подаването трябва да бъде ID на подаване в целевия клон +pulls.blocked_by_user = Не можете да създадете заявка за сливане в това хранилище, защото сте блокирани от притежателя на хранилището. +pulls.merge_conflict_summary = Съобщение за грешка +pulls.editable_explanation = Тази заявка за сливане позволява редакции от поддържащите. Можете да допринесете директно към нея. +pulls.allow_edits_from_maintainers_desc = Потребители с право на запис в основния клон могат също да изтласкват към този клон +pulls.merge_conflict = Сливането е неуспешно: Възникна конфликт по време на сливането. Подсказка: Опитайте различна стратегия +pulls.has_merged = Неуспешно: Заявката за сливане е слята, не можете да слеете отново или да промените целевия клон. +pulls.cmd_instruction_merge_warning = Предупреждение: Настройката „Автоматично откриване на ръчно сливане“ не е включена за това хранилище, ще трябва да отбележите тази заявка за сливане като ръчно слята след това. pulls.delete.title = Да се изтрие ли тази заявка за сливане? +pulls.push_rejected_no_message = Изтласкването е неуспешно: Изтласкването е отхвърлено, но няма отдалечено съобщение. Прегледайте Git куките за това хранилище +pulls.auto_merge_newly_scheduled = Заявката за сливане е насрочена за сливане, когато всички проверки са успешни. pulls.delete.text = Наистина ли искате да изтриете тази заявка за сливане? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) -diff.data_not_available = Съдържанието на разликите не е налично +pulls.auto_merge_has_pending_schedule = %[1]s насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[2]s. +pulls.auto_merge_when_succeed = Автоматично сливане, когато всички проверки са успешни +error.csv.invalid_field_count = Не може да се визуализира този файл, защото има грешен брой полета на ред %d. diff.bin = ДВОИЧЕН -diff.file_suppressed_line_too_long = Разликите във файла са потиснати, защото един или повече редове са твърде дълги -diff.too_many_files = Някои файлове не бяха показани, защото твърде много файлове имат промени в тези разлики -diff.show_more = Показване на още +release.add_tag_msg = Използване на заглавието и съдържанието на изданието като съобщение на маркера. +release.hide_archive_links_helper = Скрийте автоматично генерираните архиви с програмен код за това издание. Например, ако качвате свои собствени. +diff.data_not_available = Съдържанието на разликите не е налично +diff.has_escaped = Този ред има скрити Уникод знаци +branch.protected_deletion_failed = Клонът „%s“ е защитен. Не може да бъде изтрит. +branch.default_deletion_failed = Клонът „%s“ е стандартният клон. Не може да бъде изтрит. 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. +issues.role.member_helper = Този потребител е участник в организацията, притежаваща това хранилище. +diff.image.overlay = Наслагване +diff.image.swipe = Плъзгане +branch.included = Включен +diff.file_suppressed_line_too_long = Разликите във файла са потиснати, защото един или повече редове са твърде дълги error.broken_git_hook = Git куките на това хранилище изглеждат повредени. Моля, последвайте документацията, за да ги поправите, след което изтласкайте подавания, за да обновите статуса. +error.csv.unexpected = Не може да се визуализира този файл, защото съдържа неочакван знак на ред %d и колона %d. +topic.count_prompt = Не можете да изберете повече от 25 теми +release.hide_archive_links = Скриване на автоматично генерираните архиви +diff.show_more = Показване на още +diff.too_many_files = Някои файлове не бяха показани, защото твърде много файлове имат промени в тези разлики +diff.review.self_reject = Авторите на заявки за сливане не могат да поискват промени в собствените си заявки +branch.included_desc = Този клон е част от стандартния клон +diff.image.side_by_side = Едно до друго +release.summary_card_alt = Карта с обобщение на издание със заглавие „%s“ в хранилище %s +release.asset_external_url = Външен URL адрес +error.csv.too_large = Не може да се визуализира този файл, защото е твърде голям. [modal] confirm = Потвърждаване @@ -1979,37 +1975,36 @@ teams.no_desc = Този екип няма описание settings.delete_org_desc = Тази организация ще бъде изтрита перманентно. Продължаване? open_dashboard = Отваряне на таблото settings.change_orgname_prompt = Бележка: Промяната на името на организацията ще промени и URL адреса на вашата организация и ще освободи старото име. - -team_access_desc = Достъп до хранилище -team_unit_desc = Разрешаване на достъп до секции на хранилището +teams.add_duplicate_users = Потребителят вече е участник в екипа. 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 = Профилната снимка на организацията е обновена. +teams.invite_team_member.list = Чакащи покани +teams.remove_all_repos_desc = Това ще премахне всички хранилища от екипа. +form.create_org_not_allowed = Нямате разрешение да създавате организация. +form.name_pattern_not_allowed = Шаблонът „%s“ не е разрешен в име на организация. +members.invite_now = Поканване сега +teams.specific_repositories = Конкретни хранилища +teams.repos.none = Няма хранилища, до които този екип да има достъп. +teams.add_all_repos_title = Добавяне на всички хранилища settings.hooks_desc = Добавете уеб-куки, които ще се задействат за всички хранилища в тази организация. +teams.add_all_repos_desc = Това ще добави всички хранилища на организацията към екипа. +members.invite_desc = Добавяне на нов участник към %s: +members.private = Скрит +settings.change_orgname_redirect_prompt.with_cooldown.few = Старото име на организацията ще бъде достъпно за всички след период на изчакване от %[1]d дни. Все още можете да си върнете старото име по време на периода на изчакване. +team_access_desc = Достъп до хранилище +teams.specific_repositories_helper = Участниците ще имат достъп само до хранилища, изрично добавени към екипа. Избирането на това няма автоматично да премахне хранилища, вече добавени с Всички хранилища. +teams.delete_team_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 = Екипът има достъп до всички хранилища. Избирането на това ще добави всички съществуващи хранилища към екипа. +team_unit_desc = Разрешаване на достъп до секции на хранилището +settings.update_setting_success = Настройките на организацията са обновени. +settings.change_orgname_redirect_prompt = Старото име ще се пренасочва, докато не бъде взето. +teams.invite_team_member = Поканване в %s +teams.admin_access = Администраторски достъп +settings.change_orgname_redirect_prompt.with_cooldown.one = Старото име на организацията ще бъде достъпно за всички след период на изчакване от %[1]d ден. Все още можете да си върнете старото име по време на периода на изчакване. +teams.add_nonexistent_repo = Хранилището, което се опитвате да добавите, не съществува, моля, първо го създайте. teams.invite.by = Поканен от %s [install] @@ -2343,18 +2338,17 @@ TreeName = Път до файла AdminEmail = Администраторски адрес за ел. поща email_domain_is_not_allowed = Домейнът на адреса за ел. поща на потребителя %s е в конфликт с EMAIL_DOMAIN_ALLOWLIST или EMAIL_DOMAIN_BLOCKLIST. Уверете се, че сте въвели правилно адреса за ел. поща. email_been_used = Адресът за ел. поща вече се използва. - +unable_verify_ssh_key = Не може да се потвърди SSH ключът, проверете го отново за грешки. +enterred_invalid_owner_name = Името на новия притежател не е валидно. NewBranchName = Име на новия клон -git_ref_name_error = ` трябва да е правилно форматирано име на Git препратка.` +invalid_ssh_key = Не може да се потвърди вашият SSH ключ: %s +required_prefix = Въведеният текст трябва да започва с „%s“ 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“ +git_ref_name_error = ` трябва да е правилно форматирано име на Git препратка.` +last_org_owner = Не можете да премахнете последния потребител от екипа на „притежателите“. Трябва да има поне един притежател за организация. [action] close_issue = `затвори задача %[3]s#%[2]s` @@ -2374,7 +2368,7 @@ merge_pull_request = `сля заявка за сливане %[ auto_merge_pull_request = `сля автоматично заявка за сливане %[3]s#%[2]s` watched_repo = започна да наблюдава %[2]s delete_tag = изтри маркера %[2]s от %[3]s -delete_branch = изтри клона %[2]s от %[3]s +delete_branch = изтри клона %[2]s от %[3]s create_branch = създаде клон %[3]s на %[4]s publish_release = `публикува издание %[4]s на %[3]s` push_tag = изтласка маркер %[3]s към %[4]s @@ -2383,12 +2377,11 @@ reject_pull_request = `предложи промени за %[3] 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` +mirror_sync_delete = синхронизира и изтри препратка %[2]s на %[3]s от огледало [auth] tab_openid = OpenID @@ -2522,15 +2515,14 @@ 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.cancelled = Отменено status.running = Изпълнява се status.success = Успешно +status.waiting = Изчаква се +status.unknown = Неизвестно status.failure = Неуспешно -status.cancelled = Отменено status.skipped = Пропуснато +unit.desc = Управление на интегрирани CI/CD pipelines с Forgejo Actions. [heatmap] less = По-малко @@ -2613,31 +2605,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.generate_hash = Неуспешно генериране на хеш на подаването +error.extract_sign = Неуспешно извличане на подпис error.probable_bad_signature = ВНИМАНИЕ! Въпреки че има ключ с това ID в базата данни, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. +error.failed_retrieval_gpg_keys = Неуспешно извличане на ключ, свързан с акаунта на подаващия error.probable_bad_default_signature = ВНИМАНИЕ! Въпреки че ключът по подразбиране има това ID, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. +error.no_committer_account = Няма акаунт, свързан с адреса за ел. поща на подаващия + +[repo.permissions] +projects.read = Четене: Достъп до проектните табла на хранилището. +wiki.write = Писане: Създаване, обновяване и изтриване на страници в интегрираното уики. +issues.read = Четене: Четене и създаване на задачи и коментари. +pulls.read = Четене: Четене и създаване на заявки за сливане. +pulls.write = Писане: Затваряне на заявки за сливане и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. +projects.write = Писане: Създаване и редактиране на проекти и колони. +releases.read = Четене: Преглед и изтегляне на издания. +wiki.read = Четене: Четене на интегрираното уики и неговата история. +code.read = Четене: Достъп и клониране на кода на хранилището. +code.write = Писане: Изтласкване към хранилището, създаване на клонове и маркери. +issues.write = Писане: Затваряне на задачи и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. [units] -unit = Елемент error.no_unit_allowed_repo = Нямате разрешение за достъп до никоя секция на това хранилище. -error.unit_not_allowed = Нямате разрешение за достъп до тази секция на хранилището. +unit = Елемент +error.unit_not_allowed = Нямате разрешение за достъп до тази секция на хранилището. \ No newline at end of file diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 30fa95e6be..b079bc839a 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -340,7 +340,7 @@ no_reply_address=Skrytá e-mailová doména no_reply_address_helper=Název domény pro uživatele se skrytou e-mailovou adresou. Příklad: pokud je název skryté e-mailové domény nastaven na „noreply.example.org“, uživatelské jméno „joe“ bude zaznamenáno v Gitu jako „joe@noreply.example.org“. password_algorithm=Hashovací algoritmus hesla invalid_password_algorithm=Neplatný algoritmus hashe hesla -password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a sílu. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy. +password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a síly. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy. enable_update_checker=Povolit kontrolu aktualizací env_config_keys=Konfigurace prostředí env_config_keys_prompt=Následující proměnné prostředí budou také použity pro váš konfigurační soubor: @@ -468,7 +468,7 @@ email_domain_blacklisted=Nemůžete se registrovat s vaší e-mailovou adresou. authorize_application=Autorizovat aplikaci authorize_redirect_notice=Budete přesměrováni na %s, pokud autorizujete tuto aplikaci. authorize_application_created_by=Tuto aplikaci vytvořil %s. -authorize_application_description=Pokud povolíte přístup, bude moci přistupovat a zapisovat do všech vašich informací o účtu včetně soukromých repozitářů a organizací. +authorize_application_description=Pokud udělíte přístup, bude moci přistupovat a zapisovat do všech vašich informací o účtu včetně soukromých repozitářů a organizací. authorize_title=Autorizovat „%s“ pro přístup k vašemu účtu? authorization_failed=Autorizace selhala authorization_failed_desc=Autorizace selhala, protože jsme detekovali neplatný požadavek. Kontaktujte prosím správce aplikace, kterou jste se pokoušeli autorizovat. @@ -724,7 +724,7 @@ following_one = %d sledovaný followers.title.one = Sledující followers.title.few = Sledující following.title.one = Sleduje -following.title.few = Sleudje +following.title.few = Sleduje public_activity.visibility_hint.self_private = Vaše aktivita je viditelná pouze vám a správcům instance. Nastavení. public_activity.visibility_hint.admin_private = Tato aktivita je pro vás viditelná, protože jste administrátor, ale uživatel chce, aby zůstala soukromá. public_activity.visibility_hint.self_public = Vaše aktivita je viditelná všem, mimo interakcí v soukromých prostorech. Nastavení. @@ -828,7 +828,7 @@ activations_pending=Čekající aktivace can_not_add_email_activations_pending=Existuje čekající aktivace, zkuste to znovu za pár minut, pokud chcete přidat nový e-mail. delete_email=Smazat email_deletion=Odstranit e-mailovou adresu -email_deletion_desc=E-mailová adresa a přidružené informace budou z vašeho účtu odstraněny. Revize Gitu s touto e-mailovou adresou zůstanou nezměněny. Pokračovat? +email_deletion_desc=Tato e-mailová adresa a přidružené informace budou z vašeho účtu odstraněny. Revize Gitu s touto e-mailovou adresou zůstanou nezměněny. Pokračovat? email_deletion_success=E-mailová adresa byla odstraněna. theme_update_success=Váš motiv vzhledu byl aktualizován. theme_update_error=Vybraný motiv vzhledu neexistuje. @@ -1090,6 +1090,7 @@ regenerate_token_success = Token byl znovu vygenerován. Aplikace, které jej po regenerate_token = Resetovat access_token_regeneration = Znovu vygenerovat přístupový token access_token_regeneration_desc = Opětovným vygenerováním tokenu znemožníte přístup k vašemu účtu aplikacím, které jej používají. Tato akce je nevratná. Chcete pokračovat? +ssh_token_help_ssh_agent = nebo, pokud používáte agenta SSH (s nastavenou proměnnou SSH_AUTH_SOCK): [repo] new_repo_helper=Repozitář obsahuje všechny soubory projektu, včetně historie revizí. Už jej hostujete jinde? Migrovat repozitář. @@ -1155,7 +1156,7 @@ mirror_interval_invalid=Interval zrcadlení není platný. mirror_sync_on_commit=Synchronizovat při nahrávání revizí mirror_address=Klonovat z URL mirror_address_desc=Zadejte požadované přístupové údaje do sekce Ověření. -mirror_address_url_invalid=Poskytnutá URL je neplatná. Všechny části musíte správně nahradit escape sekvencí. +mirror_address_url_invalid=Zadaná adresa URL je neplatná. Ujistěte se, že jsou všechny části adresy správně escapované. mirror_address_protocol_invalid=Zadaná URL je neplatná. Mohou být zrcadleny pouze umístění http(s):// nebo git://. mirror_lfs=Úložiště velkých souborů (LFS) mirror_lfs_desc=Aktivovat zrcadlení dat LFS. @@ -1243,7 +1244,7 @@ migrate_items_releases=Vydání migrate_repo=Migrovat repozitář migrate.clone_address=Migrovat / klonovat z URL migrate.clone_address_desc=HTTP(S) nebo URL Git „clone“ existujícího repozitáře -migrate.github_token_desc=Můžete sem vložit jeden nebo více tokenů oddělených čárkou, abyste urychlili migraci kvůli omezení rychlosti rozhraní GitHub API. VAROVÁNÍ: Zneužití této funkce může vést k porušení zásad poskytovatele služeb a zablokování účtu. +migrate.github_token_desc=Sem můžete vložit jeden nebo více tokenů oddělených čárkami, abyste urychlili migraci obejitím omezení rychlosti rozhraní GitHub API. VAROVÁNÍ: Zneužití této funkce může vést k porušení zásad poskytovatele služeb a zablokování účtu. migrate.clone_local_path=nebo místní cesta serveru migrate.permission_denied=Není dovoleno importovat místní repozitáře. migrate.permission_denied_blocked=Nelze importovat z nepovolených hostitelů, prosím požádejte správce, aby zkontroloval nastavení ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. @@ -1763,7 +1764,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=`vynutil/a 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 %[8]s do %[4]s %[9]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í @@ -1875,7 +1876,7 @@ pulls.select_commit_hold_shift_for_range=Vyberte revizi. Podržte klávesu Shift pulls.review_only_possible_for_full_diff=Posouzení je možné pouze při zobrazení plného rozlišení pulls.filter_changes_by_commit=Filtrovat podle revize pulls.nothing_to_compare=Tyto větve jsou stejné. Není třeba vytvářet žádost o sloučení. -pulls.nothing_to_compare_have_tag = Vybraná větev a značka jsou shodné. +pulls.nothing_to_compare_have_tag = Vybrané větve a značky jsou shodné. pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tato žádost o sloučení bude prázdná. pulls.has_pull_request=`Žádost o sloučení mezi těmito větvemi již existuje: %[2]s#%[3]d` pulls.create=Vytvořit žádost o sloučení @@ -2023,7 +2024,7 @@ milestones.filter_sort.most_issues=Nejvíce problémů milestones.filter_sort.least_issues=Nejméně problémů signing.will_sign=Tato revize bude podepsána klíčem „%s“. -signing.wont_sign.error=Došlo k chybě při kontrole, zda může být revize podepsána. +signing.wont_sign.error=Při kontrole, zda může být revize podepsána, došlo k chybě. signing.wont_sign.nokey=Tato instance nemá žádný klíč k podepsání této revize. signing.wont_sign.never=Revize nebudou nikdy podepsány. signing.wont_sign.always=Revize budou vždy podepsány. @@ -3636,7 +3637,7 @@ auto_merge_pull_request=`automaticky sloučen požadavek na natažení %s push_tag=nahrál/a značku %[3]s do %[4]s delete_tag=smazal/a značku %[2]s z %[3]s -delete_branch=smazal/a větev %[2]s z %[3]s +delete_branch=smazal/a větev %[2]s z %[3]s compare_branch=Porovnat compare_commits=Porovnat %d revizí compare_commits_general=Porovnat revize @@ -3834,7 +3835,7 @@ owner.settings.cargo.initialize.error=Nepodařilo se inicializovat Cargo index: owner.settings.cargo.initialize.success=Index Cargo byl úspěšně vytvořen. owner.settings.cargo.rebuild=Znovu vytvořit index owner.settings.cargo.rebuild.error=Obnovení Cargo indexu se nezdařilo: %v -owner.settings.cargo.rebuild.success=Cargo Index byl úspěšně obnoven. +owner.settings.cargo.rebuild.success=Index Cargo byl úspěšně znovu sestaven. owner.settings.cleanuprules.title=Pravidla čištění owner.settings.cleanuprules.add=Přidat pravidlo pro čištění owner.settings.cleanuprules.edit=Upravit pravidlo pro čištění @@ -3991,7 +3992,7 @@ variables.update.success=Proměnná byla upravena. runners.none = Nejsou dostupné žádné runnery runs.workflow = Workflow runners = Runnery -runs.pushed_by = pushnuto uživatelem +runs.pushed_by = pushnuta uživatelem need_approval_desc = Potřebovat schválení pro spouštění workflowů pro žádosti o sloučení forků. runners.runner_manage_panel = Správa runnerů runs.no_job_without_needs = Workflow musí obsahovat alespoň jednu práci bez závislostí. diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index 99b5789de3..179ceb07ca 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -1,3 +1,6 @@ + + + [common] home = Hjem dashboard = Instrumentpanel @@ -1693,7 +1696,7 @@ issues.due_date_overdue = Forfalden issues.lock.unknown_reason = Kan ikke låse et problem med en ukendt årsag. issues.lock.notice_3 = - Du kan altid låse op for dette problem igen i fremtiden. issues.push_commits_n = tilføjet %d commits %s -issues.force_push_codes = `force-pushed %[1]s fra %[2]s til %[4]s %[6]s` +issues.force_push_codes = `force-pushed %[1]s fra %[2]s %[8]s til %[4]s %[9]s %[6]s` issues.force_push_compare = Sammenlign issues.due_date_form = yyyy-mm-dd issues.due_date_form_edit = Redigere @@ -2765,7 +2768,7 @@ 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 +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` diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 9f55f79fe9..937d8d90e2 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -633,7 +633,7 @@ repository_files_already_exist=Dateien für dieses Repository sind bereits vorha repository_files_already_exist.adopt=Dateien für dieses Repository existieren bereits und können nur übernommen werden. repository_files_already_exist.delete=Dateien für dieses Repository sind bereits vorhanden. Du must sie löschen. repository_files_already_exist.adopt_or_delete=Dateien für dieses Repository existieren bereits. Du musst sie entweder übernehmen oder löschen. -visit_rate_limit=Das Rate-Limit bei der Gegenseite wurde erreicht. +visit_rate_limit=Die Ratenbegrenzung bei der Gegenseite wurde erreicht. 2fa_auth_required=Die Gegenseite benötigt Zweifaktorauthentifikation. org_name_been_taken=Der Organisationsname ist bereits vergeben. team_name_been_taken=Der Teamname ist bereits vergeben. @@ -830,7 +830,7 @@ activations_pending=Aktivierung ausstehend can_not_add_email_activations_pending=Es gibt eine ausstehende Aktivierung, versuche es in ein paar Minuten erneut, wenn du eine neue E-Mail hinzufügen möchtest. delete_email=Löschen email_deletion=E-Mail-Adresse löschen -email_deletion_desc=Die E-Mail-Adresse und die damit verbundenen Informationen werden von deinem Konto entfernt. Git-Commits von dieser E-Mail-Addresse bleiben unverändert. Fortfahren? +email_deletion_desc=Diese E-Mail-Adresse und die damit verbundenen Informationen werden von deinem Konto entfernt. Git-Commits von dieser E-Mail-Addresse bleiben unverändert. Fortfahren? email_deletion_success=Die E-Mail-Adresse wurde entfernt. theme_update_success=Deine Theme-Auswahl wurde gespeichert. theme_update_error=Das ausgewählte Theme existiert nicht. @@ -885,9 +885,9 @@ ssh_key_verified=Verifizierter Schlüssel ssh_key_verified_long=Der Schlüssel wurde mit einem Token verifiziert. Er kann verwendet werden, um Commits zu verifizieren, die mit irgendeiner für diesen Nutzer aktivierten E-Mail-Adresse und irgendeiner Identität dieses Schlüssels übereinstimmen. ssh_key_verify=Verifizieren ssh_invalid_token_signature=Der gegebene SSH-Schlüssel, Signatur oder Token stimmen nicht überein oder der Token ist veraltet. -ssh_token_required=Du musst eine Signatur für den Token unten angeben +ssh_token_required=Sie müssen eine Signatur für das Token unten angeben ssh_token=Token -ssh_token_help=Du kannst eine Signatur wie folgt generieren: +ssh_token_help=Sie können eine Signatur wie folgt generieren: ssh_token_signature=SSH-Textsignatur (armored signature) key_signature_ssh_placeholder=Beginnt mit „-----BEGIN SSH SIGNATURE-----“ verify_ssh_key_success=SSH-Key „%s“ wurde verifiziert. @@ -1092,6 +1092,7 @@ regenerate_token_success = Der Token wurde regeneriert. Anwendungen, die ihn ben access_token_regeneration = Zugangstoken regenerieren access_token_regeneration_desc = Einen Token zu regenerieren, wird den Zugriff auf deinen Account von Anwendungen, die ihn nutzen, zurückziehen. Dies kann nicht rückgängig gemacht werden. Fortsetzen? regenerate_token = Regenerieren +ssh_token_help_ssh_agent = , oder, falls Sie einen SSH-Agenten benutzen (mit der Variable SSH_AUTH_SOCK gesetzt): [repo] owner=Besitzer @@ -1154,7 +1155,7 @@ mirror_interval_invalid=Das Spiegel-Intervall ist ungültig. mirror_sync_on_commit=Synchronisieren, wenn Commits gepusht wurden mirror_address=Klonen via URL mirror_address_desc=Gib alle erforderlichen Anmeldedaten im Abschnitt „Authentifizierung“ ein. -mirror_address_url_invalid=Die angegebene URL ist ungültig. Achte darauf, alle Komponenten der URL korrekt zu maskieren. +mirror_address_url_invalid=Die angegebene URL ist ungültig. Achte darauf, dass alle Komponenten der URL korrekt escaped wurden. mirror_address_protocol_invalid=Die angegebene URL ist ungültig. Nur Orte mit „http(s)://“ oder „git://“ können fürs Spiegeln benutzt werden. mirror_lfs=Großdatei-Speicher (LFS) mirror_lfs_desc=Spiegeln von LFS-Dateien aktivieren. @@ -1243,7 +1244,7 @@ migrate_items_releases=Releases migrate_repo=Repository migrieren migrate.clone_address=Migrations-/Klon-URL migrate.clone_address_desc=Die HTTP(S)- oder „git clone“-URL eines bereits existierenden Repositorys -migrate.github_token_desc=Du kannst hier ein oder mehrere Token durch Komma getrennt eintippen, um die Migration aufgrund der GitHub-API-Ratenlimitierung zu beschleunigen. WARNUNG: Der Missbrauch dieser Funktion kann gegen die Richtlinien des Diensteanbieters verstoßen und zur Kontosperrung führen. +migrate.github_token_desc=Du kannst hier ein oder mehrere Tokens durch Komma getrennt eingeben, um die Migration schneller zu machen, indem die GitHub-API-Ratenbegrenzung umgangen wird. WARNUNG: Der Missbrauch dieser Funktion kann gegen die Richtlinien des Diensteanbieters verstoßen und zur Sperrung deines Kontos bzw. deiner Konten führen. migrate.clone_local_path=oder ein lokaler Serverpfad migrate.permission_denied=Du hast keine Berechtigung zum Importieren lokaler Repositorys. migrate.permission_denied_blocked=Du kannst von keinen nicht erlaubten Hosts importieren. Bitte fragen deinen Administrator, die Einstellungen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS zu überprüfen. @@ -1515,7 +1516,7 @@ issues.filter_assignees=Verantwortliche filtern issues.filter_milestones=Meilenstein filtern issues.filter_projects=Projekt filtern issues.filter_labels=Label filtern -issues.filter_reviewers=Sichter filtern +issues.filter_reviewers=Prüfer filtern issues.new=Neues Issue issues.new.title_empty=Der Titel kann nicht leer sein issues.new.labels=Labels @@ -1535,7 +1536,7 @@ issues.new.closed_milestone=Geschlossene Meilensteine issues.new.assignees=Zuständige issues.new.clear_assignees=Zuständige entfernen issues.new.no_assignees=Niemand zuständig -issues.new.no_reviewers=Keine Sichter +issues.new.no_reviewers=Keine Prüfer issues.choose.get_started=Los geht's issues.choose.open_external_link=Öffnen issues.choose.blank=Standard @@ -1599,7 +1600,7 @@ issues.filter_type.assigned_to_you=Dir zugewiesen issues.filter_type.created_by_you=Von dir erstellt issues.filter_type.mentioning_you=Hat dich erwähnt issues.filter_type.review_requested=Sichtung angefordert -issues.filter_type.reviewed_by_you=Von dir gesichtet +issues.filter_type.reviewed_by_you=Von dir überprüft issues.filter_sort=Sortieren issues.filter_sort.latest=Neueste issues.filter_sort.oldest=Älteste @@ -1654,7 +1655,7 @@ issues.create_comment=Kommentieren issues.closed_at=`hat dieses Issue %s geschlossen` issues.reopened_at=`hat dieses Issue %s wieder geöffnet` issues.commit_ref_at=`hat dieses Issue %s aus einem Commit referenziert` -issues.ref_issue_from=`hat %[1]s auf dieses Issue verwiesen %[3]s` +issues.ref_issue_from=`auf dieses Issue verwiesen %[3]s %[1]s` issues.ref_pull_from=`referenzierte diesen Pull-Request %[3]s %[1]s` issues.ref_closing_from=`referenzierte dieses Issue aus einem Pull-Request %[3]s der es schließen wird, %[1]s` issues.ref_reopening_from=`referenzierte dieses Issue aus einem Pull-Request %[3]s der es wieder öffnen wird, %[1]s` @@ -1762,7 +1763,7 @@ issues.error_modifying_due_date=Fehler beim Ändern des Fälligkeitsdatums. issues.error_removing_due_date=Fehler beim Entfernen des Fälligkeitsdatums. issues.push_commit_1=hat %d Commit %s hinzugefügt issues.push_commits_n=hat %d Commits %s hinzugefügt -issues.force_push_codes=`hat %[6]s %[1]s von %[2]s zu %[4]s force-gepusht` +issues.force_push_codes=`hat %[6]s %[1]s von %[2]s %[8]s zu %[4]s %[9]s force-gepusht` issues.force_push_compare=Vergleichen issues.due_date_form=JJJJ-MM-TT issues.due_date_form_add=Fälligkeitsdatum hinzufügen @@ -1809,7 +1810,7 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen. issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen. issues.review.approve=hat die Änderungen %s genehmigt -issues.review.comment=hat %s gesichtet +issues.review.comment=hat %s überprüft issues.review.dismissed=verwarf %ss Review %s issues.review.dismissed_label=Verworfen issues.review.left_comment=hat einen Kommentar hinterlassen @@ -1822,7 +1823,7 @@ issues.review.remove_review_request_self=hat die Sichtung %s verweigert issues.review.pending=Ausstehend issues.review.pending.tooltip=Dieser Kommentar ist derzeit nicht für andere Benutzer sichtbar. Um deine ausstehenden Kommentare einzureichen, wähle „%s“ -> „%s/%s/%s“ oben auf der Seite. issues.review.review=Review -issues.review.reviewers=Sichter +issues.review.reviewers=Prüfer issues.review.outdated=Veraltet issues.review.outdated_description=Der Inhalt hat sich geändert, seit dieser Kommentar abgegeben wurde issues.review.option.show_outdated_comments=Veraltete Kommentare anzeigen @@ -1905,8 +1906,8 @@ pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht e pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen. pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin zusammenführen. pulls.blocked_by_approvals=Dieser Pull-Request hat noch nicht genügend Genehmigungen. %d von %d Genehmigungen erteilt. -pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Sichter angefragt wurden. -pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Sichtern fehlt. +pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Prüfer angefragt wurden. +pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Prüfern fehlt. pulls.blocked_by_outdated_branch=Dieser Pull-Request ist blockiert, da er veraltet ist. pulls.blocked_by_changed_protected_files_1=Dieser Pull-Request ist blockiert, weil er eine geschützte Datei ändert: pulls.blocked_by_changed_protected_files_n=Dieser Pull-Request ist blockiert, weil er geschützte Dateien ändert: @@ -2470,8 +2471,8 @@ settings.protect_required_approvals=Erforderliche Genehmigungen settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Sichtungen. settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Positivliste beschränken settings.protect_approvals_whitelist_enabled_desc=Nur Sichtungen von Benutzern oder Teams auf der Positivliste zählen zu den erforderlichen Genehmigungen. Existiert keine Positivliste, so zählen Sichtungen von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. -settings.protect_approvals_whitelist_users=Nutzer, die sichten dürfen -settings.protect_approvals_whitelist_teams=Teams, die sichten dürfen +settings.protect_approvals_whitelist_users=Autorisierte Prüfer +settings.protect_approvals_whitelist_teams=Teams, autorisiert zum Prüfen settings.dismiss_stale_approvals=Entferne alte Genehmigungen settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt. settings.require_signed_commits=Signierte Commits erforderlich @@ -2490,7 +2491,7 @@ settings.remove_protected_branch_failed=Entfernen der Branchschutzregel „%s“ settings.protected_branch_deletion=Branch-Schutz löschen settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren? settings.block_rejected_reviews=Zusammenführung bei abgelehnten Sichtungen blockieren -settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Sichter angefragt werden, auch wenn genügend Genehmigungen existieren. +settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Prüfer angefragt werden, auch wenn genügend Genehmigungen existieren. settings.block_on_official_review_requests=Merge bei offiziellen Sichtungsanfragen blockieren settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Sichtungsanfrangen vorliegen, selbst wenn genügend Genehmigungen existieren. settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist @@ -2757,7 +2758,7 @@ generated = Erzeugt editor.invalid_commit_mail = Ungültige E-Mail für die Erstellung eines Commits. commits.renamed_from = Umbenannt von %s commits.browse_further = Weiter browsen -pulls.nothing_to_compare_have_tag = Der gewählte Branch/Tag ist gleich. +pulls.nothing_to_compare_have_tag = Die gewählten Branches/Tags sind gleich. pulls.status_checks_hide_all = Alle Prüfungen verbergen pulls.status_checks_show_all = Alle Prüfungen anzeigen pulls.cmd_instruction_hint = Anweisungen für die Kommandozeile betrachten @@ -2902,7 +2903,7 @@ issues.reaction.alt_add = Füge %[1]s Reaktion zum Kommentar hinzu. issues.reaction.alt_remove = Entferne %[1]s Reaktion von diesem Kommentar. summary_card_alt = Zusammenfassungskarte des Repositorys %s release.summary_card_alt = Übersichtskarte eines Releases mit dem Titel „%s“ im Repository %s -archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht gesichtet werden. +archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht überprüft werden. editor.commit_email = Commit-E-Mail commits.view_single_diff = Änderungen an dieser Datei, die in diesem Commit eingeführt wurden, betrachten pulls.editable = Bearbeitbar @@ -3124,7 +3125,7 @@ dashboard.resync_all_hooks=Die „pre-receive“-, „update“- und „post-rec dashboard.reinit_missing_repos=Alle Git-Repositorys neu einlesen, für die Einträge existieren dashboard.sync_external_users=Externe Benutzerdaten synchronisieren dashboard.cleanup_hook_task_table=Hook-Task-Tabelle bereinigen -dashboard.cleanup_packages=Veraltete Pakete löschen +dashboard.cleanup_packages=Veraltete Pakete bereinigen dashboard.cleanup_actions=Abgelaufene Logs und Artefakte von Actions bereinigen dashboard.server_uptime=Server-Uptime dashboard.current_goroutine=Aktuelle Goroutinen @@ -3640,7 +3641,7 @@ auto_merge_pull_request=`führte Pull-Request %[3]s#%[2]s au transfer_repo=hat Repository %s übertragen zu %s push_tag=hat Tag %[3]s auf %[4]s gepusht delete_tag=hat Tag %[2]s in %[3]s gelöscht -delete_branch=hat Branch %[2]s in %[3]s gelöscht +delete_branch=hat Branch %[2]s in %[3]s gelöscht compare_branch=Vergleichen compare_commits=Vergleiche %d Commits compare_commits_general=Commits vergleichen diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 41c55beb11..a33117db22 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -1737,7 +1737,7 @@ issues.error_modifying_due_date=Προέκυψε σφάλμα κατά την α issues.error_removing_due_date=Προέκυψε σφάλμα κατά την κατάργηση ημερομηνίας παράδοσης. issues.push_commit_1=πρόσθεσε %d υποβολή %s issues.push_commits_n=πρόσθεσε %d υποβολές %s -issues.force_push_codes=`έκανε force-push %[1]s από το %[2]s στο %[4]s %[6]s` +issues.force_push_codes=`έκανε force-push %[1]s από το %[2]s %[8]s στο %[4]s %[9]s %[6]s` issues.force_push_compare=Σύγκριση issues.due_date_form=εεεε-μμ-ηη issues.due_date_form_add=Προσθήκη ημερομηνίας παράδοσης @@ -3563,7 +3563,7 @@ auto_merge_pull_request=`αυτόματη συγχώνευση του pull reque transfer_repo=μετέφερε το repository %s σε %s push_tag=ώθησε την ετικέτα %[3]s σε %[4]s delete_tag=διέγραψε την ετικέτα %[2]s από %[3]s -delete_branch=διέγραψε το κλάδο %[2]s από %[3]s +delete_branch=διέγραψε το κλάδο %[2]s από %[3]s compare_branch=Σύγκριση compare_commits=Σύγκριση %d commit compare_commits_general=Σύγκριση commits diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5fd2ebd163..1dc6d38c8c 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -372,7 +372,7 @@ no_reply_address = Hidden email domain no_reply_address_helper = Domain name for users with a hidden email address. For example, the username "joe" will be logged in Git as "joe@noreply.example.org" if the hidden email domain is set to "noreply.example.org". password_algorithm = Password hash algorithm invalid_password_algorithm = Invalid password hash algorithm -password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems. +password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strengths. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems. enable_update_checker = Enable update checker env_config_keys = Environment Configuration env_config_keys_prompt = The following environment variables will also be applied to your configuration file: @@ -479,7 +479,7 @@ email_domain_blacklisted = You cannot register with your email address. authorize_application = Authorize Application authorize_redirect_notice = You will be redirected to %s if you authorize this application. authorize_application_created_by = This application was created by %s. -authorize_application_description = If you grant the access, it will be able to access and write to all your account information, including private repos and organizations. +authorize_application_description = If you grant access, it will be able to access and write to all your account information, including private repos and organizations. authorize_title = Authorize "%s" to access your account? authorization_failed = Authorization failed authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you have tried to authorize. @@ -838,7 +838,7 @@ activations_pending = Activations pending can_not_add_email_activations_pending = There is a pending activation, try again in a few minutes if you want to add a new email. delete_email = Remove email_deletion = Remove email address -email_deletion_desc = The email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue? +email_deletion_desc = This email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue? email_deletion_success = The email address has been removed. theme_update_success = Your theme was updated. theme_update_error = The selected theme does not exist. @@ -895,6 +895,7 @@ ssh_invalid_token_signature = The provided SSH key, signature or token do not ma ssh_token_required = You must provide a signature for the below token ssh_token = Token ssh_token_help = You can generate a signature using: +ssh_token_help_ssh_agent = or, if you're using an SSH agent (with the SSH_AUTH_SOCK variable set): ssh_token_signature = Armored SSH signature key_signature_ssh_placeholder = Begins with "-----BEGIN SSH SIGNATURE-----" verify_ssh_key_success = SSH key "%s" has been verified. @@ -1056,7 +1057,7 @@ user_block_success = The user has been blocked successfully. user_block_yourself = You cannot block yourself. quota.applies_to_user = The following quota rules apply to your account -quota.applies_to_org = The following quota rules apply to this organisation +quota.applies_to_org = The following quota rules apply to this organization quota.rule.exceeded = Exceeded quota.rule.exceeded.helper = The total size of objects for this rule has exceeded the quota. quota.rule.no_limit = Unlimited @@ -1153,7 +1154,7 @@ mirror_sync = synced mirror_sync_on_commit = Sync when commits are pushed mirror_address = Clone from URL mirror_address_desc = Put any required credentials in the Authorization section. -mirror_address_url_invalid = The provided URL is invalid. You must escape all components of the URL correctly. +mirror_address_url_invalid = The provided URL is invalid. Make sure that components of the URL are escaped correctly. mirror_address_protocol_invalid = The provided URL is invalid. Only http(s):// or git:// locations can be used for mirroring. mirror_lfs = Large File Storage (LFS) mirror_lfs_desc = Activate mirroring of LFS data. @@ -1251,7 +1252,7 @@ migrate_repo = Migrate repository migrate.repo_desc_helper = Leave empty to import existing description migrate.clone_address = Migrate / Clone from URL migrate.clone_address_desc = The HTTP(S) or Git "clone" URL of an existing repository -migrate.github_token_desc = You can put one or more tokens with comma separated here to make migrating faster because of GitHub API rate limit. WARN: Abusing this feature may violate the service provider's policy and lead to account blocking. +migrate.github_token_desc = You can put one or more tokens here separated by commas to make migrating faster by circumventing the GitHub API rate limit. WARNING: Abusing this feature may violate the service provider's policy and may lead to getting your account(s) blocked. migrate.clone_local_path = or a local server path migrate.permission_denied = You are not allowed to import local repositories. migrate.permission_denied_blocked = You cannot import from disallowed hosts, please ask the admin to check ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS settings. @@ -1803,7 +1804,7 @@ issues.time_spent_from_all_authors = `Total time spent: %s` issues.due_date = Due date issues.push_commit_1 = added %d commit %s issues.push_commits_n = added %d commits %s -issues.force_push_codes = `force-pushed %[1]s from %[2]s to %[4]s %[6]s` +issues.force_push_codes = `force-pushed %[1]s from %[2]s %[8]s to %[4]s %[9]s %[6]s` issues.force_push_compare = Compare issues.due_date_form = yyyy-mm-dd issues.due_date_form_edit = Edit @@ -1882,7 +1883,6 @@ issues.content_history.created = created issues.content_history.delete_from_history = Delete from history issues.content_history.delete_from_history_confirm = Delete from history? issues.content_history.options = Options -issues.reference_link = Reference: %s issues.blocked_by_user = You cannot create issues in this repository because you are blocked by the repository owner. comment.blocked_by_user = Commenting is not possible because you are blocked by the repository owner or the author. issues.reopen.blocked_by_user = You cannot reopen this issue because you are blocked by the repository owner or the poster of this issue. @@ -1920,7 +1920,7 @@ pulls.select_commit_hold_shift_for_range = Select commit. Hold shift + click to pulls.review_only_possible_for_full_diff = Review is only possible when viewing the full diff pulls.filter_changes_by_commit = Filter by commit pulls.nothing_to_compare = These branches are equal. There is no need to create a pull request. -pulls.nothing_to_compare_have_tag = The selected branch/tag are equal. +pulls.nothing_to_compare_have_tag = The selected branches/tags are equal. pulls.nothing_to_compare_and_allow_empty_pr = These branches are equal. This PR will be empty. pulls.has_pull_request = `A pull request between these branches already exists: %[2]s#%[3]d` pulls.create = Create pull request @@ -2089,7 +2089,7 @@ milestones.filter_sort.most_issues = Most issues milestones.filter_sort.least_issues = Least issues signing.will_sign = This commit will be signed with key "%s". -signing.wont_sign.error = There was an error whilst checking if the commit could be signed. +signing.wont_sign.error = There was an error while checking if the commit could be signed. signing.wont_sign.nokey = This instance has no key to sign this commit with. signing.wont_sign.never = Commits are never signed. signing.wont_sign.always = Commits are always signed. @@ -2852,7 +2852,6 @@ find_file.no_matching = No matching file found error.csv.too_large = Can't render this file because it is too large. error.csv.unexpected = Can't render this file because it contains an unexpected character in line %d and column %d. error.csv.invalid_field_count = Can't render this file because it has a wrong number of fields in line %d. -error.broken_git_hook = Git hooks of this repository seem to be broken. Please follow the documentation to fix them, then push some commits to refresh the status. [repo.permissions] code.read = Read: Access and clone the code of the repository. @@ -3066,9 +3065,9 @@ dashboard.resync_all_sshprincipals = Update the ".ssh/authorized_principals" fil dashboard.resync_all_hooks = Resynchronize pre-receive, update and post-receive hooks of all repositories dashboard.reinit_missing_repos = Reinitialize all missing Git repositories for which records exist dashboard.sync_external_users = Synchronize external user data -dashboard.cleanup_hook_task_table = Cleanup hook_task table -dashboard.cleanup_packages = Cleanup expired packages -dashboard.cleanup_actions = Cleanup expired logs and artifacts from actions +dashboard.cleanup_hook_task_table = Clean up hook_task table +dashboard.cleanup_packages = Clean up expired packages +dashboard.cleanup_actions = Clean up expired logs and artifacts from actions dashboard.server_uptime = Server uptime dashboard.current_goroutine = Current goroutines dashboard.current_memory_usage = Current memory usage @@ -3564,7 +3563,7 @@ auto_merge_pull_request = `automatically merged pull request %[3 transfer_repo = transferred repository %s to %s push_tag = pushed tag %[3]s to %[4]s delete_tag = deleted tag %[2]s from %[3]s -delete_branch = deleted branch %[2]s from %[3]s +delete_branch = deleted branch %[2]s from %[3]s compare_branch = Compare compare_commits = Compare %d commits compare_commits_general = Compare commits @@ -3798,7 +3797,7 @@ owner.settings.cargo.initialize.success = The Cargo index was successfully creat owner.settings.cargo.rebuild = Rebuild index owner.settings.cargo.rebuild.description = Rebuilding can be useful if the index is not synchronized with the stored Cargo packages. owner.settings.cargo.rebuild.error = Failed to rebuild Cargo index: %v -owner.settings.cargo.rebuild.success = The Cargo index was successfully rebuild. +owner.settings.cargo.rebuild.success = The Cargo index was successfully rebuilt. owner.settings.cargo.rebuild.no_index = Cannot rebuild, no index is initialized. owner.settings.cleanuprules.title = Cleanup rules owner.settings.cleanuprules.add = Add cleanup rule diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index c783b1605b..f6b2459f51 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -1759,7 +1759,7 @@ issues.error_modifying_due_date=Fallo al modificar la fecha de vencimiento. issues.error_removing_due_date=Fallo al eliminar la fecha de vencimiento. issues.push_commit_1=añadió %d commit %s issues.push_commits_n=añadió %d commits %s -issues.force_push_codes=`empujó forzosamente %[1]s de %[2]s a %[4]s %[6]s` +issues.force_push_codes=`empujó forzosamente %[1]s de %[2]s %[8]s a %[4]s %[9]s %[6]s` issues.force_push_compare=Comparar issues.due_date_form=aaaa-mm-dd issues.due_date_form_add=Añadir fecha de vencimiento @@ -2887,7 +2887,6 @@ pulls.delete_after_merge.head_branch.is_default = La rama actual que desea elimi summary_card_alt = Tarjeta de resumen del repositorio %s settings.pull_mirror_sync_quota_exceeded = Cuota excedida, no se empujan los cambios. archive.nocomment = No es posible hacer comentarios porque el repositorio está archivado. - sync_fork.branch_behind_one = Esta rama esta %[1]d cambios detrás de %[2]s sync_fork.branch_behind_few = Esta rama está %[1]d confirmaciones detrás de %[2]s @@ -3589,7 +3588,7 @@ auto_merge_pull_request=`fusionado automáticamente pull request %s a %s push_tag=hizó push la etiqueta %[3]s a %[4]s delete_tag=etiqueta eliminada %[2]s de %[3]s -delete_branch=rama %[2]s eliminada, de %[3]s +delete_branch=rama %[2]s eliminada, de %[3]s compare_branch=Comparar compare_commits=Comparar %d commits compare_commits_general=Comparar commits diff --git a/options/locale/locale_et.ini b/options/locale/locale_et.ini index 441a7d8e07..79361cf43d 100644 --- a/options/locale/locale_et.ini +++ b/options/locale/locale_et.ini @@ -1,67 +1,67 @@ [common] tracked_time_summary = Kokkuvõte jälgitavast ajast, mis põhineb probleemide nimekirja filtritel -your_settings = Seaded +your_settings = Seadistused home = Avaleht -dashboard = Armatuurlaud -explore = Uurige -help = Abi +dashboard = Töölaud +explore = Uuri +help = Abiteave logo = Logo sign_in = Logi sisse -sign_in_with_provider = Logi sisse koos %s +sign_in_with_provider = Logi sisse kasutajakontoga: %s sign_in_or = või -sign_out = Registreeru välja +sign_out = Logi välja sign_up = Registreeru -link_account = Lingi konto +link_account = Lingi väline kasutajakonto register = Registreeru version = Versioon page = Lehekülg template = Mall language = Keel -notifications = Teated +notifications = Teavitused active_stopwatch = Aktiivne aja jälgimine create_new = Loo… -user_profile_and_more = Profiil ja seaded… +user_profile_and_more = Profiil ja seadistused… signed_in_as = Sisselogitud kui -enable_javascript = See veebileht nõuab JavaScripti. +enable_javascript = See veebileht eeldab JavaScripti kasutamise lubamist. toc = Sisukord licenses = Litsentsid username = Kasutajanimi -webauthn_error_unable_to_process = Server ei saanud teie taotlust töödelda. -webauthn_error_duplicated = Turvalisuse võti ei ole selle taotluse puhul lubatud. Palun veenduge, et võti ei ole juba registreeritud. +webauthn_error_unable_to_process = Server ei saanud sinu päringut töödelda. +webauthn_error_duplicated = Turvavõti ei ole selle päringu puhul lubatud. Palun veendu, et võti ei ole juba registreeritud. return_to_forgejo = Tagasi Forgejo'sse -toggle_menu = Lülitage menüü -more_items = Rohkem esemeid +toggle_menu = Lülita menüü sisse/välja +more_items = Rohkem objekte email = E-posti aadress -password = Parool -access_token = Juurdepääsutähis -re_type = Kinnita parool +password = Salasõna +access_token = Tunnusluba +re_type = Kinnita salasõna twofa = Kahefaktoriline autentimine twofa_scratch = Kahefaktoriline kriipsukood -passcode = Passkood -webauthn_insert_key = Sisestage oma turvavõti -webauthn_sign_in = Vajutage turvavõtme nuppu. Kui teie turvavõtmel ei ole nuppu, sisestage see uuesti. -webauthn_press_button = Palun vajutage turvavõtme nuppu… -webauthn_use_twofa = Kasutage oma telefonist kahefaktorilist koodi -webauthn_error = Teie turvavõti ei saanud lugeda. -webauthn_unsupported_browser = Teie brauser ei toeta praegu WebAuthn. -webauthn_error_unknown = Tekkis tundmatu viga. Palun proovige uuesti. -webauthn_error_insecure = WebAuthn toetab ainult turvalisi ühendusi. HTTP kaudu testimiseks võite kasutada päritolu "localhost" või "127.0.0.1" -webauthn_error_empty = Sellele võtmele tuleb määrata nimi. -webauthn_error_timeout = Ajakatkestus saavutati enne võtme lugemist. Palun laadige see lehekülg uuesti ja proovige uuesti. +passcode = Salakood +webauthn_insert_key = Sisesta oma turvavõti +webauthn_sign_in = Vajuta turvavõtme nuppu. Kui sinu turvavõtmel ei ole nuppu, sisesta see uuesti. +webauthn_press_button = Palun vajuta turvavõtme nuppu… +webauthn_use_twofa = Sisesta oma telefonist kahefaktorilise autentimise kood +webauthn_error = Sinu turvavõtit ei saanud lugeda. +webauthn_unsupported_browser = Sinu veebibrauser ei toeta praegu WebAuthn-liidestust. +webauthn_error_unknown = Tekkis tundmatu viga. Palun proovi uuesti. +webauthn_error_insecure = WebAuthn toetab ainult turvalisi ühendusi. HTTP kaudu testimiseks võid kasutada lähteaadressina „localhost“ või „127.0.0.1“ +webauthn_error_empty = Palun lisa sellele võtmele täisnimi. +webauthn_error_timeout = Päring aegus enne võtme lugemist. Palun laadi see lehekülg uuesti ja proovi uuesti. repository = Hoidla organization = Organisatsioon -new_fork = Uus hoidla haru +new_fork = Uus lähekoodihoidla haru new_project = Uus projekt new_project_column = Uus veerg -admin_panel = Saidi administreerimine -settings = Seaded +admin_panel = Saidi haldus +settings = Seadistused your_profile = Profiil your_starred = Tähistatud tärniga -new_repo.title = Uus hoidla -new_migrate.title = Uus sisseränne +new_repo.title = Uus lähtekoodi hoidla +new_migrate.title = Uus kolimine new_org.title = Uus organisatsioon -new_repo.link = Uus hoidla -new_migrate.link = Uus sisseränne +new_repo.link = Uus lähtekoodi hoidla +new_migrate.link = Uus kolimine new_org.link = Uus organisatsioon all = Kõik sources = Allikad @@ -69,10 +69,10 @@ mirror = Peegelpilt mirrors = Peegelpildid forks = Harud activities = Tegevused -pull_requests = Tõmbepäringud -issues = Probleemid +pull_requests = Päringud koodi mestimiseks +issues = Veahaldus milestones = Verstapostid -ok = OK +ok = Sobib cancel = Tühista retry = Proovi uuesti rerun = Käivita uuesti @@ -81,74 +81,78 @@ add = Lisa add_all = Lisa kõik remove = Eemalda remove_all = Eemalda kõik -remove_label_str = Eemalda ühik "%s" -edit = Redigeeri +remove_label_str = Eemalda „%s“ objekt +edit = Muuda view = Vaata -test = Test -enabled = Võimaldatud -disabled = Välja lülitatud -locked = Lukkus +test = Testi +enabled = Kasutusel +disabled = Pole kasutusel +locked = Lukustatud copy = Kopeeri -copy_url = Kopeeri URL -copy_hash = Kooperi hash +copy_url = Kopeeri võrguaadress +copy_hash = Kopeeri räsi copy_content = Kopeeri sisu copy_branch = Kopeeri haru nimi copy_success = Kopeeritud! -copy_error = Kopeerimine ebaõnnestus +copy_error = Kopeerimine ei õnnestunud copy_type_unsupported = Seda failitüüpi ei saa kopeerida write = Kirjuta preview = Eelvaade -loading = Laadimine… +loading = Laadin… error = Viga -error404 = Lehekülge, millele te üritate jõuda, kas ei ole olemas või teil ei ole õigust seda vaadata. +error404 = Lehekülge, millele sa üritad jõuda, kas ei ole olemas, ta on eemaldatud või sul ei ole õigust seda vaadata. error413 = Sa oled oma kvoodi ammendanud. go_back = Mine tagasi -invalid_data = Kehtetud andmed: %v +invalid_data = Vigased andmed: %v never = Mitte kunagi unknown = Teadmata -rss_feed = RSS infovoog -confirm_delete_artifact = Kas oled kindel et soovite artefakti "%s" kustutada? -pin = +rss_feed = RSS-voog +confirm_delete_artifact = Kas oled kindel et soovid „%s“ artefakti kustutada? +pin =Tõsta esile artifacts = Artefaktid archived = Arhiveeritud -concept_system_global = Ülemaailmne +concept_system_global = Üldine concept_user_individual = Individuaalne -concept_code_repository = Hoidla +concept_code_repository = Lähtekoodi hoidla concept_user_organization = Organisatsioon -show_timestamps = Näita ajatemplid -show_log_seconds = Näita sekundit -download_logs = Logide allalaadimine +show_timestamps = Näita ajatempleid +show_log_seconds = Näita sekundeid +download_logs = Laadi logid alla name = Nimi value = Väärtus filter = Filter -filter.clear = Tühjendage filtrid +filter.clear = Tühjenda filtrid filter.is_archived = Arhiveeritud -filter.not_archived = Mitte arhiveeritud -filter.is_fork = Harud -filter.not_fork = Mitte harud -filter.is_mirror = Peegelpiltid -filter.not_mirror = Mitte peegelpiltid +filter.not_archived = Arhiveerimata +filter.is_fork = Koodiharud +filter.not_fork = Pole koodiharud +filter.is_mirror = Peegelpildid +filter.not_mirror = Pole peegelpilte filter.is_template = Mallid -filter.not_template = Mitte Mallid +filter.not_template = Pole mallid filter.public = Avalik filter.private = Privaatne rerun_all = Käivita uuesti kõik tööd new_mirror = Uus peegelpilt copy_generic = Kopeeri lõikelauale -confirm_delete_selected = Kinnitage et kustutada kõik valitud elemendid? -show_full_screen = Näita täisekraanil +confirm_delete_selected = Kas kinnitad kõikide valitud objektide kustutamise? +show_full_screen = Näita täisekraanivaates +copy_path = Kopeeri asukoht +captcha = Robotilõks +unpin = Lõpeta esiletõstmine +powered_by = Siin on kasutusel %s [search] -search = Otsi... +search = Otsi… fuzzy = Hägus -fuzzy_tooltip = Lisage tulemused mis vastavad ka otsingu terminile +fuzzy_tooltip = Lisa tulemused mis lähedalt vastavad otsingusõnale union = Märksõnad exact = Täpne -exact_tooltip = Sisaldab ainult tulemusi mis vastavad täpsele otsingusõnale -repo_kind = Otsi hoidlad… +exact_tooltip = Sisaldab ainult tulemusi, mis vastavad täpsele otsingusõnale +repo_kind = Otsi lähtekoodihoidlad… user_kind = Otsi kasutajaid… org_kind = Otsi organisatsioone… -team_kind = Otsi meeskonnad… +team_kind = Otsi tiime… code_kind = Otsi koodi… code_search_by_git_grep = Praeguse koodi otsingu tulemused annab "git grep". Paremaid tulemusi võib saada, kui saidi administraator lubab koodi indekseerija. package_kind = Otsi pakette… @@ -160,41 +164,53 @@ no_results = Sobivaid tulemusi ei leitud. issue_kind = Otsi probleeme… milestone_kind = Otsi verstapostid... type_tooltip = Otsingu tüüp -code_search_unavailable = Koodide otsing ei ole praegu saadaval. Palun võtke ühendust saidi administraatoriga. -union_tooltip = Sisaldab tulemused mis vastavad mis tahes tühikutega eraldatud võtmesõnadele -keyword_search_unavailable = Otsing märksõna järgi ei ole praegu saadaval. Palun võtke ühendust saidi administraatoriga. +code_search_unavailable = Koodiotsing ei ole praegu saadaval. Palun võta ühendust saidi administraatoriga. +union_tooltip = Kaasa tulemusi, mis vastavad mis tahes tühikutega eraldatud märksõnadele +keyword_search_unavailable = Otsing märksõna järgi ei ole praegu saadaval. Palun võtke ühendust saidi haldajaga. pull_kind = Otsi tõmbepäringuid… [aria] navbar = Navigatsiooniriba -footer.software = Selle tarkvara kohta +footer.software = Teave selle tarkvara kohta footer.links = Lingid +footer = Jalus [heatmap] -number_of_contributions_in_the_last_12_months = %s panused viimase 12 kuu jooksul -contributions_zero = Panused ei ole +number_of_contributions_in_the_last_12_months = %s kaastööd viimase 12 kuu jooksul +contributions_zero = Kaastöid ei ole contributions_format = {contributions} {day} {month}, {year} -contributions_few = panused +contributions_few = kaastööd less = Vähem more = Rohkem -contributions_one = panus +contributions_one = kaastöö [editor] buttons.heading.tooltip = Lisa pealkiri -buttons.italic.tooltip = Lisa kursiivne tekst -buttons.quote.tooltip = Tsitaadi tekst +buttons.italic.tooltip = Lisa kaldkirjas tekst +buttons.quote.tooltip = Tsiteeri teksti buttons.code.tooltip = Lisa kood buttons.link.tooltip = Lisa link buttons.list.ordered.tooltip = Lisa nummerdatud nimekiri buttons.list.unordered.tooltip = Lisa nimekiri buttons.list.task.tooltip = Lisa ülesannete nimekiri -buttons.ref.tooltip = Viide probleemile või tõmbepäringule -buttons.switch_to_legacy.tooltip = Kasutage selle asemel pärandredaktorit -buttons.enable_monospace_font = Võimalda püsisammkiri -buttons.disable_monospace_font = Lülita välja püsisammkiri -buttons.indent.tooltip = Pesa esemed ühe taseme võrra -buttons.bold.tooltip = Lisa rasvane tekst -buttons.mention.tooltip = Mainige kasutajat või meeskonda +buttons.ref.tooltip = Viide probleemile või mestimispäringule +buttons.switch_to_legacy.tooltip = Selle asemel kasuta pärandredaktorit +buttons.enable_monospace_font = Kasuta püsisammkirja +buttons.disable_monospace_font = Lülita püsisammkiri välja +buttons.indent.tooltip = Liiguta objekte ühe taseme võrra +buttons.bold.tooltip = Lisa paks tekst +buttons.mention.tooltip = Maini kasutajat või tiimi +buttons.unindent.tooltip = Võta tagasi objektide liigutamine ühe taseme võrra +buttons.new_table.tooltip = Lisa tabel +table_modal.header = Lisa tabel +table_modal.placeholder.header = Päis +table_modal.placeholder.content = Sisu +table_modal.label.rows = Read +table_modal.label.columns = Veerud +link_modal.header = Lisa link +link_modal.url = Võrguaadress +link_modal.description = Kirjeldus +link_modal.paste_reminder = Soovitus: kui link on uba lõikelaual olemas, siis võis ta otse lisada ja link luuakse automaatselt. [filter] string.asc = A - Z @@ -205,114 +221,179 @@ occurred = Tekkis viga invalid_csrf = Halb taotlus: vigane CSRF token not_found = Sihtmärki ei leitud. network_error = Võrguviga -server_internal = Sisemine serveri viga -report_message = Kui usute et tegemist on Forgejo veaga siis otsige probleeme Codebergist või avage vajadusel uus probleem. +server_internal = Serveri sisemine viga +report_message = Kui usud, et tegemist on Forgejo veaga, siis otsige sarnaseid vigu Codebergist või vajadusel koosta uus veakirjeldus. [startpage] -app_desc = Valutu, isehostitatud Git'i teenus +app_desc = Muretu Git'i teenus sinu omas serveris install = Lihtne paigaldada platform = Platvormiülene -platform_desc = Forgejo on kinnitust leidnud et töötab nii libre operatsioonisüsteemides nagu Linux ja FreeBSD, kui ka erinevatel protsessorarhitektuuridel. Valige see mis teile meeldib! +platform_desc = On kinnitust leidnud, et Forgejo töötab nii avatud operatsioonisüsteemides nagu Linux ja FreeBSD, kui ka erinevatel protsessorarhitektuuridel. Vali see mis sulle meeldib! lightweight = Kergekaaluline -lightweight_desc = Forgejo on väikeste miinimumnõuetega ja seda saab kasutada odaval Raspberry Pi'l. Säästa oma masina energiat! +lightweight_desc = Forgejo on väikeste miinimumnõuetega ja seda saad kasutada odaval Raspberry Pi'l. Säästa oma masina energiat! license = Avatud lähtekood -install_desc = Lihtsalt käivitage oma platvormi binaarsüsteem, tarnige see koos Dockeriga, või saada see pakendatud. -license_desc = Mine võta Forgejo! Liitu meiega andes oma panuse et muuta see projekt veelgi paremaks. Ärge häbenege olla kaasaaitaja! +install_desc = Lihtsalt käivita rakenduse fail oma platvormil, pane ta tööle Dockeriga või laadi ta pakendatuna. +license_desc = Laadi Forgejo alla! Liitu meiega andes oma panuse, et muuta see projekt veelgi paremaks. Ära pelga osalemist! [install] install = Paigaldamine title = Esialgne konfiguratsioon -docker_helper = Kui käivitate Forgejo't Dockeri sees, lugege dokumentatsiooni enne seadete muutmine. -require_db_desc = Forgejo vajab MySQL, PostgreSQL, SQLite3 või TiDB (MySQL protokoll). -db_title = Andmebaasi seaded +docker_helper = Kui käivitad Forgejo't Dockeris, loe enne seadistuste muutmist dokumentatsiooni. +require_db_desc = Forgejo vajab tööks ühte järgnevast andmebaasidest: MySQL, PostgreSQL, SQLite3 või TiDB (MySQL protokoll). +db_title = Andmebaasi seadistused db_type = Andmebaasi tüüp -host = Vastuvõtja +host = Server user = Kasutajanimi -password = Parool +password = Salasõna db_name = Andmebaasi nimi -db_schema = Skeem -db_schema_helper = Jäta tühjaks andmebaasi vaikimisi ("avalik"). +db_schema = Andmeskeem +db_schema_helper = Vaikimisi valiku kasutamiseks jäta tühjaks („public“). ssl_mode = SSL -path = Tee -sqlite_helper = SQLite3 andmebaasi failitee.
Sisestage absoluutne tee, kui käivitate Forgejo't teenusena. -reinstall_error = Sa üritad installeerida olemasolevasse Forgejo andmebaasi -reinstall_confirm_message = Olemasoleva Forgejo andmebaasi uuesti paigaldamine võib põhjustada mitmeid probleeme. Enamasti peaksite Forgejo käivitamiseks kasutama olemasolevat "app.ini". Kui te teate, mida teete, kinnitage järgmist: -reinstall_confirm_check_1 = Andmed, mis on krüpteeritud SECRET_KEY'ga app.ini's, võivad kaduda: kasutajad ei pruugi saada 2FA/OTP-ga sisse logima ja peegelpiltid ei pruugi õigesti toimida. Selle kasti märkimisega kinnitate, et praegune app.ini fail sisaldab õiget SECRET_KEY'd. -reinstall_confirm_check_3 = Te kinnitate, et olete täiesti kindel, et see Forgejo töötab õiges app.ini asukohas ja et olete kindel, et peate uuesti installima. Te kinnitate, et tunnistate ülaltoodud riske. -err_empty_db_path = SQLite3 andmebaasi tee ei saa olla tühi. -no_admin_and_disable_registration = Kasutajate iseregistreerimist ei saa keelata ilma administraatori kontot loomata. -err_empty_admin_password = Administraatori parool ei saa olla tühi. -err_empty_admin_email = Administraatori e-posti aadress ei saa olla tühi. -err_admin_name_is_reserved = Administraatori kasutajanimi on kehtetu, kasutajanimi on reserveeritud -err_admin_name_pattern_not_allowed = Administraatori kasutajanimi on kehtetu, kasutajanimi vastab reserveeritud mustrile -err_admin_name_is_invalid = Administraatori kasutajanimi on kehtetu -general_title = Üldised seaded -app_name = Instantsi pealkiri -app_name_helper = Sisestage siia oma instantsi nimi. See kuvatakse igal leheküljel. -app_slogan = Instantse loosung -repo_path = Hoidla juurte tee -lfs_path = Git LFS'i juurte tee -lfs_path_helper = Failid jälgitatud Git LFS'ist salvestatakse sellesse kaustale. Jätke tühjaks et välja lülitada. +path = Asukoht +sqlite_helper = SQLite3 andmebaasi aukoht.
Kui käivitad Forgejo'd teenusena, siis sisesta absoluutne asukoht. +reinstall_error = Sa üritad paigaldada olemasolevasse Forgejo andmebaasi +reinstall_confirm_message = Olemasoleva Forgejo andmebaasi uuesti paigaldamine võib põhjustada mitmeid probleeme. Enamasti peaksid Forgejo käivitamiseks kasutama olemasolevat „app.ini“. Kui te tead, mida teed, kinnita järgmist: +reinstall_confirm_check_1 = Andmed, mis on krüpteeritud SECRET_KEY'ga app.ini's, võivad kaduda: kasutajad ei pruugi saada sisselogimisel kaasutada 2FA/OTP võimalusi ja peegelpildid ei pruugi õigesti toimida. Selle kasti märkimisega kinnitad, et praegune app.ini fail sisaldab õiget SECRET_KEY'd. +reinstall_confirm_check_3 = Sa kinnitad, et oled täiesti kindel, et see Forgejo töötab õiges app.ini asukohas ja et oled kindel, et pead uuesti paigaldama. Sa kinnitad, et oled teadlik ülaltoodud riskidest. +err_empty_db_path = SQLite3 andmebaasi asukoht ei saa olla tühi. +no_admin_and_disable_registration = Kasutajate iseregistreerimist ei saa keelata ilma peakasutaja kontot loomata. +err_empty_admin_password = Peakasutaja salasõna ei saa olla tühi. +err_empty_admin_email = Peakasutaja e-posti aadress ei saa olla tühi. +err_admin_name_is_reserved = Peakasutaja kasutajanimi on vigane, kasutajanimi on reserveeritud +err_admin_name_pattern_not_allowed = Peakasutaja kasutajanimi on vigane, kasutajanimi vastab reserveeritud mustrile +err_admin_name_is_invalid = Peakasutaja kasutajanimi on vigane +general_title = Üldised seadistused +app_name = Serveri nimi või pealkiri +app_name_helper = Sisestage siia oma serveri või teenuse nimi. See kuvatakse igal leheküljel. +app_slogan = Serveri tunnuslause +repo_path = Hoidla juurkausta asukoht +lfs_path = Git LFS'i juurkausta asukoht +lfs_path_helper = Git LFS'ist jälgitud failid salvestatakse siia kausta. Kui jätad tühjaks, siis pole see võimalus kasutusel. run_user = Kasutaja kellena käivitada -run_user_helper = Operatsioonisüsteemi kasutajanimi, mille all Forgejo töötab. Pange tähele, et sellel kasutajal peab olema juurdepääs hoidlate juurte teele. +run_user_helper = Operatsioonisüsteemi kasutajanimi, kellena Forgejo töötab. Pane tähele, et sellel kasutajal peab olema juurdepääs hoidlate juurkaustale. domain = Serveri domeen -domain_helper = Serveri domeen või hostiaadress. +domain_helper = Serveri domeen või hosti aadress. ssh_port = SSH-serveri port -ssh_port_helper = Pordi number, mida SSH-server kasutab. Jätke tühjaks et välja lülitada SSH-serveri. -http_port = HTTP-kuulamise port +ssh_port_helper = Pordi number, mida SSH-server kasutab. SSH-serveri väljalülitamiseks jäta tühjaks. +http_port = Serveri HTTP port http_port_helper = Pordi number, mida Forgejo veebiserver kasutab. -app_url = Baasi URL -app_url_helper = Baasaadress HTTP(S) kloonimise URL-ide ja e-posti teadete jaoks. -log_root_path = Logi tee -log_root_path_helper = Logifailid kirjutatakse sellesse kaustale. -optional_title = Vabatahtlikud seaded -email_title = E-posti seaded -smtp_addr = SMTP vastuvõtja +app_url = Juur-võrguaadress +app_url_helper = Lähteaadress HTTP(S) kloonimise võrguaadresside ja e-posti teadete jaoks. +log_root_path = Logi asukoht +log_root_path_helper = Logifailid kirjutatakse siia kausta. +optional_title = Täiendavad seadistused +email_title = E-posti seadistused +smtp_addr = SMTP server smtp_port = SMTP port smtp_from = Saada e-kirjad nagu -smtp_from_invalid = "Saada e-kirjad nagu" aadress on kehtetu -smtp_from_helper = E-posti aadress, mida Forgejo kasutab. Sisestage tavaline e-posti aadress või kasutage formaati "Nimi" . +smtp_from_invalid = „Saada e-kirjad nagu“ aadress on vigane +smtp_from_helper = E-posti aadress, mida Forgejo kasutab. Sisesta tavaline e-posti aadress või kasutage vormingut "Nimi" . mailer_user = SMTP kasutajanimi -mailer_password = SMTP parool -register_confirm = Registreerimiseks on vaja e-posti kinnitust -mail_notify = Lubage e-posti teated -server_service_title = Serveri ja kolmanda osapoole teenuste seaded -offline_mode = Lülita sisse lokaalse režiimi -disable_gravatar = Lülita välja Gravatar -federated_avatar_lookup = Lülita sisse föderaalsed avatarid -federated_avatar_lookup.description = Otsige avatare kasutades Libravatar'i. +mailer_password = SMTP salasõna +register_confirm = Registreerimiseks on vaja e-posti kinnitamist +mail_notify = Kasuta teavitamist e-posti teel +server_service_title = Serveri ja kolmanda osapoole teenuste seadistused +offline_mode = Lülita sisse lokaalne režiim +disable_gravatar = Lülita Gravatar välja +federated_avatar_lookup = Kaasuta födereeritud tunnuspilte +federated_avatar_lookup.description = Otsi tunnuspilte Libravatari teenusest. disable_registration = Lülita välja iseregistreerimine allow_only_external_registration = Luba registreerimine ainult väliste teenuste kaudu allow_only_external_registration.description = Kasutajad saavad uusi kontosid luua ainult seadistatud väliste teenuste abil. -openid_signin = Lülita sisse OpenID sisselogimise -openid_signin.description = Luba kasutajatel OpenID kaudu sisse logida. -openid_signup = Lülita sisse OpenID iseregistreerimine -enable_captcha = Lülita sisse registreerimise CAPTCHA -enable_captcha.description = Nõudke kasutajatelt CAPTCHA läbimist kontode loomiseks. -require_sign_in_view = Nõua sisselogimist et vaadata instantsi sisu -default_keep_email_private = Peida e-posti aadressid vaikimisi -default_keep_email_private.description = Lülita sisse uute kasutajate e-posti aadressi varjamine vaikimisi, et see teave ei lekiks kohe pärast registreerimist. -default_allow_create_organization = Lubada organisatsioonide loomine vaikimisi -default_enable_timetracking = Aja jälgimise sisselülitamine vaikimisi -default_enable_timetracking.description = Lubage uute repositooriumide jaoks vaikimisi aja jälgimise funktsiooni kasutamine. -admin_title = Administraatori konto seaded -admin_setting.description = Administraatori konto loomine on vabatahtlik. Esimesest registreeritud kasutajast saab automaatselt administraator. -admin_name = Administraatori kasutajanimi -admin_password = Parool -confirm_password = Parooli kinnitamine +openid_signin = Kasuta OpenID abil sisselogimist +openid_signin.description = Luba kasutajatel OpenID abil sisse logida. +openid_signup = Kasuta OpenID abil iseregistreerimist +enable_captcha = Kasuta registreerimisel robotilõksu +enable_captcha.description = Eelda, et kasutajad lahendavad registreerimisel robotilõksu ülesande. +require_sign_in_view = Serveri sisu vaatamiseks eelda sisselogimist +default_keep_email_private = Vaikimisi peida e-posti aadressid +default_keep_email_private.description = Lülita sisse uute kasutajate e-posti aadresside vaikimisi varjamine - see väldib vastava teabe lekkimist kohe pärast registreerimist. +default_allow_create_organization = Vaikimisi luba organisatsioonide loomine +default_enable_timetracking = Vaikimisi lülita sisse aja jälgimine +default_enable_timetracking.description = Vaikimisi luba uute lähtekoodihoidlate jaoks aja jälgimise funktsiooni kasutamise. +admin_title = Peakasutaja konto seaded +admin_setting.description = Peakasutaja konto loomine on vabatahtlik. Esimesest registreeritud kasutajast saab automaatselt peakasutaja. +admin_name = Peakasutaja kasutajanimi +admin_password = Salasõna +confirm_password = Kinnita salasõna admin_email = E-posti aadress -config_location_hint = Need konfiguratsioonivalikud salvestatakse sees: +config_location_hint = Need konfiguratsioonivalikud salvestatakse siin: install_btn_confirm = Paigalda Forgejo -test_git_failed = Ei saanud testida käsku "git": %v -invalid_db_setting = Andmebaasi seaded on vigased: %v -invalid_db_table = Andmebaasi tabel "%s" on vigane: %v +test_git_failed = Ei saanud testida käsku „git“: %v +invalid_db_setting = Andmebaasi seadistused on vigased: %v +invalid_db_table = Andmebaasi tabel „%s“ on vigane: %v allow_dots_in_usernames = Luba kasutajatel kasutada oma kasutajanimedes punkte. Ei mõjuta olemasolevaid kontosid. -default_allow_create_organization.description = Lubage uutel kasutajatel vaikimisi luua organisatsioone. Kui see valik on välja lülitatud, peab administraator andma uutele kasutajatele organisatsioonide loomise loa. -disable_gravatar.description = Lülita välja Gravatari või muude kolmandate osapoolte avatariallikate kasutamine. Kasutajate avatarite jaoks kasutatakse vaikimisi pilte, kui nad ei lae oma avatari üles. -openid_signup.description = Luba kasutajatel luua kontosid OpenID kaudu, kui iseregistreerimine on sisse lülitatud. -require_sign_in_view.description = Piirake sisule juurdepääsu sisselogitud kasutajatele. Külalised saavad külastada ainult autentimislehti. -reinstall_confirm_check_2 = Hoidlad ja seadeid võib olla vaja uuesti sünkroniseerida. Selle kasti märkimisega kinnitate, et sünkroniseerite hoidlate ja authorized_keys'i faili konksud käsitsi uuesti. Te kinnitate, et tagate, et hoidlate ja peegelpilti seaded on õiged. -app_slogan_helper = Sisestage siia oma loosung. Jätke tühjaks, et välja lülitada. -repo_path_helper = Kauged Git-hoidlad salvestatakse sellesse kaustale. -sqlite3_not_available = See Forgejo versioon ei toeta SQLite3. Palun laadige alla ametlik binaarversioon %s (mitte "gobuild"i versioon). -offline_mode.description = Lülitage kolmandate osapoolte sisu edastamise võrgud välja ja teenindage kõiki ressursse lokaalselt. \ No newline at end of file +default_allow_create_organization.description = Luba uutel kasutajatel vaikimisi luua organisatsioone. Kui see valik on välja lülitatud, peab peakasutaja andma uutele kasutajatele organisatsioonide loomise loa. +disable_gravatar.description = Lülita välja Gravatari või muude kolmandate osapoolte tunnuspiltide allikate kasutamine. Kasutajate tunnuspiltidena kasutatakse vaikimisi pilte, kui nad enda oma ei laadi üles. +openid_signup.description = Kui iseregistreerimine on kasutusel, luba kasutajatel luua kontosid OpenID abil. +require_sign_in_view.description = Piira juurdepääsu vaid sisselogitud kasutajatega. Külalised saavad külastada ainult autentimislehti. +reinstall_confirm_check_2 = Hoidlad ja seadistused võivad uuesti vajada sünkroniseerimist. Selle kasti märkimisega kinnitad, et sünkroniseerid hoidlate ja authorized_keys'i faili haagid käsitsi uuesti. Sa kinnitad, et tagad, et hoidlate ja peegelpiltide seadistused on õiged. +app_slogan_helper = Sisesta siia oma serveri tunnuslause. Kui jätad tühjaks, siis pole tunnuslause kasutusel. +repo_path_helper = Kaugseadmete Git-hoidlad salvestatakse siia kausta. +sqlite3_not_available = See Forgejo versioon ei toeta SQLite3 andmebaasi. Palun laadi alla ametlik binaarversioon %s (mitte „gobuild“-versioon). +offline_mode.description = Lülitage kolmandate osapoolte sisuedastusvõrgud välja ja jaga kõiki ressursse kohalikust serverist. +password_algorithm = Salasõna räsialgoritm +invalid_password_algorithm = Vigane salasõna räsialgoritm + +[auth] +allow_password_change = Eelda, et kasutajad muudavad oma salasõna (soovitatav) +forgot_password_title = Ununenud salasõna +must_change_password = Muuda oma salasõna +forgot_password = Kas salasõna ununes? + +[mail] +password_change.subject = Sinu salasõna on muutunud +password_change.text_1 = Sinu kasutajakonto salasõna on just muutunud. +totp_disabled.text_1 = Lisaautentimine ehk ajapõhise salasõna (TOTP) kasutamine on sinu kasutajakontol just välja lülitatud. + +[form] +Retype = Korda salasõna +password_not_match = Salasõnad ei klapi. +Password = Salasõna +username_password_incorrect = Kassutajanimi või salasõna pole õige. + +[settings] +retype_new_password = Korda uut salasõna +password_incorrect = Senine salasõna pole õige. +change_password_success = Sina salasõna on nüüd muudetud. Edaspidi kasuta sisselogimiseks seda uut salasõna. +update_password = Muuda salasõna +old_password = Senine salasõna +new_password = Uus salasõna + +[repo] +mirror_sync_on_commit = Sünkrooni sissekannete tegemisel +mirror_interval_invalid = Peegelpildi välp pole korrektne. +mirror_sync = sünkroonis +mirror_use_ssh.text = Kasuta SSH-autentimist +mirror_public_key = Avalik SSH-võti +mirror_password_placeholder = (Muutmata) +mirror_password_help = Salvestatud salasõna kustutamiseks muuda kasutajanime. +mirror_address = Klooni võrguaadressilt +mirror_password_blank_placeholder = (Seadistamata) +commits.message = Sõnum +commits.search_all = Kõik harud +commits.author = Autor +commits.browse_further = Sirvi edasi +commits.renamed_from = Nimi muudetud, vana nimi oli „%s“ +commits.date = Kuupäev +commits.older = Vanemad +commits.newer = Uuemad +commitstatus.error = Viga +commits.search_branch = See haru +projects = Projektid +commitstatus.failure = Tõrge +commitstatus.pending = Ootel +commitstatus.success = Valmis +ext_issues = Välised vead + +[actions] +variables = Muutujad +variables.deletion = Eemalda muutuja +variables.creation = Lisa muutuja +variables.none = Muutujaid veel pole. +variables.management = Halda muutujaid + +[admin] +users.password_helper = Kui sa ei taha salasõna muuta, siis jäta väli tühjaks. +config.reset_password_code_lives = Taastekoodi aegumine +auths.bind_password = Seo salasõna \ No newline at end of file diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index 23cb74f814..9d568ba2e4 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -301,7 +301,7 @@ federated_avatar_lookup=فعال سازی آواتار مشترک federated_avatar_lookup.description=پیدا کردن آواتار با استفاده از Libravatar. disable_registration=غیرفعال‌کردن خود ثبت نامی disable_registration.description=غیرفعال کردن ثبت نام کاربر. تنها مدیر ها قادر خواهند بود حساب کاربری جدید اضافه کنند. -allow_only_external_registration.description=اجازه ثبت نام فقط از طریق خدمات خارجی +allow_only_external_registration.description=اجازه ثبت نام فقط از طریق خدمات خارجی. openid_signin=فعالسازی ورود با OpenID openid_signin.description=فعالسازی ورود کاربر با OpenID. openid_signup=فعالسازی ثبت نام با OpenID @@ -1338,7 +1338,7 @@ issues.error_modifying_due_date=تغییر موعد مقرر با شکست مو issues.error_removing_due_date=حذف موعد مقرر با شکست مواجه شد. issues.push_commit_1=%d اعمال تغییر اضافه شده است %s issues.push_commits_n=%d اعمال تغییرات اضافه شده است %s -issues.force_push_codes=`پوش شده اجباری %[1]s از %[2]s به %[4]s %[6]s` +issues.force_push_codes=`پوش شده اجباری %[1]s از %[2]s %[8]s به %[4]s %[9]s %[6]s` issues.force_push_compare=مقایسه issues.due_date_form=yyyy-mm-dd issues.due_date_form_add=افزودن موعد مقرر @@ -2762,7 +2762,7 @@ directory = پوشه [search] type_tooltip = جستجو گونه -search = جستجو... +search = جستجو… fuzzy = درهم fuzzy_tooltip = پیامدهایی را درج کنید که دقیقا با عبارت جستجو همخوانی داشته باشند regexp = عبارات باقاعده diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 7eea87f959..263fab8774 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -667,14 +667,13 @@ username_change_not_local_user = Ei-paikallisten käyttäjien ei sallita vaihtaa admin_cannot_delete_self = Et voi poistaa itseäsi, kun olet ylläpitäjä. Poista ensin ylläpito-oikeudet itseltäsi. username_claiming_cooldown = Käyttäjänimeä ei voi ottaa käyttöön, koska siihen kohdistuva suojaamisjakso ei ole vielä päättynyt. Käyttäjänimen voi ottaa käyttöön %[1]s. email_domain_is_not_allowed = Käyttäjän sähköpostiosoitteen %s verkkotunnus on ristiriidassa EMAIL_DOMAIN_ALLOWLIST:in tai EMAIL_DOMAIN_BLOCKLIST:in kanssa. Varmista, että olen asettanut sähköpostiosoitteen oikein. - - -invalid_group_team_map_error = ` kuvaus ei ole kelvollinen: %s` -visit_rate_limit = Etävierailujen pyyntörajoitukset. -2fa_auth_required = Etävierailu vaati kaksivaiheisen todennuksen. -unset_password = Kirjautuneen käyttäjän salasanaa ei ole asetettu. unsupported_login_type = Tällä kirjautumistavalla ei voi poistaa tunnusta. invalid_ssh_principal = Väärä toimija: %s +unset_password = Kirjautuneen käyttäjän salasanaa ei ole asetettu. +invalid_group_team_map_error = ` kuvaus ei ole kelvollinen: %s` +2fa_auth_required = Etävierailu vaati kaksivaiheisen todennuksen. +visit_rate_limit = Etävierailujen pyyntörajoitukset. + [user] change_avatar=Vaihda profiilikuvasi… @@ -1044,7 +1043,6 @@ passcode_invalid = Virheellinen pääsykoodi. Yritä uudelleen. then_enter_passcode = Kirjoita sovelluksessa näkyvä pääsykoodi: gpg_key_matched_identities_long = Tähän avaimeen upotetut identiteetit vastaavat tämän käyttäjän seuraavia aktivoituja sähköpostiosoitteita. Kommitit, jotka vastaavat näitä sähköpostiosoitteita, voidaan vahvistaa tällä avaimella. twofa_failed_get_secret = Salaisuuden saaminen epäonnistui. - uid = UID hidden_comment_types.ref_tooltip = Kommentit missä tähän ongelmaan viitattiin toisesta ongelmasta/kommitista/… @@ -3190,7 +3188,7 @@ close_issue = `sulki ongelman %[3]s#%[2]s` merge_pull_request = `yhdisti vetopyynnön %[3]s#%[2]s` comment_pull = `kommentoi vetopyyntöä %[3]s#%[2]s` auto_merge_pull_request = `automaattisesti yhdisti vetopyynnön %[3]s#%[2]s` -delete_branch = poisti haaran %[2]s tietovarastosta %[3]s +delete_branch = poisti haaran %[2]s tietovarastosta %[3]s watched_repo = aloitti tietovaraston %[2]s tarkkailun approve_pull_request = `hyväksyi %[3]s#%[2]s` starred_repo = lisäsi tähden tietovarastolle %[2]s diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 487768ea22..ed1128ceb6 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -313,7 +313,7 @@ no_reply_address = Domain ng nakatagong email no_reply_address_helper = Domain name para sa mga user na may nakatagong email address. Halimbawa, ang username na "kita" ay mala-log sa Git bilang "kita@noreply.example.org" kapag ang nakatagong email domain ay nakatakda sa "noreply.example.org". password_algorithm = Algorithm ng password hash invalid_password_algorithm = Hindi angkop na algorithm ng password hash -password_algorithm_helper = Itakda ang password hashing algorithm. Ang mga algorithm ay may magkakaibang mga kinakailangan at lakas. Ang algorithm ng Argon2 ay sa halip ay ligtas ngunit gumagamit ng maraming memory at maaaring hindi naaangkop para sa mga maliliit na sistema. +password_algorithm_helper = Itakda ang password hashing algorithm. Ang mga algorithm ay may magkakaibang mga kinakailangan at lakas. Ang algorithm na Argon2 ay sa halip ay ligtas ngunit gumagamit ng maraming memory at maaaring hindi naaangkop para sa mga maliliit na sistema. enable_update_checker = I-enable ang tagasuri ng update env_config_keys = Configuration ng Environment env_config_keys_prompt = Ang mga sumusunod na mga environment variable ay ilalapat rin sa iyong configuration file: @@ -760,7 +760,7 @@ comment_type_group_milestone = Milestone comment_type_group_issue_ref = Pagsangguni ng isyu keep_activity_private_popup = Makikita mo lang at mga tagapangasiwa ang iyong aktibidad can_not_add_email_activations_pending = Mayroong isang nakabinbing pag-activate, subukang muli sa loob ng ilang minuto kung nais mong magdagdag ng isang bagong email. -email_deletion_desc = Ang email address at mga kaugnay na impormasyon ay tatanggalin sa iyong account. Ang mga Git commit sa itong email address ay iiwanang hindi nabago. Magpatuloy? +email_deletion_desc = Ang email address na ito at mga kaugnay na impormasyon ay tatanggalin sa iyong account. Ang mga Git commit sa itong email address ay iiwanang hindi nabago. Magpatuloy? add_email = Idagdag ang email eddress gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig delete_token_success = Nabura na ang token. Ang mga application na gumagamit nito ay hindi na maa-access ang iyong account. @@ -1033,6 +1033,7 @@ access_token_regeneration = I-regenerate ang access token regenerate_token = I-regenerate access_token_regeneration_desc = Ang pag-regenerate ng token ay babawiin ang access sa iyong account para sa mga application na gumagamit nito. Hindi ito mababawi. Magpatuloy? regenerate_token_success = Na-generate muli ang token. Ang mga application na gumagamit nito ay hindi na maa-access ang iyong account at dapat ma-update gamit ang bagong token. +ssh_token_help_ssh_agent = o, kung gumagamit ka ng SSH agent (na nakatakda ang variable na SSH_AUTH_SOCK): [repo] template_description = Ang mga template na repositoryo ay pinapayagan ang mga gumagamit na mag-generate ng mga bagong repositoryo na may magkatulad na istraktura ng direktoryo, mga file, at opsyonal na mga setting. @@ -1115,7 +1116,7 @@ default_branch = Default na branch default_branch_label = default mirror_prune = Pungusan mirror_prune_desc = Tanggalin ang mga antikuwado na sangguni ng remote-tracking -mirror_address_url_invalid = Ang ibinigay na url ay hindi wasto. Kailangan mong i-escape ang lahat ng mga components ng URL ng tama. +mirror_address_url_invalid = Hindi wasto ang ibinigay na URL. Siguraduhin na ang mga component ng URL ay na-escape nang tama. mirror_address_protocol_invalid = Ang ibinigay na URL ay hindi wasto. Ang http(s):// o git:// na lokasyon lamang ay magagamit para sa pag-mirror. mirror_lfs = Imbakan ng Malaking File (LFS) mirror_lfs_desc = I-activate ang pag-mirror ng LFS data. @@ -1203,7 +1204,7 @@ template.avatar = Avatar migrate_options = Mga opsyon sa paglipat migrate.clone_address_desc = Ang HTTP(S) o Git "clone" URL ng umiiral na repositoryo need_auth = Awtorisasyon -migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng account. +migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token dito na hinihiwalay ng mga kuwit upang gawing mas-mabilis ang pag-migrate sa pamamagitan ng pag-iwas sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng iyong (mga) account. template.invalid = Kailangang pumili ng kahit isang template na repositoryo migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang matukoy ang LFS server. Maaari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar. blame.ignore_revs.failed = Nabigong hindi pansinin ang mga rebisyon sa .git-blame-ignore-revs. @@ -1628,7 +1629,7 @@ projects.column.new_title = Pangalan projects.card_type.desc = Mga preview ng card commits.desc = I-browse ang history ng pagbabago ng source code. commits.search.tooltip = Maaari kang mag-prefix ng mga keyword gamit ang "author:", "committer:", "after:", o "before:", hal. "revert author:Nijika before:2022-10-09". -issues.force_push_codes = `puwersahang itinulak ang %[1]s mula %[2]s sa %[4]s %[6]s` +issues.force_push_codes = `puwersahang itinulak ang %[1]s mula %[2]s %[8]s sa %[4]s %[9]s %[6]s` issues.push_commit_1 = idinagdag ang %d commit %s issues.push_commits_n = idinagdag ang %d mga commit %s issues.new.no_reviewers = Walang mga tagasuri @@ -2225,7 +2226,7 @@ milestones.modify = I-update ang milestone milestones.filter_sort.earliest_due_data = Pinakamalapit na takdang petsa milestones.filter_sort.least_complete = Hindi bababa sa kumpleto signing.will_sign = Isa-sign ang commit gamit ang key na "%s". -signing.wont_sign.error = May error na naganap habang sinusuri kung masa-sign ang commit. +signing.wont_sign.error = May error na naganap habang sinusuri kung mailalagda ang commit. signing.wont_sign.always = Palaging naka-sign ang mga commit. signing.wont_sign.twofa = Kailangang naka-enable ang authentikasyong two factor para naka-sign ang mga commit. wiki.delete_page_notice_1 = Ang pagtanggal sa pahina ng wiki na "%s" ay hindi na mababawi. Magpatuloy? @@ -2864,7 +2865,7 @@ dashboard.resync_all_sshprincipals = I-update ang ".ssh/authorized_principals" f dashboard.resync_all_hooks = I-resychronize ang mga pre-receive, update at post-receive hook para sa lahat ng mga repositoryo dashboard.cleanup_hook_task_table = Linisin ang hook_task table dashboard.cleanup_packages = Linisin ang mga na-expire na package -dashboard.cleanup_actions = Linisin ang mga na-expire na log at artifact mula sa mga aksyon +dashboard.cleanup_actions = Linisin ang mga nag-expire na log at artifact mula sa mga aksyon dashboard.server_uptime = Uptime ng server dashboard.current_goroutine = Mga kasalukuyang goroutine dashboard.total_memory_allocated = Kabuuan na na-allocate na memory @@ -3758,7 +3759,7 @@ close_issue = `sinara ang isyu na %[3]s#%[2]s` review_dismissed = `na-dismiss ang pagsusuri mula %[4]s para sa %[3]s#%[2]s` close_pull_request = `sinara ang hiling sa paghila na %[3]s#%[2]s` transfer_repo = nilipat ang repositoryo na %s sa %s -delete_branch = binura ang branch %[2]s mula %[3]s +delete_branch = binura ang branch %[2]s mula %[3]s mirror_sync_push = na-sync ang mga commit sa %[3]s sa %[4]s mula sa mirror mirror_sync_create = na-syng ang bagong reference %[3]s sa %[4]s mula sa mirror publish_release = `inilabas ang %[4]s sa %[3]s` diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index db9872236b..2f1aba4609 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -340,7 +340,7 @@ no_reply_address=Domaine pour les courriels cachés no_reply_address_helper=Nom de domaine pour les utilisateurs ayant une adresse courriel cachée. Par exemple, l’utilisateur « fred » sera associé à « fred@noreply.example.org » par Git si le domaine est « noreply.example.org ». password_algorithm=Algorithme de hachage du mot de passe invalid_password_algorithm=Algorithme de hachage du mot de passe invalide -password_algorithm_helper=Définissez l’algorithme de hachage du mot de passe. Les algorithmes ont des exigences matérielles et une résistance différentes. L’algorithme argon2 est bien sécurisé mais utilise beaucoup de mémoire et peut être inapproprié pour les systèmes limités en ressources. +password_algorithm_helper=Définissez l’algorithme de hachage du mot de passe. Les algorithmes ont des exigences et une résistance différentes. L’algorithme argon2 est bien sécurisé mais utilise beaucoup de mémoire et peut être inapproprié pour les systèmes limités en ressources. enable_update_checker=Activer la vérification des mises-à-jour env_config_keys=Configuration de l'environnement env_config_keys_prompt=Les variables d'environnement suivantes seront également ajoutées à votre fichier de configuration : @@ -773,7 +773,7 @@ language=Langue ui=Thème hidden_comment_types=Catégories de commentaires masqués hidden_comment_types_description=Cochez les catégories suivantes pour masquer les commentaires correspondants des fils d'activité. Par exemple, « Label » cache les commentaires du genre « Cerise a attribué le label Bug il y a 2 heures». -hidden_comment_types.ref_tooltip=Commentaires où ce ticket a été référencé sur un autre ticket, révision, etc. +hidden_comment_types.ref_tooltip=Commentaires où ce ticket a été référencé sur un autre ticket/révision/… hidden_comment_types.issue_ref_tooltip=Commentaires où l’utilisateur change la branche/étiquette associée au ticket comment_type_group_reference=Référence comment_type_group_label=Label @@ -1154,7 +1154,7 @@ mirror_sync = synchronisé mirror_sync_on_commit=Synchroniser quand les révisions sont soumis mirror_address=Cloner depuis une URL mirror_address_desc=Insérez tous les identifiants requis dans la section Autorisation. -mirror_address_url_invalid=L’URL fournie est invalide. Vous devez échapper tous les composants de l'URL correctement. +mirror_address_url_invalid=L’URL fournie est invalide. Assurez vous que tous les composants de l'URL sont correctement échappés. mirror_address_protocol_invalid=L'URL fournie est invalide. Seuls les protocoles http(s):// ou git:// peuvent référencer un miroir. mirror_lfs=Stockage de fichiers volumineux (LFS) mirror_lfs_desc=Activer la mise en miroir des données LFS. @@ -1764,7 +1764,7 @@ issues.error_modifying_due_date=Impossible de modifier l'échéance. issues.error_removing_due_date=Impossible de supprimer l'échéance. issues.push_commit_1=a ajouté %d révision %s issues.push_commits_n=a ajouté %d révisions %s -issues.force_push_codes=`a forcé %[1]s de %[2]s à %[4]s %[6]s.` +issues.force_push_codes=`a forcé %[1]s de %[2]s %[8]s à %[4]s %[9]s %[6]s.` issues.force_push_compare=Comparer issues.due_date_form=aaaa-mm-jj issues.due_date_form_add=Ajouter une échéance @@ -2027,7 +2027,7 @@ milestones.filter_sort.most_issues=Le plus de tickets milestones.filter_sort.least_issues=Le moins de tickets signing.will_sign=Cette révision sera signée avec la clé « %s ». -signing.wont_sign.error=Impossible de vérifier la signature de la révision. +signing.wont_sign.error=Une erreur s'est produite lors des vérifications préliminaires à la signature de la révision. signing.wont_sign.nokey=Aucune clé n’est disponible sur cette instance pour signer cette révision. signing.wont_sign.never=Les révisions ne sont jamais signées. signing.wont_sign.always=Les révisions sont toujours signées. @@ -2757,7 +2757,7 @@ issues.comment.blocked_by_user = Vous ne pouvez pas créer un commentaire sur ce editor.invalid_commit_mail = Courriel invalide pour la création d'un commit. commits.browse_further = Continuer la navigation commits.renamed_from = Renommé depuis %s -pulls.nothing_to_compare_have_tag = La branche ou le tag sélectionné sont identiques. +pulls.nothing_to_compare_have_tag = La branche/tag sélectionné sont identiques. issues.blocked_by_user = Vous ne pouvez pas créer de tickets sur ce dépôt car vous avez été bloqué par son propriétaire. pulls.blocked_by_user = Vous ne pouvez pas créer une pull request sur ce dépôt car vous êtes bloqué par son propriétaire. wiki.cancel = Annuler @@ -3636,7 +3636,7 @@ auto_merge_pull_request=`a fusionné automatiquement la demande d’ajout %s vers %s push_tag=a poussé l’étiquette %[3]s de %[4]s delete_tag=a supprimé l’étiquette %[2]s de %[3]s -delete_branch=a supprimée la branche %[2]s de %[3]s +delete_branch=a supprimée la branche %[2]s de %[3]s compare_branch=Comparer compare_commits=Comparer %d révisions compare_commits_general=Comparer les révisions diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index c8f4d2a72c..1d7414a0d8 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -2678,7 +2678,7 @@ auto_merge_pull_request = `iarratas tarraingthe cumasctha go huathoibríoch %s go %s push_tag = brú %[3]s go %[4]s delete_tag = scriosta clib %[2]s ó %[3]s -delete_branch = brainse scriosta %[2]s ó %[3]s +delete_branch = brainse scriosta %[2]s ó %[3]s compare_branch = Déan comparáid compare_commits = Déan comparáid idir tiomáintí %d compare_commits_general = Déan comparáid idir tiomáintí diff --git a/options/locale/locale_he.ini b/options/locale/locale_he.ini index 26e3084809..8a5bc86163 100644 --- a/options/locale/locale_he.ini +++ b/options/locale/locale_he.ini @@ -1,3 +1,6 @@ + + + [common] webauthn_error_unable_to_process = שרת זה נכשל בעיבוד בקשתך. help = עזרה @@ -381,7 +384,6 @@ allow_password_change = הכרחת המשתמש לעדכן את סיסמת חש account_activated = חשבונך הופעל resent_limit_prompt = כבר ביקשת מייל אימות בשלושת הדקות האחרונות. נא לחכות ולנסות שוב. has_unconfirmed_mail = שלום %s, חשבונך משויך לכתובת אימייל לא מאומתת (%s). אם לא קיבלת הודעת אימות באימייל, או שאתה צריך חדשה, נא ללחוץ על הכפתור למטה. - confirmation_mail_sent_prompt = אימייל אימות חדש נשלח ל־%s. יש לבדוק את תיבת הדואר וללחוץ על הלינק תוך %s על מנת להשלים את רישום החשבון. אם כתובת המייל שגוייה, אפשר להיכנס לחשבון ולבקש דוא"ל אימות לכתובת אחרת. reset_password_mail_sent_prompt = אימייל אימות חדש נשלח ל־%s. יש לבדוק את תיבת הדואר וללחוץ על הלינק תוך %s על מנת להשלים את רישום החשבון. @@ -469,7 +471,7 @@ uploaded_avatar_not_a_image = הקובץ שהועלה לא תמונה. [repo] new_advanced = הגדרות מתקדמות -new_advanced_expand = +new_advanced_expand = owner = בעלים repo_name = שם הקרפיף repo_name_helper = שמות קרפיפים טובים הם זכירים, קצרים וייחודיים. @@ -661,10 +663,9 @@ issues.label_exclusive = בחירה בודדת issues.label_archive = לארכיון issues.label_archived_filter = הצגת תוויות מהארכיון issues.label_archive_tooltip = תוויות בארכיון לא מוצעות בחיפוש על־בסיס תווית כברירת מחדל. - issues.deleted_milestone = נמחק -issues.deleted_project = נמחק issues.self_assign_at = `שייךה עצמית %s` +issues.deleted_project = נמחק [translation_meta] test = ואהבת לרעך כמוך diff --git a/options/locale/locale_hi.ini b/options/locale/locale_hi.ini index 2f75c8d13e..e666db425d 100644 --- a/options/locale/locale_hi.ini +++ b/options/locale/locale_hi.ini @@ -39,4 +39,170 @@ organization = संगठन mirror = छवि settings = सेटिंग्स your_settings = आपकी सेटिंग्स -return_to_forgejo = फ़ोर्जेगो पे वापस जाएं \ No newline at end of file +return_to_forgejo = फ़ोर्जेगो पे वापस जाएं +access_token = एक्सेस टोकन +webauthn_insert_key = अपनी सिक्योरिटी की डालें +webauthn_press_button = अपनी सिक्योरिटी की पर बटन दबाएं… +webauthn_use_twofa = अपने फ़ोन से दो फैक्टर कोड लाएं +webauthn_unsupported_browser = आपका ब्राउज़र वेबौथ सपोर्ट नहीं करता। +webauthn_error_unknown = कोई अंजान एरर हुई, फिर से कोशिश करें। +webauthn_error_unable_to_process = सर्वर आपकी रिक्वेस्ट प्रोसेस नहीं कर पाया। +webauthn_error_empty = कृपया इस चाभी का नाम रखें। +webauthn_error_timeout = आपकी की पढ़ने से पहले टाइमआउट हो गया। पृष्ट रीलोड करें और फिर कोशिश करें। +new_project = नया प्रोजेक्ट +test = टेस्ट +locked = लॉक्ड +copy = कॉपी +copy_generic = क्लिपबोर्ड पर कॉपी करें +copy_url = URL कॉपी करें +copy_hash = कॉपी हैश +copy_path = राह कॉपी करें +admin_panel = वेबसाइट प्रबंधन +your_profile = प्रोफाइल +your_starred = सीतारित +new_mirror = नया मिरर +new_fork = नयी रिपॉजिटरी फोर्क +all = सब +sources = स्रोत +new_project_column = नया स्तम्भ +mirrors = मिर्रोर्स +toggle_menu = चालू-बंद करें +add_all = सब जोड़ें +remove_all = सारा हटाएं +new_org.title = नई संस्था +new_repo.link = नई रिपॉजिटरी +new_migrate.link = नया प्रवासन +new_org.link = नई संस्था +collaborative = सहयोगी +forks = फोर्क्स +issues = इश्यूज +milestones = महत्वपूर्ण +ok = ओके +cancel = रद्द करें +retry = फिर से करें +rerun = फिर से +save = सेव करें +add = जोड़ें +remove = हटाएं +remove_label_str = हटाएं आइटम “%s” +edit = संपादित करना +view = देखें +disabled = असक्षम किया गया +webauthn_sign_in = सिक्योरिटी की का बटन दबाएं, नहीं है तो फिर से प्लग करें। +webauthn_error_insecure = वेबौथ पर सिर्फ सुरक्षित कनेक्शन हो. HTTP टेस्ट के लिए ओरिजिन “लोकलहोस्ट” या “127.0.0.1” +new_repo.title = नई रिपॉजिटरी +pull_requests = पुल्ल करें +enabled = सक्षम किया गया +webauthn_error = सिक्योरिटी की रीड नहीं हो पा रही। +webauthn_error_duplicated = सिक्योरिटी की इस रिक्वेस्ट के लिए नहीं है। कृपया देखें की पहले से रजिस्टर्ड तो नहीं। +new_migrate.title = नया प्रवासन +activities = गतिविधियाँ +rerun_all = फिर से सारे काम करें +filter.is_template = टेम्पलेट्स +filter.not_template = टेम्पलेट्स नहीं +filter.public = सार्वजनिक +filter.private = निजी +copy_success = कॉपी हो गया! +copy_content = विषय-वस्तु कॉपी करें +copy_branch = शाखा नाम कॉपी करें +write = लिखें +archived = संग्रहीत +show_timestamps = समय-मोहर दिखाएं +value = मूल्य +confirm_delete_selected = ये सारे डिलीट कर दें? +filter = फ़िल्टर +filter.is_fork = फोर्क्स +filter.not_fork = फोर्क्स नहीं +filter.is_mirror = मिर्रोर्स +copy_error = कॉपी नहीं हुआ +copy_type_unsupported = इस तरह की फाइल कॉपी नहीं होगी +loading = लोड हो रहा… +error = त्रुटि +go_back = वापस जाएं +unknown = अनजान +pin = पिन +unpin = पिन हटाएं +artifacts = पुरावशेष +concept_system_global = वैश्विक +show_log_seconds = सेकंड दिखाएं +show_full_screen = पूरी स्क्रीन पे दिखाएं +name = नाम +filter.clear = फ़िल्टर हटाएं +filter.is_archived = संगृहीत +filter.not_archived = संगृहीत नहीं +filter.not_mirror = मिर्रोर्स नहीं +error413 = आपका कोटा ख़तम हो गया। +concept_user_individual = एकल +confirm_delete_artifact = क्या आप ये पुरावशेष हटाना चाहते हैं "%s" ? +concept_code_repository = रिपॉजिटरी +concept_user_organization = संस्था +download_logs = लॉग डाउनलोड करें +rss_feed = RSS फीड +preview = पूर्वावलोकन +error404 = जो पृष्ट आप देखना चाहते हैं या तो है ही नहीं , हटा दिया गया या आप देखने को अधिकृत नहीं की देख पाएं। +invalid_data = डाटा मान्य नहीं: %v +never = कभी नहीं + +[search] +search = ढूंढें… +type_tooltip = ढूंढ़ने का जैसे +commit_kind = कमिट्स ढूढें… +runner_kind = रनर्स ढूंढें… +fuzzy = फ़ज़्ज़ी +fuzzy_tooltip = जो उपाय ढूंढे गए टर्म से जुड़े हैं भी दिखाएं +exact_tooltip = सिर्फ वो रिजल्ट दिखाएं जो एकदम सर्च टर्म से मिलते हैं +repo_kind = रेपो ढूंढें… +user_kind = यूजर ढूंढें… +org_kind = संस्था ढूंढें… +issue_kind = इश्यूज ढूंढें… +union_tooltip = वो उपाय भी दिखाएं जो जगह छोढ़े कीवर्ड्स से मिलते हैं +union = केंद्र +team_kind = टीम ढूंढें… +exact = एकदम +branch_kind = शाखा ढूंढें… +pull_kind = पुल्स ढूंढें… +regexp = रेगएक्सप् +code_kind = कोड ढूंढें… +code_search_unavailable = कोड ढूंढ़ना अभी नहीं हो सकता। कृपया एडमिनिस्ट्रेटर से बात करें। +no_results = मिलता उपाय नहीं है। +regexp_tooltip = व्याख्या करें सर्च टर्म की व्यावहारिक तरीके से +package_kind = पैकेजेस ढूंढें… +project_kind = प्रोजेक्ट्स ढूंढें… +keyword_search_unavailable = कीवर्ड से ढूंढ़ना अभी नहीं हो सकता। कृपया एडमिनिस्ट्रेटर से बात करें। + +[aria] +navbar = संचालन बार +footer = फुटर +footer.software = इस सॉफ्टवेयर के बारे में +footer.links = संयोजक + +[heatmap] +contributions_few = योगदानों +more = ज़्यादा +contributions_zero = कोई योगदान नहीं +contributions_format = {योगदान} आज {दिन} {महीना} {साल} +contributions_one = योगदान +less = कम +number_of_contributions_in_the_last_12_months = %s योगदान पिछले 12 महीनो में + +[editor] +buttons.italic.tooltip = इटैलिक लेख जोड़ें +buttons.heading.tooltip = शीर्षक जोड़ें +buttons.bold.tooltip = बोल्ड लेख जोड़ें +buttons.list.unordered.tooltip = बुलेट लिस्ट जोड़ें +buttons.quote.tooltip = पाठ जोड़ें +buttons.list.ordered.tooltip = अंकित लिस्ट जोड़ें +buttons.list.task.tooltip = कार्यों की सूचि जोड़ें +buttons.mention.tooltip = यूजर या टीम को ज़ाहिर करें +buttons.ref.tooltip = इशू या पुल्ल निवेदन ज़ाहिर करें +buttons.disable_monospace_font = एकसमान रिक्ति फ़ॉन्ट बंद करें +buttons.indent.tooltip = चीज़ों को एक लेवल नेस्ट करें +buttons.code.tooltip = कोड जोड़ें +buttons.link.tooltip = संयोजक जोड़ें +buttons.enable_monospace_font = एकसमान रिक्ति फ़ॉन्ट चालू करें +buttons.switch_to_legacy.tooltip = पुराना एडिटर इस्तेमाल करें +table_modal.header = टेबल जोड़ें +table_modal.placeholder.header = शीर्षक +table_modal.placeholder.content = विषयवस्तु +buttons.new_table.tooltip = टेबल जोड़ें +buttons.unindent.tooltip = चीज़ों को एक लेवल नेस्ट से निकालें \ No newline at end of file diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index c6eca84ac7..841fb76bbe 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -1694,7 +1694,7 @@ create_repo=létrehozott tárolót: %s rename_repo=átnevezte a(z) %[1]s tárolót %[3]s-ra/re transfer_repo=áthelyezett egy tárolót innen: %s ide: %s delete_tag=címke %[2]s törölve innen: %[3]s -delete_branch=ág %[2]s törölve innen: %[3]s +delete_branch=ág %[2]s törölve innen: %[3]s compare_branch=Összehasonlítás compare_commits=%d commit összehasonlítása compare_commits_general=Commitok összehasonlítása diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 56993bec0d..c935bd9540 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -1368,7 +1368,7 @@ create_repo=repositori dibuat %s rename_repo=ganti nama gudang penyimpanan dari %[1]s ke %[3]s transfer_repo=ditransfer repositori %s ke %s delete_tag=tag dihapus %[2]s dari %[3]s -delete_branch=cabang dihapus %[2]s dari %[3]s +delete_branch=cabang dihapus %[2]s dari %[3]s compare_commits=Bandingkan %d melakukan [tool] diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 50405ed756..ca1d1433b6 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -1640,7 +1640,7 @@ issues.error_modifying_due_date=Impossibile modificare la scadenza. issues.error_removing_due_date=Impossibile rimuovere la scadenza. issues.push_commit_1=ha aggiunto %d commit %s issues.push_commits_n=ha aggiunto %d commit %s -issues.force_push_codes=`ha forzato l'immissione %[1]s da %[2]s a %[4]s %[6]s` +issues.force_push_codes=`ha forzato l'immissione %[1]s da %[2]s %[8]s a %[4]s %[9]s %[6]s` issues.force_push_compare=Confronta issues.due_date_form=aaaa-mm-dd issues.due_date_form_add=Aggiungi scadenza @@ -3624,7 +3624,7 @@ merge_pull_request=`ha fuso la richiesta di modifica %[3]s#%[2]s transfer_repo=repository %s trasferito in %s push_tag=ha inviato il tag %[3]s su %[4]s delete_tag=tag eliminato %[2]s da %[3]s -delete_branch=branch eliminato %[2]s da %[3]s +delete_branch=branch eliminato %[2]s da %[3]s compare_branch=Confronta compare_commits=Confronta %d commits compare_commits_general=Confronta commit diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index f3a8922f88..5b8e8019f2 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -1047,7 +1047,6 @@ language.title = 既定の言語 keep_activity_private.description = 公開アクティビティは、あなたとインスタンス管理者にのみ表示されます。 language.description = この言語はアカウントに保存され、ログイン後にデフォルトとして使用されます。 language.localization_project = Forgejo をあなたの言語に翻訳するのを手伝ってください。詳細はこちら。 - quota = クオータ [repo] @@ -1723,7 +1722,7 @@ issues.error_modifying_due_date=期日を変更できませんでした。 issues.error_removing_due_date=期日を削除できませんでした。 issues.push_commit_1=が %d コミット追加 %s issues.push_commits_n=が %d コミット追加 %s -issues.force_push_codes=`が %[1]s を強制プッシュ ( %[2]s から %[4]s へ ) %[6]s` +issues.force_push_codes=`が %[1]s を強制プッシュ ( %[2]s %[8]s から %[4]s %[9]s へ ) %[6]s` issues.force_push_compare=比較 issues.due_date_form=yyyy-mm-dd issues.due_date_form_add=期日の追加 @@ -3538,7 +3537,7 @@ auto_merge_pull_request=`がプルリクエスト %[3]s#%[2]s%s を %s へ移転しました push_tag=がタグ %[3]s%[4]s にプッシュしました delete_tag=がタグ %[2]s を %[3]s から削除しました -delete_branch=がブランチ %[2]s を %[3]s から削除しました +delete_branch=がブランチ %[2]s%[3]s から削除しました compare_branch=比較 compare_commits=%d件のコミットを比較 compare_commits_general=コミットを比較 diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index 5e2354f3a8..8e8d9b1d6b 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -384,7 +384,7 @@ allow_password_change=사용자에게 비밀번호 변경을 요청 (권장됨) reset_password_mail_sent_prompt=확인 메일이 %s로 전송되었습니다. 받은 편지함으로 도착한 메일을 %s 안에 확인해서 비밀번호 찾기 절차를 완료하십시오. active_your_account=계정 활성화 account_activated=계정이 활성화 되었습니다 -prohibit_login = +prohibit_login = resent_limit_prompt=활성화를 위한 이메일을 이미 전송했습니다. 3분 내로 이메일을 받지 못한 경우 재시도해주세요. has_unconfirmed_mail=안녕하세요 %s, 이메일 주소(%s)가 확인되지 않았습니다. 확인 메일을 받으시지 못하겼거나 새로운 확인 메일이 필요하다면, 아래 버튼을 클릭해 재발송하실 수 있습니다. resend_mail=여기를 눌러 확인 메일 재전송 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 4a98e1aa9d..868babacfb 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1089,6 +1089,7 @@ regenerate_token = Izveidot no jauna access_token_regeneration = Izveidot piekļuves pilnvaru no jauna regenerate_token_success = Pilnvara tika izveidota no jauna. Lietotnēm, kas to izmanto, vairs nav piekļuve kontam, un tajās ir jāizmanto jaunā pilnvara. access_token_regeneration_desc = Pilnvaras izveidošana no jauna atsauks piekļuvi kontam lietotnēm, kuras to izmanto. Darbība ir neatgriezeniska. Turpināt? +ssh_token_help_ssh_agent = vai, ja izmanto SSH aģentu (ar iestatītu mainīgo SSH_AUTH_SOCK): [repo] new_repo_helper=Glabātava satur visas projekta datnes, tajā skaitā izmaiņu vēsturi. Jau tiek izmantota kaut kur citur? Pārcelt glabātavu. @@ -1152,7 +1153,7 @@ mirror_interval_invalid=Starplaiks starp spoguļošanu nav derīgs. mirror_sync_on_commit=Sinhronizēt, kad tiek aizgādāti iesūtījumi mirror_address=Klonēt no URL mirror_address_desc=Nepieciešamie pieslēgšanās dati jānorāda pilnvarošanas sadaļā. -mirror_address_url_invalid=Norādītais URL ir nederīgs. Visas URL daļas ir jānorāda pareizi. +mirror_address_url_invalid=Norādītais URL ir nederīgs. Jāpārliecinās, ka visas URL daļas ir pareizi norādītas. mirror_address_protocol_invalid=Norādītais URL ir nederīgs. Var spoguļot tikai no http(s):// vai git:// adresēm. mirror_lfs=Lielu datņu krātuve (LFS) mirror_lfs_desc=Aktivēt LFS datu spoguļošanu. @@ -1242,7 +1243,7 @@ migrate_items_releases=Laidienus migrate_repo=Pārcelt glabātavu migrate.clone_address=Pārcelt/klonēt no URL migrate.clone_address_desc=Esošas glabātavas HTTP(S) vai Git "clone" URL -migrate.github_token_desc=Šeit var pievienot vienu vai vairākas ar komatiem atdalītas pilnvaras, lai pārcelšana būtu ātrāka, ja tā tiek ierobežota no GitHub API puses. Uzmanību: šīs iespējas ļaunprātīga izmantošana var pārkāpt pakalpojumu sniedzēja noteikumus un novest pie piekļuves liegšanas kontam. +migrate.github_token_desc=Šeit var pievienot vienu vai vairākas ar komatiem atdalītas pilnvaras, lai padarītu pārcelšanu ātrāku ar GitHub API pieprasījumu biežuma ierobežojuma apiešanu. Uzmanību: šīs iespējas ļaunprātīga izmantošana var pārkāpt pakalpojumu sniedzēja noteikumus un var novest pie piekļuves liegšanas kontam/iem. migrate.clone_local_path=vai servera lokālais ceļš migrate.permission_denied=Nav ļauts ievietot vietējas glabātavas. migrate.permission_denied_blocked=Nav iespējams ievietot no neatļautiem saimniekdatoriem, lūgums vaicāt pārvaldītājam pārbaudīt ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS iestatījumus. @@ -1762,7 +1763,7 @@ issues.error_modifying_due_date=Neizdevās izmainīt izpildes termiņu. issues.error_removing_due_date=Neizdevās noņemt izpildes termiņu. issues.push_commit_1=pievienoja %d iesūtījumu %s issues.push_commits_n=pievienoja %d iesūtījumus %s -issues.force_push_codes=`veica uzspiestu aizgādāšanu zarā %[1]s no %[2]s uz %[4]s %[6]s` +issues.force_push_codes=`veica uzspiestu aizgādāšanu zarā %[1]s no %[2]s %[8]s uz %[4]s %[9]s %[6]s` issues.force_push_compare=Salīdzināt issues.due_date_form=dd.mm.gggg. issues.due_date_form_add=Pievienot izpildes termiņu @@ -3635,7 +3636,7 @@ auto_merge_pull_request=`automātiski iekļāva izmaiņu pieprasījumu %s push_tag=aizgādāja birku %[3]s uz %[4]s delete_tag=izdzēsa birku %[2]s no %[3]s -delete_branch=izdzēsa zaru %[2]s no %[3]s +delete_branch=izdzēsa zaru %[2]s no %[3]s compare_branch=Salīdzināt compare_commits=Salīdzināt %d iesūtījumus compare_commits_general=Salīdzināt iesūtījumus diff --git a/options/locale/locale_nb_NO.ini b/options/locale/locale_nb_NO.ini index 7dda7499a5..59673fd4e3 100644 --- a/options/locale/locale_nb_NO.ini +++ b/options/locale/locale_nb_NO.ini @@ -133,16 +133,15 @@ webauthn_error_duplicated = Sikkerhetsnøkkelen er ikke tillatt for denne foresp webauthn_error_timeout = Et tidsavbrudd oppsto før nøkkelen din kunne leses. Vennligst last inn siden på nytt og prøv igjen. new_fork = Ny fork av repository collaborative = Samarbeidende - -tracked_time_summary = Oppsummering av sporet tid basert på problemfiltre -pull_requests = Pull requests -issues = Saker -copy_branch = Kopier branch navn -error404 = Siden du forsøker å nå eksisterer ikke, er blitt fjernet eller du har ikke tilgang til å se den. error413 = Du har brukt opp kvoten din. +issues = Saker unpin = Løsne filter.is_fork = Forks filter.not_fork = Ikke forks +pull_requests = Pull requests +copy_branch = Kopier branch navn +error404 = Siden du forsøker å nå eksisterer ikke, er blitt fjernet eller du har ikke tilgang til å se den. +tracked_time_summary = Oppsummering av sporet tid basert på problemfiltre [search] search = Søk… @@ -151,26 +150,25 @@ fuzzy = Fuzzy union = Union regexp = RegExp exact = Nøyaktig - -fuzzy_tooltip = Inkluder resultater som også stemmer godt overens med søketermen -union_tooltip = Inkluder resultater som samsvarer med ett eller flere av nøkkelordene adskilt med mellomrom -exact_tooltip = Inkluder kun resultater som samsvarer nøyaktig med søkeordet -regexp_tooltip = Tolk søkeordet som et regulæruttrykk -repo_kind = Søk i repositorer… -user_kind = Søk i brukere… -org_kind = Søk i organisasjoner… team_kind = Søk i teams… code_kind = Søk i kode… -code_search_unavailable = Kodesøk er ikke tilgjengelig. Kontakt administratoren. package_kind = Søk i pakker… project_kind = Søk i prosjekter… branch_kind = Søk i brancher… commit_kind = Søk i commits… -runner_kind = Søk i runners… -no_results = Ingen treff funnet. -issue_kind = Søk i saker… +regexp_tooltip = Tolk søkeordet som et regulæruttrykk pull_kind = Søk i pulls… keyword_search_unavailable = Søk etter nøkkelord er for øyeblikket ikke tilgjengelig. Kontakt administratoren. +exact_tooltip = Inkluder kun resultater som samsvarer nøyaktig med søkeordet +repo_kind = Søk i repositorer… +fuzzy_tooltip = Inkluder resultater som også stemmer godt overens med søketermen +org_kind = Søk i organisasjoner… +issue_kind = Søk i saker… +runner_kind = Søk i runners… +no_results = Ingen treff funnet. +union_tooltip = Inkluder resultater som samsvarer med ett eller flere av nøkkelordene adskilt med mellomrom +code_search_unavailable = Kodesøk er ikke tilgjengelig. Kontakt administratoren. +user_kind = Søk i brukere… [auth] verify = Bekreft @@ -184,81 +182,70 @@ oauth_signup_submit = Fullfør konto [home] uname_holder = Brukernavn eller e-postadresse -[aria] -navbar = Navigasjonslinje -footer = Bunntekst -footer.software = Om dette programmet -footer.links = Linker - [heatmap] -number_of_contributions_in_the_last_12_months = %s bidrag de siste 12 månedene contributions_zero = Ingen bidrag -contributions_format = {contributions} den {day} {month} {year} +number_of_contributions_in_the_last_12_months = %s bidrag de siste 12 månedene contributions_one = bidrag -contributions_few = bidrag +contributions_format = {contributions} den {day} {month} {year} less = Mindre +contributions_few = bidrag more = Mer [editor] buttons.heading.tooltip = Legg til overskrift buttons.bold.tooltip = Legg til uthevet tekst buttons.italic.tooltip = Legg til kursiv text -buttons.quote.tooltip = Siter tekst -buttons.code.tooltip = Legg til kode -buttons.link.tooltip = Legg til link buttons.list.unordered.tooltip = Legg til punktliste buttons.list.ordered.tooltip = Legg til nummerert liste -buttons.list.task.tooltip = Legg til liste over saker +buttons.link.tooltip = Legg til link +buttons.quote.tooltip = Siter tekst buttons.mention.tooltip = Nevn en bruker eller team -buttons.ref.tooltip = Referanse til en sak eller pull request -buttons.switch_to_legacy.tooltip = Bruk den gamle editoren istedenfor -buttons.enable_monospace_font = Aktiver monospace font -buttons.disable_monospace_font = Deaktiver monospace font -buttons.indent.tooltip = Grupper elementene med et nivå -buttons.unindent.tooltip = Pakk ut elementene med et nivå -buttons.new_table.tooltip = Legg til tabell +buttons.list.task.tooltip = Legg til liste over saker +buttons.code.tooltip = Legg til kode table_modal.header = Legg til tabell table_modal.placeholder.header = Overskrift table_modal.placeholder.content = Innhold -table_modal.label.rows = Rader table_modal.label.columns = Kolonner -link_modal.header = Legg til en link +buttons.new_table.tooltip = Legg til tabell +table_modal.label.rows = Rader +buttons.switch_to_legacy.tooltip = Bruk den gamle editoren istedenfor +buttons.disable_monospace_font = Deaktiver monospace font +buttons.ref.tooltip = Referanse til en sak eller pull request +buttons.indent.tooltip = Grupper elementene med et nivå +buttons.unindent.tooltip = Pakk ut elementene med et nivå link_modal.url = Url link_modal.description = Beskrivelse +link_modal.header = Legg til en link +buttons.enable_monospace_font = Aktiver monospace font link_modal.paste_reminder = Tips: Når du har en URL i utklippstavlen kan du lime den direkte inn i editoren for å lage en lenke. +[aria] +footer = Bunntekst +navbar = Navigasjonslinje +footer.links = Linker +footer.software = Om dette programmet + [filter] string.asc = A - Z string.desc = Z - A [error] occurred = En feil oppstod -report_message = Hvis du mener dette er en feil i Forgejo kan du søke på Codeberg eller åpne en ny sak. not_found = Kunne ikke finne målet. +report_message = Hvis du mener dette er en feil i Forgejo kan du søke på Codeberg eller åpne en ny sak. network_error = Nettverks feil server_internal = Intern server feil -[startpage] -app_desc = En enkel Git-tjeneste du kan drifte selv -install = Enkel å installere -install_desc = Du kan enkelt kjøre programfilen for din platform, bruke Docker, eller hente den som en ferdig pakke. -platform = Plattformuavhengig -platform_desc = Forgejo fungerer på frie operativsystemer som Linux og FreeBSD, og støtter flere CPU-arkitekturer. Velg den plattformen du foretrekker! -lightweight = Lettvekt -lightweight_desc = Forgejo krever lite ressurser og kan kjøres på en rimelig Raspberry Pi. Spar strøm og miljøet! -license = Åpen kildekode -license_desc = Last ned Forgejo! Bli med ved å bidra for å gjøre prosjektet enda bedre. Ikke vær redd for å bli en bidragsyter! - [install] +docker_helper = Dersom du bruker Forgejo med Docker, anbefales det å lese dokumentasjonen før du gjør endringer i konfigurasjonen. +db_title = Database innstillinger +require_db_desc = Forgejo krever MySQL, PostgreSQL, SQLite3 eller TiDB (MySQL protokoll). +db_type = Database type +password = Passord +user = Brukernavn install = Installasjon title = Førstegangsoppsett -docker_helper = Dersom du bruker Forgejo med Docker, anbefales det å lese dokumentasjonen før du gjør endringer i konfigurasjonen. -require_db_desc = Forgejo krever MySQL, PostgreSQL, SQLite3 eller TiDB (MySQL protokoll). -db_title = Database innstillinger -db_type = Database type host = Server -user = Brukernavn -password = Passord db_name = Database navn db_schema = Skjema db_schema_helper = La stå tomt for databasens standardverdi ("public"). @@ -266,4 +253,15 @@ ssl_mode = SSL path = Sti sqlite_helper = Sti til SQLite3-databasen.
Bruk absolutt filsti dersom Forgejo kjøres som en tjeneste. reinstall_error = Du prøver å installere i en eksisterende Forgejo-database -reinstall_confirm_message = Å installere på nytt med en eksisterende Forgejo-database kan føre til problemer. I de fleste tilfeller bør du bruke din eksisterende "app.ini" for å kjøre Forgejo. Hvis du vet hva du gjør, og vil fortsette, bekreft følgende: \ No newline at end of file +reinstall_confirm_message = Å installere på nytt med en eksisterende Forgejo-database kan føre til problemer. I de fleste tilfeller bør du bruke din eksisterende "app.ini" for å kjøre Forgejo. Hvis du vet hva du gjør, og vil fortsette, bekreft følgende: + +[startpage] +install = Enkel å installere +platform = Plattformuavhengig +install_desc = Du kan enkelt kjøre programfilen for din platform, bruke Docker, eller hente den som en ferdig pakke. +lightweight_desc = Forgejo krever lite ressurser og kan kjøres på en rimelig Raspberry Pi. Spar strøm og miljøet! +app_desc = En enkel Git-tjeneste du kan drifte selv +lightweight = Lettvekt +license = Åpen kildekode +platform_desc = Forgejo fungerer på frie operativsystemer som Linux og FreeBSD, og støtter flere CPU-arkitekturer. Velg den plattformen du foretrekker! +license_desc = Last ned Forgejo! Bli med ved å bidra for å gjøre prosjektet enda bedre. Ikke vær redd for å bli en bidragsyter! \ No newline at end of file diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index fe504b2a04..bf60b23ef8 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -333,7 +333,7 @@ has_unconfirmed_mail = Moin %s, du hest eene nich utwiesen E-Mail-Adress (%s< non_local_account = Frömde Brukers könen hör Passwoord nich dör de Forgejo-Internett-Brukerschnittstee vernejen. openid_register_desc = De utköört OpenID-URI is unbekannt. Verbinn dat hier mit eenem nejen Konto. disable_forgot_password_mail = Konto-Torügghalen is utknipst, denn keene E-Mail is inricht. Bidde kuntakteer dienen Sied-Chef. -authorize_application_description = Wenn du de Togang verlöövst, kann dat all diene Konto-Informatioon lesen un schrieven, ok privaate Repos un Vereenigungen. +authorize_application_description = Wenn du Togang verlöövst, kann dat all diene Konto-Informatioon lesen un schrieven, ok privaate Repos un Vereenigungen. authorization_failed_desc = Dat Anmellen is fehlslagen, denn wi hebben eene ungültig Anfraag funnen. Bidde kuntakteer de Chef vun de Programm, wat du anmellen willst. twofa_scratch_used = Du hest dien Eenmaalpasswoord bruukt. Du büst to de Twee-Faktooren-Instellens-Sied umleit worden, waar du 2FA utknipsen of een nejes Eenmaalpasswoord maken kannst. oauth.signin.error.temporarily_unavailable = Anmellen fehlslagen, denn de Anmell-Server is jüüst nich verföögbaar. Bidde versöök dat naher noch eenmaal. @@ -843,7 +843,7 @@ profile_desc = Över di hidden_comment_types_description = Kommentaar-Arden, wat hier utköört sünd, worden in Gefall-Sieden nich wiest. Wenn du to’n Bispööl »Vermark« utköörst, worden all de »›Bruker‹ hett ›Vermark‹ hentoföögt/wegdaan«-Kommentaren wegdaan. email_desc = Diene Höövd-E-Mail-Adress word för Narichtens, Passwoord-Torügghalen un, wenn se nich verburgen is, Git-Aktioonen över ’t Internett bruukt. can_not_add_email_activations_pending = Een Aktiveren staht noch ut. Wenn du eene neje E-Mail-Adress hentofögen willst, versöök dat in een paar Menüten noch eenmaal. -email_deletion_desc = De E-Mail-Adress un daarmit verbunnen Informatioon word ut dienem Konto wegdaan. Git-Kommitterens vun deeser E-Mail-Adress worden nich ännert. Wiedermaken? +email_deletion_desc = Deese E-Mail-Adress un daarmit verbunnen Informatioon word ut dienem Konto wegdaan. Git-Kommitterens vun deeser E-Mail-Adress worden nich ännert. Wiedermaken? principal_desc = Deese SSH-Zertifikaat-Höövdmannen sünd mit dienem Konto verbunnen un geven kumpleten Togriep up diene Repositoriums. add_email_confirmation_sent = Eene Utwiesens-E-Mail is an »%s« schickt worden. Um diene E-Mail-Adress uttowiesen, kiek bidde in dienen E-Mail-Ingang un folg de Verwies daarin in de anner %s. ssh_desc = Deese publiken SSH-Slötels sünd mit dienem Konto verbunnen. De tohörig privaate Slötel gifft kumpleten Togriep up diene Repositoriums. SSH-Slötels, wat utwiest worden sünd, könen bruukt worden, um SSH-unnerschreven Git-Kommitterens uttowiesen. @@ -887,6 +887,7 @@ access_token_regeneration = Togang-Teken neei maken regenerate_token = Neei maken access_token_regeneration_desc = Wenn du een Teken neei maakst, verlesen Anwennens, wat ’t bruken, Togang to dienem Konto. Dat kann nich torüggnohmen worden. Wiedermaken? regenerate_token_success = Dat Teken is neei maakt worden. Anwennens, wat ’t bruken, hebben keenen Togang to dienem Konto mehr un mutten mit de nejen Teken verneeit worden. +ssh_token_help_ssh_agent = of, wenn du eenen SSH-Agenten bruukst (un de SSH_AUTH_SOCK-Variaabel sett is): [repo] rss.must_be_on_branch = Du muttst up eenem Twieg wesen, um eenen RSS-Schuuv to hebben. @@ -1602,7 +1603,7 @@ issues.due_date_invalid = Dat Anstahns-Datum is ungültig of buten de Rieg. Bidd issues.dependency.remove = Wegdoon issues.dependency.issue_close_blocks = Deeses Gefall blockeert dat Dichtmaken vun deesen Gefallens issues.review.outdated_description = Inholl hett sik ännert, siet deeser Kommentaar schreven worden is -issues.force_push_codes = `hett %[1]s vun %[2]s to %[4]s %[6]s dwangsschuven` +issues.force_push_codes = `hett %[1]s vun %[2]s %[8]s to %[4]s %[9]s %[6]s dwangsschuven` issues.dependency.pr_remove_text = Dat word de Ofhangen vun deesem Haalvörslag wegdoon. Wiedermaken? issues.review.pending = Staht ut issues.review.option.hide_outdated_comments = Verollte Kommentarens verbargen @@ -1638,7 +1639,7 @@ pulls.showing_specified_commit_range = Blots Ännerns vun Kommitterens %[1]s bit pulls.review_only_possible_for_full_diff = Nakieken gaht blots, wenn de hele Unnerscheed wiest word pulls.filter_changes_by_commit = Na Kommitteren filtern pulls.nothing_to_compare = Deese Twiegen sünd gliek. ’t is nich nödig, eenen Haalvörslag to maken. -pulls.nothing_to_compare_have_tag = De utköört Twieg/Mark sünd gliek. +pulls.nothing_to_compare_have_tag = De utköört Twiegen/Marken sünd gliek. pulls.create = Haalvörslag maken pulls.title_desc_one = will %[1]d Kommitteren vun %[2]s na %[3]s tosamenföhren pulls.merged_title_desc_one = hett %[1]d Kommitteren vun %[2]s na %[3]s %[4]s tosamenföhrt @@ -2547,12 +2548,12 @@ issues.label_exclusive_desc = Benööm de Vermark Rebeet/Ding, daar issues.label_exclusive_warning = Elkeen anner Vermark in de sülve Rebeet word wegdaan, wenn de Vermarkens vun eenem Gefall of Haalvörslag bewarkt worden. blame.ignore_revs.failed = Kunn de Versioonen in de .git-blame-ignore-revs nich minnachten. invisible_runes_line = `Deese Rieg hett verburgen Unicode-Bookstavens` -mirror_address_url_invalid = De angeven URL is ungültig. Du muttst all Delen vun de URL recht utkielen. +mirror_address_url_invalid = De angeven URL is ungültig. Wees wiss, dat de Delen vun de URL recht utkielt sünd. mirror_address_protocol_invalid = De angeven URL is ungültig. Blots Steden vun de Aarden »http(s)://« of »git://« könen tum Spegeln bruukt worden. mirror_use_ssh.helper = Forgejo spegelt dat Repositorium mit Git över SSH un maakt för di een Slötelpaar, wenn du deese Instellen utköörst. Du muttst wiss maken, dat de maakt publike Slötel dat Recht kriggt, to de Enn-Repositorium to schuven. Wenn du dat utköörst, kannst du keen Anmellen mit Passwoord bruken. migrate.permission_denied_blocked = Du kannst nich vun verboden Servers importeren; bidde fraag de Chef, of he de Instellens ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS överprüfen maag. blame.ignore_revs = Minnacht Versioonen in .git-blame-ignore-revs. Klick hier, um daar överwegtogahn un de normaale Schüld-Ansicht to wiesen. -migrate.github_token_desc = Du kannst hier een of mehr Tekens angeven, wat mit Kommas trennt sünd, um dat Umtrecken fixer to maken, um de GitHub-API-Togrieps-Begrenz. WAHRSCHAU: Wenn du dat missbruukst, kannst du de Richtlienjen vun de Deenstbedriev verletzen un dien Konto kann sperrt worden. +migrate.github_token_desc = Du kannst hier een of mehr Tekens angeven, wat mit Kommas trennt sünd, um de GitHub-API-Togrieps-Begrenz to umgahn un so dat Umtrecken fixer to maken. WAHRSCHAU: Wenn du dat missbruukst, kannst du de Richtlienjen vun de Deenstbedriev verletzen un diene Konten könen villicht sperrt worden. issues.edit.already_changed = Kann Ännerns an de Gefall nich sekern. Dat schient, dat de Inholl al vun een anner Bruker ännert worden is. Bidde laad de Sied neei un versöök, dat dann noch eenmaal to bewarken, daarmit du hör Ännerns nich överschriffst broken_message = De Git-Daten unner deesem Repositorium könen nich lesen worden. Kuntakteer de Chef vun deeser Instanz of löske dat Repositorium. ambiguous_runes_header = `Deese Datei enthollt verwesselbaare Unicode-Bookstavens` @@ -3304,7 +3305,7 @@ comment_issue = `hett up Gefall %[3]s #%[2]s kommenteert` comment_pull = `hett up Haalvörslag %[3]s #%[2]s kommenteert` auto_merge_pull_request = `hett Haalvörslag %[3]s #%[2]s automatisk tosamenföhrt` transfer_repo = hett Repositorium %s na %s överdragen -delete_branch = hett Twieg %[2]s vun %[3]s lösket +delete_branch = hett Twieg %[2]s vun %[3]s lösket compare_branch = Verglieken compare_commits = %d Kommitterens verglieken compare_commits_general = Kommitterens verglieken diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 7f121395b9..c4f586338b 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -343,7 +343,7 @@ invalid_db_table = De database tabel "%s" is ongeldig: %v allow_dots_in_usernames = Sta gebruikers toe om punten te gebruiken in hun gebruikersnaam. Heeft geen invloed op bestaande accounts. enable_update_checker = Updatecontrole inschakelen invalid_password_algorithm = Ongeldig wachtwoord hash-algoritme -password_algorithm_helper = Stel het hashing-algoritme voor wachtwoorden in. De algoritmes hebben verschillende vereisten en sterkte. Het argon2-algoritme is tamelijk veilig, maar gebruikt veel geheugen en kan ongeschikt zijn voor kleine systemen. +password_algorithm_helper = Stel het hashing-algoritme voor wachtwoorden in. De algoritmes hebben verschillende vereisten en sterktes. Het argon2-algoritme is tamelijk veilig, maar gebruikt veel geheugen en kan ongeschikt zijn voor kleine systemen. run_user_helper = De gebruikersnaam van het besturingssysteem waaronder Forgejo draait. Merk op dat deze gebruiker toegang moet hebben tot de hoofdmap van de repository. require_sign_in_view.description = Beperk de inhoudstoegang tot aangemelde gebruikers. Bezoekers kunnen alleen de verificatiepagina's bezoeken. enable_update_checker_helper_forgejo = Het zal periodiek controleren op nieuwe Forgejo-versies door een TXT DNS-record op release.forgejo.org te controleren. @@ -559,7 +559,7 @@ team_invite.text_2 = Klik alstublieft op de volgende link om aan het team deel t admin.new_user.text = Klik hier om deze gebruiker te beheren vanuit het beheerderspaneel. password_change.subject = Uw wachtwoord is gewijzigd password_change.text_1 = Het wachtwoord voor je account is zojuist gewijzigd. -reset_password.text_1 = +reset_password.text_1 = totp_disabled.subject = TOTP is uitgeschakeld primary_mail_change.subject = Uw primaire e-mail is gewijzigd totp_disabled.no_2fa = Er zijn geen andere 2FA methodes meer geconfigureerd, wat betekent dat het niet langer nodig is om in te loggen op uw account met 2FA. @@ -815,7 +815,7 @@ activate_email=Stuur activatie activations_pending=Activaties in behandeling delete_email=Verwijder email_deletion=Verwijder e-mailadres -email_deletion_desc=Het e-mailadres en verwante informatie worden verwijderd uit je account. Git commits van dit e-mailadres blijven ongewijzigd. Wil je doorgaan? +email_deletion_desc=Dit e-mailadres en verwante informatie worden verwijderd uit je account. Git commits van dit e-mailadres blijven ongewijzigd. Wil je doorgaan? email_deletion_success=Het e-mailadres is verwijderd. theme_update_success=Je thema is bijgewerkt. theme_update_error=Het geselecteerde thema bestaat niet. @@ -1092,6 +1092,7 @@ access_token_regeneration = Toegangstoken opnieuw genereren regenerate_token = Opnieuw genereren regenerate_token_success = De token is opnieuw gegenereerd. Toepassingen die het gebruiken, hebben niet langer toegang tot uw account en moeten worden bijgewerkt om de nieuwe token te gebruiken. access_token_regeneration_desc = Als u een token opnieuw genereert, wordt de toegang tot uw account ingetrokken voor toepassingen die de token gebruiken. Dit kan niet ongedaan worden gemaakt. Doorgaan? +ssh_token_help_ssh_agent = of, als u een SSH-agent gebruikt (met de variabele SSH_AUTH_SOCK ingesteld): [repo] owner=Eigenaar @@ -1222,7 +1223,7 @@ migrate_items_releases=Releases migrate_repo=Migreer repository migrate.clone_address=Migreer / kloon van URL migrate.clone_address_desc=De HTTP(S) of Git "kloon" URL van een bestaande repository -migrate.github_token_desc=Je kunt hier een of meerdere tokens met komma gescheiden plaatsen om sneller te migreren door de GitHub API limiet te beperken. WAARSCHUWING: Het misbruik van deze functie kan in strijd zijn met het beleid van de serviceprovider en leiden tot het blokkeren van rekeningen. +migrate.github_token_desc=U kunt hier een of meer tokens plaatsen, gescheiden door komma's, om de migratie te versnellen door de GitHub API-limiet te omzeilen. WAARSCHUWING: Misbruik van deze functie kan in strijd zijn met het beleid van de serviceprovider en kan ertoe leiden dat uw account(s) worden geblokkeerd. migrate.clone_local_path=of een lokaal pad migrate.permission_denied=U bent niet gemachtigd om deze lokale repositories te importeren. migrate.permission_denied_blocked=Je kunt niet importeren uit niet-toegestane hosts, vraag de beheerder om de instellingen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS te controleren. @@ -1644,7 +1645,7 @@ issues.error_modifying_due_date=Deadline aanpassen mislukt. issues.error_removing_due_date=Deadline verwijderen mislukt. issues.push_commit_1=toegevoegd %d commit %s issues.push_commits_n=toegevoegd %d commits %s -issues.force_push_codes=`force-push %[1]s van %[2]s naar %[4]s %[6]s` +issues.force_push_codes=`force-push %[1]s van %[2]s %[8]s naar %[4]s %[9]s %[6]s` issues.force_push_compare=Vergelijk issues.due_date_form=jjjj-mm-dd issues.due_date_form_add=Vervaldatum toevoegen @@ -2495,7 +2496,7 @@ issues.label_archive = Label archiveren issues.label_exclusive_warning = Eventuele conflicterende scoped labels worden verwijderd bij het bewerken van de labels van een issue of pull request. issues.unpin_comment = ontpind dit %s pulls.show_changes_since_your_last_review = Wijzigingen weergeven sinds je laatste beoordeling -mirror_address_url_invalid = De opgegeven URL is ongeldig. Je moet alle componenten van de URL correct escapen. +mirror_address_url_invalid = De opgegeven URL is ongeldig. Zorg ervoor dat de onderdelen van de URL correct worden geëscape. desc.sha256 = SHA256 form.name_reserved = De repository naam "%s" is gereserveerd. form.name_pattern_not_allowed = Het patroon "%s" is niet toegestaan in een repository naam. @@ -2538,7 +2539,7 @@ editor.directory_is_a_file = Mapnaam "%s" wordt al gebruikt als bestandsnaam in commits.renamed_from = Hernoemd van %s projects.card_type.desc = Kaart voorbeeld pulls.filter_changes_by_commit = Filter op commit -pulls.nothing_to_compare_have_tag = De geselecteerde branch/tag zijn gelijk. +pulls.nothing_to_compare_have_tag = De geselecteerde branches/tags zijn gelijk. pulls.merged_success = Pull request succesvol samengevoegd en gesloten pulls.closed = Pull request gesloten pulls.merged_info_text = De branch %s kan nu verwijderd worden. @@ -2557,7 +2558,7 @@ signing.wont_sign.parentsigned = De vastlegging zal niet ondertekend worden, omd signing.wont_sign.headsigned = De samenvoeging zal niet ondertekend worden, omdat de hoofd commit niet ondertekend is. signing.wont_sign.commitssigned = De samenvoeging zal niet getekend worden, omdat alle geassocieerde commits niet getekend zijn. signing.wont_sign.never = Commits worden nooit ondertekend. -signing.wont_sign.error = Er is een fout opgetreden tijdens het controleren of de commit ondertekend kon worden. +signing.wont_sign.error = Er is een fout opgetreden bij het controleren of de commit kon worden ondertekend. signing.will_sign = Deze commit wordt ondertekend met sleutel "%s". milestones.filter_sort.latest_due_date = Uiterste vervaldatum milestones.filter_sort.earliest_due_data = Dichtstbijzijnde vervaldatum @@ -2912,17 +2913,16 @@ comment.blocked_by_user = Commentaar geven is niet mogelijk omdat u geblokkeerd sync_fork.button = Synchroniseer sync_fork.branch_behind_one = Deze branch is %[1]d commit achter %[2]s sync_fork.branch_behind_few = Deze branch is %[1]d commits achter %[2]s - - - -issues.filter_type.all_pull_requests = Alle pull requests -settings.event_header_action = Actie run evenementen settings.event_action_failure = Mislukking settings.event_action_failure_desc = Action run is mislukt. settings.event_action_recover = Herstel -settings.event_action_recover_desc = Action run is geslaagd nadat de laatste action run in dezelfde workflow is mislukt. settings.event_action_success = Succes settings.event_action_success_desc = Action run is geslaagd. +settings.event_header_action = Actie run evenementen +issues.filter_type.all_pull_requests = Alle pull requests +settings.event_action_recover_desc = Action run is geslaagd nadat de laatste action run in dezelfde workflow is mislukt. + + [graphs] component_loading_info = Dit kan even duren… @@ -3626,7 +3626,7 @@ create_repo=repository aangemaakt in %s rename_repo=hernoemde repository van %[1]s naar %[3]s transfer_repo=repository verplaatst naar %s naar %s delete_tag=heeft label %[2]s van %[3]s verwijderd -delete_branch=heeft branch %[2]s in %[3]s verwijderd +delete_branch=heeft branch %[2]s in %[3]s verwijderd compare_branch=Vergelijk compare_commits=Vergelijk %d commits compare_commits_general=Vergelijk commits diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index a788badb47..1e069b8474 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -2788,7 +2788,7 @@ pulls.has_pull_request = `Pull request między tymi gałęziami już istnieje: < settings.wiki_branch_rename_success = Gałąź wiki dla repozytorium została znormalizowana pomyślnie. settings.web_hook_name_larksuite_only = Lark Suite settings.packagist_api_token = Token API -issues.force_push_codes = `wymusił(a) wypchnięcie %[1]s z %[2]s do %[4]s %[6]s` +issues.force_push_codes = `wymusił(a) wypchnięcie %[1]s z %[2]s %[8]s do %[4]s %[9]s %[6]s` issues.filter_label_select_no_label = Brak etykiety issues.filter_project_all = Wszystkie projekty issues.filter_type.reviewed_by_you = Recenzowane przez ciebie @@ -3552,7 +3552,7 @@ create_repo=tworzy repozytorium %s rename_repo=zmienia nazwę repozytorium %[1]s na %[3]s transfer_repo=przenosi repozytorium %s do %s delete_tag=usuwa tag %[2]s z %[3]s -delete_branch=usuwa gałąź %[2]s z %[3]s +delete_branch=usuwa gałąź %[2]s z %[3]s compare_branch=Porównaj compare_commits=Porównaj %d commitów compare_commits_general=Porównaj commity diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index cd5cc14833..ccf2c22ab1 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -468,7 +468,7 @@ email_domain_blacklisted=Você não pode se cadastrar com seu endereço de e-mai authorize_application=Autorizar aplicativo authorize_redirect_notice=Você será redirecionado para %s se você autorizar este aplicativo. authorize_application_created_by=Este aplicativo foi criado por %s. -authorize_application_description=Se você conceder o acesso, isso permitirá acessar e alterar todas as informações da sua conta, incluindo repositórios privados e organizações. +authorize_application_description=Se você conceder acesso, isso permitirá acessar e alterar todas as informações da sua conta, incluindo repositórios privados e organizações. authorize_title=Autorizar "%s" para acessar sua conta? authorization_failed=Autorização falhou authorization_failed_desc=A autorização falhou porque detectamos uma solicitação inválida. Entre em contato com o responsável do aplicativo que você tentou autorizar. @@ -829,7 +829,7 @@ activations_pending=Ativações pendentes can_not_add_email_activations_pending=Há uma ativação pendente, tente novamente em alguns minutos se quiser adicionar um novo e-mail. delete_email=Remover email_deletion=Remover endereço de e-mail -email_deletion_desc=O endereço de e-mail e informações relacionadas serão removidos de sua conta. Commits aplicados por este endereço de e-mail permanecerão inalterados. Continuar? +email_deletion_desc=Este endereço de e-mail e informações relacionadas serão removidos de sua conta. Commits aplicados por este endereço de e-mail permanecerão inalterados. Continuar? email_deletion_success=O endereço de e-mail foi removido. theme_update_success=Seu tema foi atualizado. theme_update_error=O tema selecionado não existe. @@ -1151,7 +1151,7 @@ mirror_interval_invalid=O intervalo do espelhamento não é válido. mirror_sync_on_commit=Sincronizar quando commits forem enviados mirror_address=Clonar a partir de URL mirror_address_desc=Coloque todas as credenciais necessárias na seção de autorização. -mirror_address_url_invalid=A URL fornecida é inválida. Você deve escapar todos os componentes da URL corretamente. +mirror_address_url_invalid=A URL fornecida é inválida. Certifique-se de escapar os componentes da URL corretamente. mirror_address_protocol_invalid=O URL fornecido é inválido. Somente locais http(s):// ou git:// podem ser usados para espelhamento. mirror_lfs=Armazenamento de Arquivo Grande (LFS) mirror_lfs_desc=Ativar espelhamento de dados LFS. @@ -1235,7 +1235,7 @@ migrate_items_releases=Versões migrate_repo=Migrar repositório migrate.clone_address=Migrar / Clonar de URL migrate.clone_address_desc=URL HTTP(S) ou comando git "clone" de um repositório existente -migrate.github_token_desc=Você pode colocar aqui um ou mais tokens separados por vírgulas para tornar a migração mais rápida para compensar o limite de taxa de API do GitHub. AVISO: abusar desse recurso pode violar a política do provedor de serviços e levar ao bloqueio da conta. +migrate.github_token_desc=Você pode colocar aqui um ou mais tokens separados por vírgulas para tornar a migração mais rápida ao evitar o limite de taxa de API do GitHub. AVISO: abusar desse recurso pode violar a política do provedor de serviços e pode levar ao bloqueio da(s) conta(s). migrate.clone_local_path=ou um caminho de servidor local migrate.permission_denied=Você não pode importar repositórios locais. migrate.permission_denied_blocked=Você não pode importar dos hosts não permitidos, por favor peça ao administrador para verificar as configurações ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. @@ -1744,7 +1744,7 @@ issues.error_modifying_due_date=Falha ao modificar a data limite. issues.error_removing_due_date=Falha ao remover a data limite. issues.push_commit_1=adicionou %d commit %s issues.push_commits_n=adicionou %d commits %s -issues.force_push_codes=`forçou o push %[1]s de %[2]s para %[4]s %[6]s` +issues.force_push_codes=`forçou o push %[1]s de %[2]s %[8]s para %[4]s %[9]s %[6]s` issues.force_push_compare=Comparar issues.due_date_form=dd/mm/aaaa issues.due_date_form_add=Adicionar data limite @@ -2773,7 +2773,7 @@ subscribe.issue.guest.tooltip = Faça login para receber notificações desta qu settings.federation_not_enabled = O recurso de federação não está habilitado em seu servidor. settings.trust_model.committer.desc = Uma assinatura de commit é considerada "confiável" caso corresponda ao autor do commit, caso contrário será definida como "discordante". Isso permite delegar a autoria de commits ao Forgejo, adicionando créditos ao autor original nos campos "Co-authored-by" e "Co-commited-by" no final do commit. A chave padrão do Forgejo deve corresponder à chave de um usuário no banco de dados. settings.wiki_branch_rename_success = O nome do ramo da wiki do repositório foi regularizado com sucesso. -pulls.nothing_to_compare_have_tag = O ramo/etiqueta escolhidos são iguais. +pulls.nothing_to_compare_have_tag = Os branches/etiquetas escolhidos são iguais. settings.sourcehut_builds.secrets = Segredos release.download_count_few = %s downloads release.hide_archive_links = Ocultar arquivos gerados automaticamente @@ -3603,7 +3603,7 @@ config.cache_test_slow = Teste de cache bem-sucedido, mas a resposta é lenta: % config.cache_test = Cache de Teste config.cache_test_failed = Falha ao sondar o cache: %v. self_check.database_collation_mismatch = Esperar que o banco de dados use o ordenamento: %s -dashboard.cleanup_actions = Limpar logs expirados e artefatos de ações +dashboard.cleanup_actions = Limpar logs expirados e artefatos de Actions emails.delete = Deletar email emails.delete_primary_email_error = Você não pode excluir o email principal. emails.deletion_success = O endereço de email foi excluído. @@ -3636,7 +3636,7 @@ auto_merge_pull_request=`fez merge automático do pull request % transfer_repo=transferiu repositório de %s para %s push_tag=fez push da tag %[3]s to %[4]s delete_tag=excluiu tag %[2]s de %[3]s -delete_branch=excluiu branch %[2]s de %[3]s +delete_branch=excluiu branch %[2]s de %[3]s compare_branch=Comparar compare_commits=Compare %d commits compare_commits_general=Comparar commits diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index d82ae69fa4..a8afe522d7 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -341,7 +341,7 @@ no_reply_address=Domínio dos emails ocultos no_reply_address_helper=Nome de domínio para utilizadores com um endereço de email oculto. Por exemplo, o nome de utilizador "silva" será registado no Git como "silva@semresposta.exemplo.org" se o domínio de email oculto estiver definido como "semresposta.exemplo.org". password_algorithm=Algoritmo de Hash da palavra-passe invalid_password_algorithm=Algoritmo de hash da senha inválido -password_algorithm_helper=Definir o algoritmo de hash da senha. Os algoritmos têm requisitos e resistência distintos. `argon2` é bastante seguro, mas usa muita memória e pode ser inapropriado para sistemas pequenos. +password_algorithm_helper=Defina o algoritmo de hash da palavra-passe. Os algoritmos têm requisitos e níveis de segurança diferentes. O algoritmo argon2 é bastante seguro, mas consome muita memória e pode não ser apropriado para sistemas com poucos recursos. enable_update_checker=Habilitar verificador de novidades env_config_keys=Configuração do ambiente env_config_keys_prompt=As seguintes variáveis de ambiente também serão aplicadas ao seu ficheiro de configuração: @@ -829,7 +829,7 @@ activations_pending=Habilitações pendentes can_not_add_email_activations_pending=Existe uma validação pendente. Tente de novo dentro de alguns minutos, se quiser adicionar um novo email. delete_email=Remover email_deletion=Remover endereço de email -email_deletion_desc=O endereço de email e informações relacionadas serão removidos da sua conta. Os cometimentos feitos no Git com este endereço de email permanecerão inalterados. Quer continuar? +email_deletion_desc=Este endereço de email e informações relacionadas serão removidos da sua conta. Os cometimentos feitos no Git com este endereço de email permanecerão inalterados. Quer continuar? email_deletion_success=O endereço de email foi removido. theme_update_success=O seu tema foi substituído. theme_update_error=O tema escolhido não existe. @@ -1060,7 +1060,7 @@ user_unblock_success = O utilizador foi desbloqueado com sucesso. language.title = Idioma predefinido keep_activity_private.description = O seu trabalho público apenas estará visível para si e para os administradores da instância. language.description = Este idioma vai ser guardado na sua conta e ser usado como o predefinido depois de iniciar sessão. -language.localization_project = Ajude-nos a traduzir o Forgejo para o seu idioma! Ler mais. +language.localization_project = Ajude-nos a traduzir o Forgejo para o seu idioma! Saiba mais. pronouns_custom_label = Pronomes personalizados user_block_yourself = Não se pode bloquear a si próprio. change_username_redirect_prompt.with_cooldown.one = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dia. Pode ainda reivindicar o nome de utilizador antigo durante o período de espera. @@ -1157,7 +1157,7 @@ mirror_sync=sincronizado mirror_sync_on_commit=Sincronizar quando forem enviados cometimentos mirror_address=Clonar a partir do URL mirror_address_desc=Coloque, na secção de autorização, as credenciais que, eventualmente, sejam necessárias. -mirror_address_url_invalid=O URL fornecido é inválido. Tem que codificar adequadamente todos os componentes do URL. +mirror_address_url_invalid=O URL fornecido é inválido. Certifique-se de que os componentes da URL estão codificados corretamente. mirror_address_protocol_invalid=O URL fornecido é inválido. Só se pode replicar a partir de endereços http(s):// ou git://. mirror_lfs=Armazenamento de Ficheiros Grandes (LFS) mirror_lfs_desc=Habilitar a réplica de dados LFS. @@ -1247,7 +1247,7 @@ migrate_items_releases=Lançamentos migrate_repo=Migrar o repositório migrate.clone_address=Migrar / clonar a partir do URL migrate.clone_address_desc=O URL de clonagem HTTP(S) ou Git de um repositório existente -migrate.github_token_desc=Pode colocar aqui um ou mais códigos separados por vírgulas para tornar mais rápida a migração, para compensar a limitação de velocidade da API do GitHub. AVISO: O abuso desta funcionalidade poderá violar a política do seu fornecedor de serviço e levar ao bloqueio da conta. +migrate.github_token_desc=Pode colocar aqui um ou mais códigos aqui, separados por vírgulas, para tornar mais rápida a migração, contornando a limitação de taxa da API do GitHub. AVISO: O uso abusivo desta funcionalidade poderá violar a política do seu fornecedor de serviço e levar ao bloqueio da(s) sua(s) conta(s). migrate.clone_local_path=ou uma localização no servidor local migrate.permission_denied=Não está autorizado a importar repositórios locais. migrate.permission_denied_blocked=Não pode importar de servidores não permitidos, por favor peça ao administrador para verificar as configurações ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. @@ -1768,7 +1768,7 @@ issues.error_modifying_due_date=Falhou a modificação da data de vencimento. issues.error_removing_due_date=Falhou a remoção da data de vencimento. issues.push_commit_1=adicionou %d cometimento %s issues.push_commits_n=adicionou %d cometimentos %s -issues.force_push_codes=`forçou o envio %[1]s de %[2]s para %[4]s %[6]s` +issues.force_push_codes=`forçou o envio %[1]s de %[2]s %[8]s para %[4]s %[9]s %[6]s` issues.force_push_compare=Comparar issues.due_date_form=aaaa-mm-dd issues.due_date_form_add=Adicionar data de vencimento @@ -1880,7 +1880,7 @@ pulls.select_commit_hold_shift_for_range=Escolha o comentimento. Mantenha premid pulls.review_only_possible_for_full_diff=A revisão só é possível ao visualizar o diff completo pulls.filter_changes_by_commit=Filtrar por cometimento pulls.nothing_to_compare=Estes ramos são iguais. Não há necessidade de criar um pedido de integração. -pulls.nothing_to_compare_have_tag=O ramo/etiqueta escolhidos são iguais. +pulls.nothing_to_compare_have_tag=Os ramos/etiquetas escolhidos são iguais. pulls.nothing_to_compare_and_allow_empty_pr=Estes ramos são iguais. Este pedido de integração ficará vazio. pulls.has_pull_request=`Já existe um pedido de integração entre estes ramos: %[2]s#%[3]d` pulls.create=Criar um pedido de integração @@ -2922,7 +2922,6 @@ settings.event_header_action = Eventos da execução de ações settings.event_action_recover_desc = A execução de ação foi bem sucedida depois da última execução de ação na mesma sequência de trabalho ter falhado. settings.event_action_success = Sucesso settings.event_action_success_desc = A Execução de ação foi bem sucedida. - issues.filter_type.all_pull_requests = Todos os pedidos de integração [graphs] @@ -3638,7 +3637,7 @@ auto_merge_pull_request=`fez automaticamente a integração constante no pedido transfer_repo=transferiu o repositório %s para %s push_tag=enviou a etiqueta %[3]s para %[4]s delete_tag=eliminou a etiqueta %[2]de %[3]s -delete_branch=eliminou o ramo %[2]s de %[3]s +delete_branch=eliminou o ramo %[2]s de %[3]s compare_branch=Comparar compare_commits=Comparar %d comentimentos compare_commits_general=Comparar comentimentos diff --git a/options/locale/locale_ro.ini b/options/locale/locale_ro.ini index c52788a05e..f12301307d 100644 --- a/options/locale/locale_ro.ini +++ b/options/locale/locale_ro.ini @@ -1,3 +1,6 @@ + + + [common] return_to_forgejo = Înapoi la Forgejo explore = Explorează @@ -15,7 +18,7 @@ concept_user_organization = Organizație logo = Logo help = Ajutor sign_up = Înregistrare -link_account = Conectare cont +link_account = Conectați conturi register = Înregistrare template = Șablon language = Limbă @@ -88,7 +91,7 @@ remove_label_str = Șterge elementul "%s" save = Salvează remove = Șterge copy_path = Copiază cale -error404 = Pagina pe care încerci să o vizitezi fie nu există sau nu ești autorizat să o vezi. +error404 = Pagina pe care încercați să o vizitați fie nu există, a fost ștearsă sau nu sunteți autorizat să o puteți vedea. filter.not_archived = Nearhivat activities = Activități confirm_delete_selected = Ștergi toate elementele selectate? @@ -101,8 +104,47 @@ home = Acasă dashboard = Panou de Control version = Versiune powered_by = Susținut de %s -active_stopwatch = Monitorizor de timp activ +active_stopwatch = Contor timp activ more_items = Mai multe elemente +tracked_time_summary = Rezumat al timpului monitorizat, bazat pe filtrele listei de probleme +signed_in_as = Conectat ca +toggle_menu = Afișează sau ascunde meniul +passcode = Cod de acces +repository = Repozitoriu +new_project_column = Coloană nouă +new_fork = Bifurcație de repozitoriu nouă +new_repo.title = Repozitoriu nou +new_repo.link = Repozitoriu nou +all = Tot +forks = Bifurcații +pull_requests = Cereri de extragere +issues = Probleme +milestones = Etape +twofa_scratch = Cod de rezervă pentru autentificare prin doi factori +collaborative = Colaborativ +rerun = Reporniți +rerun_all = Reporniți toate joburile +add_all = Adaugă toate +remove_all = Șterge toate +copy_branch = Copiați numele ramurii +write = Scrieți +error413 = V-ați epuizat cota. +invalid_data = Date invalide: %v +unpin = Desprindeți +concept_code_repository = Repozitoriu +show_timestamps = Afișați marcajele temporale +pin = Fixați +show_full_screen = Afișați pe tot ecranul +download_logs = Descărcați jurnalele +value = Valoare +filter.not_fork = Nu sunt bifurcații +filter.is_mirror = Copii identice +filter.not_mirror = Nu sunt copii identice +filter.public = Publice +filter.private = Private +filter.not_template = Nu sunt șabloane +filter.is_fork = Bifurcații +filter.is_template = Șabloane [editor] table_modal.header = Adaugă tabel @@ -118,6 +160,19 @@ buttons.bold.tooltip = Adaugă text aldin buttons.code.tooltip = Adaugă cod buttons.quote.tooltip = Citează text buttons.link.tooltip = Adaugă un link +buttons.heading.tooltip = Adăugați titlu +buttons.list.unordered.tooltip = Adăugați o listă +buttons.list.task.tooltip = Adăugați o listă de sarcini +buttons.switch_to_legacy.tooltip = Folosiți în schimb editorul vechi +buttons.ref.tooltip = Faceți o referire la o problemă sau o cerere de extragere +buttons.enable_monospace_font = Activați fontul monospațiat +buttons.disable_monospace_font = Dezactivați fontul monospațiat +buttons.indent.tooltip = Indentați obiectele cu un nivel +buttons.unindent.tooltip = Nu mai indentați obiectele cu un nivel +link_modal.url = URL +link_modal.description = Descriere +link_modal.header = Adăugați un link +link_modal.paste_reminder = Indiciu: Cu un URL salvat în clipboard, puteți lipi direct în editor pentru a crea un link. [filter] string.asc = A - Z @@ -126,10 +181,14 @@ string.desc = Z - A [error] server_internal = Eroare internă a serverului network_error = Eroare de rețea +occurred = A apărut o eroare +not_found = Obiectul nu a putut fi găsit. +report_message = Dacă credeți că aceasta este o problemă cu Forgejo, vă rugăm să căutați probleme pe Codeberg sau să deschideți o nouă problemă dacă este necesar. [startpage] install = Ușor de instalat license = Sursă deschisă +app_desc = Un serviciu Git fără probleme, auto-găzduit [install] require_db_desc = Forgejo are nevoie de MySQL, PostgreSQL, SQLite3 sau TiDB (protocol MySQL). @@ -220,15 +279,31 @@ invalid_db_setting = Setările pentru bază de date sunt invalide: %v no_reply_address = Domeniu pentru adrese de email ascunse [search] -user_kind = Caută utilizatori… -team_kind = Caută echipe… -code_kind = Caută cod… -project_kind = Caută proiecte… -package_kind = Caută pachete… -org_kind = Caută organizații… +user_kind = Căutați utilizatori… +team_kind = Căutați echipe… +code_kind = Căutați cod… +project_kind = Căutați proiecte… +package_kind = Căutați pachete… +org_kind = Căutați organizații… code_search_unavailable = Căutarea de cod nu este disponibilă momentan. Te rog contactează administratorul site-ului. keyword_search_unavailable = Căutarea după cuvânt cheie nu este disponibilă momentan. Te rog contactează administratorul site-ului. no_results = Nu a fost găsit niciun rezultat corespunzător. +search = Căutați… +type_tooltip = Tipul căutării +fuzzy = Aproximată +union = Cuvinte cheie +union_tooltip = Includeți rezultate care sunt asemănătoare cuvintelor cheie separate prin spațiu +exact = Exactă +exact_tooltip = Includeți doar rezultate care se potrivesc exact termenului de căutare +regexp = Expresie regulată +regexp_tooltip = Interpretați termenul de căutare ca o expresie regulată +branch_kind = Căutați ramuri… +commit_kind = Căutați comiteri… +issue_kind = Căutați probleme… +pull_kind = Căutați cereri de extragere… +fuzzy_tooltip = Includeți rezultate care sunt asemănătoare termenului de căutare +repo_kind = Căutați repozitorii… +runner_kind = Căutați executori… [aria] navbar = Bară de navigare diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 304b07b7ee..3f007ce4ba 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -755,7 +755,7 @@ public_profile=Публичный профиль biography_placeholder=Кратко расскажите о себе другим! (Можно использовать Markdown) location_placeholder=Пусть все знают, откуда вы profile_desc=Ваш профиль -password_username_disabled=Нелокальным пользователям запрещено изменение их имени пользователя. Для получения более подробной информации обратитесь к администратору сайта. +password_username_disabled=Нелокальные пользователи не могут изменить имя. Для подробностей обратитесь к администрации сайта. full_name=Полное имя website=Веб-сайт location=Местоположение @@ -766,7 +766,7 @@ update_language_not_found=Язык «%s» недоступен. update_language_success=Язык обновлён. update_profile_success=Ваш профиль успешно обновлён. change_username=Ваше имя пользователя было изменено. -change_username_prompt=Обратите внимание: изменение имени пользователя также меняет URL вашей учётной записи. +change_username_prompt=Учтите, что при изменении имени пользователя ссылка на ваш профиль тоже будет изменена. change_username_redirect_prompt=Старое имя будет перенаправлять на новое до тех пор, пока оно не будет занято. continue=Далее cancel=Отмена @@ -1053,7 +1053,7 @@ hints = Подсказки additional_repo_units_hint = Предлагать включить больше разделов в репозиториях update_hints = Обновить подсказки update_hints_success = Подсказки обновлены. -additional_repo_units_hint_description = Показывать подсказку "Включить больше разделов" в репозиториях, в которых включены не все разделы. +additional_repo_units_hint_description = Показывать подсказку «Включить больше разделов» в репозиториях, в которых включены не все разделы. pronouns_custom = Другие pronouns = Местоимения pronouns_unspecified = Не указаны @@ -1232,7 +1232,7 @@ migrate_items_releases=Выпуски migrate_repo=Перенос репозитория migrate.clone_address=Перенос / Клонирование по URL migrate.clone_address_desc=HTTP/HTTPS или Git адрес существующего репозитория -migrate.github_token_desc=Вы можете указать один или несколько разделенных запятыми токенов, чтобы ускорить перенос за счёт обхода ограничений частоты обращений к API GitHub. ПРЕДУПРЕЖДЕНИЕ: злоупотребление этой функцией может нарушить условия предоставления услуг и привести к блокировке учётной записи. +migrate.github_token_desc=Вы можете указать один или несколько токенов, разделенных запятыми, чтобы ускорить перенос за счёт обхода ограничения частоты обращений к API GitHub. ПРЕДУПРЕЖДЕНИЕ: злоупотребление этой функцией может нарушить политику поставщика услуг и привести к блокировке вашей учётной записи. migrate.clone_local_path=или локальный путь на сервере migrate.permission_denied=У вас нет прав на импорт локальных репозиториев. migrate.permission_denied_blocked=Вы не можете импортировать с запрещённых хостов, пожалуйста, попросите администратора проверить настройки ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. @@ -1748,7 +1748,7 @@ issues.error_modifying_due_date=Не удалось изменить срок в issues.error_removing_due_date=Не удалось убрать срок выполнения. issues.push_commit_1=добавлен %d коммит %s issues.push_commits_n=добавлены %d коммита(ов) %s -issues.force_push_codes=`форсированное обновление изменений %[1]s %[4]s вместо %[2]s %[6]s` +issues.force_push_codes=`форсированное обновление изменений %[1]s %[4]s %[8]s вместо %[2]s %[9]s %[6]s` issues.force_push_compare=Сравнить issues.due_date_form=гггг-мм-дд issues.due_date_form_add=Добавить срок выполнения @@ -2698,7 +2698,7 @@ error.csv.invalid_field_count=Не удается отобразить этот mirror_address_protocol_invalid = Эта ссылка недействительна. Для зеркалирования можно использовать только расположения http(s):// и git:// . fork_no_valid_owners = Невозможно создать ответвление этого репозитория, т.к. здесь нет действующих владельцев. new_repo_helper = Репозиторий содержит все файлы проекта и историю изменений. Уже где-то есть репозиторий? Выполните перенос. -mirror_address_url_invalid = Эта ссылка недействительна. Необходимо правильно указать все части адреса. +mirror_address_url_invalid = Указанная ссылка недействительна. Убедитесь, что все части экранированы правильно. issues.comment.blocked_by_user = Вы не можете комментировать под этой задачей, т.к. вы заблокированы владельцем репозитория или автором задачи. pulls.blocked_by_user = Невозможно создать запрос на слияние в этом репозитории, т.к. вы заблокированы его владельцем. settings.add_collaborator_blocked_our = Невозможно добавить соучастника, т.к. он заблокирован в этом репозитории. @@ -2756,7 +2756,7 @@ ambiguous_runes_description = `Этот файл содержит символы editor.invalid_commit_mail = Неправильная почта для создания коммита. pulls.has_merged = Слияние не удалось: запрос уже был слит, изменение целевой ветви или повторное слияние невозможно. settings.enter_repo_name = Введите имя владельца и название репозитория как указано: -signing.wont_sign.error = Не удалось проверить возможность подписать коммит. +signing.wont_sign.error = Не удалось проверить возможность подписания коммита. signing.wont_sign.nokey = Сервер не предоставляет ключ для подписи коммита. settings.wiki_globally_editable = Разрешить редактирование вики всем пользователям settings.webhook.test_delivery_desc_disabled = Активируйте этот веб-хук для проверки тестовым событием. @@ -3120,7 +3120,7 @@ dashboard.resync_all_hooks=Повторно синхронизировать х dashboard.reinit_missing_repos=Переинициализировать все отсутствующие Git репозитории, для которых существуют записи dashboard.sync_external_users=Синхронизировать данные сторонних пользователей dashboard.cleanup_hook_task_table=Очистить таблицу hook_task -dashboard.cleanup_packages=Очистка устаревших пакетов +dashboard.cleanup_packages=Удалить устаревшие пакеты dashboard.server_uptime=Время работы dashboard.current_goroutine=Выполняемые goroutines dashboard.current_memory_usage=Текущее использование памяти @@ -3582,7 +3582,7 @@ self_check.no_problem_found = Пока проблем не обнаружено. auths.tip.gitea = Зарегистрируйте новое приложение OAuth2. Доступна инструкция: %s auths.tips.oauth2.general.tip = При регистрации нового приложения OAuth2 ссылка обратного перенаправления должна быть: self_check.database_fix_mysql = Пользователи MySQL и MariaDB могут исправить проблемы с сопоставлением командой "forgejo doctor convert". Также можно вручную вписать "ALTER ... COLLATE ..." в SQL. -dashboard.cleanup_actions = Очистить устаревшие журналы и артефакты Действий +dashboard.cleanup_actions = Удалить устаревшие журналы и артефакты Действий dashboard.sync_repo_branches = Синхронизировать ветви из Git в базу данных assets = Кодовые объекты dashboard.sync_tag.started = Начата синхронизация тегов @@ -3639,7 +3639,7 @@ auto_merge_pull_request=`автоматически принят запрос н transfer_repo=репозиторий %s был передан: %s push_tag=отправлен тег %[3]s в %[4]s delete_tag=удалён тег %[2]s в %[3]s -delete_branch=удалена ветвь %[2]s в %[3]s +delete_branch=удалена ветвь %[2]s в %[3]s compare_branch=Сравнить compare_commits=Сравнить %d коммитов compare_commits_general=Сравнить коммиты @@ -3845,7 +3845,7 @@ owner.settings.cargo.initialize.error=Не удалось инициализир owner.settings.cargo.initialize.success=Индекс Cargo успешно создан. owner.settings.cargo.rebuild=Перестроить индекс owner.settings.cargo.rebuild.error=Не удалось перестроить индекс Cargo: %v -owner.settings.cargo.rebuild.success=Индекс Cargo успешно перестроен. +owner.settings.cargo.rebuild.success=Индекс Cargo был перестроен успешно. owner.settings.cleanuprules.title=Правила очистки owner.settings.cleanuprules.add=Добавить правило очистки owner.settings.cleanuprules.edit=Изменить правило очистки diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index e1e19fa78e..4e1690af9f 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -1187,7 +1187,7 @@ issues.error_modifying_due_date=නියමිත දිනය වෙනස් issues.error_removing_due_date=නියමිත දිනය ඉවත් කිරීමට අපොහොසත් විය. issues.push_commit_1=එකතු %d කැප %s issues.push_commits_n=එකතු %d විවරයන් %s -issues.force_push_codes=`බලය-pushed%[1]s සිට %[2]s %[4]s ගේ %[6]s` +issues.force_push_codes=`බලය-pushed%[1]s සිට %[2]s %[8]s %[4]s ගේ %[9]s %[6]s` issues.force_push_compare=සසඳන්න issues.due_date_form=Yyy-mm-dd issues.due_date_form_add=නියමිත දිනය එකතු කරන්න @@ -2488,7 +2488,7 @@ merge_pull_request=`ඒකාබද්ධ අදින්න ඉල්ලීම transfer_repo=මාරු කරන ලද ගබඩාව %s සිට %s push_tag=තල්ලු ටැගය %[3]s ගේ %[4]s ගේ delete_tag=මකාදැමුවා ටැගය%[2]s සිට %[3]s -delete_branch=මකාදැමූ ශාඛාව %[2]s සිට %[3]s +delete_branch=මකාදැමූ ශාඛාව %[2]s සිට %[3]s compare_branch=සසඳන්න compare_commits=%d විවරයන් සසඳා බලන්න compare_commits_general=විවරයන් සසඳා බලන්න diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index ec57f025cd..553db1b19a 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -204,15 +204,20 @@ buttons.switch_to_legacy.tooltip = Använd legacy-redigeraren istället link_modal.url = Url link_modal.description = Beskrivning link_modal.header = Lägg till en länk +buttons.disable_monospace_font = Avaktivera jämnbrett typsnitt +link_modal.paste_reminder = Tips: Med ett URL i ditt klippbord, kan du klistra in direkt i textredigeraren för att skapa en länk. +buttons.enable_monospace_font = Aktivera jämnbrett typsnitt [filter] string.asc = A - Ö +string.desc = Ö - A [error] occurred = Ett fel har inträffat server_internal = Internt serverfel network_error = Nätverksfel report_message = Om du tror att detta är en Forgejo-bugg, sök efter ärenden på Codeberg eller öppna ett nytt ärende om det behövs. +not_found = Målet kunde inte hittas. [startpage] app_desc=En smidig, självhostad Git-tjänst @@ -333,6 +338,7 @@ internal_token_failed = Misslyckades att generera intern token: %v password_algorithm = Hashalgoritm för lösenord invalid_password_algorithm = Ogiltig hashalgoritm för lösenord env_config_keys_prompt = Följande miljövariabler kommer också att tillämpas på din konfigurationsfil: +smtp_from_invalid = "Skicka E-post som" adressen är ogiltig [home] uname_holder=Användarnamn eller e-postadress @@ -2226,7 +2232,7 @@ create_repo=skapade utvecklingskatalog %s rename_repo=döpte om utvecklingskalatogen från %[1]s till %[3]s transfer_repo=överförde utvecklingskalatogen %s till %s delete_tag=tog bort taggen %[2]s från %[3]s -delete_branch=tog bort branchen %[2]s from %[3]s +delete_branch=tog bort branchen %[2]s from %[3]s compare_branch=Jämför compare_commits=Jämför %d commits compare_commits_general=Jämför commits diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 0bb49a2181..edb32e616f 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -181,7 +181,6 @@ less=Daha az more=Daha Fazla contributions_one = katkı contributions_few = katkı - contributions_format = {day} {month} {year} tarihinde {contributions} katkı [editor] @@ -205,13 +204,12 @@ table_modal.placeholder.header = Başlık table_modal.placeholder.content = İçerik table_modal.label.rows = Satırlar table_modal.label.columns = Sütunlar - -buttons.indent.tooltip = Bir seviye girinti artır -buttons.unindent.tooltip = Bir seviye girinti azalt link_modal.header = Bağlantı ekle link_modal.url = Url link_modal.description = Açıklama link_modal.paste_reminder = İpucu: Panonuzdaki bir URL'yi doğrudan düzenleyiciye yapıştırarak bir bağlantı oluşturabilirsiniz. +buttons.unindent.tooltip = Bir seviye girinti azalt +buttons.indent.tooltip = Bir seviye girinti artır [filter] string.asc=A - Z @@ -235,7 +233,6 @@ lightweight=Hafif lightweight_desc=Forgejo'nın minimal gereksinimleri çok düşüktür ve ucuz bir Raspberry Pi üzerinde çalışabilmektedir. Makine enerjinizden tasarruf edin! license=Açık Kaynak license_desc=Gidin ve Forgejo'yı edinin! Bu projeyi daha da iyi yapmak için katkıda bulunarak bize katılın. Katkıda bulunmaktan çekinmeyin! - platform_desc = Forgejo'nun Linux ve FreeBSD gibi özgür işletim sistemlerinde ve farklı CPU mimarilerinde çalıştığı doğrulandı. Sevdiğinizi seçin! [install] @@ -355,7 +352,6 @@ enable_update_checker_helper_forgejo = release.forgejo.org adresindeki TXT DNS k allow_dots_in_usernames = Kullanıcı isimlerinde noktaya izin ver. Var olan kullanıcıları etkilemez. smtp_from_invalid = `"E-posta Olarak Gönder" adresi geçersiz` - config_location_hint = Bu yapılandırma seçenekleri şuraya kaydedilecek: [home] @@ -490,7 +486,6 @@ hint_register = Hesaba ihtiyacın var mı? Hemen kaydol. sign_in_openid = OpenID ile giriş yap hint_login = Mevcut hesabın var mı? Hemen giriş yap! use_onetime_code = Tek kullanımlık kod kullan - change_unconfirmed_email = Kayıt sırasında yanlış e-posta adresi verdiyseniz, aşağıdan değiştirebilirsiniz; yeni adresinize bir onay mesajı gönderilecektir. [mail] @@ -1720,7 +1715,7 @@ issues.error_modifying_due_date=Bitiş tarihi değiştirilemedi. issues.error_removing_due_date=Bitiş tarihi silinemedi. issues.push_commit_1=%d işlemeyi %s ekledi issues.push_commits_n=%d işlemeyi %s ekledi -issues.force_push_codes=`%[1]s %[2]s hedefinden %[4]s hedefine zorla gönderildi %[6]s` +issues.force_push_codes=`%[1]s %[2]s %[8]s hedefinden %[4]s %[9]s hedefine zorla gönderildi %[6]s` issues.force_push_compare=Karşılaştır issues.due_date_form=yyyy-aa-gg issues.due_date_form_add=Bitiş tarihi ekle @@ -2736,26 +2731,25 @@ settings.mirror_settings.pushed_repository = İtilmiş depo settings.ignore_stale_approvals = Eskimiş onayları yoksay settings.ignore_stale_approvals_desc = Daha eski işlemelere (eski incelemelere) yapılmış olan onayları, Dİ'nin kaç onayı olduğunu belirlerken sayma. Eskimiş incelemeler atıldıysa bu ilgisizdir. error.broken_git_hook = Bu deponun Git İstemcileri bozuk gibi gözüküyor. Onarmak için lütfen belgelere bakın, daha sonra durumu yenilemek için bazı işlemeler itin. - -mirror_public_key = Ortak SSH anahtarı -mirror_use_ssh.text = SSH yetkilendirme kullan -mirror_use_ssh.helper = Forgejo, bu seçeneği seçtiğinizde deponuzu SSH üzerinden Git üzerinden yansıtacak ve sizin için bir anahtar çifti oluşturacaktır. Oluşturulan ortak anahtarın hedef depoya gönderilmek üzere yetkilendirildiğinden emin olmalısınız. Bunu seçerken parola tabanlı yetkilendirme kullanamazsınız. -mirror_use_ssh.not_available = SSH yetkilendirme kullanılamıyor. -mirror_denied_combination = Ortak anahtar ve parola tabanlı kimlik doğrulama birlikte kullanılamaz. -n_commit_few = %s gönderi -n_branch_one = %s dal -n_branch_few = %s dal -n_tag_few = %s etiket commits.browse_further = Daha fazlasına göz at -commits.renamed_from = %s adından yeniden adlandırıldı -issues.filter_no_results = Sonuç yok -issues.filter_no_results_placeholder = Arama filtrelerini değiştirmeyi deneyin. -issues.num_participants_one = %d katılımcı settings.units.units = Birimler +commits.renamed_from = %s adından yeniden adlandırıldı +mirror_use_ssh.text = SSH yetkilendirme kullan +settings.default_update_style_desc = Temel dalın ardındaki çekme isteklerini güncellemek için kullanılan varsayılan güncelleme stili. +mirror_denied_combination = Ortak anahtar ve parola tabanlı kimlik doğrulama birlikte kullanılamaz. +mirror_public_key = Ortak SSH anahtarı +n_branch_few = %s dal +n_commit_few = %s gönderi +n_tag_few = %s etiket +settings.wiki_rename_branch_main = Viki dal adını normalleştir +mirror_use_ssh.not_available = SSH yetkilendirme kullanılamıyor. +mirror_use_ssh.helper = Forgejo, bu seçeneği seçtiğinizde deponuzu SSH üzerinden Git üzerinden yansıtacak ve sizin için bir anahtar çifti oluşturacaktır. Oluşturulan ortak anahtarın hedef depoya gönderilmek üzere yetkilendirildiğinden emin olmalısınız. Bunu seçerken parola tabanlı yetkilendirme kullanamazsınız. +n_branch_one = %s dal settings.units.add_more = Daha fazlasını etkinleştir settings.wiki_globally_editable = Vikiyi herkesin düzenlemesine izin ver -settings.default_update_style_desc = Temel dalın ardındaki çekme isteklerini güncellemek için kullanılan varsayılan güncelleme stili. -settings.wiki_rename_branch_main = Viki dal adını normalleştir +issues.filter_no_results_placeholder = Arama filtrelerini değiştirmeyi deneyin. +issues.filter_no_results = Sonuç yok +issues.num_participants_one = %d katılımcı [graphs] component_loading = %s yükleniyor... @@ -3452,7 +3446,7 @@ auto_merge_pull_request=`%[3]s#%[2]s değişiklik isteği ot transfer_repo=depo %s %s'a aktarıldı push_tag=%[3]s etiketini %[4]s dalına gönderdi delete_tag=%[2]s etiketi %[3]s deposundan silindi -delete_branch=%[3]s deposundan %[2]s dalı silindi +delete_branch=%[3]s deposundan %[2]s dalı silindi compare_branch=Karşılaştır compare_commits=%d işlemeyi karşılaştır compare_commits_general=İşlemeleri karşılaştır @@ -3830,15 +3824,16 @@ union_tooltip = Boşlukla ayrılmış anahtar kelime eşleşmelerini dahil et exact_tooltip = Sadece arama terimiyle tam uyuşan sonuçları dahit et. fuzzy = Bulanık exact = Tam -union = Anahtar sözcük regexp = Düzenliİfade regexp_tooltip = Arama terimini düzenli ifade olarak yorumla +union = Anahtar sözcük + [munits.data] -b = B -kib = KiB -mib = MiB -gib = GiB tib = TiB +kib = KiB pib = PiB -eib = EiB +mib = MiB +b = B +gib = GiB +eib = EiB \ No newline at end of file diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 2e0928bb1f..56294441e9 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -15,7 +15,7 @@ page=Сторінка template=Шаблон language=Мова notifications=Сповіщення -active_stopwatch=Трекер робочого часу +active_stopwatch=Облік робочого часу create_new=Створити… user_profile_and_more=Профіль і налаштування… signed_in_as=Увійшов як @@ -320,7 +320,7 @@ sqlite3_not_available=Ця версія Forgejo не підтримує SQLite3. invalid_db_setting=Налаштування бази даних є некоректними: %v invalid_repo_path=Помилковий шлях до кореня репозиторію: %v invalid_app_data_path=Некоректний шлях до даних програми: %v -run_user_not_match=Ім'я в «Користувач, від якого запустити» не є ім'ям поточного користувача: %s -> %s +run_user_not_match=Ім'я в «Користувач, від якого запустити» не є ім'ям поточного користувача: %s → %s internal_token_failed=Не вдалося згенерувати внутрішній токен: %v secret_key_failed=Не вдалося згенерувати секретний ключ: %v save_config_failed=Не в змозі зберегти конфігурацію: %v @@ -660,13 +660,13 @@ Location = Розташування AccessToken = Токен доступу unable_verify_ssh_key = Не вдалося перевірити ключ SSH, перевірте його на наявність помилок. repository_force_private = Увімкнено примусову приватність: приватні репозиторії не можна зробити публічними. -must_use_public_key = Ключ, який ви надали, є приватним. Будь ласка, нікуди не завантажуйте свій приватний ключ. Використовуйте замість нього публічний ключ. +must_use_public_key = Ключ, який ви надали, є приватним. Будь ласка, нікуди не завантажуйте свій приватний ключ. Використовуйте замість нього відкритий ключ. openid_been_used = Адреса OpenID «%s» вже використовується. still_has_org = Ваш обліковий запис є учасником однієї або декількох організацій, спочатку покиньте їх. duplicate_invite_to_team = Цього користувача вже запрошено як учасника команди. organization_leave_success = Ви успішно покинули організацію %s. include_error = ` має містити підрядок «%s».` -invalid_group_team_map_error = ` призначення недійсне: %s` +invalid_group_team_map_error = ` зіставлення недійсне: %s` unsupported_login_type = Цей тип входу не підтримує видалення облікового запису. admin_cannot_delete_self = Ви не можете видалити себе, якщо ви є адміністратором. Спочатку зніміть із себе права адміністратора. unset_password = Для користувача не встановлено пароль. @@ -792,7 +792,7 @@ activate_email=Надіслати активацію activations_pending=Активації в очікуванні delete_email=Видалити email_deletion=Видалити адресу електронної пошти -email_deletion_desc=Електронна адреса та пов'язана з нею інформація буде видалена з вашого облікового запису. Git коміти, здійснені через цю електронну адресу, залишиться без змін. Продовжити? +email_deletion_desc=Цю електронну адресу та пов'язану з нею інформацію буде видалено з вашого облікового запису. Коміти Git, здійснені через цю електронну адресу, залишаться без змін. Продовжити? email_deletion_success=Адресу електронної пошти було видалено. theme_update_success=Тему оновлено. theme_update_error=Вибрана тема не існує. @@ -815,7 +815,7 @@ manage_gpg_keys=Керування ключами GPG add_key=Додати ключ ssh_desc=Ці відкриті ключі SSH повʼязані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв. Підтверджені ключі можна використати для підтвердження комітів Git, підписаних із SSH. principal_desc=Ці настройки SSH сертифікатів вказані у вашому обліковому записі та надають повний доступ до ваших репозиторіїв. -gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом і використовуються для підтвердження комітів. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють підписувати коміти вашим особистим підписом. +gpg_desc=Ці відкриті ключі GPG пов'язані з вашим обліковим записом і використовуються для підтвердження комітів. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють підписувати коміти вашим особистим підписом. ssh_helper=Потрібна допомога? Дивіться гід на GitHub з генерації ключів SSH або виправлення типових неполадок SSH. gpg_helper= Потрібна допомога? Перегляньте посібник GitHub про GPG . add_new_key=Додати SSH ключ @@ -895,7 +895,7 @@ oauth2_applications_desc=Програми OAuth2 дають можливість remove_oauth2_application=Видалити програму OAuth2 remove_oauth2_application_desc=Видалення програми OAuth2 скасує доступ до всіх підписаних токенів доступу. Продовжити? remove_oauth2_application_success=Програму видалено. -create_oauth2_application=Створити новий додаток OAuth2 +create_oauth2_application=Створити нову програму OAuth2 create_oauth2_application_button=Створити програму oauth2_application_name=Назва програми save_application=Зберегти @@ -908,7 +908,7 @@ oauth2_application_create_description=Програми OAuth2 надають в authorized_oauth2_applications=Авторизовані програми OAuth2 revoke_key=Відкликати -revoke_oauth2_grant=Скасувати доступ +revoke_oauth2_grant=Відкликати доступ revoke_oauth2_grant_description=Скасування доступу для цієї програми третьої сторони не дозволить їй отримувати доступ до ваших даних. Ви впевнені? twofa_desc=Двофакторна автентифікація підвищує безпеку вашого облікового запису. @@ -934,7 +934,7 @@ manage_account_links_desc=Ці зовнішні акаунти прив'язан account_links_not_available=Наразі немає зовнішніх облікових записів, пов'язаних із вашим обліковим записом Forgejo. link_account=Прив'язати обліковий запис remove_account_link=Видалити пов'язаний обліковий запис -remove_account_link_desc=Видалення пов'язаного облікового запису відкликає його доступ до вашого облікового запису Forgejo. Продовжити? +remove_account_link_desc=Видалення пов'язаного облікового запису скасує його доступ до вашого облікового запису Forgejo. Продовжити? remove_account_link_success=Зв'язаний обліковий запис видалено. @@ -999,7 +999,7 @@ comment_type_group_pull_request_push = Додані коміти permissions_public_only = Тільки публічні select_permissions = Виберіть дозволи permissions_access_all = Усі (публічні, приватні й обмежені) -create_oauth2_application_success = Ви успішно створили новий додаток OAuth2. +create_oauth2_application_success = Ви успішно створили нову програму OAuth2. keep_email_private_popup = Ваша адреса електронної пошти не буде відображатися у вашому профілі і не буде використовуватися за замовчуванням для комітів, зроблених через веб-інтерфейс, таких як завантаження файлів, редагування і об'єднання комітів. Натомість ви можете використовувати спеціальну адресу %s для прив'язки комітів до свого облікового запису. Ця опція не вплине на існуючі коміти. blocked_since = Заблокований з %s can_not_add_email_activations_pending = Очікується активація, спробуйте ще раз за кілька хвилин, якщо хочете додати нову адресу електронної пошти. @@ -1023,7 +1023,7 @@ pronouns_custom_label = Інші займенники repo_and_org_access = Доступ до репозиторію та організації change_username_redirect_prompt.with_cooldown.few = Старе ім'я користувача буде доступне всім після періоду захисту, який триватиме %[1]d днів. Протягом періоду захисту ви ще можете повернути собі старе ім'я. change_username_redirect_prompt.with_cooldown.one = Старе ім'я користувача буде доступне всім після періоду захисту, який триватиме %[1]d день. Протягом періоду захисту ви ще можете повернути собі старе ім'я. -change_username_redirect_prompt = Старе ім'я користувача буде перенаправленням, поки хтось не присвоїть ім'я собі. +change_username_redirect_prompt = Старе ім'я користувача буде переспрямуванням, поки хтось не присвоїть ім'я собі. comment_type_group_lock = Стан блокування webauthn_alternative_tip = Можливо, ви бажаєте налаштувати додатковий спосіб входу. user_unblock_success = Користувач_ку успішно розблоковано. @@ -1076,9 +1076,9 @@ quota.rule.exceeded = Перевищено regenerate_token = Згенерувати знову access_token_regeneration = Згенерувати новий токен доступу quota.sizes.assets.all = Ресурси - access_token_regeneration_desc = Регенерація токена скасує доступ програм, які використовують цей токен, до вашого облікового запису. Це незворотна дія. Продовжити? regenerate_token_success = Токен згенеровано наново. Програми, які його використовують, більше не мають доступу до вашого облікового запису; ви повинні відновити доступ за допомогою нового токена. +ssh_token_help_ssh_agent = або, якщо ви використовуєте агент SSH (із встановленою змінною SSH_AUTH_SOCK): [repo] owner=Власник @@ -1093,7 +1093,7 @@ template_description=Шаблонні репозиторії дозволяют visibility=Видимість visibility_description=Тільки власник або члени організації які мають віповідні права, зможуть побачити. visibility_helper_forced=Адміністратор вашого сайту налаштував параметри: всі нові репозиторії будуть приватними. -visibility_fork_helper=(Ці зміни вплинуть на всі форки.) +visibility_fork_helper=(Буде змінено видимість усіх форків.) clone_helper=Потрібна допомога у клонуванні? Відвідайте сторінку Допомога. fork_repo=Створити форк репозиторію fork_from=Форк з @@ -1158,9 +1158,9 @@ delete_preexisting_success=Видалено неприйняті файли в % blame_prior=Переглянути анотацію, що передує цій зміні -transfer.accept=Дозволити трансфер +transfer.accept=Прийняти передачу transfer.accept_desc=`Перемістити до "%s"` -transfer.reject=Відхилити трансфер +transfer.reject=Відхилити передачу transfer.reject_desc=`Скасувати переміщення до "%s"` desc.private=Приватний @@ -1201,7 +1201,7 @@ migrate_items_pullrequests=Запити на злиття migrate_items_merge_requests=Запити на об'єднання migrate_items_releases=Релізи migrate_repo=Перенести репозиторій -migrate.clone_address=Міграція / клонувати з URL-адреси +migrate.clone_address=Міграція / клонування з URL-адреси migrate.clone_address_desc=URL-адреса HTTP(S) або Git «clone» існуючого репозиторію migrate.clone_local_path=або шлях до локального серверу migrate.permission_denied=Вам не дозволено імпортувати локальні репозиторії. @@ -1550,9 +1550,9 @@ issues.unlock=Розблокування обговорення issues.lock.unknown_reason=Неможливо заблокувати задачу з невідомою причиною. issues.lock_duplicate=Задача не може бути заблокованим двічі. issues.unlock_error=Не можливо розблокувати задачу, яка не заблокована. -issues.lock_with_reason=заблоковано як %s та обмежене обговорення для співавторів %s -issues.lock_no_reason=заблоковано та обмежене обговорення для співавторів %s -issues.unlock_comment=розблоковане обговорення %s +issues.lock_with_reason=блокує як %s та обмежує обговорення до співавторів %s +issues.lock_no_reason=блокує та обмежує обговорення до співавторів %s +issues.unlock_comment=розблоковує обговорення %s issues.lock_confirm=Заблокувати issues.unlock_confirm=Розблокувати issues.lock.notice_1=- Інші користувачі не можуть додавати нові коментарі до цієї задачі. @@ -1591,7 +1591,7 @@ issues.error_modifying_due_date=Не вдалося змінити дату за issues.error_removing_due_date=Не вдалося видалити дату завершення. issues.push_commit_1=додає %d коміт %s issues.push_commits_n=додає %d коміти(-ів) %s -issues.force_push_codes=`примусово залито %[1]s з %[2]s до %[4]s %[6]s` +issues.force_push_codes=`примусово залито %[1]s з %[2]s %[8]s до %[4]s %[9]s %[6]s` issues.force_push_compare=Порівняти issues.due_date_form=рррр-мм-дд issues.due_date_form_add=Додати дату завершення @@ -1702,8 +1702,8 @@ pulls.required_status_check_administrator=Як адміністратор ви pulls.can_auto_merge_desc=Цей запит можна об'єднати автоматично. pulls.cannot_auto_merge_desc=Цей запит на злиття не може бути злитий автоматично через конфлікти. pulls.cannot_auto_merge_helper=Злийте вручну для вирішення конфліктів. -pulls.num_conflicting_files_1=%d конфліктуючий файл -pulls.num_conflicting_files_n=%d конфліктуючі файли +pulls.num_conflicting_files_1=%d конфліктний файл +pulls.num_conflicting_files_n=%d конфліктних файлів pulls.approve_count_1=%d схвалення pulls.approve_count_n=%d схвалень pulls.reject_count_1=%d запит на зміну @@ -1818,7 +1818,7 @@ activity.active_prs_count_1=%d активний запит на activity.active_prs_count_n=%d активних запитів на злиття activity.merged_prs_count_1=Об'єднаний запит на злиття activity.merged_prs_count_n=Об'єднані запити на злиття -activity.opened_prs_count_1=Запропонований запит на злиття +activity.opened_prs_count_1=Запропоновані запити на злиття activity.opened_prs_count_n=Запропонованих запитів на злиття activity.title.user_1=%d користувачем activity.title.user_n=%d користувачами @@ -1840,8 +1840,8 @@ activity.closed_issue_label=Закрито activity.new_issues_count_1=Нова задача activity.new_issues_count_n=Нові задачі activity.new_issue_label=Відкриті -activity.title.unresolved_conv_1=%d Незавершене обговорення -activity.title.unresolved_conv_n=%d Незавершених обговорень +activity.title.unresolved_conv_1=%d незавершене обговорення +activity.title.unresolved_conv_n=%d незавершених обговорень activity.unresolved_conv_desc=Список всіх старих задач і Pull Request'ів з недавньої активністю, але ще не закритих або прийнятих. activity.unresolved_conv_label=Відкрити activity.title.releases_1=%d випуск @@ -1912,20 +1912,20 @@ settings.use_external_wiki=Використовувати зовнішню ві settings.external_wiki_url=URL зовнішньої вікі settings.external_wiki_url_error=Зовнішня URL-адреса wiki не є допустимою URL-адресою. settings.external_wiki_url_desc=Відвідувачі будуть перенаправлені на URL-адресу, коли вони клацають по вкладці. -settings.issues_desc=Увімкнути відстеження задач в репозиторію +settings.issues_desc=Увімкнути відстеження задач settings.use_internal_issue_tracker=Використовувати вбудовану систему відстеження задач -settings.use_external_issue_tracker=Використовувати зовнішню систему обліку задач +settings.use_external_issue_tracker=Використовувати зовнішню систему відстеження задач settings.external_tracker_url=URL зовнішньої системи відстеження задач settings.external_tracker_url_error=URL зовнішнього баг-трекера не є допустимою URL-адресою. settings.external_tracker_url_desc=Відвідувачі перенаправляються на зовнішню URL-адресу, коли натискають вкладку 'Задачі'. -settings.tracker_url_format=Формат URL зовнішнього трекера задач +settings.tracker_url_format=Формат URL зовнішньої системи відстеження задач settings.tracker_url_format_error=Неправильний формат URL-адреси зовнішнього баг-трекера. -settings.tracker_issue_style=Формат номера для зовнішньої системи обліку задач +settings.tracker_issue_style=Формат номера для зовнішньої системи відстеження задач settings.tracker_issue_style.numeric=Цифровий settings.tracker_issue_style.alphanumeric=Буквено-цифровий settings.tracker_url_format_desc=Використовуйте шаблони {user}, {repo} та {index} для імені користувача, репозиторію та номеру задічі. settings.enable_timetracker=Увімкнути відстеження часу -settings.allow_only_contributors_to_track_time=Враховувати тільки учасників розробки в підрахунку часу +settings.allow_only_contributors_to_track_time=Дозволити відстеження часу тільки учасникам розробки settings.pulls_desc=Увімкнути запити на злиття в репозиторій settings.pulls.ignore_whitespace=Ігнорувати пробіл у конфліктах settings.pulls.enable_autodetect_manual_merge=Увімкнути автовизначення ручного злиття (Примітка: у деяких особливий випадках можуть виникнуть помилки) @@ -1958,7 +1958,7 @@ settings.transfer_notices_1=- Ви втратите доступ до репоз settings.transfer_notices_2=- Ви збережете доступ, якщо новим власником стане організація, власником якої ви є. settings.transfer_notices_3=- Якщо репозиторій є приватним і передається окремому користувачеві, ця дія гарантує, що користувач має хоча б дозвіл на читаня репозитарію (і при необхідності змінює права дозволів). settings.transfer_owner=Новий власник -settings.transfer_perform=Здійснити перенесення +settings.transfer_perform=Здійснити передачу settings.transfer_started=`Цей репозиторій чекає підтвердження перенесення від "%s"` settings.transfer_succeed=Репозиторій був перенесений. settings.signing_settings=Параметри перевірки підпису @@ -2033,7 +2033,7 @@ settings.slack_icon_url=URL іконки settings.slack_color=Колір settings.discord_username=Ім'я кристувача settings.discord_icon_url=URL іконки -settings.event_desc=Тригер: +settings.event_desc=Спрацьовує на: settings.event_push_only=Push події settings.event_send_everything=Усі події settings.event_choose=Власні події… @@ -2336,7 +2336,7 @@ issues.filter_poster_no_select = Усі автори pulls.merged_info_text = Гілку %s тепер можна видалити. find_file.go_to_file = Знайти файл visibility_helper = Зробити репозиторій приватним -projects.card_type.desc = Попередній вигляд карток +projects.card_type.desc = Попередній перегляд карток projects.card_type.text_only = Лише текст projects.card_type.images_and_text = Зображення і текст issues.filter_poster = Автор @@ -2531,7 +2531,7 @@ issues.new.assign_to_me = Призначити собі contributors.contribution_type.additions = Додавання settings.add_web_hook_desc = Інтегрувати %s у цей репозиторій. settings.event_wiki_desc = Вікі-сторінку створено, перейменовано, відредаговано або видалено. -settings.mirror_settings.push_mirror.copy_public_key = Копіювати публічний ключ +settings.mirror_settings.push_mirror.copy_public_key = Копіювати відкритий ключ editor.add_tmpl.filename = назва файлу settings.unarchive.button = Розархівувати object_format = Формат об'єкта @@ -2573,7 +2573,7 @@ new_repo_helper = Репозиторій містить усі файли про issues.reference_link = Посилання: %s object_format_helper = Формат об'єктів репозиторія. Змінити потім неможливо. SHA1 — найсумісніший. mirror_interval = Інтервал синхронізації дзеркала (часові одиниці — «h», «m», «s»). 0 вимикає синхронізацію. (Мінімальний інтервал: %s) -mirror_address_url_invalid = Вказано хибну URL-адресу. Замініть escape-послідовностями всі неоднозначні складники адреси. +mirror_address_url_invalid = Вказано хибну URL-адресу. Впевніться, що всі неоднозначні складники адреси замінено escape-послідовностями. mirror_address_protocol_invalid = Хибна URL-адреса. Лише адреси http(s):// чи git:// можна використовувати для віддзеркалення. stars_remove_warning = Це вилучить усі зірки репозиторія. mirror_use_ssh.not_available = SSH-вхід недоступний. @@ -2672,163 +2672,186 @@ settings.event_action_recover = Відновлено commitstatus.success = Успіх commitstatus.failure = Збій issues.filter_type.all_pull_requests = Усі запити на злиття - -summary_card_alt = Підсумкова картка репозиторію %s -migrate_options_lfs_endpoint.placeholder = Якщо залишити порожнім, то кінцеву точку буде визначено з URL-адреси клону -migrate.invalid_local_path = Локальний шлях недійсний. Він не існує або не є каталогом. -subscribe.issue.guest.tooltip = Увійдіть, щоб підписатися на цю задачу. -subscribe.pull.guest.tooltip = Увійдіть, щоб підписатися на цей запит на злиття. -cite_this_repo = Послатися на цей репозиторій broken_message = Неможливо прочитати дані Git, що лежать в основі цього репозиторію. Зверніться до адміністратора цього екземпляра або видаліть репозиторій. -invisible_runes_header = `Цей файл містить невидимі символи Юнікоду` -invisible_runes_description = `Цей файл містить невидимі символи Юнікоду, які людині неможливо розрізнити, але які по-різному обробляються комп'ютером. Якщо так зроблено навмисно, можете ігнорувати це попередження. Щоб показати ці символи, скористайтеся кнопкою «Escape».` -ambiguous_runes_header = `Цей файл містить неоднозначні символи Юнікоду` -ambiguous_runes_description = `Цей файл містить символи Юнікоду, які легко сплутати з іншими символами. Якщо так зроблено навмисно, можете ігнорувати це попередження. Щоб показати ці символи, скористайтеся кнопкою «Escape».` -invisible_runes_line = `У цьому рядку є невидимі символи Юнікоду` -ambiguous_runes_line = `У цьому рядку є неоднозначні символи Юнікоду` -ambiguous_character = `%[1]c [U+%04[1]X] можна сплутати з %[2]c [U+%04[2]X]` -escape_control_characters = Escape -unescape_control_characters = Unescape -no_eol.text = Без EOL -editor.filename_is_invalid = Хибна назва файлу: «%s». -editor.directory_is_a_file = Назва каталогу «%s» уже використовується в цьому репозиторії як назва файлу. +migrate.invalid_local_path = Локальний шлях недійсний. Він не існує або не є каталогом. editor.filename_is_a_directory = Назва файлу «%s» уже використовується в цьому репозиторії як назва каталогу. +editor.filename_is_invalid = Хибна назва файлу: «%s». +migrate_options_lfs_endpoint.placeholder = Якщо залишити порожнім, то кінцеву точку буде визначено з URL-адреси клону +cite_this_repo = Послатися на цей репозиторій +editor.directory_is_a_file = Назва каталогу «%s» уже використовується в цьому репозиторії як назва файлу. +subscribe.issue.guest.tooltip = Увійдіть, щоб підписатися на цю задачу. +invisible_runes_header = `Цей файл містить невидимі символи Юнікоду` +invisible_runes_line = `У цьому рядку є невидимі символи Юнікоду` +subscribe.pull.guest.tooltip = Увійдіть, щоб підписатися на цей запит на злиття. +ambiguous_runes_header = `Цей файл містить неоднозначні символи Юнікоду` +ambiguous_runes_line = `У цьому рядку є неоднозначні символи Юнікоду` issues.choose.invalid_config = У конфігурації задачі є помилки: -issues.summary_card_alt = Підсумкова картка задачі «%s» в репозиторії %s +escape_control_characters = Escape +ambiguous_runes_description = `Цей файл містить символи Юнікоду, які легко сплутати з іншими символами. Якщо так зроблено навмисно, можете ігнорувати це попередження. Щоб показати ці символи, скористайтеся кнопкою «Escape».` +unescape_control_characters = Unescape +invisible_runes_description = `Цей файл містить невидимі символи Юнікоду, які людині неможливо розрізнити, але які по-різному обробляються комп'ютером. Якщо так зроблено навмисно, можете ігнорувати це попередження. Щоб показати ці символи, скористайтеся кнопкою «Escape».` +ambiguous_character = `%[1]c [U+%04[1]X] можна сплутати з %[2]c [U+%04[2]X]` settings.sourcehut_builds.secrets_helper = Надати завданню доступ до секретів збірки (потрібен дозвіл SECRETS:RO) +no_eol.text = Без EOL settings.remove_protected_branch_failed = Не вдалося видалити правило захисту гілок «%s». +summary_card_alt = Підсумкова картка репозиторію %s +issues.summary_card_alt = Підсумкова картка задачі «%s» в репозиторії %s release.summary_card_alt = Підсумкова картка випуску «%s» в репозиторії %s - -view_git_blame = Переглянути git blame -vendored = Сторонній -editor.file_editing_no_longer_exists = Файл «%s», який ви редагуєте, більше не існує у цьому репозиторії. -editor.file_deleting_no_longer_exists = Файл «%s», який ви видаляєте, більше не існує у цьому репозиторії. -editor.file_already_exists = Файл із назвою «%s» вже є у цьому репозиторії. -editor.push_rejected_no_message = Зміну відхилено сервером без повідомлення. Будь ласка, перевірте Git-хуки. -editor.push_rejected = Зміну відхилено сервером. Будь ласка, перевірте Git-хуки. -editor.unable_to_upload_files = Не вдалося завантажити файли в «%s» через помилку: %v -editor.upload_files_to_dir = Завантажити файли в «%s» -editor.cannot_commit_to_protected_branch = Неможливо здійснити коміт до захищеної гілки «%s». -commits.renamed_from = Перейменовано з %s -commits.ssh_key_fingerprint = Відбиток ключа SSH -commits.view_path = Переглянути на цьому етапі історії -issues.filter_sort.relevance = За відповідністю -issues.action_check = Поставити/зняти позначку -issues.action_check_all = Поставити/зняти позначку з усіх елементів -issues.closed_by = від %[3]s закрито %[1]s -issues.closed_by_fake = від %[2]s закрито %[1]s -issues.label_archived_filter = Показати архівовані мітки -pulls.allow_edits_from_maintainers_err = Не вдалося оновити -pulls.has_viewed_file = Переглянуто -pulls.viewed_files_label = %[1]d з %[2]d файлів переглянуто -pulls.status_checks_hide_all = Приховати всі перевірки -milestones.new_subheader = Етапи допомагають організувати задачі та відстежувати прогрес їх виконання. -milestones.filter_sort.name = За назвою -wiki.delete_page_notice_1 = Видалення вікі-сторінки «%s» неможливо скасувати. Продовжити? +release.hide_archive_links_helper = Приховати для цього випуску архіви вихідного коду, що генеруються автоматично. Наприклад, якщо ви завантажуєте свої архіви. activity.published_prerelease_label = Пре-реліз -settings.mirror_settings.docs.doc_link_pull_section = розділ документації «Отримання з віддаленого репозиторію». -settings.mirror_settings.docs.pulling_remote_title = Отримання з віддаленого репозиторію -settings.mirror_settings.push_mirror.edit_sync_time = Змінити інтервал синхронізації дзеркала -settings.mirror_settings.push_mirror.none_ssh = Немає +release.title = Назва випуску +release.system_generated = Це вкладення згенеровано автоматично. +release.hide_archive_links = Приховати автоматично генеровані архіви +branch.delete_desc = Видалення гілки є остаточним. Хоча видалена гілка може існувати ще деякий час до того, як її буде видалено, цю дію НЕМОЖЛИВО скасувати в більшості випадків. Продовжити? +release.deletion_desc = Видалення випуску видаляє його лише з Forgejo. При цьому тег Git, вміст репозиторію чи його історію не буде змінено. Продовжити? +settings.sourcehut_builds.manifest_path = Шлях до маніфесту збірки +settings.wiki_branch_rename_failure = Не вдалося нормалізувати назву вікі-гілки репозиторію. +settings.sourcehut_builds.visibility = Видимість завдань +settings.unarchive.header = Розархівувати цей репозиторій +settings.unarchive.success = Репозиторій успішно розархівовано. +diff.git-notes.remove-body = Цю примітку буде видалено. +tag.ahead.target = до %s після цього тегу +branch.tag_collision = Неможливо створити гілку «%s», оскільки у репозиторії вже є тег із такою назвою. +branch.branch_name_conflict = Назва гілки «%s» конфліктує з наявною гілкою «%s». +settings.wiki_branch_rename_success = Назву вікі-гілки репозиторію успішно нормалізовано. +diff.has_escaped = У цьому рядку є приховані символи Юнікоду +settings.unarchive.text = Розархівування репозиторію відновить можливість надсилати до нього коміти і виконувати push, а також створювати задачі і запити на злиття. +settings.wiki_rename_branch_main_notices_1 = Цю операцію НЕМОЖЛИВО скасувати. +settings.unarchive.error = Сталася помилка при спробі розархівувати репозиторій. Докладнішу інформацію див. у журналі. +settings.wiki_rename_branch_main = Нормалізувати назву вікі-гілки +settings.wiki_rename_branch_main_notices_2 = Внутрішню вікі-гілку репозиторію %s буде назавжди перейменовано. Існуючі перевірки потрібно буде оновити. settings.pull_mirror_sync_in_progress = Триває отримання змін з віддаленого репозиторію %s. -settings.pull_mirror_sync_quota_exceeded = Перевищено квоту, отримання змін неможливе. -settings.push_mirror_sync_in_progress = Триває надсилання змін до віддаленого репозиторію %s. +settings.enter_repo_name = Уведіть ім'я власника і назву репозиторію, як показано: settings.pulls.allow_rebase_update = Увімкнути оновлення гілки запиту на злиття за допомогою перебазування -settings.releases_desc = Увімкнути випуски в репозиторії -settings.admin_code_indexer = Індексатор коду +settings.mirror_settings.docs.pulling_remote_title = Отримання з віддаленого репозиторію +settings.mirror_settings.docs.doc_link_pull_section = розділ документації «Отримання з віддаленого репозиторію». +settings.pull_mirror_sync_quota_exceeded = Перевищено квоту, отримання змін неможливе. +settings.mirror_settings.push_mirror.none_ssh = Немає settings.admin_stats_indexer = Індексатор статистики коду settings.admin_indexer_commit_sha = Останній індексований коміт -settings.admin_indexer_unindexed = Не індексовано -settings.enter_repo_name = Уведіть ім'я власника і назву репозиторію, як показано: -settings.wiki_rename_branch_main = Нормалізувати назву вікі-гілки -settings.wiki_rename_branch_main_notices_1 = Цю операцію НЕМОЖЛИВО скасувати. -settings.wiki_rename_branch_main_notices_2 = Внутрішню вікі-гілку репозиторію %s буде назавжди перейменовано. Існуючі перевірки потрібно буде оновити. -settings.wiki_branch_rename_success = Назву вікі-гілки репозиторію успішно нормалізовано. -settings.wiki_branch_rename_failure = Не вдалося нормалізувати назву вікі-гілки репозиторію. -settings.add_webhook.invalid_path = Шлях не повинен містити частини «.» або «..» і не повинен бути порожнім. Він не може починатися або закінчуватися косою рискою. settings.discord_icon_url.exceeds_max_length = URL-адреса значка не може бути довшою, ніж 2048 символи -settings.web_hook_name_telegram = Telegram -settings.sourcehut_builds.manifest_path = Шлях до маніфесту збірки -settings.sourcehut_builds.visibility = Видимість завдань -settings.sourcehut_builds.access_token_helper = Токен доступу, який має дозвіл JOBS:RW. Згенеруйте токен builds.sr.ht або токен builds.sr.ht з доступом до секретів на meta.sr.ht. -settings.protect_status_check_patterns_desc = Уведіть шаблони, щоб вказати, які перевірки стану повинні пройти гілки, перш ніж їх буде об'єднано у гілку, що відповідає цьому правилу. Кожен рядок визначає шаблон. Шаблони не можуть бути порожніми. settings.protect_invalid_status_check_pattern = Недійсний шаблон перевірки стану: «%s». settings.protect_no_valid_status_check_patterns = Немає дійсних шаблонів перевірки стану. -settings.protect_patterns = Шаблони -settings.protected_branch_duplicate_rule_name = Для цього набору гілок уже є правило -settings.thread_id = Thread ID +settings.admin_indexer_unindexed = Не індексовано +settings.push_mirror_sync_in_progress = Триває надсилання змін до віддаленого репозиторію %s. +settings.releases_desc = Увімкнути випуски в репозиторії +settings.admin_code_indexer = Індексатор коду +settings.protect_status_check_patterns_desc = Уведіть шаблони, щоб вказати, які перевірки стану повинні пройти гілки, перш ніж їх буде об'єднано у гілку, що відповідає цьому правилу. Кожен рядок визначає шаблон. Шаблони не можуть бути порожніми. +settings.add_webhook.invalid_path = Шлях не повинен містити частини «.» або «..» і не повинен бути порожнім. Він не може починатися або закінчуватися косою рискою. settings.matrix.access_token_helper = Рекомендується створити окремий обліковий запис Matrix. Токен доступу можна отримати у вебклієнті Element (у приватній/інкогніто вкладці): User menu (вгорі ліворуч) > All settings > Help & About > Advanced > Access Token (під URL-адресою Homeserver). Закрийте приватну вкладку (вихід із системи зробить токен недійсним). +settings.protect_patterns = Шаблони +settings.sourcehut_builds.access_token_helper = Токен доступу, який має дозвіл JOBS:RW. Згенеруйте токен builds.sr.ht або токен builds.sr.ht з доступом до секретів на meta.sr.ht. +editor.unable_to_upload_files = Не вдалося завантажити файли в «%s» через помилку: %v +view_git_blame = Переглянути git blame +commits.ssh_key_fingerprint = Відбиток ключа SSH +settings.web_hook_name_telegram = Telegram +pulls.status_checks_hide_all = Приховати всі перевірки +settings.thread_id = Thread ID settings.matrix.room_id_helper = ID кімнати можна отримати у вебклієнті Element: Room Settings > Advanced > Internal room ID. Приклад: %s. -settings.unarchive.header = Розархівувати цей репозиторій -settings.unarchive.text = Розархівування репозиторію відновить можливість надсилати до нього коміти і виконувати push, а також створювати задачі і запити на злиття. -settings.unarchive.success = Репозиторій успішно розархівовано. -settings.unarchive.error = Сталася помилка при спробі розархівувати репозиторій. Докладнішу інформацію див. у журналі. -diff.git-notes.remove-body = Цю примітку буде видалено. -diff.has_escaped = У цьому рядку є приховані символи Юнікоду +editor.push_rejected_no_message = Зміну відхилено сервером без повідомлення. Будь ласка, перевірте Git-хуки. +editor.upload_files_to_dir = Завантажити файли в «%s» +issues.filter_sort.relevance = За відповідністю +editor.cannot_commit_to_protected_branch = Неможливо здійснити коміт до захищеної гілки «%s». +editor.file_deleting_no_longer_exists = Файл «%s», який ви видаляєте, більше не існує у цьому репозиторії. +editor.file_editing_no_longer_exists = Файл «%s», який ви редагуєте, більше не існує у цьому репозиторії. diff.show_file_tree = Показати дерево файлів +milestones.filter_sort.name = За назвою +pulls.allow_edits_from_maintainers_err = Не вдалося оновити +pulls.has_viewed_file = Переглянуто +editor.file_already_exists = Файл із назвою «%s» вже є у цьому репозиторії. +commits.view_path = Переглянути на цьому етапі історії +editor.push_rejected = Зміну відхилено сервером. Будь ласка, перевірте Git-хуки. +commits.renamed_from = Перейменовано з %s diff.hide_file_tree = Приховати дерево файлів -tag.ahead.target = до %s після цього тегу -release.title = Назва випуску -release.deletion_desc = Видалення випуску видаляє його лише з Forgejo. При цьому тег Git, вміст репозиторію чи його історію не буде змінено. Продовжити? -release.hide_archive_links = Приховати автоматично генеровані архіви -release.hide_archive_links_helper = Приховати для цього випуску архіви вихідного коду, що генеруються автоматично. Наприклад, якщо ви завантажуєте свої архіви. -release.system_generated = Це вкладення згенеровано автоматично. -branch.delete_desc = Видалення гілки є остаточним. Хоча видалена гілка може існувати ще деякий час до того, як її буде видалено, цю дію НЕМОЖЛИВО скасувати в більшості випадків. Продовжити? -branch.branch_name_conflict = Назва гілки «%s» конфліктує з наявною гілкою «%s». -branch.tag_collision = Неможливо створити гілку «%s», оскільки у репозиторії вже є тег із такою назвою. - -blame.ignore_revs = Зміни в .git-blame-ignore-revs ігноруються. Натисніть тут, щоб обійти це і переглянути авторство у звичайному вигляді. +pulls.viewed_files_label = %[1]d з %[2]d файлів переглянуто +settings.protected_branch_duplicate_rule_name = Для цього набору гілок уже є правило +settings.mirror_settings.push_mirror.edit_sync_time = Змінити інтервал синхронізації дзеркала +wiki.delete_page_notice_1 = Видалення вікі-сторінки «%s» неможливо скасувати. Продовжити? +issues.label_archived_filter = Показати архівовані мітки +milestones.new_subheader = Етапи допомагають організувати задачі та відстежувати прогрес їх виконання. +issues.closed_by_fake = від %[2]s закрито %[1]s +issues.closed_by = від %[3]s закрито %[1]s +issues.action_check = Поставити/зняти позначку +issues.action_check_all = Поставити/зняти позначку з усіх елементів +vendored = Сторонній blame.ignore_revs.failed = Не вдалося проігнорувати зміни в .git-blame-ignore-revs. -template.git_hooks_tooltip = Наразі ви не можете змінювати або видаляти додані Git-хуки. Вибирайте лише якщо ви довіряєте репозиторію шаблону. -migrate.github_token_desc = Ви можете ввести тут один або кілька токенів через кому, щоб пришвидшити міграцію в обхід обмеження частоти звернень до API GitHub. ОБЕРЕЖНО: зловживання цією функцією може порушити політику постачальника послуг і призвести до блокування облікового запису. -migrate.github.description = Перенесіть дані з github.com або сервера GitHub Enterprise. -migrate.cancel_migrating_confirm = Бажаєте скасувати перенесення? +blame.ignore_revs = Зміни в .git-blame-ignore-revs ігноруються. Натисніть тут, щоб обійти це і переглянути авторство у звичайному вигляді. editor.new_branch_name = Укажіть назву нової гілки для цього коміту -projects.column.edit = Редагувати стовпчик -projects.column.new_submit = Створити стовпчик -projects.column.new = Новий стовпчик -projects.column.set_default_desc = Призначити цей стовпчик за замовчуванням для задач і запитів на злиття без категорії -projects.column.delete = Видалити стовпчик projects.column.deletion_desc = Видалення стовпчика проєкту призведе до переміщення всіх пов'язаних із ним задач до стовпчика за замовчуванням. Продовжити? -issues.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст задачі. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін -issues.label_templates.fail_to_load_file = Не вдалося завантажити файл шаблону міток «%s»: %v -issues.reaction.add = Додати реакцію -issues.reaction.alt_many = %[1]s і ще %[2]d реагують %[3]s. -issues.reaction.alt_remove = Прибрати реакцію %[1] з коментаря. -issues.reaction.alt_add = Додати реакцію %[1]s до коментаря. -issues.archived_label_description = (Архівна) %s -issues.delete.text = Ви дійсно хочете видалити цю задачу? (Весь її вміст буде остаточно видалено. Можливо, варто її закрити і зберегти в архіві) -pulls.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст запиту на злиття. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін +signing.wont_sign.approved = Злиття не буде підписано, оскільки запит на злиття не схвалено. pulls.allow_edits_from_maintainers = Дозволити редагування від супроводжувачів pulls.showing_only_single_commit = Показано тільки зміни коміту %[1]s pulls.showing_specified_commit_range = Показано тільки зміни між %[1]s..%[2]s -pulls.blocked_by_approvals = Цей запит на злиття ще не має достатньої кількості схвалень. Отримано %d з %d схвалень. -pulls.blocked_by_rejection = Цей запит на злиття містить зміни, запропоновані офіційним рецензентом. -pulls.blocked_by_official_review_requests = Цей запит на злиття заблоковано, оскільки йому бракує схвалення від одного або кількох офіційних рецензентів. pulls.blocked_by_outdated_branch = Цей запит на злиття заблоковано, оскільки він застарів. -pulls.blocked_by_changed_protected_files_1 = Цей запит на злиття заблоковано, оскільки він змінює захищений файл: pulls.blocked_by_changed_protected_files_n = Цей запит на злиття заблоковано, оскільки він змінює захищені файли: pulls.auto_merge_newly_scheduled = Заплановано об'єднати запит на злиття після успішного завершення всіх перевірок. -pulls.auto_merge_has_pending_schedule = %[1]s планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[2]s. pulls.auto_merge_newly_scheduled_comment = `планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[1]s` -pulls.auto_merge_canceled_schedule_comment = `скасовує автоматичне об'єднання цього запиту на злиття після успішного завершення всіх перевірок %[1]s` -pulls.delete.text = Ви дійсно хочете видалити цей запит на злиття? (Весь його вміст буде остаточно видалено. Можливо, варто його закрити і зберегти в архіві) comments.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст коментаря. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін -signing.wont_sign.pubkey = Коміт не буде підписано, оскільки у вас немає публічного ключа, пов'язаного з вашим обліковим записом. -signing.wont_sign.twofa = Щоб підписувати коміти, у вас повинна бути ввімкнена двофакторна автентифікація. -signing.wont_sign.parentsigned = Цей коміт не буде підписано, оскільки не підписано батьківський коміт. +pulls.auto_merge_canceled_schedule_comment = `скасовує автоматичне об'єднання цього запиту на злиття після успішного завершення всіх перевірок %[1]s` +signing.wont_sign.pubkey = Коміт не буде підписано, оскільки у вас немає відкритого ключа, пов'язаного з вашим обліковим записом. signing.wont_sign.basesigned = Злиття не буде підписано, оскільки не підписано базовий коміт. signing.wont_sign.headsigned = Злиття не буде підписано, оскільки не підписано головний коміт. +projects.column.new_submit = Створити стовпчик +settings.authorization_header_desc = За наявності буде включено як заголовок авторизації для запитів. Приклади: %s. +template.git_hooks_tooltip = Наразі ви не можете змінювати або видаляти додані Git-хуки. Вибирайте лише якщо ви довіряєте репозиторію шаблону. +projects.column.edit = Редагувати стовпчик +projects.column.set_default_desc = Призначити цей стовпчик за замовчуванням для задач і запитів на злиття без категорії +projects.column.new = Новий стовпчик +projects.column.delete = Видалити стовпчик +pulls.blocked_by_official_review_requests = Цей запит на злиття заблоковано, оскільки йому бракує схвалення від одного або кількох офіційних рецензентів. +pulls.auto_merge_has_pending_schedule = %[1]s планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[2]s. signing.wont_sign.commitssigned = Злиття не буде підписано, оскільки всі пов'язані з ним коміти не підписані. -signing.wont_sign.approved = Злиття не буде підписано, оскільки запит на злиття не схвалено. +settings.authorization_header = Заголовок авторизації +issues.archived_label_description = (Архівна) %s +issues.reaction.add = Додати реакцію +issues.reaction.alt_add = Додати реакцію %[1]s до коментаря. +issues.reaction.alt_remove = Прибрати реакцію %[1] з коментаря. +migrate.github_token_desc = Ви можете ввести тут один або кілька токенів через кому, щоб пришвидшити міграцію в обхід обмеження частоти звернень до API GitHub. ОБЕРЕЖНО: зловживання цією функцією може порушити політику постачальника послуг і призвести до блокування облікового запису. +issues.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст задачі. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін +pulls.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст запиту на злиття. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін +issues.reaction.alt_many = %[1]s і ще %[2]d реагують %[3]s. +settings.pulls.default_allow_edits_from_maintainers = За замовчуванням дозволити редагування від супроводжувачів +pulls.blocked_by_changed_protected_files_1 = Цей запит на злиття заблоковано, оскільки він змінює захищений файл: +pulls.delete.text = Ви дійсно хочете видалити цей запит на злиття? (Весь його вміст буде остаточно видалено. Можливо, варто його закрити і зберегти в архіві) +pulls.blocked_by_rejection = Цей запит на злиття містить зміни, запропоновані офіційним рецензентом. +signing.wont_sign.parentsigned = Цей коміт не буде підписано, оскільки не підписано батьківський коміт. +settings.ignore_stale_approvals = Ігнорувати застарілі схвалення +pulls.blocked_by_approvals = Цей запит на злиття ще не має достатньої кількості схвалень. Отримано %d з %d схвалень. +issues.delete.text = Ви дійсно хочете видалити цю задачу? (Весь її вміст буде остаточно видалено. Можливо, варто її закрити і зберегти в архіві) +signing.wont_sign.twofa = Щоб підписувати коміти, у вас повинна бути ввімкнена двофакторна автентифікація. settings.mirror_settings.docs = Налаштуйте свій репозиторій на автоматичну синхронізацію комітів, тегів і гілок з іншим репозиторієм. -settings.mirror_settings.docs.disabled_pull_mirror.instructions = Налаштуйте свій проєкт на автоматичне надсилання комітів, тегів і гілок до іншого репозиторію. Pull-дзеркала вимкнено адміністратором сайту. settings.mirror_settings.docs.disabled_push_mirror.instructions = Налаштуйте свій проєкт на автоматичне отримання комітів, тегів і гілок з іншого репозиторію. settings.mirror_settings.docs.disabled_push_mirror.info = Push-дзеркала вимкнено адміністратором сайту. -settings.pulls.default_allow_edits_from_maintainers = За замовчуванням дозволити редагування від супроводжувачів -settings.authorization_header = Заголовок авторизації -settings.authorization_header_desc = За наявності буде включено як заголовок авторизації для запитів. Приклади: %s. -settings.ignore_stale_approvals = Ігнорувати застарілі схвалення +settings.mirror_settings.docs.disabled_pull_mirror.instructions = Налаштуйте свій проєкт на автоматичне надсилання комітів, тегів і гілок до іншого репозиторію. Pull-дзеркала вимкнено адміністратором сайту. +issues.label_templates.fail_to_load_file = Не вдалося завантажити файл шаблону міток «%s»: %v +migrate.github.description = Перенести дані з github.com або сервера GitHub Enterprise. +migrate.cancel_migrating_confirm = Бажаєте скасувати перенесення? +transfer.no_permission_to_accept = У вас немає дозволу прийняти цю передачу. +transfer.no_permission_to_reject = У вас немає дозволу відхилити цю передачу. +issues.choose.ignore_invalid_templates = Недійсні шаблони проігноровано +issues.choose.invalid_templates = Знайдено %v недійсних шаблонів +settings.transfer_abort_success = Передачу репозиторію %s успішно скасовано. +pulls.cmd_instruction_checkout_title = Переключення гілок +pulls.cmd_instruction_checkout_desc = З репозиторію вашого проєкту переключіться на нову гілку і протестуйте зміни. +issues.review.pending.tooltip = Цей коментар наразі невидимий для інших користувачів. Щоб надіслати коментарі у черзі, виберіть «%s» → «%s/%s/%s» у верхній частині сторінки. +issues.dependency.issue_batch_close_blocked = Неможливо виконати пакетне закриття вибраних задач, оскільки задача #%d має відкриті залежності +commit.revert = Вивернути +commit.revert-header = Вивернути: %s +editor.revert = Вивернути %s на: +commit.revert-content = Оберіть гілку, на яку вивернути: +pulls.cmd_instruction_merge_warning = Увага: в цьому репозиторії не ввімкнено «Автовизначення ручного об'єднання», тож позначити цей запит як об'єднаний вручну вам доведеться самостійно. +topic.format_prompt = Теми повинні починатися з літери або цифри, можуть містити дефіси («-») і крапки («.») і мати довжину до 35 символів. Дозволено використання лише малих літер. +issues.label_archive = Архівна мітка +editor.invalid_commit_mail = Недійсна пошта для створення коміту. +issues.label_exclusive = Ексклюзивний +issues.filter_type.reviewed_by_you = Перевірено вами +editor.push_out_of_date = Схоже, дані для відправки застаріли. +editor.commit_email = Пошта автора +issues.cancel_tracking_history = `скасовує відстеження часу %s` +issues.label_archive_tooltip = Архівовані мітки за замовчуванням виключаються з пропозицій під час пошуку за міткою. +settings.protect_status_check_matched = Збіг +settings.webhook.delivery.success = Подію додано до черги доставки. Може знадобитися кілька секунд, перш ніж вона з'явиться в історії доставки. +settings.protected_branch_required_rule_name = Необхідна назва правила [graphs] contributors.what = внески @@ -2958,9 +2981,12 @@ teams.invite_team_member = Запросити до %s teams.write_access = Запис teams.invite.by = Вас запрошує %s teams.invite_team_member.list = Запрошення в очікуванні - form.name_pattern_not_allowed = Вираз «%s» не може бути частиною назви організації. teams.add_nonexistent_repo = Репозиторій, який ви намагаєтеся додати, не існує. Спочатку створіть його. +settings.change_orgname_prompt = Зауважте, зміна назви організації також змінить URL-адресу організації і звільнить стару назву. +teams.none_access_helper = Опція «Немає доступу» впливає лише на приватні репозиторії. +teams.general_access_helper = Дозволи учасників будуть визначатися відповідно до наведеної нижче таблиці дозволів. +teams.general_access = Індивідуальний доступ [admin] dashboard=Панель управління @@ -3013,7 +3039,7 @@ dashboard.resync_all_sshprincipals=Оновити файл «.ssh/authorized_pri dashboard.resync_all_hooks=Пересинхронізувати хуки pre-receive, update та post-receive в усіх репозиторіях dashboard.reinit_missing_repos=Переініціалізувати усі репозитрії git-файли яких втрачено dashboard.sync_external_users=Синхронізувати дані зовнішніх користувачів -dashboard.cleanup_hook_task_table=Очистити hook_task таблицю +dashboard.cleanup_hook_task_table=Очистити таблицю hook_task dashboard.server_uptime=Час роботи сервера dashboard.current_goroutine=Поточна кількість Goroutines dashboard.current_memory_usage=Поточне використання пам'яті @@ -3035,16 +3061,16 @@ dashboard.mspan_structures_obtained=Отримано структур MSpan dashboard.mcache_structures_usage=Використання структур MCache dashboard.mcache_structures_obtained=Отримано структур MCache dashboard.profiling_bucket_hash_table_obtained=Отримано хеш-таблиць профілювання -dashboard.gc_metadata_obtained=Отримано метаданих збирача сміття (GC) +dashboard.gc_metadata_obtained=Отримано метаданих збирача сміття dashboard.other_system_allocation_obtained=Отримання інших виділень пам'яті -dashboard.next_gc_recycle=Наступний цикл збирача сміття (GC) -dashboard.last_gc_time=З останнього запуску збирача сміття (GC) +dashboard.next_gc_recycle=Наступний цикл збирача сміття +dashboard.last_gc_time=З останнього запуску збирача сміття dashboard.total_gc_time=Загальна пауза збирача сміття (GC) -dashboard.total_gc_pause=Загальна пауза збирача сміття (GC) -dashboard.last_gc_pause=Остання пауза збирача сміття (GC) -dashboard.gc_times=Кількість запусків збирача сміття (GC) -dashboard.delete_old_actions=Видалити всі старі дії з бази даних -dashboard.delete_old_actions.started=Видалення всіх старі дії з бази даних розпочато. +dashboard.total_gc_pause=Загальна пауза збирача сміття +dashboard.last_gc_pause=Остання пауза збирача сміття +dashboard.gc_times=Кількість запусків збирача сміття +dashboard.delete_old_actions=Видалити всі старі активності з бази даних +dashboard.delete_old_actions.started=Розпочато видалення всіх старих активностей з бази даних. users.user_manage_panel=Керування обліковими записами users.new_account=Створити обліковий запис @@ -3068,10 +3094,10 @@ users.update_profile_success=Обліковий запис користувач users.edit_account=Редагувати обліковий запис users.max_repo_creation=Максимальна кількість репозиторіїв users.max_repo_creation_desc=(Введіть -1, щоб використовувати глобальний ліміт за замовчуванням.) -users.is_activated=Обліковий запис користувача увімкнено +users.is_activated=Активований обліковий запис users.prohibit_login=Заблокований обліковий запис users.is_admin=Обліковий запис адміністратора -users.is_restricted=Обмежений +users.is_restricted=Обмежений обліковий запис users.allow_git_hook=Може створювати Git-хуки users.allow_git_hook_tooltip=Git-хуки виконуються від імені користувача ОС, від якого запущено Forgejo, і мають той самий рівень доступу до хоста. Таким чином, користувачі зі спеціальними правами Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних Forgejo. Вони також здатні отримати права адміністратора Forgejo. users.allow_import_local=Може імпортувати локальні репозиторії @@ -3153,16 +3179,16 @@ auths.domain=Домен auths.host=Хост auths.port=Порт auths.bind_dn=Прив'язати DN -auths.bind_password=Прив'язати пароль +auths.bind_password=Пароль bind auths.user_base=База пошуку користувачів auths.user_dn=DN користувача auths.attribute_username=Атрибут імені користувача auths.attribute_username_placeholder=Залиште порожнім, щоб використовувати ім'я користувача для реєстрації. auths.attribute_name=Атрибут імені -auths.attribute_surname=Атрибут Surname -auths.attribute_mail=Атрибут Email -auths.attribute_ssh_public_key=Атрибут Відкритий SSH ключ -auths.attributes_in_bind=Витягувати атрибути в контексті Bind DN +auths.attribute_surname=Атрибут прізвища +auths.attribute_mail=Атрибут адреси email +auths.attribute_ssh_public_key=Атрибут відкритого ключа SSH +auths.attributes_in_bind=Отримувати атрибути в контексті bind DN auths.allow_deactivate_all=Дозволити порожньому результату пошуку відключити всіх користувачів auths.use_paged_search=Використовувати посторінковий пошук auths.search_page_size=Розмір сторінки @@ -3216,15 +3242,15 @@ auths.tips.oauth2.general=Автентифікація OAuth2 auths.tip.oauth2_provider=Постачальник OAuth2 auths.tip.bitbucket=Зареєструйте нового споживача OAuth на %s і додайте дозвіл «Обліковий запис» — «Читання» auths.tip.nextcloud=`Зареєструйте нового споживача OAuth у вашому екземплярі за допомогою наступного меню "Налаштування -> Безпека -> клієнт OAuth 2.0"` -auths.tip.dropbox=Створіть новий додаток на %s -auths.tip.facebook=Зареєструйте новий додаток на %s і додайте модуль «Facebook Login» -auths.tip.github=Зареєструйте новий додаток OAuth на %s +auths.tip.dropbox=Створіть нову програму на %s +auths.tip.facebook=Зареєструйте нову програму на %s і додайте модуль «Facebook Login» +auths.tip.github=Зареєструйте нову програму OAuth на %s auths.tip.gitlab=Додайте новий додаток на https://gitlab.com/profile/applications auths.tip.google_plus=Отримайте облікові дані клієнта OAuth2 в консолі Google API на сторінці %s auths.tip.openid_connect=Використовуйте OpenID Connect Discovery URL (/.well-known/openid-configuration) для автоматичної настройки входу OAuth auths.tip.twitter=Перейдіть на %s, створіть програму і переконайтеся, що ввімкнено опцію «Дозволити використання цієї програми для входу через Twitter» -auths.tip.discord=Зареєструйте новий додаток на %s -auths.tip.yandex=Створіть новий додаток на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» +auths.tip.discord=Зареєструйте нову програму на %s +auths.tip.yandex=Створіть нову програму на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» auths.tip.mastodon=Введіть URL спеціального екземпляра для екземпляра mastodon, який ви хочете автентифікувати за допомогою (або використовувати за замовчуванням) auths.edit=Редагувати джерело автентифікації auths.activated=Це джерело автентифікація активоване @@ -3252,7 +3278,7 @@ config.git_version=Версія Git config.repo_root_path=Шлях до кореня репозиторію config.lfs_root_path=Кореневий шлях LFS config.log_file_root_path=Шлях до лог файлу -config.script_type=Тип скрипта +config.script_type=Тип скрипту config.reverse_auth_user=Користувач для авторизації на зворотному проксі config.ssh_config=Конфігурація SSH @@ -3298,10 +3324,10 @@ config.default_keep_email_private=Приховувати адреси елект config.default_allow_create_organization=Дозволити створення організацій за замовчуванням config.enable_timetracking=Увімкнути відстеження часу config.default_enable_timetracking=Увімкнути відстеження часу за замовчуванням -config.default_allow_only_contributors_to_track_time=Враховувати тільки учасників розробки в підрахунку часу +config.default_allow_only_contributors_to_track_time=Дозволити відстеження часу тільки учасникам розробки config.no_reply_address=Домен прихованих адрес електронної пошти config.default_visibility_organization=Видимість за замовчуванням для нових організацій -config.default_enable_dependencies=Увімкнути залежності задачі за замовчуванням +config.default_enable_dependencies=Увімкнути залежності задач за замовчуванням config.webhook_config=Конфігурація вебхуків config.queue_length=Довжина черги @@ -3326,13 +3352,13 @@ config.cache_config=Конфігурація кешу config.cache_adapter=Адаптер кешу config.cache_interval=Інтервал кешування config.cache_conn=Підключення до кешу -config.cache_item_ttl=Час зберігання даних кешу +config.cache_item_ttl=Час зберігання даних у кеші config.session_config=Конфігурація сесії config.session_provider=Провайдер сесії config.provider_config=Конфігурація постачальника config.cookie_name=Назва файлу cookie -config.gc_interval_time=Інтервал запуску збирача сміття (GC) +config.gc_interval_time=Інтервал запуску збирача сміття config.session_life_time=Час життя сесії config.https_only=Тільки HTTPS config.cookie_life_time=Час життя файлу cookie @@ -3347,12 +3373,12 @@ config.git_disable_diff_highlight=Вимкнути підсвітку синта config.git_max_diff_lines=Максимум рядків на diff (на один файл) config.git_max_diff_line_characters=Максимум символів на diff (на одну строку) config.git_max_diff_files=Максимум diff-файлів (для показу) -config.git_gc_args=Аргументи збирача сміття (GC) +config.git_gc_args=Аргументи збирача сміття config.git_migrate_timeout=Тайм-аут міграції config.git_mirror_timeout=Тайм-аут оновлення дзеркала config.git_clone_timeout=Тайм-аут операції клонування -config.git_pull_timeout=Тайм-аут операції Pull -config.git_gc_timeout=Тайм-аут операції збирача сміття (GC) +config.git_pull_timeout=Тайм-аут операції отримання змін +config.git_gc_timeout=Тайм-аут операції збирача сміття config.log_config=Конфігурація журналу config.disabled_logger=Вимкнено @@ -3391,10 +3417,10 @@ monitor.queue.settings.submit=Оновити налаштування monitor.queue.settings.changed=Налаштування оновлено notices.system_notice_list=Сповіщення системи -notices.view_detail_header=Переглянути деталі повідомлення +notices.view_detail_header=Подробиці сповіщення notices.select_all=Вибрати все -notices.deselect_all=Скасувати виділення -notices.inverse_selection=Інвертувати виділене +notices.deselect_all=Скасувати вибір +notices.inverse_selection=Інвертувати вибір notices.delete_selected=Видалити вибране notices.delete_all=Видалити всі cповіщення notices.type=Тип @@ -3439,8 +3465,8 @@ monitor.queue.settings.desc = Пули динамічно зростають у monitor.queue.settings.remove_all_items_done = Усі елементи в черзі видалено. monitor.queue.settings.remove_all_items = Видалити всі config.app_slogan = Гасло екземпляра -auths.tip.gitea = Зареєструйте новий додаток OAuth. Інструкцію можна знайти на %s -auths.tip.gitlab_new = Зареєструйте новий додаток на %s +auths.tip.gitea = Зареєструйте нову програму OAuth. Інструкцію можна знайти на %s +auths.tip.gitlab_new = Зареєструйте нову програму на %s monitor.duration = Тривалість (с) users.reserved = Зарезервовано systemhooks.desc = Вебхуки автоматично сповіщають HTTP-сервер POST-запитами, коли в Forgejo відбуваються певні події. Вказані тут вебхуки спрацьовуватимуть для всіх репозиторіїв системи, тож врахуйте всі ймовірні наслідки для швидкодії. Докладніше — в посібнику з вебхуків. @@ -3469,27 +3495,41 @@ defaulthooks.desc = Вебхуки автоматично сповіщають H assets = Ресурси коду auths.invalid_openIdConnectAutoDiscoveryURL = Неправильна URL-адреса автоматичного виявлення (повинна бути дійсна URL-адреса, що починається з http:// або https://) settings = Налаштування адміністратора - - -dashboard.cancel_abandoned_jobs = Скасувати покинуті завдання дій dashboard.start_schedule_tasks = Запустити заплановані завдання дій config.logger_name_fmt = Журнал: %s -config.access_log_template = Шаблон журналу доступу config.set_setting_failed = Не вдалося встановити параметр %s +config.access_log_template = Шаблон журналу доступу +dashboard.cancel_abandoned_jobs = Скасувати покинуті завдання дій monitor.download_diagnosis_report = Завантажити діагностичний звіт - +auths.oauth2_map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи. +config.mailer_smtp_addr = Адреса SMTP dashboard.update_checker = Перевірка оновлень auths.map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи LDAP auths.enable_ldap_groups = Увімкнути групи LDAP -auths.oauth2_map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи. -config.mailer_smtp_addr = Адреса SMTP - -auths.new_success = Метод автентифікації «%s» додано. auths.unable_to_initialize_openid = Не вдалося ініціалізувати постачальника OpenID Connect: %s -config.cache_test = Перевірити кеш -config.cache_test_failed = Не вдалося перевірити кеш: %v. +auths.new_success = Метод автентифікації «%s» додано. config.cache_test_slow = Перевірку кешу завершено успішно, але відповідь повільна: %s. config.cache_test_succeeded = Перевірку кешу завершено успішно, відповідь отримано через %s. +config.cache_test = Перевірити кеш +config.cache_test_failed = Не вдалося перевірити кеш: %v. +dashboard.rebuild_issue_indexer = Перебудувати індексатор задач +users.details = Дані користувача +auths.login_source_exist = Джерело автентифікації «%s» вже існує. +dashboard.delete_old_system_notices = Видалити всі старі сповіщення системи з бази даних +users.purge = Повністю видалити користувач_ку +auths.tips.oauth2.general.tip = При реєстрації нової програми OAuth2 URL-адреса зворотного виклику/переспрямування повинна бути: +auths.attribute_avatar = Атрибут аватара +auths.oauth2_required_claim_name = Необхідна назва заявки +auths.oauth2_admin_group = Значення групової заявки для адміністраторів. (Необов'язково — потрібна назва заявки вище) +auths.oauth2_required_claim_name_helper = Вкажіть назву, щоб обмежити вхід з цього джерела користувачами із заявкою з такою назвою +auths.map_group_to_team = Зіставити групи LDAP з командами організації (залиште поле порожнім, щоб пропустити) +auths.oauth2_required_claim_value = Необхідне значення заявки +auths.oauth2_map_group_to_team = Зіставити заявлені групи з командами організації. (Необов'язково — потрібна назва заявки вище) +auths.oauth2_required_claim_value_helper = Вкажіть значення, щоб обмежити вхід з цього джерела користувачами із заявкою з такими назвою і значенням +auths.oauth2_group_claim_name = Назва заявки, що надає назви груп для цього джерела. (Необов'язково) +auths.oauth2_restricted_group = Значення групової заявки для обмежених користувачів. (Необов'язково — потрібна назва заявки вище) +users.local_import.description = Дозволити імпорт репозиторіїв з локальної файлової системи сервера. Це може становити загрозу безпеці. + [action] create_repo=створив(ла) репозиторій %s @@ -3507,7 +3547,7 @@ merge_pull_request=`прийняв запит злиття %[3]s transfer_repo=перенесено репозиторій %s у %s push_tag=надсилає тег %[3]s в %[4]s delete_tag=видаляє тег %[2]s із %[3]s -delete_branch=видалено гілку %[2]s з %[3]s +delete_branch=видалено гілку %[2]s з %[3]s compare_branch=Порівняти compare_commits=Порівняти %d комітів compare_commits_general=Порівняти коміти @@ -3708,17 +3748,38 @@ alpine.registry = Налаштуйте цей реєстр, додавши URL cran.registry = Налаштуйте цей реєстр у файлі Rprofile.site: npm.registry = Налаштуйте цей реєстр у файлі .npmrc свого проєкту: chef.registry = Налаштуйте цей реєстр у файлі ~/.chef/config.rb: -owner.settings.chef.keypair.description = Запити до реєстру Chef повинні бути криптографічно підписані як засіб автентифікації. При генерації пари ключів на Forgejo зберігається тільки публічний ключ. Приватний ключ надається вам для використання команд knife. Генерація нової пари ключів замінить попередню. - +owner.settings.chef.keypair.description = Запити до реєстру Chef повинні бути криптографічно підписані як засіб автентифікації. При генерації пари ключів на Forgejo зберігається тільки відкритий ключ. Приватний ключ надається вам для використання команд knife. Генерація нової пари ключів замінить попередню. nuget.dependency.framework = Цільовий фреймворк owner.settings.cleanuprules.preview.overview = Заплановано видалити %d пакунків. - -arch.pacman.repo.multi = %s має одну й ту саму версію в різних дистрибутивах. -maven.install2 = Запустити з командного рядка: +owner.settings.cleanuprules.pattern_full_match = Застосувати шаблон до повної назви пакунка maven.download = Щоб завантажити залежність, запустіть із командного рядка: +maven.install2 = Запустити з командного рядка: npm.dependencies.bundle = Пакетні залежності npm.dependencies.peer = Однорангові залежності -owner.settings.cleanuprules.pattern_full_match = Застосувати шаблон до повної назви пакунка +arch.pacman.repo.multi = %s має одну й ту саму версію в різних дистрибутивах. +owner.settings.cargo.rebuild = Перебудувати індекс +owner.settings.cleanuprules.none = Правил очистки ще немає. +owner.settings.cleanuprules.preview.none = Правило очистки не відповідає жодному пакунку. +owner.settings.cargo.initialize = Ініціалізувати індекс +owner.settings.cargo.initialize.description = Для реєстру Cargo потрібен спеціальний Git-репозиторій з індексом. Використання цієї опції (пере)створить репозиторій і автоматично його налаштує. +owner.settings.cargo.initialize.error = Не вдалося ініціалізувати індекс Cargo: %v +owner.settings.cargo.initialize.success = Індекс Cargo успішно створено. +owner.settings.cargo.rebuild.error = Не вдалося перебудувати індекс Cargo: %v +owner.settings.cargo.title = Індекс реєстру Cargo +owner.settings.cleanuprules.title = Правила очистки +owner.settings.cleanuprules.add = Додати правило очистки +owner.settings.cargo.rebuild.success = Індекс Cargo успішно перебудовано. +owner.settings.cleanuprules.success.update = Правило очистки оновлено. +owner.settings.cleanuprules.success.delete = Правило очистки видалено. +owner.settings.cleanuprules.edit = Редагувати правило очистки +owner.settings.cleanuprules.preview = Попередній перегляд правила очистки +owner.settings.cargo.rebuild.description = Перебудування може бути корисним, якщо індекс не синхронізовано зі збереженими пакунками Cargo. +rpm.distros.redhat = у дистрибутивах на основі RedHat +rpm.distros.suse = у дистрибутивах на основі SUSE +owner.settings.cargo.rebuild.no_index = Неможливо перебудувати, індекс не ініціалізовано. +alpine.registry.key = Завантажте відкритий RSA-ключ реєстру в папку /etc/apk/keys/ для перевірки підпису індексу: +rubygems.required.ruby = Необхідна версія Ruby +rubygems.required.rubygems = Необхідна версія RubyGem [secrets] deletion = Видалити секрет @@ -3822,14 +3883,14 @@ runners.update_runner_success = Ранер оновлено runners.delete_runner_header = Підтвердіть видалення ранера runners.status.offline = Неактивний runners.status.idle = Простоює - - - - runs.invalid_workflow_helper = Недійсний файл конфігурації робочого потоку. Будь ласка, перевірте файл конфігурації: %s -runs.no_job_without_needs = Робочий потік повинен містити принаймні одне завдання без залежностей. runs.no_job = Робочий потік повинен містити принаймні одне завдання workflow.dispatch.use_from = Використати робочий потік із +runs.no_job_without_needs = Робочий потік повинен містити принаймні одне завдання без залежностей. +workflow.dispatch.trigger_found = Цей робочий потік спрацьовує на події workflow_dispatch. + + + [projects] type-3.display_name = Проєкт організації @@ -3895,8 +3956,9 @@ wiki.write = Писати: створювати, оновлювати т issues.read = Читати: дивитись і створювати задачі та коментарі. wiki.read = Читати: переглядати вбудовану вікі та її історію. actions.write = Писати: вручну запускати, перезапускати, скасовувати або схвалювати конвеєри CI/CD в очікуванні. - projects.write = Писати: створювати проєкти і стовпчики та редагувати їх. +ext_issues = Доступ до посилання на зовнішню систему відстеження задач. Налаштування дозволів відбувається поза сайтом. +ext_wiki = Доступ до посилання на зовнішню вікі. Налаштування дозволів відбувається поза сайтом. [munits.data] pib = ПіБ diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index fd6e625b5e..ab99f94728 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -828,7 +828,7 @@ activations_pending=等待激活 can_not_add_email_activations_pending=有一个待处理的激活请求,请稍等几分钟后再尝试添加新的电子邮件地址。 delete_email=移除 email_deletion=移除电子邮件地址 -email_deletion_desc=电子邮件地址和相关信息将会被删除。使用此电子邮件地址发送的Git提交将会保留,继续? +email_deletion_desc=此电子邮件地址及相关信息将被删除。使用此电子邮件地址的Git提交将被保留。继续吗? email_deletion_success=您的电子邮件地址已被移除。 theme_update_success=您的主题已更新。 theme_update_error=所选主题不存在。 @@ -1156,7 +1156,7 @@ mirror_sync = 已同步 mirror_sync_on_commit=推送提交时同步 mirror_address=从 URL 克隆 mirror_address_desc=在授权框中输入必要的凭据。 -mirror_address_url_invalid=URL无效。请检查您所输入的URL是否正确。 +mirror_address_url_invalid=URL无效。请检查所输入URL的所有部分是否被正确地转译。 mirror_address_protocol_invalid=提供的URL无效。只能使用http(s)://或git://地址进行镜像操作。 mirror_lfs=大文件存储(LFS) mirror_lfs_desc=镜像 LFS 数据。 @@ -1245,7 +1245,7 @@ migrate_items_releases=版本发布 migrate_repo=迁移仓库 migrate.clone_address=从 URL 迁移/克隆 migrate.clone_address_desc=现有仓库的 HTTP(s) 或 Git “clone” URL -migrate.github_token_desc=由于 GitHub API 速率限制,您可以在此处放置一个或多个以逗号分隔的令牌,以加快迁移速度。 警告:滥用此功能可能会违反服务提供商的政策并导致帐户被封。 +migrate.github_token_desc=您可以在此填写一个或多个以逗号分隔的令牌,以规避 GitHub API 速率限制并加快迁移速度。 警告:滥用此功能可能会违反服务提供商的政策并导致您的帐户被封禁。 migrate.clone_local_path=或服务器本地路径 migrate.permission_denied=您没有获得导入本地仓库的权限。 migrate.permission_denied_blocked=您不能从不允许的主机导入,请询问管理员以检查 ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS 设置。 @@ -1766,7 +1766,7 @@ issues.error_modifying_due_date=修改到期时间失败。 issues.error_removing_due_date=删除到期时间失败。 issues.push_commit_1=于 %[2]s 推送了 %[1]d 个提交 issues.push_commits_n=于 %[2]s 推送了 %[1]d 个提交 -issues.force_push_codes=`于 %[6]s 强制推送 %[1]s,从 %[2]s,至 %[4]s` +issues.force_push_codes=`于 %[6]s 强制推送 %[1]s,从 %[2]s %[8]s,至 %[4]s %[9]s` issues.force_push_compare=比较 issues.due_date_form=yyyy-mm-dd issues.due_date_form_add=设置到期时间 @@ -3636,7 +3636,7 @@ auto_merge_pull_request=`自动合并了拉取请求 %[3]s#%[2]s transfer_repo=将仓库 %s 转移至 %s push_tag=推送了标签 %[3]s 至仓库 %[4]s delete_tag=从%[3]s 删除了标签 %[2]s -delete_branch=从 %[3]s 删除分支 %[2]s +delete_branch=删除了 %[3]s%[2]s 分支 compare_branch=比较 compare_commits=比较 %d 提交 compare_commits_general=比较提交 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index ddc31730d7..d56c397812 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -1702,7 +1702,7 @@ issues.error_modifying_due_date=無法修改截止日期。 issues.error_removing_due_date=無法移除截止日期。 issues.push_commit_1=加入了 %d 個提交 %s issues.push_commits_n=加入了 %d 個提交 %s -issues.force_push_codes=`強制推送了 %[1]s 自 %[2]s%[4]s %[6]s` +issues.force_push_codes=`強制推送了 %[1]s 自 %[2]s %[8]s 至 %[4]s %[9]s %[6]s` issues.force_push_compare=比較 issues.due_date_form=yyyy年mm月dd日 issues.due_date_form_add=新增截止日期 @@ -3633,7 +3633,7 @@ auto_merge_pull_request=`自動合併了合併請求 %[3]s#%[2]s transfer_repo=將儲存庫 %s 轉移至 %s push_tag=推送了標籤 %[3]s%[4]s delete_tag=刪除了 %[3]s 的標籤 %[2]s -delete_branch=刪除了 %[3]s 的 %[2]s 分支 +delete_branch=刪除了 %[3]s%[2]s 分支 compare_branch=比較 compare_commits=比較 %d 個提交 compare_commits_general=比較提交 diff --git a/options/locale_next/locale_ar.json b/options/locale_next/locale_ar.json index 1574d3eb7c..aafb85cb4e 100644 --- a/options/locale_next/locale_ar.json +++ b/options/locale_next/locale_ar.json @@ -4,71 +4,143 @@ "home.explore_repos": "اكتشف المستودعات", "home.explore_users": "اكتشف المستخدمين", "home.explore_orgs": "اكتشف المنظمات", - "moderation.abuse_category.malware": "برمجية خبيثة", "moderation.abuse_category.illegal_content": "محتوى غير مشروع", + "moderation.abuse_category.malware": "برمجية خبيثة", + "relativetime.now": "الآن", + "relativetime.1month": "الشهر الفائت", + "relativetime.2weeks": "منذ أسبوعين", + "search.milestone_kind": "معالم البحث…", + "moderation.abuse_category.other_violations": "انتهاكات أخرى لقواعد المنصة", + "repo.issue_indexer.title": "مفهرس الإبلاغات", + "incorrect_root_url": "تم تكوين هذه النسخة من Forgejo لتعمل على العنوان \"%s\". أنت تقوم حاليًا بتصفّح Forgejo عبر رابط مختلف، مما قد يتسبب في تعطل بعض أجزاء التطبيق. يتم تحديد الرابط الرسمي (canonical URL) من قِبل مسؤولي Forgejo من خلال إعداد `ROOT_URL` في ملف `app.ini`.", + "error.not_found.title": "الصفحة غير موجودة", + "themes.names.forgejo-auto": "فورجيو (اتبع سمة النظام)", + "themes.names.forgejo-light": "فورجيجو المضيء", + "themes.names.forgejo-dark": "فورجيجو الداكن", "stars.list.none": "لم يقم أحد بتمييز هذا المستودع بنجمة.", "watch.list.none": "لا أحد يشاهد هذا المستودع.", "followers.incoming.list.self.none": "لا أحد يتابع ملفك الشخصي.", "followers.incoming.list.none": "لا أحد يتابع هذا المستخدم.", "followers.outgoing.list.self.none": "أنت لا تتبع أي شخص.", - "followers.outgoing.list.none": "لا يتابع %s أي شخص.", - "relativetime.now": "الآن", - "relativetime.future": "في المستقبل", "relativetime.1day": "الأمس", - "relativetime.2days": "منذ يومين", + "followers.outgoing.list.none": "لا يتابع %s أي شخص.", "relativetime.1week": "أخر أسبوع", - "relativetime.2weeks": "منذ أسبوعين", - "relativetime.1month": "الشهر الفائت", + "relativetime.2days": "منذ يومين", "relativetime.2months": "منذ شهرين", "relativetime.1year": "السنة الفائتة", "relativetime.2years": "منذ سنتين", "repo.form.cannot_create": "بلغت جميع المساحات التي يمكنك إنشاء مستودعات بها حدها.", - "repo.issue_indexer.title": "مفهرس الإبلاغات", - "search.milestone_kind": "معالم البحث…", - "incorrect_root_url": "تم تكوين هذه النسخة من Forgejo لتعمل على العنوان \"%s\". أنت تقوم حاليًا بتصفّح Forgejo عبر رابط مختلف، مما قد يتسبب في تعطل بعض أجزاء التطبيق. يتم تحديد الرابط الرسمي (canonical URL) من قِبل مسؤولي Forgejo من خلال إعداد `ROOT_URL` في ملف `app.ini`.", - "themes.names.forgejo-auto": "فورجيو (اتبع سمة النظام)", - "themes.names.forgejo-light": "فورجيجو المضيء", - "themes.names.forgejo-dark": "فورجيجو الداكن", - "error.not_found.title": "الصفحة غير موجودة", "alert.asset_load_failed": "تعذّر تحميل ملفات الأصول من {path}. تأكد من أن الملفات متاحة للوصول.", - "alert.range_error": " يجب أن يكون رقمًا بين %[1]s و %[2]s.", - "install.invalid_lfs_path": "غير قادر على إنشاء جذر LFS في المسار المحدد: %[1]s", - "profile.actions.tooltip": "إجراءات إضافية", - "profile.edit.link": "عدِّل ملف التعريف", - "feed.atom.link": "موجز Atom", - "keys.ssh.link": "مفاتيح SSH", + "settings.visibility.description": "رؤية ملفك الشخصي تؤثر في قدرة الآخرين على الوصول إلى مستودعاتك غير الخاصة. اعرف المزيد", + "relativetime.mins": { + "zero": "الآن", + "one": "منذ دقيقة مضت", + "two": "منذ دقيقتين مضت", + "few": "منذ %d دقائق مضت", + "many": "منذ %d دقيقة مضت", + "other": "منذ %d دقيقة مضت" + }, + "relativetime.hours": { + "zero": "الآن", + "one": "منذ ساعة", + "two": "منذ ساعتين", + "few": "منذ %d ساعات", + "many": "منذ %d ساعة", + "other": "منذ %d ساعة" + }, + "moderation.report_remarks": "الملاحظات", + "repo.diff.commit.next-short": "التالي", + "repo.diff.commit.previous-short": "السابق", + "admin.dashboard.cleanup_offline_runners": "تنظيف وحدات التشغيل غير المتصلة", + "relativetime.days": { + "zero": "اليوم", + "one": "منذ يوم واحد", + "two": "منذ يومين", + "few": "منذ %d أيام", + "many": "منذ %d يوماً", + "other": "منذ %d يوماً" + }, + "relativetime.weeks": { + "zero": "هذا الأسبوع", + "one": "منذ أسبوع واحد", + "two": "منذ أسبوعين", + "few": "منذ %d أسابيع", + "many": "منذ %d أسبوعاً", + "other": "منذ %d أسبوعاً" + }, + "relativetime.years": { + "zero": "هذه السنة", + "one": "منذ سنة واحدة", + "two": "منذ سنتين", + "few": "منذ %d سنوات", + "many": "منذ %d سنة", + "other": "منذ %d سنة" + }, + "repo.settings.push_mirror.branch_filter.label": "تصفية الفرع (اختياري)", + "repo.settings.push_mirror.branch_filter.description": "الفروع المطلوب عكسها. اترك الحقل فارغًا لعكس جميع الفروع. راجع توثيق %[2]s للاطلاع على الصيغة. أمثلة: main, release/*", + "og.repo.summary_card.alt_description": "بطاقة تلخيصية للمستودع %[1]، موصوفة بـ %[1]: %[2]s", + "meta.last_line": "شكرًا لك على ترجمة Forgejo! هذا السطر لا يراه المستخدمون ولكنه يخدم أغراضًا أخرى في إدارة الترجمة. يمكنك وضع حقيقة ممتعة في الترجمة بدلاً من ترجمتها.", + "relativetime.future": "في المستقبل", + "avatar.constraints_hint": "لا يمكن أن يتجاوز حجم الصورة الشخصية المخصصة %[1]s، ولا أبعادها عن %[2]d×%[3]d بكسل", + "repo.pulls.merged_title_desc": { + "zero": "لم دمج أي إيداع من %[2]s إلى %[3]s %[4]s", + "one": "تم دمج إيداع واحد من %[2]s إلى %[3]s %[4]s", + "two": "تم دمج إيداعين اثنين من %[2]s إلى %[3]s %[4]s", + "few": "تم دمج %[1]d إيداعات من %[2]s إلى %[3]s %[4]s", + "many": "تم دمج %[1]d إيداعاً من %[2]s إلى %[3]s %[4]s", + "other": "تم دمج %[1]d إيداعاً من %[2]s إلى %[3]s %[4]s" + }, + "relativetime.months": { + "zero": "هذا الشهر", + "one": "منذ شهر", + "two": "منذ شهرين", + "few": "منذ %d أشهر", + "many": "منذ %d شهراً", + "other": "منذ %d شهراً" + }, + "repo.pulls.title_desc": { + "zero": "لا يريد دمج أي إيداع من %[2]s إلى %[3]s", + "one": "يريد دمج إيداع واحد من %[2]s إلى %[3]s", + "two": "يريد دمج إيداعين من %[2]s إلى %[3]s", + "few": "يريد دمج %[1]d إيداعات من %[2]s إلى %[3]s", + "many": "يريد دمج %[1]d إيداعًا من %[2]s إلى %[3]s", + "other": "يريد دمج %[1]d إيداعًا من %[2]s إلى %[3]s" + }, + "mail.actions.run_info_sha": "إيداع: %[1]s", "keys.gpg.link": "مفاتيح GPG", - "admin.config.moderation_config": "تهيئة الإشراف", - "moderation.report_abuse": "الإبلاغ عن إساءة الاستخدام", - "moderation.report_content": "محتوى التقارير", - "moderation.report_abuse_form.header": "الإبلاغ عن الإساءة إلى المسؤول", - "moderation.report_abuse_form.details": "يتعين استخدام هذا النموذج للإبلاغ عن المستخدمين الذين ينشئون ملفات تعريف ، أو مستودعات ، أو إبلاغات ، أو تعليقات ، أو يتصرفون بشكل غير لائق.", - "moderation.report_abuse_form.invalid": "معاملا غير صالحة", - "moderation.report_abuse_form.already_reported": "‍لقد قمت بالفعل بالإبلاغ عن هذا المحتوى", + "moderation.abuse_category.spam": "غير مرغوب به", + "keys.ssh.link": "مفاتيح SSH", + "profile.edit.link": "عدِّل ملف التعريف", "moderation.abuse_category": "الفئة", "moderation.abuse_category.placeholder": "حدد الفئة", - "moderation.abuse_category.spam": "غير مرغوب به", - "moderation.abuse_category.other_violations": "انتهاكات أخرى لقواعد المنصة", - "moderation.report_remarks": "الملاحظات", "moderation.report_remarks.placeholder": "يُرجى تقديم بعض التفاصيل المتعلقة بالإساءة التي أبلغت عنها.", "moderation.submit_report": "إرسال التقرير", "moderation.reporting_failed": "تعذر إرسال تقرير إساءة الاستخدام الجديد: %v", "moderation.reported_thank_you": "شكراُ لك على تقريرك. وقد تم إبلاغ الإدارة به.", - "mail.actions.successful_run_after_failure_subject": "تم استعادة سير العمل %[1]s في المستودع %[2]s", + "mail.actions.run_info_trigger": "تم تشغيله بسبب: %[1]s عبر: %[2]s", + "install.invalid_lfs_path": "غير قادر على إنشاء جذر LFS في المسار المحدد: %[1]s", + "alert.range_error": " يجب أن يكون رقمًا بين %[1]s و %[2]s.", "mail.actions.not_successful_run_subject": "سير العمل %[1]s فشل في المستودت %[2]s", "mail.actions.successful_run_after_failure": "تم استعادة سير العمل %[1]s في المستودع %[2]s", "mail.actions.not_successful_run": "فشل سير العمل %[1]s في المستودع %[2]s", "mail.actions.run_info_cur_status": "حالة هذا التشغيل: %[1]s (تم تحديثها من %[2]s للتو)", "mail.actions.run_info_previous_status": "حالة التشغيل السابقة: %[1]s", - "mail.actions.run_info_sha": "إيداع: %[1]s", - "mail.actions.run_info_trigger": "تم تشغيله بسبب: %[1]s عبر: %[2]s", - "repo.diff.commit.next-short": "التالي", - "repo.diff.commit.previous-short": "السابق", "discussion.locked": "تم إغلاق هذه المناقشة. يقتصر التعليق على المساهمين فقط.", "editor.textarea.tab_hint": "السطر مُزاح بالفعل. اضغط Tab مرة أخرى أو Escape لمغادرة المحرر.", "editor.textarea.shift_tab_hint": "لا توجد مسافة بادئة في هذا السطر. اضغط Shift + Tab مرة أخرى أو Escape لمغادرة المحرر.", - "admin.dashboard.cleanup_offline_runners": "تنظيف وحدات التشغيل غير المتصلة", - "settings.visibility.description": "رؤية ملفك الشخصي تؤثر في قدرة الآخرين على الوصول إلى مستودعاتك غير الخاصة. اعرف المزيد", - "avatar.constraints_hint": "لا يمكن أن يتجاوز حجم الصورة الشخصية المخصصة %[1]s، ولا أبعادها عن %[2]d×%[3]d بكسل", - "meta.last_line": "شكرًا لك على ترجمة Forgejo! هذا السطر لا يراه المستخدمون ولكنه يخدم أغراضًا أخرى في إدارة الترجمة. يمكنك وضع حقيقة ممتعة في الترجمة بدلاً من ترجمتها." + "profile.actions.tooltip": "إجراءات إضافية", + "moderation.report_content": "محتوى التقارير", + "moderation.report_abuse_form.header": "الإبلاغ عن الإساءة إلى المسؤول", + "moderation.report_abuse_form.details": "يتعين استخدام هذا النموذج للإبلاغ عن المستخدمين الذين ينشئون ملفات تعريف ، أو مستودعات ، أو إبلاغات ، أو تعليقات ، أو يتصرفون بشكل غير لائق.", + "moderation.report_abuse_form.invalid": "معاملا غير صالحة", + "moderation.report_abuse_form.already_reported": "‍لقد قمت بالفعل بالإبلاغ عن هذا المحتوى", + "moderation.report_abuse": "الإبلاغ عن إساءة الاستخدام", + "feed.atom.link": "موجز Atom", + "admin.config.moderation_config": "تهيئة الإشراف", + "mail.actions.successful_run_after_failure_subject": "تم استعادة سير العمل %[1]s في المستودع %[2]s", + "discussion.sidebar.reference": "مرجع", + "admin.moderation.moderation_reports": "تقارير الإشراف", + "admin.moderation.reports": "التقارير", + "admin.moderation.no_open_reports": "لا يوجد حالياً أية تقارير مفتوحة.", + "admin.moderation.deleted_content_ref": "المحتوى المُبلغ عنه بالنوع %[1]v والمعرف %[2]d لم يعد موجوداً" } diff --git a/options/locale_next/locale_bg.json b/options/locale_next/locale_bg.json index 1faf269aba..be8477a98a 100644 --- a/options/locale_next/locale_bg.json +++ b/options/locale_next/locale_bg.json @@ -7,7 +7,6 @@ "one": "иска да слее %[1]d подаване от %[2]s в %[3]s", "other": "иска да слее %[1]d подавания от %[2]s в %[3]s" }, - "mail.actions.run_info_ref": "Клон: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Задействано поради: %[1]s от: %[2]s", "meta.last_line": "В България расте най-старото дърво в страната, Байкушевата мура, на възраст над 1300 години.", "relativetime.1day": "вчера", diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 7fb22a0d33..73371a6fc2 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -28,16 +28,15 @@ "mail.actions.not_successful_run": "Workflow %[1]s selhal v repozitáři %[2]s", "mail.actions.run_info_cur_status": "Stav tohoto procesu: %[1]s (právě aktualizováno z %[2]s)", "mail.actions.run_info_previous_status": "Stav předchozího procesu: %[1]s", - "mail.actions.run_info_ref": "Větev: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Spuštěno z důvodu: %[1]s od: %[2]s", "mail.actions.successful_run_after_failure_subject": "Workflow %[1]s obnoven v repozitáři %[2]s", "mail.actions.successful_run_after_failure": "Workflow %[1]s obnoven v repozitáři %[2]s", "discussion.locked": "Tato diskuze byla uzamčena. Komentování je omezené na přispěvatele.", "relativetime.future": "v budoucnu", "relativetime.years": { - "one": "Před %d rokem", - "few": "Před %d lety", - "other": "Před %d lety" + "one": "před %d rokem", + "few": "před %d lety", + "other": "před %d lety" }, "relativetime.1day": "včera", "relativetime.2days": "před dvěma dny", @@ -48,30 +47,30 @@ "relativetime.1year": "minulý rok", "relativetime.2years": "před dvěma lety", "relativetime.weeks": { - "one": "Před %d týdnem", - "few": "Před %d týdny", - "other": "Před %d týdny" + "one": "před %d týdnem", + "few": "před %d týdny", + "other": "před %d týdny" }, "relativetime.days": { - "one": "Před %d dnem", - "few": "Před %d dny", - "other": "Před %d dny" + "one": "před %d dnem", + "few": "před %d dny", + "other": "před %d dny" }, "relativetime.mins": { - "one": "Před %d minutou", - "few": "Před %d minutami", - "other": "Před %d minutami" + "one": "před %d minutou", + "few": "před %d minutami", + "other": "před %d minutami" }, "relativetime.hours": { - "one": "Před %d hodinou", - "few": "Před %d hodinami", - "other": "Před %d hodinami" + "one": "před %d hodinou", + "few": "před %d hodinami", + "other": "před %d hodinami" }, "relativetime.now": "nyní", "relativetime.months": { - "one": "Před %d měsícem", - "few": "Před %d měsíci", - "other": "Před %d měsíci" + "one": "před %d měsícem", + "few": "před %d měsíci", + "other": "před %d měsíci" }, "moderation.report_content": "Nahlásit obsah", "moderation.report_abuse_form.details": "Tento formulář je určen k nahlašování uživatelů, kteří si vytvářejí spamové profily, repozitáře, problémy, komentáře nebo se chovají nevhodně.", @@ -102,14 +101,36 @@ "editor.textarea.tab_hint": "Řádek je již odsazen. Pro opuštění editoru stiskněte znovu Tab nebo Escape.", "editor.textarea.shift_tab_hint": "Na tomto řádku není žádné odsazení. Pro opuštění editoru stiskněte znovu Shift + Tab nebo Escape.", "admin.dashboard.cleanup_offline_runners": "Vymazat offline runnery", - "settings.visibility.description": "Viditelnost profilu ovlivňuje možnost ostatních přistupovat k vašim veřejným repozitářům. Zjistit více", + "settings.visibility.description": "Viditelnost profilu ovlivňuje možnost ostatních přistupovat k vašim veřejným repozitářům. Zjistit více.", "avatar.constraints_hint": "Velikost vlastního avataru nesmí překročit %[1]s nebo být větší než %[2]dx%[3]d pixelů", "repo.diff.commit.next-short": "Další", "repo.diff.commit.previous-short": "Předchozí", "profile.actions.tooltip": "Další akce", + "keys.gpg.link": "Klíče GPG", "profile.edit.link": "Upravit profil", "feed.atom.link": "Zdroj Atom", "keys.ssh.link": "Klíče SSH", - "keys.gpg.link": "Klíče GPG", - "mail.actions.run_info_sha": "Revize: %[1]s" + "og.repo.summary_card.alt_description": "Karta se souhrnem repozitáře %[1]s, popsaným jako: %[2]s", + "mail.actions.run_info_sha": "Revize: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Filtr větve (nepovinný)", + "repo.settings.push_mirror.branch_filter.description": "Větve, které mají být zrcadleny. Ponechte prázdné pro zrcadlení všech větví. Syntaxi naleznete v dokumentaci %[2]s. Příklady: main, release/*", + "discussion.sidebar.reference": "Reference", + "admin.moderation.moderation_reports": "Hlášení moderace", + "admin.moderation.reports": "Hlášení", + "admin.moderation.deleted_content_ref": "Nahlášený obsah s typem %[1]v a ID %[2]d již neexistuje", + "admin.moderation.no_open_reports": "Momentálně nejsou otevřena žádná hlášení.", + "admin.dashboard.remove_resolved_reports": "Odstranit vyřešená hlášení", + "compare.branches.title": "Porovnat větve", + "repo.commit.load_tags_failed": "Načtení značek selhalo z důvodu interní chyby", + "admin.auths.allow_username_change": "Povolit změnu uživatelského jména", + "admin.auths.allow_username_change.description": "Povolit uživatelům změnit své uživatelské jméno v nastavení profilu", + "warning.repository.out_of_sync": "Databázová reprezentace tohoto repozitáře není synchronizovaná. Pokud se toto varování zobrazuje i po odeslání revize do repozitáře, kontaktujte administrátora.", + "repo.pulls.already_merged": "Sloučení selhalo: tato žádost již byla sloučena.", + "migrate.pagure.description": "Migrovat data z pagure.io nebo jiných instancí Pagure.", + "migrate.pagure.incorrect_url": "Byla zadána nesprávná adresa URL zdrojového repozitáře", + "migrate.pagure.project_url": "Adresa projektu Pagure", + "migrate.pagure.project_example": "Adresa URL projektu Pagure, např. https://pagure.io/pagure", + "migrate.pagure.token_label": "Token", + "migrate.pagure.token_body_a": "Zadejte token API Pagure s přístupem k soukromým problémům pro vytvoření repozitáře obsahujícího pouze soukromé problémy", + "migrate.pagure.token_body_b": "Nezapomeňte nastavit repozitář jako soukromý výše, pokud chcete, aby byl repozitář soukromý" } diff --git a/options/locale_next/locale_da.json b/options/locale_next/locale_da.json index c43a7e3d37..2620127b8c 100644 --- a/options/locale_next/locale_da.json +++ b/options/locale_next/locale_da.json @@ -28,7 +28,6 @@ "mail.actions.not_successful_run": "Arbejdsgangen %[1]s mislykkedes i depotet %[2]s", "mail.actions.run_info_cur_status": "Status for denne kørsel: %[1]s (lige opdateret fra %[2]s)", "mail.actions.run_info_previous_status": "Status for forrige kørsel: %[1]s", - "mail.actions.run_info_ref": "Gren: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Udløst fordi: %[1]s af: %[2]s", "discussion.locked": "Denne diskussion er blevet låst. Kommentarer er begrænset til bidragydere.", "relativetime.now": "nu", diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index c64e25b242..1b4041074d 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -27,7 +27,6 @@ "mail.actions.successful_run_after_failure": "Arbeitsablauf %[1]s in Repository %[2]s wiederhergestellt", "mail.actions.not_successful_run": "Arbeitsablauf %[1]s in Repository %[2]s fehlgeschlagen", "mail.actions.run_info_previous_status": "Vorheriger Status des Runs: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Ausgelöst, weil: %[1]s durch: %[2]s", "mail.actions.run_info_cur_status": "Status dieses Runs: %[1]s (gerade eben von %[2]s aktualisiert)", "discussion.locked": "Diese Diskussion wurde gesperrt. Nur Mitwirkende können kommentieren.", @@ -94,7 +93,7 @@ "editor.textarea.tab_hint": "Zeile bereits eingerückt. Drücke nochmals Tab oder Escape, um den Editor zu verlassen.", "editor.textarea.shift_tab_hint": "Keine Einrückung auf dieser Zeile. Drücke nochmals Shift + Tab oder Escape, um den Editor zu verlassen.", "admin.dashboard.cleanup_offline_runners": "Aufräumen der Offline-Runner", - "settings.visibility.description": "Die Profilsichtbarkeit beeinflusst die Möglichkeit anderer, auf deine nicht-privaten Repositorys zuzugreifen. Erfahre mehr", + "settings.visibility.description": "Die Profilsichtbarkeit beeinflusst die Möglichkeit anderer, auf deine nicht-privaten Repositorys zuzugreifen. Erfahre mehr.", "avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]d×%[3]d Pixel sein", "repo.diff.commit.next-short": "Nächste", "repo.diff.commit.previous-short": "Vorherige", @@ -103,5 +102,27 @@ "keys.ssh.link": "SSH-Schlüssel", "keys.gpg.link": "GPG-Schlüssel", "profile.actions.tooltip": "Mehr Aktionen", - "mail.actions.run_info_sha": "Commit: %[1]s" + "og.repo.summary_card.alt_description": "Zusammenfassungskarte des Repositorys %[1]s, beschrieben als %[2]s", + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Branch-Filter (optional)", + "repo.settings.push_mirror.branch_filter.description": "Zu spiegelnde Branches. Leer lassen, um alle Branches zu spiegeln. Siehe die „%[2]s“-Dokumentation für die Syntax. Beispiele: main, release/*", + "discussion.sidebar.reference": "Referenz", + "admin.moderation.deleted_content_ref": "Gemeldeter Inhalt vom Typ %[1]v und ID %[2]d existiert nicht mehr", + "admin.moderation.moderation_reports": "Moderationsmeldungen", + "admin.moderation.reports": "Meldungen", + "admin.moderation.no_open_reports": "Es gibt momentan keine offenen Meldungen.", + "admin.dashboard.remove_resolved_reports": "Erledigte Meldungen entfernen", + "compare.branches.title": "Branches vergleichen", + "repo.commit.load_tags_failed": "Laden von Tags aufgrund eines internen Fehlers fehlgeschlagen", + "admin.auths.allow_username_change": "Benutzernamen ändern zulassen", + "admin.auths.allow_username_change.description": "Benutzern erlauben, ihren Benutzernamen in den Profileinstellungen zu ändern", + "warning.repository.out_of_sync": "Die Datenbankdarstellung dieses Repositorys ist nicht synchronisiert. Falls diese Warnung immer noch angezeigt wird, nachdem ein Commit zu diesem Repository gepusht wurde, kontaktieren Sie den Administrator.", + "migrate.pagure.token_body_b": "Stellen Sie sicher, dass das Flag für private Repos oben gesetzt ist, wenn Sie möchten, dass dieses Repo privat ist", + "repo.pulls.already_merged": "Zusammenführung fehlgeschlagen. Der Pull-Request wurde bereits zusammengeführt.", + "migrate.pagure.incorrect_url": "Falsche Quellrepository-URL wurde angegeben", + "migrate.pagure.token_body_a": "Stellen Sie ein Pagure-API-Token mit Zugriff auf die privaten Issues zur Verfügung, um ein Repository mit nur den privaten Issues darin zu erstellen", + "migrate.pagure.project_example": "Die URL des Pagure-Projekts, z. B. https://pagure.io/pagure", + "migrate.pagure.description": "Daten von pagure.io oder anderen Pagure-Instanzen migrieren.", + "migrate.pagure.token_label": "Token", + "migrate.pagure.project_url": "Pagure-Projekt-URL" } diff --git a/options/locale_next/locale_el-GR.json b/options/locale_next/locale_el-GR.json index d3ba25b170..cca89efd0e 100644 --- a/options/locale_next/locale_el-GR.json +++ b/options/locale_next/locale_el-GR.json @@ -12,7 +12,6 @@ "mail.actions.successful_run_after_failure_subject": "Η ροή εργασίας %[1]s αποκαταστάθηκε στο αποθετήριο %[2]s", "mail.actions.not_successful_run_subject": "Η ροή εργασίας %[1]s απέτυχε στο αποθετήριο %[2]s", "mail.actions.run_info_trigger": "Ενεργοποιήθηκε επειδή: %[1]s από: %[2]s", - "mail.actions.run_info_ref": "Αποθετήριο: %[1]s (%[2]s)", "meta.last_line": "Ευχαριστούμε που μεταφράζετε το Forgejo! Αυτή η γραμμή δεν είναι ορατή από τους χρήστες αλλά εξυπηρετεί άλλους σκοπούς στη διαχείριση της μετάφρασης. Μπορείτε να γράψετε κάποιο αστείο αντί για μετάφραση.", "mail.actions.successful_run_after_failure": "Η ροή εργασίας %[1]s αποκαταστάθηκε στο αποθετήριο %[2]s", "discussion.locked": "Αυτή η συζήτηση έχει κλειδωθεί. Ο σχολιασμός περιορίζεται στους συνεισφέροντες.", diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 4f1c3904e2..24c0835256 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -44,6 +44,7 @@ "relativetime.2months": "two months ago", "relativetime.1year": "last year", "relativetime.2years": "two years ago", + "repo.pulls.already_merged": "Merge failed: This pull request has already been merged.", "repo.pulls.merged_title_desc": { "one": "merged %[1]d commit from %[2]s into %[3]s %[4]s", "other": "merged %[1]d commits from %[2]s into %[3]s %[4]s" @@ -55,11 +56,14 @@ "repo.form.cannot_create": "All spaces in which you can create repositories have reached the limit of repositories.", "repo.issue_indexer.title": "Issue Indexer", "search.milestone_kind": "Search milestones…", + "repo.settings.push_mirror.branch_filter.label": "Branch filter (optional)", + "repo.settings.push_mirror.branch_filter.description": "Branches to be mirrored. Leave blank to mirror all branches. See %[2]s documentation for syntax. Examples: main, release/*", "incorrect_root_url": "This Forgejo instance is configured to be served on \"%s\". You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini.", "themes.names.forgejo-auto": "Forgejo (follow system theme)", "themes.names.forgejo-light": "Forgejo light", "themes.names.forgejo-dark": "Forgejo dark", "error.not_found.title": "Page not found", + "warning.repository.out_of_sync": "The database representation of this repository is out of synchronization. If this warning is still shown after pushing a commit to this repository contact the administrator.", "alert.asset_load_failed": "Failed to load asset files from {path}. Please make sure the asset files can be accessed.", "alert.range_error": " must be a number between %[1]s and %[2]s.", "install.invalid_lfs_path": "Unable to create the LFS root at the specified path: %[1]s", @@ -69,6 +73,10 @@ "keys.ssh.link": "SSH keys", "keys.gpg.link": "GPG keys", "admin.config.moderation_config": "Moderation configuration", + "admin.moderation.moderation_reports": "Moderation reports", + "admin.moderation.reports": "Reports", + "admin.moderation.no_open_reports": "There are currently no open reports.", + "admin.moderation.deleted_content_ref": "Reported content with type %[1]v and id %[2]d no longer exists", "moderation.report_abuse": "Report abuse", "moderation.report_content": "Report content", "moderation.report_abuse_form.header": "Report abuse to administrator", @@ -97,10 +105,34 @@ "repo.diff.commit.next-short": "Next", "repo.diff.commit.previous-short": "Prev", "discussion.locked": "This discussion has been locked. Commenting is limited to contributors.", + "discussion.sidebar.reference": "Reference", "editor.textarea.tab_hint": "Line already indented. Press Tab again or Escape to leave the editor.", "editor.textarea.shift_tab_hint": "No indentation on this line. Press Shift + Tab again or Escape to leave the editor.", + "admin.auths.allow_username_change": "Allow username change", + "admin.auths.allow_username_change.description": "Allow users to change their username in the profile settings", "admin.dashboard.cleanup_offline_runners": "Cleanup offline runners", - "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more", + "admin.dashboard.remove_resolved_reports": "Remove resolved reports", + "admin.config.security": "Security configuration", + "admin.config.global_2fa_requirement.title": "Global two-factor requirement", + "admin.config.global_2fa_requirement.none": "No", + "admin.config.global_2fa_requirement.all": "All users", + "admin.config.global_2fa_requirement.admin": "Administrators", + "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more.", + "settings.twofa_unroll_unavailable": "Two-factor authentication is required for your account and cannot be disabled.", + "settings.twofa_reenroll": "Re-enroll two-factor authentication", + "settings.twofa_reenroll.description": "Re-enroll your two-factor authentication", + "settings.must_enable_2fa": "This Forgejo instance requires users to enable two-factor authentication before they can access their accounts.", + "error.must_enable_2fa": "This Forgejo instance requires users to enable two-factor authentication before they can access their accounts. Enable it at: %s", "avatar.constraints_hint": "Custom avatar may not exceed %[1]s in size or be larger than %[2]dx%[3]d pixels", + "og.repo.summary_card.alt_description": "Summary card of repository %[1]s, described as: %[2]s", + "repo.commit.load_tags_failed": "Load tags failed because of internal error", + "compare.branches.title": "Compare branches", + "migrate.pagure.description": "Migrate data from pagure.io or other Pagure instances.", + "migrate.pagure.incorrect_url": "Incorrect source repository URL has been provided", + "migrate.pagure.project_url": "Pagure project URL", + "migrate.pagure.project_example": "The Pagure project url, e.g. https://pagure.io/pagure", + "migrate.pagure.token_label": "Token", + "migrate.pagure.token_body_a": "Provide a Pagure API token with access to the private issues to create a repository with just the private issues in it", + "migrate.pagure.token_body_b": "Be sure to set the private repo flag above if you want this repo to be private", "meta.last_line": "Thank you for translating Forgejo! This line isn't seen by the users but it serves other purposes in the translation management. You can place a fun fact in the translation instead of translating it." } diff --git a/options/locale_next/locale_et.json b/options/locale_next/locale_et.json index a10447fa98..768d6e2eea 100644 --- a/options/locale_next/locale_et.json +++ b/options/locale_next/locale_et.json @@ -1,3 +1,58 @@ { - "search.milestone_kind": "Otsi verstapostid..." + "search.milestone_kind": "Otsi verstaposte…", + "relativetime.2years": "kaks aastat tagasi", + "error.not_found.title": "Lehte ei leidu", + "themes.names.forgejo-light": "Forgejo hele kujundus", + "themes.names.forgejo-dark": "Forgejo tume kujundus", + "themes.names.forgejo-auto": "Forgejo (süsteemi kujundus)", + "relativetime.2days": "kaks päeva tagasi", + "relativetime.1week": "eelmisel nädalal", + "relativetime.2weeks": "kaks nädalat tagasi", + "relativetime.1month": "eelmisel kuul", + "relativetime.2months": "kaks kuud tagasi", + "relativetime.months": { + "one": "%d kuu tagasi", + "other": "%d kuud tagasi" + }, + "relativetime.weeks": { + "one": "% nädal tagasi", + "other": "% nädalat tagasi" + }, + "relativetime.years": { + "one": "%d aasta tagasi", + "other": "%d aastat tagasi" + }, + "relativetime.mins": { + "one": "%d minut tagasi", + "other": "%d minutit tagasi" + }, + "relativetime.hours": { + "one": "%d tund tagasi", + "other": "%d tundi tagasi" + }, + "relativetime.days": { + "one": "% päev tagasi", + "other": "% päeva tagasi" + }, + "relativetime.1day": "eile", + "relativetime.1year": "eelmisel aastal", + "relativetime.now": "praegu", + "relativetime.future": "tulevikus", + "stars.list.none": "Keegi pole seda koodihoidlat veel tähekeega märgistanud.", + "watch.list.none": "Keegi pole seda koodihoidlat veel jälgima asunud.", + "followers.incoming.list.self.none": "Mitte keegi ei jälgi sinu kasutajaprofiili.", + "followers.incoming.list.none": "Mitte keegi ei jälgi seda kasutajat.", + "followers.outgoing.list.self.none": "Sina ei jälgi mitte kedagi.", + "followers.outgoing.list.none": "%s ei jälgi mitte kedagi.", + "alert.range_error": " peab olema number %[1]s ja %[2]s vahel.", + "home.welcome.no_activity": "Hetkel on siin tühjus", + "home.explore_repos": "Uuri lähtekoodi hoidlaid", + "home.explore_users": "Otsi kasutajaid", + "home.explore_orgs": "Tutvu organisatsioonidega", + "meta.last_line": "Tänud, et oled Forgejo'd tõlkinud! Work hard and put in the effort, and love will come. (Tee tööd ja näe vaeva, siis tuleb armastus - A. H. Tammsaare)", + "keys.ssh.link": "SSH võtmed", + "keys.gpg.link": "GPG võtmed", + "profile.edit.link": "Muuda profiili", + "feed.atom.link": "Atom-uudisvoog", + "home.welcome.activity_hint": "Sinu uudisvoos ei leidu veel mitte midagi. Kui toimetad midagi sinu jälgitavates lähtekoodihoidlates, siis sinu tegevused ja aktiivsus on siin näha." } diff --git a/options/locale_next/locale_fi-FI.json b/options/locale_next/locale_fi-FI.json index 14481acf6f..fdc740b978 100644 --- a/options/locale_next/locale_fi-FI.json +++ b/options/locale_next/locale_fi-FI.json @@ -63,7 +63,6 @@ "mail.actions.successful_run_after_failure": "Työnkulku %[1]s palautettu tietovarastoon %[2]s", "mail.actions.run_info_cur_status": "Tämän juoksun tila: %[1]s (juuri päivitetty %[2]s:sta)", "mail.actions.run_info_previous_status": "Edellisen ajon tila: %[1]s", - "mail.actions.run_info_ref": "Haara: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Laukaistui, koska: %[1]s, tekijänä: %[2]s", "moderation.abuse_category.malware": "Haittaohjelma" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 20b35b8edb..a36c0b48f0 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -25,7 +25,6 @@ "mail.actions.successful_run_after_failure": "Na-recover ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.not_successful_run": "Nabigo ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.run_info_previous_status": "Nakaraang Status ng Run: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Na-trigger dahil: %[1]s ni/ng: %[2]s", "mail.actions.successful_run_after_failure_subject": "Na-recover ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.not_successful_run_subject": "Nabigo ang workflow na %[1]s sa repositoryong %[2]s", @@ -33,19 +32,19 @@ "relativetime.now": "ngayon", "relativetime.mins": { "one": "%d minuto ang nakalipas", - "other": "%d minuto ang nakalipas" + "other": "%d (na) minuto ang nakalipas" }, "relativetime.days": { "one": "%d araw ang nakalipas", - "other": "%d araw ang nakalipas" + "other": "%d (na) araw ang nakalipas" }, "relativetime.weeks": { "one": "%d linggo ang nakalipas", - "other": "%d linggo ang nakalipas" + "other": "%d (na) linggo ang nakalipas" }, "relativetime.years": { "one": "%d taon ang nakalipas", - "other": "%d taon ang nakalipas" + "other": "%d (na) taon ang nakalipas" }, "relativetime.2days": "2 araw ang nakalipas", "relativetime.2weeks": "2 linggo ang nakalipas", @@ -55,12 +54,12 @@ "relativetime.2years": "2 taon ang nakalipas", "relativetime.1day": "kahapon", "relativetime.hours": { - "one": "%d oras ang nakalipas", - "other": "%d oras ang nakalipas" + "one": "%d kras ang nakalipas", + "other": "%d (na) oras ang nakalipas" }, "relativetime.months": { "one": "%d buwan ang nakalipas", - "other": "%d buwan ang nakalipas" + "other": "%d (na) buwan ang nakalipas" }, "discussion.locked": "Naka-kandado ang pag-uusap na ito. Nilimitahan ang pagkomento sa mga tagatulong.", "relativetime.1month": "nakaraang buwan", @@ -94,7 +93,7 @@ "editor.textarea.tab_hint": "Naka-indent na ang linya. Pindutin ulit ang Tab o Escape para umalis sa editor.", "editor.textarea.shift_tab_hint": "Walang indentation sa linyang ito. Pindutin ang Shift + Tab ulit o Escape para umalis sa editor.", "admin.dashboard.cleanup_offline_runners": "Linisin ang mga offline na runner", - "settings.visibility.description": "Maaapektuhan ng visibility ng profile ang kakayahan ng iba na i-access ang iyong mga hindi pribadong repositoryo. Matuto pa", + "settings.visibility.description": "Maaapektuhan ng visibility ng profile ang kakayahan ng iba na i-access ang iyong mga hindi pribadong repositoryo. Matuto pa.", "avatar.constraints_hint": "Hindi maaaring lumagpas sa laking %[1]s o mas malaki sa %[2]dx%[3]d pixel ang custom na avatar", "repo.diff.commit.next-short": "Susunod", "repo.diff.commit.previous-short": "Nakaraan", @@ -103,5 +102,27 @@ "keys.ssh.link": "Mga SSH key", "keys.gpg.link": "Mga GPG key", "profile.actions.tooltip": "Higit pang mga aksyon", - "mail.actions.run_info_sha": "Commit: %[1]s" + "og.repo.summary_card.alt_description": "Card ng pangkalahatang ideya ng repositoryong %[1]s, inilalarawan bilang: %[2]s", + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Filter ng branch (opsyonal)", + "repo.settings.push_mirror.branch_filter.description": "Mga branch na imi-mirror. Iwanang walang laman para i-mirror ang lahat ng mga branch. Tignan ang dokumentasyon ng %[2]s para sa syntax. Halimbawa: main, your-reality, release/*", + "discussion.sidebar.reference": "Sangguni", + "admin.moderation.moderation_reports": "Mga ulat sa moderation", + "admin.moderation.no_open_reports": "Kasalukuyang walang mga nakabukas na ulat.", + "admin.moderation.reports": "Mga ulat", + "admin.moderation.deleted_content_ref": "Hindi na umiiral ang inulat na nilalaman na may uri na %[1]v at ID %[2]d", + "admin.dashboard.remove_resolved_reports": "Tanggalin ang mga naresolbang ulat", + "compare.branches.title": "Ikumpara ang mga branch", + "repo.commit.load_tags_failed": "Nabigo ang pag-load ng mga tag dahil sa isang panloob na error", + "admin.auths.allow_username_change": "Payagan ang pagpalit ng username", + "admin.auths.allow_username_change.description": "Payagan ang mga user na palitan ang kanilang username sa mga setting ng profile", + "warning.repository.out_of_sync": "Wala sa pag-sync ang database na representasyon ng repositoryong ito. Kung pinapakita pa rin ang babala na ito pagkagapos magtulak ng commit sa repositoryong ito, makipagugnayan sa tagapangasiwa.", + "repo.pulls.already_merged": "Nabigo ang pagsasama: Naisama na ang hiling sa paghila na ito.", + "migrate.pagure.incorrect_url": "Maling pinagmulang URL ng repositoryo ang ibinigay", + "migrate.pagure.project_url": "URL ng Pagure na proyekto", + "migrate.pagure.token_label": "Token", + "migrate.pagure.token_body_a": "Magbigay ng Pagure API token na may access sa mga pribadong isyu para gumawa ng repositoryo na may mga pribadong isyu lang", + "migrate.pagure.project_example": "Ang URL ng Pagure na proyekto, hal. https://pagure.io/pagure", + "migrate.pagure.token_body_b": "Siguraduhin na itakda ang pribadong repositoryo na flag sa itaas kung gusto mo na pribado ang repositoryong ito", + "migrate.pagure.description": "Mag-migrate ng data mula sa pagure.io o sa ibang mga Pagure na instansya." } diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index da26d56107..9c2af655a0 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -10,7 +10,6 @@ "other": "" }, "search.milestone_kind": "Recherche dans les jalons…", - "mail.actions.run_info_ref": "Branche : %[1]s (%[2]s)", "discussion.locked": "Cette discussion a été bloqué. Les commentaires sont limités aux contributeurs.", "relativetime.now": "maintenant", "relativetime.future": "dans le future", @@ -100,8 +99,20 @@ "repo.form.cannot_create": "Tous les espaces dans lesquels vous pouvez créer des dépôts ont atteint la limite de dépôts.", "admin.dashboard.cleanup_offline_runners": "Nettoyer les exécuteurs hors ligne", "mail.actions.run_info_trigger": "Déclenché parce que : %[1]s par : %[2]s", - "settings.visibility.description": "La visibilité du profil affecte la capacité des autres à accéder à vos dépôts non-privés. Voir plus", + "settings.visibility.description": "La visibilité du profil affecte la capacité des autres à accéder à vos dépôts non-privés. Voir plus.", "editor.textarea.shift_tab_hint": "Pas d'indentation sur cette ligne. Appuyez sur Maj + Tab une nouvelle fois ou sur Échap pour quitter l'éditeur.", "avatar.constraints_hint": "L'avatar personnalisé ne doit pas dépasser une taille de %[1]s ou être plus grand que %[2]dx%[3]d pixels", - "editor.textarea.tab_hint": "Ligne déjà indentée. Appuyez sur Tab une nouvelle fois ou sur Échap pour quitter l'éditeur." + "editor.textarea.tab_hint": "Ligne déjà indentée. Appuyez sur Tab une nouvelle fois ou sur Échap pour quitter l'éditeur.", + "discussion.sidebar.reference": "Référence", + "repo.settings.push_mirror.branch_filter.label": "Filtre de branche (optionnel)", + "repo.diff.commit.next-short": "Suiv.", + "repo.diff.commit.previous-short": "Préc.", + "profile.actions.tooltip": "Plus d'actions", + "profile.edit.link": "Éditer le profil", + "keys.ssh.link": "Clé SSH", + "keys.gpg.link": "Clés GPG", + "compare.branches.title": "Comparer les branches", + "repo.settings.push_mirror.branch_filter.description": "Branches a répliquer. Laisser vide pour répliquer toutes les branches. Voir la documentation %[2] pour la syntaxe. Exemples: main, release/*", + "mail.actions.run_info_sha": "Commit: %[1]s", + "admin.moderation.deleted_content_ref": "Le signalement avec le type %[1]v et l'identifiant %[2]d n'existe plus." } diff --git a/options/locale_next/locale_hi.json b/options/locale_next/locale_hi.json index 0967ef424b..5eec8ae1e2 100644 --- a/options/locale_next/locale_hi.json +++ b/options/locale_next/locale_hi.json @@ -1 +1,81 @@ -{} +{ + "relativetime.2days": "दो दिन पहले", + "relativetime.1day": "कल", + "repo.form.cannot_create": "वो जगह जहाँ पे रिपॉजिटरी रखीं जातीं हैं वहां जगह ख़त्म", + "moderation.abuse_category.other_violations": "प्लेटफार्म का कोई रूल तोडा हैं", + "moderation.report_remarks": "टिप्पणी", + "repo.issue_indexer.title": "इशू ठौर", + "themes.names.forgejo-auto": "फॉरगेजो - सिस्टम थीम फॉलो करें", + "themes.names.forgejo-light": "फॉरगेजो लाइट", + "error.not_found.title": "पृष्ठ नहीं मिला", + "incorrect_root_url": "इस फॉरगेजो इंस्टैंस को %d सर्वर पे कॉन्फ़िगर किया गया है। आप फॉरगेजो को किसी अन्य url से देख रहे हैं, जिससे एप्लीकेशन टूटती है। अच्छा यूआरएल फॉरगेजो के एडमिन कण्ट्रोल करते हैं और रुट_यूआरएल जो की app. ini में है", + "themes.names.forgejo-dark": "फॉरगेजो डार्क", + "stars.list.none": "किसी ने भी चिन्हित/स्टार नहीं किया", + "watch.list.none": "कोई भी ये रिपॉजिटरी नहीं देख रहा", + "followers.incoming.list.self.none": "कोई भी प्रोफाइल फॉलो नहीं कर रहा", + "followers.incoming.list.none": "कोई भी यूजर को फॉलो नहीं कर रहा", + "followers.outgoing.list.self.none": "आप किसी को फॉलो ही नहीं कर रहे", + "followers.outgoing.list.none": "%s किसी को फॉलो नहीं कर रहे", + "relativetime.1week": "पिछले हफ्ते", + "relativetime.2weeks": "दो हफ्ते पहले", + "relativetime.1month": "पिछले महीने", + "relativetime.2months": "दो महीने पहले", + "relativetime.1year": "पिछले साल", + "relativetime.2years": "दो साल पहले", + "alert.asset_load_failed": "एसेट फाइल लोड नहीं हो पायी (path) ये पक्का करें की एसेट फाइल एक्सेस हो सकती है", + "alert.range_error": " एक अंक %d और %d के बीच में", + "search.milestone_kind": "माइलस्टोन ढूंढें…", + "home.welcome.no_activity": "गतिविधि नहीं", + "home.explore_repos": "रिपॉजिटरी निहारें", + "home.explore_users": "उसेर्स देखें", + "home.explore_orgs": "संस्थाएं देखें", + "relativetime.days": { + "one": "%d दिन पहले", + "other": "%d दिनों पहले" + }, + "repo.settings.push_mirror.branch_filter.label": "शाखा फ़िल्टर", + "repo.settings.push_mirror.branch_filter.description": "शाखा मिरर होनीं हैं। खली छोड़ने पर सब मिरर होंगी।", + "install.invalid_lfs_path": "LFS रुट नहीं बना पाया इस path पर", + "relativetime.years": { + "one": "%d साल पहले", + "other": "%d सालों पहले" + }, + "relativetime.weeks": { + "one": "%d हफ्ते पहले", + "other": "%d हफ़्तों पहले" + }, + "relativetime.months": { + "one": "%d महीने पहले", + "other": "%d महीनों पहले" + }, + "meta.last_line": "तीतर के दो पीछे तीतर तीतर के दो आगे तीतर बोलो कितने तीतर ?", + "relativetime.now": "अभी", + "avatar.constraints_hint": "कस्टम अवतार का फ़ाइल आकार 200 किलोबाइट से अधिक नहीं होना चाहिए और 125x125 पिक्सेल से बड़ा नहीं होना चाहिए", + "home.welcome.activity_hint": "आपकी फीड में अभी कुछ भी नहीं है। आपके कार्य और रिपॉजिटरी यहाँ दिखेंगे।", + "relativetime.future": "भविष्य में", + "moderation.report_abuse_form.details": "इस फॉर्म का प्रयोग वो करें जो बताना चाहते हैं स्पैम प्रोफाइल, रिपॉजिटरी, इशू, कमैंट्स या गलत पेशी", + "moderation.report_abuse_form.invalid": "इनवैलिड आर्गुमेंट", + "moderation.report_abuse_form.already_reported": "आप पहले शिकायत कर चुके हैं", + "moderation.abuse_category": "वर्ग", + "moderation.abuse_category.placeholder": "वर्ग चुनें", + "moderation.abuse_category.spam": "स्पैम", + "moderation.abuse_category.malware": "मैलवेयर", + "moderation.abuse_category.illegal_content": "गैर कानूनी कंटेंट", + "relativetime.mins": { + "one": "%d मिनट पहले", + "other": "" + }, + "profile.actions.tooltip": "और एक्शन्स", + "profile.edit.link": "प्रोफाइल बनाएं", + "feed.atom.link": "एटम फीड", + "keys.ssh.link": "SSH कीस", + "keys.gpg.link": "GPG कीस", + "admin.config.moderation_config": "सत्यापन कॉन्फ़िगरेशन", + "moderation.report_abuse": "कंप्लेंट करें", + "moderation.report_content": "कंप्लेंट करें", + "moderation.report_abuse_form.header": "कंप्लेंट करें एडमिनिस्ट्रेटर से", + "moderation.report_remarks.placeholder": "कुछ डिटेल्स बताओ जिस बारे में रिपोर्ट कर रहे हो", + "moderation.submit_report": "रिपोर्ट सबमिट करो", + "moderation.reporting_failed": "नयी रिपोर्ट सबमिट नहीं हो सकती", + "moderation.reported_thank_you": "रिपोर्ट के लिए शुक्रिया। एडमिन को बताया गया है" +} diff --git a/options/locale_next/locale_id-ID.json b/options/locale_next/locale_id-ID.json index 0cf7ea4799..14bf491789 100644 --- a/options/locale_next/locale_id-ID.json +++ b/options/locale_next/locale_id-ID.json @@ -1,9 +1,5 @@ { - "repo.pulls.merged_title_desc": { - "other": "commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "other": "ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s" - }, + "repo.pulls.merged_title_desc": "commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s", + "repo.pulls.title_desc": "ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s", "moderation.abuse_category.malware": "Perangkat pembahaya" } diff --git a/options/locale_next/locale_it-IT.json b/options/locale_next/locale_it-IT.json index 8464d6244e..f4cc7755f0 100644 --- a/options/locale_next/locale_it-IT.json +++ b/options/locale_next/locale_it-IT.json @@ -82,7 +82,6 @@ "moderation.report_abuse_form.invalid": "Argomenti non validi", "moderation.reporting_failed": "Impossibile inviare segnalazione: %v", "moderation.reported_thank_you": "Grazie per la segnalazione. L'amministratore è stato avvertito.", - "mail.actions.run_info_ref": "Ramo: %[1]s (%[2]s)", "alert.asset_load_failed": "Impossibile caricare i file di risorsa da {path}. Controlla che i file di risorsa siano accessibili.", "install.invalid_lfs_path": "Non è possibile creare una root LFS nel percorso specificato: %[1]s", "home.welcome.activity_hint": "Non c'è nulla nel tuo feed. Le tue azioni e le attività dei repositori che segui verranno mostrate qui.", diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 75835bc89c..3f8e3a165a 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -30,7 +30,6 @@ "mail.actions.not_successful_run": "Darbplūsmas %[1] atteice glabātavā %[2]s", "mail.actions.run_info_cur_status": "Šī izpildījuma stāvoklis: %[1]s (tikko atjaunināts no %[2]s)", "mail.actions.run_info_previous_status": "Iepriekšējā izpildījuma stāvoklis: %[1]s", - "mail.actions.run_info_ref": "Zars: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Izraisīšanas iemesls: %[2]s no: %[2]s", "discussion.locked": "Šī apspriede tika slēgta. Piebilžu pievienošana ir ļauta tikai līdzdalībniekiem.", "relativetime.future": "nākotnē", @@ -102,7 +101,7 @@ "editor.textarea.tab_hint": "Rinda jau ir ar atkāpi. Spied Tab vēlreiz vai Escape, lai izietu no redaktora!", "editor.textarea.shift_tab_hint": "Šajā rindā nav atkāpes. Spied Shift + Tab vēlreiz vai Escape, lai izietu no redaktora!", "admin.dashboard.cleanup_offline_runners": "Notīrīt bezsaistes izpildītājus", - "settings.visibility.description": "Profila redzamība ietekmē iespēju citiem piekļūt Tavām glabātavām, kas nav privātas. Uzzināt vairāk", + "settings.visibility.description": "Profila redzamība ietekmē iespēju citiem piekļūt Tavām glabātavām, kas nav privātas. Uzzināt vairāk.", "avatar.constraints_hint": "Pielāgots profila attēls nevar pārsniegt %[1]s vai būt lielāks par %[2]dx%[3]d pikseļiem", "repo.diff.commit.next-short": "Nāk.", "repo.diff.commit.previous-short": "Iepr.", @@ -111,5 +110,27 @@ "feed.atom.link": "Atom barotne", "keys.ssh.link": "SSH atslēgas", "keys.gpg.link": "GPG atslēgas", - "mail.actions.run_info_sha": "Iesūtījums: %[1]s" + "og.repo.summary_card.alt_description": "Glabātavas %[1]s kopsavilkuma kartīte, aprakstīta kā: %[2]s", + "mail.actions.run_info_sha": "Iesūtījums: %[1]s", + "repo.settings.push_mirror.branch_filter.description": "Zarus, kurus spoguļot. Atstāt tukšu, lai spoguļotu visus zarus. Pierakstu skatīt %[2]s dokumentācijā. Piemēri: main, release/*", + "repo.settings.push_mirror.branch_filter.label": "Zaru atlasītājs (izvēles)", + "discussion.sidebar.reference": "Atsauce", + "admin.moderation.no_open_reports": "Pašlaik nav atvērtu pārskatu.", + "admin.moderation.moderation_reports": "Satura pārraudzības pārskati", + "admin.moderation.reports": "Pārskati", + "admin.moderation.deleted_content_ref": "Saturs, par kuru ziņots, ar veidu %[1]v un Id %[2]d vairs nepastāv", + "admin.dashboard.remove_resolved_reports": "Noņemt atrisinātos ziņojumus", + "compare.branches.title": "Salīdzināt zarus", + "admin.auths.allow_username_change": "Atļaut lietotājvārda mainīšanu", + "repo.commit.load_tags_failed": "Birku ielādēšana neizdevās iekšējas kļūdas dēļ", + "admin.auths.allow_username_change.description": "Ļaut lietotājiem profila iestatījumos mainīt savu lietotājvārdu", + "warning.repository.out_of_sync": "Šīs glabātavas atspoguļojums datubāzē nav sinhronizēts. Ja šis brīdinājums tiek rādīts pēc iesūtījuma aizgādāšanas šajā glabātavā, jāsazinās ar pārvaldītāju.", + "migrate.pagure.description": "Pārcelt datus no pagure.io vai citiem Pagure serveriem.", + "migrate.pagure.incorrect_url": "Ir norādīts nepareizs avota glabātavas URL", + "migrate.pagure.project_url": "Pagure projekta URL", + "migrate.pagure.project_example": "Pagure projekta URL, piem., https://pagure.io/pagure", + "migrate.pagure.token_label": "Pilnvara", + "migrate.pagure.token_body_a": "Jānorāda Pagura API pilnvara ar piekļuvi privātajiem pieteikumiem, lai izveidotu glabātavu tikai ar privātiem pieteikumiem tajā", + "migrate.pagure.token_body_b": "Jāpārliecinās, ka augstāk ir iestatīts privātas glabātavas karogs, ja vēlies, lai šī glabātava būtu privāta", + "repo.pulls.already_merged": "Apvienošana neizdevās: šis izmaiņu pieprasījums jau ir iekļauts." } diff --git a/options/locale_next/locale_nb_NO.json b/options/locale_next/locale_nb_NO.json index 8f2f9b5e1c..b6b10cebb5 100644 --- a/options/locale_next/locale_nb_NO.json +++ b/options/locale_next/locale_nb_NO.json @@ -1,18 +1,25 @@ { - "home.welcome.no_activity": "Ingen aktivitet", - "home.welcome.activity_hint": "Det er foreløpig ingenting i feeden din. Aktivitetene dine og handlingene dine fra repositorier du følger, vil vises her etter hvert.", - "home.explore_repos": "Utforsk repositorier", - "home.explore_users": "Utforsk brukere", - "home.explore_orgs": "Utforsk organisasjoner", + "relativetime.1day": "i går", + "moderation.abuse_category.other_violations": "Andre regel overtredelser", + "moderation.report_remarks": "Kommentar", + "moderation.report_remarks.placeholder": "Skriv noen detaljer rundt missbruket du rapporterer.", + "moderation.submit_report": "Send rapport", + "moderation.reporting_failed": "Missbruk rapporten kunne ikke sendes inn: %v", + "relativetime.hours": { + "one": "%d time siden", + "other": "%d timer siden" + }, + "repo.form.cannot_create": "Det maksimale antallet repositories er nådd i alle områdene du har tilgang til.", + "repo.issue_indexer.title": "Saksindekserer", + "moderation.abuse_category.illegal_content": "Ulovlig innhold", + "error.not_found.title": "Fant ikke siden", + "themes.names.forgejo-light": "Forgejo lyst", + "themes.names.forgejo-dark": "Forgejo mørk", "stars.list.none": "Ingen har gitt stjerner til dette repoet.", "watch.list.none": "Ingen følger dette repoet.", - "followers.incoming.list.self.none": "Ingen følger profilen din.", "followers.incoming.list.none": "Ingen følger denne brukeren.", "followers.outgoing.list.self.none": "Du følger ikke noen.", "followers.outgoing.list.none": "%s følger ikke noen.", - "relativetime.now": "nå", - "relativetime.future": "i fremtiden", - "relativetime.1day": "i går", "relativetime.2days": "to dager siden", "relativetime.1week": "forrige uke", "relativetime.2weeks": "to uker siden", @@ -20,19 +27,56 @@ "relativetime.2months": "to måneder siden", "relativetime.1year": "i fjor", "relativetime.2years": "to år siden", - "repo.form.cannot_create": "Det maksimale antallet repositories er nådd i alle områdene du har tilgang til.", - "repo.issue_indexer.title": "Saksindekserer", - "search.milestone_kind": "Søker i milepæler…", - "incorrect_root_url": "Denne Forgejo instansen er konfigurert til å bruke \"%s\". Du bruker Forgejo via en annen URL, noe som kan forårsake at deler av applikasjonen ikke fungerer. Den kanoniske URL-en styres av Forgejo-administratorer via innstillingen ROOT_URL i app.ini-filen.", - "themes.names.forgejo-auto": "Forgejo (følg systemtema)", - "themes.names.forgejo-light": "Forgejo lyst", - "themes.names.forgejo-dark": "Forgejo mørk", - "error.not_found.title": "Fant ikke siden", "alert.asset_load_failed": "Kunne ikke laste inn ressursfiler fra {path}. Sørg for at ressursfilene er tilgjengelige.", - "alert.range_error": " må være et nummer mellom %[1]s og %[2]s.", + "search.milestone_kind": "Søker i milepæler…", + "home.welcome.no_activity": "Ingen aktivitet", + "home.explore_repos": "Utforsk repositorier", + "home.explore_users": "Utforsk brukere", + "home.explore_orgs": "Utforsk organisasjoner", + "relativetime.mins": { + "one": "%d minutt siden", + "other": "%d minutter siden" + }, + "relativetime.months": { + "one": "%d måned siden", + "other": "%d måneder siden" + }, + "relativetime.years": { + "one": "%d år siden", + "other": "%d år siden" + }, + "repo.pulls.title_desc": { + "one": "ønsker å slå sammen %[1]d commit fra %[2]s inn i %[3]s", + "other": "ønsker å slå sammen %[1]d commits fra %[2]s inn i %[3]s" + }, + "og.repo.summary_card.alt_description": "Sammendrag for repository %[1]s, beskrevet som: %[2]s", + "followers.incoming.list.self.none": "Ingen følger profilen din.", "install.invalid_lfs_path": "Kan ikke opprette LFS-root på: %[1]s", + "relativetime.now": "nå", + "relativetime.future": "i fremtiden", + "repo.pulls.merged_title_desc": { + "one": "slo sammen %[1]d commit fra %[2]s inn i %[3]s %[4]s", + "other": "slo sammen %[1]d commits fra %[2]s inn i %[3]s %[4]s" + }, + "editor.textarea.tab_hint": "Linjen er allerede innrykket. Trykk Tab igjen eller trykk Escape for å gå ut av editoren.", + "editor.textarea.shift_tab_hint": "Ingen innrykk på denne linjen. Trykk Shift + Tab igjen eller Escape for å gå ut av editoren.", + "alert.range_error": " må være et nummer mellom %[1]s og %[2]s.", + "themes.names.forgejo-auto": "Forgejo (følg systemtema)", + "home.welcome.activity_hint": "Det er foreløpig ingenting i feeden din. Aktivitetene dine og handlingene dine fra repositorier du følger, vil vises her etter hvert.", + "incorrect_root_url": "Denne Forgejo instansen er konfigurert til å bruke \"%s\". Du bruker Forgejo via en annen URL, noe som kan forårsake at deler av applikasjonen ikke fungerer. Den kanoniske URL-en styres av Forgejo-administratorer via innstillingen ROOT_URL i app.ini-filen.", + "mail.actions.run_info_trigger": "Startet på grunn av: %[1]s by: %[2]s", + "admin.dashboard.cleanup_offline_runners": "Rydd opp offline runners", + "settings.visibility.description": "Profilens synlighet påvirker andres mulighet til å få tilgang til dine ikke-private repositorier. Les mer", "profile.actions.tooltip": "Flere handlinger", "profile.edit.link": "Rediger profil", + "relativetime.days": { + "one": "%d dag siden", + "other": "%d dager siden" + }, + "relativetime.weeks": { + "one": "%d uke siden", + "other": "%d uker siden" + }, "feed.atom.link": "Atom feed", "keys.ssh.link": "SSH nøkler", "keys.gpg.link": "GPG nøkler", @@ -47,12 +91,6 @@ "moderation.abuse_category.placeholder": "Velg en kategori", "moderation.abuse_category.spam": "Spam", "moderation.abuse_category.malware": "Skadelig programvare", - "moderation.abuse_category.illegal_content": "Ulovlig innhold", - "moderation.abuse_category.other_violations": "Andre regel overtredelser", - "moderation.report_remarks": "Kommentar", - "moderation.report_remarks.placeholder": "Skriv noen detaljer rundt missbruket du rapporterer.", - "moderation.submit_report": "Send rapport", - "moderation.reporting_failed": "Missbruk rapporten kunne ikke sendes inn: %v", "moderation.reported_thank_you": "Takk for meldingen. Vi har varslet administratorene.", "mail.actions.successful_run_after_failure_subject": "Arbeidsflyten %[1]s er gjenopprettet i repository %[2]s", "mail.actions.not_successful_run_subject": "Arbeidsflyten %[1]s feilet i repository %[2]s", @@ -60,14 +98,9 @@ "mail.actions.not_successful_run": "Arbeidsflyten %[1]s feilet i repository %[2]s", "mail.actions.run_info_cur_status": "Status for denne kjøringen: %[1]s (oppdatert fra %[2]s)", "mail.actions.run_info_previous_status": "Status for forrige kjøring: %[1]s", - "mail.actions.run_info_trigger": "Startet på grunn av: %[1]s by: %[2]s", "repo.diff.commit.next-short": "Neste", "repo.diff.commit.previous-short": "Forrige", "discussion.locked": "Denne diskusjonen er låst. Kommentarer kan kun gjøres av bidragsytere.", - "editor.textarea.tab_hint": "Linjen er allerede innrykket. Trykk Tab igjen eller trykk Escape for å gå ut av editoren.", - "editor.textarea.shift_tab_hint": "Ingen innrykk på denne linjen. Trykk Shift + Tab igjen eller Escape for å gå ut av editoren.", - "admin.dashboard.cleanup_offline_runners": "Rydd opp offline runners", - "settings.visibility.description": "Profilens synlighet påvirker andres mulighet til å få tilgang til dine ikke-private repositorier. Les mer", "avatar.constraints_hint": "Egendefinert avatar kan ikke overstige %[1]s i størrelse eller være større enn %[2]d × %[3]d piksler", "meta.last_line": "Vi gir oss ikke. Kongen har sagt nei!" } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index c1769e55e6..64ef8db60f 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -28,7 +28,6 @@ "mail.actions.not_successful_run": "Warkwies %[1]s in Repositorium %[2]s is fehlslagen", "mail.actions.run_info_cur_status": "Tostand vun deesem Utföhren: %[1]s (jüüst vun %[2]s verneeit)", "mail.actions.run_info_previous_status": "Tostand vun de vörig Utföhren: %[1]s", - "mail.actions.run_info_ref": "Twieg: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Utlööst um: %[1]s vun: %[2]s", "discussion.locked": "Deeser Snack is tosloten worden. Blots Bidragers könen kommenteren.", "relativetime.future": "in Tokunft", @@ -94,7 +93,7 @@ "editor.textarea.tab_hint": "Rieg al inschuven. Drück weer Tab of Esc, um de Bewarker to verlaten.", "editor.textarea.shift_tab_hint": "Keen Inschuuv in deeser Rieg. Drück weer Umschalt+Tab of Esc, um de Bewarker to verlaten.", "admin.dashboard.cleanup_offline_runners": "Nich verbunnen Lopers uprümen", - "settings.visibility.description": "De Profil-Sichtbaarkeid maakt daar wat an, of un wo anner Lüü diene nich-privaaten Repositoriums ankieken könen. Mehr unnerhören", + "settings.visibility.description": "De Profil-Sichtbaarkeid maakt daar wat an, of un wo anner Lüü diene nich-privaaten Repositoriums ankieken könen. Mehr unnerhören.", "avatar.constraints_hint": "Dat eegene Kontobill düür nich groter as %[1]s wesen of groter as %[2]d×%[3]d Billtüttels wesen", "repo.diff.commit.next-short": "Anner", "repo.diff.commit.previous-short": "Vörig", @@ -103,5 +102,27 @@ "keys.gpg.link": "GPG-Slötels", "profile.actions.tooltip": "Mehr Aktioonen", "profile.edit.link": "Profil bewarken", - "mail.actions.run_info_sha": "Kommitteren: %[1]s" + "og.repo.summary_card.alt_description": "Tosamenfatens-Kaart vun de Repositorium %[1]s, beschrieven as: %[2]s", + "mail.actions.run_info_sha": "Kommitteren: %[1]s", + "repo.settings.push_mirror.branch_filter.description": "Twiegen tum Spegeln. Laat dat leeg, um all Twiegen to spegeln. Lees de %[2]s-Dokumenteren för de Syntax. Bispölen: main, release/*", + "repo.settings.push_mirror.branch_filter.label": "Twieg-Filter (wenn du willst)", + "discussion.sidebar.reference": "Nömen", + "admin.moderation.moderation_reports": "Moderatioons-Berichten", + "admin.moderation.no_open_reports": "Dat gifft jüüst keene open Berichten.", + "admin.moderation.reports": "Berichten", + "admin.moderation.deleted_content_ref": "Mellt Inholl mit Aard %[1]v un Kennteken %[2]d gifft dat nich mehr", + "admin.dashboard.remove_resolved_reports": "Lööst Berichten wegdoon", + "compare.branches.title": "Twiegen verglieken", + "repo.commit.load_tags_failed": "Kunn de Markens um eenen binnern Fehler nich laden", + "admin.auths.allow_username_change.description": "Verlööv Brukers, hör Brukernaam in de Profil-Instellens to ännern", + "admin.auths.allow_username_change": "Brukernaam-Ännern verlöven", + "warning.repository.out_of_sync": "De Datenbank-Tostand vun deesem Repositorium is verschuven. Wenn deese Wahrschau immer noch wiest word, nadeem een Kommitteren to deesem Repositorium schuven word, kuntakteer de Sied-Chef.", + "repo.pulls.already_merged": "Tosamenföhren fehlslagen: Deeser Haalvörslag is al tosamenföhrt worden.", + "migrate.pagure.description": "Daten vun pagure.io of anner Pagure-Instanzen umtrecken.", + "migrate.pagure.project_url": "Pagure-Projekt-URL", + "migrate.pagure.project_example": "De URL vum Projekt up Pagure, to’n Bispööl https://pagure.io/pagure", + "migrate.pagure.token_label": "Teken", + "migrate.pagure.token_body_b": "Wees wiss, boven the Flagg för een privaates Repo to setten, wenn du willst, dat deeses Repo privaat wesen sall", + "migrate.pagure.incorrect_url": "Ungültige Quell-Repositoriums-URL is angeven worden", + "migrate.pagure.token_body_a": "Giff een Pagure-API-Teken mit Togang to de privaaten Gefallens an, um een Repositorium mit blots the privaaten Gefallens daarin to maken" } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index 5c9c8a9b07..14b638f7ec 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -28,7 +28,6 @@ "mail.actions.not_successful_run": "Werkstroom %[1]s mislukt in repositorie %[2]s", "mail.actions.run_info_cur_status": "De status van deze run: %[1]s (zojuist bijgewerkt van %[2]s)", "mail.actions.run_info_previous_status": "Status vorige run: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Getriggerd omdat: %[1]s door: %[2]s", "discussion.locked": "Deze discussie is afgesloten. Commentaar is alleen mogelijk voor bijdragers.", "relativetime.now": "nu", @@ -93,15 +92,37 @@ "followers.incoming.list.self.none": "Niemand volgt uw profiel.", "followers.incoming.list.none": "Deze gebruiker wordt door niemand gevolgd.", "followers.outgoing.list.self.none": "U volgt niemand.", + "settings.visibility.description": "Profielzichtbaarheid beïnvloedt de mogelijkheid van anderen om toegang te krijgen tot je niet-privé repositories. Lees meer.", + "repo.diff.commit.next-short": "Volgende", + "admin.dashboard.cleanup_offline_runners": "Offline runners opruimen", + "keys.ssh.link": "SSH sleutels", + "keys.gpg.link": "GPG sleutels", "profile.actions.tooltip": "Meer acties", "profile.edit.link": "Profiel bewerken", "feed.atom.link": "Atom-feed", - "keys.ssh.link": "SSH sleutels", - "keys.gpg.link": "GPG sleutels", - "repo.diff.commit.next-short": "Volgende", "repo.diff.commit.previous-short": "Vorige", - "admin.dashboard.cleanup_offline_runners": "Offline runners opruimen", - "settings.visibility.description": "Profielzichtbaarheid beïnvloedt de mogelijkheid van anderen om toegang te krijgen tot je niet-privé repositories. Lees meer", "avatar.constraints_hint": "Eigen avatars mogen niet groter zijn dan %[1]s in grootte of groter zijn dan %[2]dx%[3]d pixels", - "mail.actions.run_info_sha": "Commit: %[1]s" + "og.repo.summary_card.alt_description": "Samenvattingsoverzicht van repositorie %[1]s, omschreven als: %[2]s", + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Branch filter (optioneel)", + "repo.settings.push_mirror.branch_filter.description": "Branches die gespiegeld moeten worden. Laat het laag om alle branches te spiegelen. Zie %[2]s documentatie voor de syntax. Voorbeeld: main, release/*", + "admin.dashboard.remove_resolved_reports": "Opgeloste meldingen verwijderen", + "compare.branches.title": "Vergelijk branches", + "repo.commit.load_tags_failed": "Het laden van tags is mislukt vanwege een interne fout", + "admin.auths.allow_username_change": "Gebruikersnaam wijzigen toestaan", + "admin.auths.allow_username_change.description": "Gebruikers toestaan hun gebruikersnaam te wijzigen in de profielinstellingen", + "discussion.sidebar.reference": "Referentie", + "admin.moderation.moderation_reports": "Moderatiemeldingen", + "admin.moderation.no_open_reports": "Er zijn momenteel geen openstaande meldingen.", + "admin.moderation.reports": "Meldingen", + "admin.moderation.deleted_content_ref": "Gemelde inhoud met type %[1]v en id %[2]d bestaat niet meer", + "warning.repository.out_of_sync": "De databaserepresentatie van deze repository is niet gesynchroniseerd. Als deze waarschuwing nog steeds wordt weergegeven nadat u een commit naar deze repository hebt gepusht, neem dan contact op met de beheerder.", + "repo.pulls.already_merged": "Samenvoegen mislukt: deze pull request is al samengevoegd.", + "migrate.pagure.description": "Migreer gegevens van pagure.io of andere Pagure-instanties.", + "migrate.pagure.incorrect_url": "Er is een onjuiste URL voor de bronrepository opgegeven", + "migrate.pagure.project_url": "Pagure-project URL", + "migrate.pagure.project_example": "De url van het Pagure-project, bijvoorbeeld https://pagure.io/pagure", + "migrate.pagure.token_label": "Token", + "migrate.pagure.token_body_a": "Geef een Pagure API-token met toegang tot de privé-issues om een repository te maken met alleen de privé-issues erin", + "migrate.pagure.token_body_b": "Zorg ervoor dat u de vlag voor privé-repository hierboven instelt als u wilt dat deze repository privé is" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 0fc65a7dd6..e2fabdaf74 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -26,7 +26,6 @@ "meta.last_line": "real hot girl shit", "mail.actions.run_info_cur_status": "Status desta execução: %[1]s (atualizado recentemente de %[2]s)", "mail.actions.run_info_previous_status": "Status da execução anterior: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.successful_run_after_failure_subject": "Workflow %[1]s recuperado no repositório %[2]s", "mail.actions.not_successful_run_subject": "Workflow %[1]s falhou no repositório %[2]s", "mail.actions.successful_run_after_failure": "Workflow %[1]s recuperado no repositório %[2]s", @@ -103,13 +102,26 @@ "editor.textarea.shift_tab_hint": "Sem indentação nesta linha. Pressione Shift + Tab novamente ou Esc para sair do editor.", "admin.dashboard.cleanup_offline_runners": "Limpar runners desconectados", "avatar.constraints_hint": "Imagem de perfil personalizada não pode exceder %[1]s em tamanho ou ser maior que %[2]dx%[3]d pixels", - "settings.visibility.description": "A visibilidade do perfil afeta a habilidade de acessarem seus repositórios não-privados. Saiba mais", + "settings.visibility.description": "A visibilidade do perfil afeta a habilidade de acessarem seus repositórios não-privados. Saiba mais.", "repo.diff.commit.next-short": "Próximo", "repo.diff.commit.previous-short": "Anterior", - "profile.actions.tooltip": "Mais Actions", "profile.edit.link": "Editar perfil", "feed.atom.link": "Feed Atom", - "keys.ssh.link": "Chaves SSH", "keys.gpg.link": "Chaves GPG", - "mail.actions.run_info_sha": "Commit: %[1]s" + "og.repo.summary_card.alt_description": "Cartão de resumo do repositório %[1]s, descrito como: %[2]s", + "profile.actions.tooltip": "Mais Actions", + "keys.ssh.link": "Chaves SSH", + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Filtro de branches (opcional)", + "repo.settings.push_mirror.branch_filter.description": "Branches para espelhar. Deixe em branco para espelhar todos os branches. Veja %[2]s documentação sobre a sintaxe. Exemplos: main, release/*", + "discussion.sidebar.reference": "Referência", + "admin.moderation.deleted_content_ref": "Conteúdo denunciado do tipo %[1]v e ID %[2]d não existe mais", + "admin.moderation.moderation_reports": "Denúncias para moderação", + "admin.moderation.reports": "Denúncias", + "admin.moderation.no_open_reports": "Não há denúncias abertas atualmente.", + "admin.dashboard.remove_resolved_reports": "Remover denúncias resolvidas", + "compare.branches.title": "Comparar branches", + "admin.auths.allow_username_change": "Permitir mudança de nome de usuário", + "admin.auths.allow_username_change.description": "Permitir que usuários alterem seus nomes de usuário nas configurações do perfil", + "repo.commit.load_tags_failed": "Carregamento de etiquetas falhou devido a um erro interno" } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index 5d68db624b..0bdabfd431 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -27,7 +27,6 @@ "mail.actions.successful_run_after_failure_subject": "Sequência de trabalho %[1]s foi recuperada no repositório %[2]s", "mail.actions.not_successful_run_subject": "Sequência de trabalho %[1]s falhou no repositório %[2]s", "mail.actions.not_successful_run": "Sequência de trabalho %[1]s falhou no repositório %[2]s", - "mail.actions.run_info_ref": "Ramo: %[1]s (%[2]s)", "mail.actions.successful_run_after_failure": "Sequência de trabalho %[1]s foi recuperada no repositório %[2]s", "discussion.locked": "Esta discussão foi fechada. Apenas contribuidores podem publicar comentários.", "mail.actions.run_info_cur_status": "Estado desta execução: %[1]s (atualizado recentemente de %[2]s)", @@ -102,14 +101,23 @@ "editor.textarea.shift_tab_hint": "Sem indentação nesta linha. Pressione Shift + Tab novamente ou Escape para sair do editor.", "stars.list.none": "Ninguém juntou este repositório aos favoritos.", "admin.dashboard.cleanup_offline_runners": "Limpeza de executores offline", - "settings.visibility.description": "A visibilidade do perfil afecta a capacidade de outros acederem aos seus repositórios não privados. Ler mais", + "settings.visibility.description": "A visibilidade do perfil afecta a capacidade de outros acederem aos seus repositórios não privados. Saiba mais.", "avatar.constraints_hint": "O avatar personalizado não pode exceder %[1]s de tamanho ou ser maior do que %[2]dx%[3]d pixéis", + "repo.diff.commit.next-short": "Seg.", "profile.actions.tooltip": "Mais Actions", "profile.edit.link": "Editar perfil", "feed.atom.link": "Feed Atom", "keys.ssh.link": "Chaves SSH", "keys.gpg.link": "Chaves GPG", - "repo.diff.commit.next-short": "Seg.", "repo.diff.commit.previous-short": "Ant.", - "mail.actions.run_info_sha": "Cometimento: %[1]s" + "og.repo.summary_card.alt_description": "Cartão de resumo do repositório %[1]s, descrito como: %[2]s", + "repo.settings.push_mirror.branch_filter.label": "Filtro de ramos (opcional)", + "repo.settings.push_mirror.branch_filter.description": "Ramos a serem espelhados. Deixe em branco para espelhar todos os ramos. Veja a %[2]s documentação sobre a sintaxe. Exemplos: main, release/*", + "mail.actions.run_info_sha": "Cometimento: %[1]s", + "discussion.sidebar.reference": "Referência", + "admin.dashboard.remove_resolved_reports": "Remover denúncias resolvidas", + "admin.moderation.no_open_reports": "Atualmente não há denúncias em aberto.", + "admin.moderation.deleted_content_ref": "O conteúdo denunciado do tipo %[1]v e id %[2]d já não existe", + "admin.moderation.moderation_reports": "Denúncias aos moderadores", + "admin.moderation.reports": "Denúncias" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index e49eb79ca2..0838e9063e 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -26,7 +26,6 @@ "meta.last_line": "...ъъ", "mail.actions.not_successful_run_subject": "Провал раб. потока %[1]s в репозитории %[2]s", "mail.actions.successful_run_after_failure_subject": "Возобновление раб. потока %[1]s в репозитории %[2]s", - "mail.actions.run_info_ref": "Ветвь: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Причина срабатывания: %[1]s by: %[2]s", "mail.actions.successful_run_after_failure": "Рабочий поток %[1]s в репозитории %[2]s был возобновлён после провала", "mail.actions.run_info_cur_status": "Текущее состояние: %[1]s (обновлено после %[2]s)", @@ -103,7 +102,7 @@ "editor.textarea.shift_tab_hint": "В строке нет отступов. Нажмите Shift + Tab снова или Escape, чтобы покинуть редактор.", "admin.dashboard.cleanup_offline_runners": "Удалить недоступных исполнителей", "avatar.constraints_hint": "Изображение профиля не может быть более %[1]s и крупнее %[2]dx%[3]d пикселей", - "settings.visibility.description": "Видимость профиля влияет на доступ других до ваших не частных репозиториев. Подробнее", + "settings.visibility.description": "Видимость профиля влияет на доступ других до ваших не частных репозиториев. Подробнее.", "repo.diff.commit.previous-short": "Пред.", "repo.diff.commit.next-short": "След.", "profile.actions.tooltip": "Показать действия", @@ -111,5 +110,18 @@ "keys.ssh.link": "Ключи SSH", "keys.gpg.link": "Ключи GPG", "profile.edit.link": "Изменить профиль", - "mail.actions.run_info_sha": "Коммит: %[1]s" + "og.repo.summary_card.alt_description": "Карточка со сводкой о репозитории %s. Описание: %[2]s", + "mail.actions.run_info_sha": "Коммит: %[1]s", + "repo.settings.push_mirror.branch_filter.description": "Синхронизируемые ветви. Оставьте пустым, чтобы синхронизировать все. Ознакомьтесь с синтаксисом в документации %[2]s. Примеры: main, release/*", + "repo.settings.push_mirror.branch_filter.label": "Выбор ветвей (опционально)", + "discussion.sidebar.reference": "Ссылка", + "admin.moderation.moderation_reports": "Жалобы модерации", + "admin.moderation.deleted_content_ref": "Содержимое типа %[1]v и ид. %[2]d, на которое пожаловались, более не существует", + "admin.moderation.reports": "Жалобы", + "admin.moderation.no_open_reports": "Нет открытых жалоб.", + "admin.dashboard.remove_resolved_reports": "Удалить разрешённые жалобы", + "compare.branches.title": "Сравнение ветвей", + "repo.commit.load_tags_failed": "Загрузка тегов не удалась из-за внутренней ошибки", + "admin.auths.allow_username_change.description": "Пользователи смогут изменять свои имена после регистрации", + "admin.auths.allow_username_change": "Разрешить смену имён" } diff --git a/options/locale_next/locale_sv-SE.json b/options/locale_next/locale_sv-SE.json index c8c50baa76..1195647bf5 100644 --- a/options/locale_next/locale_sv-SE.json +++ b/options/locale_next/locale_sv-SE.json @@ -27,7 +27,6 @@ "moderation.reporting_failed": "Det gick inte att skicka in den nya övergreppsrapporten: %v", "mail.actions.run_info_cur_status": "Status för denna körning: %[1]s (just uppdaterad från %[2]s)", "mail.actions.run_info_previous_status": "Status för föregående körning: %[1]s", - "mail.actions.run_info_ref": "Gren: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Utlöses på grund av: %[1]s av: %[2]s", "alert.asset_load_failed": "Misslyckades med att läsa in resursfiler från {path}. Kontrollera att resursfilerna är åtkomliga.", "install.invalid_lfs_path": "Det gick inte att skapa LFS-roten på den angivna sökvägen: %[1]s", @@ -96,11 +95,12 @@ "moderation.abuse_category.malware": "Skadlig kod", "settings.visibility.description": "Profilens synlighet påverkar andras möjlighet att komma åt dina icke-privata förråd. Läs mer", "avatar.constraints_hint": "Anpassade avatarer får inte vara större än %[1] eller %[2]dx%[3] bildpunkter", + "og.repo.summary_card.alt_description": "Sammanfattningskort för arkivet %[1]s, beskrivet som: %[2]s", "profile.actions.tooltip": "Fler åtgärder", - "profile.edit.link": "Redigera profil", - "feed.atom.link": "Atom-flöde", - "keys.ssh.link": "SSH-nycklar", "keys.gpg.link": "GPG-nycklar", + "profile.edit.link": "Redigera profil", + "keys.ssh.link": "SSH-nycklar", "repo.diff.commit.next-short": "Nästa", - "repo.diff.commit.previous-short": "Föreg" + "repo.diff.commit.previous-short": "Föreg", + "feed.atom.link": "Atom-flöde" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index 81b69ec859..1fbe503003 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -25,7 +25,6 @@ "alert.range_error": " має бути числом від %[1]s до %[2]s.", "meta.last_line": "Не зливай злий запити на злиття — зіллється зле.", "mail.actions.successful_run_after_failure": "Робочий потік %[1]s відновлено в репозиторії %[2]s", - "mail.actions.run_info_ref": "Гілка: %[1]s (%[2]s)", "mail.actions.successful_run_after_failure_subject": "Робочий потік %[1]s відновлено в репозиторії %[2]s", "mail.actions.run_info_previous_status": "Стан попереднього запуску: %[1]s", "mail.actions.run_info_cur_status": "Стан цього запуску: %[1]s (щойно оновлено з %[2]s)", @@ -102,7 +101,7 @@ "editor.textarea.tab_hint": "У рядку вже є відступ. Натисніть Tab ще раз або Esc, щоб вийти з редактора.", "editor.textarea.shift_tab_hint": "У цьому рядку немає відступів. Натисніть Shift + Tab ще раз або Esc, щоб вийти з редактора.", "admin.dashboard.cleanup_offline_runners": "Очистити неактивні раннери", - "settings.visibility.description": "Видимість профілю впливає на можливість інших користувачів отримати доступ до ваших неприватних репозиторіїв. Дізнатися більше", + "settings.visibility.description": "Видимість профілю впливає на можливість інших користувачів отримати доступ до ваших неприватних репозиторіїв. Дізнатися більше.", "avatar.constraints_hint": "Розмір користувацького аватара не може перевищувати %[1]s або бути більшим за %[2]d×%[3]d пікселів", "repo.diff.commit.next-short": "Наступний", "repo.diff.commit.previous-short": "Попередній", @@ -111,5 +110,27 @@ "profile.edit.link": "Редагувати профіль", "feed.atom.link": "Стрічка Atom", "profile.actions.tooltip": "Більше дій", - "mail.actions.run_info_sha": "Коміт: %[1]s" + "og.repo.summary_card.alt_description": "Підсумкова картка репозиторію %[1]s з описом: %[2]s", + "mail.actions.run_info_sha": "Коміт: %[1]s", + "repo.settings.push_mirror.branch_filter.description": "Гілки для дзеркалювання. Залиште порожнім, щоб віддзеркалити всі гілки. Дивіться синтаксис у документації %[2]s. Приклади: main, release/*", + "repo.settings.push_mirror.branch_filter.label": "Фільтр гілок (необов'язково)", + "discussion.sidebar.reference": "Посилання", + "admin.moderation.no_open_reports": "Відкритих скарг наразі немає.", + "admin.moderation.reports": "Скарги", + "admin.moderation.deleted_content_ref": "Вміст типу %[1]v з ідентифікатором %[2]d, на який подано скаргу, більше не існує", + "admin.moderation.moderation_reports": "Скарги модераторам", + "admin.dashboard.remove_resolved_reports": "Видалити закриті скарги", + "compare.branches.title": "Порівняти гілки", + "repo.commit.load_tags_failed": "Завантаження тегів не вдалося через внутрішню помилку", + "admin.auths.allow_username_change.description": "Дозволити користувачам змінювати ім'я користувача в налаштуваннях профілю", + "admin.auths.allow_username_change": "Дозволити змінювати імена", + "warning.repository.out_of_sync": "База даних цього репозиторію не синхронізована. Якщо ви знову бачите це попередження після відправлення коміту в цей репозиторій, зверніться до адміністратора.", + "repo.pulls.already_merged": "Не вдалося об'єднати: цей запит на злиття вже об'єднано.", + "migrate.pagure.description": "Перенести дані з pagure.io або інших екземплярів Pagure.", + "migrate.pagure.token_label": "Токен", + "migrate.pagure.project_url": "URL-адреса проєкту Pagure", + "migrate.pagure.project_example": "URL-адреса проєкту Pagure, наприклад, https://pagure.io/pagure", + "migrate.pagure.token_body_b": "Якщо хочете зробити цей репозиторій приватним, обов'язково встановіть прапорець «Приватний репозиторій» вище", + "migrate.pagure.incorrect_url": "Вказано неправильну URL-адресу репозиторію-джерела", + "migrate.pagure.token_body_a": "Уведіть токен API Pagure з доступом до приватних задач, щоб створити репозиторій, який міститиме лише приватні задачі" } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 743cced8c1..d26d95bbf7 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -20,7 +20,6 @@ "mail.actions.not_successful_run_subject": "仓库 %[2]s 中的工作流 %[1]s 已失败", "mail.actions.successful_run_after_failure": "仓库 %[2]s 中的工作流 %[1]s 已恢复", "mail.actions.run_info_previous_status": "上次运行的状态:%[1]s", - "mail.actions.run_info_ref": "分支:%[1]s(%[2]s)", "mail.actions.run_info_cur_status": "此次运行的状态:%[1]s(从 %[2]s 更新)", "mail.actions.run_info_trigger": "由 %[2]s %[1]s 触发", "mail.actions.not_successful_run": "仓库 %[2]s 中的工作流 %[1]s 已失败", @@ -70,7 +69,7 @@ "editor.textarea.tab_hint": "此行已缩进。再次按 Tab 或按 Escape 退出编辑器。", "editor.textarea.shift_tab_hint": "此行无缩进。再次按 Shift + Tab 或按 Escape 退出编辑器。", "admin.dashboard.cleanup_offline_runners": "清理离线运行器", - "settings.visibility.description": "个人资料可见性设置会影响他人对您的非私有仓库的访问。了解更多", + "settings.visibility.description": "个人资料可见性设置会影响他人对您的非私有仓库的访问。了解更多。", "avatar.constraints_hint": "自定义头像大小不得超过 %[1]s,且分辨率不得大于 %[2]d×%[3]d 像素", "keys.ssh.link": "SSH 密钥", "keys.gpg.link": "GPG 密钥", @@ -79,5 +78,18 @@ "repo.diff.commit.previous-short": "上一个", "feed.atom.link": "Atom 订阅源", "profile.edit.link": "编辑个人资料", - "mail.actions.run_info_sha": "提交:%[1]s" + "og.repo.summary_card.alt_description": "仓库 %[1]s 的摘要卡片,描述为:%[2]s", + "repo.settings.push_mirror.branch_filter.label": "分支过滤器(可选)", + "repo.settings.push_mirror.branch_filter.description": "欲镜像的分支。留空以镜像所有分支。关于语法的更多信息,请参见 %[2]s 文档。例如:main, release/*", + "mail.actions.run_info_sha": "提交:%[1]s", + "discussion.sidebar.reference": "引用", + "admin.dashboard.remove_resolved_reports": "移除已解决的举报", + "compare.branches.title": "比较分支", + "admin.auths.allow_username_change.description": "允许用户在个人设置中更改用户名", + "admin.moderation.moderation_reports": "举报", + "admin.moderation.reports": "举报", + "admin.moderation.no_open_reports": "目前没有打开的举报。", + "admin.moderation.deleted_content_ref": "类型为 %[1]v、ID为 %[2]d 的举报不存在", + "repo.commit.load_tags_failed": "由于内部错误,无法加载标签", + "admin.auths.allow_username_change": "允许更改用户名" } diff --git a/options/locale_next/locale_zh-HK.json b/options/locale_next/locale_zh-HK.json index 7d624f24df..277385cf5f 100644 --- a/options/locale_next/locale_zh-HK.json +++ b/options/locale_next/locale_zh-HK.json @@ -1,6 +1,4 @@ { - "repo.pulls.merged_title_desc": { - "other": "於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s" - }, + "repo.pulls.merged_title_desc": "於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s", "moderation.abuse_category.malware": "惡意程式" } diff --git a/options/locale_next/locale_zh-TW.json b/options/locale_next/locale_zh-TW.json index 3ae0b00d2b..8a5a17a774 100644 --- a/options/locale_next/locale_zh-TW.json +++ b/options/locale_next/locale_zh-TW.json @@ -55,7 +55,6 @@ "mail.actions.not_successful_run": "儲存庫 %[2]s 中的工作流程 %[1]s 已失敗", "mail.actions.run_info_cur_status": "本次執行狀態:%[1]s(剛從 %[2]s 更新)", "mail.actions.run_info_previous_status": "前一次執行狀態:%[1]s", - "mail.actions.run_info_ref": "分支:%[1]s (%[2]s)", "mail.actions.run_info_trigger": "觸發原因:%[1]s,由 %[2]s 執行", "discussion.locked": "此討論已被鎖定。僅限貢獻者留言。", "alert.asset_load_failed": "無法從 {path} 載入資源檔案。請確保這些資源檔案可以被存取。", diff --git a/package-lock.json b/package-lock.json index 9de06a8055..9f3f1aef12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,12 +28,12 @@ "esbuild-loader": "4.3.0", "escape-goat": "4.0.0", "fast-glob": "3.3.3", - "htmx.org": "1.9.12", + "htmx.org": "2.0.6", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.6.0", - "mini-css-extract-plugin": "2.9.2", + "mermaid": "11.8.1", + "mini-css-extract-plugin": "2.9.3", "minimatch": "10.0.3", "monaco-editor": "0.52.2", "monaco-editor-webpack-plugin": "7.1.0", @@ -52,55 +52,55 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.5.16", + "vue": "3.5.18", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", - "webpack": "5.99.9", + "webpack": "5.101.1", "webpack-cli": "6.0.1", "wrap-ansi": "9.0.0" }, "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.52.0", + "@playwright/test": "1.54.2", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "4.4.1", - "@stylistic/stylelint-plugin": "3.1.2", - "@vitejs/plugin-vue": "5.2.4", - "@vitest/coverage-v8": "3.2.3", - "@vitest/eslint-plugin": "1.2.2", + "@stylistic/eslint-plugin": "5.2.3", + "@stylistic/stylelint-plugin": "4.0.0", + "@vitejs/plugin-vue": "6.0.1", + "@vitest/coverage-v8": "3.2.4", + "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", - "eslint": "9.28.0", - "eslint-import-resolver-typescript": "4.4.3", + "eslint": "9.33.0", + "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", - "eslint-plugin-import-x": "4.15.1", + "eslint-plugin-import-x": "4.16.1", "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", - "eslint-plugin-playwright": "2.2.0", - "eslint-plugin-regexp": "2.9.0", - "eslint-plugin-sonarjs": "3.0.2", + "eslint-plugin-playwright": "2.2.2", + "eslint-plugin-regexp": "2.10.0", + "eslint-plugin-sonarjs": "3.0.4", "eslint-plugin-toml": "0.12.0", - "eslint-plugin-unicorn": "59.0.1", + "eslint-plugin-unicorn": "60.0.0", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "10.2.0", - "eslint-plugin-vue-scoped-css": "2.10.0", + "eslint-plugin-vue": "10.4.0", + "eslint-plugin-vue-scoped-css": "2.11.0", "eslint-plugin-wc": "3.0.1", - "globals": "16.1.0", - "happy-dom": "18.0.0", + "globals": "16.3.0", + "happy-dom": "18.0.1", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", - "sharp": "0.34.2", - "stylelint": "16.20.0", + "sharp": "0.34.3", + "stylelint": "16.23.1", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", - "svgo": "3.2.0", + "svgo": "4.0.0", "typescript": "5.8.3", - "typescript-eslint": "8.34.0", - "vite-string-plugin": "1.3.4", - "vitest": "3.2.3" + "typescript-eslint": "8.39.0", + "vite-string-plugin": "1.4.6", + "vitest": "3.2.4" }, "engines": { "node": ">= 20.0.0" @@ -155,9 +155,9 @@ } }, "node_modules/@asyncapi/specs": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.8.1.tgz", - "integrity": "sha512-czHoAk3PeXTLR+X8IUaD+IpT+g+zUvkcgMDJVothBsan+oHN3jfcFcFUNdOPAAFoUCQN1hXF1dWuphWy05THlA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.10.0.tgz", + "integrity": "sha512-vB5oKLsdrLUORIZ5BXortZTlVyGWWMC1Nud/0LtgxQ3Yn2738HigAD6EVqScvpPsDUI/bcLVsYEXN4dtXQHVng==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -216,12 +216,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -231,18 +231,18 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -481,9 +481,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", - "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", + "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", "dev": true, "funding": [ { @@ -500,8 +500,52 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.1", - "@csstools/css-tokenizer": "^3.0.1" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/selector-resolve-nested": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", + "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" } }, "node_modules/@discoveryjs/json-ext": { @@ -525,21 +569,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", - "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", + "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.0.2", + "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", "dev": true, "license": "MIT", "optional": true, @@ -548,9 +592,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", - "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", + "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", "dev": true, "license": "MIT", "optional": true, @@ -559,9 +603,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], @@ -575,9 +619,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], @@ -591,9 +635,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], @@ -607,9 +651,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], @@ -623,9 +667,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -639,9 +683,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -655,9 +699,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], @@ -671,9 +715,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], @@ -687,9 +731,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], @@ -703,9 +747,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], @@ -719,9 +763,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], @@ -735,9 +779,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], @@ -751,9 +795,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], @@ -767,9 +811,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], @@ -783,9 +827,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], @@ -799,9 +843,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], @@ -815,9 +859,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", - "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], @@ -831,9 +875,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], @@ -847,9 +891,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -863,9 +907,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], @@ -879,9 +923,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -894,10 +938,26 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -911,9 +971,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -927,9 +987,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -943,9 +1003,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -1021,9 +1081,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", - "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1049,9 +1109,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", - "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1059,9 +1119,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1146,9 +1206,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", - "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", "dev": true, "license": "MIT", "engines": { @@ -1169,32 +1229,19 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", - "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.0", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@github/combobox-nav": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-2.3.1.tgz", @@ -1340,9 +1387,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", "cpu": [ "arm64" ], @@ -1359,13 +1406,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" + "@img/sharp-libvips-darwin-arm64": "1.2.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", "cpu": [ "x64" ], @@ -1382,13 +1429,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" + "@img/sharp-libvips-darwin-x64": "1.2.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", "cpu": [ "arm64" ], @@ -1403,9 +1450,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", "cpu": [ "x64" ], @@ -1420,9 +1467,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", "cpu": [ "arm" ], @@ -1437,9 +1484,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", "cpu": [ "arm64" ], @@ -1454,9 +1501,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", "cpu": [ "ppc64" ], @@ -1471,9 +1518,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", "cpu": [ "s390x" ], @@ -1488,9 +1535,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", "cpu": [ "x64" ], @@ -1505,9 +1552,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", "cpu": [ "arm64" ], @@ -1522,9 +1569,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", "cpu": [ "x64" ], @@ -1539,9 +1586,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", "cpu": [ "arm" ], @@ -1558,13 +1605,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" + "@img/sharp-libvips-linux-arm": "1.2.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", "cpu": [ "arm64" ], @@ -1581,13 +1628,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", "cpu": [ "s390x" ], @@ -1604,13 +1674,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" + "@img/sharp-libvips-linux-s390x": "1.2.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", - "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", "cpu": [ "x64" ], @@ -1627,13 +1697,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" + "@img/sharp-libvips-linux-x64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", "cpu": [ "arm64" ], @@ -1650,13 +1720,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", - "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", "cpu": [ "x64" ], @@ -1673,13 +1743,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", "cpu": [ "wasm32" ], @@ -1687,7 +1757,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.3" + "@emnapi/runtime": "^1.4.4" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1697,9 +1767,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", "cpu": [ "arm64" ], @@ -1717,9 +1787,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", "cpu": [ "ia32" ], @@ -1737,9 +1807,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", "cpu": [ "x64" ], @@ -1795,9 +1865,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "license": "MIT", "engines": { "node": ">=12" @@ -1884,17 +1954,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1906,19 +1972,10 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1926,15 +1983,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1981,39 +2038,11 @@ } }, "node_modules/@keyv/serialize": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.3.tgz", - "integrity": "sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.0.tgz", + "integrity": "sha512-RlDgexML7Z63Q8BSaqhXdCYNBy/JQnqYIwxofUrNLGCblOMHp+xux2Q8nLMLlPpgHQPoU0Do8Z6btCpRBEqZ8g==", "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^6.0.3" - } - }, - "node_modules/@keyv/serialize/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } + "license": "MIT" }, "node_modules/@kurkle/color": { "version": "0.3.4", @@ -2022,18 +2051,18 @@ "license": "MIT" }, "node_modules/@lit-labs/ssr-dom-shim": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.3.0.tgz", - "integrity": "sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.4.0.tgz", + "integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==", "license": "BSD-3-Clause" }, "node_modules/@lit/reactive-element": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.0.tgz", - "integrity": "sha512-L2qyoZSQClcBmq0qajBVbhYEcG6iK0XfLn66ifLe/RfC0/ihpc+pl0Wdn8bJ8o+hj38cG0fGXRgSS20MuXn7qA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.1.tgz", + "integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==", "license": "BSD-3-Clause", "dependencies": { - "@lit-labs/ssr-dom-shim": "^1.2.0" + "@lit-labs/ssr-dom-shim": "^1.4.0" } }, "node_modules/@mcaptcha/core-glue": { @@ -2088,9 +2117,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", - "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.2.tgz", + "integrity": "sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==", "license": "MIT", "dependencies": { "langium": "3.3.1" @@ -2109,16 +2138,16 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", - "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.9.0" + "@tybys/wasm-util": "^0.10.0" } }, "node_modules/@nodelib/fs.scandir": { @@ -2187,13 +2216,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", - "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.2.tgz", + "integrity": "sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.52.0" + "playwright": "1.54.2" }, "bin": { "playwright": "cli.js" @@ -2221,6 +2250,13 @@ "object-assign": "^4.1.1" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/plugin-commonjs": { "version": "22.0.2", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz", @@ -2269,9 +2305,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz", - "integrity": "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", "cpu": [ "arm" ], @@ -2283,9 +2319,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz", - "integrity": "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", "cpu": [ "arm64" ], @@ -2297,9 +2333,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz", - "integrity": "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", "cpu": [ "arm64" ], @@ -2311,9 +2347,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz", - "integrity": "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", "cpu": [ "x64" ], @@ -2325,9 +2361,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz", - "integrity": "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", "cpu": [ "arm64" ], @@ -2339,9 +2375,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz", - "integrity": "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", "cpu": [ "x64" ], @@ -2353,9 +2389,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz", - "integrity": "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", "cpu": [ "arm" ], @@ -2367,9 +2403,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz", - "integrity": "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", "cpu": [ "arm" ], @@ -2381,9 +2417,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz", - "integrity": "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", "cpu": [ "arm64" ], @@ -2395,9 +2431,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz", - "integrity": "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", "cpu": [ "arm64" ], @@ -2409,9 +2445,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz", - "integrity": "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", "cpu": [ "loong64" ], @@ -2422,10 +2458,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz", - "integrity": "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", "cpu": [ "ppc64" ], @@ -2437,9 +2473,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz", - "integrity": "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", "cpu": [ "riscv64" ], @@ -2451,9 +2487,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz", - "integrity": "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", "cpu": [ "riscv64" ], @@ -2465,9 +2501,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz", - "integrity": "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", "cpu": [ "s390x" ], @@ -2479,9 +2515,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz", - "integrity": "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", "cpu": [ "x64" ], @@ -2493,9 +2529,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz", - "integrity": "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", "cpu": [ "x64" ], @@ -2507,9 +2543,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz", - "integrity": "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", "cpu": [ "arm64" ], @@ -2521,9 +2557,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz", - "integrity": "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", "cpu": [ "ia32" ], @@ -2535,9 +2571,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz", - "integrity": "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", "cpu": [ "x64" ], @@ -3056,17 +3092,18 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.4.1.tgz", - "integrity": "sha512-CEigAk7eOLyHvdgmpZsKFwtiqS2wFwI1fn4j09IU9GmD4euFM4jEBAViWeCqaNLlbX2k2+A/Fq9cje4HQBXuJQ==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.3.tgz", + "integrity": "sha512-oY7GVkJGVMI5benlBDCaRrSC1qPasafyv5dOBLLv5MTilMGnErKhO6ziEfodDDIZbo5QxPUNW360VudJOFODMw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.32.1", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/types": "^8.38.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "estraverse": "^5.3.0", - "picomatch": "^4.0.2" + "picomatch": "^4.0.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3076,9 +3113,9 @@ } }, "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -3089,26 +3126,54 @@ } }, "node_modules/@stylistic/stylelint-plugin": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.2.tgz", - "integrity": "sha512-tylFJGMQo62alGazK74MNxFjMagYOHmBZiePZFOJK2n13JZta0uVkB3Bh5qodUmOLtRH+uxH297EibK14UKm8g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-4.0.0.tgz", + "integrity": "sha512-CFwt3K4Y/7bygNCLCQ8Sy4Hzgbhxq3BsNW0FIuYxl17HD3ywptm54ocyeiLVRrk5jtz1Zwks7Xr9eiZt8SWHAw==", "dev": true, "license": "MIT", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.1", - "@csstools/css-tokenizer": "^3.0.1", - "@csstools/media-query-list-parser": "^3.0.1", - "is-plain-object": "^5.0.0", - "postcss-selector-parser": "^6.1.2", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3", + "postcss": "^8.5.6", + "postcss-selector-parser": "^7.1.0", "postcss-value-parser": "^4.2.0", - "style-search": "^0.1.0", - "stylelint": "^16.8.2" + "style-search": "^0.1.0" }, "engines": { "node": "^18.12 || >=20.9" }, "peerDependencies": { - "stylelint": "^16.8.0" + "stylelint": "^16.22.0" + } + }, + "node_modules/@stylistic/stylelint-plugin/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, "node_modules/@swc/helpers": { @@ -3117,20 +3182,10 @@ "integrity": "sha512-wpCQMhf5p5GhNg2MmGKXzUNwxe7zRiCsmqYsamez2beP7mKPCSiu+BjZcdN95yYSzO857kr0VfQewmGpS77nqA==", "license": "MIT" }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", + "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", "dev": true, "license": "MIT", "optional": true, @@ -3248,9 +3303,9 @@ "license": "MIT" }, "node_modules/@types/d3-dispatch": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", - "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", "license": "MIT" }, "node_modules/@types/d3-drag": { @@ -3509,9 +3564,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", - "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", + "version": "20.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz", + "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3561,17 +3616,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", - "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz", + "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.34.0", - "@typescript-eslint/type-utils": "8.34.0", - "@typescript-eslint/utils": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/type-utils": "8.39.0", + "@typescript-eslint/utils": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3585,9 +3640,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.34.0", + "@typescript-eslint/parser": "^8.39.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -3601,16 +3656,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", - "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", + "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.34.0", - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/typescript-estree": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", "debug": "^4.3.4" }, "engines": { @@ -3622,18 +3677,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", - "integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", + "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.34.0", - "@typescript-eslint/types": "^8.34.0", + "@typescript-eslint/tsconfig-utils": "^8.39.0", + "@typescript-eslint/types": "^8.39.0", "debug": "^4.3.4" }, "engines": { @@ -3644,18 +3699,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", - "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", + "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0" + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3666,9 +3721,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", - "integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", + "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", "dev": true, "license": "MIT", "engines": { @@ -3679,18 +3734,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", - "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz", + "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.34.0", - "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3703,13 +3759,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", - "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", + "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", "dev": true, "license": "MIT", "engines": { @@ -3721,16 +3777,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", - "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", + "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.34.0", - "@typescript-eslint/tsconfig-utils": "8.34.0", - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0", + "@typescript-eslint/project-service": "8.39.0", + "@typescript-eslint/tsconfig-utils": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3746,7 +3802,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { @@ -3783,16 +3839,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", - "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", + "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.34.0", - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/typescript-estree": "8.34.0" + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3803,18 +3859,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", - "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", + "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.34.0", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.39.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3825,9 +3881,9 @@ } }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.0.tgz", - "integrity": "sha512-h1T2c2Di49ekF2TE8ZCoJkb+jwETKUIPDJ/nO3tJBKlLFPu+fyd93f0rGP/BvArKx2k2HlRM4kqkNarj3dvZlg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", "cpu": [ "arm" ], @@ -3839,9 +3895,9 @@ ] }, "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.0.tgz", - "integrity": "sha512-sG1NHtgXtX8owEkJ11yn34vt0Xqzi3k9TJ8zppDmyG8GZV4kVWw44FHwKwHeEFl07uKPeC4ZoyuQaGh5ruJYPA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", "cpu": [ "arm64" ], @@ -3853,9 +3909,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.0.tgz", - "integrity": "sha512-nJ9z47kfFnCxN1z/oYZS7HSNsFh43y2asePzTEZpEvK7kGyuShSl3RRXnm/1QaqFL+iP+BjMwuB+DYUymOkA5A==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", "cpu": [ "arm64" ], @@ -3867,9 +3923,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.0.tgz", - "integrity": "sha512-TK+UA1TTa0qS53rjWn7cVlEKVGz2B6JYe0C++TdQjvWYIyx83ruwh0wd4LRxYBM5HeuAzXcylA9BH2trARXJTw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", "cpu": [ "x64" ], @@ -3881,9 +3937,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.0.tgz", - "integrity": "sha512-6uZwzMRFcD7CcCd0vz3Hp+9qIL2jseE/bx3ZjaLwn8t714nYGwiE84WpaMCYjU+IQET8Vu/+BNAGtYD7BG/0yA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", "cpu": [ "x64" ], @@ -3895,9 +3951,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.0.tgz", - "integrity": "sha512-bPUBksQfrgcfv2+mm+AZinaKq8LCFvt5PThYqRotqSuuZK1TVKkhbVMS/jvSRfYl7jr3AoZLYbDkItxgqMKRkg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", "cpu": [ "arm" ], @@ -3909,9 +3965,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.0.tgz", - "integrity": "sha512-uT6E7UBIrTdCsFQ+y0tQd3g5oudmrS/hds5pbU3h4s2t/1vsGWbbSKhBSCD9mcqaqkBwoqlECpUrRJCmldl8PA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", "cpu": [ "arm" ], @@ -3923,9 +3979,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.0.tgz", - "integrity": "sha512-vdqBh911wc5awE2bX2zx3eflbyv8U9xbE/jVKAm425eRoOVv/VseGZsqi3A3SykckSpF4wSROkbQPvbQFn8EsA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", "cpu": [ "arm64" ], @@ -3937,9 +3993,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.0.tgz", - "integrity": "sha512-/8JFZ/SnuDr1lLEVsxsuVwrsGquTvT51RZGvyDB/dOK3oYK2UqeXzgeyq6Otp8FZXQcEYqJwxb9v+gtdXn03eQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", "cpu": [ "arm64" ], @@ -3951,9 +4007,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.0.tgz", - "integrity": "sha512-FkJjybtrl+rajTw4loI3L6YqSOpeZfDls4SstL/5lsP2bka9TiHUjgMBjygeZEis1oC8LfJTS8FSgpKPaQx2tQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", "cpu": [ "ppc64" ], @@ -3965,9 +4021,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.0.tgz", - "integrity": "sha512-w/NZfHNeDusbqSZ8r/hp8iL4S39h4+vQMc9/vvzuIKMWKppyUGKm3IST0Qv0aOZ1rzIbl9SrDeIqK86ZpUK37w==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", "cpu": [ "riscv64" ], @@ -3979,9 +4035,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.0.tgz", - "integrity": "sha512-bEPBosut8/8KQbUixPry8zg/fOzVOWyvwzOfz0C0Rw6dp+wIBseyiHKjkcSyZKv/98edrbMknBaMNJfA/UEdqw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", "cpu": [ "riscv64" ], @@ -3993,9 +4049,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.0.tgz", - "integrity": "sha512-LDtMT7moE3gK753gG4pc31AAqGUC86j3AplaFusc717EUGF9ZFJ356sdQzzZzkBk1XzMdxFyZ4f/i35NKM/lFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", "cpu": [ "s390x" ], @@ -4007,9 +4063,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.0.tgz", - "integrity": "sha512-WmFd5KINHIXj8o1mPaT8QRjA9HgSXhN1gl9Da4IZihARihEnOylu4co7i/yeaIpcfsI6sYs33cNZKyHYDh0lrA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", "cpu": [ "x64" ], @@ -4021,9 +4077,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.0.tgz", - "integrity": "sha512-CYuXbANW+WgzVRIl8/QvZmDaZxrqvOldOwlbUjIM4pQ46FJ0W5cinJ/Ghwa/Ng1ZPMJMk1VFdsD/XwmCGIXBWg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", "cpu": [ "x64" ], @@ -4035,9 +4091,9 @@ ] }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.0.tgz", - "integrity": "sha512-6Rp2WH0OoitMYR57Z6VE8Y6corX8C6QEMWLgOV6qXiJIeZ1F9WGXY/yQ8yDC4iTraotyLOeJ2Asea0urWj2fKQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", "cpu": [ "wasm32" ], @@ -4052,9 +4108,9 @@ } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.0.tgz", - "integrity": "sha512-rknkrTRuvujprrbPmGeHi8wYWxmNVlBoNW8+4XF2hXUnASOjmuC9FNF1tGbDiRQWn264q9U/oGtixyO3BT8adQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", "cpu": [ "arm64" ], @@ -4066,9 +4122,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.0.tgz", - "integrity": "sha512-Ceymm+iBl+bgAICtgiHyMLz6hjxmLJKqBim8tDzpX61wpZOx2bPK6Gjuor7I2RiUynVjvvkoRIkrPyMwzBzF3A==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", "cpu": [ "ia32" ], @@ -4080,9 +4136,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.0.tgz", - "integrity": "sha512-k59o9ZyeyS0hAlcaKFezYSH2agQeRFEB7KoQLXl3Nb3rgkqT1NY9Vwy+SqODiLmYnEjxWJVRE/yq2jFVqdIxZw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", "cpu": [ "x64" ], @@ -4094,23 +4150,26 @@ ] }, "node_modules/@vitejs/plugin-vue": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", - "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", "dev": true, "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", "vue": "^3.2.25" } }, "node_modules/@vitest/coverage-v8": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.3.tgz", - "integrity": "sha512-D1QKzngg8PcDoCE8FHSZhREDuEy+zcKmMiMafYse41RZpBE5EDJyKOTdqK3RQfsV2S2nyKor5KCs8PyPRFqKPg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", + "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4132,8 +4191,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.2.3", - "vitest": "3.2.3" + "@vitest/browser": "3.2.4", + "vitest": "3.2.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -4152,13 +4211,13 @@ } }, "node_modules/@vitest/eslint-plugin": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.2.2.tgz", - "integrity": "sha512-R8NwW+VxyKqVGcMfYsUbdThQyMbtNcoeg+jJeTgMHqWdFdcS0nrODAQXhkplvWzgd7jIJ+GQeydGqFLibsxMxg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.3.4.tgz", + "integrity": "sha512-EOg8d0jn3BAiKnR55WkFxmxfWA3nmzrbIIuOXyTe6A72duryNgyU+bdBEauA97Aab3ho9kLmAwgPX63Ckj4QEg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.24.0" + "@typescript-eslint/utils": "^8.24.1" }, "peerDependencies": { "eslint": ">= 8.57.0", @@ -4175,15 +4234,15 @@ } }, "node_modules/@vitest/expect": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.3.tgz", - "integrity": "sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.3", - "@vitest/utils": "3.2.3", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -4192,13 +4251,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.3.tgz", - "integrity": "sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.2.3", + "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -4246,9 +4305,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.3.tgz", - "integrity": "sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, "license": "MIT", "dependencies": { @@ -4259,13 +4318,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.3.tgz", - "integrity": "sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.2.3", + "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" }, @@ -4274,13 +4333,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.3.tgz", - "integrity": "sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.3", + "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -4299,9 +4358,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.3.tgz", - "integrity": "sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { @@ -4312,14 +4371,14 @@ } }, "node_modules/@vitest/utils": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.3.tgz", - "integrity": "sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.3", - "loupe": "^3.1.3", + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" }, "funding": { @@ -4327,42 +4386,42 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.16.tgz", - "integrity": "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", + "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.2", - "@vue/shared": "3.5.16", + "@babel/parser": "^7.28.0", + "@vue/shared": "3.5.18", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz", - "integrity": "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", + "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.16", - "@vue/shared": "3.5.16" + "@vue/compiler-core": "3.5.18", + "@vue/shared": "3.5.18" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz", - "integrity": "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", + "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.2", - "@vue/compiler-core": "3.5.16", - "@vue/compiler-dom": "3.5.16", - "@vue/compiler-ssr": "3.5.16", - "@vue/shared": "3.5.16", + "@babel/parser": "^7.28.0", + "@vue/compiler-core": "3.5.18", + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18", "estree-walker": "^2.0.2", "magic-string": "^0.30.17", - "postcss": "^8.5.3", + "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, @@ -4375,64 +4434,92 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz", - "integrity": "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==", + "node_modules/@vue/compiler-sfc/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.16", - "@vue/shared": "3.5.16" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", + "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/shared": "3.5.18" } }, "node_modules/@vue/reactivity": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.16.tgz", - "integrity": "sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz", + "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.16" + "@vue/shared": "3.5.18" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.16.tgz", - "integrity": "sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz", + "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.16", - "@vue/shared": "3.5.16" + "@vue/reactivity": "3.5.18", + "@vue/shared": "3.5.18" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.16.tgz", - "integrity": "sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", + "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.16", - "@vue/runtime-core": "3.5.16", - "@vue/shared": "3.5.16", + "@vue/reactivity": "3.5.18", + "@vue/runtime-core": "3.5.18", + "@vue/shared": "3.5.18", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.16.tgz", - "integrity": "sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz", + "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.16", - "@vue/shared": "3.5.16" + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18" }, "peerDependencies": { - "vue": "3.5.16" + "vue": "3.5.18" } }, "node_modules/@vue/shared": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.16.tgz", - "integrity": "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", + "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", "license": "MIT" }, "node_modules/@vue/test-utils": { @@ -4683,6 +4770,18 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -4972,13 +5071,13 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.4.tgz", + "integrity": "sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.29", "estree-walker": "^3.0.3", "js-tokens": "^9.0.1" } @@ -5155,9 +5254,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "version": "4.25.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", + "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", "funding": [ { "type": "opencollective", @@ -5174,8 +5273,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", + "caniuse-lite": "^1.0.30001733", + "electron-to-chromium": "^1.5.199", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -5257,24 +5356,24 @@ } }, "node_modules/cacheable": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.0.tgz", - "integrity": "sha512-SSgQTAnhd7WlJXnGlIi4jJJOiHzgnM5wRMEPaXAU4kECTAMpBoYKoZ9i5zHmclIEZbxcu3j7yY/CF8DTmwIsHg==", + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.4.tgz", + "integrity": "sha512-Gd7ccIUkZ9TE2odLQVS+PDjIvQCdJKUlLdJRVvZu0aipj07Qfx+XIej7hhDrKGGoIxV5m5fT/kOJNJPQhQneRg==", "dev": true, "license": "MIT", "dependencies": { - "hookified": "^1.8.2", - "keyv": "^5.3.3" + "hookified": "^1.11.0", + "keyv": "^5.5.0" } }, "node_modules/cacheable/node_modules/keyv": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.4.tgz", - "integrity": "sha512-ypEvQvInNpUe+u+w8BIcPkQvEqXquyyibWE/1NB5T2BTzIpS5cGEV1LZskDzPSTvNAaT4+5FutvzlvnkxOSKlw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.0.tgz", + "integrity": "sha512-QG7qR2tijh1ftOvClut4YKKg1iW6cx3GZsKoGyJPxHkGWK9oJhG9P3j5deP0QQOGDowBMVQFaP+Vm4NpGYvmIQ==", "dev": true, "license": "MIT", "dependencies": { - "@keyv/serialize": "^1.0.3" + "@keyv/serialize": "^1.1.0" } }, "node_modules/call-bind": { @@ -5346,9 +5445,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001723", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", - "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", + "version": "1.0.30001735", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", + "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", "funding": [ { "type": "opencollective", @@ -5366,9 +5465,9 @@ "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", + "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", "dev": true, "license": "MIT", "dependencies": { @@ -5379,7 +5478,7 @@ "pathval": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/chalk": { @@ -5398,6 +5497,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -5551,9 +5657,9 @@ } }, "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", "dev": true, "funding": [ { @@ -5652,9 +5758,9 @@ } }, "node_modules/codemirror": { - "version": "5.65.19", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.19.tgz", - "integrity": "sha512-+aFkvqhaAVr1gferNMuN8vkTSrWIFvzlMV9I2KBLCWS2WpZ2+UAkZjlMZmEuT+gcXTi6RrGQCkWq1/bDtGqhIA==", + "version": "5.65.20", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.20.tgz", + "integrity": "sha512-i5dLDDxwkFCbhjvL2pNjShsojoL3XHyDwsGv1jqETUoW+lzpBKKqNTUWgQwVAOa0tUm4BwekT455ujafi8payA==", "license": "MIT" }, "node_modules/codemirror-spell-checker": { @@ -5774,13 +5880,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.43.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", - "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", + "version": "3.45.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.0.tgz", + "integrity": "sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.25.0" + "browserslist": "^4.25.1" }, "funding": { "type": "opencollective", @@ -5894,9 +6000,9 @@ } }, "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5925,9 +6031,9 @@ } }, "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -5992,9 +6098,9 @@ "license": "MIT" }, "node_modules/cytoscape": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.32.0.tgz", - "integrity": "sha512-5JHBC9n75kz5851jeklCPmZWcg3hUe6sjqJvyk3+hVqFaKcHwHgxsjeN1yLmggoUc6STbtm9/NQyabQehfjvWQ==", + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", "engines": { "node": ">=0.10" @@ -6916,9 +7022,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.167", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", - "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", + "version": "1.5.203", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz", + "integrity": "sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -6937,9 +7043,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -7170,9 +7276,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", - "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -7182,31 +7288,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/esbuild-loader": { @@ -7262,20 +7369,20 @@ } }, "node_modules/eslint": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", - "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.1", - "@eslint/core": "^0.14.0", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.28.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -7286,9 +7393,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -7339,14 +7446,14 @@ } }, "node_modules/eslint-import-context": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.8.tgz", - "integrity": "sha512-bq+F7nyc65sKpZGT09dY0S0QrOnQtuDVIfyTGQ8uuvtMIF7oHp6CEP3mouN0rrnYF3Jqo6Ke0BfU/5wASZue1w==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz", + "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==", "dev": true, "license": "MIT", "dependencies": { "get-tsconfig": "^4.10.1", - "stable-hash-x": "^0.1.1" + "stable-hash-x": "^0.2.0" }, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" @@ -7364,9 +7471,9 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.3.tgz", - "integrity": "sha512-elVDn1eWKFrWlzxlWl9xMt8LltjKl161Ix50JFC50tHXI5/TRP32SNEqlJ/bo/HV+g7Rou/tlPQU2AcRtIhrOg==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz", + "integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==", "dev": true, "license": "ISC", "dependencies": { @@ -7374,7 +7481,7 @@ "eslint-import-context": "^0.1.8", "get-tsconfig": "^4.10.1", "is-bun-module": "^2.0.0", - "stable-hash-x": "^0.1.1", + "stable-hash-x": "^0.2.0", "tinyglobby": "^0.2.14", "unrs-resolver": "^1.7.11" }, @@ -7412,21 +7519,21 @@ } }, "node_modules/eslint-plugin-import-x": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.15.1.tgz", - "integrity": "sha512-JfVpNg1qMkPD66iaSgmMoSYeUCGS8UFSm3GwHV0IbuV3Knar/SyK5qqCct9+AxoMIzaM+KSO7KK5pOeOkC/3GQ==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz", + "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "^8.33.1", + "@typescript-eslint/types": "^8.35.0", "comment-parser": "^1.4.1", "debug": "^4.4.1", - "eslint-import-context": "^0.1.7", + "eslint-import-context": "^0.1.9", "is-glob": "^4.0.3", "minimatch": "^9.0.3 || ^10.0.1", "semver": "^7.7.2", - "stable-hash-x": "^0.1.1", - "unrs-resolver": "^1.7.10" + "stable-hash-x": "^0.2.0", + "unrs-resolver": "^1.9.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7478,14 +7585,11 @@ } }, "node_modules/eslint-plugin-playwright": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.2.0.tgz", - "integrity": "sha512-qSQpAw7RcSzE3zPp8FMGkthaCWovHZ/BsXtpmnGax9vQLIovlh1bsZHEa2+j2lv9DWhnyeLM/qZmp7ffQZfQvg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.2.2.tgz", + "integrity": "sha512-j0jKpndIPOXRRP9uMkwb9l/nSmModOU3452nrFdgFJoEv/435J1onk8+aITzjDW8DfypxgmVaDMdmVIa6F7I0w==", "dev": true, "license": "MIT", - "workspaces": [ - "examples" - ], "dependencies": { "globals": "^13.23.0" }, @@ -7513,9 +7617,9 @@ } }, "node_modules/eslint-plugin-regexp": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.9.0.tgz", - "integrity": "sha512-9WqJMnOq8VlE/cK+YAo9C9YHhkOtcEtEk9d12a+H7OSZFwlpI6stiHmYPGa2VE0QhTzodJyhlyprUaXDZLgHBw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.10.0.tgz", + "integrity": "sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng==", "dev": true, "license": "MIT", "dependencies": { @@ -7535,9 +7639,9 @@ } }, "node_modules/eslint-plugin-sonarjs": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-3.0.2.tgz", - "integrity": "sha512-LxjbfwI7ypENeTmGyKmDyNux3COSkMi7H/6Cal5StSLQ6edf0naP45SZR43OclaNR7WfhVTZdhOn63q3/Y6puQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-3.0.4.tgz", + "integrity": "sha512-ftQcP811kRJNXapqpQXHErEoVOdTPfYPPYd7n3AExIPwv4qWKKHf4slFvXmodiOnfgy1Tl3waPZZLD7lcvJOtw==", "dev": true, "license": "LGPL-3.0-only", "dependencies": { @@ -7546,10 +7650,11 @@ "bytes": "3.1.2", "functional-red-black-tree": "1.0.1", "jsx-ast-utils": "3.3.5", + "lodash.merge": "4.6.2", "minimatch": "9.0.5", "scslre": "0.3.0", - "semver": "7.7.1", - "typescript": "^5" + "semver": "7.7.2", + "typescript": ">=5" }, "peerDependencies": { "eslint": "^8.0.0 || ^9.0.0" @@ -7588,19 +7693,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-plugin-sonarjs/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-plugin-toml": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/eslint-plugin-toml/-/eslint-plugin-toml-0.12.0.tgz", @@ -7624,65 +7716,39 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", + "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", + "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", - "globals": "^16.0.0", + "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" + "node": "^20.10.0 || >=21.0.0" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "eslint": ">=9.29.0" } }, "node_modules/eslint-plugin-vitest-globals": { @@ -7693,9 +7759,9 @@ "license": "MIT" }, "node_modules/eslint-plugin-vue": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.2.0.tgz", - "integrity": "sha512-tl9s+KN3z0hN2b8fV2xSs5ytGl7Esk1oSCxULLwFcdaElhZ8btYYZFrWxvh4En+czrSDtuLCeCOGa8HhEZuBdQ==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.4.0.tgz", + "integrity": "sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw==", "dev": true, "license": "MIT", "dependencies": { @@ -7710,24 +7776,30 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "vue-eslint-parser": "^10.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/parser": { + "optional": true + } } }, "node_modules/eslint-plugin-vue-scoped-css": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.10.0.tgz", - "integrity": "sha512-oH2NY7XFHF3EGOotvuPdnhB0x4uOmjoRoWZVfMnJ2PILDKVgZgM8WZ0rhDlh+fsr9jO9P8CAXO5/9s9v/GZNhg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.11.0.tgz", + "integrity": "sha512-rrJgLY8iroTIUMSyxhyhJzFcRxABbk3gFrOLkl41F9G1VBqNNpDShyf6PmDoBEWDk07/bJlnqYlvnQ3giUrRYQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "eslint-compat-utils": "^0.6.0", + "eslint-compat-utils": "^0.6.5", "lodash": "^4.17.21", "postcss": "^8.4.31", "postcss-safe-parser": "^6.0.0", "postcss-scss": "^4.0.3", - "postcss-selector-parser": "^6.0.9", + "postcss-selector-parser": "^7.0.0", "postcss-styl": "^0.12.0" }, "engines": { @@ -7741,6 +7813,20 @@ "vue-eslint-parser": ">=7.1.0" } }, + "node_modules/eslint-plugin-vue/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/eslint-plugin-wc": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-3.0.1.tgz", @@ -7917,9 +8003,9 @@ } }, "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -7927,9 +8013,9 @@ } }, "node_modules/exsolve": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", - "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", "license": "MIT" }, "node_modules/fast-deep-equal": { @@ -8464,9 +8550,9 @@ } }, "node_modules/globals": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", - "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, "license": "MIT", "engines": { @@ -8563,9 +8649,9 @@ } }, "node_modules/happy-dom": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-18.0.0.tgz", - "integrity": "sha512-o3p2Axi1EdIfMaOUulDzO/5yXzLLV0g/54eLPVrkt3u20r3yOuOenHpyp2clAJ0eHMc+HyE139ulQxl+8pEJIw==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-18.0.1.tgz", + "integrity": "sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==", "dev": true, "license": "MIT", "dependencies": { @@ -8676,9 +8762,9 @@ } }, "node_modules/hookified": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.9.1.tgz", - "integrity": "sha512-u3pxtGhKjcSXnGm1CX6aXS9xew535j3lkOCegbA6jdyh0BaAjTbXI4aslKstCr6zUNtoCxFGFKwjbSHdGrMB8g==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.11.0.tgz", + "integrity": "sha512-aDdIN3GyU5I6wextPplYdfmWCo+aLmjjVbntmX6HLD5RCi/xKsivYEBhnRD+d9224zFf008ZpLMPlWF0ZodYZw==", "dev": true, "license": "MIT" }, @@ -8746,9 +8832,9 @@ } }, "node_modules/htmx.org": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.12.tgz", - "integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.6.tgz", + "integrity": "sha512-7ythjYneGSk3yCHgtCnQeaoF+D+o7U2LF37WU3O0JYv3gTZSicdEFiI/Ai/NJyC5ZpYJWMpUb11OC5Lr6AfAqA==", "license": "0BSD" }, "node_modules/iconv-lite": { @@ -9802,9 +9888,9 @@ } }, "node_modules/jsdoc-type-pratt-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", - "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.8.0.tgz", + "integrity": "sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==", "dev": true, "license": "MIT", "engines": { @@ -9880,9 +9966,9 @@ "license": "MIT" }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, "license": "MIT", "dependencies": { @@ -9993,9 +10079,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.36.0.tgz", - "integrity": "sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz", + "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==", "dev": true, "license": "MIT" }, @@ -10116,9 +10202,9 @@ } }, "node_modules/lit": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.0.tgz", - "integrity": "sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.1.tgz", + "integrity": "sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==", "license": "BSD-3-Clause", "dependencies": { "@lit/reactive-element": "^2.1.0", @@ -10127,20 +10213,20 @@ } }, "node_modules/lit-element": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.0.tgz", - "integrity": "sha512-MGrXJVAI5x+Bfth/pU9Kst1iWID6GHDLEzFEnyULB/sFiRLgkd8NPK/PeeXxktA3T6EIIaq8U3KcbTU5XFcP2Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.1.tgz", + "integrity": "sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==", "license": "BSD-3-Clause", "dependencies": { - "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit-labs/ssr-dom-shim": "^1.4.0", "@lit/reactive-element": "^2.1.0", "lit-html": "^3.3.0" } }, "node_modules/lit-html": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.0.tgz", - "integrity": "sha512-RHoswrFAxY2d8Cf2mm4OZ1DgzCoBKUKSPvA1fhtSELxUERq2aQQ2h05pO9j81gS1o7RIRJ+CePLogfyahwmynw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.1.tgz", + "integrity": "sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==", "license": "BSD-3-Clause", "dependencies": { "@types/trusted-types": "^2.0.2" @@ -10251,9 +10337,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", "dev": true, "license": "MIT" }, @@ -10562,14 +10648,14 @@ } }, "node_modules/mermaid": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", - "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", + "version": "11.8.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.8.1.tgz", + "integrity": "sha512-VSXJLqP1Sqw5sGr273mhvpPRhXwE6NlmMSqBZQw+yZJoAJkOIPPn/uT3teeCBx60Fkt5zEI3FrH2eVT0jXRDzw==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.4", "@iconify/utils": "^2.1.33", - "@mermaid-js/parser": "^0.4.0", + "@mermaid-js/parser": "^0.6.1", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", @@ -10578,7 +10664,7 @@ "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.13", - "dompurify": "^3.2.4", + "dompurify": "^3.2.5", "katex": "^0.16.9", "khroma": "^2.1.0", "lodash-es": "^4.17.21", @@ -11188,9 +11274,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", - "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.3.tgz", + "integrity": "sha512-tRA0+PsS4kLVijnN1w9jUu5lkxBwUk9E8SbgEB5dBJqchE6pVYdawROG6uQtpmAri7tdCK9i7b1bULeVWqS6Ag==", "license": "MIT", "dependencies": { "schema-utils": "^4.0.0", @@ -11344,9 +11430,9 @@ } }, "node_modules/napi-postinstall": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.4.tgz", - "integrity": "sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", + "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", "dev": true, "license": "MIT", "bin": { @@ -11827,9 +11913,9 @@ "license": "MIT" }, "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { @@ -11943,24 +12029,24 @@ } }, "node_modules/pkg-types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz", - "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", + "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", "license": "MIT", "dependencies": { - "confbox": "^0.2.1", - "exsolve": "^1.0.1", + "confbox": "^0.2.2", + "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "node_modules/playwright": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", - "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz", + "integrity": "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.52.0" + "playwright-core": "1.54.2" }, "bin": { "playwright": "cli.js" @@ -11973,9 +12059,9 @@ } }, "node_modules/playwright-core": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", - "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.2.tgz", + "integrity": "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -12171,19 +12257,6 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-modules-scope": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", @@ -12199,19 +12272,6 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-modules-values": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", @@ -12252,6 +12312,19 @@ "postcss": "^8.2.14" } }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-nesting": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", @@ -12279,63 +12352,6 @@ "postcss": "^8.4" } }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", - "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-resolve-nested-selector": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", @@ -12388,9 +12404,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -12509,9 +12525,9 @@ } }, "node_modules/quansync": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz", - "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", "funding": [ { "type": "individual", @@ -13213,9 +13229,9 @@ } }, "node_modules/sharp": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", - "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -13231,27 +13247,28 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.2", - "@img/sharp-darwin-x64": "0.34.2", - "@img/sharp-libvips-darwin-arm64": "1.1.0", - "@img/sharp-libvips-darwin-x64": "1.1.0", - "@img/sharp-libvips-linux-arm": "1.1.0", - "@img/sharp-libvips-linux-arm64": "1.1.0", - "@img/sharp-libvips-linux-ppc64": "1.1.0", - "@img/sharp-libvips-linux-s390x": "1.1.0", - "@img/sharp-libvips-linux-x64": "1.1.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", - "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.2", - "@img/sharp-linux-arm64": "0.34.2", - "@img/sharp-linux-s390x": "0.34.2", - "@img/sharp-linux-x64": "0.34.2", - "@img/sharp-linuxmusl-arm64": "0.34.2", - "@img/sharp-linuxmusl-x64": "0.34.2", - "@img/sharp-wasm32": "0.34.2", - "@img/sharp-win32-arm64": "0.34.2", - "@img/sharp-win32-ia32": "0.34.2", - "@img/sharp-win32-x64": "0.34.2" + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" } }, "node_modules/shebang-command": { @@ -13452,9 +13469,9 @@ } }, "node_modules/solid-js": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.7.tgz", - "integrity": "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==", + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.9.tgz", + "integrity": "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==", "license": "MIT", "dependencies": { "csstype": "^3.1.0", @@ -13564,9 +13581,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", "dev": true, "license": "CC0-1.0" }, @@ -13590,9 +13607,9 @@ } }, "node_modules/stable-hash-x": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.1.1.tgz", - "integrity": "sha512-l0x1D6vhnsNUGPFVDx45eif0y6eedVC8nm5uACTrVFJFtl2mLRW17aWtVyxFCpn5t94VUPkjU8vSLwIuwwqtJQ==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", + "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==", "dev": true, "license": "MIT", "engines": { @@ -13801,9 +13818,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.20.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.20.0.tgz", - "integrity": "sha512-B5Myu9WRxrgKuLs3YyUXLP2H0mrbejwNxPmyADlACWwFsrL8Bmor/nTSh4OMae5sHjOz6gkSeccQH34gM4/nAw==", + "version": "16.23.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.23.1.tgz", + "integrity": "sha512-dNvDTsKV1U2YtiUDfe9d2gp902veFeo3ecCWdGlmLm2WFrAV0+L5LoOj/qHSBABQwMsZPJwfC4bf39mQm1S5zw==", "dev": true, "funding": [ { @@ -13817,9 +13834,9 @@ ], "license": "MIT", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3", "@csstools/selector-specificity": "^5.0.0", "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", @@ -13830,21 +13847,21 @@ "debug": "^4.4.1", "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^10.1.0", + "file-entry-cache": "^10.1.3", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^7.0.4", + "ignore": "^7.0.5", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.36.0", + "known-css-properties": "^0.37.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", - "postcss": "^8.5.3", + "postcss": "^8.5.6", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.0", @@ -13906,73 +13923,26 @@ "stylelint": ">=16" } }, - "node_modules/stylelint/node_modules/@csstools/media-query-list-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", - "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/stylelint/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.1.tgz", - "integrity": "sha512-zcmsHjg2B2zjuBgjdnB+9q0+cWcgWfykIcsDkWDB4GTPtl1eXUA+gTI6sO0u01AqK3cliHryTU55/b2Ow1hfZg==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.4.tgz", + "integrity": "sha512-5XRUFc0WTtUbjfGzEwXc42tiGxQHBmtbUG1h9L2apu4SulCGN3Hqm//9D6FAolf8MYNL7f/YlJl9vy08pj5JuA==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^6.1.10" + "flat-cache": "^6.1.13" } }, "node_modules/stylelint/node_modules/flat-cache": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.10.tgz", - "integrity": "sha512-B6/v1f0NwjxzmeOhzfXPGWpKBVA207LS7lehaVKQnFrVktcFRfkzjZZ2gwj2i1TkEUMQht7ZMJbABUT5N+V1Nw==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.13.tgz", + "integrity": "sha512-gmtS2PaUjSPa4zjObEIn4WWliKyZzYljgxODBfxugpK6q6HU9ClXzgCJ+nlcPKY9Bt090ypTOLIFWkV0jbKFjw==", "dev": true, "license": "MIT", "dependencies": { - "cacheable": "^1.10.0", + "cacheable": "^1.10.4", "flatted": "^3.3.3", - "hookified": "^1.9.1" + "hookified": "^1.11.0" } }, "node_modules/stylelint/node_modules/ignore": { @@ -13985,6 +13955,35 @@ "node": ">= 4" } }, + "node_modules/stylelint/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/stylelint/node_modules/postcss-safe-parser": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", @@ -14012,20 +14011,6 @@ "postcss": "^8.4.31" } }, - "node_modules/stylelint/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/stylelint/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -14064,13 +14049,13 @@ } }, "node_modules/stylus/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">= 8" + "node": ">= 12" } }, "node_modules/sucrase": { @@ -14202,25 +14187,25 @@ "dev": true }, "node_modules/svgo": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", - "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz", + "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==", "dev": true, "license": "MIT", "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", + "commander": "^11.1.0", "css-select": "^5.1.0", - "css-tree": "^2.3.1", + "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1", + "sax": "^1.4.1" }, "bin": { - "svgo": "bin/svgo" + "svgo": "bin/svgo.js" }, "engines": { - "node": ">=14.0.0" + "node": ">=16" }, "funding": { "type": "opencollective", @@ -14228,35 +14213,21 @@ } }, "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=16" } }, - "node_modules/svgo/node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "node_modules/svgo/node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/svgo/node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true, - "license": "CC0-1.0" + "license": "ISC" }, "node_modules/swagger-ui-dist": { "version": "5.17.14", @@ -14366,6 +14337,19 @@ } } }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/tapable": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", @@ -14376,9 +14360,9 @@ } }, "node_modules/terser": { - "version": "5.42.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.42.0.tgz", - "integrity": "sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==", + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -14583,11 +14567,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -14598,9 +14585,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -14611,9 +14598,9 @@ } }, "node_modules/tinypool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz", - "integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -14872,15 +14859,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.0.tgz", - "integrity": "sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz", + "integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", - "@typescript-eslint/utils": "8.34.0" + "@typescript-eslint/eslint-plugin": "8.39.0", + "@typescript-eslint/parser": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14891,13 +14879,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typo-js": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.5.tgz", - "integrity": "sha512-F45vFWdGX8xahIk/sOp79z2NJs8ETMYsmMChm9D5Hlx3+9j7VnCyQyvij5MOCrNY3NNe8noSyokRjQRfq+Bc7A==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.3.1.tgz", + "integrity": "sha512-elJkpCL6Z77Ghw0Lv0lGnhBAjSTOQ5FhiVOCfOuxhaoTT2xtLVbqikYItK5HHchzPbHEUFAcjOH669T2ZzeCbg==", "license": "BSD-3-Clause" }, "node_modules/uc.micro": { @@ -14955,38 +14943,38 @@ } }, "node_modules/unrs-resolver": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.0.tgz", - "integrity": "sha512-wqaRu4UnzBD2ABTC1kLfBjAqIDZ5YUTr/MLGa7By47JV1bJDSW7jq/ZSLigB7enLe7ubNaJhtnBXgrc/50cEhg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "napi-postinstall": "^0.2.2" + "napi-postinstall": "^0.3.0" }, "funding": { "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.9.0", - "@unrs/resolver-binding-android-arm64": "1.9.0", - "@unrs/resolver-binding-darwin-arm64": "1.9.0", - "@unrs/resolver-binding-darwin-x64": "1.9.0", - "@unrs/resolver-binding-freebsd-x64": "1.9.0", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.0", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.0", - "@unrs/resolver-binding-linux-arm64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-arm64-musl": "1.9.0", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-riscv64-musl": "1.9.0", - "@unrs/resolver-binding-linux-s390x-gnu": "1.9.0", - "@unrs/resolver-binding-linux-x64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-x64-musl": "1.9.0", - "@unrs/resolver-binding-wasm32-wasi": "1.9.0", - "@unrs/resolver-binding-win32-arm64-msvc": "1.9.0", - "@unrs/resolver-binding-win32-ia32-msvc": "1.9.0", - "@unrs/resolver-binding-win32-x64-msvc": "1.9.0" + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "node_modules/update-browserslist-db": { @@ -15093,24 +15081,24 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz", + "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -15119,14 +15107,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" @@ -15168,9 +15156,9 @@ } }, "node_modules/vite-node": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.3.tgz", - "integrity": "sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { @@ -15191,25 +15179,28 @@ } }, "node_modules/vite-string-plugin": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.3.4.tgz", - "integrity": "sha512-mHvcooHgZ0nVbHtj9o+c5dzD2/nclr/SOG023EFYF/zRnO8bxB63bV9WUA9X+njlgLpOwCJ3LI2IdihKoi0gZQ==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.4.6.tgz", + "integrity": "sha512-Csjtny8/uVIynzlaRRj4RpHrPAakNwlH9jw6kgQ8tQhc2f0zzA6bCbAgWD0y84EgB8aLNrz7pZFUqSt3LOtk+w==", "dev": true, "license": "BSD-2-Clause" }, "node_modules/vite/node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/vite/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -15235,9 +15226,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -15247,14 +15238,43 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/vite/node_modules/rollup": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz", - "integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -15264,44 +15284,44 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.43.0", - "@rollup/rollup-android-arm64": "4.43.0", - "@rollup/rollup-darwin-arm64": "4.43.0", - "@rollup/rollup-darwin-x64": "4.43.0", - "@rollup/rollup-freebsd-arm64": "4.43.0", - "@rollup/rollup-freebsd-x64": "4.43.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.43.0", - "@rollup/rollup-linux-arm-musleabihf": "4.43.0", - "@rollup/rollup-linux-arm64-gnu": "4.43.0", - "@rollup/rollup-linux-arm64-musl": "4.43.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.43.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-musl": "4.43.0", - "@rollup/rollup-linux-s390x-gnu": "4.43.0", - "@rollup/rollup-linux-x64-gnu": "4.43.0", - "@rollup/rollup-linux-x64-musl": "4.43.0", - "@rollup/rollup-win32-arm64-msvc": "4.43.0", - "@rollup/rollup-win32-ia32-msvc": "4.43.0", - "@rollup/rollup-win32-x64-msvc": "4.43.0", + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", "fsevents": "~2.3.2" } }, "node_modules/vitest": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.3.tgz", - "integrity": "sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.3", - "@vitest/mocker": "3.2.3", - "@vitest/pretty-format": "^3.2.3", - "@vitest/runner": "3.2.3", - "@vitest/snapshot": "3.2.3", - "@vitest/spy": "3.2.3", - "@vitest/utils": "3.2.3", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", @@ -15312,10 +15332,10 @@ "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", - "tinypool": "^1.1.0", + "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.3", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -15331,8 +15351,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.3", - "@vitest/ui": "3.2.3", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, @@ -15371,9 +15391,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -15440,16 +15460,16 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.16.tgz", - "integrity": "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz", + "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.16", - "@vue/compiler-sfc": "3.5.16", - "@vue/runtime-dom": "3.5.16", - "@vue/server-renderer": "3.5.16", - "@vue/shared": "3.5.16" + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-sfc": "3.5.18", + "@vue/runtime-dom": "3.5.18", + "@vue/server-renderer": "3.5.18", + "@vue/shared": "3.5.18" }, "peerDependencies": { "typescript": "*" @@ -15471,16 +15491,16 @@ } }, "node_modules/vue-component-type-helpers": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.10.tgz", - "integrity": "sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==", + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz", + "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==", "dev": true, "license": "MIT" }, "node_modules/vue-eslint-parser": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.1.3.tgz", - "integrity": "sha512-dbCBnd2e02dYWsXoqX5yKUZlOt+ExIpq7hmHKPb5ZqKcjf++Eo0hMseFTZMLKThrUk61m+Uv6A2YSBve6ZvuDQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz", + "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==", "dev": true, "license": "MIT", "peer": true, @@ -15490,7 +15510,6 @@ "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.6.0", - "lodash": "^4.17.21", "semver": "^7.6.3" }, "engines": { @@ -15558,21 +15577,22 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.99.9", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", - "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", + "version": "5.101.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.1.tgz", + "integrity": "sha512-rHY3vHXRbkSfhG6fH8zYQdth/BtDgXXuR2pHF++1f/EBkI8zkgM5XWfsC3BvOoW9pr1CvZ1qQCxhCEsbNgT50g==", "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", + "enhanced-resolve": "^5.17.3", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -15586,7 +15606,7 @@ "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -15708,9 +15728,9 @@ } }, "node_modules/webpack/node_modules/webpack-sources": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz", - "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "license": "MIT", "engines": { "node": ">=10.13.0" @@ -15909,9 +15929,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "license": "MIT", "engines": { "node": ">=12" @@ -16012,9 +16032,9 @@ } }, "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index f7df1b3f38..b2cd765ed3 100644 --- a/package.json +++ b/package.json @@ -27,12 +27,12 @@ "esbuild-loader": "4.3.0", "escape-goat": "4.0.0", "fast-glob": "3.3.3", - "htmx.org": "1.9.12", + "htmx.org": "2.0.6", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.6.0", - "mini-css-extract-plugin": "2.9.2", + "mermaid": "11.8.1", + "mini-css-extract-plugin": "2.9.3", "minimatch": "10.0.3", "monaco-editor": "0.52.2", "monaco-editor-webpack-plugin": "7.1.0", @@ -51,55 +51,55 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.5.16", + "vue": "3.5.18", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", - "webpack": "5.99.9", + "webpack": "5.101.1", "webpack-cli": "6.0.1", "wrap-ansi": "9.0.0" }, "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.52.0", + "@playwright/test": "1.54.2", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "4.4.1", - "@stylistic/stylelint-plugin": "3.1.2", - "@vitejs/plugin-vue": "5.2.4", - "@vitest/coverage-v8": "3.2.3", - "@vitest/eslint-plugin": "1.2.2", + "@stylistic/eslint-plugin": "5.2.3", + "@stylistic/stylelint-plugin": "4.0.0", + "@vitejs/plugin-vue": "6.0.1", + "@vitest/coverage-v8": "3.2.4", + "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", - "eslint": "9.28.0", - "eslint-import-resolver-typescript": "4.4.3", + "eslint": "9.33.0", + "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", - "eslint-plugin-import-x": "4.15.1", + "eslint-plugin-import-x": "4.16.1", "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", - "eslint-plugin-playwright": "2.2.0", - "eslint-plugin-regexp": "2.9.0", - "eslint-plugin-sonarjs": "3.0.2", + "eslint-plugin-playwright": "2.2.2", + "eslint-plugin-regexp": "2.10.0", + "eslint-plugin-sonarjs": "3.0.4", "eslint-plugin-toml": "0.12.0", - "eslint-plugin-unicorn": "59.0.1", + "eslint-plugin-unicorn": "60.0.0", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "10.2.0", - "eslint-plugin-vue-scoped-css": "2.10.0", + "eslint-plugin-vue": "10.4.0", + "eslint-plugin-vue-scoped-css": "2.11.0", "eslint-plugin-wc": "3.0.1", - "globals": "16.1.0", - "happy-dom": "18.0.0", + "globals": "16.3.0", + "happy-dom": "18.0.1", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", - "sharp": "0.34.2", - "stylelint": "16.20.0", + "sharp": "0.34.3", + "stylelint": "16.23.1", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", - "svgo": "3.2.0", + "svgo": "4.0.0", "typescript": "5.8.3", - "typescript-eslint": "8.34.0", - "vite-string-plugin": "1.3.4", - "vitest": "3.2.3" + "typescript-eslint": "8.39.0", + "vite-string-plugin": "1.4.6", + "vitest": "3.2.4" }, "browserslist": [ "defaults" diff --git a/public/assets/img/svg/gitea-alt.svg b/public/assets/img/svg/gitea-alt.svg index 53e3f17c13..efe4830a0b 100644 --- a/public/assets/img/svg/gitea-alt.svg +++ b/public/assets/img/svg/gitea-alt.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-chef.svg b/public/assets/img/svg/gitea-chef.svg index c5e8a721cc..8fd8ed325d 100644 --- a/public/assets/img/svg/gitea-chef.svg +++ b/public/assets/img/svg/gitea-chef.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-debian.svg b/public/assets/img/svg/gitea-debian.svg index fa2f2f49dc..e92d0b6937 100644 --- a/public/assets/img/svg/gitea-debian.svg +++ b/public/assets/img/svg/gitea-debian.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-gitbucket.svg b/public/assets/img/svg/gitea-gitbucket.svg index 62f603484e..b9e99724b2 100644 --- a/public/assets/img/svg/gitea-gitbucket.svg +++ b/public/assets/img/svg/gitea-gitbucket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-gitlab.svg b/public/assets/img/svg/gitea-gitlab.svg index 03fcb0b87e..e2d708e7be 100644 --- a/public/assets/img/svg/gitea-gitlab.svg +++ b/public/assets/img/svg/gitea-gitlab.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-google.svg b/public/assets/img/svg/gitea-google.svg index 7dd2622df6..26ee04cb64 100644 --- a/public/assets/img/svg/gitea-google.svg +++ b/public/assets/img/svg/gitea-google.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-maven.svg b/public/assets/img/svg/gitea-maven.svg index 320d01a234..f6ece7dc28 100644 --- a/public/assets/img/svg/gitea-maven.svg +++ b/public/assets/img/svg/gitea-maven.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-microsoftonline.svg b/public/assets/img/svg/gitea-microsoftonline.svg index f2ce13ac22..c143eccbb6 100644 --- a/public/assets/img/svg/gitea-microsoftonline.svg +++ b/public/assets/img/svg/gitea-microsoftonline.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-npm.svg b/public/assets/img/svg/gitea-npm.svg index 7ef74e72bd..2b05c79353 100644 --- a/public/assets/img/svg/gitea-npm.svg +++ b/public/assets/img/svg/gitea-npm.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-onedev.svg b/public/assets/img/svg/gitea-onedev.svg index 94ad1bab31..7ecd18895d 100644 --- a/public/assets/img/svg/gitea-onedev.svg +++ b/public/assets/img/svg/gitea-onedev.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-openid.svg b/public/assets/img/svg/gitea-openid.svg index f4702d2cdf..10c37145a3 100644 --- a/public/assets/img/svg/gitea-openid.svg +++ b/public/assets/img/svg/gitea-openid.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-pagure.svg b/public/assets/img/svg/gitea-pagure.svg new file mode 100644 index 0000000000..d9dbd31f88 --- /dev/null +++ b/public/assets/img/svg/gitea-pagure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-rubygems.svg b/public/assets/img/svg/gitea-rubygems.svg index 4e43bdf2f4..7cd9d34e6a 100644 --- a/public/assets/img/svg/gitea-rubygems.svg +++ b/public/assets/img/svg/gitea-rubygems.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-swift.svg b/public/assets/img/svg/gitea-swift.svg index 4182100185..891ac12b56 100644 --- a/public/assets/img/svg/gitea-swift.svg +++ b/public/assets/img/svg/gitea-swift.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-vagrant.svg b/public/assets/img/svg/gitea-vagrant.svg index ba50101d52..18b05e900d 100644 --- a/public/assets/img/svg/gitea-vagrant.svg +++ b/public/assets/img/svg/gitea-vagrant.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/release-notes-assistant.sh b/release-notes-assistant.sh index 89fd0833f5..4963edf286 100755 --- a/release-notes-assistant.sh +++ b/release-notes-assistant.sh @@ -64,7 +64,7 @@ function test_main() { test "$(categorize)" = 'ZE Other changes without a feature or bug label' test_payload_labels - test "$(categorize)" = 'ZF Included for completeness but not worth a release note' + test "$(categorize)" = 'ZF Included for completeness but not user-facing (chores, etc.)' test_payload_draft "fix(security)!: breaking security bug fix" test "$(categorize)" = 'AA Breaking security bug fixes' @@ -117,11 +117,12 @@ function categorize() { # # If there was no release-notes/N.md file and it is not - # worth a release note, just forget about it. + # directly user-facing, we include it in a separate section + # for completeness. # if test -z "$(jq --raw-output .Draft <$payload)"; then if ! $worth; then - echo -n ZF Included for completeness but not worth a release note + echo -n ZF Included for completeness but not user-facing \(chores, etc.\) exit 0 fi fi diff --git a/release-notes-published/11.0.3.md b/release-notes-published/11.0.3.md new file mode 100644 index 0000000000..ed6f150af9 --- /dev/null +++ b/release-notes-published/11.0.3.md @@ -0,0 +1,34 @@ +## Git update fixing CVE-2025-48385 + +Git vulnerabilities were [disclosed 8 July 2025](https://groups.google.com/g/git-packagers/c/cYJ6peBtyxk/m/xVukiATcBQAJ) and require an update of the Git version used by Forgejo to Git [v2.43.7, v2.44.4, v2.45.4, v2.46.4, v2.47.3, v2.48.2, v2.49.1, or v2.50.1](https://nvd.nist.gov/vuln/detail/CVE-2025-48385). The [containers of this release](https://codeberg.org/forgejo/-/packages/container/forgejo/11.0.3) include a Git binary that is not vulnerable. If Forgejo was installed using a container, it is enough to upgrade the container to get the latest Git binary. + +Security bug fixes are only for Git, there are no security fixes for Forgejo itself in this release. + +## Wiki permissions manual steps + +If collaborators with write access can't edit the wiki, an administrator can now go to the Units settings (`//settings/units#wiki`) and Save the wiki settings (no change is needed) to fix the problem. This is a manual step that will trigger a database update that is currently not possible to automate for Forgejo stable releases. + + + +## Release notes + +- User Interface bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8246) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8283)): fix(ui): add missing lazy load attribute to images (#8246) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8170) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8262)): fix(ui): erroneous list continuation on Cmd+Enter on macOS +- Localization + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8300): i18n: backport of translation updates +- Bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8189) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8456)): fix: do not ignore automerge while a PR is checking for conflicts + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8367) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8385)): fix: user activation with uppercase email address + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8234) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8237)): fix: collaborator can edit wiki with write access +- Included for completeness but not worth a release note + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8460) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8465)): chore: disable mismatched root URL e2e test for safari + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8461) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8462)): chore: do not navigate to same URL in E2E test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8258) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8445)): fix: corrupted wiki unit default permission (#8234 follow-up) (#8258) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8261) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8412)): fix: skip empty tokens in SearchOptions.Tokens() + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8400) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8401)): chore: improve reliability of webauthn e2e test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8326) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8333)): fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8226) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8292)): chore: sort mailer messages in test assertion + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8002) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8289)): fix(ui): release: name is overridden with tag name on edit + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8286) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8287)): Revert "fix(api): document `is_system_webhook` field (#7784)" + diff --git a/release-notes-published/12.0.0.md b/release-notes-published/12.0.0.md new file mode 100644 index 0000000000..f52480cc88 --- /dev/null +++ b/release-notes-published/12.0.0.md @@ -0,0 +1,464 @@ + + + + +## Release notes + +- Breaking security features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7924): remove API authentication methods that uses the URL query. They are disabled by default and this only has an impact if `[security].DISABLE_QUERY_AUTH_TOKEN=false` is explicitly set. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#removing-deprecated-api-authentication-methods). +- Security features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7829): relax email requirements. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#relaxing-the-requirements-on-email-addresses). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7693): consider WebAuthn & SSH for instance signing. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6897): add SSH signing support for instances. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#instance-signing-with-ssh). +- Breaking features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8035): The `forgejo docs` command is deprecated and CLI errors are now displayed on stderr instead of stdout. These breaking changes happened because the package used to parse the command line arguments was [upgraded from v2 to v3](https://cli.urfave.org/migrate-v2-to-v3/). A [separate project was initiated](https://github.com/urfave/cli-docs) to re-implement the `docs` command, but it is not yet production ready. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7745): remove the legacy `TEST_CONFLICTING_PATCHES_WITH_GIT_APPLY` setting +- Breaking bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8139): fail if `sha` is not provided to the `POST /repos/{owner}/{repo}/contents` API endpoint. Although it was documented to be required, it was not enforced and clients that do not set the `sha` will no longer succeed. +- User Interface features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8185): transform fediverse handles (ex. @forgejo@floss.social and !forgejo@programming.dev) into links to https://fedirect.toolforge.org. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#redirecting-fediverse-handles). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8121): add user visibility description in the settings page. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8111): add model viewer for `.glb` (GLTF) model in file view. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#gltf-viewer). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7998): show size constraints of custom avatar. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7992): add links to milestones and projects in issue comments. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7958): global styling for the kbd tag. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7947): hints in empty usercards lists. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7906): the user profile has been redesigned. The most notable change is that actions have been moved to a dropdown and several new actions were added. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7531): improve the description in the packages settings. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7516): inline public ssh key in verification command. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7481): use switch element for markdown editor modes. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7388): make JS asset load error message translatable. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7385): improve performances by using `git switch -c` instead of `git checkout -b`. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7377): clarify the desired autocompletion type for the clone address in migrations. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7373): improve the clarity of the migration description textarea. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7361): automatically refresh workflows in the "Actions" list. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#automatically-refreshing-workflows). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7274): improve error pages. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7155): improve the user experience to review individual commits in a pull request. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#improved-ux-for-per-commit-reviews). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7128): use the available screen width when displaying Forgejo Actions logs. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6933): show if a commit is verified in the activity feed of a user or an organization. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6813): reimplemented editor Tab key handling with accessibility safeguards. Balance having the editor work as expected by developers (with Tab key affecting indentation) while also not impeding keyboard navigation. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#tabs-indentations-in-the-comment-editor) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6795): redesign the migration selection page. +- User Interface bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8417) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8514)): multiple ComboMarkdownEditors on one page interfere with each other. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7749) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8362)): pasting images into the comment editor will now show that image in the ‘dropzone’. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8296) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8317)): add missing trust status to pull review commits. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8246) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8282)): add missing lazy load attribute to images. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8067): retain sort type when viewing issue or pull requests. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7893): include enough activity for the entire heatmap. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7726): show warning in locked issue discussion. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7492): ensure consistent switch position in the markdown editor. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7420): display user-friendly message for range error. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7402): make limits clearer in the create repository form. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7307): don't put trailing slash in autogenerated name in the migration form. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7245): allow user with actions write permission to run a workflow from the web UI. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6799): ensure usercards in grid have the same width. +- Localization + - Updates from Codeberg Translate: [#7275](https://codeberg.org/forgejo/forgejo/pulls/7275), [#7363](https://codeberg.org/forgejo/forgejo/pulls/7363), [#7438](https://codeberg.org/forgejo/forgejo/pulls/7438), [#7507](https://codeberg.org/forgejo/forgejo/pulls/7507), [#7572](https://codeberg.org/forgejo/forgejo/pulls/7572), [#7637](https://codeberg.org/forgejo/forgejo/pulls/7637), [#7742](https://codeberg.org/forgejo/forgejo/pulls/7742), [#7819](https://codeberg.org/forgejo/forgejo/pulls/7819), [#7895](https://codeberg.org/forgejo/forgejo/pulls/7895), [#7969](https://codeberg.org/forgejo/forgejo/pulls/7969), [#8077](https://codeberg.org/forgejo/forgejo/pulls/8077), [#8178](https://codeberg.org/forgejo/forgejo/pulls/8178), [#8294](https://codeberg.org/forgejo/forgejo/pulls/8294) (backport of [#8238](https://codeberg.org/forgejo/forgejo/pulls/8238)), [#8534](https://codeberg.org/forgejo/forgejo/pulls/8534) (backport of [#8295](https://codeberg.org/forgejo/forgejo/issues/8295), [#8410](https://codeberg.org/forgejo/forgejo/issues/8410), [#8490](https://codeberg.org/forgejo/forgejo/issues/8490)) +- Features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8179): new `GET /repos/{owner}/{repo}/git/blobs` API endpoint to retrieve multiple blobs at once. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8177): always publish the link to the commit status. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8143): improve the performances of the generation of bundled assets. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8115): enable `mlkem768x25519-sha256` by default for builtin ssh. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8070): support artifact uploads for OCI container packages. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8047): add `admin user reset-mfa` CLI command. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7988): update the list of ambigious characters. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7986): make Forgejo Actions server logs less noisy. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7968): allow searching issues by number, prioritize title matches when sorted by relevance. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7953): replace go-rpmutils library with our own. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#forgejo-build-time-optimization). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7902): configurable default units for mirrors. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7896): a repository administrator has control over reindexing the issues. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7803): auto cleanup of offline runners. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7727): improved performances when checking for conflicts on pull requests. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#faster-conflict-checking). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7716): allow access to publicly available `/api/v1/packages/{username}` without a token. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7699): implement the `GET /repos/{owner}/{repo}/actions/runs` and `GET /repos/{owner}/{repo}/actions/runs/{run_id}` API endpoints. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7527): use git-replay for rebasing for better performances. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7509): send mail on failed or recovered Forgejo Actions run. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#forgejo-actions-email-notifications-on-failure). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7508): Forgejo Actions failure, success, recover webhooks. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7418): add `last_commit_when` to API contents responses. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7387): include a default robots.txt to reduce the impact of crawlers. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#default-robotstxt). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7212): use XORM EngineGroup instead of single Engine connection. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#xorm-enginegroup-connections-for-optimized-database-query-routing-and-load-balancing). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/2364): sync forks. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#keeping-forks-in-sync). +- Bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8511) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8516)): pull requests were not blocked by review request for a whitelisted team. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8475) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8480)): several fixes of the ALT RPM package registry. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8391) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8459)): allow lowercase as well as uppercase token keyword in the auth header. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8450) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8454)): correctly mark reviews as stale for AGit pull requests. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8367) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8386)): user activation failed when an email address contained uppercase letters. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8330) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8349)): fix: load OldMilestone based on OldMilestoneID, not MilestoneID + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8037): omit Content-Length on 307 redirects when serving direct manifest for containers. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8034): fix a bug causing the PASCAL-modifier to return camel-case. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8028): remove the trailing slash from the issuer in OAuth claims. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8025): return the correct AGit type in ssh_info. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7932): fix url validation in the webhook add/edit API. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7930): add error reporting to pull requests with invalid Forgejo Actions workflow files. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7644): allow instance API URLs in release assets. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7604): improve the dashboard loading performances. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7454): fix a border case where it was not possible to cancel a pull request review. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7409): fix acme renewal. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6352): migrate Maven packages to "groupId:artifactId" name concatenation, regenerate metadata and fix missing groupId. +- Included for completeness but not worth a release note + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8534): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8530) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8532)): fix(packages): skip another stack frame from logging + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8527) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8528)): fix: ignore "Close" error when uploading container blob + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8524) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8526)): chore: failed authentication attempts are not errors and are displayed at the log info level + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8519) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8523)): fix: expanding exactly 20 lines between diff sections leaves visual artifact + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8301) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8518)): chore: use eventually for mysql collation test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8492) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8498)): fix(code-search): HighlightSearchResultCode should count the number of bytes and not the number of runes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8464) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8474)): fix: use parent context for new transactions + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8460) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8466)): chore: disable mismatched root URL e2e test for safari + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8461) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8463)): chore: do not navigate to same URL in E2E test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8448) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8455)): fix(email): actions notification template confuses branch with PR + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8258) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8439)): fix: corrupted wiki unit default permission (#8234 follow-up) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8366) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8404)): fix: cancelled or skipped runs are not failures for notifications + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8400) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8402)): chore: improve reliability of webauthn e2e test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8261) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8398)): fix: skip empty tokens in SearchOptions.Tokens() + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8374) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8390)): fix: disable Forgejo Actions email notifications on recovery + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8326) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8331)): fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8320) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8321)): chore: sort blocked users list for determistic results + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8267) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8319)): fix: abuse reports string data types + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8304) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8318)): fix: pass doer's ID for CRUD instance signing + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8002) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8290)): fix(ui): release: name is overridden with tag name on edit + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8286) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8288)): Revert "fix(api): document `is_system_webhook` field (#7784)" + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8271) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8277)): CI debug: testSleep: show actual times on failures + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8268): chore: update security option in issue templates + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8260): Token.ParseIssueReference crashing on empty string + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8256): Update renovate to v41.1.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8253): Update renovate to v41 (forgejo) (major) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8252): add an index to the ActionRun.stopped column + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8250): bug: unify RepoActionRun and ActionRun structs + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8248): Update module github.com/go-chi/chi/v5 to v5.2.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8245): Downgrade playwright temporarily and allow running all e2e tests + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8243): git/blob use NewTruncatedReader for profile and codeowners + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8242): bug: Forgejo Actions email notifications are opt-in + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8236): prevent 500 message on invalid username + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8227): only send Forgejo Actions notifications to one user + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8226): chore: sort mailer messages in test assertion + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8224): chore(release-notes): Forgejo v11.0.2 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8223): blob: GetBlobContent: reduce allocations + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8220): fix(tests): TestInitInstructions must use forEachObjectFormat + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8219): Update module code.forgejo.org/forgejo/act to v1.28.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8218): Update data.forgejo.org/oci/alpine Docker tag to v3.22 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8217): Update module github.com/minio/minio-go/v7 to v7.0.94 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8216): chore: migrate to `@stylistic/eslint-plugin` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8215): Update dependency eslint-plugin-wc to v3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8214): fix(ui): issue comment anchor on time stamp + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8209): git_model.CommitStatusesHideActionsURL is obsolete + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8207): Remove 1ms delay before inserting list prefix, fix race condition in tests + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8205): chore: remove gopls in Makefile + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8202): do not check for `object_format_name` field + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8199): feat(ui): use kbd in label selector hint, remove enter + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8198): [gitea] week 2025-22 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8197): Fix sentence structure mentioning cooldown period + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8195): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8194): Update renovate to v40.57.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8190): Update dependency chart.js to v4.5.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8188): make test suite run on older git version + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8186): Update module github.com/go-sql-driver/mysql to v1.9.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8183): Update environment-to-ini README + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8181): Update https://data.forgejo.org/forgejo/forgejo-build-publish action to v5.3.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8176): Dockerfile should re-use bindata files when possible + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8174): Update dependency minimatch to v10.0.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8172): use zstd.WithLowerEncoderMem for generate-bindata + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8168): do not mix urfave v2 with urfave v3 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8166): chore(ci): skip tests if TEST_{MINIO_ENDPOINT,ELASTICSEARCH_URL} is not set + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8163): Update module gitlab.com/gitlab-org/api/client-go to v0.130.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8162): Update module github.com/minio/minio-go/v7 to v7.0.93 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8160): Update dependency postcss to v8.5.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8159): Update dependency minimatch to v10.0.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8157): chore(ci): use code.forgejo.org/forgejo/migration-test for migration tests + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8150): Update dependency happy-dom to v18 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8149): Update dependency typescript-eslint to v8.34.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8148): Update dependency @playwright/test to v1.53.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8147): Update ghcr.io/devcontainers/features/git-lfs Docker tag to v1.2.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8146): Update dependency postcss-nesting to v13.0.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8145): Update dependency @vitest/eslint-plugin to v1.2.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8144): chore: fix tests for old git versions + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8140): chore(ci): run additional tests in integration + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8137): Remove shebang from bash autocompletion + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8134): Make relative-time a self-maintaining custom element + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8129): create shell.nix; update flake.* + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8128): Update module connectrpc.com/connect to v1.18.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8127): Update dependency markdownlint-cli to v0.45.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8126): Update dependency eslint-plugin-regexp to v2.9.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8125): Update vitest monorepo to v3.2.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8118): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8117): Update renovate to v40.48.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8112): remove download attribute from external assets + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8108): always render detailed team permissions table in sidebar + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8103): Update x/tools to v0.34.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8102): Update vitest monorepo to v3.2.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8101): Update module github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker to v3.3.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8099): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8098): Update dependency happy-dom to v17.6.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8097): Update dependency @vitejs/plugin-vue to v5.2.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8096): Update dependency @axe-core/playwright to v4.10.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8094): show membership of limited orgs + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8091): Update module golang.org/x/crypto to v0.39.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8090): Update module github.com/sergi/go-diff to v1.4.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8086): Update dependency go to v1.24.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8078): Federated user activity following: Isolated model changes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8066): rename api.{List,}ActionRun to api.{List,}RepoActionRun + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8065): Update module go.uber.org/mock/mockgen to v0.5.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8064): Update module github.com/blevesearch/bleve/v2 to v2.5.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8063): Update dependency vue to v3.5.16 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8062): Update dependency postcss to v8.5.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8061): chore: extract commit header template + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8058): chore: drop unused `misspell` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8056): Update module code.forgejo.org/f3/gof3/v3 to v3.11.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8055): chore(renovate): disable indirect digest updates for stable + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8054): chore: drop unused `@typescript-eslint/parser` package + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8052): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8051): Update dependency happy-dom to v17.6.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8050): chore(renovate): bump to v40.40.0 and disable v7 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8049): CLI is forgejo not Forgejo + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8044): add missing bottom margin for verification-button in release view + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8041): [gitea] Always use an empty line to separate the commit message and trailer + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8040): [gitea] week 2025-21 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8038): Add a GNU Guix manifest + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8021): chore(cleanup): suppress non actionable XORM warnings + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8019): migrate repository.topics column for SQLite + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7994): chore(cleanup): replaces unnecessary calls to formatting functions by non-formatting equivalents + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7987): aggregate deleted team as ghost team + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7981): add validating user password as trace region + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7978): chore(cleanup): fix and simplify API comparison helper + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7975): fix(i18n): use correct base capitalization style + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7961): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7959): Update renovate to v40.31.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7954): Update forgejo go-chi packages (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7952): Update module github.com/ProtonMail/go-crypto to v1.3.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7950): fix(ui): relative time elements were reset on htmx swap + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7945): `overflow-wrap` strategy in `.markup` CSS class + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7944): fix(ui): change escaping button bg on selected lines + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7933): do not unconditionally append $(GITEA_COMPATIBILITY) in version + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7925): fix(ui): center footer links + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7922): Update go-openapi packages (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7920): unify repository topics field by replacing JSON null with empty array + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7919): Update dependency webpack to v5.99.9 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7918): Update dependency sharp to v0.34.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7917): chore: QoL improvements to tests + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7914): chore(renovate): disable indirect major updates for stable branches + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7910): Update dependency clippie to v4.1.7 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7908): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7907): Update renovate to v40.26.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7901): Update module github.com/yuin/goldmark to v1.7.12 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7898): chore(ui): cleanup unused color CSS + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7894): fix(ui): fix force-push compare line layout + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7890): Update module github.com/alecthomas/chroma/v2 to v2.18.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7884): parse `change-id` in the git commit header + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7881): Update module github.com/blevesearch/bleve/v2 to v2.5.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7879): Update dependency vue to v3.5.14 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7877): Migrate renovate config + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7872): fix(ui): disable autocapitalization/autocorrect for username inputs + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7868): fix(ui): disable spellcheck on TOTP form fields + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7866): chore: remove unused update-locales.sh + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7857): Update module github.com/msteinert/pam/v2 to v2.1.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7856): Update module code.forgejo.org/forgejo/act to v1.26.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7852): Fix TestSSHPushMirror/Normal/Check_mirrored_content test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7849): Update github.com/golang-jwt/jwt/v4 (indirect) to v4.5.2 [SECURITY] (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7848): ci(renovate): only fail on error + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7847): Update module github.com/niklasfasching/go-org to v1.8.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7840): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7835): remove redundant permission check in RemoveLabel + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7833): test(ui-e2e): fix flaky repo wiki test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7830): Update module github.com/go-webauthn/webauthn to v0.13.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7822): chore(ui): clean up hashbox CSS, small design changes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7817): replace ß with ss in normalizeUserName + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7814): Update dependency globals to v16.1.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7809): Update module golang.org/x/oauth2 to v0.30.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7807): Update dependency go to v1.24.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7799): add missing loadbalancing policies for EngineGroup connections + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7798): Update module golang.org/x/net to v0.40.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7797): Update module golang.org/x/image to v0.27.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7792): [skip ci] chore: remove backport script + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7789): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7785): [SKIP CI] chore: update CODEOWNERS + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7784): fix(api): document `is_system_webhook` field + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7778): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7777): Update renovate to v40 (forgejo) (major) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7776): Update renovate to v39.264.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7773): remove artificial delay for PR update + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7772): chore: Fix outdated usage of unittest.OverrideFixtures + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7770): Update module github.com/42wim/httpsig to v1.2.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7769): Update dependency @github/relative-time-element to v4.4.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7768): Update github.com/42wim/sshsig digest to 5100632 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7765): chore(release-notes): Forgejo v7.0.15 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7764): chore(release-notes): Forgejo v11.0.1 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7762): document that /repos/{owner}/{repo}/pulls may contain nulls + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7760): Update dependency forgejo/release-notes-assistant to v1.2.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7759): chore(renovate): use `gitea-releases` datasource for rna + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7758): chore(renovate): allow updating release notes assistant + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7746): fix(ui): improve force-push compare line layout + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7744): Update dependency eslint-plugin-unicorn to v59 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7740): fix(ui): multiple fixes of sync fork UI + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7739): Update module github.com/redis/go-redis/v9 to v9.8.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7738): Update module github.com/alecthomas/chroma/v2 to v2.17.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7736): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7735): Update dependency happy-dom to v17.4.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7732): ci: add clarification regarding test label + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7731): chore: fix test to avoid data race + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7728): refactor & enhance AP elements used + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7720): chore(release): next-digest moved to invisible.forgejo.org + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7715): chore: replace `github.com/go-testfixtures/testfixtures` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7714): enhance validateable interface + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7713): [gitea] week 2025-17 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7707): do not set GOPROXY=direct in Dockerfile* + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7705): chore(ui): remove unused fomantic font size classes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7697): better comments and variable names for ActionRunNowDone + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7696): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7695): Update renovate to v39.261.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7690): fix various typos + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7689): Update module github.com/yuin/goldmark to v1.7.11 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7687): chore: tune down remote user promotion debug message shown as error + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7685): use `linguist-generated` for language stats + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7683): set default restricted for OAuth2 user + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7682): chore: simplify `GetDiff` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7680): chore: remove unused linters + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7678): add label for avatar settings + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7667): i18n(en): fix typo in archive notice + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7665): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7663): Update dependency @playwright/test to v1.52.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7662): Update module github.com/PuerkitoBio/goquery to v1.10.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7661): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7659): Update dependency webpack to v5.99.7 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7648): chore: merge tests.AddFixtures and unittest.OverrideFixtures + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7640): Remove "create branch" button on mirrored repos + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7616): fix(ui): make pagination labels always visible to screenreader + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7613): Update module mvdan.cc/gofumpt to v0.8.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7612): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7611): Update vitest monorepo to v3.1.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7610): Update dependency @vitest/eslint-plugin to v1.1.43 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7607): Update module github.com/alecthomas/chroma/v2 to v2.17.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7606): Update module github.com/minio/minio-go/v7 to v7.0.91 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7602): [gitea] week 2025-16 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7597): delay-write trace.dat for forgejo diagnosis + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7588): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7587): Update renovate to v39.252.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7580): feat(ui): enlarge metadata line gaps in issue list and refactor + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7579): i18n(en): add positional hints to sync_fork placeholders + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7578): fix(ui): overflow tabular menu CSS fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7577): feat(ui): make fork related banners more consistent + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7575): fix(ui): use correct branch name in branch tag selector + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7573): Update dependency webpack to v5.99.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7571): fix(ui): show commit icon in branch dropdown button when viewing a commit + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7565): grammar in a release API error message + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7561): Update module github.com/caddyserver/certmagic to v0.23.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7560): Update module github.com/mattn/go-sqlite3 to v1.14.28 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7558): fix(ui): make tag dropdown clickable again + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7557): Update dependency python to v3.13.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7555): chore(release): v10.0 is EOL [skip ci] + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7554): chore(renovate): fix package name matching for go majors [skip ci] + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7552): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7551): Update https://data.forgejo.org/actions/git-backporting action to v4.8.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7550): Update dependency chart.js to v4.4.9 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7545): Update Node.js to v22 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7543): match PackageBlob.HashBlake2b definition and migration + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7539): fix(UI): i18n: improve naming + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7538): [gitea] week 2025-15 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7534): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7532): Update renovate to v39.240.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7528): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7520): package_blob.has_blake2b may be null + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7512): chore: use `sharp` to generate images + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7510): chore: refactor for Actions Done Notification + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7505): Update module golang.org/x/tools/cmd/deadcode to v0.32.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7504): Update module golang.org/x/net to v0.39.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7503): Update dependency katex to v0.16.22 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7499): chore(i18n): update contributing documentation with JSON format + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7497): Update module golang.org/x/image to v0.26.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7495): Update module github.com/minio/minio-go/v7 to v7.0.90 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7494): Update dependency webpack to v5.99.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7491): Actions Done Notification + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7490): Update dependency webpack to v5.99.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7488): Update module go.uber.org/mock to v0.5.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7486): [gitea] week 2025-14 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7485): Update module golang.org/x/crypto to v0.37.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7484): Update renovate to v39.233.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7479): Minor grammatical fix to runner deletion message + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7475): Update module golang.org/x/oauth2 to v0.29.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7473): Update module github.com/fsnotify/fsnotify to v1.9.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7472): Update dependency clippie to v4.1.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7471): fix(ui): ensure dimmer always covers whole page + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7470): fix(ui): only run auth_name code on new and edit page + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7469): chore(release-notes): Forgejo v11.0.0 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7468): Update module github.com/blevesearch/bleve/v2 to v2.5.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7467): Update dependency typescript to v5.8.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7465): Update dependency ansi_up to v6.0.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7463): chore: always enable webpack progress + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7462): chore(i18n): add a meta line to the base json translation + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7459): Update module github.com/alecthomas/chroma/v2 to v2.16.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7458): Update module code.gitea.io/sdk/gitea to v0.21.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7456): Update dependency ansi_up to v6.0.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7455): Makefile & BSDmakefile changes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7453): chore(sec): unify usage of `crypto/rand.Read` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7452): make installing Forgejo work again + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7451): chore(i18n): cleanup `settings.adopt` string + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7434): Update module github.com/mattn/go-sqlite3 to v1.14.27 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7433): feat(build): run lint-locale-usage w/o --allow-missing-msgids + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7431): make repo clone https/ssh listener conditional + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7429): feat(build): uniform ini parsing + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7425): feat(locale Iter): properly support trPluralString + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7424): Update module github.com/mattn/go-sqlite3 to v1.14.25 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7423): Update module github.com/go-webauthn/webauthn to v0.12.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7422): fix(i18n): fix several usages of i18n + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7421): fix(migrations): transfer PR flow information + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7419): chore: add empty `action_variable` fixture + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7417): Update dependency go to v1.24.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7416): chore(ui): remove fomantic's dimmer module + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7414): chore(ui): remove ineffective class small in inline code preview + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7408): chore: enable several no-jquery rules + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7407): Update vitest monorepo to v3.1.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7406): Update linters to v8.29.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7400): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7399): Update renovate to v39.222.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7397): [gitea] week 2025-13 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7396): chore: use dynamic id + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7394): use correct init instruction for sha256 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7392): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7391): Update github.com/google/pprof digest to a4b03ec (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7390): Accessibility: fix unreadable captcha with dark themes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7384): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7383): Update dependency yamllint to v1.37.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7382): Update vitest monorepo to v3.0.9 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7381): Update dependency @vitest/eslint-plugin to v1.1.38 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7379): prepend AppSubURL to visibility hint URLs + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7378): chore: improve repo migrate e2e test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7376): chore(tests): fix testing failure caused by dep update + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7374): chore(tests): refactor migration form test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7372): Update dependency eslint-plugin-unicorn to v58 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7371): chore: use correct import + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7367): Update module github.com/golangci/golangci-lint/cmd/golangci-lint to v2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7366): Update https://data.forgejo.org/tj-actions/changed-files action to v46 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7362): Update module golang.org/x/net to v0.38.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7356): fix(ui): improve vertical alignment of icons with text in the overflow menu (#7314) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7355): 4108-empty-slice-encoded-to-null + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7350): refactor(cli): improve dump's temporary file handling + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7348): introduce gitNeeded bool in setup + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7344): fix(ui): Do not check for `vertical-align` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7339): Update dependency mermaid to v11.6.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7337): chore: branding import path + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7314): fix(ui): improve vertical alignment of icons with text in the overflow menu + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7278): feat(build): lint-locale-usage should detect more Tr functions + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7271): feat(ui): improve button gap consistency, make it variable, larger on touchscreens + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7261): redirect to submodule instead of throwing 500 error when viewing submodule entry + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7203): add port and schema to federation host + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7092): feat(repo,locale): merge PR/issues cases for some repo/issue strings + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7035): enable HTTP signatures on all ActivityPub endpoints + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6977): feat!: Abusive content reporting + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6154): Replace the 'relative-time' element scripting with custom, translatable rewrite +- Already announced in the release notes of an older stable release + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8234): collaborator can edit wiki with write access + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8189): do not ignore automerge while a PR is checking for conflicts + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8170): erroneous list continuation on Cmd+Enter (#8153) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8155): do not fail when release or wiki is set in `/repos/migrate` API + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7979): pull request cross references + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7976): ignore expired artifacts for quota calculation + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7883): quote reply in Chromium + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7775): make hash pattern more strict + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7755): fix(sec): add tests for OAuth2 signup + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7752): fix(sec): only degrade permission check for git push + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7650): display the list of tasks in the runner edit page + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7594): fix(i18n): prevent incorrect logging on strings missing in JSON locales + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7584): fix(ui/pr): use eye icon for reviews + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7581): fix(ui): use gap in switch items + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7395): validate input for default_{merge,update}_style + diff --git a/release-notes-published/12.0.1.md b/release-notes-published/12.0.1.md new file mode 100644 index 0000000000..e3de6cae3a --- /dev/null +++ b/release-notes-published/12.0.1.md @@ -0,0 +1,24 @@ +Insecure authentication methods have been deprecated since 2023. They were [removed in v12.0.0](https://codeberg.org/forgejo/forgejo/pulls/7924) but they were [restored in v12.0.1](https://codeberg.org/forgejo/forgejo/pulls/8653). Certain OAuth2 clients and packages in the Forgejo ecosystem still rely on these methods and it was premature to remove them. + + + +## Release notes + +- User Interface bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8575) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8576)): allow for tracked time to be removed again + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8565) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8574)): correct image source for quoted reply + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8553) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8555)): prevent render failure on faulty org settings post +- Bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8633) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8653)): Revert "feat: remove API authentication methods that uses the URL query (#7924)" + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8644) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8646)): update i18n usage in comments + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8609) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8613)): upgrade fails or hang at migration[31]: Migrate maven package name concatenation + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8622) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8624)): rebase and fast forward merge breaks commit signatures + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8617) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8618)): make the action feed resilient to database inconsistencies + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8533) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8584)): make sure to use unaltered fields when saving a shadow copy for updated profiles or comments + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8596) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8610)): follow symlinks for local assets + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8550) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8552)): use correct ACME default +- Included for completeness but not user-facing (chores, etc.) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8638) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8641)): Revert "fix(ci): pull stylus from github:stylus/stylus#0.57.0 (#8625)" + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8625) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8627)): fix(ci): pull stylus from github:stylus/stylus#0.57.0 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8611) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8616)): chore: disable E2E test for webkit + diff --git a/release-notes-published/7.0.16.md b/release-notes-published/7.0.16.md new file mode 100644 index 0000000000..5d09ddc245 --- /dev/null +++ b/release-notes-published/7.0.16.md @@ -0,0 +1,11 @@ +## Git update fixing CVE-2025-48385 + +Git vulnerabilities were [disclosed 8 July 2025](https://groups.google.com/g/git-packagers/c/cYJ6peBtyxk/m/xVukiATcBQAJ) and require an update of the Git version used by Forgejo to Git [v2.43.7, v2.44.4, v2.45.4, v2.46.4, v2.47.3, v2.48.2, v2.49.1, or v2.50.1](https://nvd.nist.gov/vuln/detail/CVE-2025-48385). The [containers of this release](https://codeberg.org/forgejo/-/packages/container/forgejo/7.0.16) include a Git binary that is not vulnerable. If Forgejo was installed using a container, it is enough to upgrade the container to get the latest Git binary. + +Security bug fixes are only for Git, there are no security fixes for Forgejo itself in this release. + + + +## Release notes + + diff --git a/release-notes/8502.md b/release-notes/8502.md new file mode 100644 index 0000000000..b7fa0fd892 --- /dev/null +++ b/release-notes/8502.md @@ -0,0 +1 @@ +Forgejo Actions workflows are verified with a YAML schema and common errors such as using an incorrect context (e.g. `${{ badcontext.FORGEJO_REPOSITORY }}`) or a typo in a required keyword (e.g. `ruins-on:` instead of `runs-on:`) will be reported in the action page and the web page that displays the file in the repository. It is recommended to verify existing workflows are successfully verified prior to upgrading, [as explained in the Forgejo runner release notes](https://code.forgejo.org/forgejo/runner/src/branch/main/RELEASE-NOTES.md#8-0-0). diff --git a/renovate.json b/renovate.json index 7796490544..d76b6c7d23 100644 --- a/renovate.json +++ b/renovate.json @@ -6,9 +6,10 @@ "docker:pinDigests", "helpers:pinGitHubActionDigests" ], - "baseBranches": [ + "baseBranchPatterns": [ "$default", - "/^v11\\.\\d+/forgejo$/" + "/^v11\\.\\d+/forgejo$/", + "/^v12\\.\\d+/forgejo$/" ], "postUpdateOptions": ["gomodTidy", "gomodUpdateImportPaths", "npmDedupe"], "prConcurrentLimit": 10, @@ -27,8 +28,6 @@ "matchPackageNames": [ "docker.io/bitnami/minio", "github.com/go-ap/activitypub", - "github.com/nektos/act", - "gitea.com/gitea/act" ], "dependencyDashboardApproval": true }, diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index 9a7fd03aa8..873ce5c23a 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -432,11 +432,6 @@ func EndUploadBlob(ctx *context.Context) { return } - // There was a strange bug: the "Close" fails with error "close .../tmp/package-upload/....: file already closed" - // AFAIK there should be no other "Close" call to the uploader between NewBlobUploader and this line. - // At least it's safe to call Close twice, so ignore the error. - _ = uploader.Close() - if err := container_service.RemoveBlobUploadByID(ctx, uploader.ID); err != nil { apiError(ctx, http.StatusInternalServerError, err) return diff --git a/routers/api/shared/middleware.go b/routers/api/shared/middleware.go index 7d537f1ef9..59d9f28d60 100644 --- a/routers/api/shared/middleware.go +++ b/routers/api/shared/middleware.go @@ -4,8 +4,10 @@ package shared import ( + "fmt" "net/http" + auth_model "forgejo.org/models/auth" "forgejo.org/modules/log" "forgejo.org/modules/setting" "forgejo.org/routers/common" @@ -30,6 +32,7 @@ func Middlewares() (stack []any) { return append(stack, context.APIContexter(), + checkDeprecatedAuthMethods, // Get user from session if logged in. apiAuth(buildAuthGroup()), verifyAuthWithOptions(&common.VerifyOptions{ @@ -91,6 +94,25 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC }) return } + + if ctx.Doer.MustHaveTwoFactor() { + hasTwoFactor, err := auth_model.HasTwoFactorByUID(ctx, ctx.Doer.ID) + if err != nil { + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + log.Error("Error getting 2fa: %s", err) + ctx.JSON(http.StatusInternalServerError, map[string]string{ + "message": fmt.Sprintf("Error getting 2fa: %s", err), + }) + return + } + if !hasTwoFactor { + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": ctx.Locale.TrString("error.must_enable_2fa", fmt.Sprintf("%suser/settings/security", setting.AppURL)), + }) + return + } + } } // Redirect to dashboard if user tries to visit any non-login page. @@ -126,6 +148,13 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC } } +// check for and warn against deprecated authentication options +func checkDeprecatedAuthMethods(ctx *context.APIContext) { + if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" { + ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") + } +} + func securityHeaders() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { diff --git a/routers/api/v1/activitypub/actor.go b/routers/api/v1/activitypub/actor.go index e49f277842..0ff822c7f4 100644 --- a/routers/api/v1/activitypub/actor.go +++ b/routers/api/v1/activitypub/actor.go @@ -32,7 +32,7 @@ func Actor(ctx *context.APIContext) { actor := ap.ActorNew(ap.IRI(link), ap.ApplicationType) actor.PreferredUsername = ap.NaturalLanguageValuesNew() - err := actor.PreferredUsername.Set("en", ap.Content(setting.Domain)) + err := actor.PreferredUsername.Set("en", ap.Content("ghost")) if err != nil { ctx.ServerError("PreferredUsername.Set", err) return @@ -41,8 +41,6 @@ func Actor(ctx *context.APIContext) { actor.URL = ap.IRI(setting.AppURL) actor.Inbox = ap.IRI(link + "/inbox") - actor.Outbox = ap.IRI(link + "/outbox") - actor.PublicKey.ID = ap.IRI(link + "#main-key") actor.PublicKey.Owner = ap.IRI(link) diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go index 1da7933418..72f9680b9b 100644 --- a/routers/api/v1/activitypub/person.go +++ b/routers/api/v1/activitypub/person.go @@ -4,14 +4,17 @@ package activitypub import ( - "fmt" "net/http" - "strings" + "forgejo.org/models/activities" "forgejo.org/modules/activitypub" + "forgejo.org/modules/forgefed" "forgejo.org/modules/log" - "forgejo.org/modules/setting" + "forgejo.org/modules/web" + "forgejo.org/routers/api/v1/utils" "forgejo.org/services/context" + "forgejo.org/services/convert" + "forgejo.org/services/federation" ap "github.com/go-ap/activitypub" "github.com/go-ap/jsonld" @@ -34,45 +37,12 @@ func Person(ctx *context.APIContext) { // "200": // "$ref": "#/responses/ActivityPub" - // TODO: the setting.AppURL during the test doesn't follow the definition: "It always has a '/' suffix" - link := fmt.Sprintf("%s/api/v1/activitypub/user-id/%d", strings.TrimSuffix(setting.AppURL, "/"), ctx.ContextUser.ID) - person := ap.PersonNew(ap.IRI(link)) - - person.Name = ap.NaturalLanguageValuesNew() - err := person.Name.Set("en", ap.Content(ctx.ContextUser.FullName)) + person, err := convert.ToActivityPubPerson(ctx, ctx.ContextUser) if err != nil { - ctx.ServerError("Set Name", err) + ctx.ServerError("convert.ToActivityPubPerson", err) return } - person.PreferredUsername = ap.NaturalLanguageValuesNew() - err = person.PreferredUsername.Set("en", ap.Content(ctx.ContextUser.Name)) - if err != nil { - ctx.ServerError("Set PreferredUsername", err) - return - } - - person.URL = ap.IRI(ctx.ContextUser.HTMLURL()) - - person.Icon = ap.Image{ - Type: ap.ImageType, - MediaType: "image/png", - URL: ap.IRI(ctx.ContextUser.AvatarLink(ctx)), - } - - person.Inbox = ap.IRI(link + "/inbox") - person.Outbox = ap.IRI(link + "/outbox") - - person.PublicKey.ID = ap.IRI(link + "#main-key") - person.PublicKey.Owner = ap.IRI(link) - - publicKeyPem, err := activitypub.GetPublicKey(ctx, ctx.ContextUser) - if err != nil { - ctx.ServerError("GetPublicKey", err) - return - } - person.PublicKey.PublicKeyPem = publicKeyPem - binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(person) if err != nil { ctx.ServerError("MarshalJSON", err) @@ -99,8 +69,174 @@ func PersonInbox(ctx *context.APIContext) { // type: integer // required: true // responses: - // "204": + // "202": // "$ref": "#/responses/empty" - ctx.Status(http.StatusNoContent) + form := web.GetForm(ctx) + activity := form.(*ap.Activity) + result, err := federation.ProcessPersonInbox(ctx, ctx.ContextUser, activity) + if err != nil { + ctx.Error(federation.HTTPStatus(err), "PersonInbox", err) + return + } + responseServiceResult(ctx, result) +} + +// PersonFeed returns the recorded activities in the user's feed +func PersonFeed(ctx *context.APIContext) { + // swagger:operation GET /activitypub/user-id/{user-id}/outbox activitypub activitypubPersonFeed + // --- + // summary: List the user's recorded activity + // produces: + // - application/json + // parameters: + // - name: user-id + // in: path + // description: user ID of the user + // type: integer + // required: true + // responses: + // "200": + // "$ref": "#/responses/PersonFeed" + // "403": + // "$ref": "#/responses/forbidden" + + listOptions := utils.GetListOptions(ctx) + opts := activities.GetFollowingFeedsOptions{ + ListOptions: listOptions, + } + items, count, err := activities.GetFollowingFeeds(ctx, ctx.ContextUser.ID, opts) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetFollowingFeeds", err) + return + } + ctx.SetTotalCountHeader(count) + + feed := ap.OrderedCollectionNew(ap.IRI(ctx.ContextUser.APActorID() + "/outbox")) + feed.AttributedTo = ap.IRI(ctx.ContextUser.APActorID()) + for _, item := range items { + if err := feed.OrderedItems.Append(convert.ToActivityPubPersonFeedItem(item)); err != nil { + ctx.Error(http.StatusInternalServerError, "OrderedItems.Append", err) + return + } + } + + binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(feed) + if err != nil { + ctx.ServerError("MarshalJSON", err) + return + } + + ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) + ctx.Resp.WriteHeader(http.StatusOK) + if _, err = ctx.Resp.Write(binary); err != nil { + log.Error("write to resp err: %v", err) + } +} + +func getActivity(ctx *context.APIContext, id int64) (*forgefed.ForgeUserActivity, error) { + action, err := activities.GetActivityByID(ctx, id) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetActivityByID", err.Error()) + return nil, err + } + + if action.UserID != action.ActUserID || action.ActUserID != ctx.ContextUser.ID { + ctx.NotFound() + return nil, err + } + + actions := activities.ActionList{action} + if err := actions.LoadAttributes(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "action.LoadAttributes", err.Error()) + return nil, err + } + + activity, err := convert.ActionToForgeUserActivity(ctx, actions[0]) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ActionToForgeUserActivity", err.Error()) + return nil, err + } + + return &activity, nil +} + +// PersonActivity returns a user's given activity +func PersonActivity(ctx *context.APIContext) { + // swagger:operation GET /activitypub/user-id/{user-id}/activities/{activity-id}/activity activitypub activitypubPersonActivity + // --- + // summary: Get a specific activity of the user + // produces: + // - application/json + // parameters: + // - name: user-id + // in: path + // description: user ID of the user + // type: integer + // required: true + // - name: activity-id + // in: path + // description: activity ID of the sought activity + // type: integer + // required: true + // responses: + // "200": + // "$ref": "#/responses/ActivityPub" + + id := ctx.ParamsInt64("activity-id") + activity, err := getActivity(ctx, id) + if err != nil { + return + } + + binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(activity) + if err != nil { + ctx.ServerError("MarshalJSON", err) + return + } + ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) + ctx.Resp.WriteHeader(http.StatusOK) + if _, err = ctx.Resp.Write(binary); err != nil { + log.Error("write to resp err: %v", err) + } +} + +// PersonActivity returns the Object part of a user's given activity +func PersonActivityNote(ctx *context.APIContext) { + // swagger:operation GET /activitypub/user-id/{user-id}/activities/{activity-id} activitypub activitypubPersonActivityNote + // --- + // summary: Get a specific activity object of the user + // produces: + // - application/json + // parameters: + // - name: user-id + // in: path + // description: user ID of the user + // type: integer + // required: true + // - name: activity-id + // in: path + // description: activity ID of the sought activity + // type: integer + // required: true + // responses: + // "200": + // "$ref": "#/responses/ActivityPub" + + id := ctx.ParamsInt64("activity-id") + activity, err := getActivity(ctx, id) + if err != nil { + return + } + + binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(activity.Object) + if err != nil { + ctx.ServerError("MarshalJSON", err) + return + } + ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) + ctx.Resp.WriteHeader(http.StatusOK) + if _, err = ctx.Resp.Write(binary); err != nil { + log.Error("write to resp err: %v", err) + } } diff --git a/routers/api/v1/activitypub/repository.go b/routers/api/v1/activitypub/repository.go index c506840f1c..3eaa6b82c5 100644 --- a/routers/api/v1/activitypub/repository.go +++ b/routers/api/v1/activitypub/repository.go @@ -71,10 +71,11 @@ func RepositoryInbox(ctx *context.APIContext) { repository := ctx.Repo.Repository log.Info("RepositoryInbox: repo: %v", repository) form := web.GetForm(ctx) - // TODO: Decide between like/undo{like} activity - httpStatus, title, err := federation.ProcessLikeActivity(ctx, form, repository.ID) + activity := form.(*ap.Activity) + result, err := federation.ProcessRepositoryInbox(ctx, activity, repository.ID) if err != nil { - ctx.Error(httpStatus, title, err) + ctx.Error(federation.HTTPStatus(err), "Processing Repository Inbox failed", result) + return } - ctx.Status(http.StatusNoContent) + responseServiceResult(ctx, result) } diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index b84fbe05fa..38cb067b89 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -4,132 +4,17 @@ package activitypub import ( - "crypto" - "crypto/x509" - "database/sql" - "encoding/pem" - "errors" - "fmt" "net/http" - "net/url" - "forgejo.org/models/db" - "forgejo.org/models/forgefed" - "forgejo.org/models/user" - "forgejo.org/modules/activitypub" - fm "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/modules/setting" - gitea_context "forgejo.org/services/context" + services_context "forgejo.org/services/context" "forgejo.org/services/federation" "github.com/42wim/httpsig" - ap "github.com/go-ap/activitypub" ) -func decodePublicKeyPem(pubKeyPem string) ([]byte, error) { - block, _ := pem.Decode([]byte(pubKeyPem)) - if block == nil || block.Type != "PUBLIC KEY" { - return nil, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type") - } - - return block.Bytes, nil -} - -func getFederatedUser(ctx *gitea_context.APIContext, person *ap.Person, federationHost *forgefed.FederationHost) (*user.FederatedUser, error) { - personID, err := fm.NewPersonID(person.ID.String(), string(federationHost.NodeInfo.SoftwareName)) - if err != nil { - return nil, err - } - _, federatedUser, err := user.FindFederatedUser(ctx, personID.ID, federationHost.ID) - if err != nil { - return nil, err - } - - if federatedUser != nil { - return federatedUser, nil - } - - _, newFederatedUser, err := federation.CreateUserFromAP(ctx, personID, federationHost.ID) - if err != nil { - return nil, err - } - - return newFederatedUser, nil -} - -func storePublicKey(ctx *gitea_context.APIContext, person *ap.Person, pubKeyBytes []byte) error { - federationHost, err := federation.GetFederationHostForURI(ctx, person.ID.String()) - if err != nil { - return err - } - - if person.Type == ap.ActivityVocabularyType("Application") { - federationHost.KeyID = sql.NullString{ - String: person.PublicKey.ID.String(), - Valid: true, - } - - federationHost.PublicKey = sql.Null[sql.RawBytes]{ - V: pubKeyBytes, - Valid: true, - } - - _, err = db.GetEngine(ctx).ID(federationHost.ID).Update(federationHost) - if err != nil { - return err - } - } else if person.Type == ap.ActivityVocabularyType("Person") { - federatedUser, err := getFederatedUser(ctx, person, federationHost) - if err != nil { - return err - } - - federatedUser.KeyID = sql.NullString{ - String: person.PublicKey.ID.String(), - Valid: true, - } - - federatedUser.PublicKey = sql.Null[sql.RawBytes]{ - V: pubKeyBytes, - Valid: true, - } - - _, err = db.GetEngine(ctx).ID(federatedUser.ID).Update(federatedUser) - if err != nil { - return err - } - } - - return nil -} - -func getPublicKeyFromResponse(b []byte, keyID *url.URL) (person *ap.Person, pubKeyBytes []byte, p crypto.PublicKey, err error) { - person = ap.PersonNew(ap.IRI(keyID.String())) - err = person.UnmarshalJSON(b) - if err != nil { - return nil, nil, nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err) - } - - pubKey := person.PublicKey - if pubKey.ID.String() != keyID.String() { - return nil, nil, nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b)) - } - - pubKeyBytes, err = decodePublicKeyPem(pubKey.PublicKeyPem) - if err != nil { - return nil, nil, nil, err - } - - p, err = x509.ParsePKIXPublicKey(pubKeyBytes) - if err != nil { - return nil, nil, nil, err - } - - return person, pubKeyBytes, p, err -} - -func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) { +func verifyHTTPUserOrInstanceSignature(ctx services_context.APIContext) (authenticated bool, err error) { if !setting.Federation.SignatureEnforced { return true, nil } @@ -142,84 +27,64 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er return false, err } - ID := v.KeyId() - idIRI, err := url.Parse(ID) + signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0]) + pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId()) + if err != nil || pubKey == nil { + pubKey, err = federation.FindOrCreateFederationHostKey(ctx, v.KeyId()) + if err != nil { + return false, err + } + } + + err = v.Verify(pubKey, signatureAlgorithm) + if err != nil { + return false, err + } + return true, nil +} + +func verifyHTTPUserSignature(ctx services_context.APIContext) (authenticated bool, err error) { + if !setting.Federation.SignatureEnforced { + return true, nil + } + + r := ctx.Req + + // 1. Figure out what key we need to verify + v, err := httpsig.NewVerifier(r) if err != nil { return false, err } signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0]) - - // 2. Fetch the public key of the other actor - // Try if the signing actor is an already known federated user - _, federationUser, err := user.FindFederatedUserByKeyID(ctx, idIRI.String()) + pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId()) if err != nil { return false, err } - if federationUser != nil && federationUser.PublicKey.Valid { - pubKey, err := x509.ParsePKIXPublicKey(federationUser.PublicKey.V) - if err != nil { - return false, err - } - - authenticated = v.Verify(pubKey, signatureAlgorithm) == nil - return authenticated, err - } - - // Try if the signing actor is an already known federation host - federationHost, err := forgefed.FindFederationHostByKeyID(ctx, idIRI.String()) + err = v.Verify(pubKey, signatureAlgorithm) if err != nil { return false, err } - - if federationHost != nil && federationHost.PublicKey.Valid { - pubKey, err := x509.ParsePKIXPublicKey(federationHost.PublicKey.V) - if err != nil { - return false, err - } - - authenticated = v.Verify(pubKey, signatureAlgorithm) == nil - return authenticated, err - } - - // Fetch missing public key - actionsUser := user.NewAPServerActor() - clientFactory, err := activitypub.GetClientFactory(ctx) - if err != nil { - return false, err - } - - apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID()) - if err != nil { - return false, err - } - - b, err := apClient.GetBody(idIRI.String()) - if err != nil { - return false, err - } - - person, pubKeyBytes, pubKey, err := getPublicKeyFromResponse(b, idIRI) - if err != nil { - return false, err - } - - authenticated = v.Verify(pubKey, signatureAlgorithm) == nil - if authenticated { - err = storePublicKey(ctx, person, pubKeyBytes) - if err != nil { - return false, err - } - } - - return authenticated, err + return true, nil } // ReqHTTPSignature function -func ReqHTTPSignature() func(ctx *gitea_context.APIContext) { - return func(ctx *gitea_context.APIContext) { - if authenticated, err := verifyHTTPSignatures(ctx); err != nil { +func ReqHTTPUserOrInstanceSignature() func(ctx *services_context.APIContext) { + return func(ctx *services_context.APIContext) { + if authenticated, err := verifyHTTPUserOrInstanceSignature(*ctx); err != nil { + log.Warn("verifyHttpSignatures failed: %v", err) + ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed") + } else if !authenticated { + ctx.Error(http.StatusForbidden, "reqSignature", "request signature verification failed") + } + } +} + +// ReqHTTPUserSignature function +func ReqHTTPUserSignature() func(ctx *services_context.APIContext) { + return func(ctx *services_context.APIContext) { + if authenticated, err := verifyHTTPUserSignature(*ctx); err != nil { log.Warn("verifyHttpSignatures failed: %v", err) ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed") } else if !authenticated { diff --git a/routers/api/v1/activitypub/response.go b/routers/api/v1/activitypub/response.go index a97f363cc2..64413cebb1 100644 --- a/routers/api/v1/activitypub/response.go +++ b/routers/api/v1/activitypub/response.go @@ -10,12 +10,46 @@ import ( "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/services/context" + "forgejo.org/services/federation" ap "github.com/go-ap/activitypub" "github.com/go-ap/jsonld" ) // Respond with an ActivityStreams object +func responseServiceResult(ctx *context.APIContext, result federation.ServiceResult) { + ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) + + switch { + case result.StatusOnly(): + ctx.Resp.WriteHeader(result.HTTPStatus) + return + case result.WithBytes(): + ctx.Resp.WriteHeader(result.HTTPStatus) + if _, err := ctx.Resp.Write(result.Bytes); err != nil { + log.Error("Error writing a response: %v", err) + ctx.Error(http.StatusInternalServerError, "Error writing a response", err) + return + } + case result.WithActivity(): + binary, err := jsonld.WithContext( + jsonld.IRI(ap.ActivityBaseURI), + jsonld.IRI(ap.SecurityContextURI), + jsonld.IRI(forgefed.ForgeFedNamespaceURI), + ).Marshal(result.Activity) + if err != nil { + ctx.ServerError("Marshal", err) + return + } + ctx.Resp.WriteHeader(result.HTTPStatus) + if _, err = ctx.Resp.Write(binary); err != nil { + log.Error("write to resp err: %v", err) + } + } +} + +// Respond with an ActivityStreams object +// Deprecated func response(ctx *context.APIContext, v any) { binary, err := jsonld.WithContext( jsonld.IRI(ap.ActivityBaseURI), diff --git a/routers/api/v1/admin/email.go b/routers/api/v1/admin/email.go index 906780a44b..9f6ef0fedf 100644 --- a/routers/api/v1/admin/email.go +++ b/routers/api/v1/admin/email.go @@ -17,7 +17,7 @@ import ( func GetAllEmails(ctx *context.APIContext) { // swagger:operation GET /admin/emails admin adminGetAllEmails // --- - // summary: List all emails + // summary: List all users' email addresses // produces: // - application/json // parameters: @@ -60,7 +60,7 @@ func GetAllEmails(ctx *context.APIContext) { func SearchEmail(ctx *context.APIContext) { // swagger:operation GET /admin/emails/search admin adminSearchEmails // --- - // summary: Search all emails + // summary: Search users' email addresses // produces: // - application/json // parameters: diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go index b3db2eb5e3..36ca6831e6 100644 --- a/routers/api/v1/admin/hooks.go +++ b/routers/api/v1/admin/hooks.go @@ -17,11 +17,11 @@ import ( webhook_service "forgejo.org/services/webhook" ) -// ListHooks list system's webhooks +// ListHooks list system webhooks func ListHooks(ctx *context.APIContext) { // swagger:operation GET /admin/hooks admin adminListHooks // --- - // summary: List system's webhooks + // summary: List global (system) webhooks // produces: // - application/json // parameters: diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 8aa67b3b0a..f3e321a047 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -51,11 +51,11 @@ func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64 u.LoginSource = source.ID } -// CreateUser create a user +// CreateUser create a user account func CreateUser(ctx *context.APIContext) { // swagger:operation POST /admin/users admin adminCreateUser // --- - // summary: Create a user + // summary: Create a user account // consumes: // - application/json // produces: @@ -274,7 +274,7 @@ func EditUser(ctx *context.APIContext) { func DeleteUser(ctx *context.APIContext) { // swagger:operation DELETE /admin/users/{username} admin adminDeleteUser // --- - // summary: Delete a user + // summary: Delete user account // produces: // - application/json // parameters: @@ -324,11 +324,11 @@ func DeleteUser(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// CreatePublicKey api for creating a public key to a user +// CreatePublicKey adds an SSH public key to user's account func CreatePublicKey(ctx *context.APIContext) { // swagger:operation POST /admin/users/{username}/keys admin adminCreatePublicKey // --- - // summary: Add a public key on behalf of a user + // summary: Add an SSH public key to user's account // consumes: // - application/json // produces: @@ -356,11 +356,11 @@ func CreatePublicKey(ctx *context.APIContext) { user.CreateUserPublicKey(ctx, *form, ctx.ContextUser.ID) } -// DeleteUserPublicKey api for deleting a user's public key +// DeleteUserPublicKey removes an SSH public key from user's account func DeleteUserPublicKey(ctx *context.APIContext) { // swagger:operation DELETE /admin/users/{username}/keys/{id} admin adminDeleteUserPublicKey // --- - // summary: Delete a user's public key + // summary: Remove a public key from user's account // produces: // - application/json // parameters: @@ -436,26 +436,6 @@ func SearchUsers(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) - sort := ctx.FormString("sort") - var orderBy db.SearchOrderBy - - switch sort { - case "oldest": - orderBy = db.SearchOrderByOldest - case "newest": - orderBy = db.SearchOrderByNewest - case "alphabetically": - orderBy = db.SearchOrderByAlphabetically - case "reversealphabetically": - orderBy = db.SearchOrderByAlphabeticallyReverse - case "recentupdate": - orderBy = db.SearchOrderByRecentUpdated - case "leastupdate": - orderBy = db.SearchOrderByLeastUpdated - default: - orderBy = db.SearchOrderByAlphabetically - } - intSource, err := strconv.ParseInt(ctx.FormString("source_id"), 10, 64) var sourceID optional.Option[int64] if ctx.FormString("source_id") == "" || err != nil { @@ -469,7 +449,7 @@ func SearchUsers(ctx *context.APIContext) { Type: user_model.UserTypeIndividual, LoginName: ctx.FormTrim("login_name"), SourceID: sourceID, - OrderBy: orderBy, + OrderBy: utils.GetDbSearchOrder(ctx), ListOptions: listOptions, }) if err != nil { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index bf08bdd249..26a2c0ffe3 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -22,6 +22,8 @@ // // Security: // - BasicAuth : +// - Token : +// - AccessToken : // - AuthorizationHeaderToken : // - SudoParam : // - SudoHeader : @@ -30,6 +32,16 @@ // SecurityDefinitions: // BasicAuth: // type: basic +// Token: +// type: apiKey +// name: token +// in: query +// description: This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. +// AccessToken: +// type: apiKey +// name: access_token +// in: query +// description: This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. // AuthorizationHeaderToken: // type: apiKey // name: Authorization @@ -69,7 +81,6 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unit" user_model "forgejo.org/models/user" - "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/modules/setting" api "forgejo.org/modules/structs" @@ -92,6 +103,7 @@ import ( _ "forgejo.org/routers/api/v1/swagger" // for swagger generation "code.forgejo.org/go-chi/binding" + ap "github.com/go-ap/activitypub" ) func sudo() func(ctx *context.APIContext) { @@ -826,24 +838,27 @@ func Routes() *web.Route { if setting.Federation.Enabled { m.Get("/nodeinfo", misc.NodeInfo) m.Group("/activitypub", func() { - // deprecated, remove in 1.20, use /user-id/{user-id} instead - m.Group("/user/{username}", func() { - m.Get("", activitypub.ReqHTTPSignature(), activitypub.Person) - m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) - }, context.UserAssignmentAPI(), checkTokenPublicOnly()) m.Group("/user-id/{user-id}", func() { - m.Get("", activitypub.ReqHTTPSignature(), activitypub.Person) - m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) + m.Get("", activitypub.ReqHTTPUserOrInstanceSignature(), activitypub.Person) + m.Post("/inbox", + activitypub.ReqHTTPUserSignature(), + bind(ap.Activity{}), + activitypub.PersonInbox) + m.Group("/activities/{activity-id}", func() { + m.Get("", activitypub.PersonActivityNote) + m.Get("/activity", activitypub.PersonActivity) + }) + m.Get("/outbox", activitypub.ReqHTTPUserSignature(), activitypub.PersonFeed) }, context.UserIDAssignmentAPI(), checkTokenPublicOnly()) m.Group("/actor", func() { m.Get("", activitypub.Actor) - m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.ActorInbox) + m.Post("/inbox", activitypub.ReqHTTPUserOrInstanceSignature(), activitypub.ActorInbox) }) m.Group("/repository-id/{repository-id}", func() { - m.Get("", activitypub.ReqHTTPSignature(), activitypub.Repository) + m.Get("", activitypub.ReqHTTPUserSignature(), activitypub.Repository) m.Post("/inbox", - bind(forgefed.ForgeLike{}), - activitypub.ReqHTTPSignature(), + bind(ap.Activity{}), + activitypub.ReqHTTPUserSignature(), activitypub.RepositoryInbox) }, context.RepositoryIDAssignmentAPI()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub)) @@ -1178,7 +1193,7 @@ func Routes() *web.Route { }) m.Group("/workflows", func() { - m.Group("/{workflowname}", func() { + m.Group("/{workflowfilename}", func() { m.Post("/dispatches", reqToken(), reqRepoWriter(unit.TypeActions), mustNotBeArchived, bind(api.DispatchWorkflowOption{}), repo.DispatchWorkflow) }) }) diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index 0d8550a019..8b330aa752 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -20,11 +20,11 @@ import ( secret_service "forgejo.org/services/secrets" ) -// ListActionsSecrets list an organization's actions secrets +// ListActionsSecrets lists actions secrets of an organization func (Action) ListActionsSecrets(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets // --- - // summary: List an organization's actions secrets + // summary: List actions secrets of an organization // produces: // - application/json // parameters: @@ -218,7 +218,7 @@ func (Action) SearchActionRunJobs(ctx *context.APIContext) { func (Action) ListVariables(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList // --- - // summary: Get an org-level variables list + // summary: List variables of an organization // produces: // - application/json // parameters: @@ -266,11 +266,11 @@ func (Action) ListVariables(ctx *context.APIContext) { ctx.JSON(http.StatusOK, variables) } -// GetVariable get an org-level variable +// GetVariable gives organization's variable func (Action) GetVariable(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/actions/variables/{variablename} organization getOrgVariable // --- - // summary: Get an org-level variable + // summary: Get organization's variable by name // produces: // - application/json // parameters: @@ -315,11 +315,11 @@ func (Action) GetVariable(ctx *context.APIContext) { ctx.JSON(http.StatusOK, variable) } -// DeleteVariable delete an org-level variable +// DeleteVariable deletes an organization's variable func (Action) DeleteVariable(ctx *context.APIContext) { // swagger:operation DELETE /orgs/{org}/actions/variables/{variablename} organization deleteOrgVariable // --- - // summary: Delete an org-level variable + // summary: Delete organization's variable by name // produces: // - application/json // parameters: @@ -359,11 +359,11 @@ func (Action) DeleteVariable(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// CreateVariable create an org-level variable +// CreateVariable creates a new variable in organization func (Action) CreateVariable(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/actions/variables/{variablename} organization createOrgVariable // --- - // summary: Create an org-level variable + // summary: Create a new variable in organization // consumes: // - application/json // produces: @@ -423,11 +423,11 @@ func (Action) CreateVariable(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// UpdateVariable update an org-level variable +// UpdateVariable updates variable in organization func (Action) UpdateVariable(ctx *context.APIContext) { // swagger:operation PUT /orgs/{org}/actions/variables/{variablename} organization updateOrgVariable // --- - // summary: Update an org-level variable + // summary: Update variable in organization // consumes: // - application/json // produces: diff --git a/routers/api/v1/org/avatar.go b/routers/api/v1/org/avatar.go index 824a9f3495..043da3186f 100644 --- a/routers/api/v1/org/avatar.go +++ b/routers/api/v1/org/avatar.go @@ -13,11 +13,11 @@ import ( user_service "forgejo.org/services/user" ) -// UpdateAvatarupdates the Avatar of an Organisation +// UpdateAvatar updates an organization's avatar func UpdateAvatar(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/avatar organization orgUpdateAvatar // --- - // summary: Update Avatar + // summary: Update an organization's avatar // produces: // - application/json // parameters: @@ -52,11 +52,11 @@ func UpdateAvatar(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// DeleteAvatar deletes the Avatar of an Organisation +// DeleteAvatar deletes an organization's avatar func DeleteAvatar(ctx *context.APIContext) { // swagger:operation DELETE /orgs/{org}/avatar organization orgDeleteAvatar // --- - // summary: Delete Avatar + // summary: Delete an organization's avatar. It will be replaced by a default one // produces: // - application/json // parameters: diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 87bc27be63..a4ea924979 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -180,7 +180,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) { func GetAll(ctx *context.APIContext) { // swagger:operation Get /orgs organization orgGetAll // --- - // summary: Get list of organizations + // summary: List all organizations // produces: // - application/json // parameters: diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index dbc4933de6..fe29d534ff 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -615,7 +615,7 @@ func ListActionTasks(ctx *context.APIContext) { // DispatchWorkflow dispatches a workflow func DispatchWorkflow(ctx *context.APIContext) { - // swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflowname}/dispatches repository DispatchWorkflow + // swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflowfilename}/dispatches repository DispatchWorkflow // --- // summary: Dispatches a workflow // consumes: @@ -631,7 +631,7 @@ func DispatchWorkflow(ctx *context.APIContext) { // description: name of the repo // type: string // required: true - // - name: workflowname + // - name: workflowfilename // in: path // description: name of the workflow // type: string @@ -649,13 +649,13 @@ func DispatchWorkflow(ctx *context.APIContext) { // "$ref": "#/responses/notFound" opt := web.GetForm(ctx).(*api.DispatchWorkflowOption) - name := ctx.Params("workflowname") + name := ctx.Params("workflowfilename") if len(opt.Ref) == 0 { ctx.Error(http.StatusBadRequest, "ref", "ref is empty") return } else if len(name) == 0 { - ctx.Error(http.StatusBadRequest, "workflowname", "workflow name is empty") + ctx.Error(http.StatusBadRequest, "workflowfilename", "workflow file name is empty") return } diff --git a/routers/api/v1/repo/avatar.go b/routers/api/v1/repo/avatar.go index 84aafe764d..12308ce2df 100644 --- a/routers/api/v1/repo/avatar.go +++ b/routers/api/v1/repo/avatar.go @@ -13,11 +13,11 @@ import ( repo_service "forgejo.org/services/repository" ) -// UpdateVatar updates the Avatar of an Repo +// UpdateVatar updates repo avatar func UpdateAvatar(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/avatar repository repoUpdateAvatar // --- - // summary: Update avatar + // summary: Update a repository's avatar // produces: // - application/json // parameters: @@ -56,11 +56,11 @@ func UpdateAvatar(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// UpdateAvatar deletes the Avatar of an Repo +// DeleteAvatar deletes repo avatar func DeleteAvatar(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/avatar repository repoDeleteAvatar // --- - // summary: Delete avatar + // summary: Delete a repository's avatar // produces: // - application/json // parameters: diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 7c9593d625..e043448590 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -594,7 +594,7 @@ func CreateBranchProtection(ctx *context.APIContext) { isPlainRule := !git_model.IsRuleNameSpecial(ruleName) var isBranchExist bool if isPlainRule { - isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), ruleName) + isBranchExist = ctx.Repo.GitRepo.IsBranchExist(ruleName) } protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, ruleName) @@ -982,7 +982,7 @@ func EditBranchProtection(ctx *context.APIContext) { isPlainRule := !git_model.IsRuleNameSpecial(bpName) var isBranchExist bool if isPlainRule { - isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), bpName) + isBranchExist = ctx.Repo.GitRepo.IsBranchExist(bpName) } if isBranchExist { diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 549fe9fae0..6c1671d21c 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -241,7 +241,7 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn return nil, nil, nil } - if entry.IsDir() || entry.IsSubModule() { + if entry.IsDir() || entry.IsSubmodule() { ctx.NotFound("getBlobForEntry", nil) return nil, nil, nil } diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 5495c4a6ba..442e109843 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -121,6 +121,12 @@ func SearchIssues(ctx *context.APIContext) { // description: Number of items per page // type: integer // minimum: 0 + // - name: sort + // in: query + // description: Type of sort + // type: string + // enum: [relevance, latest, oldest, recentupdate, leastupdate, mostcomment, leastcomment, nearduedate, farduedate] + // default: latest // responses: // "200": // "$ref": "#/responses/IssueList" @@ -276,7 +282,7 @@ func SearchIssues(ctx *context.APIContext) { IsClosed: isClosed, IncludedAnyLabelIDs: includedAnyLabels, MilestoneIDs: includedMilestones, - SortBy: issue_indexer.SortByCreatedDesc, + SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc), } if since != 0 { @@ -305,9 +311,10 @@ func SearchIssues(ctx *context.APIContext) { } } - // FIXME: It's unsupported to sort by priority repo when searching by indexer, - // it's indeed an regression, but I think it is worth to support filtering by indexer first. - _ = ctx.FormInt64("priority_repo_id") + priorityRepoID := ctx.FormInt64("priority_repo_id") + if priorityRepoID > 0 { + searchOpt.PriorityRepoID = optional.Some(priorityRepoID) + } ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt) if err != nil { diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 3b2935305c..f2e79ea417 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -384,7 +384,7 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { ctx.Status(http.StatusForbidden) - return nil, nil, nil + return nil, nil, errors.New("not issue/pull writer") } err = issue_service.SetIssueUpdateDate(ctx, issue, form.Updated, ctx.Doer) diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index bc48c6acb7..08ef68cbfc 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -251,11 +251,11 @@ func GetPushMirrorByName(ctx *context.APIContext) { ctx.JSON(http.StatusOK, m) } -// AddPushMirror adds a push mirror to a repository +// AddPushMirror sets up a new push mirror in a repository func AddPushMirror(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/push_mirrors repository repoAddPushMirror // --- - // summary: add a push mirror to the repository + // summary: Set up a new push mirror in a repository // consumes: // - application/json // produces: @@ -296,11 +296,11 @@ func AddPushMirror(ctx *context.APIContext) { CreatePushMirror(ctx, pushMirror) } -// DeletePushMirrorByRemoteName deletes a push mirror from a repository by remoteName +// DeletePushMirrorByRemoteName removes a push mirror from a repository by remoteName func DeletePushMirrorByRemoteName(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/push_mirrors/{name} repository repoDeletePushMirror // --- - // summary: deletes a push mirror from a repository by remoteName + // summary: Remove a push mirror from a repository by remoteName // produces: // - application/json // parameters: @@ -389,6 +389,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro Interval: interval, SyncOnCommit: mirrorOption.SyncOnCommit, RemoteAddress: remoteAddress, + BranchFilter: mirrorOption.BranchFilter, } var plainPrivateKey []byte diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 9360ff1335..812468f6b4 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1016,6 +1016,9 @@ func MergePullRequest(ctx *context.APIContext) { } else if models.IsErrMergeUnrelatedHistories(err) { conflictError := err.(models.ErrMergeUnrelatedHistories) ctx.JSON(http.StatusConflict, conflictError) + } else if models.IsErrPullRequestHasMerged(err) { + conflictError := err.(models.ErrPullRequestHasMerged) + ctx.JSON(http.StatusConflict, conflictError) } else if git.IsErrPushOutOfDate(err) { ctx.Error(http.StatusConflict, "Merge", "merge push out of date") } else if models.IsErrSHADoesNotMatch(err) { @@ -1084,7 +1087,6 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) err error ) - // If there is no head repository, it means pull request between same repository. headInfos := strings.Split(form.Head, ":") if len(headInfos) == 1 { isSameRepo = true @@ -1094,7 +1096,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) headUser, err = user_model.GetUserByName(ctx, headInfos[0]) if err != nil { if user_model.IsErrUserNotExist(err) { - ctx.NotFound("GetUserByName") + ctx.NotFound(fmt.Errorf("the owner %s does not exist", headInfos[0])) } else { ctx.Error(http.StatusInternalServerError, "GetUserByName", err) } @@ -1104,7 +1106,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) // The head repository can also point to the same repo isSameRepo = ctx.Repo.Owner.ID == headUser.ID } else { - ctx.NotFound() + ctx.NotFound(fmt.Errorf("the head part of {basehead} %s must contain zero or one colon (:) but contains %d", form.Head, len(headInfos)-1)) return nil, nil, nil, "", "" } @@ -1116,13 +1118,8 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch) baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch) if !baseIsCommit && !baseIsBranch && !baseIsTag { - // Check for short SHA usage - if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(baseBranch); baseCommit != nil { - baseBranch = baseCommit.ID.String() - } else { - ctx.NotFound("BaseNotExist") - return nil, nil, nil, "", "" - } + ctx.NotFound(fmt.Errorf("could not find '%s' to be a commit, branch or tag in the base repository %s/%s", baseBranch, baseRepo.Owner.Name, baseRepo.Name)) + return nil, nil, nil, "", "" } headRepo := repo_model.GetForkedRepo(ctx, headUser.ID, baseRepo.ID) @@ -1135,7 +1132,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) if baseRepo.BaseRepo == nil || baseRepo.BaseRepo.OwnerID != headUser.ID { log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID) - ctx.NotFound("GetBaseRepo") + ctx.NotFound(fmt.Errorf("%[1]s does not have a fork of %[2]s/%[3]s and %[2]s/%[3]s is not a fork of a repository from %[1]s", headUser.Name, baseRepo.Owner.Name, baseRepo.Name)) return nil, nil, nil, "", "" } headRepo = baseRepo.BaseRepo @@ -1203,17 +1200,16 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) headIsBranch := headGitRepo.IsBranchExist(headBranch) headIsTag := headGitRepo.IsTagExist(headBranch) if !headIsCommit && !headIsBranch && !headIsTag { - // Check if headBranch is short sha commit hash - if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil { - headBranch = headCommit.ID.String() - } else { - headGitRepo.Close() - ctx.NotFound("IsRefExist", nil) - return nil, nil, nil, "", "" - } + ctx.NotFound(fmt.Errorf("could not find '%s' to be a commit, branch or tag in the head repository %s/%s", headBranch, headRepo.Owner.Name, headRepo.Name)) + return nil, nil, nil, "", "" } headBranchRef := headBranch + if headIsBranch { + headBranchRef = git.BranchPrefix + headBranch + } else if headIsTag { + headBranchRef = git.TagPrefix + headBranch + } compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranchRef, headBranchRef, false, false) if err != nil { diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 830a62bf54..06b47fef70 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -716,7 +716,7 @@ func prepareSingleReview(ctx *context.APIContext) (*issues_model.Review, *issues func CreateReviewRequests(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoCreatePullReviewRequests // --- - // summary: create review requests for a pull request + // summary: Create review requests for a pull request // produces: // - application/json // parameters: @@ -757,7 +757,7 @@ func CreateReviewRequests(ctx *context.APIContext) { func DeleteReviewRequests(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoDeletePullReviewRequests // --- - // summary: cancel review requests for a pull request + // summary: Cancel review requests for a pull request // produces: // - application/json // parameters: diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index daa637936e..8829e37bc3 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -253,17 +253,17 @@ func DeleteTopic(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// TopicSearch search for creating topic +// TopicSearch searches known topics, i.e. when adding a topic to a repository func TopicSearch(ctx *context.APIContext) { // swagger:operation GET /topics/search repository topicSearch // --- - // summary: search topics via keyword + // summary: Search for topics by keyword // produces: // - application/json // parameters: // - name: q // in: query - // description: keywords to search + // description: keyword to search for // required: true // type: string // - name: page diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index bb4cf0f211..7b6a00408a 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -5,6 +5,7 @@ package repo import ( "encoding/base64" + "errors" "fmt" "net/http" "net/url" @@ -506,11 +507,8 @@ func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit) // given tree entry, encoded with base64. Writes to ctx if an error occurs. func wikiContentsByEntry(ctx *context.APIContext, entry *git.TreeEntry) string { blob := entry.Blob() - if blob.Size() > setting.API.DefaultMaxBlobSize { - return "" - } - content, err := blob.GetBlobContentBase64() - if err != nil { + content, err := blob.GetContentBase64(setting.API.DefaultMaxBlobSize) + if err != nil && !errors.As(err, &git.BlobTooLargeError{}) { ctx.Error(http.StatusInternalServerError, "GetBlobContentBase64", err) return "" } diff --git a/routers/api/v1/swagger/activitypub.go b/routers/api/v1/swagger/activitypub.go index 6235009572..a11fc4098c 100644 --- a/routers/api/v1/swagger/activitypub.go +++ b/routers/api/v1/swagger/activitypub.go @@ -13,3 +13,10 @@ type swaggerResponseActivityPub struct { // in:body Body api.ActivityPub `json:"body"` } + +// Personfeed +// swagger:response PersonFeed +type swaggerResponsePersonFeed struct { + // in:body + Body []api.APPersonFollowItem `json:"body"` +} diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 138ad5a1d2..65955913fd 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -71,11 +71,11 @@ func ListAccessTokens(ctx *context.APIContext) { ctx.JSON(http.StatusOK, &apiTokens) } -// CreateAccessToken create access tokens +// CreateAccessToken creates an access token for doer func CreateAccessToken(ctx *context.APIContext) { // swagger:operation POST /users/{username}/tokens user userCreateToken // --- - // summary: Create an access token + // summary: Generate an access token for the current user // consumes: // - application/json // produces: @@ -141,11 +141,11 @@ func CreateAccessToken(ctx *context.APIContext) { }) } -// DeleteAccessToken delete access tokens +// DeleteAccessToken deletes an access token from doer's account func DeleteAccessToken(ctx *context.APIContext) { // swagger:operation DELETE /users/{username}/tokens/{token} user userDeleteAccessToken // --- - // summary: delete an access token + // summary: Delete an access token from current user's account // produces: // - application/json // parameters: @@ -214,7 +214,7 @@ func DeleteAccessToken(ctx *context.APIContext) { func CreateOauth2Application(ctx *context.APIContext) { // swagger:operation POST /user/applications/oauth2 user userCreateOAuth2Application // --- - // summary: creates a new OAuth2 application + // summary: Creates a new OAuth2 application // produces: // - application/json // parameters: @@ -298,11 +298,11 @@ func ListOauth2Applications(ctx *context.APIContext) { ctx.JSON(http.StatusOK, &apiApps) } -// DeleteOauth2Application delete OAuth2 Application +// DeleteOauth2Application delete OAuth2 application func DeleteOauth2Application(ctx *context.APIContext) { // swagger:operation DELETE /user/applications/oauth2/{id} user userDeleteOAuth2Application // --- - // summary: delete an OAuth2 Application + // summary: Delete an OAuth2 application // produces: // - application/json // parameters: @@ -334,11 +334,11 @@ func DeleteOauth2Application(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// GetOauth2Application get OAuth2 Application +// GetOauth2Application returns an OAuth2 application func GetOauth2Application(ctx *context.APIContext) { // swagger:operation GET /user/applications/oauth2/{id} user userGetOAuth2Application // --- - // summary: get an OAuth2 Application + // summary: Get an OAuth2 application // produces: // - application/json // parameters: @@ -377,11 +377,11 @@ func GetOauth2Application(ctx *context.APIContext) { ctx.JSON(http.StatusOK, convert.ToOAuth2Application(app)) } -// UpdateOauth2Application update OAuth2 Application +// UpdateOauth2Application updates an OAuth2 application func UpdateOauth2Application(ctx *context.APIContext) { // swagger:operation PATCH /user/applications/oauth2/{id} user userUpdateOAuth2Application // --- - // summary: update an OAuth2 Application, this includes regenerating the client secret + // summary: Update an OAuth2 application, this includes regenerating the client secret // produces: // - application/json // parameters: diff --git a/routers/api/v1/user/avatar.go b/routers/api/v1/user/avatar.go index 453682a37b..2b0659c251 100644 --- a/routers/api/v1/user/avatar.go +++ b/routers/api/v1/user/avatar.go @@ -13,11 +13,11 @@ import ( user_service "forgejo.org/services/user" ) -// UpdateAvatar updates the Avatar of an User +// UpdateAvatar updates doer's avatar func UpdateAvatar(ctx *context.APIContext) { // swagger:operation POST /user/avatar user userUpdateAvatar // --- - // summary: Update Avatar + // summary: Update avatar of the current user // produces: // - application/json // parameters: @@ -49,11 +49,11 @@ func UpdateAvatar(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// DeleteAvatar deletes the Avatar of an User +// DeleteAvatar deletes doer's avatar func DeleteAvatar(ctx *context.APIContext) { // swagger:operation DELETE /user/avatar user userDeleteAvatar // --- - // summary: Delete Avatar + // summary: Delete avatar of the current user. It will be replaced by a default one // produces: // - application/json // responses: diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go index 03d8d14b90..38da23442d 100644 --- a/routers/api/v1/user/email.go +++ b/routers/api/v1/user/email.go @@ -16,12 +16,12 @@ import ( user_service "forgejo.org/services/user" ) -// ListEmails list all of the authenticated user's email addresses +// ListEmails lists doer's all email addresses // see https://github.com/gogits/go-gogs-client/wiki/Users-Emails#list-email-addresses-for-a-user func ListEmails(ctx *context.APIContext) { // swagger:operation GET /user/emails user userListEmails // --- - // summary: List the authenticated user's email addresses + // summary: List all email addresses of the current user // produces: // - application/json // responses: @@ -44,11 +44,11 @@ func ListEmails(ctx *context.APIContext) { ctx.JSON(http.StatusOK, &apiEmails) } -// AddEmail add an email address +// AddEmail adds an email address to doer's account func AddEmail(ctx *context.APIContext) { // swagger:operation POST /user/emails user userAddEmail // --- - // summary: Add email addresses + // summary: Add an email addresses to the current user's account // produces: // - application/json // parameters: @@ -102,11 +102,11 @@ func AddEmail(ctx *context.APIContext) { ctx.JSON(http.StatusCreated, apiEmails) } -// DeleteEmail delete email +// DeleteEmail deletes an email address from doer's account func DeleteEmail(ctx *context.APIContext) { // swagger:operation DELETE /user/emails user userDeleteEmail // --- - // summary: Delete email addresses + // summary: Delete email addresses from the current user's account // produces: // - application/json // parameters: diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 886e33b205..bb7f3d3522 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -249,11 +249,11 @@ type swaggerUserCurrentPostGPGKey struct { Form api.CreateGPGKeyOption } -// CreateGPGKey create a GPG key belonging to the authenticated user +// CreateGPGKey adds a GPG public key doer's account func CreateGPGKey(ctx *context.APIContext) { // swagger:operation POST /user/gpg_keys user userCurrentPostGPGKey // --- - // summary: Create a GPG key + // summary: Add a GPG public key to current user's account // consumes: // - application/json // produces: @@ -274,11 +274,11 @@ func CreateGPGKey(ctx *context.APIContext) { CreateUserGPGKey(ctx, *form, ctx.Doer.ID) } -// DeleteGPGKey remove a GPG key belonging to the authenticated user +// DeleteGPGKey removes a GPG public key from doer's account func DeleteGPGKey(ctx *context.APIContext) { // swagger:operation DELETE /user/gpg_keys/{id} user userCurrentDeleteGPGKey // --- - // summary: Remove a GPG key + // summary: Remove a GPG public key from current user's account // produces: // - application/json // parameters: diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go index 134b448718..53455bcd75 100644 --- a/routers/api/v1/user/settings.go +++ b/routers/api/v1/user/settings.go @@ -14,11 +14,11 @@ import ( user_service "forgejo.org/services/user" ) -// GetUserSettings returns user settings +// GetUserSettings returns doer's account settings func GetUserSettings(ctx *context.APIContext) { // swagger:operation GET /user/settings user getUserSettings // --- - // summary: Get user settings + // summary: Get current user's account settings // produces: // - application/json // responses: @@ -31,11 +31,11 @@ func GetUserSettings(ctx *context.APIContext) { ctx.JSON(http.StatusOK, convert.User2UserSettings(ctx.Doer)) } -// UpdateUserSettings returns user settings +// UpdateUserSettings updates settings in doer's account func UpdateUserSettings(ctx *context.APIContext) { // swagger:operation PATCH /user/settings user updateUserSettings // --- - // summary: Update user settings + // summary: Update settings in current user's account // parameters: // - name: body // in: body diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 5bdd56c892..2bd2829d09 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -33,6 +33,11 @@ func Search(ctx *context.APIContext) { // description: ID of the user to search for // type: integer // format: int64 + // - name: sort + // in: query + // description: sort order of results + // type: string + // enum: [oldest, newest, alphabetically, reversealphabetically, recentupdate, leastupdate] // - name: page // in: query // description: page number of results to return (1-based) @@ -81,6 +86,7 @@ func Search(ctx *context.APIContext) { SearchByEmail: true, Visible: visible, ListOptions: listOptions, + OrderBy: utils.GetDbSearchOrder(ctx), }) if err != nil { ctx.JSON(http.StatusInternalServerError, map[string]any{ @@ -260,7 +266,7 @@ func ListBlockedUsers(ctx *context.APIContext) { func BlockUser(ctx *context.APIContext) { // swagger:operation PUT /user/block/{username} user userBlockUser // --- - // summary: Blocks a user from the doer. + // summary: Blocks a user from the doer // produces: // - application/json // parameters: @@ -293,7 +299,7 @@ func BlockUser(ctx *context.APIContext) { func UnblockUser(ctx *context.APIContext) { // swagger:operation PUT /user/unblock/{username} user userUnblockUser // --- - // summary: Unblocks a user from the doer. + // summary: Unblocks a user from the doer // produces: // - application/json // parameters: diff --git a/routers/api/v1/utils/db_search_order.go b/routers/api/v1/utils/db_search_order.go new file mode 100644 index 0000000000..f089ba5f16 --- /dev/null +++ b/routers/api/v1/utils/db_search_order.go @@ -0,0 +1,28 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package utils + +import ( + "forgejo.org/models/db" + "forgejo.org/services/context" +) + +func GetDbSearchOrder(ctx *context.APIContext) db.SearchOrderBy { + switch ctx.FormString("sort") { + case "oldest": + return db.SearchOrderByOldest + case "newest": + return db.SearchOrderByNewest + case "alphabetically": + return db.SearchOrderByAlphabetically + case "reversealphabetically": + return db.SearchOrderByAlphabeticallyReverse + case "recentupdate": + return db.SearchOrderByRecentUpdated + case "leastupdate": + return db.SearchOrderByLeastUpdated + default: + return db.SearchOrderByAlphabetically + } +} diff --git a/routers/common/errpage.go b/routers/common/errpage.go index 907c278ab1..4dc5a58858 100644 --- a/routers/common/errpage.go +++ b/routers/common/errpage.go @@ -15,7 +15,6 @@ import ( "forgejo.org/modules/templates" "forgejo.org/modules/web/middleware" "forgejo.org/modules/web/routing" - "forgejo.org/services/context" ) const tplStatus500 base.TplName = "status/500" @@ -36,8 +35,8 @@ func RenderPanicErrorPage(w http.ResponseWriter, req *http.Request, err any) { httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform") w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) - tmplCtx := context.TemplateContext{} - tmplCtx["Locale"] = middleware.Locale(w, req) + tmplCtx := templates.NewContext(req.Context()) + tmplCtx.Locale = middleware.Locale(w, req) ctxData := middleware.GetContextData(req.Context()) // This recovery handler could be called without Gitea's web context, so we shouldn't touch that context too much. diff --git a/routers/common/middleware.go b/routers/common/middleware.go index d44f046a1e..7bc4890a43 100644 --- a/routers/common/middleware.go +++ b/routers/common/middleware.go @@ -46,7 +46,11 @@ func ProtocolMiddlewares() (handlers []any) { defer finished() trace.Log(ctx, "method", req.Method) trace.Log(ctx, "url", req.RequestURI) - next.ServeHTTP(context.WrapResponseWriter(resp), req.WithContext(cache.WithCacheContext(ctx))) + + respWriter := context.WrapResponseWriter(resp) + next.ServeHTTP(respWriter, req.WithContext(cache.WithCacheContext(ctx))) + + trace.Logf(ctx, "status", "%d", respWriter.WrittenStatus()) }) }) diff --git a/routers/init.go b/routers/init.go index 90a1cb1e89..9a304527fa 100644 --- a/routers/init.go +++ b/routers/init.go @@ -38,6 +38,7 @@ import ( "forgejo.org/services/auth/source/oauth2" "forgejo.org/services/automerge" "forgejo.org/services/cron" + federation_service "forgejo.org/services/federation" feed_service "forgejo.org/services/feed" indexer_service "forgejo.org/services/indexer" "forgejo.org/services/mailer" @@ -122,6 +123,7 @@ func InitWebInstalled(ctx context.Context) { mailer.NewContext(ctx) mustInit(cache.Init) mustInit(feed_service.Init) + mustInit(federation_service.Init) mustInit(uinotification.Init) mustInitCtx(ctx, archiver.Init) diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go index c7748b01c8..a856a7a00a 100644 --- a/routers/private/hook_post_receive.go +++ b/routers/private/hook_post_receive.go @@ -205,7 +205,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) { // post update for agit pull request // FIXME: use pr.Flow to test whether it's an Agit PR or a GH PR - if git.SupportProcReceive && refFullName.IsPull() { + if refFullName.IsPull() { if repo == nil { repo = loadRepository(ctx, ownerName, repoName) if ctx.Written() { diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 4c0e9a8551..45992e8522 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -205,7 +205,7 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { preReceiveBranch(ourCtx, oldCommitID, newCommitID, refFullName) case refFullName.IsTag(): preReceiveTag(ourCtx, oldCommitID, newCommitID, refFullName) - case git.SupportProcReceive && refFullName.IsFor(): + case refFullName.IsFor(): preReceiveFor(ourCtx, oldCommitID, newCommitID, refFullName) default: if ourCtx.isOverQuota { diff --git a/routers/private/hook_proc_receive.go b/routers/private/hook_proc_receive.go index cd45794261..9f6e23f158 100644 --- a/routers/private/hook_proc_receive.go +++ b/routers/private/hook_proc_receive.go @@ -7,7 +7,6 @@ import ( "net/http" repo_model "forgejo.org/models/repo" - "forgejo.org/modules/git" "forgejo.org/modules/log" "forgejo.org/modules/private" "forgejo.org/modules/web" @@ -18,10 +17,6 @@ import ( // HookProcReceive proc-receive hook - only handles agit Proc-Receive requests at present func HookProcReceive(ctx *gitea_context.PrivateContext) { opts := web.GetForm(ctx).(*private.HookOptions) - if !git.SupportProcReceive { - ctx.Status(http.StatusNotFound) - return - } results, err := agit.ProcReceive(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, opts) if err != nil { diff --git a/routers/private/manager.go b/routers/private/manager.go index 7ab198f71b..90b48256df 100644 --- a/routers/private/manager.go +++ b/routers/private/manager.go @@ -145,6 +145,7 @@ func AddLogger(ctx *context.PrivateContext) { writerMode.Prefix, _ = opts.Config["prefix"].(string) writerMode.Expression, _ = opts.Config["expression"].(string) + writerMode.Exclusion, _ = opts.Config["exclusion"].(string) switch writerType { case "console": diff --git a/routers/private/serv.go b/routers/private/serv.go index 4c5b7bbccb..7f08d4ca34 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -9,12 +9,12 @@ import ( "strings" asymkey_model "forgejo.org/models/asymkey" + "forgejo.org/models/auth" "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/modules/git" "forgejo.org/modules/log" "forgejo.org/modules/private" "forgejo.org/modules/setting" @@ -23,6 +23,27 @@ import ( wiki_service "forgejo.org/services/wiki" ) +func checkTwoFactor(ctx *context.PrivateContext, user *user_model.User) { + if !user.MustHaveTwoFactor() { + return + } + + hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, user.ID) + if err != nil { + log.Error("Error getting 2fa: %s", err) + ctx.JSON(http.StatusInternalServerError, private.Response{ + Err: fmt.Sprintf("Error getting 2fa: %s", err), + }) + return + } + if !hasTwoFactor { + ctx.JSON(http.StatusForbidden, private.Response{ + UserMsg: ctx.Locale.TrString("error.must_enable_2fa", fmt.Sprintf("%suser/settings/security", setting.AppURL)), + }) + return + } +} + // ServNoCommand returns information about the provided keyid func ServNoCommand(ctx *context.PrivateContext) { keyID := ctx.ParamsInt64(":keyid") @@ -70,6 +91,12 @@ func ServNoCommand(ctx *context.PrivateContext) { }) return } + + checkTwoFactor(ctx, user) + if ctx.Written() { + return + } + results.Owner = user } ctx.JSON(http.StatusOK, &results) @@ -267,6 +294,11 @@ func ServCommand(ctx *context.PrivateContext) { return } + checkTwoFactor(ctx, user) + if ctx.Written() { + return + } + results.UserName = user.Name if !user.KeepEmailPrivate { results.UserEmail = user.Email @@ -303,7 +335,7 @@ func ServCommand(ctx *context.PrivateContext) { // the permission check to read. The pre-receive hook will do another // permission check which ensure for non AGit flow references the write // permission is checked. - if git.SupportProcReceive && unitType == unit.TypeCode && ctx.FormString("verb") == "git-receive-pack" { + if unitType == unit.TypeCode && ctx.FormString("verb") == "git-receive-pack" { mode = perm.AccessModeRead } diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 2c6dc76305..c352b6ad1a 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -190,6 +190,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source { AdminGroup: form.Oauth2AdminGroup, GroupTeamMap: form.Oauth2GroupTeamMap, GroupTeamMapRemoval: form.Oauth2GroupTeamMapRemoval, + AllowUsernameChange: form.AllowUsernameChange, } } diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go index dcc99ff1a8..e1c3a5f9ee 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -128,6 +128,7 @@ func Config(ctx *context.Context) { ctx.Data["AppBuiltWith"] = setting.AppBuiltWith ctx.Data["Domain"] = setting.Domain ctx.Data["OfflineMode"] = setting.OfflineMode + ctx.Data["GlobalTwoFactorRequirement"] = setting.GlobalTwoFactorRequirement ctx.Data["RunUser"] = setting.RunUser ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode) ctx.Data["GitVersion"] = git.VersionInfo() diff --git a/routers/web/admin/reports.go b/routers/web/admin/reports.go new file mode 100644 index 0000000000..ac43d1296f --- /dev/null +++ b/routers/web/admin/reports.go @@ -0,0 +1,157 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package admin + +import ( + "fmt" + "net/http" + + "forgejo.org/models/issues" + "forgejo.org/models/moderation" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/user" + "forgejo.org/modules/base" + "forgejo.org/services/context" + moderation_service "forgejo.org/services/moderation" +) + +const ( + tplModerationReports base.TplName = "admin/moderation/reports" + tplModerationReportDetails base.TplName = "admin/moderation/report_details" +) + +// AbuseReports renders the reports overview page from admin moderation section. +func AbuseReports(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("admin.moderation.reports") + ctx.Data["PageIsAdminModerationReports"] = true + + reports, err := moderation.GetOpenReports(ctx) + if err != nil { + ctx.ServerError("Failed to load abuse reports", err) + return + } + + ctx.Data["Reports"] = reports + ctx.Data["AbuseCategories"] = moderation.AbuseCategoriesTranslationKeys + ctx.Data["GhostUserName"] = user.GhostUserName + + ctx.HTML(http.StatusOK, tplModerationReports) +} + +// AbuseReportDetails renders a report details page opened from the reports overview from admin moderation section. +func AbuseReportDetails(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("admin.moderation.reports") + ctx.Data["PageIsAdminModerationReports"] = true + + ctx.Data["Type"] = ctx.ParamsInt64(":type") + ctx.Data["ID"] = ctx.ParamsInt64(":id") + + contentType := moderation.ReportedContentType(ctx.ParamsInt64(":type")) + + if !contentType.IsValid() { + ctx.Flash.Error("Invalid content type") + return + } + + reports, err := moderation.GetOpenReportsByTypeAndContentID(ctx, contentType, ctx.ParamsInt64(":id")) + if err != nil { + ctx.ServerError("Failed to load reports", err) + return + } + if len(reports) == 0 { + // something is wrong + ctx.HTML(http.StatusOK, tplModerationReportDetails) + return + } + + ctx.Data["Reports"] = reports + ctx.Data["AbuseCategories"] = moderation.AbuseCategoriesTranslationKeys + ctx.Data["GhostUserName"] = user.GhostUserName + + ctx.Data["GetShadowCopyMap"] = moderation_service.GetShadowCopyMap + + if err = setReportedContentDetails(ctx, reports[0]); err != nil { + if user.IsErrUserNotExist(err) || issues.IsErrCommentNotExist(err) || issues.IsErrIssueNotExist(err) || repo_model.IsErrRepoNotExist(err) { + ctx.Data["ContentReference"] = ctx.Tr("admin.moderation.deleted_content_ref", reports[0].ContentType, reports[0].ContentID) + } else { + ctx.ServerError("Failed to load reported content details", err) + return + } + } + + ctx.HTML(http.StatusOK, tplModerationReportDetails) +} + +// setReportedContentDetails adds some values into context data for the given report +// (icon name, a reference, the URL and in case of issues and comments also the poster name). +func setReportedContentDetails(ctx *context.Context, report *moderation.AbuseReportDetailed) error { + contentReference := "" + var contentURL string + var poster string + contentType := report.ContentType + contentID := report.ContentID + + ctx.Data["ContentTypeIconName"] = report.ContentTypeIconName() + + switch contentType { + case moderation.ReportedContentTypeUser: + reportedUser, err := user.GetUserByID(ctx, contentID) + if err != nil { + return err + } + + contentReference = reportedUser.Name + contentURL = reportedUser.HomeLink() + case moderation.ReportedContentTypeRepository: + repo, err := repo_model.GetRepositoryByID(ctx, contentID) + if err != nil { + return err + } + + contentReference = repo.FullName() + contentURL = repo.Link() + case moderation.ReportedContentTypeIssue: + issue, err := issues.GetIssueByID(ctx, contentID) + if err != nil { + return err + } + if err = issue.LoadRepo(ctx); err != nil { + return err + } + if err = issue.LoadPoster(ctx); err != nil { + return err + } + if issue.Poster != nil { + poster = issue.Poster.Name + } + + contentReference = fmt.Sprintf("%s#%d", issue.Repo.FullName(), issue.Index) + contentURL = issue.Link() + case moderation.ReportedContentTypeComment: + comment, err := issues.GetCommentByID(ctx, contentID) + if err != nil { + return err + } + if err = comment.LoadIssue(ctx); err != nil { + return err + } + if err = comment.Issue.LoadRepo(ctx); err != nil { + return err + } + if err = comment.LoadPoster(ctx); err != nil { + return err + } + if comment.Poster != nil { + poster = comment.Poster.Name + } + + contentURL = comment.Link(ctx) + contentReference = contentURL + } + + ctx.Data["ContentReference"] = contentReference + ctx.Data["ContentURL"] = contentURL + ctx.Data["Poster"] = poster + return nil +} diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index e8e5d2c54b..1119e1947b 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -489,7 +489,7 @@ func AuthorizeOAuth(ctx *context.Context) { }, form.RedirectURI) return } - if err := ctx.Session.Set("CodeChallengeMethod", form.CodeChallenge); err != nil { + if err := ctx.Session.Set("CodeChallenge", form.CodeChallenge); err != nil { handleAuthorizeError(ctx, AuthorizeError{ ErrorCode: ErrorCodeServerError, ErrorDescription: "cannot set code challenge", @@ -668,6 +668,11 @@ func GrantApplicationOAuth(ctx *context.Context) { // OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities func OIDCWellKnown(ctx *context.Context) { + if !setting.OAuth2.Enabled { + ctx.Status(http.StatusNotFound) + return + } + ctx.Data["SigningKey"] = oauth2.DefaultSigningKey ctx.Data["Issuer"] = strings.TrimSuffix(setting.AppURL, "/") ctx.JSONTemplate("user/auth/oidc_wellknown") diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go index cb6b22e5b7..c645bbdede 100644 --- a/routers/web/auth/password.go +++ b/routers/web/auth/password.go @@ -264,7 +264,7 @@ func ResetPasswdPost(ctx *context.Context) { func MustChangePassword(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("auth.must_change_password") ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password" - ctx.Data["MustChangePassword"] = true + ctx.Data["HideNavbarLinks"] = true ctx.HTML(http.StatusOK, tplMustChangePassword) } diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 01e663a672..7b09c92ee5 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -25,7 +25,7 @@ import ( "forgejo.org/services/context" "github.com/gorilla/feeds" - "github.com/jaytaylor/html2text" + "github.com/inbucket/html2text" ) func toBranchLink(ctx *context.Context, act *activities_model.Action) string { diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go index 646241c021..d24fa6ecc7 100644 --- a/routers/web/feed/release.go +++ b/routers/web/feed/release.go @@ -29,7 +29,7 @@ func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleas if isReleasesOnly { title = ctx.Locale.TrString("repo.release.releases_for", repo.FullName()) - link = &feeds.Link{Href: repo.HTMLURL() + "/release"} + link = &feeds.Link{Href: repo.HTMLURL() + "/releases"} } else { title = ctx.Locale.TrString("repo.release.tags_for", repo.FullName()) link = &feeds.Link{Href: repo.HTMLURL() + "/tags"} diff --git a/routers/web/home.go b/routers/web/home.go index bd9942748a..55dfe2538e 100644 --- a/routers/web/home.go +++ b/routers/web/home.go @@ -9,6 +9,7 @@ import ( "net/http" "strconv" + auth_model "forgejo.org/models/auth" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" user_model "forgejo.org/models/user" @@ -35,18 +36,37 @@ func Home(ctx *context.Context) { if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { ctx.Data["Title"] = ctx.Tr("auth.active_your_account") ctx.HTML(http.StatusOK, auth.TplActivate) - } else if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin { + return + } + if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin { log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr()) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } else if ctx.Doer.MustChangePassword { + return + } + if ctx.Doer.MustChangePassword { ctx.Data["Title"] = ctx.Tr("auth.must_change_password") ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) ctx.Redirect(setting.AppSubURL + "/user/settings/change_password") - } else { - user.Dashboard(ctx) + return } + if ctx.Doer.MustHaveTwoFactor() { + hasTwoFactor, err := auth_model.HasTwoFactorByUID(ctx, ctx.Doer.ID) + if err != nil { + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + log.Error("Error getting 2fa: %s", err) + ctx.Error(http.StatusInternalServerError, "HasTwoFactorByUID", err.Error()) + return + } + if !hasTwoFactor { + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + ctx.Redirect(setting.AppSubURL + "/user/settings/security") + return + } + } + + user.Dashboard(ctx) return // Check non-logged users landing page. } else if setting.LandingPageURL != setting.LandingPageHome { diff --git a/routers/web/misc/misc.go b/routers/web/misc/misc.go index 87b5247599..22fdccf79f 100644 --- a/routers/web/misc/misc.go +++ b/routers/web/misc/misc.go @@ -7,7 +7,6 @@ import ( "net/http" "path" - "forgejo.org/modules/git" "forgejo.org/modules/httpcache" "forgejo.org/modules/log" "forgejo.org/modules/setting" @@ -15,10 +14,6 @@ import ( ) func SSHInfo(rw http.ResponseWriter, req *http.Request) { - if !git.SupportProcReceive { - rw.WriteHeader(http.StatusNotFound) - return - } rw.Header().Set("content-type", "text/json;charset=UTF-8") _, err := rw.Write([]byte(`{"type":"agit","version":1}`)) if err != nil { diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index c83242754b..9b4e01597b 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -71,6 +71,9 @@ func SettingsPost(ctx *context.Context) { ctx.Data["PageIsSettingsOptions"] = true ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod + ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize + ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth + ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight if ctx.HasError() { ctx.HTML(http.StatusOK, tplSettingsOptions) diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 7aa52ddd4c..0c07e1968e 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -27,7 +27,7 @@ import ( "forgejo.org/services/context" "forgejo.org/services/convert" - "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/model" ) const ( @@ -111,7 +111,7 @@ func List(ctx *context.Context) { ctx.ServerError("GetContentFromEntry", err) return } - wf, err := model.ReadWorkflow(bytes.NewReader(content)) + wf, err := model.ReadWorkflow(bytes.NewReader(content), true) if err != nil { workflow.ErrMsg = ctx.Locale.TrString("actions.runs.invalid_workflow_helper", err.Error()) workflows = append(workflows, workflow) diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index ccdd59f2dd..f4cc2a2cea 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -82,19 +82,19 @@ func RefBlame(ctx *context.Context) { return } - ctx.Data["NumLinesSet"] = true - ctx.Data["NumLines"], err = blob.GetBlobLineCount() - if err != nil { - ctx.ServerError("GetBlobLineCount", err) - return - } - result, err := performBlame(ctx, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormBool("bypass-blame-ignore")) if err != nil { ctx.ServerError("performBlame", err) return } + ctx.Data["NumLinesSet"] = true + numLines := 0 + for _, p := range result.Parts { + numLines += len(p.Lines) + } + ctx.Data["NumLines"] = numLines + ctx.Data["UsesIgnoreRevs"] = result.UsesIgnoreRevs ctx.Data["FaultyIgnoreRevsFile"] = result.FaultyIgnoreRevsFile diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index f3192266ad..408a2844de 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -85,6 +85,17 @@ func Commits(ctx *context.Context) { } ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository) + commitIDs := make([]string, 0, len(commits)) + for _, c := range commits { + commitIDs = append(commitIDs, c.ID.String()) + } + commitTagsMap, err := repo_model.FindTagsByCommitIDs(ctx, ctx.Repo.Repository.ID, commitIDs...) + if err != nil { + log.Error("FindTagsByCommitIDs: %v", err) + ctx.Flash.Error(ctx.Tr("repo.commit.load_tags_failed")) + } else { + ctx.Data["CommitTagsMap"] = commitTagsMap + } ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["CommitCount"] = commitsCount diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index e3020c92d2..feedeef945 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -312,22 +312,16 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch) if !baseIsCommit && !baseIsBranch && !baseIsTag { - // Check if baseBranch is short sha commit hash - if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(ci.BaseBranch); baseCommit != nil { - ci.BaseBranch = baseCommit.ID.String() - ctx.Data["BaseBranch"] = ci.BaseBranch - baseIsCommit = true - } else if ci.BaseBranch == ctx.Repo.GetObjectFormat().EmptyObjectID().String() { + if ci.BaseBranch == ctx.Repo.GetObjectFormat().EmptyObjectID().String() { if isSameRepo { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch)) } else { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadRepo.FullName()) + ":" + util.PathEscapeSegments(ci.HeadBranch)) } - return nil } else { ctx.NotFound("IsRefExist", nil) - return nil } + return nil } ctx.Data["BaseIsCommit"] = baseIsCommit ctx.Data["BaseIsBranch"] = baseIsBranch @@ -514,15 +508,8 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { headIsBranch := ci.HeadGitRepo.IsBranchExist(ci.HeadBranch) headIsTag := ci.HeadGitRepo.IsTagExist(ci.HeadBranch) if !headIsCommit && !headIsBranch && !headIsTag { - // Check if headBranch is short sha commit hash - if headCommit, _ := ci.HeadGitRepo.GetCommit(ci.HeadBranch); headCommit != nil { - ci.HeadBranch = headCommit.ID.String() - ctx.Data["HeadBranch"] = ci.HeadBranch - headIsCommit = true - } else { - ctx.NotFound("IsRefExist", nil) - return nil - } + ctx.NotFound("IsRefExist", nil) + return nil } ctx.Data["HeadIsCommit"] = headIsCommit ctx.Data["HeadIsBranch"] = headIsBranch @@ -533,17 +520,6 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch } - if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) { - if log.IsTrace() { - log.Trace("Permission Denied: User: %-v cannot create/read pull requests in Repo: %-v\nUser in baseRepo has Permissions: %-+v", - ctx.Doer, - baseRepo, - permBase) - } - ctx.NotFound("ParseCompareInfo", nil) - return nil - } - baseBranchRef := ci.BaseBranch if baseIsBranch { baseBranchRef = git.BranchPrefix + ci.BaseBranch diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go index fc82ece4cb..9fb4d78fe3 100644 --- a/routers/web/repo/download.go +++ b/routers/web/repo/download.go @@ -92,7 +92,7 @@ func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) { return nil, nil } - if entry.IsDir() || entry.IsSubModule() { + if entry.IsDir() || entry.IsSubmodule() { ctx.NotFound("getBlobForEntry", nil) return nil, nil } diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 5114cc9c05..3e3cb0016d 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -189,7 +189,7 @@ func editFile(ctx *context.Context, isNewFile bool) { buf = buf[:n] // Only some file types are editable online as text. - if !typesniffer.DetectContentType(buf).IsRepresentableAsText() { + if !typesniffer.DetectContentType(buf, blob.Name()).IsRepresentableAsText() { ctx.NotFound("typesniffer.IsRepresentableAsText", nil) return } diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 650b1d88f4..a60c213113 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -183,9 +183,7 @@ func httpBase(ctx *context.Context) *serviceHandler { if repoExist { // Because of special ref "refs/for" .. , need delay write permission check - if git.SupportProcReceive { - accessMode = perm.AccessModeRead - } + accessMode = perm.AccessModeRead if ctx.Data["IsActionsToken"] == true { taskID := ctx.Data["ActionsTaskID"].(int64) @@ -194,24 +192,19 @@ func httpBase(ctx *context.Context) *serviceHandler { ctx.ServerError("GetTaskByID", err) return nil } - if task.RepoID != repo.ID { + + p, err := access_model.GetActionRepoPermission(ctx, repo, task) + if err != nil { + ctx.ServerError("GetActionRepoPermission", err) + return nil + } + + if !p.CanAccess(accessMode, unitType) { ctx.PlainText(http.StatusForbidden, "User permission denied") return nil } - if task.IsForkPullRequest { - if accessMode > perm.AccessModeRead { - ctx.PlainText(http.StatusForbidden, "User permission denied") - return nil - } - environ = append(environ, fmt.Sprintf("%s=%d", repo_module.EnvActionPerm, perm.AccessModeRead)) - } else { - if accessMode > perm.AccessModeWrite { - ctx.PlainText(http.StatusForbidden, "User permission denied") - return nil - } - environ = append(environ, fmt.Sprintf("%s=%d", repo_module.EnvActionPerm, perm.AccessModeWrite)) - } + environ = append(environ, fmt.Sprintf("%s=%d", repo_module.EnvActionPerm, p.AccessMode)) } else { p, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) if err != nil { diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index ef4589c8e6..0373d06ea0 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2401,10 +2401,6 @@ func UpdateIssueMilestone(ctx *context.Context) { } if ctx.FormBool("htmx") { - renderMilestones(ctx) - if ctx.Written() { - return - } prepareHiddenCommentType(ctx) if ctx.Written() { return @@ -2418,6 +2414,7 @@ func UpdateIssueMilestone(ctx *context.Context) { ctx.ServerError("GetMilestoneByRepoID", err) return } + ctx.Data["OpenMilestones"] = true } else { issue.Milestone = nil } @@ -2775,7 +2772,7 @@ func SearchIssues(ctx *context.Context) { IncludedAnyLabelIDs: includedAnyLabels, MilestoneIDs: includedMilestones, ProjectID: projectID, - SortBy: issue_indexer.SortByCreatedDesc, + SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc), } if since != 0 { @@ -2804,9 +2801,10 @@ func SearchIssues(ctx *context.Context) { } } - // FIXME: It's unsupported to sort by priority repo when searching by indexer, - // it's indeed an regression, but I think it is worth to support filtering by indexer first. - _ = ctx.FormInt64("priority_repo_id") + priorityRepoID := ctx.FormInt64("priority_repo_id") + if priorityRepoID > 0 { + searchOpt.PriorityRepoID = optional.Some(priorityRepoID) + } ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt) if err != nil { @@ -2944,7 +2942,7 @@ func ListIssues(ctx *context.Context) { IsPull: isPull, IsClosed: isClosed, ProjectID: projectID, - SortBy: issue_indexer.SortByCreatedDesc, + SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc), } if since != 0 { searchOpt.UpdatedAfterUnix = optional.Some(since) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 4e365f24ea..18ba2b2a9f 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -360,7 +360,7 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) { ctx.Data["Issue"] = issue if !issue.IsPull { - ctx.NotFound("ViewPullCommits", nil) + ctx.Redirect(issue.Link()) return nil, false } @@ -638,7 +638,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C if pull.Flow == issues_model.PullRequestFlowGithub { headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch) } else { - headBranchExist = git.IsReferenceExist(ctx, baseGitRepo.Path, pull.GetGitRefName()) + headBranchExist = baseGitRepo.IsReferenceExist(pull.GetGitRefName()) } if headBranchExist { @@ -1152,7 +1152,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi ctx.ServerError("GetUserRepoPermission", err) return } - ctx.Data["HeadBranchIsEditable"] = pull.HeadRepo.CanEnableEditor() && issues_model.CanMaintainerWriteToBranch(ctx, headRepoPerm, pull.HeadBranch, ctx.Doer) + ctx.Data["HeadBranchIsEditable"] = pull.HeadRepo.CanEnableEditor() && issues_model.CanMaintainerWriteToBranch(ctx, headRepoPerm, pull.HeadBranch, ctx.Doer) && pull.Flow != issues_model.PullRequestFlowAGit ctx.Data["SourceRepoLink"] = pull.HeadRepo.Link() ctx.Data["HeadBranch"] = pull.HeadBranch } @@ -1445,6 +1445,10 @@ func MergePullRequest(ctx *context.Context) { log.Debug("MergeUnrelatedHistories error: %v", err) ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories")) ctx.JSONRedirect(issue.Link()) + } else if models.IsErrPullRequestHasMerged(err) { + log.Debug("MergePullRequestHasMerged error: %v", err) + ctx.Flash.Error(ctx.Tr("repo.pulls.already_merged")) + ctx.JSONRedirect(issue.Link()) } else if git.IsErrPushOutOfDate(err) { log.Debug("MergePushOutOfDate error: %v", err) ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date")) diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go index b31e2e203a..05eeadc519 100644 --- a/routers/web/repo/render.go +++ b/routers/web/repo/render.go @@ -41,7 +41,7 @@ func RenderFile(ctx *context.Context) { n, _ := util.ReadAtMost(dataRc, buf) buf = buf[:n] - st := typesniffer.DetectContentType(buf) + st := typesniffer.DetectContentType(buf, blob.Name()) isTextFile := st.IsText() rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{}) diff --git a/routers/web/repo/setting/avatar.go b/routers/web/repo/setting/avatar.go index 84d7cccdb8..20e211316d 100644 --- a/routers/web/repo/setting/avatar.go +++ b/routers/web/repo/setting/avatar.go @@ -45,7 +45,7 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error { if err != nil { return fmt.Errorf("io.ReadAll: %w", err) } - st := typesniffer.DetectContentType(data) + st := typesniffer.DetectContentType(data, "") if !st.IsImage() || st.IsSvgImage() { return errors.New(ctx.Locale.TrString("settings.uploaded_avatar_not_a_image")) } diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index b9cb86bd08..9930d03e8e 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -291,7 +291,7 @@ func LFSFileGet(ctx *context.Context) { } buf = buf[:n] - st := typesniffer.DetectContentType(buf) + st := typesniffer.DetectContentType(buf, "") ctx.Data["IsTextFile"] = st.IsText() isRepresentableAsText := st.IsRepresentableAsText() diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 6f35e19880..595fdace83 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -6,6 +6,7 @@ package setting import ( + go_context "context" "errors" "fmt" "net/http" @@ -589,6 +590,23 @@ func SettingsPost(ctx *context.Context) { ctx.ServerError("UpdatePushMirrorInterval", err) return } + + if m.BranchFilter != form.PushMirrorBranchFilter { + // replace `remote..push` in config and db + m.BranchFilter = form.PushMirrorBranchFilter + if err := db.WithTx(ctx, func(ctx go_context.Context) error { + // Update the DB + if err = repo_model.UpdatePushMirrorBranchFilter(ctx, m); err != nil { + return err + } + // Update the repo config + return mirror_service.UpdatePushMirrorBranchFilter(ctx, m) + }); err != nil { + ctx.ServerError("UpdatePushMirrorBranchFilter", err) + return + } + } + // Background why we are adding it to Queue // If we observed its implementation in the context of `push-mirror-sync` where it // is evident that pushing to the queue is necessary for updates. @@ -684,6 +702,7 @@ func SettingsPost(ctx *context.Context) { SyncOnCommit: form.PushMirrorSyncOnCommit, Interval: interval, RemoteAddress: remoteAddress, + BranchFilter: form.PushMirrorBranchFilter, } var plainPrivateKey []byte diff --git a/routers/web/repo/treelist.go b/routers/web/repo/treelist.go index 5c37f2ebca..20ea9babbe 100644 --- a/routers/web/repo/treelist.go +++ b/routers/web/repo/treelist.go @@ -42,7 +42,7 @@ func isExcludedEntry(entry *git.TreeEntry) bool { return true } - if entry.IsSubModule() { + if entry.IsSubmodule() { return true } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index bb3e1388a8..cd0af75b8f 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -56,7 +56,7 @@ import ( repo_service "forgejo.org/services/repository" files_service "forgejo.org/services/repository/files" - "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/model" _ "golang.org/x/image/bmp" // for processing bmp images _ "golang.org/x/image/webp" // for processing webp images @@ -228,7 +228,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, n, _ := util.ReadAtMost(dataRc, buf) buf = buf[:n] - st := typesniffer.DetectContentType(buf) + st := typesniffer.DetectContentType(buf, blob.Name()) isTextFile := st.IsText() // FIXME: what happens when README file is an image? @@ -262,7 +262,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, } buf = buf[:n] - st = typesniffer.DetectContentType(buf) + st = typesniffer.DetectContentType(buf, blob.Name()) return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil } @@ -434,11 +434,11 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { if err != nil { log.Error("actions.GetContentFromEntry: %v", err) } - _, workFlowErr := model.ReadWorkflow(bytes.NewReader(content)) + _, workFlowErr := model.ReadWorkflow(bytes.NewReader(content), true) if workFlowErr != nil { ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error()) } - } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) { + } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS", ".forgejo/CODEOWNERS"}, ctx.Repo.TreePath) { if rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize); err == nil { _, warnings := issue_model.GetCodeOwnersFromReader(ctx, rc, size > setting.UI.MaxDisplayFileSize) if len(warnings) > 0 { @@ -1057,14 +1057,13 @@ func renderHomeCode(ctx *context.Context) { return } - if entry.IsSubModule() { - subModuleURL, err := ctx.Repo.Commit.GetSubModule(entry.Name()) + if entry.IsSubmodule() { + submodule, err := ctx.Repo.Commit.GetSubmodule(ctx.Repo.TreePath, entry) if err != nil { - HandleGitError(ctx, "Repo.Commit.GetSubModule", err) + HandleGitError(ctx, "Repo.Commit.GetSubmodule", err) return } - subModuleFile := git.NewSubModuleFile(ctx.Repo.Commit, subModuleURL, entry.ID.String()) - ctx.Redirect(subModuleFile.RefURL(setting.AppURL, ctx.Repo.Repository.FullName(), setting.SSH.Domain)) + ctx.Redirect(submodule.ResolveUpstreamURL(ctx.Repo.Repository.HTMLURL())) } else if entry.IsDir() { renderDirectory(ctx) } else { @@ -1222,7 +1221,7 @@ func checkOutdatedBranch(ctx *context.Context) { } if dbBranch.CommitID != commit.ID.String() { - ctx.Flash.Warning(ctx.Tr("repo.error.broken_git_hook", "https://docs.gitea.com/help/faq#push-hook--webhook--actions-arent-running"), true) + ctx.Flash.Warning(ctx.Tr("warning.repository.out_of_sync"), true) } } diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 400ee71f08..e0ce88b582 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -151,7 +151,7 @@ func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser * return fmt.Errorf("io.ReadAll: %w", err) } - st := typesniffer.DetectContentType(data) + st := typesniffer.DetectContentType(data, "") if !st.IsImage() || st.IsSvgImage() { return errors.New(ctx.Locale.TrString("settings.uploaded_avatar_not_a_image")) } diff --git a/routers/web/user/setting/security/2fa.go b/routers/web/user/setting/security/2fa.go index 8b362c4f08..d23917f8b2 100644 --- a/routers/web/user/setting/security/2fa.go +++ b/routers/web/user/setting/security/2fa.go @@ -56,6 +56,21 @@ func DisableTwoFactor(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsSecurity"] = true + if ctx.Doer.MustHaveTwoFactor() { + ctx.NotFound("DisableTwoFactor", nil) + return + } + + disableTwoFactor(ctx) + if ctx.Written() { + return + } + + ctx.Flash.Success(ctx.Tr("settings.twofa_disabled")) + ctx.Redirect(setting.AppSubURL + "/user/settings/security") +} + +func disableTwoFactor(ctx *context.Context) { t, err := auth.GetTwoFactorByUID(ctx, ctx.Doer.ID) if err != nil { if auth.IsErrTwoFactorNotEnrolled(err) { @@ -82,9 +97,6 @@ func DisableTwoFactor(ctx *context.Context) { ctx.ServerError("SendDisabledTOTP", err) return } - - ctx.Flash.Success(ctx.Tr("settings.twofa_disabled")) - ctx.Redirect(setting.AppSubURL + "/user/settings/security") } func twofaGenerateSecretAndQr(ctx *context.Context) bool { @@ -172,7 +184,6 @@ func EnrollTwoFactor(ctx *context.Context) { // EnrollTwoFactorPost handles enrolling the user into 2FA. func EnrollTwoFactorPost(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.TwoFactorAuthForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsSecurity"] = true @@ -188,6 +199,12 @@ func EnrollTwoFactorPost(ctx *context.Context) { return } + enrollTwoFactor(ctx) +} + +func enrollTwoFactor(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.TwoFactorAuthForm) + if ctx.HasError() { if !twofaGenerateSecretAndQr(ctx) { return @@ -213,10 +230,10 @@ func EnrollTwoFactorPost(ctx *context.Context) { return } - t = &auth.TwoFactor{ + twoFactor := &auth.TwoFactor{ UID: ctx.Doer.ID, } - token := t.GenerateScratchToken() + token := twoFactor.GenerateScratchToken() // Now we have to delete the secrets - because if we fail to insert then it's highly likely that they have already been used // If we can detect the unique constraint failure below we can move this to after the NewTwoFactor @@ -238,7 +255,7 @@ func EnrollTwoFactorPost(ctx *context.Context) { return } - if err = auth.NewTwoFactor(ctx, t, secret); err != nil { + if err := auth.NewTwoFactor(ctx, twoFactor, secret); err != nil { // FIXME: We need to handle a unique constraint fail here it's entirely possible that another request has beaten us. // If there is a unique constraint fail we should just tolerate the error ctx.ServerError("SettingsTwoFactor: Failed to save two factor", err) @@ -248,3 +265,41 @@ func EnrollTwoFactorPost(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.twofa_enrolled", token)) ctx.Redirect(setting.AppSubURL + "/user/settings/security") } + +// ReenrollTwoFactor shows the page where the user can reenroll 2FA. +func ReenrollTwoFactor(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("settings") + ctx.Data["PageIsSettingsSecurity"] = true + ctx.Data["ReenrollTwofa"] = true + + _, err := auth.GetTwoFactorByUID(ctx, ctx.Doer.ID) + if auth.IsErrTwoFactorNotEnrolled(err) { + ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled")) + ctx.Redirect(setting.AppSubURL + "/user/settings/security") + return + } + if err != nil { + ctx.ServerError("SettingsTwoFactor: GetTwoFactorByUID", err) + return + } + + if !twofaGenerateSecretAndQr(ctx) { + return + } + + ctx.HTML(http.StatusOK, tplSettingsTwofaEnroll) +} + +// ReenrollTwoFactorPost handles reenrolling the user 2FA. +func ReenrollTwoFactorPost(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("settings") + ctx.Data["PageIsSettingsSecurity"] = true + ctx.Data["ReenrollTwofa"] = true + + disableTwoFactor(ctx) + if ctx.Written() { + return + } + + enrollTwoFactor(ctx) +} diff --git a/routers/web/web.go b/routers/web/web.go index 6cca2a9f2e..f1cbc6b7ad 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -6,9 +6,11 @@ package web import ( gocontext "context" + "fmt" "net/http" "strings" + auth_model "forgejo.org/models/auth" "forgejo.org/models/perm" quota_model "forgejo.org/models/quota" "forgejo.org/models/unit" @@ -169,6 +171,19 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont ctx.Redirect(setting.AppSubURL + "/") return } + + if ctx.Doer.MustHaveTwoFactor() && !strings.HasPrefix(ctx.Req.URL.Path, "/user/settings/security") { + hasTwoFactor, err := auth_model.HasTwoFactorByUID(ctx, ctx.Doer.ID) + if err != nil { + log.Error("Error getting 2fa: %s", err) + ctx.Error(http.StatusInternalServerError, "HasTwoFactorByUID", err.Error()) + return + } + if !hasTwoFactor { + ctx.Redirect(setting.AppSubURL + "/user/settings/security") + return + } + } } // Redirect to dashboard (or alternate location) if user tries to visit any non-login page. @@ -309,6 +324,20 @@ func registerRoutes(m *web.Route) { } } + requiredTwoFactor := func(ctx *context.Context) { + if !ctx.Doer.MustHaveTwoFactor() { + return + } + + hasTwoFactor, err := auth_model.HasTwoFactorByUID(ctx, ctx.Doer.ID) + if err != nil { + ctx.Error(http.StatusInternalServerError, fmt.Sprintf("Error getting 2fa: %s", err)) + return + } + ctx.Data["MustEnableTwoFactor"] = !hasTwoFactor + ctx.Data["HideNavbarLinks"] = !hasTwoFactor + } + openIDSignInEnabled := func(ctx *context.Context) { if !setting.Service.EnableOpenIDSignIn { ctx.Error(http.StatusForbidden) @@ -564,6 +593,8 @@ func registerRoutes(m *web.Route) { m.Post("/disable", security.DisableTwoFactor) m.Get("/enroll", security.EnrollTwoFactor) m.Post("/enroll", web.Bind(forms.TwoFactorAuthForm{}), security.EnrollTwoFactorPost) + m.Get("/reenroll", security.ReenrollTwoFactor) + m.Post("/reenroll", web.Bind(forms.TwoFactorAuthForm{}), security.ReenrollTwoFactorPost) }) m.Group("/webauthn", func() { m.Post("/request_register", web.Bind(forms.WebauthnRegistrationForm{}), security.WebAuthnRegister) @@ -576,7 +607,7 @@ func registerRoutes(m *web.Route) { m.Post("/toggle_visibility", security.ToggleOpenIDVisibility) }, openIDSignInEnabled) m.Post("/account_link", linkAccountEnabled, security.DeleteAccountLink) - }) + }, requiredTwoFactor) m.Group("/applications", func() { // oauth2 applications @@ -781,7 +812,14 @@ func registerRoutes(m *web.Route) { addSettingsRunnersRoutes() addSettingsVariablesRoutes() }) - }, adminReq, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled)) + + if setting.Moderation.Enabled { + m.Group("/moderation/reports", func() { + m.Get("", admin.AbuseReports) + m.Get("/type/{type:1|2|3|4}/id/{id}", admin.AbuseReportDetails) + }) + } + }, adminReq, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "EnableModeration", setting.Moderation.Enabled)) // ***** END: Admin ***** m.Group("", func() { diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go index be3c2925fe..5f67e436bf 100644 --- a/routers/web/webfinger.go +++ b/routers/web/webfinger.go @@ -58,7 +58,33 @@ func WebfingerQuery(ctx *context.Context) { return } + // Instance actor + if parts[0] == "ghost" { + aliases := []string{ + appURL.String() + "api/v1/activitypub/actor", + } + + links := []*webfingerLink{ + { + Rel: "self", + Type: "application/activity+json", + Href: appURL.String() + "api/v1/activitypub/actor", + }, + } + + ctx.Resp.Header().Add("Access-Control-Allow-Origin", "*") + ctx.JSON(http.StatusOK, &webfingerJRD{ + Subject: fmt.Sprintf("acct:%s@%s", "ghost", appURL.Host), + Aliases: aliases, + Links: links, + }) + ctx.Resp.Header().Set("Content-Type", "application/jrd+json") + + return + } + u, err = user_model.GetUserByName(ctx, parts[0]) + case "mailto": u, err = user_model.GetUserByEmail(ctx, resource.Opaque) if u != nil && u.KeepEmailPrivate { @@ -153,7 +179,7 @@ func WebfingerQuery(ctx *context.Context) { }, { Rel: "http://openid.net/specs/connect/1.0/issuer", - Href: appURL.String(), + Href: strings.TrimSuffix(appURL.String(), "/"), }, } diff --git a/services/actions/TestServiceActions_startTask/action_schedule.yml b/services/actions/TestServiceActions_startTask/action_schedule.yml new file mode 100644 index 0000000000..d0e7234475 --- /dev/null +++ b/services/actions/TestServiceActions_startTask/action_schedule.yml @@ -0,0 +1,41 @@ +# A corrupted cron spec with a valid schedule workflow +- + id: 1 + title: schedule_title1 + specs: + - '* * * * *' + repo_id: 4 + owner_id: 2 + workflow_id: 'workflow1.yml' + trigger_user_id: 2 + ref: main + commit_sha: shashasha + event: "schedule" + event_payload: "fakepayload" + content: | + jobs: + job2: + runs-on: ubuntu-latest + steps: + - run: true + +# A valid cron spec with a corrupted schedule workflow +- + id: 2 + title: schedule_title2 + specs: + - '* * * * *' + repo_id: 4 + owner_id: 2 + workflow_id: 'workflow2.yml' + trigger_user_id: 2 + ref: main + commit_sha: shashasha + event: "schedule" + event_payload: "fakepayload" + content: | + jobs: + job2: { invalid yaml + runs-on: ubuntu-latest + steps: + - run: true diff --git a/services/actions/TestServiceActions_startTask/action_schedule_spec.yml b/services/actions/TestServiceActions_startTask/action_schedule_spec.yml new file mode 100644 index 0000000000..7bcc78f010 --- /dev/null +++ b/services/actions/TestServiceActions_startTask/action_schedule_spec.yml @@ -0,0 +1,15 @@ +# A corrupted cron spec with a valid schedule workflow +- + id: 1 + repo_id: 4 + schedule_id: 1 + next: 1 + spec: 'corrupted * *' + +# A valid cron spec with a corrupted schedule workflow +- + id: 2 + repo_id: 4 + schedule_id: 2 + next: 1 + spec: '* * * * *' diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 755fa648dc..d588c0f178 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -19,7 +19,7 @@ import ( webhook_module "forgejo.org/modules/webhook" commitstatus_service "forgejo.org/services/repository/commitstatus" - "github.com/nektos/act/pkg/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" ) // CreateCommitStatus creates a commit status for the given job. @@ -80,7 +80,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er repo := run.Repo // TODO: store workflow name as a field in ActionRun to avoid parsing runName := path.Base(run.WorkflowID) - if wfs, err := jobparser.Parse(job.WorkflowPayload); err == nil && len(wfs) > 0 { + if wfs, err := jobparser.Parse(job.WorkflowPayload, false); err == nil && len(wfs) > 0 { runName = wfs[0].Name } ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event) diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go index 942c698e73..03428736b8 100644 --- a/services/actions/job_emitter.go +++ b/services/actions/job_emitter.go @@ -13,7 +13,7 @@ import ( "forgejo.org/modules/graceful" "forgejo.org/modules/queue" - "github.com/nektos/act/pkg/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" "xorm.io/builder" ) @@ -142,7 +142,7 @@ func (r *jobStatusResolver) resolve() map[int64]actions_model.Status { } else { // Check if the job has an "if" condition hasIf := false - if wfJobs, _ := jobparser.Parse(r.jobMap[id].WorkflowPayload); len(wfJobs) == 1 { + if wfJobs, _ := jobparser.Parse(r.jobMap[id].WorkflowPayload, false); len(wfJobs) == 1 { _, wfJob := wfJobs[0].Job() hasIf = len(wfJob.If.Value) > 0 } diff --git a/services/actions/job_parser.go b/services/actions/job_parser.go new file mode 100644 index 0000000000..294f1c4a0e --- /dev/null +++ b/services/actions/job_parser.go @@ -0,0 +1,31 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package actions + +import ( + "fmt" + + "code.forgejo.org/forgejo/runner/v9/act/jobparser" +) + +func jobParser(workflow []byte, options ...jobparser.ParseOption) ([]*jobparser.SingleWorkflow, error) { + singleWorkflows, err := jobparser.Parse(workflow, false, options...) + if err != nil { + return nil, err + } + nameToSingleWorkflows := make(map[string][]*jobparser.SingleWorkflow, len(singleWorkflows)) + duplicates := make(map[string]int, len(singleWorkflows)) + for _, singleWorkflow := range singleWorkflows { + id, job := singleWorkflow.Job() + nameToSingleWorkflows[job.Name] = append(nameToSingleWorkflows[job.Name], singleWorkflow) + if len(nameToSingleWorkflows[job.Name]) > 1 { + duplicates[job.Name]++ + job.Name = fmt.Sprintf("%s-%d", job.Name, duplicates[job.Name]) + if err := singleWorkflow.SetJob(id, job); err != nil { + return nil, err + } + } + } + return singleWorkflows, nil +} diff --git a/services/actions/job_parser_test.go b/services/actions/job_parser_test.go new file mode 100644 index 0000000000..9c1361d74e --- /dev/null +++ b/services/actions/job_parser_test.go @@ -0,0 +1,212 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package actions + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestServiceActions_jobParser(t *testing.T) { + for _, testCase := range []struct { + name string + workflow string + singleWorkflows []string + }{ + { + name: "OneJobNoDuplicate", + workflow: ` +jobs: + job1: + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `jobs: + job1: + name: job1 + runs-on: docker + steps: + - run: echo OK +`, + }, + }, + { + name: "MatrixTwoJobsWithSameJobName", + workflow: ` +name: test +jobs: + job1: + name: shadowdefaultmatrixgeneratednames + strategy: + matrix: + version: [1.17, 1.19] + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `name: test +jobs: + job1: + name: shadowdefaultmatrixgeneratednames + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.17 +`, + `name: test +jobs: + job1: + name: shadowdefaultmatrixgeneratednames-1 + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.19 +`, + }, + }, + { + name: "MatrixTwoJobsWithMatrixGeneratedNames", + workflow: ` +name: test +jobs: + job1: + strategy: + matrix: + version: [1.17, 1.19] + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `name: test +jobs: + job1: + name: job1 (1.17) + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.17 +`, + `name: test +jobs: + job1: + name: job1 (1.19) + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.19 +`, + }, + }, + { + name: "MatrixTwoJobsWithDistinctInterpolatedNames", + workflow: ` +name: test +jobs: + job1: + name: myname-${{ matrix.version }} + strategy: + matrix: + version: [1.17, 1.19] + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `name: test +jobs: + job1: + name: myname-1.17 + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.17 +`, + `name: test +jobs: + job1: + name: myname-1.19 + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.19 +`, + }, + }, + { + name: "MatrixTwoJobsWithIdenticalInterpolatedNames", + workflow: ` +name: test +jobs: + job1: + name: myname-${{ matrix.typo }} + strategy: + matrix: + version: [1.17, 1.19] + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `name: test +jobs: + job1: + name: myname- + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.17 +`, + `name: test +jobs: + job1: + name: myname--1 + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.19 +`, + }, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + sw, err := jobParser([]byte(testCase.workflow)) + require.NoError(t, err) + for i, sw := range sw { + actual, err := sw.Marshal() + require.NoError(t, err) + assert.Equal(t, testCase.singleWorkflows[i], string(actual)) + } + }) + } +} diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index e240c996b5..e1df5776fb 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -30,8 +30,8 @@ import ( webhook_module "forgejo.org/modules/webhook" "forgejo.org/services/convert" - "github.com/nektos/act/pkg/jobparser" - "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/model" ) type methodCtx struct{} @@ -345,7 +345,7 @@ func handleWorkflows( Status: actions_model.StatusWaiting, } - if workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content)); err == nil { + if workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content), false); err == nil { notifications, err := workflow.Notifications() if err != nil { log.Error("Notifications: %w", err) @@ -372,7 +372,7 @@ func handleWorkflows( continue } - jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars)) + jobs, err := jobParser(dwf.Content, jobparser.WithVars(vars)) if err != nil { run.Status = actions_model.StatusFailure log.Info("jobparser.Parse: invalid workflow, setting job status to failed: %v", err) @@ -537,7 +537,7 @@ func handleSchedules( crons := make([]*actions_model.ActionSchedule, 0, len(detectedWorkflows)) for _, dwf := range detectedWorkflows { // Check cron job condition. Only working in default branch - workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content)) + workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content), false) if err != nil { log.Error("ReadWorkflow: %v", err) continue diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index cf8b29ead7..1e90ad4b3f 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -18,8 +18,9 @@ import ( "forgejo.org/modules/timeutil" webhook_module "forgejo.org/modules/webhook" - "github.com/nektos/act/pkg/jobparser" - act_model "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" + act_model "code.forgejo.org/forgejo/runner/v9/act/model" + "github.com/robfig/cron/v3" "xorm.io/builder" ) @@ -83,20 +84,33 @@ func startTasks(ctx context.Context) error { } return fmt.Errorf("GetUnit: %w", err) } - if cfg.ActionsConfig().IsWorkflowDisabled(row.Schedule.WorkflowID) { + actionConfig := cfg.ActionsConfig() + if actionConfig.IsWorkflowDisabled(row.Schedule.WorkflowID) { continue } - if err := CreateScheduleTask(ctx, row.Schedule); err != nil { - log.Error("CreateScheduleTask: %v", err) - return err + createAndSchedule := func(row *actions_model.ActionScheduleSpec) (cron.Schedule, error) { + if err := CreateScheduleTask(ctx, row.Schedule); err != nil { + return nil, fmt.Errorf("CreateScheduleTask: %v", err) + } + + // Parse the spec + schedule, err := row.Parse() + if err != nil { + return nil, fmt.Errorf("Parse(Spec=%v): %v", row.Spec, err) + } + return schedule, nil } - // Parse the spec - schedule, err := row.Parse() + schedule, err := createAndSchedule(row) if err != nil { - log.Error("Parse: %v", err) - return err + log.Error("RepoID=%v WorkflowID=%v: %v", row.Schedule.RepoID, row.Schedule.WorkflowID, err) + actionConfig.DisableWorkflow(row.Schedule.WorkflowID) + if err := repo_model.UpdateRepoUnit(ctx, cfg); err != nil { + log.Error("RepoID=%v WorkflowID=%v: CreateScheduleTask: %v", row.Schedule.RepoID, row.Schedule.WorkflowID, err) + return err + } + continue } // Update the spec's next run time and previous run time @@ -142,7 +156,7 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) return err } - workflow, err := act_model.ReadWorkflow(bytes.NewReader(cron.Content)) + workflow, err := act_model.ReadWorkflow(bytes.NewReader(cron.Content), false) if err != nil { return err } @@ -153,7 +167,7 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) run.NotifyEmail = notifications // Parse the workflow specification from the cron schedule - workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars)) + workflows, err := jobParser(cron.Content, jobparser.WithVars(vars)) if err != nil { return err } diff --git a/services/actions/schedule_tasks_test.go b/services/actions/schedule_tasks_test.go index 7073985252..31ed5ec813 100644 --- a/services/actions/schedule_tasks_test.go +++ b/services/actions/schedule_tasks_test.go @@ -7,7 +7,9 @@ import ( "testing" actions_model "forgejo.org/models/actions" + "forgejo.org/models/db" repo_model "forgejo.org/models/repo" + "forgejo.org/models/unit" "forgejo.org/models/unittest" webhook_module "forgejo.org/modules/webhook" @@ -15,6 +17,58 @@ import ( "github.com/stretchr/testify/require" ) +func TestServiceActions_startTask(t *testing.T) { + defer unittest.OverrideFixtures("services/actions/TestServiceActions_startTask")() + require.NoError(t, unittest.PrepareTestDatabase()) + + // Load fixtures that are corrupted and create one valid scheduled workflow + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) + + workflowID := "some.yml" + schedules := []*actions_model.ActionSchedule{ + { + Title: "scheduletitle1", + RepoID: repo.ID, + OwnerID: repo.OwnerID, + WorkflowID: workflowID, + TriggerUserID: repo.OwnerID, + Ref: "branch", + CommitSHA: "fakeSHA", + Event: webhook_module.HookEventSchedule, + EventPayload: "fakepayload", + Specs: []string{"* * * * *"}, + Content: []byte( + ` +jobs: + job2: + runs-on: ubuntu-latest + steps: + - run: true +`), + }, + } + + require.Equal(t, 2, unittest.GetCount(t, actions_model.ActionScheduleSpec{})) + require.NoError(t, actions_model.CreateScheduleTask(t.Context(), schedules)) + require.Equal(t, 3, unittest.GetCount(t, actions_model.ActionScheduleSpec{})) + _, err := db.GetEngine(db.DefaultContext).Exec("UPDATE `action_schedule_spec` SET next = 1") + require.NoError(t, err) + + // After running startTasks an ActionRun row is created for the valid scheduled workflow + require.Empty(t, unittest.GetCount(t, actions_model.ActionRun{WorkflowID: workflowID})) + require.NoError(t, startTasks(t.Context())) + require.NotEmpty(t, unittest.GetCount(t, actions_model.ActionRun{WorkflowID: workflowID})) + + // The invalid workflows loaded from the fixtures are disabled + repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) + actionUnit, err := repo.GetUnit(t.Context(), unit.TypeActions) + require.NoError(t, err) + actionConfig := actionUnit.ActionsConfig() + assert.True(t, actionConfig.IsWorkflowDisabled("workflow2.yml")) + assert.True(t, actionConfig.IsWorkflowDisabled("workflow1.yml")) + assert.False(t, actionConfig.IsWorkflowDisabled("some.yml")) +} + func TestCreateScheduleTask(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: 2}) diff --git a/services/actions/variables.go b/services/actions/variables.go index fed1fd0890..a9f42105a3 100644 --- a/services/actions/variables.go +++ b/services/actions/variables.go @@ -87,7 +87,7 @@ func GetVariable(ctx context.Context, opts actions_model.FindVariablesOpts) (*ac // https://docs.github.com/en/actions/learn-github-actions/variables#naming-conventions-for-configuration-variables // https://docs.github.com/en/actions/security-guides/encrypted-secrets#naming-your-secrets var ( - forbiddenEnvNameCIRx = regexp.MustCompile("(?i)^CI") + forbiddenEnvNameCIRx = regexp.MustCompile("(?i)^CI$") ) func envNameCIRegexMatch(name string) error { diff --git a/services/actions/variables_test.go b/services/actions/variables_test.go new file mode 100644 index 0000000000..f69bc674e1 --- /dev/null +++ b/services/actions/variables_test.go @@ -0,0 +1,17 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package actions + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestServicesAction_envNameCIRegexMatch(t *testing.T) { + require.ErrorContains(t, envNameCIRegexMatch("ci"), "cannot be ci") + require.ErrorContains(t, envNameCIRegexMatch("CI"), "cannot be ci") + assert.NoError(t, envNameCIRegexMatch("CI_SOMETHING")) +} diff --git a/services/actions/workflows.go b/services/actions/workflows.go index fbba3fd667..1bc46bfadc 100644 --- a/services/actions/workflows.go +++ b/services/actions/workflows.go @@ -24,8 +24,8 @@ import ( "forgejo.org/modules/webhook" "forgejo.org/services/convert" - "github.com/nektos/act/pkg/jobparser" - act_model "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" + act_model "code.forgejo.org/forgejo/runner/v9/act/model" ) type InputRequiredErr struct { @@ -56,7 +56,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette return nil, nil, err } - wf, err := act_model.ReadWorkflow(bytes.NewReader(content)) + wf, err := act_model.ReadWorkflow(bytes.NewReader(content), false) if err != nil { return nil, nil, err } @@ -138,7 +138,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette return nil, nil, err } - jobs, err := jobparser.Parse(content, jobparser.WithVars(vars)) + jobs, err := jobParser(content, jobparser.WithVars(vars)) if err != nil { return nil, nil, err } diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index 4fdd15d7ec..fa13c20a7f 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -122,6 +122,18 @@ func (o *OAuth2) Name() string { // representing whether the token exists or not func parseToken(req *http.Request) (string, bool) { _ = req.ParseForm() + if !setting.DisableQueryAuthToken { + // Check token. + if token := req.Form.Get("token"); token != "" { + return token, true + } + // Check access token. + if token := req.Form.Get("access_token"); token != "" { + return token, true + } + } else if req.Form.Get("token") != "" || req.Form.Get("access_token") != "" { + log.Warn("API token sent in query string but DISABLE_QUERY_AUTH_TOKEN=true") + } // check header token if auHead := req.Header.Get("Authorization"); auHead != "" { diff --git a/services/auth/source/oauth2/source.go b/services/auth/source/oauth2/source.go index 5245f88270..a3126cf353 100644 --- a/services/auth/source/oauth2/source.go +++ b/services/auth/source/oauth2/source.go @@ -29,6 +29,7 @@ type Source struct { GroupTeamMapRemoval bool RestrictedGroup string SkipLocalTwoFA bool `json:",omitempty"` + AllowUsernameChange bool // reference to the authSource authSource *auth.Source diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go index cbfe3bd54e..0cdc113379 100644 --- a/services/automerge/automerge.go +++ b/services/automerge/automerge.go @@ -162,7 +162,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) { return } case issues_model.PullRequestFlowAGit: - headBranchExist := git.IsReferenceExist(ctx, baseGitRepo.Path, pr.GetGitRefName()) + headBranchExist := baseGitRepo.IsReferenceExist(pr.GetGitRefName()) if !headBranchExist { log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch(Agit): %s]", pr, pr.HeadRepoID, pr.HeadBranch) return diff --git a/services/context/context.go b/services/context/context.go index 1a839773a8..68074964c8 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -41,7 +41,7 @@ type Render interface { type Context struct { *Base - TemplateContext TemplateContext + TemplateContext *templates.Context Render Render PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData` @@ -64,8 +64,6 @@ type Context struct { Package *Package } -type TemplateContext map[string]any - func init() { web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider { return req.Context().Value(WebContextKey).(*Context) @@ -98,10 +96,11 @@ func GetValidateContext(req *http.Request) (ctx *ValidateContext) { return ctx } -func NewTemplateContextForWeb(ctx *Context) TemplateContext { - tmplCtx := NewTemplateContext(ctx) - tmplCtx["Locale"] = ctx.Locale - tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx) +func NewTemplateContextForWeb(ctx *Context) *templates.Context { + tmplCtx := templates.NewContext(ctx) + tmplCtx.Locale = ctx.Locale + tmplCtx.AvatarUtils = templates.NewAvatarUtils(ctx) + tmplCtx.Data = ctx.Data return tmplCtx } diff --git a/services/context/context_response.go b/services/context/context_response.go index e64f478420..386bdd2652 100644 --- a/services/context/context_response.go +++ b/services/context/context_response.go @@ -17,6 +17,7 @@ import ( "syscall" "time" + "forgejo.org/models/auth" user_model "forgejo.org/models/user" "forgejo.org/modules/base" "forgejo.org/modules/httplib" @@ -129,6 +130,21 @@ func (ctx *Context) RenderWithErr(msg any, tpl base.TplName, form any) { ctx.HTML(http.StatusOK, tpl) } +// validateTwoFactorRequirement sets ctx-data to hide/show ui-elements depending on the GlobalTwoFactorRequirement +func (ctx *Context) validateTwoFactorRequirement() { + if ctx.Doer == nil || !ctx.Doer.MustHaveTwoFactor() { + return + } + + hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, ctx.Doer.ID) + if err != nil { + log.ErrorWithSkip(2, "Error getting 2fa: %s", err) + // fallthrough to set the variables + } + ctx.Data["MustEnableTwoFactor"] = !hasTwoFactor + ctx.Data["HideNavbarLinks"] = !hasTwoFactor +} + // NotFound displays a 404 (Not Found) page and prints the given error, if any. func (ctx *Context) NotFound(logMsg string, logErr error) { ctx.notFoundInternal(logMsg, logErr) @@ -156,6 +172,7 @@ func (ctx *Context) notFoundInternal(logMsg string, logErr error) { return } + ctx.validateTwoFactorRequirement() ctx.Data["IsRepo"] = ctx.Repo.Repository != nil ctx.Data["Title"] = ctx.Locale.TrString("error.not_found.title") ctx.HTML(http.StatusNotFound, tplStatus404) @@ -181,6 +198,7 @@ func (ctx *Context) serverErrorInternal(logMsg string, logErr error) { } } + ctx.validateTwoFactorRequirement() ctx.HTML(http.StatusInternalServerError, tplStatus500) } diff --git a/services/context/context_template.go b/services/context/context_template.go deleted file mode 100644 index 7878d409ca..0000000000 --- a/services/context/context_template.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package context - -import ( - "context" - "time" -) - -var _ context.Context = TemplateContext(nil) - -func NewTemplateContext(ctx context.Context) TemplateContext { - return TemplateContext{"_ctx": ctx} -} - -func (c TemplateContext) parentContext() context.Context { - return c["_ctx"].(context.Context) -} - -func (c TemplateContext) Deadline() (deadline time.Time, ok bool) { - return c.parentContext().Deadline() -} - -func (c TemplateContext) Done() <-chan struct{} { - return c.parentContext().Done() -} - -func (c TemplateContext) Err() error { - return c.parentContext().Err() -} - -func (c TemplateContext) Value(key any) any { - return c.parentContext().Value(key) -} diff --git a/services/context/package.go b/services/context/package.go index b95e02a882..50ffa8eb7c 100644 --- a/services/context/package.go +++ b/services/context/package.go @@ -97,7 +97,7 @@ func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.A return perm.AccessModeNone, nil } - if doer != nil && !doer.IsGhost() && (!doer.IsActive || doer.ProhibitLogin) { + if doer != nil && !doer.IsGhost() && !doer.IsAccessAllowed(ctx) { return perm.AccessModeNone, nil } diff --git a/services/context/repo.go b/services/context/repo.go index cce3a5fa70..c8876d7166 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -644,7 +644,11 @@ func RepoAssignment(ctx *Context) context.CancelFunc { ctx.Data["OpenGraphImageURL"] = repo.SummaryCardURL() ctx.Data["OpenGraphImageWidth"] = cardWidth ctx.Data["OpenGraphImageHeight"] = cardHeight - ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.summary_card_alt", repo.FullName()) + if util.IsEmptyString(repo.Description) { + ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.summary_card_alt", repo.FullName()) + } else { + ctx.Data["OpenGraphImageAltText"] = ctx.Tr("og.repo.summary_card.alt_description", repo.FullName(), repo.Description) + } if repo.IsFork { RetrieveBaseRepo(ctx, repo) diff --git a/services/convert/activitypub_person.go b/services/convert/activitypub_person.go new file mode 100644 index 0000000000..2c05f8c1c0 --- /dev/null +++ b/services/convert/activitypub_person.go @@ -0,0 +1,62 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + "context" + + "forgejo.org/models/activities" + user_model "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + + ap "github.com/go-ap/activitypub" +) + +func ToActivityPubPersonFeedItem(item *activities.FederatedUserActivity) ap.Note { + return ap.Note{ + AttributedTo: ap.IRI(item.ActorURI), + Content: ap.NaturalLanguageValues{{Value: ap.Content(item.NoteContent), Ref: ap.NilLangRef}}, + ID: ap.IRI(item.NoteURL), + URL: ap.IRI(item.OriginalNote), + } +} + +func ToActivityPubPerson(ctx context.Context, user *user_model.User) (*ap.Person, error) { + link := user.APActorID() + person := ap.PersonNew(ap.IRI(link)) + + person.Name = ap.NaturalLanguageValuesNew() + err := person.Name.Set("en", ap.Content(user.FullName)) + if err != nil { + return nil, err + } + + person.PreferredUsername = ap.NaturalLanguageValuesNew() + err = person.PreferredUsername.Set("en", ap.Content(user.Name)) + if err != nil { + return nil, err + } + + person.URL = ap.IRI(user.HTMLURL()) + + person.Icon = ap.Image{ + Type: ap.ImageType, + MediaType: "image/png", + URL: ap.IRI(user.AvatarLink(ctx)), + } + + person.Inbox = ap.IRI(link + "/inbox") + person.Outbox = ap.IRI(link + "/outbox") + + person.PublicKey.ID = ap.IRI(link + "#main-key") + person.PublicKey.Owner = ap.IRI(link) + + publicKeyPem, err := activitypub.GetPublicKey(ctx, user) + if err != nil { + return nil, err + } + person.PublicKey.PublicKeyPem = publicKeyPem + + return person, nil +} diff --git a/services/convert/activitypub_user_action.go b/services/convert/activitypub_user_action.go new file mode 100644 index 0000000000..9c62e6f25c --- /dev/null +++ b/services/convert/activitypub_user_action.go @@ -0,0 +1,177 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + "context" + "fmt" + "html" + "net/url" + "time" + + activities_model "forgejo.org/models/activities" + issues_model "forgejo.org/models/issues" + fm "forgejo.org/modules/forgefed" + "forgejo.org/modules/json" + "forgejo.org/modules/markup" + "forgejo.org/modules/markup/markdown" +) + +func ActionToForgeUserActivity(ctx context.Context, action *activities_model.Action) (fm.ForgeUserActivity, error) { + render := func(format string, args ...any) string { + return fmt.Sprintf(`%s %s`, action.ActUser.HTMLURL(), action.GetActDisplayName(ctx), fmt.Sprintf(format, args...)) + } + renderIssue := func(issue *issues_model.Issue) string { + return fmt.Sprintf(`%s#%d`, + issue.HTMLURL(), + action.GetRepoPath(ctx), + issue.Index, + ) + } + renderRepo := func() string { + return fmt.Sprintf(`%s`, action.Repo.HTMLURL(), action.GetRepoPath(ctx)) + } + renderBranch := func() string { + return fmt.Sprintf(`%s`, action.GetRefLink(ctx), action.GetBranch()) + } + renderTag := func() string { + return fmt.Sprintf(`%s`, action.GetRefLink(ctx), action.GetTag()) + } + + makeUserActivity := func(format string, args ...any) (fm.ForgeUserActivity, error) { + return fm.NewForgeUserActivity(action.ActUser, action.ID, render(format, args...)) + } + + switch action.OpType { + case activities_model.ActionCreateRepo: + return makeUserActivity("created a new repository: %s", renderRepo()) + case activities_model.ActionRenameRepo: + return makeUserActivity("renamed a repository: %s", renderRepo()) + case activities_model.ActionStarRepo: + return makeUserActivity("starred a repository: %s", renderRepo()) + case activities_model.ActionWatchRepo: + return makeUserActivity("started watching a repository: %s", renderRepo()) + case activities_model.ActionCommitRepo: + type PushCommit struct { + Sha1 string + Message string + AuthorEmail string + AuthorName string + CommitterEmail string + CommitterName string + Timestamp time.Time + } + type PushCommits struct { + Commits []*PushCommit + HeadCommit *PushCommit + CompareURL string + Len int + } + + commits := &PushCommits{} + if err := json.Unmarshal([]byte(action.GetContent()), commits); err != nil { + return fm.ForgeUserActivity{}, err + } + commitsHTML := "" + renderCommit := func(commit *PushCommit) string { + return fmt.Sprintf(`
  • %s
    %s
  • `, + fmt.Sprintf("%s/commit/%s", action.GetRepoAbsoluteLink(ctx), url.PathEscape(commit.Sha1)), + commit.Sha1, + html.EscapeString(commit.Message), + ) + } + for _, commit := range commits.Commits { + commitsHTML += renderCommit(commit) + } + return makeUserActivity("pushed to %s at %s:
      %s
    ", renderBranch(), renderRepo(), commitsHTML) + case activities_model.ActionCreateIssue: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("opened issue %s", renderIssue(action.Issue)) + case activities_model.ActionCreatePullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("opened pull request %s", renderIssue(action.Issue)) + case activities_model.ActionTransferRepo: + return makeUserActivity("transferred %s", renderRepo()) + case activities_model.ActionPushTag: + return makeUserActivity("pushed %s at %s", renderTag(), renderRepo()) + case activities_model.ActionCommentIssue: + renderedComment, err := markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, + }, action.Comment.Content) + if err != nil { + return fm.ForgeUserActivity{}, err + } + + return makeUserActivity(`commented on %s:
    %s
    `, + action.GetCommentHTMLURL(ctx), + renderIssue(action.Comment.Issue), + renderedComment, + ) + case activities_model.ActionMergePullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("merged pull request %s", renderIssue(action.Issue)) + case activities_model.ActionCloseIssue: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("closed issue %s", renderIssue(action.Issue)) + case activities_model.ActionReopenIssue: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("reopened issue %s", renderIssue(action.Issue)) + case activities_model.ActionClosePullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("closed pull request %s", renderIssue(action.Issue)) + case activities_model.ActionReopenPullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("reopened pull request %s", renderIssue(action.Issue)) + case activities_model.ActionDeleteTag: + return makeUserActivity("deleted tag %s at %s", action.GetTag(), renderRepo()) + case activities_model.ActionDeleteBranch: + return makeUserActivity("deleted branch %s at %s", action.GetBranch(), renderRepo()) + case activities_model.ActionApprovePullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("approved pull request %s", renderIssue(action.Issue)) + case activities_model.ActionRejectPullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("rejected pull request %s", renderIssue(action.Issue)) + case activities_model.ActionCommentPull: + renderedComment, err := markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, + }, action.Comment.Content) + if err != nil { + return fm.ForgeUserActivity{}, err + } + + return makeUserActivity(`commented on %s:
    %s
    `, + action.GetCommentHTMLURL(ctx), + renderIssue(action.Comment.Issue), + renderedComment, + ) + case activities_model.ActionMirrorSyncPush: + case activities_model.ActionMirrorSyncCreate: + case activities_model.ActionMirrorSyncDelete: + case activities_model.ActionPublishRelease: + case activities_model.ActionPullReviewDismissed: + case activities_model.ActionPullRequestReadyForReview: + case activities_model.ActionAutoMergePullRequest: + } + + return makeUserActivity("performed an unrecognised action: %s", action.OpType.String()) +} diff --git a/services/convert/git_commit.go b/services/convert/git_commit.go index 4603cfac4d..6a691966b8 100644 --- a/services/convert/git_commit.go +++ b/services/convert/git_commit.go @@ -15,7 +15,6 @@ import ( api "forgejo.org/modules/structs" "forgejo.org/modules/util" ctx "forgejo.org/services/context" - "forgejo.org/services/gitdiff" ) // ToCommitUser convert a git.Signature to an api.CommitUser @@ -210,17 +209,15 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep // Get diff stats for commit if opts.Stat { - diff, _, err := gitdiff.GetDiffSimple(ctx, gitRepo, &gitdiff.DiffOptions{ - AfterCommitID: commit.ID.String(), - }) + _, totalAdditions, totalDeletions, err := gitRepo.GetCommitShortStat(commit.ID.String()) if err != nil { return nil, err } res.Stats = &api.CommitStats{ - Total: diff.TotalAddition + diff.TotalDeletion, - Additions: diff.TotalAddition, - Deletions: diff.TotalDeletion, + Total: totalAdditions + totalDeletions, + Additions: totalAdditions, + Deletions: totalDeletions, } } diff --git a/services/convert/issue_test.go b/services/convert/issue_test.go index 97bacfb229..ea8ad9b7ef 100644 --- a/services/convert/issue_test.go +++ b/services/convert/issue_test.go @@ -24,10 +24,11 @@ func TestLabel_ToLabel(t *testing.T) { label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: label.RepoID}) assert.Equal(t, &api.Label{ - ID: label.ID, - Name: label.Name, - Color: "abcdef", - URL: fmt.Sprintf("%sapi/v1/repos/user2/repo1/labels/%d", setting.AppURL, label.ID), + ID: label.ID, + Name: label.Name, + Color: "abcdef", + Description: label.Description, + URL: fmt.Sprintf("%sapi/v1/repos/user2/repo1/labels/%d", setting.AppURL, label.ID), }, ToLabel(label, repo, nil)) } diff --git a/services/convert/mirror.go b/services/convert/mirror.go index 9e7d2659ab..5a815f3a5c 100644 --- a/services/convert/mirror.go +++ b/services/convert/mirror.go @@ -23,5 +23,6 @@ func ToPushMirror(ctx context.Context, pm *repo_model.PushMirror) (*api.PushMirr Interval: pm.Interval.String(), SyncOnCommit: pm.SyncOnCommit, PublicKey: pm.GetPublicKey(), + BranchFilter: pm.BranchFilter, }, nil } diff --git a/services/convert/utils.go b/services/convert/utils.go index 3bbd4e39bd..70c5f5cc18 100644 --- a/services/convert/utils.go +++ b/services/convert/utils.go @@ -38,6 +38,8 @@ func ToGitServiceType(value string) structs.GitServiceType { return structs.GitBucketService case "forgejo": return structs.ForgejoService + case "pagure": + return structs.PagureService default: return structs.PlainGitService } diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 322fe27ca0..3006601366 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -15,6 +15,7 @@ import ( issue_indexer "forgejo.org/modules/indexer/issues" "forgejo.org/modules/setting" "forgejo.org/modules/updatechecker" + moderation_service "forgejo.org/services/moderation" repo_service "forgejo.org/services/repository" archiver_service "forgejo.org/services/repository/archiver" user_service "forgejo.org/services/user" @@ -225,6 +226,24 @@ func registerRebuildIssueIndexer() { }) } +func registerRemoveResolvedReports() { + type ReportConfig struct { + BaseConfig + ConfigKeepResolvedReportsFor time.Duration + } + RegisterTaskFatal("remove_resolved_reports", &ReportConfig{ + BaseConfig: BaseConfig{ + Enabled: false, + RunAtStart: false, + Schedule: "@every 24h", + }, + ConfigKeepResolvedReportsFor: setting.Moderation.KeepResolvedReportsFor, + }, func(ctx context.Context, _ *user_model.User, config Config) error { + reportConfig := config.(*ReportConfig) + return moderation_service.RemoveResolvedReports(ctx, reportConfig.ConfigKeepResolvedReportsFor) + }) +} + func initExtendedTasks() { registerDeleteInactiveUsers() registerDeleteRepositoryArchives() @@ -240,4 +259,7 @@ func initExtendedTasks() { registerDeleteOldSystemNotices() registerGCLFS() registerRebuildIssueIndexer() + if setting.Moderation.Enabled { + registerRemoveResolvedReports() + } } diff --git a/services/federation/delivery_queue.go b/services/federation/delivery_queue.go new file mode 100644 index 0000000000..f71467e9f0 --- /dev/null +++ b/services/federation/delivery_queue.go @@ -0,0 +1,76 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "fmt" + "io" + + "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/process" + "forgejo.org/modules/queue" +) + +type deliveryQueueItem struct { + Doer *user.User + InboxURL string + Payload []byte + DeliveryCount int +} + +var deliveryQueue *queue.WorkerPoolQueue[deliveryQueueItem] + +func initDeliveryQueue() error { + deliveryQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "activitypub_inbox_delivery", deliveryQueueHandler) + if deliveryQueue == nil { + return fmt.Errorf("unable to create activitypub_inbox_delivery queue") + } + go graceful.GetManager().RunWithCancel(deliveryQueue) + + return nil +} + +func deliveryQueueHandler(items ...deliveryQueueItem) (unhandled []deliveryQueueItem) { + for _, item := range items { + item.DeliveryCount++ + err := deliverToInbox(item) + if err != nil && item.DeliveryCount < 10 { + unhandled = append(unhandled, item) + } + } + return unhandled +} + +func deliverToInbox(item deliveryQueueItem) error { + ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), + fmt.Sprintf("Delivering an Activity via user[%d] (%s), to %s", item.Doer.ID, item.Doer.Name, item.InboxURL)) + defer finished() + + clientFactory, err := activitypub.GetClientFactory(ctx) + if err != nil { + return err + } + apclient, err := clientFactory.WithKeys(ctx, item.Doer, item.Doer.APActorID()+"#main-key") + if err != nil { + return err + } + + log.Debug("Delivering %s to %s", item.Payload, item.InboxURL) + res, err := apclient.Post(item.Payload, item.InboxURL) + if err != nil { + return err + } + if res.StatusCode >= 400 { + defer res.Body.Close() + body, _ := io.ReadAll(io.LimitReader(res.Body, 16*1024)) + + log.Warn("Delivering to %s failed: %d %s, %v times", item.InboxURL, res.StatusCode, string(body), item.DeliveryCount) + return fmt.Errorf("delivery failed") + } + + return nil +} diff --git a/services/federation/error.go b/services/federation/error.go new file mode 100644 index 0000000000..425035d0d5 --- /dev/null +++ b/services/federation/error.go @@ -0,0 +1,44 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "fmt" + "net/http" +) + +type ErrNotAcceptable struct { + Message string +} + +func NewErrNotAcceptablef(format string, a ...any) ErrNotAcceptable { + message := fmt.Sprintf(format, a...) + return ErrNotAcceptable{Message: message} +} + +func (err ErrNotAcceptable) Error() string { + return fmt.Sprintf("NotAcceptable: %v", err.Message) +} + +type ErrInternal struct { + Message string +} + +func NewErrInternalf(format string, a ...any) ErrInternal { + message := fmt.Sprintf(format, a...) + return ErrInternal{Message: message} +} + +func (err ErrInternal) Error() string { + return fmt.Sprintf("InternalServerError: %v", err.Message) +} + +func HTTPStatus(err error) int { + switch err.(type) { + case ErrNotAcceptable: + return http.StatusNotAcceptable + default: + return http.StatusInternalServerError + } +} diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go index a3b719d1a7..ccdb9bbab0 100644 --- a/services/federation/federation_service.go +++ b/services/federation/federation_service.go @@ -5,15 +5,12 @@ package federation import ( "context" - "errors" + "database/sql" "fmt" - "net/http" "net/url" "strings" - "time" "forgejo.org/models/forgefed" - "forgejo.org/models/repo" "forgejo.org/models/user" "forgejo.org/modules/activitypub" "forgejo.org/modules/auth/password" @@ -25,87 +22,84 @@ import ( "github.com/google/uuid" ) -// ProcessLikeActivity receives a ForgeLike activity and does the following: -// Validation of the activity -// Creation of a (remote) federationHost if not existing -// Creation of a forgefed Person if not existing -// Validation of incoming RepositoryID against Local RepositoryID -// Star the repo if it wasn't already stared -// Do some mitigation against out of order attacks -func ProcessLikeActivity(ctx context.Context, form any, repositoryID int64) (int, string, error) { - activity := form.(*fm.ForgeLike) - if res, err := validation.IsValid(activity); !res { - return http.StatusNotAcceptable, "Invalid activity", err +func Init() error { + if !setting.Federation.Enabled { + return nil } - log.Info("Activity validated:%v", activity) + return initDeliveryQueue() +} - // parse actorID (person) - actorURI := activity.Actor.GetID().String() - log.Info("actorURI was: %v", actorURI) - federationHost, err := GetFederationHostForURI(ctx, actorURI) +func FindOrCreateFederationHost(ctx context.Context, actorURI string) (*forgefed.FederationHost, error) { + rawActorID, err := fm.NewActorID(actorURI) if err != nil { - return http.StatusInternalServerError, "Wrong FederationHost", err + return nil, err } - if !activity.IsNewer(federationHost.LatestActivity) { - return http.StatusNotAcceptable, "Activity out of order.", errors.New("Activity already processed") + federationHost, err := forgefed.FindFederationHostByFqdnAndPort(ctx, rawActorID.Host, rawActorID.HostPort) + if err != nil { + return nil, err + } + if federationHost == nil { + result, err := createFederationHostFromAP(ctx, rawActorID) + if err != nil { + return nil, err + } + federationHost = result + } + return federationHost, nil +} + +func FindOrCreateFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { + user, federatedUser, federationHost, err := findFederatedUser(ctx, actorURI) + if err != nil { + return nil, nil, nil, err + } + personID, err := fm.NewPersonID(actorURI, string(federationHost.NodeInfo.SoftwareName)) + if err != nil { + return nil, nil, nil, err + } + + if user != nil { + log.Trace("Local ActivityPub user found (actorURI: %#v, user: %#v)", actorURI, user) + } else { + log.Trace("Attempting to create new user and federatedUser for actorURI: %#v", actorURI) + user, federatedUser, err = createUserFromAP(ctx, personID, federationHost.ID) + if err != nil { + return nil, nil, nil, err + } + log.Trace("Created user %#v with federatedUser %#v from distant server", user, federatedUser) + } + log.Trace("Got user: %v", user.Name) + + return user, federatedUser, federationHost, nil +} + +func findFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { + federationHost, err := FindOrCreateFederationHost(ctx, actorURI) + if err != nil { + return nil, nil, nil, err } actorID, err := fm.NewPersonID(actorURI, string(federationHost.NodeInfo.SoftwareName)) if err != nil { - return http.StatusNotAcceptable, "Invalid PersonID", err + return nil, nil, nil, err } - log.Info("Actor accepted:%v", actorID) - // parse objectID (repository) - objectID, err := fm.NewRepositoryID(activity.Object.GetID().String(), string(forgefed.ForgejoSourceType)) + user, federatedUser, err := user.FindFederatedUser(ctx, actorID.ID, federationHost.ID) if err != nil { - return http.StatusNotAcceptable, "Invalid objectId", err - } - if objectID.ID != fmt.Sprint(repositoryID) { - return http.StatusNotAcceptable, "Invalid objectId", err - } - log.Info("Object accepted:%v", objectID) - - // Check if user already exists - user, _, err := user.FindFederatedUser(ctx, actorID.ID, federationHost.ID) - if err != nil { - return http.StatusInternalServerError, "Searching for user failed", err - } - if user != nil { - log.Info("Found local federatedUser: %v", user) - } else { - user, _, err = CreateUserFromAP(ctx, actorID, federationHost.ID) - if err != nil { - return http.StatusInternalServerError, "Error creating federatedUser", err - } - log.Info("Created federatedUser from ap: %v", user) - } - log.Info("Got user:%v", user.Name) - - // execute the activity if the repo was not stared already - alreadyStared := repo.IsStaring(ctx, user.ID, repositoryID) - if !alreadyStared { - err = repo.StarRepo(ctx, user.ID, repositoryID, true) - if err != nil { - return http.StatusNotAcceptable, "Error staring", err - } - } - federationHost.LatestActivity = activity.StartTime - err = forgefed.UpdateFederationHost(ctx, federationHost) - if err != nil { - return http.StatusNotAcceptable, "Error updating federatedHost", err + return nil, nil, nil, err } - return 0, "", nil + return user, federatedUser, federationHost, nil } -func CreateFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) { +func createFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) { actionsUser := user.NewAPServerActor() + clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, err } - client, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID()) + client, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.KeyID()) if err != nil { return nil, err } @@ -130,6 +124,7 @@ func CreateFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forge return nil, err } + // TODO: we should get key material here also to have it immediately result, err := forgefed.NewFederationHost(actorID.Host, nodeInfo, actorID.HostPort, actorID.HostSchema) if err != nil { return nil, err @@ -143,34 +138,14 @@ func CreateFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forge return &result, nil } -func GetFederationHostForURI(ctx context.Context, actorURI string) (*forgefed.FederationHost, error) { - log.Info("Input was: %v", actorURI) - rawActorID, err := fm.NewActorID(actorURI) - if err != nil { - return nil, err - } - federationHost, err := forgefed.FindFederationHostByFqdnAndPort(ctx, rawActorID.Host, rawActorID.HostPort) - if err != nil { - return nil, err - } - if federationHost == nil { - result, err := CreateFederationHostFromAP(ctx, rawActorID) - if err != nil { - return nil, err - } - federationHost = result - } - return federationHost, nil -} - -func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID int64) (*user.User, *user.FederatedUser, error) { +func fetchUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID int64) (*user.User, *user.FederatedUser, error) { actionsUser := user.NewAPServerActor() clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, nil, err } - apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID()) + apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.KeyID()) if err != nil { return nil, nil, err } @@ -190,7 +165,7 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI return nil, nil, err } - log.Info("Fetched valid person:%q", person) + log.Info("Fetched valid person from distant server: %q", person) localFqdn, err := url.ParseRequestURI(setting.AppURL) if err != nil { @@ -216,6 +191,11 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI return nil, nil, err } + pubKeyBytes, err := decodePublicKeyPem(person.PublicKey.PublicKeyPem) + if err != nil { + return nil, nil, err + } + newUser := user.User{ LowerName: strings.ToLower(name), Name: name, @@ -234,86 +214,30 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI FederationHostID: federationHostID, InboxPath: inbox.Path, NormalizedOriginalURL: personID.AsURI(), + KeyID: sql.NullString{ + String: person.PublicKey.ID.String(), + Valid: true, + }, + PublicKey: sql.Null[sql.RawBytes]{ + V: pubKeyBytes, + Valid: true, + }, } - err = user.CreateFederatedUser(ctx, &newUser, &federatedUser) + log.Info("Fetched person's %q federatedUser from distant server: %q", person, federatedUser) + return &newUser, &federatedUser, nil +} + +func createUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID int64) (*user.User, *user.FederatedUser, error) { + newUser, federatedUser, err := fetchUserFromAP(ctx, personID, federationHostID) + if err != nil { + return nil, nil, err + } + err = user.CreateFederatedUser(ctx, newUser, federatedUser) if err != nil { return nil, nil, err } - log.Info("Created federatedUser:%q", federatedUser) - return &newUser, &federatedUser, nil -} - -// Create or update a list of FollowingRepo structs -func StoreFollowingRepoList(ctx context.Context, localRepoID int64, followingRepoList []string) (int, string, error) { - followingRepos := make([]*repo.FollowingRepo, 0, len(followingRepoList)) - for _, uri := range followingRepoList { - federationHost, err := GetFederationHostForURI(ctx, uri) - if err != nil { - return http.StatusInternalServerError, "Wrong FederationHost", err - } - followingRepoID, err := fm.NewRepositoryID(uri, string(federationHost.NodeInfo.SoftwareName)) - if err != nil { - return http.StatusNotAcceptable, "Invalid federated repo", err - } - followingRepo, err := repo.NewFollowingRepo(localRepoID, followingRepoID.ID, federationHost.ID, uri) - if err != nil { - return http.StatusNotAcceptable, "Invalid federated repo", err - } - followingRepos = append(followingRepos, &followingRepo) - } - - if err := repo.StoreFollowingRepos(ctx, localRepoID, followingRepos); err != nil { - return 0, "", err - } - - return 0, "", nil -} - -func DeleteFollowingRepos(ctx context.Context, localRepoID int64) error { - return repo.StoreFollowingRepos(ctx, localRepoID, []*repo.FollowingRepo{}) -} - -func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error { - followingRepos, err := repo.FindFollowingReposByRepoID(ctx, repoID) - log.Info("Federated Repos is: %v", followingRepos) - if err != nil { - return err - } - - likeActivityList := make([]fm.ForgeLike, 0) - for _, followingRepo := range followingRepos { - log.Info("Found following repo: %v", followingRepo) - target := followingRepo.URI - likeActivity, err := fm.NewForgeLike(doer.APActorID(), target, time.Now()) - if err != nil { - return err - } - likeActivityList = append(likeActivityList, likeActivity) - } - - apclientFactory, err := activitypub.GetClientFactory(ctx) - if err != nil { - return err - } - - apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorKeyID()) - if err != nil { - return err - } - for i, activity := range likeActivityList { - activity.StartTime = activity.StartTime.Add(time.Duration(i) * time.Second) - json, err := activity.MarshalJSON() - if err != nil { - return err - } - - _, err = apclient.Post(json, fmt.Sprintf("%s/inbox", activity.Object)) - if err != nil { - log.Error("error %v while sending activity: %q", err, activity) - } - } - - return nil + log.Info("Created federatedUser: %q", federatedUser) + return newUser, federatedUser, nil } diff --git a/services/federation/person_inbox_accept.go b/services/federation/person_inbox_accept.go new file mode 100644 index 0000000000..d0a840bd2d --- /dev/null +++ b/services/federation/person_inbox_accept.go @@ -0,0 +1,22 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "net/http" + + "forgejo.org/modules/log" + + ap "github.com/go-ap/activitypub" +) + +func processPersonInboxAccept(activity *ap.Activity) (ServiceResult, error) { + if activity.Object.GetType() != ap.FollowType { + log.Error("Invalid object type for Accept activity: %v", activity.Object.GetType()) + return ServiceResult{}, NewErrNotAcceptablef("invalid object type for Accept activity: %v", activity.Object.GetType()) + } + + // We currently do not do anything here, we just drop it. + return NewServiceResultStatusOnly(http.StatusNoContent), nil +} diff --git a/services/federation/person_inbox_create.go b/services/federation/person_inbox_create.go new file mode 100644 index 0000000000..2132c7ede1 --- /dev/null +++ b/services/federation/person_inbox_create.go @@ -0,0 +1,55 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + "net/http" + + "forgejo.org/models/activities" + "forgejo.org/models/user" + fm "forgejo.org/modules/forgefed" + "forgejo.org/modules/log" + + ap "github.com/go-ap/activitypub" +) + +func processPersonInboxCreate(ctx context.Context, user *user.User, activity *ap.Activity) (ServiceResult, error) { + createAct, err := fm.NewForgeUserActivityFromAp(*activity) + if err != nil { + log.Error("Invalid user activity: %v, %v", activity, err) + return ServiceResult{}, NewErrNotAcceptablef("Invalid user activity: %v", err) + } + + actorURI := createAct.Actor.GetLink().String() + federatedBaseUser, _, _, err := findFederatedUser(ctx, actorURI) + if err != nil { + log.Error("Federated user not found (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("federated user not found (%s): %v", actorURI, err) + } + if federatedBaseUser == nil { + log.Error("Federated user not found (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("federated user not found (%s): %v", actorURI, err) + } + + federatedUserActivity, err := activities.NewFederatedUserActivity( + user.ID, + federatedBaseUser.ID, + createAct.Actor.GetLink().String(), + createAct.Note.Content.String(), + createAct.Note.URL.GetID().String(), + *activity, + ) + if err != nil { + log.Error("Error creating federatedUserActivity (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("Error creating federatedUserActivity: %v", err) + } + + if err := activities.CreateUserActivity(ctx, &federatedUserActivity); err != nil { + log.Error("Unable to record activity: %v", err) + return ServiceResult{}, NewErrNotAcceptablef("Unable to record activity: %v", err) + } + + return NewServiceResultStatusOnly(http.StatusNoContent), nil +} diff --git a/services/federation/person_inbox_follow.go b/services/federation/person_inbox_follow.go new file mode 100644 index 0000000000..baa7934ad5 --- /dev/null +++ b/services/federation/person_inbox_follow.go @@ -0,0 +1,73 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + "fmt" + "net/http" + + "forgejo.org/models/user" + "forgejo.org/modules/forgefed" + "forgejo.org/modules/log" + + ap "github.com/go-ap/activitypub" + "github.com/go-ap/jsonld" +) + +func processPersonFollow(ctx context.Context, ctxUser *user.User, activity *ap.Activity) (ServiceResult, error) { + follow, err := forgefed.NewForgeFollowFromAp(*activity) + if err != nil { + log.Error("Invalid follow activity: %s", err) + return ServiceResult{}, NewErrNotAcceptablef("Invalid follow activity: %v", err) + } + + actorURI := follow.Actor.GetLink().String() + _, federatedUser, federationHost, err := FindOrCreateFederatedUser(ctx, actorURI) + if err != nil { + log.Error("Error finding or creating federated user (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("Federated user not found: %v", err) + } + + following, err := user.IsFollowingAp(ctx, ctxUser, federatedUser) + if err != nil { + log.Error("forgefed.IsFollowing: %v", err) + return ServiceResult{}, NewErrNotAcceptablef("forgefed.IsFollowing: %v", err) + } + if following { + // If the user is already following, we're good, nothing to do. + log.Trace("Local user[%d] is already following federated user[%d]", ctxUser.ID, federatedUser.ID) + return NewServiceResultStatusOnly(http.StatusNoContent), nil + } + + follower, err := user.AddFollower(ctx, ctxUser, federatedUser) + if err != nil { + log.Error("Unable to add follower: %v", err) + return ServiceResult{}, NewErrNotAcceptablef("Unable to add follower: %v", err) + } + + accept := ap.AcceptNew(ap.IRI(fmt.Sprintf( + "%s#accepts/follow/%d", ctxUser.APActorID(), follower.ID, + )), follow) + accept.Actor = ap.IRI(ctxUser.APActorID()) + payload, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI)).Marshal(accept) + if err != nil { + log.Error("Unable to Marshal JSON: %v", err) + return ServiceResult{}, NewErrInternalf("MarshalJSON: %v", err) + } + + hostURL := federationHost.AsURL() + if err := deliveryQueue.Push(deliveryQueueItem{ + InboxURL: hostURL.JoinPath(federatedUser.InboxPath).String(), + Doer: ctxUser, + Payload: payload, + }); err != nil { + log.Error("Unable to push to pending queue: %v", err) + return ServiceResult{}, NewErrInternalf("Unable to push to pending queue: %v", err) + } + + // Respond back with an accept + result := NewServiceResultWithBytes(http.StatusAccepted, []byte(`{"status":"Accepted"}`)) + return result, nil +} diff --git a/services/federation/person_inbox_undo.go b/services/federation/person_inbox_undo.go new file mode 100644 index 0000000000..4379cf242a --- /dev/null +++ b/services/federation/person_inbox_undo.go @@ -0,0 +1,47 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + "net/http" + + "forgejo.org/models/user" + "forgejo.org/modules/log" + + ap "github.com/go-ap/activitypub" +) + +func processPersonInboxUndo(ctx context.Context, ctxUser *user.User, activity *ap.Activity) (ServiceResult, error) { + if activity.Object.GetType() != ap.FollowType { + log.Error("Invalid object type for Undo activity: %v", activity.Object.GetType()) + return ServiceResult{}, NewErrNotAcceptablef("Invalid object type for Undo activity: %v", activity.Object.GetType()) + } + + actorURI := activity.Actor.GetLink().String() + _, federatedUser, _, err := findFederatedUser(ctx, actorURI) + if err != nil { + log.Error("User not found: %v", err) + return ServiceResult{}, NewErrInternalf("User not found: %v", err) + } + + if federatedUser != nil { + following, err := user.IsFollowingAp(ctx, ctxUser, federatedUser) + if err != nil { + log.Error("forgefed.IsFollowing: %v", err) + return ServiceResult{}, NewErrInternalf("forgefed.IsFollowing: %v", err) + } + if !following { + // The local user is not following the federated one, nothing to do. + log.Trace("Local user[%d] is not following federated user[%d]", ctxUser.ID, federatedUser.ID) + return NewServiceResultStatusOnly(http.StatusNoContent), nil + } + if err := user.RemoveFollower(ctx, ctxUser, federatedUser); err != nil { + log.Error("Unable to remove follower", err) + return ServiceResult{}, NewErrInternalf("Unable to remove follower: %v", err) + } + } + + return NewServiceResultStatusOnly(http.StatusNoContent), nil +} diff --git a/services/federation/person_service.go b/services/federation/person_service.go new file mode 100644 index 0000000000..d6482d013c --- /dev/null +++ b/services/federation/person_service.go @@ -0,0 +1,60 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + "net/http" + + "forgejo.org/models/user" + "forgejo.org/modules/forgefed" + "forgejo.org/modules/log" + context_service "forgejo.org/services/context" + + ap "github.com/go-ap/activitypub" + "github.com/go-ap/jsonld" +) + +func ProcessPersonInbox(ctx context.Context, user *user.User, activity *ap.Activity) (ServiceResult, error) { + switch activity.Type { + case ap.CreateType: + return processPersonInboxCreate(ctx, user, activity) + case ap.FollowType: + return processPersonFollow(ctx, user, activity) + case ap.UndoType: + return processPersonInboxUndo(ctx, user, activity) + case ap.AcceptType: + return processPersonInboxAccept(activity) + } + + log.Error("Unsupported PersonInbox activity: %v", activity.Type) + return ServiceResult{}, NewErrNotAcceptablef("unsupported activity: %v", activity.Type) +} + +func FollowRemoteActor(ctx *context_service.APIContext, localUser *user.User, actorURI string) error { + _, federatedUser, federationHost, err := FindOrCreateFederatedUser(ctx.Base, actorURI) + if err != nil { + log.Error("Federated user not found (%s): %v", actorURI, err) + ctx.Error(http.StatusNotAcceptable, "Federated user not found", err) + return err + } + + followReq, err := forgefed.NewForgeFollow(localUser.APActorID(), actorURI) + if err != nil { + return err + } + + payload, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI)). + Marshal(followReq) + if err != nil { + return err + } + + hostURL := federationHost.AsURL() + return deliveryQueue.Push(deliveryQueueItem{ + InboxURL: hostURL.JoinPath(federatedUser.InboxPath).String(), + Doer: localUser, + Payload: payload, + }) +} diff --git a/services/federation/repository_inbox_like.go b/services/federation/repository_inbox_like.go new file mode 100644 index 0000000000..478a12d92c --- /dev/null +++ b/services/federation/repository_inbox_like.go @@ -0,0 +1,147 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + "fmt" + "net/http" + "time" + + "forgejo.org/models/forgefed" + "forgejo.org/models/repo" + "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + fm "forgejo.org/modules/forgefed" + "forgejo.org/modules/log" + "forgejo.org/modules/validation" + context_service "forgejo.org/services/context" + + ap "github.com/go-ap/activitypub" +) + +// ProcessLikeActivity receives a ForgeLike activity and does the following: +// Validation of the activity +// Creation of a (remote) federationHost if not existing +// Creation of a forgefed Person if not existing +// Validation of incoming RepositoryID against Local RepositoryID +// Star the repo if it wasn't already stared +// Do some mitigation against out of order attacks +func ProcessLikeActivity(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) { + constructorLikeActivity, _ := fm.NewForgeLike(activity.Actor.GetLink().String(), activity.Object.GetLink().String(), activity.StartTime) + if res, err := validation.IsValid(constructorLikeActivity); !res { + return ServiceResult{}, NewErrNotAcceptablef("Invalid activity: %v", err) + } + log.Trace("Activity validated: %#v", activity) + + // parse actorID (person) + actorURI := activity.Actor.GetID().String() + user, _, federationHost, err := FindOrCreateFederatedUser(ctx, actorURI) + if err != nil { + log.Error("Federated user not found (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("FindOrCreateFederatedUser failed: %v", err) + } + + if !constructorLikeActivity.IsNewer(federationHost.LatestActivity) { + return ServiceResult{}, NewErrNotAcceptablef("LatestActivity: activity already processed: %v", err) + } + + // parse objectID (repository) + objectID, err := fm.NewRepositoryID(constructorLikeActivity.Object.GetID().String(), string(forgefed.ForgejoSourceType)) + if err != nil { + return ServiceResult{}, NewErrNotAcceptablef("Parsing repo objectID failed: %v", err) + } + if objectID.ID != fmt.Sprint(repositoryID) { + return ServiceResult{}, NewErrNotAcceptablef("Invalid repoId: %v", err) + } + log.Trace("Object accepted: %#v", objectID) + + // execute the activity if the repo was not stared already + alreadyStared := repo.IsStaring(ctx, user.ID, repositoryID) + if !alreadyStared { + err = repo.StarRepo(ctx, user.ID, repositoryID, true) + if err != nil { + return ServiceResult{}, NewErrNotAcceptablef("Staring failed: %v", err) + } + } + federationHost.LatestActivity = activity.StartTime + err = forgefed.UpdateFederationHost(ctx, federationHost) + if err != nil { + return ServiceResult{}, NewErrNotAcceptablef("Updating federatedHost failed: %v", err) + } + + return NewServiceResultStatusOnly(http.StatusNoContent), nil +} + +// Create or update a list of FollowingRepo structs +func StoreFollowingRepoList(ctx *context_service.Context, localRepoID int64, followingRepoList []string) (int, string, error) { + followingRepos := make([]*repo.FollowingRepo, 0, len(followingRepoList)) + for _, uri := range followingRepoList { + federationHost, err := FindOrCreateFederationHost(ctx.Base, uri) + if err != nil { + return http.StatusInternalServerError, "Wrong FederationHost", err + } + followingRepoID, err := fm.NewRepositoryID(uri, string(federationHost.NodeInfo.SoftwareName)) + if err != nil { + return http.StatusNotAcceptable, "Invalid federated repo", err + } + followingRepo, err := repo.NewFollowingRepo(localRepoID, followingRepoID.ID, federationHost.ID, uri) + if err != nil { + return http.StatusNotAcceptable, "Invalid federated repo", err + } + followingRepos = append(followingRepos, &followingRepo) + } + + if err := repo.StoreFollowingRepos(ctx, localRepoID, followingRepos); err != nil { + return 0, "", err + } + + return 0, "", nil +} + +func DeleteFollowingRepos(ctx context.Context, localRepoID int64) error { + return repo.StoreFollowingRepos(ctx, localRepoID, []*repo.FollowingRepo{}) +} + +func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error { + followingRepos, err := repo.FindFollowingReposByRepoID(ctx, repoID) + log.Trace("Federated Repos is: %#v", followingRepos) + if err != nil { + return err + } + + likeActivityList := make([]fm.ForgeLike, 0) + for _, followingRepo := range followingRepos { + log.Trace("Found following repo: %#v", followingRepo) + target := followingRepo.URI + likeActivity, err := fm.NewForgeLike(doer.APActorID(), target, time.Now()) + if err != nil { + return err + } + likeActivityList = append(likeActivityList, likeActivity) + } + + apclientFactory, err := activitypub.GetClientFactory(ctx) + if err != nil { + return err + } + apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorID()+"#main-key") + if err != nil { + return err + } + for i, activity := range likeActivityList { + activity.StartTime = activity.StartTime.Add(time.Duration(i) * time.Second) + json, err := activity.MarshalJSON() + if err != nil { + return err + } + + _, err = apclient.Post(json, fmt.Sprintf("%v/inbox", activity.Object)) + if err != nil { + log.Error("error %v while sending activity: %#v", err, activity) + } + } + + return nil +} diff --git a/services/federation/repository_service.go b/services/federation/repository_service.go new file mode 100644 index 0000000000..7891d786e2 --- /dev/null +++ b/services/federation/repository_service.go @@ -0,0 +1,19 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + + ap "github.com/go-ap/activitypub" +) + +func ProcessRepositoryInbox(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) { + switch activity.Type { + case ap.LikeType: + return ProcessLikeActivity(ctx, activity, repositoryID) + default: + return ServiceResult{}, NewErrNotAcceptablef("Not a like activity: %v", activity.Type) + } +} diff --git a/services/federation/result.go b/services/federation/result.go new file mode 100644 index 0000000000..47afb2bdf6 --- /dev/null +++ b/services/federation/result.go @@ -0,0 +1,35 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import "github.com/go-ap/activitypub" + +type ServiceResult struct { + HTTPStatus int + Bytes []byte + Activity activitypub.Activity + withBytes bool + withActivity bool + statusOnly bool +} + +func NewServiceResultStatusOnly(status int) ServiceResult { + return ServiceResult{HTTPStatus: status, statusOnly: true} +} + +func NewServiceResultWithBytes(status int, bytes []byte) ServiceResult { + return ServiceResult{HTTPStatus: status, Bytes: bytes, withBytes: true} +} + +func (serviceResult ServiceResult) WithBytes() bool { + return serviceResult.withBytes +} + +func (serviceResult ServiceResult) WithActivity() bool { + return serviceResult.withActivity +} + +func (serviceResult ServiceResult) StatusOnly() bool { + return serviceResult.statusOnly +} diff --git a/services/federation/signature_service.go b/services/federation/signature_service.go new file mode 100644 index 0000000000..fd8cbb39cd --- /dev/null +++ b/services/federation/signature_service.go @@ -0,0 +1,235 @@ +// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + "crypto/x509" + "database/sql" + "encoding/pem" + "errors" + "fmt" + "net/url" + + "forgejo.org/models/forgefed" + "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + fm "forgejo.org/modules/forgefed" + + ap "github.com/go-ap/activitypub" +) + +// Factory function for ActorID. Created struct is asserted to be valid +func NewActorIDFromKeyID(ctx context.Context, uri string) (fm.ActorID, error) { + parsedURI, err := url.Parse(uri) + parsedURI.Fragment = "" + if err != nil { + return fm.ActorID{}, err + } + + actionsUser := user.NewAPServerActor() + clientFactory, err := activitypub.GetClientFactory(ctx) + if err != nil { + return fm.ActorID{}, err + } + + apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.KeyID()) + if err != nil { + return fm.ActorID{}, err + } + + userResponse, err := apClient.GetBody(parsedURI.String()) + if err != nil { + return fm.ActorID{}, err + } + + var actor ap.Actor + err = actor.UnmarshalJSON(userResponse) + if err != nil { + return fm.ActorID{}, err + } + + result, err := fm.NewActorID(actor.PublicKey.Owner.String()) + return result, err +} + +func FindOrCreateFederatedUserKey(ctx context.Context, keyID string) (pubKey any, err error) { + var federatedUser *user.FederatedUser + var keyURL *url.URL + + keyURL, err = url.Parse(keyID) + if err != nil { + return nil, err + } + + // Try if the signing actor is an already known federated user + _, federatedUser, err = user.FindFederatedUserByKeyID(ctx, keyURL.String()) + if err != nil { + return nil, err + } + + if federatedUser == nil { + rawActorID, err := NewActorIDFromKeyID(ctx, keyID) + if err != nil { + return nil, err + } + + _, federatedUser, _, err = FindOrCreateFederatedUser(ctx, rawActorID.AsURI()) + if err != nil { + return nil, err + } + } else { + _, err = forgefed.GetFederationHost(ctx, federatedUser.FederationHostID) + if err != nil { + return nil, err + } + } + + if federatedUser.PublicKey.Valid { + pubKey, err := x509.ParsePKIXPublicKey(federatedUser.PublicKey.V) + if err != nil { + return nil, err + } + return pubKey, nil + } + + // Fetch missing public key + pubKey, pubKeyBytes, apPerson, err := fetchKeyFromAp(ctx, *keyURL) + if err != nil { + return nil, err + } + if apPerson.Type == ap.ActivityVocabularyType("Person") { + // Check federatedUser.id = person.id + if federatedUser.ExternalID != apPerson.ID.String() { + return nil, fmt.Errorf("federated user fetched (%v) does not match the stored one %v", apPerson, federatedUser) + } + // update federated user + federatedUser.KeyID = sql.NullString{ + String: apPerson.PublicKey.ID.String(), + Valid: true, + } + federatedUser.PublicKey = sql.Null[sql.RawBytes]{ + V: pubKeyBytes, + Valid: true, + } + err = user.UpdateFederatedUser(ctx, federatedUser) + if err != nil { + return nil, err + } + return pubKey, nil + } + return nil, nil +} + +func FindOrCreateFederationHostKey(ctx context.Context, keyID string) (pubKey any, err error) { + keyURL, err := url.Parse(keyID) + if err != nil { + return nil, err + } + rawActorID, err := NewActorIDFromKeyID(ctx, keyID) + if err != nil { + return nil, err + } + + // Is there an already known federation host? + federationHost, err := forgefed.FindFederationHostByKeyID(ctx, keyURL.String()) + if err != nil { + return nil, err + } + + if federationHost == nil { + federationHost, err = FindOrCreateFederationHost(ctx, rawActorID.AsURI()) + if err != nil { + return nil, err + } + } + + // Is there an already an key? + if federationHost.PublicKey.Valid { + pubKey, err := x509.ParsePKIXPublicKey(federationHost.PublicKey.V) + if err != nil { + return nil, err + } + return pubKey, nil + } + + // If not, fetch missing public key + pubKey, pubKeyBytes, apPerson, err := fetchKeyFromAp(ctx, *keyURL) + if err != nil { + return nil, err + } + if apPerson.Type == ap.ActivityVocabularyType("Application") { + // Check federationhost.id = person.id + if federationHost.HostPort != rawActorID.HostPort || federationHost.HostFqdn != rawActorID.Host || + federationHost.HostSchema != rawActorID.HostSchema { + return nil, fmt.Errorf("federation host fetched (%v) does not match the stored one %v", apPerson, federationHost) + } + // update federation host + federationHost.KeyID = sql.NullString{ + String: apPerson.PublicKey.ID.String(), + Valid: true, + } + federationHost.PublicKey = sql.Null[sql.RawBytes]{ + V: pubKeyBytes, + Valid: true, + } + err = forgefed.UpdateFederationHost(ctx, federationHost) + if err != nil { + return nil, err + } + return pubKey, nil + } + return nil, nil +} + +func fetchKeyFromAp(ctx context.Context, keyURL url.URL) (pubKey any, pubKeyBytes []byte, apPerson *ap.Person, err error) { + actionsUser := user.NewAPServerActor() + + clientFactory, err := activitypub.GetClientFactory(ctx) + if err != nil { + return nil, nil, nil, err + } + + apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.KeyID()) + if err != nil { + return nil, nil, nil, err + } + + b, err := apClient.GetBody(keyURL.String()) + if err != nil { + return nil, nil, nil, err + } + + person := ap.PersonNew(ap.IRI(keyURL.String())) + err = person.UnmarshalJSON(b) + if err != nil { + return nil, nil, nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err) + } + + pubKeyFromAp := person.PublicKey + if pubKeyFromAp.ID.String() != keyURL.String() { + return nil, nil, nil, fmt.Errorf("cannot find publicKey with id: %v in %v", keyURL, string(b)) + } + + pubKeyBytes, err = decodePublicKeyPem(pubKeyFromAp.PublicKeyPem) + if err != nil { + return nil, nil, nil, err + } + + pubKey, err = x509.ParsePKIXPublicKey(pubKeyBytes) + if err != nil { + return nil, nil, nil, err + } + + return pubKey, pubKeyBytes, person, err +} + +func decodePublicKeyPem(pubKeyPem string) ([]byte, error) { + block, _ := pem.Decode([]byte(pubKeyPem)) + if block == nil || block.Type != "PUBLIC KEY" { + return nil, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type") + } + + return block.Bytes, nil +} diff --git a/services/federation/user_activity.go b/services/federation/user_activity.go new file mode 100644 index 0000000000..0db2aee4ec --- /dev/null +++ b/services/federation/user_activity.go @@ -0,0 +1,83 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + + activities_model "forgejo.org/models/activities" + "forgejo.org/models/forgefed" + "forgejo.org/models/user" + "forgejo.org/modules/setting" + "forgejo.org/modules/structs" + "forgejo.org/services/convert" + + ap "github.com/go-ap/activitypub" + "github.com/go-ap/jsonld" +) + +func SendUserActivity(ctx context.Context, doer *user.User, activity *activities_model.Action) error { + followers, err := user.GetFollowersForUser(ctx, doer) + if err != nil { + return err + } + + userActivity, err := convert.ActionToForgeUserActivity(ctx, activity) + if err != nil { + return err + } + + payload, err := jsonld.WithContext( + jsonld.IRI(ap.ActivityBaseURI), + ).Marshal(userActivity) + if err != nil { + return err + } + + for _, follower := range followers { + _, federatedUserFollower, err := user.GetFederatedUserByUserID(ctx, follower.FollowingUserID) + if err != nil { + return err + } + + federationHost, err := forgefed.GetFederationHost(ctx, federatedUserFollower.FederationHostID) + if err != nil { + return err + } + + hostURL := federationHost.AsURL() + if err := deliveryQueue.Push(deliveryQueueItem{ + InboxURL: hostURL.JoinPath(federatedUserFollower.InboxPath).String(), + Doer: doer, + Payload: payload, + }); err != nil { + return err + } + } + + return nil +} + +func NotifyActivityPubFollowers(ctx context.Context, actions []activities_model.Action) error { + if !setting.Federation.Enabled { + return nil + } + for _, act := range actions { + if act.Repo != nil { + if act.Repo.IsPrivate { + continue + } + if act.Repo.Owner.KeepActivityPrivate || act.Repo.Owner.Visibility != structs.VisibleTypePublic { + continue + } + } + if act.ActUser.KeepActivityPrivate || act.ActUser.Visibility != structs.VisibleTypePublic { + continue + } + if err := SendUserActivity(ctx, act.ActUser, &act); err != nil { + return err + } + } + return nil +} diff --git a/services/feed/action.go b/services/feed/action.go index 7d179bd1c8..2d07f39284 100644 --- a/services/feed/action.go +++ b/services/feed/action.go @@ -19,6 +19,7 @@ import ( "forgejo.org/modules/repository" "forgejo.org/modules/setting" "forgejo.org/modules/util" + federation_service "forgejo.org/services/federation" notify_service "forgejo.org/services/notify" ) @@ -40,21 +41,19 @@ func NewNotifier() notify_service.Notifier { } func notifyAll(ctx context.Context, action *activities_model.Action) error { - _, err := activities_model.NotifyWatchers(ctx, action) + out, err := activities_model.NotifyWatchers(ctx, action) if err != nil { return err } - return err - // return federation_service.NotifyActivityPubFollowers(ctx, out) + return federation_service.NotifyActivityPubFollowers(ctx, out) } func notifyAllActions(ctx context.Context, acts []*activities_model.Action) error { - _, err := activities_model.NotifyWatchersActions(ctx, acts) + out, err := activities_model.NotifyWatchersActions(ctx, acts) if err != nil { return err } - return nil - // return federation_service.NotifyActivityPubFollowers(ctx, out) + return federation_service.NotifyActivityPubFollowers(ctx, out) } func (a *actionNotifier) NewIssue(ctx context.Context, issue *issues_model.Issue, mentions []*user_model.User) { @@ -72,7 +71,7 @@ func (a *actionNotifier) NewIssue(ctx context.Context, issue *issues_model.Issue ActUserID: issue.Poster.ID, ActUser: issue.Poster, OpType: activities_model.ActionCreateIssue, - Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title), + Content: encodeContent(fmt.Sprintf("%d", issue.Index), issue.Title), RepoID: repo.ID, Repo: repo, IsPrivate: repo.IsPrivate, @@ -88,7 +87,7 @@ func (a *actionNotifier) IssueChangeStatus(ctx context.Context, doer *user_model act := &activities_model.Action{ ActUserID: doer.ID, ActUser: doer, - Content: fmt.Sprintf("%d|%s", issue.Index, ""), + Content: encodeContent(fmt.Sprintf("%d", issue.Index), ""), RepoID: issue.Repo.ID, Repo: issue.Repo, Comment: actionComment, @@ -126,18 +125,9 @@ func (a *actionNotifier) CreateIssueComment(ctx context.Context, doer *user_mode Comment: comment, CommentID: comment.ID, IsPrivate: issue.Repo.IsPrivate, + Content: encodeContent(fmt.Sprintf("%d", issue.Index), abbreviatedComment(comment.Content)), } - truncatedContent, truncatedRight := util.SplitStringAtByteN(comment.Content, 200) - if truncatedRight != "" { - // in case the content is in a Latin family language, we remove the last broken word. - lastSpaceIdx := strings.LastIndex(truncatedContent, " ") - if lastSpaceIdx != -1 && (len(truncatedContent)-lastSpaceIdx < 15) { - truncatedContent = truncatedContent[:lastSpaceIdx] + "…" - } - } - act.Content = fmt.Sprintf("%d|%s", issue.Index, truncatedContent) - if issue.IsPull { act.OpType = activities_model.ActionCommentPull } else { @@ -168,7 +158,7 @@ func (a *actionNotifier) NewPullRequest(ctx context.Context, pull *issues_model. ActUserID: pull.Issue.Poster.ID, ActUser: pull.Issue.Poster, OpType: activities_model.ActionCreatePullRequest, - Content: fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title), + Content: encodeContent(fmt.Sprintf("%d", pull.Issue.Index), pull.Issue.Title), RepoID: pull.Issue.Repo.ID, Repo: pull.Issue.Repo, IsPrivate: pull.Issue.Repo.IsPrivate, @@ -248,7 +238,7 @@ func (a *actionNotifier) PullRequestReview(ctx context.Context, pr *issues_model actions = append(actions, &activities_model.Action{ ActUserID: review.Reviewer.ID, ActUser: review.Reviewer, - Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]), + Content: encodeContent(fmt.Sprintf("%d", review.Issue.Index), abbreviatedComment(comm.Content)), OpType: activities_model.ActionCommentPull, RepoID: review.Issue.RepoID, Repo: review.Issue.Repo, @@ -264,7 +254,7 @@ func (a *actionNotifier) PullRequestReview(ctx context.Context, pr *issues_model action := &activities_model.Action{ ActUserID: review.Reviewer.ID, ActUser: review.Reviewer, - Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]), + Content: encodeContent(fmt.Sprintf("%d", review.Issue.Index), abbreviatedComment(comment.Content)), RepoID: review.Issue.RepoID, Repo: review.Issue.Repo, IsPrivate: review.Issue.Repo.IsPrivate, @@ -294,7 +284,7 @@ func (*actionNotifier) MergePullRequest(ctx context.Context, doer *user_model.Us ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionMergePullRequest, - Content: fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title), + Content: encodeContent(fmt.Sprintf("%d", pr.Issue.Index), pr.Issue.Title), RepoID: pr.Issue.Repo.ID, Repo: pr.Issue.Repo, IsPrivate: pr.Issue.Repo.IsPrivate, @@ -308,7 +298,7 @@ func (*actionNotifier) AutoMergePullRequest(ctx context.Context, doer *user_mode ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionAutoMergePullRequest, - Content: fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title), + Content: encodeContent(fmt.Sprintf("%d", pr.Issue.Index), pr.Issue.Title), RepoID: pr.Issue.Repo.ID, Repo: pr.Issue.Repo, IsPrivate: pr.Issue.Repo.IsPrivate, @@ -317,7 +307,7 @@ func (*actionNotifier) AutoMergePullRequest(ctx context.Context, doer *user_mode } } -func (*actionNotifier) NotifyPullRevieweDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) { +func (*actionNotifier) PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) { reviewerName := review.Reviewer.Name if len(review.OriginalAuthor) > 0 { reviewerName = review.OriginalAuthor @@ -326,7 +316,7 @@ func (*actionNotifier) NotifyPullRevieweDismiss(ctx context.Context, doer *user_ ActUserID: doer.ID, ActUser: doer, OpType: activities_model.ActionPullReviewDismissed, - Content: fmt.Sprintf("%d|%s|%s", review.Issue.Index, reviewerName, comment.Content), + Content: encodeContent(fmt.Sprintf("%d", review.Issue.Index), reviewerName, abbreviatedComment(comment.Content)), RepoID: review.Issue.Repo.ID, Repo: review.Issue.Repo, IsPrivate: review.Issue.Repo.IsPrivate, @@ -483,3 +473,35 @@ func (a *actionNotifier) NewRelease(ctx context.Context, rel *repo_model.Release log.Error("NotifyWatchers: %v", err) } } + +// ... later decoded in models/activities/action.go:GetIssueInfos +func encodeContent(params ...string) string { + contentEncoded, err := json.Marshal(params) + if err != nil { + log.Error("encodeContent: Unexpected json encoding error: %v", err) + } + return string(contentEncoded) +} + +// Given a comment of arbitrary-length Markdown text, create an abbreviated Markdown text appropriate for the +// activity feed. +func abbreviatedComment(comment string) string { + firstLine := strings.Split(comment, "\n")[0] + + if strings.HasPrefix(firstLine, "```") { + // First line is is a fenced code block... with no special abbreviate we would display a blank block, or in the + // worst-case a ```mermaid would display an error. Better to omit the comment. + return "" + } + + truncatedContent, truncatedRight := util.SplitStringAtByteN(firstLine, 200) + if truncatedRight != "" { + // in case the content is in a Latin family language, we remove the last broken word. + lastSpaceIdx := strings.LastIndex(truncatedContent, " ") + if lastSpaceIdx != -1 && (len(truncatedContent)-lastSpaceIdx < 15) { + truncatedContent = truncatedContent[:lastSpaceIdx] + "…" + } + } + + return truncatedContent +} diff --git a/services/feed/action_test.go b/services/feed/action_test.go index 93ca543a1a..48de7995bf 100644 --- a/services/feed/action_test.go +++ b/services/feed/action_test.go @@ -143,3 +143,64 @@ func TestPushCommits(t *testing.T) { assert.JSONEq(t, `{"Commits":[{"Sha1":"69554a6","Message":"not signed commit","AuthorEmail":"user2@example.com","AuthorName":"User2","CommitterEmail":"user2@example.com","CommitterName":"User2","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"}],"HeadCommit":{"Sha1":"69554a6","Message":"","AuthorEmail":"","AuthorName":"","CommitterEmail":"","CommitterName":"","Signature":null,"Verification":null,"Timestamp":"0001-01-01T00:00:00Z"},"CompareURL":"","Len":0}`, newNotification.Content) }) } + +func TestAbbreviatedComment(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + { + name: "short single line comment", + input: "This is a short comment", + expected: "This is a short comment", + }, + { + name: "empty comment", + input: "", + expected: "", + }, + { + name: "multiline comment - only first line", + input: "First line of comment\nSecond line\nThird line", + expected: "First line of comment", + }, + { + name: "before clip boundry", + input: strings.Repeat("abc ", 50), + expected: strings.Repeat("abc ", 50), + }, + { + name: "after clip boundry", + input: strings.Repeat("abc ", 51), + expected: "abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc…", + }, + { + name: "byte-split would land in middle of a rune", + input: strings.Repeat("🎉", 200), + expected: "🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉…", + }, + { + name: "mermaid block", + input: "Interesting point, here's a digram with my thoughts:\n```mermaid\ngraph LR\n a -->|some text| b\n```", + expected: "Interesting point, here's a digram with my thoughts:", + }, + { + name: "block start", + input: "```\n# This file describes the expected reviewers for a PR based on the changed\n# files.\n```\n\nI think this comment is wrong...", + expected: "", + }, + { + name: "labeled block start", + input: "```mermaid\ngraph LR\n a -->|some text| b\n```", + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := abbreviatedComment(tt.input) + assert.Equal(t, tt.expected, result, "abbreviatedComment(%q)", tt.input) + }) + } +} diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index e665ca0d19..b89e87f749 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -79,6 +79,7 @@ type AuthenticationForm struct { SkipLocalTwoFA bool GroupTeamMap string `binding:"ValidGroupTeamMap"` GroupTeamMapRemoval bool + AllowUsernameChange bool } // Validate validates fields diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index bb81e939b0..d040b41395 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -141,6 +141,7 @@ type RepoSettingForm struct { PushMirrorSyncOnCommit bool PushMirrorInterval string PushMirrorUseSSH bool + PushMirrorBranchFilter string `binding:"MaxSize(2048)" preprocess:"TrimSpace"` Private bool Template bool EnablePrune bool diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 35f8cd3821..1f2a7f232f 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -449,11 +449,29 @@ func getCommitFileLineCount(commit *git.Commit, filePath string) int { if err != nil { return 0 } - lineCount, err := blob.GetBlobLineCount() + reader, err := blob.DataAsync() if err != nil { return 0 } - return lineCount + defer reader.Close() + buf := make([]byte, 32*1024) + count := 1 + lineSep := []byte{'\n'} + + c, err := reader.Read(buf) + if c == 0 && err == io.EOF { + return 0 + } + for { + count += bytes.Count(buf[:c], lineSep) + switch { + case err == io.EOF: + return count + case err != nil: + return count + } + c, err = reader.Read(buf) + } } // Diff represents a difference between two git trees. @@ -1148,7 +1166,7 @@ func GetDiffSimple(ctx context.Context, gitRepo *git.Repository, opts *DiffOptio // so if we are using at least this version of git we don't have to tell ParsePatch to do // the skipping for us parsePatchSkipToFile := opts.SkipTo - if opts.SkipTo != "" && git.CheckGitVersionAtLeast("2.31") == nil { + if opts.SkipTo != "" { cmdDiff.AddOptionFormat("--skip-to=%s", opts.SkipTo) parsePatchSkipToFile = "" } diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index 9354abd656..d4d1cd4460 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -712,6 +712,8 @@ func TestGetDiffFull(t *testing.T) { assert.Equal(t, ".gitattributes", diff.Files[0].Name) assert.Equal(t, "24139dae656713ba861751fb2c2ac38839349a7a", diff.Files[0].NameHash) + assert.Len(t, diff.Files[0].Sections, 2) + assert.Equal(t, 4, diff.Files[0].Sections[1].Lines[0].SectionInfo.LeftIdx) }) } diff --git a/services/issue/comments_test.go b/services/issue/comments_test.go index 8fa410c0f0..fcf06d9ec8 100644 --- a/services/issue/comments_test.go +++ b/services/issue/comments_test.go @@ -8,9 +8,11 @@ import ( "forgejo.org/models/db" issues_model "forgejo.org/models/issues" + "forgejo.org/models/moderation" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" webhook_model "forgejo.org/models/webhook" + "forgejo.org/modules/json" "forgejo.org/modules/setting" "forgejo.org/modules/test" issue_service "forgejo.org/services/issue" @@ -148,3 +150,40 @@ func TestUpdateComment(t *testing.T) { unittest.AssertNotExistsBean(t, &issues_model.ContentHistory{CommentID: comment.ID}) }) } + +func TestCreateShadowCopyOnCommentUpdate(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")() + require.NoError(t, unittest.PrepareTestDatabase()) + + userAlexSmithID := int64(1002) + spamCommentID := int64(18) // posted by @alexsmith + abuseReportID := int64(1) // submitted for above comment + newCommentContent := "If anyone needs help, just contact me." + + // Retrieve the abusive user (@alexsmith), their SPAM comment and the abuse report already created for this comment. + poster := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID}) + comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: spamCommentID, PosterID: poster.ID}) + report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ + ID: abuseReportID, + ContentType: moderation.ReportedContentTypeComment, + ContentID: comment.ID, + }) + // The report should not already have a shadow copy linked. + assert.False(t, report.ShadowCopyID.Valid) + + // The abusive user is updating their comment. + oldContent := comment.Content + comment.Content = newCommentContent + require.NoError(t, issue_service.UpdateComment(t.Context(), comment, 0, poster, oldContent)) + + // Reload the report. + report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID}) + // A shadow copy should have been created and linked to our report. + assert.True(t, report.ShadowCopyID.Valid) + // Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values. + shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64}) + shadowCopyCommentData := new(issues_model.CommentData) + require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyCommentData)) + // Check to see if the initial content of the comment was stored within the shadow copy. + assert.Equal(t, oldContent, shadowCopyCommentData.Content) +} diff --git a/services/issue/pull.go b/services/issue/pull.go index 2eef1fbfa8..6245344ccb 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -71,7 +71,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, } var rules []*issues_model.CodeOwnerRule - for _, file := range []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"} { + for _, file := range []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS", ".forgejo/CODEOWNERS"} { if blob, err := commit.GetBlobByPath(file); err == nil { rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize) if err == nil { diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go index ca5c645e0c..d8646d9ddd 100644 --- a/services/mailer/mailer.go +++ b/services/mailer/mailer.go @@ -29,7 +29,7 @@ import ( notify_service "forgejo.org/services/notify" ntlmssp "github.com/Azure/go-ntlmssp" - "github.com/jaytaylor/html2text" + "github.com/inbucket/html2text" "gopkg.in/gomail.v2" ) diff --git a/services/migrations/github.go b/services/migrations/github.go index 9721c86180..317eb568b5 100644 --- a/services/migrations/github.go +++ b/services/migrations/github.go @@ -364,7 +364,8 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease) // Prevent open redirect if !hasBaseURL(redirectURL, g.baseURL) && - !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") { + !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") && + !hasBaseURL(redirectURL, "https://release-assets.githubusercontent.com/") { WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.GetID(), g, redirectURL) return io.NopCloser(strings.NewReader(redirectURL)), nil diff --git a/services/migrations/pagure.go b/services/migrations/pagure.go new file mode 100644 index 0000000000..f9433671c0 --- /dev/null +++ b/services/migrations/pagure.go @@ -0,0 +1,600 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package migrations + +import ( + "context" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "forgejo.org/modules/json" + "forgejo.org/modules/log" + base "forgejo.org/modules/migration" + "forgejo.org/modules/proxy" + "forgejo.org/modules/structs" + "forgejo.org/modules/util" +) + +var ( + _ base.Downloader = &PagureDownloader{} + _ base.DownloaderFactory = &PagureDownloaderFactory{} +) + +func init() { + RegisterDownloaderFactory(&PagureDownloaderFactory{}) +} + +// PagureDownloaderFactory defines a downloader factory +type PagureDownloaderFactory struct{} + +// PagureUser defines a user on Pagure to be migrated over to Forgejo +type PagureUser struct { + FullURL string `json:"full_url"` + Fullname string `json:"fullname"` + Name string `json:"name"` + URLPath string `json:"url_path"` +} + +// PagureRepoInfo describes the repository with preliminary information +type PagureRepoInfo struct { + ID int64 `json:"id"` + Name string `json:"name"` + FullName string `json:"fullname"` + Description string `json:"description"` + Topics []string `json:"tags"` + CloseStatuses []string `json:"close_status"` + Priorities map[string]string `json:"priorities"` + Milestones map[string]struct { + Active bool `json:"active"` + Date *string `json:"date"` + } `json:"milestones"` +} + +// PagureDownloader implements a Downloader interface to get repository information from Pagure +type PagureDownloader struct { + base.NullDownloader + ctx context.Context + client *http.Client + baseURL *url.URL + meta PagureRepoInfo + repoName string + token string + privateIssuesOnlyRepo bool + repoID int64 + maxIssueIndex int64 + userMap map[string]*PagureUser + milestoneMap map[int64]string + priorities map[string]string +} + +// PagureLabelsList defines a list of labels under an issue tracker +type PagureLabelsList struct { + Labels []string `json:"tags"` +} + +// PagureLabel defines a label under the issue tracker labels list +type PagureLabel struct { + Label string `json:"tag"` + LabelColor string `json:"tag_color"` + LabelDescription string `json:"tag_description"` +} + +// PagureIssueContext confirms if a said unit is an issue ticket or a pull request +type PagureIssueContext struct { + IsPullRequest bool +} + +// PagureIssuesResponse describes a list of issue tickets under an issue tracker +type PagureIssuesResponse struct { + Issues []struct { + Assignee any `json:"assignee"` + Blocks []string `json:"blocks"` + CloseStatus string `json:"close_status"` + ClosedAt string `json:"closed_at"` + ClosedBy any `json:"closed_by"` + Comments []any `json:"comments"` + Content string `json:"content"` + CustomFields []any `json:"custom_fields"` + DateCreated string `json:"date_created"` + Depends []any `json:"depends"` + ID int64 `json:"id"` + LastUpdated string `json:"last_updated"` + Milestone string `json:"milestone"` + Priority int64 `json:"priority"` + Private bool `json:"private"` + Status string `json:"status"` + Tags []string `json:"tags"` + Title string `json:"title"` + User PagureUser `json:"user"` + } `json:"issues"` + Pagination struct { + First string `json:"first"` + Last string `json:"last"` + Next *string `json:"next"` + Page int `json:"page"` + Pages int `json:"pages"` + PerPage int `json:"per_page"` + Prev string `json:"prev"` + } +} + +// PagureIssueDetail describes a list of issue comments under an issue ticket +type PagureIssueDetail struct { + Comment string `json:"comment"` + DateCreated string `json:"date_created"` + ID int64 `json:"id"` + Notification bool `json:"notification"` + User PagureUser `json:"user"` +} + +// PagureCommitInfo describes a commit +type PagureCommitInfo struct { + Author string `json:"author"` + CommitTime int64 `json:"commit_time"` + CommitTimeOffset int `json:"commit_time_offset"` + Committer string `json:"committer"` + Hash string `json:"hash"` + Message string `json:"message"` + ParentIDs []string `json:"parent_ids"` + TreeID string `json:"tree_id"` +} + +// PagurePRRresponse describes a list of pull requests under an issue tracker +type PagurePRResponse struct { + Pagination struct { + Next *string `json:"next"` + } `json:"pagination"` + Requests []struct { + Branch string `json:"branch"` + BranchFrom string `json:"branch_from"` + CommitStop string `json:"commit_stop"` + DateCreated string `json:"date_created"` + FullURL string `json:"full_url"` + ID int `json:"id"` + InitialComment string `json:"initial_comment"` + LastUpdated string `json:"last_updated"` + Status string `json:"status"` + Tags []string `json:"tags"` + Title string `json:"title"` + User PagureUser `json:"user"` + ClosedAt string `json:"closed_at"` + ClosedBy PagureUser `json:"closed_by"` + RepoFrom struct { + FullURL string `json:"full_url"` + } `json:"repo_from"` + } `json:"requests"` +} + +// processDate converts epoch time string to Go formatted time +func processDate(dateStr *string) time.Time { + date := time.Time{} + if dateStr == nil || *dateStr == "" { + return date + } + + unix, err := strconv.Atoi(*dateStr) + if err != nil { + log.Error("Error:", err) + return date + } + + date = time.Unix(int64(unix), 0) + return date +} + +// New returns a downloader related to this factory according MigrateOptions +func (f *PagureDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) { + u, err := url.Parse(opts.CloneAddr) + if err != nil { + return nil, err + } + + var repoName string + + fields := strings.Split(strings.Trim(u.Path, "/"), "/") + if len(fields) == 2 { + repoName = fields[0] + "/" + strings.TrimSuffix(fields[1], ".git") + } else if len(fields) == 1 { + repoName = strings.TrimSuffix(fields[0], ".git") + } else { + return nil, fmt.Errorf("invalid path: %s", u.Path) + } + + u.Path, u.Fragment = "", "" + log.Info("Create Pagure downloader. BaseURL: %v RepoName: %s", u, repoName) + + return NewPagureDownloader(ctx, u, opts.AuthToken, repoName), nil +} + +// GitServiceType returns the type of Git service +func (f *PagureDownloaderFactory) GitServiceType() structs.GitServiceType { + return structs.PagureService +} + +// SetContext sets context +func (d *PagureDownloader) SetContext(ctx context.Context) { + d.ctx = ctx +} + +// NewPagureDownloader creates a new downloader object +func NewPagureDownloader(ctx context.Context, baseURL *url.URL, token, repoName string) *PagureDownloader { + var privateIssuesOnlyRepo bool + if token != "" { + privateIssuesOnlyRepo = true + } + + downloader := &PagureDownloader{ + ctx: ctx, + baseURL: baseURL, + repoName: repoName, + client: &http.Client{ + Transport: &http.Transport{ + Proxy: proxy.Proxy(), + }, + }, + token: token, + privateIssuesOnlyRepo: privateIssuesOnlyRepo, + userMap: make(map[string]*PagureUser), + milestoneMap: make(map[int64]string), + priorities: make(map[string]string), + } + + return downloader +} + +// String sets the default text for information purposes +func (d *PagureDownloader) String() string { + return fmt.Sprintf("migration from Pagure server %s [%d]/%s", d.baseURL, d.repoID, d.repoName) +} + +// LogString sets the default text for logging purposes +func (d *PagureDownloader) LogString() string { + if d == nil { + return "" + } + return fmt.Sprintf("", d.baseURL, d.repoID, d.repoName) +} + +// callAPI handles all the requests made against Pagure +func (d *PagureDownloader) callAPI(endpoint string, parameter map[string]string, result any) error { + u, err := d.baseURL.Parse(endpoint) + if err != nil { + return err + } + + if parameter != nil { + query := u.Query() + for k, v := range parameter { + query.Set(k, v) + } + u.RawQuery = query.Encode() + } + + req, err := http.NewRequestWithContext(d.ctx, "GET", u.String(), nil) + if err != nil { + return err + } + if d.privateIssuesOnlyRepo { + req.Header.Set("Authorization", "token "+d.token) + } + + resp, err := d.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + decoder := json.NewDecoder(resp.Body) + return decoder.Decode(&result) +} + +// GetRepoInfo returns repository information from Pagure +func (d *PagureDownloader) GetRepoInfo() (*base.Repository, error) { + err := d.callAPI("/api/0/"+d.repoName, nil, &d.meta) + if err != nil { + return nil, err + } + + d.repoID, d.priorities = d.meta.ID, d.meta.Priorities + + cloneURL, err := d.baseURL.Parse(d.meta.FullName) + if err != nil { + return nil, err + } + originalURL, err := d.baseURL.Parse(d.meta.FullName) + if err != nil { + return nil, err + } + + return &base.Repository{ + Name: d.meta.Name, + Description: d.meta.Description, + CloneURL: cloneURL.String() + ".git", + OriginalURL: originalURL.String(), + }, nil +} + +// GetMilestones returns milestones information from Pagure +func (d *PagureDownloader) GetMilestones() ([]*base.Milestone, error) { + milestones := make([]*base.Milestone, 0, len(d.meta.Milestones)) + for name, details := range d.meta.Milestones { + state := "closed" + if details.Active { + state = "open" + } + + deadline := processDate(details.Date) + milestones = append(milestones, &base.Milestone{ + Title: name, + Description: "", + Deadline: &deadline, + State: state, + }) + } + + return milestones, nil +} + +// GetLabels returns labels information from Pagure +func (d *PagureDownloader) GetLabels() ([]*base.Label, error) { + rawLabels := PagureLabelsList{} + + err := d.callAPI("/api/0/"+d.repoName+"/tags", nil, &rawLabels) + if err != nil { + return nil, err + } + + labels := make([]*base.Label, 0, len(rawLabels.Labels)+len(d.meta.CloseStatuses)) + + for _, label := range rawLabels.Labels { + rawLabel := PagureLabel{} + err = d.callAPI("/api/0/"+d.repoName+"/tag/"+label, nil, &rawLabel) + if err != nil { + return nil, err + } + labels = append(labels, &base.Label{ + Name: label, + Description: rawLabel.LabelDescription, + Color: strings.TrimPrefix(rawLabel.LabelColor, "#"), + }) + } + + for _, closeStatus := range d.meta.CloseStatuses { + labels = append(labels, &base.Label{ + Name: "Closed As/" + closeStatus, + Description: "Closed with the reason of " + closeStatus, + Color: "FF0000", + Exclusive: true, + }) + } + + for _, value := range d.priorities { + if value != "" { + labels = append(labels, &base.Label{ + Name: "Priority/" + value, + Description: "Priority of " + value, + Color: "FF00FF", + Exclusive: true, + }) + } + } + + return labels, nil +} + +// GetIssues returns issue tickets from Pagure +func (d *PagureDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) { + rawIssues := PagureIssuesResponse{} + + err := d.callAPI( + "/api/0/"+d.repoName+"/issues", + map[string]string{ + "page": strconv.Itoa(page), + "per_page": strconv.Itoa(perPage), + "status": "all", + }, + &rawIssues, + ) + if err != nil { + return nil, false, err + } + + issues := make([]*base.Issue, 0, len(rawIssues.Issues)) + for _, issue := range rawIssues.Issues { + log.Debug("Processing issue %d", issue.ID) + if d.privateIssuesOnlyRepo && !issue.Private { + log.Info("Skipping issue %d because it is not private and we are only downloading private issues", issue.ID) + continue + } + labels := []*base.Label{} + for _, tag := range issue.Tags { + labels = append(labels, &base.Label{Name: tag}) + } + + if issue.CloseStatus != "" { + labels = append(labels, &base.Label{Name: "Closed As/" + issue.CloseStatus}) + } + + priorityStr := "" + if issue.Priority != 0 { + priorityStr = strconv.FormatInt(issue.Priority, 10) + } + + if priorityStr != "" { + priorityValue, ok := d.priorities[priorityStr] + if ok { + labels = append(labels, &base.Label{Name: "Priority/" + priorityValue}) + } + } + log.Trace("Adding issue: %d", issue.ID) + + closedat := processDate(&issue.ClosedAt) + + issues = append(issues, &base.Issue{ + Title: issue.Title, + Number: issue.ID, + PosterName: issue.User.Name, + PosterID: -1, + Content: issue.Content, + Milestone: issue.Milestone, + State: strings.ToLower(issue.Status), + Created: processDate(&issue.DateCreated), + Updated: processDate(&issue.LastUpdated), + Closed: &closedat, + Labels: labels, + ForeignIndex: issue.ID, + Context: PagureIssueContext{IsPullRequest: false}, + }) + + if d.maxIssueIndex < issue.ID { + d.maxIssueIndex = issue.ID + } + } + hasNext := rawIssues.Pagination.Next == nil + + return issues, hasNext, nil +} + +// GetComments returns issue comments from Pagure +func (d *PagureDownloader) GetComments(commentable base.Commentable) ([]*base.Comment, bool, error) { + context, ok := commentable.GetContext().(PagureIssueContext) + if !ok { + return nil, false, fmt.Errorf("unexpected context: %+v", commentable.GetContext()) + } + + list := struct { + Comments []PagureIssueDetail `json:"comments"` + }{} + var endpoint string + + if context.IsPullRequest { + endpoint = fmt.Sprintf("/api/0/%s/pull-request/%d", d.repoName, commentable.GetForeignIndex()) + } else { + endpoint = fmt.Sprintf("/api/0/%s/issue/%d", d.repoName, commentable.GetForeignIndex()) + } + + err := d.callAPI(endpoint, nil, &list) + if err != nil { + log.Error("Error calling API: %v", err) + return nil, false, err + } + + comments := make([]*base.Comment, 0, len(list.Comments)) + for _, unit := range list.Comments { + if len(unit.Comment) == 0 { + log.Error("Empty comment") + continue + } + + log.Trace("Adding comment: %d", unit.ID) + c := &base.Comment{ + IssueIndex: commentable.GetLocalIndex(), + Index: unit.ID, + PosterName: unit.User.Name, + PosterID: -1, + Content: unit.Comment, + Created: processDate(&unit.DateCreated), + } + comments = append(comments, c) + } + + return comments, true, nil +} + +// GetPullRequests returns pull requests from Pagure +func (d *PagureDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) { + // Could not figure out how to disable this in opts, so if a private issues only repo, + // We just return an empty list + if d.privateIssuesOnlyRepo { + pullRequests := make([]*base.PullRequest, 0) + return pullRequests, true, nil + } + + rawPullRequests := PagurePRResponse{} + commit := PagureCommitInfo{} + + err := d.callAPI( + "/api/0/"+d.repoName+"/pull-requests", + map[string]string{ + "page": strconv.Itoa(page), + "per_page": strconv.Itoa(perPage), + "status": "all", + }, + &rawPullRequests, + ) + if err != nil { + return nil, false, err + } + + pullRequests := make([]*base.PullRequest, 0, len(rawPullRequests.Requests)) + + for _, pr := range rawPullRequests.Requests { + var state, baseSHA string + var merged bool + labels := []*base.Label{} + + for _, tag := range pr.Tags { + labels = append(labels, &base.Label{Name: tag}) + } + mergedtime := processDate(&pr.ClosedAt) + + err = d.callAPI("/api/0/"+d.repoName+"/c/"+pr.CommitStop+"/info", nil, &commit) + if err != nil { + return nil, false, err + } + + if util.ASCIIEqualFold(pr.Status, "merged") { + state, merged, baseSHA = "closed", true, commit.ParentIDs[0] + } else if util.ASCIIEqualFold(pr.Status, "open") { + state, merged, baseSHA = "open", false, commit.ParentIDs[0] + } else { + state, merged, baseSHA = "closed", false, commit.ParentIDs[0] + } + + pullRequests = append(pullRequests, &base.PullRequest{ + Title: pr.Title, + Number: int64(pr.ID), + PosterName: pr.User.Name, + PosterID: -1, + Content: pr.InitialComment, + State: state, + Created: processDate(&pr.DateCreated), + Updated: processDate(&pr.LastUpdated), + MergedTime: &mergedtime, + Closed: &mergedtime, + Merged: merged, + Labels: labels, + Head: base.PullRequestBranch{ + Ref: pr.BranchFrom, + SHA: pr.CommitStop, + RepoName: d.repoName, + CloneURL: pr.RepoFrom.FullURL + ".git", + }, + Base: base.PullRequestBranch{ + Ref: pr.Branch, + SHA: baseSHA, + RepoName: d.repoName, + }, + ForeignIndex: int64(pr.ID), + PatchURL: pr.FullURL + ".patch", + Context: PagureIssueContext{IsPullRequest: true}, + }) + + // SECURITY: Ensure that the PR is safe + _ = CheckAndEnsureSafePR(pullRequests[len(pullRequests)-1], d.baseURL.String(), d) + } + + hasNext := rawPullRequests.Pagination.Next == nil + + return pullRequests, hasNext, nil +} + +// GetTopics return repository topics from Pagure +func (d *PagureDownloader) GetTopics() ([]string, error) { + return d.meta.Topics, nil +} diff --git a/services/migrations/pagure_test.go b/services/migrations/pagure_test.go new file mode 100644 index 0000000000..c81bd28904 --- /dev/null +++ b/services/migrations/pagure_test.go @@ -0,0 +1,616 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package migrations + +import ( + "fmt" + "net/url" + "os" + "strings" + "testing" + "time" + + "forgejo.org/models/unittest" + base "forgejo.org/modules/migration" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPagureDownloadRepoWithPublicIssues(t *testing.T) { + // Skip tests if Pagure token is not found + cloneUser := os.Getenv("PAGURE_CLONE_USER") + clonePassword := os.Getenv("PAGURE_CLONE_PASSWORD") + apiUser := os.Getenv("PAGURE_API_USER") + apiPassword := os.Getenv("PAGURE_API_TOKEN") + + cloneAddr := fmt.Sprintf("https://%s:%s@pagure.io/protop2g-test-srce.git", cloneUser, clonePassword) + u, _ := url.Parse(cloneAddr) + + fixtPath := "./testdata/pagure/full_download/unauthorized" + server := unittest.NewMockWebServer(t, "https://pagure.io", fixtPath, false) + defer server.Close() + + factory := &PagureDownloaderFactory{} + downloader, err := factory.New(t.Context(), base.MigrateOptions{ + CloneAddr: u.String(), + AuthUsername: apiUser, + AuthPassword: apiPassword, + }) + require.NoError(t, err) + + repo, err := downloader.GetRepoInfo() + require.NoError(t, err) + + // Testing repository contents migration + assertRepositoryEqual(t, &base.Repository{ + Name: "protop2g-test-srce", + Owner: "", + Description: "The source namespace for the Pagure Exporter project to run tests against", + CloneURL: cloneAddr, + OriginalURL: strings.ReplaceAll(cloneAddr, ".git", ""), + }, repo) + + topics, err := downloader.GetTopics() + require.NoError(t, err) + + // Testing repository topics migration + assert.Equal(t, []string{"srce", "test", "gridhead", "protop2g"}, topics) + + // Testing labels migration + labels, err := downloader.GetLabels() + require.NoError(t, err) + assert.Len(t, labels, 15) + + // Testing issue tickets probing + issues, isEnd, err := downloader.GetIssues(1, 20) + require.NoError(t, err) + assert.True(t, isEnd) + + // Testing issue tickets migration + assertIssuesEqual(t, []*base.Issue{ + { + Number: 2, + Title: "This is the title of the second test issue", + Content: "This is the body of the second test issue", + PosterName: "t0xic0der", + PosterID: -1, + State: "closed", + Milestone: "Milestone BBBB", + Created: time.Date(2023, time.October, 13, 4, 1, 16, 0, time.UTC), + Updated: time.Date(2025, time.June, 25, 6, 25, 57, 0, time.UTC), + Closed: timePtr(time.Date(2025, time.June, 25, 6, 22, 59, 0, time.UTC)), + Labels: []*base.Label{ + { + Name: "cccc", + }, + { + Name: "dddd", + }, + { + Name: "Closed As/Complete", + }, + { + Name: "Priority/Rare", + }, + }, + }, + { + Number: 1, + Title: "This is the title of the first test issue", + Content: "This is the body of the first test issue", + PosterName: "t0xic0der", + PosterID: -1, + State: "open", + Milestone: "Milestone AAAA", + Created: time.Date(2023, time.October, 13, 3, 57, 42, 0, time.UTC), + Updated: time.Date(2025, time.June, 25, 6, 25, 45, 0, time.UTC), + Closed: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)), + Labels: []*base.Label{ + { + Name: "aaaa", + }, + { + Name: "bbbb", + }, + { + Name: "Priority/Epic", + }, + }, + }, + }, issues) + + // Testing comments under issue tickets + comments, _, err := downloader.GetComments(issues[0]) + require.NoError(t, err) + assertCommentsEqual(t, []*base.Comment{ + { + IssueIndex: 2, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2023, time.October, 13, 4, 3, 30, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue tagged with: cccc, dddd", + }, + { + IssueIndex: 2, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2023, time.October, 13, 4, 6, 4, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "The is the first comment under the second test issue", + }, + { + IssueIndex: 2, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2023, time.October, 13, 4, 6, 16, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "The is the second comment under the second test issue", + }, + { + IssueIndex: 2, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2023, time.October, 13, 4, 7, 12, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue status updated to: Closed (was: Open)", + }, + { + IssueIndex: 2, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.May, 8, 4, 50, 21, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone BBBB", + }, + { + IssueIndex: 2, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.June, 25, 6, 22, 52, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: None (was: Milestone BBBB)\n- Issue status updated to: Open (was: Closed)", + }, + { + IssueIndex: 2, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.June, 25, 6, 23, 0o2, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue close_status updated to: Complete\n- Issue status updated to: Closed (was: Open)", + }, + { + IssueIndex: 2, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.June, 25, 6, 24, 34, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone BBBB", + }, + { + IssueIndex: 2, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.June, 25, 6, 25, 57, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Rare", + }, + }, comments) + + prs, isEnd, err := downloader.GetPullRequests(1, 20) + require.NoError(t, err) + assert.True(t, isEnd) + + // Testing pull requests migrated + assertPullRequestsEqual(t, []*base.PullRequest{ + { + Number: 10, + Title: "Change the branch identity to `test-ffff` in the README.md file", + Content: "Signed-off-by: Akashdeep Dhar ", + PosterName: "t0xic0der", + PosterID: -1, + State: "closed", + Created: time.Date(2025, time.May, 19, 6, 12, 45, 0, time.UTC), + Updated: time.Date(2025, time.May, 19, 6, 17, 11, 0, time.UTC), + Closed: timePtr(time.Date(2025, time.May, 19, 6, 17, 11, 0, time.UTC)), + MergedTime: timePtr(time.Date(2025, time.May, 19, 6, 17, 11, 0, time.UTC)), + Merged: true, + Labels: []*base.Label{ + { + Name: "ffff", + }, + }, + Head: base.PullRequestBranch{ + Ref: "test-ffff", + SHA: "1a6ccc212aa958a0fe76155c2907c889969a7224", + RepoName: "protop2g-test-srce", + }, + Base: base.PullRequestBranch{ + Ref: "main", + SHA: "01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + RepoName: "protop2g-test-srce", + }, + }, + { + Number: 9, + Title: "Change the branch identity to `test-eeee` in the README.md file", + Content: "Signed-off-by: Akashdeep Dhar ", + PosterName: "t0xic0der", + PosterID: -1, + State: "closed", + Created: time.Date(2025, time.May, 19, 6, 12, 41, 0, time.UTC), + Updated: time.Date(2025, time.May, 19, 6, 14, 3, 0, time.UTC), + Closed: timePtr(time.Date(2025, time.May, 19, 6, 14, 3, 0, time.UTC)), + MergedTime: timePtr(time.Date(2025, time.May, 19, 6, 14, 3, 0, time.UTC)), + Merged: true, + Labels: []*base.Label{ + { + Name: "eeee", + }, + }, + Head: base.PullRequestBranch{ + Ref: "test-eeee", + SHA: "01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + RepoName: "protop2g-test-srce", + }, + Base: base.PullRequestBranch{ + Ref: "main", + SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7", + RepoName: "protop2g-test-srce", + }, + }, + { + Number: 8, + Title: "Change the branch identity to `test-dddd` in the README.md file", + Content: "Signed-off-by: Akashdeep Dhar ", + PosterName: "t0xic0der", + PosterID: -1, + State: "closed", + Created: time.Date(2025, time.May, 5, 6, 45, 32, 0, time.UTC), + Updated: time.Date(2025, time.May, 5, 6, 54, 13, 0, time.UTC), + Closed: timePtr(time.Date(2025, time.May, 5, 6, 54, 13, 0, time.UTC)), + MergedTime: timePtr(time.Date(2025, time.May, 5, 6, 54, 13, 0, time.UTC)), // THIS IS WRONG + Merged: false, + Labels: []*base.Label{ + { + Name: "dddd", + }, + }, + Head: base.PullRequestBranch{ + Ref: "test-dddd", + SHA: "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160", + RepoName: "protop2g-test-srce", + }, + Base: base.PullRequestBranch{ + Ref: "main", + SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7", + RepoName: "protop2g-test-srce", + }, + }, + { + Number: 7, + Title: "Change the branch identity to `test-cccc` in the README.md file", + Content: "Signed-off-by: Akashdeep Dhar ", + PosterName: "t0xic0der", + PosterID: -1, + State: "closed", + Created: time.Date(2025, time.May, 5, 6, 45, 6, 0, time.UTC), + Updated: time.Date(2025, time.May, 5, 6, 54, 3, 0, time.UTC), // IT SHOULD BE NIL + Closed: timePtr(time.Date(2025, time.May, 5, 6, 54, 3, 0, time.UTC)), // IT is CLOSED, Not MERGED so SHOULD NOT BE NIL + MergedTime: timePtr(time.Date(2025, time.May, 5, 6, 54, 3, 0, time.UTC)), // THIS IS WRONG + Merged: false, + Labels: []*base.Label{ + { + Name: "cccc", + }, + }, + Head: base.PullRequestBranch{ + Ref: "test-cccc", + SHA: "f1246e331cade9341b9e4f311b7a134f99893d21", + RepoName: "protop2g-test-srce", + }, + Base: base.PullRequestBranch{ + Ref: "main", + SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7", + RepoName: "protop2g-test-srce", + }, + }, + { + Number: 6, + Title: "Change the branch identity to `test-bbbb` in the README.md file", + Content: "Signed-off-by: Akashdeep Dhar ", + PosterName: "t0xic0der", + PosterID: -1, + State: "open", + Created: time.Date(2025, time.May, 5, 6, 44, 30, 0, time.UTC), + Updated: time.Date(2025, time.May, 19, 8, 30, 50, 0, time.UTC), + Closed: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)), + MergedTime: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)), + Merged: false, + Labels: []*base.Label{ + { + Name: "bbbb", + }, + }, + Head: base.PullRequestBranch{ + Ref: "test-bbbb", + SHA: "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + RepoName: "protop2g-test-srce", + }, + Base: base.PullRequestBranch{ + Ref: "main", + SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7", + RepoName: "protop2g-test-srce", + }, + }, + { + Number: 5, + Title: "Change the branch identity to `test-aaaa` in the README.md file", + Content: "Signed-off-by: Akashdeep Dhar ", + PosterName: "t0xic0der", + PosterID: -1, + State: "open", + Created: time.Date(2025, time.May, 5, 6, 43, 57, 0, time.UTC), + Updated: time.Date(2025, time.May, 19, 6, 29, 45, 0, time.UTC), + Closed: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)), + MergedTime: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)), + Merged: false, + Labels: []*base.Label{ + { + Name: "aaaa", + }, + }, + Head: base.PullRequestBranch{ + Ref: "test-aaaa", + SHA: "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + RepoName: "protop2g-test-srce", + }, + Base: base.PullRequestBranch{ + Ref: "main", + SHA: "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7", + RepoName: "protop2g-test-srce", + }, + }, + }, prs) + + // Testing comments under pull requests + comments, _, err = downloader.GetComments(prs[5]) + require.NoError(t, err) + assertCommentsEqual(t, []*base.Comment{ + { + IssueIndex: 5, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.May, 5, 6, 44, 13, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa", + }, + { + IssueIndex: 5, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.May, 7, 5, 25, 21, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "This is the first comment under this pull request.", + }, + { + IssueIndex: 5, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.May, 7, 5, 25, 29, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "This is the second comment under this pull request.", + }, + }, comments) + + // Testing milestones migration + milestones, err := downloader.GetMilestones() + require.NoError(t, err) + dict := map[string]*base.Milestone{ + "Milestone AAAA": { + Title: "Milestone AAAA", + Deadline: timePtr(time.Date(2025, time.December, 12, 0, 0, 0, 0, time.UTC)), + State: "open", + }, + "Milestone BBBB": { + Title: "Milestone BBBB", + Deadline: timePtr(time.Date(2025, time.December, 12, 0, 0, 0, 0, time.UTC)), + State: "closed", + }, + "Milestone CCCC": { + Title: "Milestone CCCC", + Deadline: timePtr(time.Date(2025, time.December, 12, 0, 0, 0, 0, time.UTC)), + State: "open", + }, + "Milestone DDDD": { + Title: "Milestone DDDD", + Deadline: timePtr(time.Date(2025, time.December, 12, 0, 0, 0, 0, time.UTC)), + State: "closed", + }, + } + + // We do not like when tests fail just because of dissimilar ordering + for _, item := range milestones { + assertMilestoneEqual(t, item, dict[item.Title]) + } +} + +func TestPagureDownloadRepoWithPrivateIssues(t *testing.T) { + // Skip tests if Pagure token is not found + cloneUser := os.Getenv("PAGURE_CLONE_USER") + clonePassword := os.Getenv("PAGURE_CLONE_PASSWORD") + apiUser := os.Getenv("PAGURE_API_USER") + apiPassword := os.Getenv("PAGURE_API_TOKEN") + if apiUser == "" || apiPassword == "" { + t.Skip("skipped test because a PAGURE_ variable was not in the environment") + } + + cloneAddr := fmt.Sprintf("https://%s:%s@pagure.io/protop2g-test-srce.git", cloneUser, clonePassword) + u, _ := url.Parse(cloneAddr) + + fixtPath := "./testdata/pagure/full_download/authorized" + server := unittest.NewMockWebServer(t, "https://pagure.io", fixtPath, false) + defer server.Close() + + factory := &PagureDownloaderFactory{} + downloader, err := factory.New(t.Context(), base.MigrateOptions{ + CloneAddr: u.String(), + AuthUsername: apiUser, + AuthPassword: apiPassword, + AuthToken: apiPassword, + }) + require.NoError(t, err) + + repo, err := downloader.GetRepoInfo() + require.NoError(t, err) + + // Testing repository contents migration + assertRepositoryEqual(t, &base.Repository{ + Name: "protop2g-test-srce", + Owner: "", + Description: "The source namespace for the Pagure Exporter project to run tests against", + CloneURL: cloneAddr, + OriginalURL: strings.ReplaceAll(cloneAddr, ".git", ""), + }, repo) + + topics, err := downloader.GetTopics() + require.NoError(t, err) + + // Testing repository topics migration + assert.Equal(t, []string{"srce", "test", "gridhead", "protop2g"}, topics) + + // Testing labels migration + labels, err := downloader.GetLabels() + require.NoError(t, err) + assert.Len(t, labels, 15) + + // Testing issue tickets probing + issues, isEnd, err := downloader.GetIssues(1, 20) + require.NoError(t, err) + assert.True(t, isEnd) + + // Testing issue tickets migration + assertIssuesEqual(t, []*base.Issue{ + { + Number: 4, + Title: "This is the title of the fourth test issue", + Content: "This is the body of the fourth test issue", + PosterName: "t0xic0der", + PosterID: -1, + State: "closed", + Milestone: "Milestone DDDD", + Created: time.Date(2023, time.November, 21, 8, 6, 56, 0, time.UTC), + Updated: time.Date(2025, time.June, 25, 6, 26, 26, 0, time.UTC), + Closed: timePtr(time.Date(2025, time.June, 25, 6, 23, 51, 0, time.UTC)), + Labels: []*base.Label{ + { + Name: "gggg", + }, + { + Name: "hhhh", + }, + { + Name: "Closed As/Baseless", + }, + { + Name: "Priority/Common", + }, + }, + }, + { + Number: 3, + Title: "This is the title of the third test issue", + Content: "This is the body of the third test issue", + PosterName: "t0xic0der", + PosterID: -1, + State: "open", + Milestone: "Milestone CCCC", + Created: time.Date(2023, time.November, 21, 8, 3, 57, 0, time.UTC), + Updated: time.Date(2025, time.June, 25, 6, 26, 7, 0, time.UTC), + Closed: timePtr(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)), + Labels: []*base.Label{ + { + Name: "eeee", + }, + { + Name: "ffff", + }, + { + Name: "Priority/Uncommon", + }, + }, + }, + }, issues) + + // Testing comments under issue tickets + comments, _, err := downloader.GetComments(issues[0]) + require.NoError(t, err) + assertCommentsEqual(t, []*base.Comment{ + { + IssueIndex: 4, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2023, time.November, 21, 8, 7, 25, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "This is the first comment under the fourth test issue", + }, + { + IssueIndex: 4, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2023, time.November, 21, 8, 7, 34, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "This is the second comment under the fourth test issue", + }, + { + IssueIndex: 4, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2023, time.November, 21, 8, 8, 1, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue status updated to: Closed (was: Open)", + }, + { + IssueIndex: 4, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.May, 8, 4, 50, 46, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone DDDD", + }, + { + IssueIndex: 4, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.June, 25, 6, 23, 46, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: None (was: Milestone DDDD)\n- Issue status updated to: Open (was: Closed)", + }, + { + IssueIndex: 4, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.June, 25, 6, 23, 52, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue close_status updated to: Baseless\n- Issue status updated to: Closed (was: Open)", + }, + { + IssueIndex: 4, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.June, 25, 6, 24, 55, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone DDDD", + }, + { + IssueIndex: 4, + PosterName: "t0xic0der", + PosterID: -1, + Created: time.Date(2025, time.June, 25, 6, 26, 26, 0, time.UTC), + Updated: time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), + Content: "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Common", + }, + }, comments) +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce new file mode 100644 index 0000000000..5f569b145c --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce @@ -0,0 +1,85 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:51:21 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 1464 +content-security-policy: default-src 'self';script-src 'self' 'nonce-Tr8ktNGt3XoBWQBIXrrPTeA1v'; style-src 'self' 'nonce-Tr8ktNGt3XoBWQBIXrrPTeA1v'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmI4NGRmYWMyY2Q2MzVlOWRkOTQwMGYwNzk2NzdlOGQxN2UxYTJlMCJ9.G2IX6Q.ETAG_sYgE1V9xYQNy_CD4HOP32Q; Expires=Sat, 23-Aug-2025 06:51:21 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo new file mode 100644 index 0000000000..e31c4193e0 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:44:23 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 452 +content-security-policy: default-src 'self';script-src 'self' 'nonce-jtS0HiLSSKCUK3FUuLv7XD9KG'; style-src 'self' 'nonce-jtS0HiLSSKCUK3FUuLv7XD9KG'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiY2EzN2Y5MTI1ZjkxNTkzMDViMWQ5ZGVhYjkyNzVlZTkwNmVjYzgzYiJ9.G2IWRw.1ZmhWySKFk3Lw_bA-EwmcNCgcz0; Expires=Sat, 23-Aug-2025 06:44:23 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1747635011, + "commit_time_offset": 0, + "committer": "Akashdeep Dhar", + "hash": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + "message": "Change the branch identity to `test-eeee` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "1e8fa9a17b4b4ddde50f334626b0a5497070cff7" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo new file mode 100644 index 0000000000..722b59a6dd --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:44:57 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 454 +content-security-policy: default-src 'self';script-src 'self' 'nonce-6gKus9S40eu36HFYePUnyxB36'; style-src 'self' 'nonce-6gKus9S40eu36HFYePUnyxB36'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZjM5ZTYyYWM5NDViYTYwMjg1MjFlMTAzNjU4OWQ3Zjc1NjA5ZmUzMSJ9.G2IWaQ._b9edyW_4DSX-umHlyVib496s00; Expires=Sat, 23-Aug-2025 06:44:57 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1697169509, + "commit_time_offset": 330, + "committer": "Akashdeep Dhar", + "hash": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160", + "message": "Change the branch identity to `test-dddd` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "586a1e8a79e572691dc086ef7bf4e2f6d34c5254" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo new file mode 100644 index 0000000000..8d3fb6f5f2 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:43:47 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 452 +content-security-policy: default-src 'self';script-src 'self' 'nonce-bDXeQlGZOYsS26Mr1BJPgodzY'; style-src 'self' 'nonce-bDXeQlGZOYsS26Mr1BJPgodzY'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiNDRkYmIwMjQwODI1ODc4MGUzYWM4ZDZhZTkxYWQ2NTkyMDFhNTg0ZSJ9.G2IWIw.U2Rb6xUm4Wk9ODweB3hH1cggWkM; Expires=Sat, 23-Aug-2025 06:43:47 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1747635352, + "commit_time_offset": 0, + "committer": "Akashdeep Dhar", + "hash": "1a6ccc212aa958a0fe76155c2907c889969a7224", + "message": "Change the branch identity to `test-ffff` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "01b420e2964928a15f790f9b7c1a0053e7b5f0a5" + ], + "tree_id": "0c5e64a6b912cb0c3d66e66896fa98a98da69fe4" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo new file mode 100644 index 0000000000..852a4b724d --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:46:23 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 454 +content-security-policy: default-src 'self';script-src 'self' 'nonce-JLGc3LbKkMMzOnQTvUIIZbAwF'; style-src 'self' 'nonce-JLGc3LbKkMMzOnQTvUIIZbAwF'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYTE0Y2EzMTZiMWRkZGZjOTJjMjkwZTkyNjM5OWEzYWZiNjA0OTZiMCJ9.G2IWvw.SNBn8NelY5GBQ-8SmLY-Uwy-uq0; Expires=Sat, 23-Aug-2025 06:46:23 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1697169040, + "commit_time_offset": 330, + "committer": "Akashdeep Dhar", + "hash": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "message": "Change the branch identity to `test-bbbb` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "7a23fc15f5a1463f2c425f0146def7c19ecf6c88" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo new file mode 100644 index 0000000000..a35fb1f416 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:46:59 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 454 +content-security-policy: default-src 'self';script-src 'self' 'nonce-ORfdixyFztBA0Sx54SlhQuMio'; style-src 'self' 'nonce-ORfdixyFztBA0Sx54SlhQuMio'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmY2MjEyYzNmMDkzMmZkZDMxYjYwYzVmMDdjYzEzY2JhZTJkMjVmZCJ9.G2IW4w.JCI6ih36NlW5IPwNX1LaCbb6v5U; Expires=Sat, 23-Aug-2025 06:46:59 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1697169185, + "commit_time_offset": 330, + "committer": "Akashdeep Dhar", + "hash": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "message": "Change the branch identity to `test-aaaa` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "e7911825d29e73f260de95d5070898ccbe6340a6" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo new file mode 100644 index 0000000000..8e161ce97c --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:45:31 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 454 +content-security-policy: default-src 'self';script-src 'self' 'nonce-chsmQCN0V1J9MIqK5NU6MqBJv'; style-src 'self' 'nonce-chsmQCN0V1J9MIqK5NU6MqBJv'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZDNmYWUzNTBlOWZkNDY0MTJlOWNkMjQ0M2FjZmYyY2VkZjBlY2ZiYSJ9.G2IWjA.co1poP1T_e07frtYRBAkxcp2RTM; Expires=Sat, 23-Aug-2025 06:45:32 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1697169354, + "commit_time_offset": 330, + "committer": "Akashdeep Dhar", + "hash": "f1246e331cade9341b9e4f311b7a134f99893d21", + "message": "Change the branch identity to `test-cccc` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "f1e37736d409bb136451dfc8e3a2017d3af2ce7d" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F3 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F3 new file mode 100644 index 0000000000..d9b5d17073 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F3 @@ -0,0 +1,17 @@ +HTTP/2 404 +date: Wed, 23 Jul 2025 06:17:46 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 233 +content-security-policy: default-src 'self';script-src 'self' 'nonce-AyzMgRLEDXIM7Oiv1cbBRx6WC'; style-src 'self' 'nonce-AyzMgRLEDXIM7Oiv1cbBRx6WC'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiMGMxMmJkYmFmNWY1ODBiNzQxY2Q2Y2RiODk1OTRmOWMwOGY1OWM2ZSJ9.G2IQCg.BSeYpRe9-FITZnjNS1EKHjwOFmM; Expires=Sat, 23-Aug-2025 06:17:46 GMT; Secure; HttpOnly; Path=/ +content-type: text/html; charset=UTF-8 + + +404 Not Found +

    Not Found

    +

    The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

    diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F4 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F4 new file mode 100644 index 0000000000..6aa95bba6a --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F4 @@ -0,0 +1,17 @@ +HTTP/2 404 +date: Wed, 23 Jul 2025 06:17:58 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 233 +content-security-policy: default-src 'self';script-src 'self' 'nonce-F7TmwHL5oVhCcN5Mddi8OGms7'; style-src 'self' 'nonce-F7TmwHL5oVhCcN5Mddi8OGms7'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTYwOWIyN2EzYjA4NmE5OTZhNjk3NWU5ZWM4OTI1MWNkN2U5OWYyMiJ9.G2IQFg.qReOtqa1f6EAhBqYOj_aQRCvgac; Expires=Sat, 23-Aug-2025 06:17:58 GMT; Secure; HttpOnly; Path=/ +content-type: text/html; charset=UTF-8 + + +404 Not Found +

    Not Found

    +

    The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

    diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues new file mode 100644 index 0000000000..84a9e44c35 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues @@ -0,0 +1,299 @@ +HTTP/2 200 +date: Wed, 30 Jul 2025 05:15:59 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 18702 +content-security-policy: default-src 'self';script-src 'self' 'nonce-SA5GWMgMPH5tNiDy97SIr6jxM'; style-src 'self' 'nonce-SA5GWMgMPH5tNiDy97SIr6jxM'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTA1NjdkYzFjN2QyMjFhMGU3NTVmN2ZlMDYzZTgyMTQ0ZTM3NzFkNiJ9.G2s8Dw.36fFhcCc63_kPkxcJp8tz-ufC8Y; Expires=Sat, 30-Aug-2025 05:15:59 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "args": { + "assignee": null, + "author": null, + "milestones": [], + "no_stones": null, + "order": null, + "priority": null, + "since": null, + "status": "all", + "tags": [] + }, + "issues": [ + { + "assignee": null, + "blocks": [], + "close_status": "Baseless", + "closed_at": "1750832631", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "This is the first comment under the fourth test issue", + "date_created": "1700554045", + "edited_on": null, + "editor": null, + "id": 885231, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under the fourth test issue", + "date_created": "1700554054", + "edited_on": null, + "editor": null, + "id": 885232, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue status updated to: Closed (was: Open)", + "date_created": "1700554081", + "edited_on": null, + "editor": null, + "id": 885233, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone DDDD", + "date_created": "1746679846", + "edited_on": null, + "editor": null, + "id": 971680, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: None (was: Milestone DDDD)\n- Issue status updated to: Open (was: Closed)", + "date_created": "1750832626", + "edited_on": null, + "editor": null, + "id": 976715, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue close_status updated to: Baseless\n- Issue status updated to: Closed (was: Open)", + "date_created": "1750832632", + "edited_on": null, + "editor": null, + "id": 976716, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone DDDD", + "date_created": "1750832695", + "edited_on": null, + "editor": null, + "id": 976718, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Common", + "date_created": "1750832786", + "edited_on": null, + "editor": null, + "id": 976722, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "content": "This is the body of the fourth test issue", + "custom_fields": [], + "date_created": "1700554016", + "depends": [], + "full_url": "https://pagure.io/protop2g-test-srce/issue/4", + "id": 4, + "last_updated": "1750832786", + "milestone": "Milestone DDDD", + "priority": 1, + "private": true, + "related_prs": [], + "status": "Closed", + "tags": [ + "gggg", + "hhhh" + ], + "title": "This is the title of the fourth test issue", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "assignee": null, + "blocks": [], + "close_status": null, + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "This is the first comment under the third test issue", + "date_created": "1700553880", + "edited_on": null, + "editor": null, + "id": 885229, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under the third test issue", + "date_created": "1700553892", + "edited_on": null, + "editor": null, + "id": 885230, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone CCCC", + "date_created": "1746679833", + "edited_on": null, + "editor": null, + "id": 971679, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Uncommon", + "date_created": "1750832767", + "edited_on": null, + "editor": null, + "id": 976721, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "content": "This is the body of the third test issue", + "custom_fields": [], + "date_created": "1700553837", + "depends": [], + "full_url": "https://pagure.io/protop2g-test-srce/issue/3", + "id": 3, + "last_updated": "1750832767", + "milestone": "Milestone CCCC", + "priority": 2, + "private": true, + "related_prs": [], + "status": "Open", + "tags": [ + "eeee", + "ffff" + ], + "title": "This is the title of the third test issue", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + ], + "pagination": { + "first": "https://pagure.io/api/0/protop2g-test-srce/issues?per_page=20&status=all&page=1", + "last": "https://pagure.io/api/0/protop2g-test-srce/issues?per_page=20&status=all&page=1", + "next": null, + "page": 1, + "pages": 1, + "per_page": 20, + "prev": null + }, + "total_issues": 2 +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10 new file mode 100644 index 0000000000..e43f97a590 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10 @@ -0,0 +1,259 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:21:04 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 6179 +content-security-policy: default-src 'self';script-src 'self' 'nonce-BK18r6rq2MUNhc0CXTZKJQyJY'; style-src 'self' 'nonce-BK18r6rq2MUNhc0CXTZKJQyJY'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTQ3NGFiNjdkNjdlZjNiNWNmOWYwZGFmODg1MDMxMTYzNTc5OTkxMyJ9.G2IQ0A.97bsQ3k_CAN3UOID130UESba38M; Expires=Sat, 23-Aug-2025 06:21:04 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "branch": "main", + "branch_from": "test-ffff", + "cached_merge_status": "unknown", + "closed_at": "1747635431", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: ffff\n- Request assigned", + "commit": null, + "date_created": "1747635211", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219623, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "rebased onto 01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + "commit": null, + "date_created": "1747635389", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219625, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "Pull-Request has been merged by t0xic0der", + "commit": null, + "date_created": "1747635431", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219626, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "1a6ccc212aa958a0fe76155c2907c889969a7224", + "commit_stop": "1a6ccc212aa958a0fe76155c2907c889969a7224", + "date_created": "1747635165", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/10", + "id": 10, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747635431", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Merged", + "tags": [ + "ffff" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-ffff` in the README.md file", + "uid": "d3b7100abf8b4b02aa220d899e063295", + "updated_on": "1747635431", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5 new file mode 100644 index 0000000000..be1c0e3a25 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5 @@ -0,0 +1,249 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:19:26 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5859 +content-security-policy: default-src 'self';script-src 'self' 'nonce-x3rXi7f95hPSGE2mV9PPJv0Db'; style-src 'self' 'nonce-x3rXi7f95hPSGE2mV9PPJv0Db'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiODFiMTQzZWQwZGEzMWIxNmE3NTk1ODYyNWE0MGZjNjcwNThmMDc3ZSJ9.G2IQbg.9qdYqcrGIqnCKtjsWYjcfMCTzok; Expires=Sat, 23-Aug-2025 06:19:26 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": null, + "branch": "main", + "branch_from": "test-aaaa", + "cached_merge_status": "CONFLICTS", + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa", + "commit": null, + "date_created": "1746427453", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219086, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under this pull request.", + "commit": null, + "date_created": "1746595521", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219190, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under this pull request.", + "commit": null, + "date_created": "1746595529", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219191, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "commit_stop": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "date_created": "1746427437", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/5", + "id": 5, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747636185", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Open", + "tags": [ + "aaaa" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-aaaa` in the README.md file", + "uid": "f9e737c5ccc1434e9798cfd49d192538", + "updated_on": "1746595529", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6 new file mode 100644 index 0000000000..878989a6cd --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6 @@ -0,0 +1,249 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:19:47 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5859 +content-security-policy: default-src 'self';script-src 'self' 'nonce-3mH3rM1hwmaCorIQiTfUsFwdZ'; style-src 'self' 'nonce-3mH3rM1hwmaCorIQiTfUsFwdZ'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiNWY5OWMwYjViOTkzZjM0YTY3OTJiYTZmNDk4YzY3MzUwMzY3MzY2OCJ9.G2IQgw.dxyk0aB89uDJGN15BTWrZEvCFWg; Expires=Sat, 23-Aug-2025 06:19:47 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": null, + "branch": "main", + "branch_from": "test-bbbb", + "cached_merge_status": "CONFLICTS", + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: bbbb", + "commit": null, + "date_created": "1746427480", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219087, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under this pull request.", + "commit": null, + "date_created": "1746595539", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219192, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under this pull request.", + "commit": null, + "date_created": "1746595552", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219193, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "commit_stop": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "date_created": "1746427470", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/6", + "id": 6, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747643450", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Open", + "tags": [ + "bbbb" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-bbbb` in the README.md file", + "uid": "9cab89d6bb8c499e8fcb47926f1f5806", + "updated_on": "1746595552", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7 new file mode 100644 index 0000000000..51c269273c --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7 @@ -0,0 +1,234 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:20:00 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5458 +content-security-policy: default-src 'self';script-src 'self' 'nonce-Bq1C3s4EngIGuBgtUcYdmK2M8'; style-src 'self' 'nonce-Bq1C3s4EngIGuBgtUcYdmK2M8'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZjUwNDcwNWQ4Y2UxZTRjODRlNjQxYTA0NzVlNDA5NGQwNzI4YTY1NyJ9.G2IQkA.KtxZfzau2wOwMTyfG_xNBHTMHSI; Expires=Sat, 23-Aug-2025 06:20:00 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": null, + "branch": "main", + "branch_from": "test-cccc", + "cached_merge_status": "FFORWARD", + "closed_at": "1746428043", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: cccc", + "commit": null, + "date_created": "1746427513", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219088, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "Pull-Request has been closed by t0xic0der", + "commit": null, + "date_created": "1746428043", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219090, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "f1246e331cade9341b9e4f311b7a134f99893d21", + "commit_stop": "f1246e331cade9341b9e4f311b7a134f99893d21", + "date_created": "1746427506", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/7", + "id": 7, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1746428043", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Closed", + "tags": [ + "cccc" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-cccc` in the README.md file", + "uid": "f696feab56b84557b4d4a8a4462420ee", + "updated_on": "1746428043", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8 new file mode 100644 index 0000000000..45ae72d6e8 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8 @@ -0,0 +1,234 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:20:17 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5458 +content-security-policy: default-src 'self';script-src 'self' 'nonce-OxOLryXyFpTZ0hyi7t17U5IG0'; style-src 'self' 'nonce-OxOLryXyFpTZ0hyi7t17U5IG0'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiM2I3NWQwYzBhY2YxMjhmYTU0YmU5NzEzYzcyNjNjZTQ4NjFlNDE5ZCJ9.G2IQoQ.Ds29JFMXbO3q2kBPxmfx2kQ7YjA; Expires=Sat, 23-Aug-2025 06:20:17 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": null, + "branch": "main", + "branch_from": "test-dddd", + "cached_merge_status": "FFORWARD", + "closed_at": "1746428053", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: dddd", + "commit": null, + "date_created": "1746427540", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219089, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "Pull-Request has been closed by t0xic0der", + "commit": null, + "date_created": "1746428053", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219091, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160", + "commit_stop": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160", + "date_created": "1746427532", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/8", + "id": 8, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1746428053", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Closed", + "tags": [ + "dddd" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-dddd` in the README.md file", + "uid": "493b294044fd48e18f424210c919d8de", + "updated_on": "1746428053", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9 b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9 new file mode 100644 index 0000000000..2e19157948 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9 @@ -0,0 +1,239 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:20:51 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5628 +content-security-policy: default-src 'self';script-src 'self' 'nonce-RVreTe5AdtWf0rvyk7mxX40mb'; style-src 'self' 'nonce-RVreTe5AdtWf0rvyk7mxX40mb'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiM2MyYzdlY2E0MDYzNWRmZjRhMDc2MGE0OTg2MjNhMTU4MWNhZDg4OSJ9.G2IQww.VZpiXbZftgigHkiS15g8DF6iQSY; Expires=Sat, 23-Aug-2025 06:20:51 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "branch": "main", + "branch_from": "test-eeee", + "cached_merge_status": "unknown", + "closed_at": "1747635243", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: eeee\n- Request assigned", + "commit": null, + "date_created": "1747635200", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219622, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "Pull-Request has been merged by t0xic0der", + "commit": null, + "date_created": "1747635243", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219624, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + "commit_stop": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + "date_created": "1747635161", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/9", + "id": 9, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747635243", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Merged", + "tags": [ + "eeee" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-eeee` in the README.md file", + "uid": "f2ad806e430a40bd8ee5894484338df4", + "updated_on": "1747635243", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests new file mode 100644 index 0000000000..15b69e8f03 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests @@ -0,0 +1,507 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:18:47 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 14090 +content-security-policy: default-src 'self';script-src 'self' 'nonce-t1H8BCX7kXOBmXS0wHPpBrAhK'; style-src 'self' 'nonce-t1H8BCX7kXOBmXS0wHPpBrAhK'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiODAwYWYyZGI3MjZlMzA2ZTdmNTdlMmIwNGVkNmU3YTBmNDYwZDUyMyJ9.G2IQRw.EDXBH36zsKcHKDETH_g7miO_r_w; Expires=Sat, 23-Aug-2025 06:18:47 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "args": { + "assignee": null, + "author": null, + "page": 1, + "per_page": 20, + "status": true, + "tags": [] + }, + "pagination": { + "first": "https://pagure.io/api/0/protop2g-test-srce/pull-requests?per_page=20&page=1", + "last": "https://pagure.io/api/0/protop2g-test-srce/pull-requests?per_page=20&page=1", + "next": null, + "page": 1, + "pages": 1, + "per_page": 20, + "prev": null + }, + "requests": [ + { + "assignee": null, + "branch": "main", + "branch_from": "test-bbbb", + "cached_merge_status": "CONFLICTS", + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: bbbb", + "commit": null, + "date_created": "1746427480", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219087, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under this pull request.", + "commit": null, + "date_created": "1746595539", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219192, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under this pull request.", + "commit": null, + "date_created": "1746595552", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219193, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "commit_stop": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "date_created": "1746427470", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/6", + "id": 6, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747643450", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Open", + "tags": [ + "bbbb" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-bbbb` in the README.md file", + "uid": "9cab89d6bb8c499e8fcb47926f1f5806", + "updated_on": "1746595552", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "assignee": null, + "branch": "main", + "branch_from": "test-aaaa", + "cached_merge_status": "CONFLICTS", + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa", + "commit": null, + "date_created": "1746427453", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219086, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under this pull request.", + "commit": null, + "date_created": "1746595521", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219190, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under this pull request.", + "commit": null, + "date_created": "1746595529", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219191, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "commit_stop": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "date_created": "1746427437", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/5", + "id": 5, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747636185", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Open", + "tags": [ + "aaaa" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-aaaa` in the README.md file", + "uid": "f9e737c5ccc1434e9798cfd49d192538", + "updated_on": "1746595529", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "total_requests": 2 +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa new file mode 100644 index 0000000000..d0f24e646e --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:34:39 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-gI8Srx27RgY20Zqw5vVkWVORV'; style-src 'self' 'nonce-gI8Srx27RgY20Zqw5vVkWVORV'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZmJhYTlmMTAzYzYxMWE5NmZiZDNkZjE1ZmRiZGQzYTA5YTQ1YTM2YyJ9.G2IF7w.CLP1edDfZSoJcXjoUNt40swcBV8; Expires=Sat, 23-Aug-2025 05:34:39 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "aaaa", + "tag_color": "#ff0000", + "tag_description": "aaaa" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb new file mode 100644 index 0000000000..a859096f25 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:34:50 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-wgQpgRvwBbyoY9WWTv55wb8Dd'; style-src 'self' 'nonce-wgQpgRvwBbyoY9WWTv55wb8Dd'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZWU4ZDUxYzY4NmUzMzM4ODg4YmRmMjQwYmU3ODVhOTA4MzQ4N2Q2NiJ9.G2IF-g.nG1k1zU4b9Eo9WFGCas8R9a5-Vg; Expires=Sat, 23-Aug-2025 05:34:50 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "bbbb", + "tag_color": "#ff0000", + "tag_description": "bbbb" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc new file mode 100644 index 0000000000..c4fdc9aa46 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:35:02 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-kAxTKES1vWlzLCIScejGQ5JpX'; style-src 'self' 'nonce-kAxTKES1vWlzLCIScejGQ5JpX'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYjhhM2YxNjJhZDYwYWU5NzE4NmQ4NDBkMzJlOWNkYmYxN2Y2NjBmMyJ9.G2IGBg.oA7d9DJHW4_ilpbiVkbveF5dM3Q; Expires=Sat, 23-Aug-2025 05:35:02 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "cccc", + "tag_color": "#ffff00", + "tag_description": "cccc" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd new file mode 100644 index 0000000000..db9ad09a74 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:36:23 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-rzXQsxeaBjeye4rVcEn3aSWKa'; style-src 'self' 'nonce-rzXQsxeaBjeye4rVcEn3aSWKa'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiY2VhZjRhOTlkZWUxZDcxODg2NWIyN2JhZGY4ZjUyMjcxZTdkZGU0MyJ9.G2IGVw.JCK3tXfD0aOgDdIAMv5MlFkl-SY; Expires=Sat, 23-Aug-2025 05:36:23 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "dddd", + "tag_color": "#ffff00", + "tag_description": "dddd" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee new file mode 100644 index 0000000000..eac0faf464 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:36:34 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-gs07ooad3wPkkzZKAQDHDTrMl'; style-src 'self' 'nonce-gs07ooad3wPkkzZKAQDHDTrMl'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGEyNDUwYzk5MDY2ZjJhZGQyNDhlZmZmM2QxN2UxZTM0ODI2NWFhZiJ9.G2IGYg.hM6ZKEPDXtOvTWlSPeQBLiZjCO4; Expires=Sat, 23-Aug-2025 05:36:34 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "eeee", + "tag_color": "#00ff00", + "tag_description": "eeee" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff new file mode 100644 index 0000000000..af5935aa46 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:36:48 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-hiIOP1ZdLfgxJERlzriEOATjs'; style-src 'self' 'nonce-hiIOP1ZdLfgxJERlzriEOATjs'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYWMyNjBhNjE5MjI0OTQ0YTU2Yzc5YjNmNzU3ZTU2MTYzZGQwMGMwNSJ9.G2IGcQ.22KbaZBjPxJkpIoTBIn1UtVzEjI; Expires=Sat, 23-Aug-2025 05:36:49 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "ffff", + "tag_color": "#00ff00", + "tag_description": "ffff" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg new file mode 100644 index 0000000000..28fd0ef15d --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:37:23 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-FYnaELqaJomAM4RnLtKUr7gbE'; style-src 'self' 'nonce-FYnaELqaJomAM4RnLtKUr7gbE'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGExMmI2MGZhYTQyYTY5MmQ0MzNkNWFlZTU0YjE4M2NjY2NmYjI3MCJ9.G2IGkw.9-wL70lPAOlpIv8cusGLLA0Np_U; Expires=Sat, 23-Aug-2025 05:37:23 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "gggg", + "tag_color": "#0000ff", + "tag_description": "gggg" +} diff --git a/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags new file mode 100644 index 0000000000..1176b532a4 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/authorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags @@ -0,0 +1,26 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:16:48 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 142 +content-security-policy: default-src 'self';script-src 'self' 'nonce-T8LPhGGs1acv00t58vtBtVmwC'; style-src 'self' 'nonce-T8LPhGGs1acv00t58vtBtVmwC'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmQ2NTQ1MmFhZWJjZjA0OWIxNTI5MjBjODQyYzUzOGRkYmYyYTkwOSJ9.G2IBwA.pKqOBVDJrtKfyrbJPyuaRfFjnR4; Expires=Sat, 23-Aug-2025 05:16:48 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tags": [ + "aaaa", + "bbbb", + "cccc", + "dddd", + "eeee", + "ffff", + "gggg", + "hhhh" + ], + "total_tags": 8 +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce new file mode 100644 index 0000000000..5f569b145c --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce @@ -0,0 +1,85 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:51:21 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 1464 +content-security-policy: default-src 'self';script-src 'self' 'nonce-Tr8ktNGt3XoBWQBIXrrPTeA1v'; style-src 'self' 'nonce-Tr8ktNGt3XoBWQBIXrrPTeA1v'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmI4NGRmYWMyY2Q2MzVlOWRkOTQwMGYwNzk2NzdlOGQxN2UxYTJlMCJ9.G2IX6Q.ETAG_sYgE1V9xYQNy_CD4HOP32Q; Expires=Sat, 23-Aug-2025 06:51:21 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo new file mode 100644 index 0000000000..e31c4193e0 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F01b420e2964928a15f790f9b7c1a0053e7b5f0a5%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:44:23 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 452 +content-security-policy: default-src 'self';script-src 'self' 'nonce-jtS0HiLSSKCUK3FUuLv7XD9KG'; style-src 'self' 'nonce-jtS0HiLSSKCUK3FUuLv7XD9KG'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiY2EzN2Y5MTI1ZjkxNTkzMDViMWQ5ZGVhYjkyNzVlZTkwNmVjYzgzYiJ9.G2IWRw.1ZmhWySKFk3Lw_bA-EwmcNCgcz0; Expires=Sat, 23-Aug-2025 06:44:23 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1747635011, + "commit_time_offset": 0, + "committer": "Akashdeep Dhar", + "hash": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + "message": "Change the branch identity to `test-eeee` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "1e8fa9a17b4b4ddde50f334626b0a5497070cff7" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo new file mode 100644 index 0000000000..722b59a6dd --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:44:57 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 454 +content-security-policy: default-src 'self';script-src 'self' 'nonce-6gKus9S40eu36HFYePUnyxB36'; style-src 'self' 'nonce-6gKus9S40eu36HFYePUnyxB36'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZjM5ZTYyYWM5NDViYTYwMjg1MjFlMTAzNjU4OWQ3Zjc1NjA5ZmUzMSJ9.G2IWaQ._b9edyW_4DSX-umHlyVib496s00; Expires=Sat, 23-Aug-2025 06:44:57 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1697169509, + "commit_time_offset": 330, + "committer": "Akashdeep Dhar", + "hash": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160", + "message": "Change the branch identity to `test-dddd` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "586a1e8a79e572691dc086ef7bf4e2f6d34c5254" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo new file mode 100644 index 0000000000..8d3fb6f5f2 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F1a6ccc212aa958a0fe76155c2907c889969a7224%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:43:47 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 452 +content-security-policy: default-src 'self';script-src 'self' 'nonce-bDXeQlGZOYsS26Mr1BJPgodzY'; style-src 'self' 'nonce-bDXeQlGZOYsS26Mr1BJPgodzY'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiNDRkYmIwMjQwODI1ODc4MGUzYWM4ZDZhZTkxYWQ2NTkyMDFhNTg0ZSJ9.G2IWIw.U2Rb6xUm4Wk9ODweB3hH1cggWkM; Expires=Sat, 23-Aug-2025 06:43:47 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1747635352, + "commit_time_offset": 0, + "committer": "Akashdeep Dhar", + "hash": "1a6ccc212aa958a0fe76155c2907c889969a7224", + "message": "Change the branch identity to `test-ffff` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "01b420e2964928a15f790f9b7c1a0053e7b5f0a5" + ], + "tree_id": "0c5e64a6b912cb0c3d66e66896fa98a98da69fe4" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo new file mode 100644 index 0000000000..852a4b724d --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2F2d40761dc53e6fa060ac49d88e1452c6751d4b1c%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:46:23 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 454 +content-security-policy: default-src 'self';script-src 'self' 'nonce-JLGc3LbKkMMzOnQTvUIIZbAwF'; style-src 'self' 'nonce-JLGc3LbKkMMzOnQTvUIIZbAwF'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYTE0Y2EzMTZiMWRkZGZjOTJjMjkwZTkyNjM5OWEzYWZiNjA0OTZiMCJ9.G2IWvw.SNBn8NelY5GBQ-8SmLY-Uwy-uq0; Expires=Sat, 23-Aug-2025 06:46:23 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1697169040, + "commit_time_offset": 330, + "committer": "Akashdeep Dhar", + "hash": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "message": "Change the branch identity to `test-bbbb` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "7a23fc15f5a1463f2c425f0146def7c19ecf6c88" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo new file mode 100644 index 0000000000..a35fb1f416 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Fb55e5c91d2572d60a8d7e71b3d3003e523127bd4%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:46:59 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 454 +content-security-policy: default-src 'self';script-src 'self' 'nonce-ORfdixyFztBA0Sx54SlhQuMio'; style-src 'self' 'nonce-ORfdixyFztBA0Sx54SlhQuMio'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmY2MjEyYzNmMDkzMmZkZDMxYjYwYzVmMDdjYzEzY2JhZTJkMjVmZCJ9.G2IW4w.JCI6ih36NlW5IPwNX1LaCbb6v5U; Expires=Sat, 23-Aug-2025 06:46:59 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1697169185, + "commit_time_offset": 330, + "committer": "Akashdeep Dhar", + "hash": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "message": "Change the branch identity to `test-aaaa` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "e7911825d29e73f260de95d5070898ccbe6340a6" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo new file mode 100644 index 0000000000..8e161ce97c --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fc%2Ff1246e331cade9341b9e4f311b7a134f99893d21%2Finfo @@ -0,0 +1,25 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:45:31 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 454 +content-security-policy: default-src 'self';script-src 'self' 'nonce-chsmQCN0V1J9MIqK5NU6MqBJv'; style-src 'self' 'nonce-chsmQCN0V1J9MIqK5NU6MqBJv'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZDNmYWUzNTBlOWZkNDY0MTJlOWNkMjQ0M2FjZmYyY2VkZjBlY2ZiYSJ9.G2IWjA.co1poP1T_e07frtYRBAkxcp2RTM; Expires=Sat, 23-Aug-2025 06:45:32 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "author": "Akashdeep Dhar", + "commit_time": 1697169354, + "commit_time_offset": 330, + "committer": "Akashdeep Dhar", + "hash": "f1246e331cade9341b9e4f311b7a134f99893d21", + "message": "Change the branch identity to `test-cccc` in the README.md file\n\nSigned-off-by: Akashdeep Dhar \n", + "parent_ids": [ + "3f12d300f62f1c5b8a1d3265bd85d61cf6d924d7" + ], + "tree_id": "f1e37736d409bb136451dfc8e3a2017d3af2ce7d" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F1 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F1 new file mode 100644 index 0000000000..ab96eec4a3 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F1 @@ -0,0 +1,125 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:17:07 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 3105 +content-security-policy: default-src 'self';script-src 'self' 'nonce-yBIiXAYKuVAlrSnvYuxYU2MVh'; style-src 'self' 'nonce-yBIiXAYKuVAlrSnvYuxYU2MVh'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZDQ4YWUxNzRmZGRjNDE3MGE3Zjg4YTY2OWJmMzI4MTA3OGQ0NDJmNyJ9.G2IP4w.fBfDUgO0_qt8TTh1QGvCxyEoNw8; Expires=Sat, 23-Aug-2025 06:17:07 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": null, + "blocks": [], + "close_status": null, + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue tagged with: aaaa, bbbb", + "date_created": "1697169699", + "edited_on": null, + "editor": null, + "id": 878471, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under the first test issue", + "date_created": "1697169880", + "edited_on": null, + "editor": null, + "id": 878473, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under the first test issue", + "date_created": "1697169924", + "edited_on": null, + "editor": null, + "id": 878474, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone AAAA", + "date_created": "1746679806", + "edited_on": null, + "editor": null, + "id": 971677, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Epic", + "date_created": "1750832745", + "edited_on": null, + "editor": null, + "id": 976719, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "content": "This is the body of the first test issue", + "custom_fields": [], + "date_created": "1697169462", + "depends": [], + "full_url": "https://pagure.io/protop2g-test-srce/issue/1", + "id": 1, + "last_updated": "1750832745", + "milestone": "Milestone AAAA", + "priority": 4, + "private": false, + "related_prs": [], + "status": "Open", + "tags": [ + "aaaa", + "bbbb" + ], + "title": "This is the title of the first test issue", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F2 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F2 new file mode 100644 index 0000000000..7488423491 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissue%2F2 @@ -0,0 +1,17 @@ +HTTP/2 404 +date: Wed, 23 Jul 2025 06:17:34 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 233 +content-security-policy: default-src 'self';script-src 'self' 'nonce-sIVvSKRvcU00FQYaXHjgZGoqv'; style-src 'self' 'nonce-sIVvSKRvcU00FQYaXHjgZGoqv'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTE1NmEzNWM2ZWU4ZGI1M2U5MTFlOGIzN2UzYzRkNzcxNDZlMjk1MCJ9.G2IP_g.YZx_ll-7EyLvtel9UcO0uQAoujM; Expires=Sat, 23-Aug-2025 06:17:34 GMT; Secure; HttpOnly; Path=/ +content-type: text/html; charset=UTF-8 + + +404 Not Found +

    Not Found

    +

    The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

    diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues new file mode 100644 index 0000000000..4f14350af2 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fissues @@ -0,0 +1,331 @@ +HTTP/2 200 +date: Wed, 30 Jul 2025 05:17:03 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 10166 +content-security-policy: default-src 'self';script-src 'self' 'nonce-rxZTVpKgLDOgYu1TX1ab1qR2p'; style-src 'self' 'nonce-rxZTVpKgLDOgYu1TX1ab1qR2p'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGRkN2YyNjZiMGQ5YmM4Mjc3YjllYjkzMjUyNjZlMmQ5OTExMWI2NCJ9.G2s8UA.oQa5peejoMGwTzJF3vWHgcS8naQ; Expires=Sat, 30-Aug-2025 05:17:04 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "args": { + "assignee": null, + "author": null, + "milestones": [], + "no_stones": null, + "order": null, + "priority": null, + "since": null, + "status": "all", + "tags": [] + }, + "issues": [ + { + "assignee": null, + "blocks": [], + "close_status": "Complete", + "closed_at": "1750832579", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue tagged with: cccc, dddd", + "date_created": "1697169810", + "edited_on": null, + "editor": null, + "id": 878472, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "The is the first comment under the second test issue", + "date_created": "1697169964", + "edited_on": null, + "editor": null, + "id": 878475, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "The is the second comment under the second test issue", + "date_created": "1697169976", + "edited_on": null, + "editor": null, + "id": 878476, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue status updated to: Closed (was: Open)", + "date_created": "1697170032", + "edited_on": null, + "editor": null, + "id": 878477, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone BBBB", + "date_created": "1746679821", + "edited_on": null, + "editor": null, + "id": 971678, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: None (was: Milestone BBBB)\n- Issue status updated to: Open (was: Closed)", + "date_created": "1750832572", + "edited_on": null, + "editor": null, + "id": 976713, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue close_status updated to: Complete\n- Issue status updated to: Closed (was: Open)", + "date_created": "1750832582", + "edited_on": null, + "editor": null, + "id": 976714, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone BBBB", + "date_created": "1750832674", + "edited_on": null, + "editor": null, + "id": 976717, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Rare", + "date_created": "1750832757", + "edited_on": null, + "editor": null, + "id": 976720, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "content": "This is the body of the second test issue", + "custom_fields": [], + "date_created": "1697169676", + "depends": [], + "full_url": "https://pagure.io/protop2g-test-srce/issue/2", + "id": 2, + "last_updated": "1750832757", + "milestone": "Milestone BBBB", + "priority": 3, + "private": false, + "related_prs": [], + "status": "Closed", + "tags": [ + "cccc", + "dddd" + ], + "title": "This is the title of the second test issue", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "assignee": null, + "blocks": [], + "close_status": null, + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue tagged with: aaaa, bbbb", + "date_created": "1697169699", + "edited_on": null, + "editor": null, + "id": 878471, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under the first test issue", + "date_created": "1697169880", + "edited_on": null, + "editor": null, + "id": 878473, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under the first test issue", + "date_created": "1697169924", + "edited_on": null, + "editor": null, + "id": 878474, + "notification": false, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue set to the milestone: Milestone AAAA", + "date_created": "1746679806", + "edited_on": null, + "editor": null, + "id": 971677, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "**Metadata Update from @t0xic0der**:\n- Issue priority set to: Epic", + "date_created": "1750832745", + "edited_on": null, + "editor": null, + "id": 976719, + "notification": true, + "parent": null, + "reactions": {}, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "content": "This is the body of the first test issue", + "custom_fields": [], + "date_created": "1697169462", + "depends": [], + "full_url": "https://pagure.io/protop2g-test-srce/issue/1", + "id": 1, + "last_updated": "1750832745", + "milestone": "Milestone AAAA", + "priority": 4, + "private": false, + "related_prs": [], + "status": "Open", + "tags": [ + "aaaa", + "bbbb" + ], + "title": "This is the title of the first test issue", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "pagination": { + "first": "https://pagure.io/api/0/protop2g-test-srce/issues?per_page=20&status=all&page=1", + "last": "https://pagure.io/api/0/protop2g-test-srce/issues?per_page=20&status=all&page=1", + "next": null, + "page": 1, + "pages": 1, + "per_page": 20, + "prev": null + }, + "total_issues": 2 +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10 new file mode 100644 index 0000000000..e43f97a590 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F10 @@ -0,0 +1,259 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:21:04 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 6179 +content-security-policy: default-src 'self';script-src 'self' 'nonce-BK18r6rq2MUNhc0CXTZKJQyJY'; style-src 'self' 'nonce-BK18r6rq2MUNhc0CXTZKJQyJY'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOTQ3NGFiNjdkNjdlZjNiNWNmOWYwZGFmODg1MDMxMTYzNTc5OTkxMyJ9.G2IQ0A.97bsQ3k_CAN3UOID130UESba38M; Expires=Sat, 23-Aug-2025 06:21:04 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "branch": "main", + "branch_from": "test-ffff", + "cached_merge_status": "unknown", + "closed_at": "1747635431", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: ffff\n- Request assigned", + "commit": null, + "date_created": "1747635211", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219623, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "rebased onto 01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + "commit": null, + "date_created": "1747635389", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219625, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "Pull-Request has been merged by t0xic0der", + "commit": null, + "date_created": "1747635431", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219626, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "1a6ccc212aa958a0fe76155c2907c889969a7224", + "commit_stop": "1a6ccc212aa958a0fe76155c2907c889969a7224", + "date_created": "1747635165", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/10", + "id": 10, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747635431", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Merged", + "tags": [ + "ffff" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-ffff` in the README.md file", + "uid": "d3b7100abf8b4b02aa220d899e063295", + "updated_on": "1747635431", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5 new file mode 100644 index 0000000000..be1c0e3a25 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F5 @@ -0,0 +1,249 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:19:26 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5859 +content-security-policy: default-src 'self';script-src 'self' 'nonce-x3rXi7f95hPSGE2mV9PPJv0Db'; style-src 'self' 'nonce-x3rXi7f95hPSGE2mV9PPJv0Db'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiODFiMTQzZWQwZGEzMWIxNmE3NTk1ODYyNWE0MGZjNjcwNThmMDc3ZSJ9.G2IQbg.9qdYqcrGIqnCKtjsWYjcfMCTzok; Expires=Sat, 23-Aug-2025 06:19:26 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": null, + "branch": "main", + "branch_from": "test-aaaa", + "cached_merge_status": "CONFLICTS", + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa", + "commit": null, + "date_created": "1746427453", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219086, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under this pull request.", + "commit": null, + "date_created": "1746595521", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219190, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under this pull request.", + "commit": null, + "date_created": "1746595529", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219191, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "commit_stop": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "date_created": "1746427437", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/5", + "id": 5, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747636185", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Open", + "tags": [ + "aaaa" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-aaaa` in the README.md file", + "uid": "f9e737c5ccc1434e9798cfd49d192538", + "updated_on": "1746595529", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6 new file mode 100644 index 0000000000..878989a6cd --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F6 @@ -0,0 +1,249 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:19:47 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5859 +content-security-policy: default-src 'self';script-src 'self' 'nonce-3mH3rM1hwmaCorIQiTfUsFwdZ'; style-src 'self' 'nonce-3mH3rM1hwmaCorIQiTfUsFwdZ'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiNWY5OWMwYjViOTkzZjM0YTY3OTJiYTZmNDk4YzY3MzUwMzY3MzY2OCJ9.G2IQgw.dxyk0aB89uDJGN15BTWrZEvCFWg; Expires=Sat, 23-Aug-2025 06:19:47 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": null, + "branch": "main", + "branch_from": "test-bbbb", + "cached_merge_status": "CONFLICTS", + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: bbbb", + "commit": null, + "date_created": "1746427480", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219087, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under this pull request.", + "commit": null, + "date_created": "1746595539", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219192, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under this pull request.", + "commit": null, + "date_created": "1746595552", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219193, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "commit_stop": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "date_created": "1746427470", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/6", + "id": 6, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747643450", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Open", + "tags": [ + "bbbb" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-bbbb` in the README.md file", + "uid": "9cab89d6bb8c499e8fcb47926f1f5806", + "updated_on": "1746595552", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7 new file mode 100644 index 0000000000..51c269273c --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F7 @@ -0,0 +1,234 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:20:00 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5458 +content-security-policy: default-src 'self';script-src 'self' 'nonce-Bq1C3s4EngIGuBgtUcYdmK2M8'; style-src 'self' 'nonce-Bq1C3s4EngIGuBgtUcYdmK2M8'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZjUwNDcwNWQ4Y2UxZTRjODRlNjQxYTA0NzVlNDA5NGQwNzI4YTY1NyJ9.G2IQkA.KtxZfzau2wOwMTyfG_xNBHTMHSI; Expires=Sat, 23-Aug-2025 06:20:00 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": null, + "branch": "main", + "branch_from": "test-cccc", + "cached_merge_status": "FFORWARD", + "closed_at": "1746428043", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: cccc", + "commit": null, + "date_created": "1746427513", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219088, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "Pull-Request has been closed by t0xic0der", + "commit": null, + "date_created": "1746428043", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219090, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "f1246e331cade9341b9e4f311b7a134f99893d21", + "commit_stop": "f1246e331cade9341b9e4f311b7a134f99893d21", + "date_created": "1746427506", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/7", + "id": 7, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1746428043", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Closed", + "tags": [ + "cccc" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-cccc` in the README.md file", + "uid": "f696feab56b84557b4d4a8a4462420ee", + "updated_on": "1746428043", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8 new file mode 100644 index 0000000000..45ae72d6e8 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F8 @@ -0,0 +1,234 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:20:17 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5458 +content-security-policy: default-src 'self';script-src 'self' 'nonce-OxOLryXyFpTZ0hyi7t17U5IG0'; style-src 'self' 'nonce-OxOLryXyFpTZ0hyi7t17U5IG0'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiM2I3NWQwYzBhY2YxMjhmYTU0YmU5NzEzYzcyNjNjZTQ4NjFlNDE5ZCJ9.G2IQoQ.Ds29JFMXbO3q2kBPxmfx2kQ7YjA; Expires=Sat, 23-Aug-2025 06:20:17 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": null, + "branch": "main", + "branch_from": "test-dddd", + "cached_merge_status": "FFORWARD", + "closed_at": "1746428053", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: dddd", + "commit": null, + "date_created": "1746427540", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219089, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "Pull-Request has been closed by t0xic0der", + "commit": null, + "date_created": "1746428053", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219091, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160", + "commit_stop": "0bc8b0c38e0790e9ef5c8d512a00b9c4dd048160", + "date_created": "1746427532", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/8", + "id": 8, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1746428053", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Closed", + "tags": [ + "dddd" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-dddd` in the README.md file", + "uid": "493b294044fd48e18f424210c919d8de", + "updated_on": "1746428053", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9 b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9 new file mode 100644 index 0000000000..2e19157948 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-request%2F9 @@ -0,0 +1,239 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:20:51 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 5628 +content-security-policy: default-src 'self';script-src 'self' 'nonce-RVreTe5AdtWf0rvyk7mxX40mb'; style-src 'self' 'nonce-RVreTe5AdtWf0rvyk7mxX40mb'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiM2MyYzdlY2E0MDYzNWRmZjRhMDc2MGE0OTg2MjNhMTU4MWNhZDg4OSJ9.G2IQww.VZpiXbZftgigHkiS15g8DF6iQSY; Expires=Sat, 23-Aug-2025 06:20:51 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "assignee": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "branch": "main", + "branch_from": "test-eeee", + "cached_merge_status": "unknown", + "closed_at": "1747635243", + "closed_by": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + }, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: eeee\n- Request assigned", + "commit": null, + "date_created": "1747635200", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219622, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "Pull-Request has been merged by t0xic0der", + "commit": null, + "date_created": "1747635243", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219624, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + "commit_stop": "01b420e2964928a15f790f9b7c1a0053e7b5f0a5", + "date_created": "1747635161", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/9", + "id": 9, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747635243", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Merged", + "tags": [ + "eeee" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-eeee` in the README.md file", + "uid": "f2ad806e430a40bd8ee5894484338df4", + "updated_on": "1747635243", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests new file mode 100644 index 0000000000..15b69e8f03 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Fpull-requests @@ -0,0 +1,507 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 06:18:47 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 14090 +content-security-policy: default-src 'self';script-src 'self' 'nonce-t1H8BCX7kXOBmXS0wHPpBrAhK'; style-src 'self' 'nonce-t1H8BCX7kXOBmXS0wHPpBrAhK'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiODAwYWYyZGI3MjZlMzA2ZTdmNTdlMmIwNGVkNmU3YTBmNDYwZDUyMyJ9.G2IQRw.EDXBH36zsKcHKDETH_g7miO_r_w; Expires=Sat, 23-Aug-2025 06:18:47 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "args": { + "assignee": null, + "author": null, + "page": 1, + "per_page": 20, + "status": true, + "tags": [] + }, + "pagination": { + "first": "https://pagure.io/api/0/protop2g-test-srce/pull-requests?per_page=20&page=1", + "last": "https://pagure.io/api/0/protop2g-test-srce/pull-requests?per_page=20&page=1", + "next": null, + "page": 1, + "pages": 1, + "per_page": 20, + "prev": null + }, + "requests": [ + { + "assignee": null, + "branch": "main", + "branch_from": "test-bbbb", + "cached_merge_status": "CONFLICTS", + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: bbbb", + "commit": null, + "date_created": "1746427480", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219087, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under this pull request.", + "commit": null, + "date_created": "1746595539", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219192, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under this pull request.", + "commit": null, + "date_created": "1746595552", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219193, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "commit_stop": "2d40761dc53e6fa060ac49d88e1452c6751d4b1c", + "date_created": "1746427470", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/6", + "id": 6, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747643450", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Open", + "tags": [ + "bbbb" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-bbbb` in the README.md file", + "uid": "9cab89d6bb8c499e8fcb47926f1f5806", + "updated_on": "1746595552", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "assignee": null, + "branch": "main", + "branch_from": "test-aaaa", + "cached_merge_status": "CONFLICTS", + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "**Metadata Update from @t0xic0der**:\n- Pull-request tagged with: aaaa", + "commit": null, + "date_created": "1746427453", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219086, + "line": null, + "notification": true, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the first comment under this pull request.", + "commit": null, + "date_created": "1746595521", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219190, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + { + "comment": "This is the second comment under this pull request.", + "commit": null, + "date_created": "1746595529", + "edited_on": null, + "editor": null, + "filename": null, + "id": 219191, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "commit_start": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "commit_stop": "b55e5c91d2572d60a8d7e71b3d3003e523127bd4", + "date_created": "1746427437", + "full_url": "https://pagure.io/protop2g-test-srce/pull-request/5", + "id": 5, + "initial_comment": "Signed-off-by: Akashdeep Dhar ", + "last_updated": "1747636185", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": [ + "ryanlerch" + ], + "collaborator": [], + "commit": [], + "owner": [ + "t0xic0der" + ], + "ticket": [] + }, + "close_status": [ + "Complete", + "Baseless" + ], + "custom_keys": [], + "date_created": "1697168063", + "date_modified": "1744795940", + "description": "The source namespace for the Pagure Exporter project to run tests against", + "full_url": "https://pagure.io/protop2g-test-srce", + "fullname": "protop2g-test-srce", + "id": 17042, + "milestones": { + "Milestone AAAA": { + "active": true, + "date": "1765497600" + }, + "Milestone BBBB": { + "active": false, + "date": "1765497600" + }, + "Milestone CCCC": { + "active": true, + "date": "1765497600" + }, + "Milestone DDDD": { + "active": false, + "date": "1765497600" + } + }, + "name": "protop2g-test-srce", + "namespace": null, + "parent": null, + "priorities": { + "": "", + "1": "Common", + "2": "Uncommon", + "3": "Rare", + "4": "Epic", + "5": "Mythic" + }, + "tags": [ + "srce", + "test", + "gridhead", + "protop2g" + ], + "url_path": "protop2g-test-srce", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + }, + "status": "Open", + "tags": [ + "aaaa" + ], + "threshold_reached": null, + "title": "Change the branch identity to `test-aaaa` in the README.md file", + "uid": "f9e737c5ccc1434e9798cfd49d192538", + "updated_on": "1746595529", + "user": { + "full_url": "https://pagure.io/user/t0xic0der", + "fullname": "Akashdeep Dhar", + "name": "t0xic0der", + "url_path": "user/t0xic0der" + } + } + ], + "total_requests": 2 +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa new file mode 100644 index 0000000000..d0f24e646e --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Faaaa @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:34:39 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-gI8Srx27RgY20Zqw5vVkWVORV'; style-src 'self' 'nonce-gI8Srx27RgY20Zqw5vVkWVORV'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZmJhYTlmMTAzYzYxMWE5NmZiZDNkZjE1ZmRiZGQzYTA5YTQ1YTM2YyJ9.G2IF7w.CLP1edDfZSoJcXjoUNt40swcBV8; Expires=Sat, 23-Aug-2025 05:34:39 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "aaaa", + "tag_color": "#ff0000", + "tag_description": "aaaa" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb new file mode 100644 index 0000000000..a859096f25 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fbbbb @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:34:50 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-wgQpgRvwBbyoY9WWTv55wb8Dd'; style-src 'self' 'nonce-wgQpgRvwBbyoY9WWTv55wb8Dd'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiZWU4ZDUxYzY4NmUzMzM4ODg4YmRmMjQwYmU3ODVhOTA4MzQ4N2Q2NiJ9.G2IF-g.nG1k1zU4b9Eo9WFGCas8R9a5-Vg; Expires=Sat, 23-Aug-2025 05:34:50 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "bbbb", + "tag_color": "#ff0000", + "tag_description": "bbbb" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc new file mode 100644 index 0000000000..c4fdc9aa46 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fcccc @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:35:02 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-kAxTKES1vWlzLCIScejGQ5JpX'; style-src 'self' 'nonce-kAxTKES1vWlzLCIScejGQ5JpX'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYjhhM2YxNjJhZDYwYWU5NzE4NmQ4NDBkMzJlOWNkYmYxN2Y2NjBmMyJ9.G2IGBg.oA7d9DJHW4_ilpbiVkbveF5dM3Q; Expires=Sat, 23-Aug-2025 05:35:02 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "cccc", + "tag_color": "#ffff00", + "tag_description": "cccc" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd new file mode 100644 index 0000000000..db9ad09a74 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fdddd @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:36:23 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-rzXQsxeaBjeye4rVcEn3aSWKa'; style-src 'self' 'nonce-rzXQsxeaBjeye4rVcEn3aSWKa'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiY2VhZjRhOTlkZWUxZDcxODg2NWIyN2JhZGY4ZjUyMjcxZTdkZGU0MyJ9.G2IGVw.JCK3tXfD0aOgDdIAMv5MlFkl-SY; Expires=Sat, 23-Aug-2025 05:36:23 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "dddd", + "tag_color": "#ffff00", + "tag_description": "dddd" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee new file mode 100644 index 0000000000..eac0faf464 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Feeee @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:36:34 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-gs07ooad3wPkkzZKAQDHDTrMl'; style-src 'self' 'nonce-gs07ooad3wPkkzZKAQDHDTrMl'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGEyNDUwYzk5MDY2ZjJhZGQyNDhlZmZmM2QxN2UxZTM0ODI2NWFhZiJ9.G2IGYg.hM6ZKEPDXtOvTWlSPeQBLiZjCO4; Expires=Sat, 23-Aug-2025 05:36:34 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "eeee", + "tag_color": "#00ff00", + "tag_description": "eeee" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff new file mode 100644 index 0000000000..af5935aa46 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fffff @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:36:48 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-hiIOP1ZdLfgxJERlzriEOATjs'; style-src 'self' 'nonce-hiIOP1ZdLfgxJERlzriEOATjs'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYWMyNjBhNjE5MjI0OTQ0YTU2Yzc5YjNmNzU3ZTU2MTYzZGQwMGMwNSJ9.G2IGcQ.22KbaZBjPxJkpIoTBIn1UtVzEjI; Expires=Sat, 23-Aug-2025 05:36:49 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "ffff", + "tag_color": "#00ff00", + "tag_description": "ffff" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg new file mode 100644 index 0000000000..28fd0ef15d --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftag%2Fgggg @@ -0,0 +1,18 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:37:23 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 77 +content-security-policy: default-src 'self';script-src 'self' 'nonce-FYnaELqaJomAM4RnLtKUr7gbE'; style-src 'self' 'nonce-FYnaELqaJomAM4RnLtKUr7gbE'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiOGExMmI2MGZhYTQyYTY5MmQ0MzNkNWFlZTU0YjE4M2NjY2NmYjI3MCJ9.G2IGkw.9-wL70lPAOlpIv8cusGLLA0Np_U; Expires=Sat, 23-Aug-2025 05:37:23 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tag": "gggg", + "tag_color": "#0000ff", + "tag_description": "gggg" +} diff --git a/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags new file mode 100644 index 0000000000..1176b532a4 --- /dev/null +++ b/services/migrations/testdata/pagure/full_download/unauthorized/GET_%2Fapi%2F0%2Fprotop2g-test-srce%2Ftags @@ -0,0 +1,26 @@ +HTTP/2 200 +date: Wed, 23 Jul 2025 05:16:48 GMT +server: Apache/2.4.37 (Red Hat Enterprise Linux) OpenSSL/1.1.1k mod_wsgi/4.6.4 Python/3.6 +x-xss-protection: 1; mode=block +x-content-type-options: nosniff +referrer-policy: same-origin +x-frame-options: ALLOW-FROM https://pagure.io/ +strict-transport-security: max-age=31536000; includeSubDomains; preload +content-length: 142 +content-security-policy: default-src 'self';script-src 'self' 'nonce-T8LPhGGs1acv00t58vtBtVmwC'; style-src 'self' 'nonce-T8LPhGGs1acv00t58vtBtVmwC'; object-src 'none';base-uri 'self';img-src 'self' https:;connect-src 'self' https://pagure.io:8088;frame-src https://docs.pagure.org;frame-ancestors https://pagure.io; +set-cookie: pagure=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmX3Rva2VuIjoiYmQ2NTQ1MmFhZWJjZjA0OWIxNTI5MjBjODQyYzUzOGRkYmYyYTkwOSJ9.G2IBwA.pKqOBVDJrtKfyrbJPyuaRfFjnR4; Expires=Sat, 23-Aug-2025 05:16:48 GMT; Secure; HttpOnly; Path=/ +content-type: application/json + +{ + "tags": [ + "aaaa", + "bbbb", + "cccc", + "dddd", + "eeee", + "ffff", + "gggg", + "hhhh" + ], + "total_tags": 8 +} diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 11b8ad459a..fdd02dedea 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -33,19 +33,22 @@ var AddPushMirrorRemote = addPushMirrorRemote func addPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { addRemoteAndConfig := func(addr, path string) error { - cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr) - if strings.Contains(addr, "://") && strings.Contains(addr, "@") { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path)) + var cmd *git.Command + if m.BranchFilter == "" { + cmd = git.NewCommand(ctx, "remote", "add", "--mirror").AddDynamicArguments(m.RemoteName, addr) } else { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path)) + cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(m.RemoteName, addr) + } + if strings.Contains(addr, "://") && strings.Contains(addr, "@") { + cmd.SetDescription(fmt.Sprintf("remote add %s %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path)) + } else { + cmd.SetDescription(fmt.Sprintf("remote add %s %s [repo_path: %s]", m.RemoteName, addr, path)) } if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil { return err } - if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { - return err - } - if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { + err := addRemotePushRefSpecs(ctx, path, m) + if err != nil { return err } return nil @@ -67,6 +70,49 @@ func addPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str return nil } +func addRemotePushRefSpecs(ctx context.Context, path string, m *repo_model.PushMirror) error { + if m.BranchFilter == "" { + // If there is no branch filter, set the push refspecs to mirror all branches and tags. + if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { + return err + } + } else { + branches := strings.SplitSeq(m.BranchFilter, ",") + for branch := range branches { + branch = strings.TrimSpace(branch) + if branch == "" { + continue + } + refspec := fmt.Sprintf("+refs/heads/%s:refs/heads/%s", branch, branch) + if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", refspec).RunStdString(&git.RunOpts{Dir: path}); err != nil { + return err + } + } + } + if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { + return err + } + return nil +} + +func UpdatePushMirrorBranchFilter(ctx context.Context, m *repo_model.PushMirror) error { + path := m.Repo.RepoPath() + + // First, remove all existing push refspecs for this remote + cmd := git.NewCommand(ctx, "config", "--unset-all").AddDynamicArguments("remote." + m.RemoteName + ".push") + if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil { + // Ignore error if the key doesn't exist + if !strings.Contains(err.Error(), "does not exist") { + return err + } + } + err := addRemotePushRefSpecs(ctx, path, m) + if err != nil { + return err + } + return nil +} + // RemovePushMirrorRemote removes the push mirror remote. func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error { cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(m.RemoteName) @@ -212,7 +258,6 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error { return util.SanitizeErrorCredentialURLs(err) } - return nil } diff --git a/services/moderation/main_test.go b/services/moderation/main_test.go new file mode 100644 index 0000000000..3a268260d2 --- /dev/null +++ b/services/moderation/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "testing" + + "forgejo.org/models/unittest" + + _ "forgejo.org/models/forgefed" + _ "forgejo.org/models/moderation" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} diff --git a/services/moderation/moderating.go b/services/moderation/moderating.go new file mode 100644 index 0000000000..f329070963 --- /dev/null +++ b/services/moderation/moderating.go @@ -0,0 +1,41 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "forgejo.org/models/issues" + "forgejo.org/models/moderation" + "forgejo.org/models/repo" + "forgejo.org/models/user" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/services/context" +) + +// GetShadowCopyMap unmarshals the shadow copy raw value of the given abuse report and returns a list of pairs +// (to be rendered when the report is reviewed by an admin). +// If the report does not have a shadow copy ID or the raw value is empty, returns nil. +// If the unmarshal fails a warning is added in the logs and returns nil. +func GetShadowCopyMap(ctx *context.Context, ard *moderation.AbuseReportDetailed) []moderation.ShadowCopyField { + if ard.ShadowCopyID.Valid && len(ard.ShadowCopyRawValue) > 0 { + var data moderation.ShadowCopyData + + switch ard.ContentType { + case moderation.ReportedContentTypeUser: + data = new(user.UserData) + case moderation.ReportedContentTypeRepository: + data = new(repo.RepositoryData) + case moderation.ReportedContentTypeIssue: + data = new(issues.IssueData) + case moderation.ReportedContentTypeComment: + data = new(issues.CommentData) + } + if err := json.Unmarshal([]byte(ard.ShadowCopyRawValue), &data); err != nil { + log.Warn("Unmarshal failed for shadow copy #%d. %v", ard.ShadowCopyID.Int64, err) + return nil + } + return data.GetFieldsMap() + } + return nil +} diff --git a/services/moderation/reporting.go b/services/moderation/reporting.go index e01156dc11..3d1bb5b32c 100644 --- a/services/moderation/reporting.go +++ b/services/moderation/reporting.go @@ -4,8 +4,11 @@ package moderation import ( + stdCtx "context" "errors" + "time" + "forgejo.org/models/db" "forgejo.org/models/issues" "forgejo.org/models/moderation" "forgejo.org/models/perm" @@ -127,3 +130,41 @@ func CanReport(ctx context.Context, doer *user.User, contentType moderation.Repo return hasAccess, nil } + +// RemoveResolvedReports removes resolved reports +func RemoveResolvedReports(ctx stdCtx.Context, keepReportsFor time.Duration) error { + log.Trace("Doing: RemoveResolvedReports") + + if keepReportsFor <= 0 { + return nil + } + + err := db.WithTx(ctx, func(ctx stdCtx.Context) error { + resolvedReports, err := moderation.GetResolvedReports(ctx, keepReportsFor) + if err != nil { + return err + } + + for _, report := range resolvedReports { + _, err := db.GetEngine(ctx).ID(report.ID).Delete(&moderation.AbuseReport{}) + if err != nil { + return err + } + + if report.ShadowCopyID.Valid { + _, err := db.GetEngine(ctx).ID(report.ShadowCopyID).Delete(&moderation.AbuseReportShadowCopy{}) + if err != nil { + return err + } + } + } + + return nil + }) + if err != nil { + return err + } + + log.Trace("Finished: RemoveResolvedReports") + return nil +} diff --git a/services/moderation/reporting_test.go b/services/moderation/reporting_test.go new file mode 100644 index 0000000000..70925bf184 --- /dev/null +++ b/services/moderation/reporting_test.go @@ -0,0 +1,97 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "testing" + "time" + + "forgejo.org/models/db" + report_model "forgejo.org/models/moderation" + "forgejo.org/models/unittest" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/require" +) + +func TestRemoveResolvedReportsWhenNoTimeSet(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + // reportAge needs to be an int64 to match what timeutil.Day expects so we cast the value + reportAge := int64(20) + resolvedReport := &report_model.AbuseReport{ + Status: report_model.ReportStatusTypeHandled, + ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository, + ContentID: 2, Category: report_model.AbuseCategoryTypeOther, + CreatedUnix: timeutil.TimeStampNow(), + ResolvedUnix: timeutil.TimeStamp(time.Now().Unix() - timeutil.Day*reportAge), + } + _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport) + require.NoError(t, err) + + // No reports should be deleted when the default time to keep is 0 + err = RemoveResolvedReports(db.DefaultContext, time.Second*0) + require.NoError(t, err) + unittest.AssertExistsIf(t, true, resolvedReport) +} + +func TestRemoveResolvedReportsWhenMatchTimeSet(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + // keepReportsFor needs to an int64 to match what timeutil.Day expects so we cast the value + keepReportsFor := int64(4) + resolvedReport := &report_model.AbuseReport{ + Status: report_model.ReportStatusTypeHandled, + ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository, + ContentID: 2, Category: report_model.AbuseCategoryTypeOther, + CreatedUnix: timeutil.TimeStampNow(), + ResolvedUnix: timeutil.TimeStamp(time.Now().Unix() - timeutil.Day*keepReportsFor), + } + + _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport) + require.NoError(t, err) + + // Report should be deleted when older than the default time to keep + err = RemoveResolvedReports(db.DefaultContext, time.Second*4) + require.NoError(t, err) + unittest.AssertExistsIf(t, false, resolvedReport) +} + +func TestRemoveResolvedReportsWhenTimeSetButReportNew(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + resolvedReport := &report_model.AbuseReport{ + Status: report_model.ReportStatusTypeHandled, + ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository, + ContentID: 2, Category: report_model.AbuseCategoryTypeOther, + CreatedUnix: timeutil.TimeStampNow(), + ResolvedUnix: timeutil.TimeStampNow(), + } + _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport) + require.NoError(t, err) + + // Report should not be deleted when newer than the default time to keep + err = RemoveResolvedReports(db.DefaultContext, time.Second*4) + require.NoError(t, err) + unittest.AssertExistsIf(t, true, resolvedReport) +} + +func TestDoesNotRemoveOpenReports(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + // keepReportsFor needs to an int64 to match what timeutil.Day expects so we cast the value + keepReportsFor := int64(4) + resolvedReport := &report_model.AbuseReport{ + Status: report_model.ReportStatusTypeOpen, + ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository, + ContentID: 2, Category: report_model.AbuseCategoryTypeOther, + CreatedUnix: timeutil.TimeStampNow(), + ResolvedUnix: timeutil.TimeStamp(time.Now().Unix() - timeutil.Day*keepReportsFor), + } + + _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport) + require.NoError(t, err) + + // Report should not be deleted when open + // and older than the default time to keep + err = RemoveResolvedReports(db.DefaultContext, time.Second*4) + require.NoError(t, err) + unittest.AssertExistsIf(t, true, resolvedReport) +} diff --git a/services/pull/check.go b/services/pull/check.go index 6002e2ae26..c31d107605 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -404,6 +404,10 @@ func CheckPRsForBaseBranch(ctx context.Context, baseRepo *repo_model.Repository, // Init runs the task queue to test all the checking status pull requests func Init() error { + if err := LoadMergeMessageTemplates(); err != nil { + return err + } + prPatchCheckerQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_patch_checker", handler) if prPatchCheckerQueue == nil { diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index 3c864c8ef2..0a95ea1152 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -12,7 +12,6 @@ import ( "forgejo.org/models/db" git_model "forgejo.org/models/git" issues_model "forgejo.org/models/issues" - "forgejo.org/modules/git" "forgejo.org/modules/gitrepo" "forgejo.org/modules/log" "forgejo.org/modules/structs" @@ -105,7 +104,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR if pr.Flow == issues_model.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) { return "", errors.New("head branch does not exist, can not merge") } - if pr.Flow == issues_model.PullRequestFlowAGit && !git.IsReferenceExist(ctx, headGitRepo.Path, pr.GetGitRefName()) { + if pr.Flow == issues_model.PullRequestFlowAGit && !headGitRepo.IsReferenceExist(pr.GetGitRefName()) { return "", errors.New("head branch does not exist, can not merge") } diff --git a/services/pull/merge.go b/services/pull/merge.go index f69f8a87b4..a2542176f0 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -35,6 +35,30 @@ import ( notify_service "forgejo.org/services/notify" ) +var mergeMessageTemplates = make(map[repo_model.MergeStyle]string, len(repo_model.MergeStyles)) + +func LoadMergeMessageTemplates() error { + // Load templates for all known merge styles + for _, mergeStyle := range repo_model.MergeStyles { + templateFilename := filepath.Join( + setting.CustomPath, + "default_merge_message", + fmt.Sprintf("%s_TEMPLATE.md", strings.ToUpper(string(mergeStyle))), + ) + + content, err := os.ReadFile(templateFilename) + if err == nil { + mergeMessageTemplates[mergeStyle] = string(content) + } else if os.IsNotExist(err) { + // The file no longer exists, so delete any previous content + delete(mergeMessageTemplates, mergeStyle) + } else { + return err + } + } + return nil +} + // getMergeMessage composes the message used when merging a pull request. func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issues_model.PullRequest, mergeStyle repo_model.MergeStyle, extraVars map[string]string) (message, body string, err error) { if err := pr.LoadBaseRepo(ctx); err != nil { @@ -79,6 +103,13 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue if _, ok := err.(git.ErrNotExist); ok { templateContent, err = commit.GetFileContent(templateFilepathGitea, setting.Repository.PullRequest.DefaultMergeMessageSize) } + + if _, ok := err.(git.ErrNotExist); ok { + if preloadedContent, ok := mergeMessageTemplates[mergeStyle]; ok { + templateContent, err = preloadedContent, nil + } + } + if err != nil { if !git.IsErrNotExist(err) { return "", "", err @@ -208,7 +239,30 @@ func AddCommitMessageTrailer(message, tailerKey, tailerValue string) string { // Merge merges pull request to base repository. // Caller should check PR is ready to be merged (review and status checks) func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, wasAutoMerged bool) error { - if err := pr.LoadBaseRepo(ctx); err != nil { + pullWorkingPool.CheckIn(fmt.Sprint(pr.ID)) + defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID)) + + pr, err := issues_model.GetPullRequestByID(ctx, pr.ID) + if err != nil { + log.Error("Unable to load pull request itself: %v", err) + return fmt.Errorf("unable to load pull request itself: %w", err) + } + + if pr.HasMerged { + return models.ErrPullRequestHasMerged{ + ID: pr.ID, + IssueID: pr.IssueID, + HeadRepoID: pr.HeadRepoID, + BaseRepoID: pr.BaseRepoID, + HeadBranch: pr.HeadBranch, + BaseBranch: pr.BaseBranch, + } + } + + if err := pr.LoadIssue(ctx); err != nil { + log.Error("Unable to load issue: %v", err) + return fmt.Errorf("unable to load issue: %w", err) + } else if err := pr.LoadBaseRepo(ctx); err != nil { log.Error("Unable to load base repo: %v", err) return fmt.Errorf("unable to load base repo: %w", err) } else if err := pr.LoadHeadRepo(ctx); err != nil { @@ -216,9 +270,6 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U return fmt.Errorf("unable to load head repo: %w", err) } - pullWorkingPool.CheckIn(fmt.Sprint(pr.ID)) - defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID)) - prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests) if err != nil { log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err) diff --git a/services/pull/merge_prepare.go b/services/pull/merge_prepare.go index fc70da10a4..4598d57b7a 100644 --- a/services/pull/merge_prepare.go +++ b/services/pull/merge_prepare.go @@ -249,6 +249,11 @@ func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) ctx.outbuf.Reset() ctx.errbuf.Reset() + // If the pull request is zero commits behind, then no rebasing needs to be done. + if ctx.pr.CommitsBehind == 0 { + return nil + } + // Check git version for availability of git-replay. If it is available, we use // it for performance and to preserve unknown commit headers like the // "change-id" header used by Jujutsu and GitButler to track changes across diff --git a/services/pull/merge_test.go b/services/pull/merge_test.go index 2a26759956..aa033b8cdb 100644 --- a/services/pull/merge_test.go +++ b/services/pull/merge_test.go @@ -4,9 +4,22 @@ package pull import ( + "os" + "path" + "strings" "testing" + "forgejo.org/models" + issues_model "forgejo.org/models/issues" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/gitrepo" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_expandDefaultMergeMessage(t *testing.T) { @@ -90,3 +103,77 @@ func TestAddCommitMessageTailer(t *testing.T) { assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTrailer("title\n\nTest-tailer: v1", "Test-tailer", "v2")) assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTrailer("title\n\nTest-tailer: v1\n", "Test-tailer", "v2")) } + +func prepareLoadMergeMessageTemplates(targetDir string) error { + for _, template := range []string{"MERGE", "REBASE", "REBASE-MERGE", "SQUASH", "MANUALLY-MERGED", "REBASE-UPDATE-ONLY"} { + file, err := os.Create(path.Join(targetDir, template+"_TEMPLATE.md")) + defer file.Close() + + if err == nil { + _, err = file.WriteString("Contents for " + template) + } + + if err != nil { + return err + } + } + + return nil +} + +func TestLoadMergeMessageTemplates(t *testing.T) { + defer test.MockVariableValue(&setting.CustomPath, t.TempDir())() + templateTemp := path.Join(setting.CustomPath, "default_merge_message") + + require.NoError(t, os.MkdirAll(templateTemp, 0o755)) + require.NoError(t, prepareLoadMergeMessageTemplates(templateTemp)) + + testStyles := []repo_model.MergeStyle{ + repo_model.MergeStyleMerge, + repo_model.MergeStyleRebase, + repo_model.MergeStyleRebaseMerge, + repo_model.MergeStyleSquash, + repo_model.MergeStyleManuallyMerged, + repo_model.MergeStyleRebaseUpdate, + } + + // Load all templates + require.NoError(t, LoadMergeMessageTemplates()) + + // Check their correctness + assert.Len(t, mergeMessageTemplates, len(testStyles)) + for _, mergeStyle := range testStyles { + assert.Equal(t, "Contents for "+strings.ToUpper(string(mergeStyle)), mergeMessageTemplates[mergeStyle]) + } + + // Unload all templates + require.NoError(t, os.RemoveAll(templateTemp)) + require.NoError(t, LoadMergeMessageTemplates()) + assert.Empty(t, mergeMessageTemplates) +} + +func TestMergeMergedPR(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + require.NoError(t, pr.LoadBaseRepo(t.Context())) + + gitRepo, err := gitrepo.OpenRepository(t.Context(), pr.BaseRepo) + require.NoError(t, err) + defer gitRepo.Close() + + assert.True(t, pr.HasMerged) + pr.HasMerged = false + + err = Merge(t.Context(), pr, doer, gitRepo, repo_model.MergeStyleRebase, "", "I should not exist", false) + require.Error(t, err) + assert.True(t, models.IsErrPullRequestHasMerged(err)) + + if mergeErr, ok := err.(models.ErrPullRequestHasMerged); ok { + assert.Equal(t, pr.ID, mergeErr.ID) + assert.Equal(t, pr.IssueID, mergeErr.IssueID) + assert.Equal(t, pr.HeadBranch, mergeErr.HeadBranch) + assert.Equal(t, pr.BaseBranch, mergeErr.BaseBranch) + } +} diff --git a/services/pull/patch.go b/services/pull/patch.go index 37a0f818e9..89581c916b 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -106,12 +106,15 @@ func (t *testPatchContext) LoadHeadRevision(ctx context.Context, pr *issues_mode } // getTestPatchCtx constructs a new testpatch context for the given pull request. -func getTestPatchCtx(ctx context.Context, pr *issues_model.PullRequest) (*testPatchContext, error) { +// If `onBare` is true, then the context will use the base repository that does +// not contain a working tree. Otherwise a temprorary repository is created that +// contains a working tree. +func getTestPatchCtx(ctx context.Context, pr *issues_model.PullRequest, onBare bool) (*testPatchContext, error) { testPatchCtx := &testPatchContext{ close: func() {}, } - if git.SupportGitMergeTree { + if onBare { if err := pr.LoadBaseRepo(ctx); err != nil { return testPatchCtx, fmt.Errorf("LoadBaseRepo: %w", err) } @@ -157,7 +160,7 @@ func getTestPatchCtx(ctx context.Context, pr *issues_model.PullRequest) (*testPa } func testPatch(ctx context.Context, pr *issues_model.PullRequest) (*testPatchContext, error) { - testPatchCtx, err := getTestPatchCtx(ctx, pr) + testPatchCtx, err := getTestPatchCtx(ctx, pr, git.SupportGitMergeTree) if err != nil { return testPatchCtx, fmt.Errorf("getTestPatchCtx: %w", err) } diff --git a/services/pull/pull.go b/services/pull/pull.go index 26210f7156..c1fe6c6b55 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -4,11 +4,9 @@ package pull import ( - "bytes" "context" "fmt" "io" - "os" "regexp" "strings" "time" @@ -30,7 +28,6 @@ import ( repo_module "forgejo.org/modules/repository" "forgejo.org/modules/setting" "forgejo.org/modules/sync" - "forgejo.org/modules/util" gitea_context "forgejo.org/services/context" issue_service "forgejo.org/services/issue" notify_service "forgejo.org/services/notify" @@ -384,98 +381,51 @@ func TestPullRequest(ctx context.Context, doer *user_model.User, repoID, olderTh // Update commit divergence. func ValidatePullRequest(ctx context.Context, pr *issues_model.PullRequest, newCommitID, oldCommitID string, doer *user_model.User) { objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) - if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() { - changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) + if newCommitID == "" || newCommitID == objectFormat.EmptyObjectID().String() { + return + } + + testPatchCtx, err := getTestPatchCtx(ctx, pr, true) + defer testPatchCtx.close() + if err != nil { + log.Error("testPatchCtx: %v", err) + return + } + + changed, err := testPatchCtx.gitRepo.CheckIfDiffDiffers(testPatchCtx.baseRev, oldCommitID, newCommitID, testPatchCtx.env) + if err != nil { + log.Error("CheckIfDiffDiffers: %v", err) + } + if changed { + if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil { + log.Error("MarkReviewsAsStale: %v", err) + } + + pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) if err != nil { - log.Error("checkIfPRContentChanged: %v", err) + log.Error("GetFirstMatchProtectedBranchRule: %v", err) } - if changed { - if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil { - log.Error("MarkReviewsAsStale: %v", err) + if pb != nil && pb.DismissStaleApprovals { + if err := DismissApprovalReviews(ctx, doer, pr); err != nil { + log.Error("DismissApprovalReviews: %v", err) } + } + } + if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil { + log.Error("MarkReviewsAsNotStale: %v", err) + } - pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) - if err != nil { - log.Error("GetFirstMatchProtectedBranchRule: %v", err) - } - if pb != nil && pb.DismissStaleApprovals { - if err := DismissApprovalReviews(ctx, doer, pr); err != nil { - log.Error("DismissApprovalReviews: %v", err) - } - } - } - if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil { - log.Error("MarkReviewsAsNotStale: %v", err) - } - divergence, err := GetDiverging(ctx, pr) + divergence, err := git.GetDivergingCommits(ctx, testPatchCtx.gitRepo.Path, testPatchCtx.baseRev, testPatchCtx.headRev, testPatchCtx.env) + if err != nil { + log.Error("GetDivergingCommits: %v", err) + } else { + err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) if err != nil { - log.Error("GetDiverging: %v", err) - } else { - err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) - if err != nil { - log.Error("UpdateCommitDivergence: %v", err) - } + log.Error("UpdateCommitDivergence: %v", err) } } } -// checkIfPRContentChanged checks if diff to target branch has changed by push -// A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged -func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) { - prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr) - if err != nil { - log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err) - return false, err - } - defer cancel() - - tmpRepo, err := git.OpenRepository(ctx, prCtx.tmpBasePath) - if err != nil { - return false, fmt.Errorf("OpenRepository: %w", err) - } - defer tmpRepo.Close() - - // Find the merge-base - _, base, err := tmpRepo.GetMergeBase("", "base", "tracking") - if err != nil { - return false, fmt.Errorf("GetMergeBase: %w", err) - } - - cmd := git.NewCommand(ctx, "diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base) - stdoutReader, stdoutWriter, err := os.Pipe() - if err != nil { - return false, fmt.Errorf("unable to open pipe for to run diff: %w", err) - } - - stderr := new(bytes.Buffer) - if err := cmd.Run(&git.RunOpts{ - Dir: prCtx.tmpBasePath, - Stdout: stdoutWriter, - Stderr: stderr, - PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { - _ = stdoutWriter.Close() - defer func() { - _ = stdoutReader.Close() - }() - return util.IsEmptyReader(stdoutReader) - }, - }); err != nil { - if err == util.ErrNotEmpty { - return true, nil - } - err = git.ConcatenateError(err, stderr.String()) - - log.Error("Unable to run diff on %s %s %s in tempRepo for PR[%d]%s/%s...%s/%s: Error: %v", - newCommitID, oldCommitID, base, - pr.ID, pr.BaseRepo.FullName(), pr.BaseBranch, pr.HeadRepo.FullName(), pr.HeadBranch, - err) - - return false, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, base, err) - } - - return false, nil -} - // PushToBaseRepo pushes commits from branches of head repository to // corresponding branches of base repository. // FIXME: Only push branches that are actually updates? diff --git a/services/pull/pull_test.go b/services/pull/pull_test.go index 010b7a6404..99607c5b35 100644 --- a/services/pull/pull_test.go +++ b/services/pull/pull_test.go @@ -92,3 +92,39 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) { assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo2:branch2 into master", mergeMessage) } + +func TestPullRequest_GetDefaultMergeMessage_GlobalTemplate(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}) + + require.NoError(t, pr.LoadBaseRepo(t.Context())) + gitRepo, err := gitrepo.OpenRepository(t.Context(), pr.BaseRepo) + require.NoError(t, err) + defer gitRepo.Close() + + templateRepo, err := git.OpenRepository(t.Context(), "./../../modules/git/tests/repos/templates_repo") + require.NoError(t, err) + defer templateRepo.Close() + + mergeMessageTemplates[repo_model.MergeStyleMerge] = "${PullRequestTitle} (${PullRequestReference})\n${PullRequestDescription}" + + // Check template is used for Merge... + mergeMessage, body, err := GetDefaultMergeMessage(t.Context(), gitRepo, pr, repo_model.MergeStyleMerge) + require.NoError(t, err) + + assert.Equal(t, "issue3 (#3)", mergeMessage) + assert.Equal(t, "content for the third issue", body) + + // ...but not for RebaseMerge + mergeMessage, _, err = GetDefaultMergeMessage(t.Context(), gitRepo, pr, repo_model.MergeStyleRebaseMerge) + require.NoError(t, err) + + assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", mergeMessage) + + // ...and that custom Merge template takes priority + mergeMessage, body, err = GetDefaultMergeMessage(t.Context(), templateRepo, pr, repo_model.MergeStyleMerge) + require.NoError(t, err) + + assert.Equal(t, "Default merge message template", mergeMessage) + assert.Equal(t, "This line was read from .forgejo/default_merge_message/MERGE_TEMPLATE.md", body) +} diff --git a/services/pull/review.go b/services/pull/review.go index c740328e4c..b0ab700fa6 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -9,8 +9,6 @@ import ( "errors" "fmt" "io" - "regexp" - "strings" "forgejo.org/models/db" issues_model "forgejo.org/models/issues" @@ -25,8 +23,6 @@ import ( notify_service "forgejo.org/services/notify" ) -var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`) - // ErrDismissRequestOnClosedPR represents an error when an user tries to dismiss a review associated to a closed or merged PR. type ErrDismissRequestOnClosedPR struct{} @@ -48,8 +44,8 @@ func (err ErrDismissRequestOnClosedPR) Unwrap() error { // If the line got changed the comment is going to be invalidated. func checkInvalidation(ctx context.Context, c *issues_model.Comment, repo *git.Repository, branch string) error { // FIXME differentiate between previous and proposed line - commit, err := repo.LineBlame(branch, repo.Path, c.TreePath, uint(c.UnsignedLine())) - if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) { + commit, err := repo.LineBlame(branch, c.TreePath, c.UnsignedLine()) + if err != nil && (errors.Is(err, git.ErrBlameFileDoesNotExist) || errors.Is(err, git.ErrBlameFileNotEnoughLines)) { c.Invalidated = true return issues_model.UpdateCommentInvalidate(ctx, c) } @@ -230,10 +226,10 @@ func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User, // FIXME validate treePath // Get latest commit referencing the commented line // No need for get commit for base branch changes - commit, err := gitRepo.LineBlame(head, gitRepo.Path, treePath, uint(line)) + commit, err := gitRepo.LineBlame(head, treePath, uint64(line)) if err == nil { commitID = commit.ID.String() - } else if !strings.Contains(err.Error(), "exit status 128 - fatal: no such path") && !notEnoughLines.MatchString(err.Error()) { + } else if !errors.Is(err, git.ErrBlameFileDoesNotExist) && !errors.Is(err, git.ErrBlameFileNotEnoughLines) { return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %w", pr.GetGitRefName(), gitRepo.Path, treePath, line, err) } } @@ -302,10 +298,16 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos if headCommitID == commitID { stale = false } else { - stale, err = checkIfPRContentChanged(ctx, pr, commitID, headCommitID) + testPatchCtx, err := getTestPatchCtx(ctx, pr, true) + defer testPatchCtx.close() if err != nil { return nil, nil, err } + + stale, err = testPatchCtx.gitRepo.CheckIfDiffDiffers(testPatchCtx.baseRev, commitID, headCommitID, testPatchCtx.env) + if err != nil { + return nil, nil, fmt.Errorf("CheckIfDiffDiffers: %w", err) + } } } diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go index 1805ffc527..76ae0df018 100644 --- a/services/pull/temp_repo.go +++ b/services/pull/temp_repo.go @@ -103,11 +103,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) remoteRepoName := "head_repo" baseBranch := "base" - fetchArgs := git.TrustedCmdArgs{"--no-tags"} - if git.CheckGitVersionAtLeast("2.25.0") == nil { - // Writing the commit graph can be slow and is not needed here - fetchArgs = append(fetchArgs, "--no-write-commit-graph") - } + fetchArgs := git.TrustedCmdArgs{"--no-tags", "--no-write-commit-graph"} // addCacheRepo adds git alternatives for the cacheRepoPath in the repoPath addCacheRepo := func(repoPath, cacheRepoPath string) error { diff --git a/services/repository/files/content.go b/services/repository/files/content.go index 3d2217df18..d701508ff0 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -5,6 +5,7 @@ package files import ( "context" + "errors" "fmt" "net/url" "path" @@ -107,7 +108,7 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType { switch { case entry.IsDir(): return ContentTypeDir - case entry.IsSubModule(): + case entry.IsSubmodule(): return ContentTypeSubmodule case entry.IsExecutable(), entry.IsRegular(): return ContentTypeRegular @@ -205,19 +206,19 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref } else if entry.IsLink() { contentsResponse.Type = string(ContentTypeLink) // The target of a symlink file is the content of the file - targetFromContent, err := entry.Blob().GetBlobContent(1024) + targetFromContent, err := entry.LinkTarget() if err != nil { return nil, err } contentsResponse.Target = &targetFromContent - } else if entry.IsSubModule() { + } else if entry.IsSubmodule() { contentsResponse.Type = string(ContentTypeSubmodule) - submoduleURL, err := commit.GetSubModule(treePath) + submodule, err := commit.GetSubmodule(treePath, entry) if err != nil { return nil, err } - if submoduleURL != "" { - contentsResponse.SubmoduleGitURL = &submoduleURL + if submodule.URL != "" { + contentsResponse.SubmoduleGitURL = &submodule.URL } } // Handle links @@ -229,7 +230,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref downloadURLString := downloadURL.String() contentsResponse.DownloadURL = &downloadURLString } - if !entry.IsSubModule() { + if !entry.IsSubmodule() { htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + url.PathEscape(string(refType)) + "/" + util.PathEscapeSegments(ref) + "/" + util.PathEscapeSegments(treePath)) if err != nil { return nil, err @@ -273,13 +274,11 @@ func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git if err != nil { return nil, err } - content := "" - if gitBlob.Size() <= setting.API.DefaultMaxBlobSize { - content, err = gitBlob.GetBlobContentBase64() - if err != nil { - return nil, err - } + content, err := gitBlob.GetContentBase64(setting.API.DefaultMaxBlobSize) + if err != nil && !errors.As(err, &git.BlobTooLargeError{}) { + return nil, err } + return &api.GitBlob{ SHA: gitBlob.ID.String(), URL: repo.APIURL() + "/git/blobs/" + url.PathEscape(gitBlob.ID.String()), diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go index 5b1dd65b5a..18b5226c02 100644 --- a/services/repository/files/patch.go +++ b/services/repository/files/patch.go @@ -147,11 +147,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user stdout := &strings.Builder{} stderr := &strings.Builder{} - cmdApply := git.NewCommand(ctx, "apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary") - if git.CheckGitVersionAtLeast("2.32") == nil { - cmdApply.AddArguments("-3") - } - + cmdApply := git.NewCommand(ctx, "apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary", "-3") if err := cmdApply.Run(&git.RunOpts{ Dir: t.basePath, Stdout: stdout, diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go index 1e575f95e8..5a369b27a5 100644 --- a/services/repository/files/tree.go +++ b/services/repository/files/tree.go @@ -87,7 +87,7 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git if entries[e].IsDir() { copy(treeURL[copyPos:], entries[e].ID.String()) tree.Entries[i].URL = string(treeURL) - } else if entries[e].IsSubModule() { + } else if entries[e].IsSubmodule() { // In Github Rest API Version=2022-11-28, if a tree entry is a submodule, // its url will be returned as an empty string. // So the URL will be set to "" here. diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go index 2d5aebdbc1..2de83ef5a2 100644 --- a/services/secrets/secrets.go +++ b/services/secrets/secrets.go @@ -15,16 +15,16 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data return nil, false, err } - s, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ + s, exists, err := db.Get[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ OwnerID: ownerID, RepoID: repoID, Name: name, - }) + }.ToConds()) if err != nil { return nil, false, err } - if len(s) == 0 { + if !exists { s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, name, data) if err != nil { return nil, false, err @@ -32,11 +32,11 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data return s, true, nil } - if err := secret_model.UpdateSecret(ctx, s[0].ID, data); err != nil { + s.SetSecret(data) + if _, err := db.GetEngine(ctx).Cols("data").ID(s.ID).Update(s); err != nil { return nil, false, err } - - return s[0], false, nil + return s, false, nil } func DeleteSecretByID(ctx context.Context, ownerID, repoID, secretID int64) error { diff --git a/services/user/delete.go b/services/user/delete.go index 9caa24c373..bed7abde07 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -218,7 +218,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) // ***** END: ExternalLoginUser ***** // If the user was reported as abusive, a shadow copy should be created before deletion. - if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u); err != nil { + if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u.ID, u); err != nil { return err } diff --git a/services/user/email.go b/services/user/email.go index 7a01fa77b3..36a1145aec 100644 --- a/services/user/email.go +++ b/services/user/email.go @@ -205,7 +205,7 @@ func MakeEmailAddressPrimary(ctx context.Context, u *user_model.User, newPrimary oldPrimaryEmail := u.Email // If the user was reported as abusive, a shadow copy should be created before first update (of certain columns). - if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u, "email"); err != nil { + if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u.ID, u, "email"); err != nil { return err } diff --git a/services/user/user.go b/services/user/user.go index d90fbac978..9cb6858f0c 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -13,6 +13,7 @@ import ( "forgejo.org/models" asymkey_model "forgejo.org/models/asymkey" + "forgejo.org/models/auth" "forgejo.org/models/db" "forgejo.org/models/organization" packages_model "forgejo.org/models/packages" @@ -25,6 +26,7 @@ import ( "forgejo.org/modules/storage" "forgejo.org/modules/util" "forgejo.org/services/agit" + "forgejo.org/services/auth/source/oauth2" org_service "forgejo.org/services/org" "forgejo.org/services/packages" container_service "forgejo.org/services/packages/container" @@ -47,10 +49,28 @@ func renameUser(ctx context.Context, u *user_model.User, newUserName string, doe } // Non-local users are not allowed to change their username. - if !u.IsOrganization() && !u.IsLocal() { - return user_model.ErrUserIsNotLocal{ - UID: u.ID, - Name: u.Name, + // If the doer is an admin, then allow the rename - they know better. + if !doerIsAdmin && !u.IsOrganization() && !u.IsLocal() { + // If the user's authentication source is OAuth2 and that source allows for + // username changes then don't make a fuzz about it. + + if !u.IsOAuth2() { + return user_model.ErrUserIsNotLocal{ + UID: u.ID, + Name: u.Name, + } + } + + source, err := auth.GetSourceByID(ctx, u.LoginSource) + if err != nil { + return err + } + sourceCfg := source.Cfg.(*oauth2.Source) + if !sourceCfg.AllowUsernameChange { + return user_model.ErrUserIsNotLocal{ + UID: u.ID, + Name: u.Name, + } } } diff --git a/services/user/user_test.go b/services/user/user_test.go index 4678d3bc9a..0747833557 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -15,13 +15,17 @@ import ( asymkey_model "forgejo.org/models/asymkey" "forgejo.org/models/auth" "forgejo.org/models/db" + "forgejo.org/models/moderation" "forgejo.org/models/organization" repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" + "forgejo.org/modules/json" + "forgejo.org/modules/optional" "forgejo.org/modules/setting" "forgejo.org/modules/test" "forgejo.org/modules/timeutil" + "forgejo.org/services/auth/source/oauth2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -137,17 +141,10 @@ func TestCreateUser(t *testing.T) { } func TestRenameUser(t *testing.T) { + defer unittest.OverrideFixtures("models/user/fixtures/")() require.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 21}) - t.Run("Non-Local", func(t *testing.T) { - u := &user_model.User{ - Type: user_model.UserTypeIndividual, - LoginType: auth.OAuth2, - } - require.ErrorIs(t, RenameUser(db.DefaultContext, u, "user_rename"), user_model.ErrUserIsNotLocal{}) - }) - t.Run("Same username", func(t *testing.T) { require.NoError(t, RenameUser(db.DefaultContext, user, user.Name)) }) @@ -216,6 +213,30 @@ func TestRenameUser(t *testing.T) { unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "redirect-1"}) unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "redirect-2"}) }) + + t.Run("Non-local", func(t *testing.T) { + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1041, LoginSource: 1001}) + authSource := unittest.AssertExistsAndLoadBean(t, &auth.Source{ID: user.LoginSource}) + assert.False(t, user.IsLocal()) + assert.True(t, user.IsOAuth2()) + + t.Run("Allowed", func(t *testing.T) { + require.NoError(t, RenameUser(t.Context(), user, "I-am-a-local-username")) + }) + + t.Run("Not allowed", func(t *testing.T) { + authSourceCfg := authSource.Cfg.(*oauth2.Source) + authSourceCfg.AllowUsernameChange = false + authSource.Cfg = authSourceCfg + _, err := db.GetEngine(t.Context()).Cols("cfg").ID(authSource.ID).Update(authSource) + require.NoError(t, err) + + require.ErrorIs(t, RenameUser(t.Context(), user, "Another-username-change"), user_model.ErrUserIsNotLocal{UID: user.ID, Name: user.Name}) + t.Run("Admin", func(t *testing.T) { + require.NoError(t, AdminRenameUser(t.Context(), user, "Another-username-change")) + }) + }) + }) } func TestCreateUser_Issue5882(t *testing.T) { @@ -277,3 +298,56 @@ func TestDeleteInactiveUsers(t *testing.T) { unittest.AssertExistsIf(t, true, newUser) unittest.AssertExistsIf(t, true, newEmail) } + +func TestCreateShadowCopyOnUserUpdate(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")() + require.NoError(t, unittest.PrepareTestDatabase()) + + userAlexSmithID := int64(1002) + abuseReportID := int64(2) // submitted for @alexsmith + newDummyValue := "[REDACTED]" // used for updating profile text fields + + // Retrieve the abusive user (@alexsmith) and the abuse report already created for this user. + abuser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID}) + report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ + ID: abuseReportID, + ContentType: moderation.ReportedContentTypeUser, + ContentID: abuser.ID, + }) + // The report should not already have a shadow copy linked. + assert.False(t, report.ShadowCopyID.Valid) + + // Keep a copy of old field values before updating them. + oldUserData := user_model.UserData{ + FullName: abuser.FullName, + Location: abuser.Location, + Website: abuser.Website, + Pronouns: abuser.Pronouns, + Description: abuser.Description, + } + + // The abusive user is updating their profile. + opts := &UpdateOptions{ + FullName: optional.Some(newDummyValue), + Location: optional.Some(newDummyValue), + Website: optional.Some(newDummyValue), + Pronouns: optional.Some(newDummyValue), + Description: optional.Some(newDummyValue), + } + require.NoError(t, UpdateUser(t.Context(), abuser, opts)) + + // Reload the report. + report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID}) + // A shadow copy should have been created and linked to our report. + assert.True(t, report.ShadowCopyID.Valid) + // Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values. + shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64}) + shadowCopyUserData := new(user_model.UserData) + require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyUserData)) + // Check to see if the initial field values of the user were stored within the shadow copy. + assert.Equal(t, oldUserData.FullName, shadowCopyUserData.FullName) + assert.Equal(t, oldUserData.Location, shadowCopyUserData.Location) + assert.Equal(t, oldUserData.Website, shadowCopyUserData.Website) + assert.Equal(t, oldUserData.Pronouns, shadowCopyUserData.Pronouns) + assert.Equal(t, oldUserData.Description, shadowCopyUserData.Description) +} diff --git a/services/webhook/discord.go b/services/webhook/discord.go index db98d40583..7259c4a995 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -350,7 +350,7 @@ func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, case webhook_module.HookEventPullRequestReviewApproved: return "approved", nil case webhook_module.HookEventPullRequestReviewRejected: - return "rejected", nil + return "requested changes", nil case webhook_module.HookEventPullRequestReviewComment: return "comment", nil default: diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 1ca5573cae..d4eaa29117 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -299,6 +299,13 @@

    {{ctx.Locale.Tr "admin.auths.skip_local_two_fa_helper"}}

    +
    +
    + + +

    {{ctx.Locale.Tr "admin.auths.allow_username_change.description"}}

    +
    +
    @@ -397,7 +404,7 @@
    - +
    @@ -414,7 +421,7 @@
    - -