diff --git a/.deadcode-out b/.deadcode-out index 24facdf12e..61c5bcb055 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -13,6 +13,13 @@ forgejo.org/models IsErrSHANotFound IsErrMergeDivergingFastForwardOnly +forgejo.org/models/activities + GetActivityByID + NewFederatedUserActivity + CreateUserActivity + GetFollowingFeeds + FederatedUserActivity.loadActor + forgejo.org/models/auth WebAuthnCredentials @@ -20,13 +27,15 @@ 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 @@ -52,10 +61,17 @@ forgejo.org/models/user IsErrExternalLoginUserAlreadyExist IsErrExternalLoginUserNotExist NewFederatedUser + NewFederatedUserFollower IsErrUserSettingIsNotExist GetUserAllSettings DeleteUserSetting GetFederatedUser + GetFederatedUserByUserID + UpdateFederatedUser + GetFollowersForUser + AddFollower + RemoveFollower + IsFollowingAp forgejo.org/modules/activitypub NewContext @@ -86,14 +102,24 @@ 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 @@ -205,6 +231,7 @@ forgejo.org/modules/util/filebuffer forgejo.org/modules/validation IsErrNotValid + ValidateIDExists forgejo.org/modules/web RouteMock @@ -221,9 +248,6 @@ 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 3f250e5682..28fa9e4555 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.5": {}, + "ghcr.io/devcontainers/features/git-lfs:1.2.4": {}, "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} }, "customizations": { diff --git a/.forgejo/issue_template/bug-report-ui.yaml b/.forgejo/issue_template/bug-report-ui.yaml index 1c66c3b648..8bb7bf1d49 100644 --- a/.forgejo/issue_template/bug-report-ui.yaml +++ b/.forgejo/issue_template/bug-report-ui.yaml @@ -23,9 +23,8 @@ body: It is running the latest development branch and will confirm the problem is not already fixed. If you can reproduce it, provide a URL in the description. options: - - "Yes, I've linked the repository below" - - "No, I've tried it and the problem is not present there" - - "No, I can't try it on the test instance for some reason" + - "Yes" + - "No" validations: required: true - type: textarea diff --git a/.forgejo/issue_template/bug-report.yaml b/.forgejo/issue_template/bug-report.yaml index c11ebf9c1f..a2b50dbca2 100644 --- a/.forgejo/issue_template/bug-report.yaml +++ b/.forgejo/issue_template/bug-report.yaml @@ -23,9 +23,8 @@ body: It is running the latest development branch and will confirm the problem is not already fixed. If you can reproduce it, provide a URL in the description. options: - - "Yes, I've linked the repository below" - - "No, I've tried it and the problem is not present there" - - "No, I can't try it on the test instance for some reason" + - "Yes" + - "No" validations: required: true - type: textarea diff --git a/.forgejo/workflows-composite/install-minimum-git-version/action.yaml b/.forgejo/workflows-composite/install-minimum-git-version/action.yaml deleted file mode 100644 index d4e6e3f2a7..0000000000 --- a/.forgejo/workflows-composite/install-minimum-git-version/action.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# -# 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 6fecac3ff6..1af6d567dd 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@v3.0.3 + uses: https://data.forgejo.org/actions/setup-forgejo@v2.0.4 with: user: root password: admin1234 diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index b70439f12f..3ab63b0589 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -164,7 +164,7 @@ jobs: - name: build container & release if: ${{ secrets.TOKEN != '' }} - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.4.1 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" @@ -183,7 +183,7 @@ jobs: - name: build rootless container if: ${{ secrets.TOKEN != '' }} - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.4.1 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5 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.3.0 + uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0 with: origin-url: ${{ env.GITHUB_SERVER_URL }} origin-repo: ${{ github.repository }} @@ -212,7 +212,6 @@ jobs: destination-repo: forgejo/end-to-end destination-branch: main destination-token: ${{ secrets.CASCADE_DESTINATION_TOKEN }} - close: true update: .forgejo/cascading-release-end-to-end - name: copy to experimental diff --git a/.forgejo/workflows/cascade-setup-end-to-end.yml b/.forgejo/workflows/cascade-setup-end-to-end.yml index e62dbd9e7d..7c8c56de13 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.3.0 + - uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0 with: origin-url: ${{ env.GITHUB_SERVER_URL }} origin-repo: ${{ github.repository }} @@ -53,5 +53,5 @@ jobs: destination-repo: forgejo/end-to-end destination-branch: main destination-token: ${{ secrets.END_TO_END_CASCADING_PR_DESTINATION }} - close: true + close-merge: true update: .forgejo/cascading-pr-end-to-end diff --git a/.forgejo/workflows/coverage.yml b/.forgejo/workflows/coverage.yml deleted file mode 100644 index 194b116251..0000000000 --- a/.forgejo/workflows/coverage.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: coverage - -on: - workflow_dispatch: - inputs: - repository: - description: 'repository' - type: string - ref: - description: 'ref' - type: string - unit-tests-env: - description: 'COVERAGE_TEST_PACKAGES=forgejo.org/modules/actions' - type: string - integration-tests-env: - description: 'COVERAGE_TEST_ARGS=-run=TestAPIListRepoComments' - type: string - -jobs: - all: - runs-on: docker - container: - image: 'data.forgejo.org/oci/ci:1' - options: --tmpfs /tmp:exec,noatime - services: - elasticsearch: - image: data.forgejo.org/oci/bitnami/elasticsearch:7 - options: --tmpfs /bitnami/elasticsearch/data - env: - discovery.type: single-node - ES_JAVA_OPTS: "-Xms512m -Xmx512m" - minio: - image: data.forgejo.org/oci/bitnami/minio:2024.8.17 - options: >- - --hostname gitea.minio --tmpfs /bitnami/minio/data:noatime - env: - MINIO_DOMAIN: minio - MINIO_ROOT_USER: 123456 - MINIO_ROOT_PASSWORD: 12345678 - mysql: - image: 'data.forgejo.org/oci/bitnami/mysql:8.4' - env: - ALLOW_EMPTY_PASSWORD: yes - MYSQL_DATABASE: testgitea - # - # See also https://codeberg.org/forgejo/forgejo/issues/976 - # - MYSQL_EXTRA_FLAGS: --innodb-adaptive-flushing=OFF --innodb-buffer-pool-size=4G --innodb-log-buffer-size=128M --innodb-flush-log-at-trx-commit=0 --innodb-flush-log-at-timeout=30 --innodb-flush-method=nosync --innodb-fsync-threshold=1000000000 --disable-log-bin - options: --tmpfs /bitnami/mysql/data:noatime - ldap: - image: data.forgejo.org/oci/test-openldap:latest - pgsql: - image: data.forgejo.org/oci/bitnami/postgresql:16 - env: - POSTGRESQL_DATABASE: test - POSTGRESQL_PASSWORD: postgres - POSTGRESQL_FSYNC: off - POSTGRESQL_EXTRA_FLAGS: -c full_page_writes=off - options: --tmpfs /bitnami/postgresql - cacher: - image: registry.redict.io/redict:7.3.5-scratch - options: --tmpfs /data:noatime - steps: - - uses: https://data.forgejo.org/actions/checkout@v4 - with: - repository: ${{ inputs.repository }} - ref: ${{ inputs.ref }} - - uses: ./.forgejo/workflows-composite/setup-env - - name: install git >= 2.42 - uses: ./.forgejo/workflows-composite/apt-install-from - with: - packages: git - - uses: ./.forgejo/workflows-composite/build-backend - - run: | - su forgejo -c '${{ inputs.unit-tests-env }} make coverage-run' - su forgejo -c '${{ inputs.integration-tests-env }} make coverage-run-pgsql' - su forgejo -c '${{ inputs.integration-tests-env }} make coverage-run-mysql' - su forgejo -c '${{ inputs.integration-tests-env }} make coverage-run-sqlite' - su forgejo -c 'make coverage-merge' - timeout-minutes: 180 - env: - TEST_ELASTICSEARCH_URL: http://elasticsearch:9200 - TEST_MINIO_ENDPOINT: minio:9000 - TEST_LDAP: 1 - TEST_REDIS_SERVER: cacher:6379 - - uses: https://code.forgejo.org/forgejo/upload-artifact@v4 - with: - name: coverage - path: ${{ forge.workspace }}/coverage/merged diff --git a/.forgejo/workflows/merge-requirements.yml b/.forgejo/workflows/merge-requirements.yml index 9bed4b8797..9aaf2af68d 100644 --- a/.forgejo/workflows/merge-requirements.yml +++ b/.forgejo/workflows/merge-requirements.yml @@ -1,4 +1,4 @@ -# Copyright 2025 The Forgejo Authors +# Copyright 2024 The Forgejo Authors # SPDX-License-Identifier: MIT name: requirements @@ -13,8 +13,7 @@ on: jobs: merge-conditions: - if: > - vars.ROLE == 'forgejo-coding' && forge.event.pull_request.head.repo.full_name != 'forgejo-cascading-pr/forgejo' + if: vars.ROLE == 'forgejo-coding' runs-on: docker container: image: 'data.forgejo.org/oci/node:22-bookworm' @@ -27,9 +26,9 @@ jobs: - name: Missing test label if: > !( - 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') + 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') ) run: | echo "A team member must set the label to either 'present', 'not-needed' or 'manual'." @@ -37,8 +36,8 @@ jobs: - name: Missing manual test instructions if: > ( - contains(toJSON(forge.event.pull_request.labels), 'test/manual') - && !contains(toJSON(forge.event.pull_request.body), '# Test') + contains(toJSON(github.event.pull_request.labels), 'test/manual') + && !contains(toJSON(github.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 5303f902e3..3aec46fb03 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.4.1 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.5 with: from-forgejo: ${{ vars.FORGEJO }} to-forgejo: ${{ vars.FORGEJO }} @@ -80,7 +80,7 @@ jobs: label: trigger - name: upgrade v*.next.forgejo.org - uses: https://data.forgejo.org/infrastructure/next-digest@v1.2.2 + uses: https://data.forgejo.org/infrastructure/next-digest@v1.1.0 with: url: https://placeholder:${{ secrets.TOKEN_NEXT_DIGEST }}@invisible.forgejo.org/infrastructure/next-digest ref_name: '${{ github.ref_name }}' diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index 57c9a23f02..7f77098357 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -5,34 +5,32 @@ on: - cron: '@daily' env: - RNA_WORKDIR: /srv/rna - RNA_VERSION: v1.4.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.2.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: if: vars.ROLE == 'forgejo-coding' runs-on: docker container: - image: 'data.forgejo.org/oci/ci:1' + image: 'data.forgejo.org/oci/node:22-bookworm' steps: - uses: https://data.forgejo.org/actions/checkout@v4 - - uses: https://data.forgejo.org/actions/cache@v4 + - uses: https://data.forgejo.org/actions/setup-go@v5 with: - key: rna-${{ env.RNA_VERSION }} - path: ${{ env.RNA_WORKDIR }} + go-version-file: "go.mod" + cache: false - - name: install release-notes-assistant + - name: apt install jq run: | - 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 + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get -q install -y -qq jq - name: update open milestones run: | set -x - 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 + curl -sS $GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/milestones?state=open | jq -r '.[] | .title' | while read forgejo version ; do milestone="$forgejo $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 + go run code.forgejo.org/forgejo/release-notes-assistant@$RNA_VERSION --config .release-notes-assistant.yaml --storage milestone --storage-location "$milestone" --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} release $version done diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index a727e57afb..cdcd2e6fe4 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.4.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.2.5 # 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 9e7f03f299..5aa6c8cd98 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.76.0 + image: data.forgejo.org/renovate/renovate:41.1.4 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: forgejo + RENOVATE_PLATFORM: gitea RENOVATE_REPOSITORY_CACHE: 'enabled' RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }} RENOVATE_GIT_AUTHOR: 'Renovate Bot ' @@ -63,10 +63,6 @@ jobs: OSV_OFFLINE_ROOT_DIR: ${{ github.workspace }}/.tmp/osv - # use direct connection for these domains for renovate go datasource instead of the go proxy - # allows faster lookups - GONOPROXY: code.forgejo.org - - name: Save renovate repo cache if: always() && env.RENOVATE_DRY_RUN != 'full' uses: https://data.forgejo.org/actions/cache/save@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml index 6822c45f39..9e5cfb92ed 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -1,8 +1,7 @@ # # 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 to the contact email of the -# organization should they fail. +# and send a notification via email should they fail. # # For debug purposes: # @@ -23,8 +22,6 @@ on: - 'forgejo' - 'v*/forgejo' -enable-email-notifications: true - jobs: test-unit: # if: vars.ROLE == 'forgejo-coding' @@ -36,8 +33,11 @@ jobs: steps: - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env - - name: install git 2.34.1 and git-lfs 3.0.2 - uses: ./.forgejo/workflows-composite/install-minimum-git-version + - name: install git 2.30 + uses: ./.forgejo/workflows-composite/apt-install-from + with: + packages: git/bullseye git-lfs/bullseye + release: bullseye - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-backend test-check' @@ -55,8 +55,11 @@ jobs: steps: - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env - - name: install git 2.34.1 and git-lfs 3.0.2 - uses: ./.forgejo/workflows-composite/install-minimum-git-version + - name: install git 2.30 + uses: ./.forgejo/workflows-composite/apt-install-from + with: + packages: git/bullseye git-lfs/bullseye + release: bullseye - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-sqlite-migration test-sqlite' @@ -66,34 +69,3 @@ 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 4c5e18a5e9..7a93bb66a8 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -37,11 +37,7 @@ jobs: - run: make deps-frontend - run: make lint-frontend - run: make checks-frontend - - 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 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 ffc493a51d..744e24a09a 100644 --- a/.gitignore +++ b/.gitignore @@ -37,8 +37,6 @@ _testmain.go *coverage.out coverage.all -coverage.html -coverage.html.gz coverage/ cpu.out @@ -55,8 +53,6 @@ cpu.out *.log *.log.*.gz -/build/lint-locale/lint-locale -/build/lint-locale-usage/lint-locale-usage /gitea /gitea-vet /debug @@ -133,4 +129,3 @@ prime/ # Manpage /man -tests/integration/api_activitypub_person_inbox_useractivity_test.go diff --git a/.golangci.yml b/.golangci.yml index b8884dd080..532132838d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -42,10 +42,6 @@ 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 - - pkg: gopkg.in/yaml.v3 - desc: use go.yaml.in/yaml instead, see https://codeberg.org/forgejo/forgejo/pulls/8956 gocritic: disabled-checks: - ifElseChain @@ -83,10 +79,6 @@ 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/CODEOWNERS b/CODEOWNERS index 25a3f698dc..34cdceca09 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -39,5 +39,4 @@ options/locale/.* @0ko options/locale_next/.* @0ko # Personal interest -build/lint-locale-usage/.* @fogti .*/webhook.* @oliverpool diff --git a/Makefile b/Makefile index 6f7a99d40c..e770f2a989 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.3.1 # renovate: datasource=go -GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 # renovate: datasource=go +GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go 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.36.0 # renovate: datasource=go -GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.6.0 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@41.76.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go +GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go +RENOVATE_NPM_PACKAGE ?= renovate@41.1.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... @@ -115,6 +115,9 @@ LDFLAGS := $(LDFLAGS) -X "main.ReleaseVersion=$(RELEASE_VERSION)" -X "main.MakeV LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64 +ifeq ($(HAS_GO), yes) + GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list forgejo.org/models/migrations/...) $(shell $(GO) list forgejo.org/models/forgejo_migrations/...) forgejo.org/tests/integration/migration-test forgejo.org/tests forgejo.org/tests/integration forgejo.org/tests/e2e,$(shell $(GO) list ./...)) +endif REMOTE_CACHER_MODULES ?= cache nosql session queue GO_TEST_REMOTE_CACHER_PACKAGES ?= $(addprefix forgejo.org/modules/,$(REMOTE_CACHER_MODULES)) @@ -156,6 +159,9 @@ GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/optio GO_SOURCES += $(GENERATED_GO_DEST) GO_SOURCES_NO_BINDATA := $(GO_SOURCES) +ifeq ($(HAS_GO), yes) + MIGRATION_PACKAGES := $(shell $(GO) list forgejo.org/models/migrations/... forgejo.org/models/forgejo_migrations/...) +endif ifeq ($(filter $(TAGS_SPLIT),bindata),bindata) GO_SOURCES += $(BINDATA_DEST) @@ -232,9 +238,6 @@ help: @echo " - test-frontend-coverage test frontend files and display code coverage" @echo " - test-backend test backend files" @echo " - test-remote-cacher test backend files that use a remote cache" - @echo " - coverage-run* test and collect coverages in the coverage/data directory" - @echo " - coverage-show-html display coverage-run results in an HTML page" - @echo " - coverage-show-percent display coverage-run per package coverage percentage" @echo " - test-e2e-sqlite[\#name.test.e2e] test end to end using playwright and sqlite" @echo " - webpack build webpack files" @echo " - svg build svg files" @@ -279,24 +282,6 @@ show-version-minor: verify-version show-version-api: verify-version @echo ${FORGEJO_VERSION_API} -### -# Package computation targets -### - -# Target to compute GO_TEST_PACKAGES - only runs when needed -.PHONY: compute-go-test-packages -compute-go-test-packages: -ifeq ($(HAS_GO), yes) - $(eval GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list forgejo.org/models/migrations/...) $(shell $(GO) list forgejo.org/models/forgejo_migrations/...) forgejo.org/tests/integration/migration-test forgejo.org/tests forgejo.org/tests/integration forgejo.org/tests/e2e,$(shell $(GO) list ./...))) -endif - -# Target to compute MIGRATION_PACKAGES - only runs when needed -.PHONY: compute-migration-packages -compute-migration-packages: -ifeq ($(HAS_GO), yes) - $(eval MIGRATION_PACKAGES := $(shell $(GO) list forgejo.org/models/migrations/... forgejo.org/models/forgejo_migrations/...)) -endif - ### # Check system and environment requirements ### @@ -475,7 +460,7 @@ lint-locale: .PHONY: lint-locale-usage lint-locale-usage: - $(GO) run ./build/lint-locale-usage --allow-masked-usages-from=build/lint-locale-usage/allowed-masked-usage.txt + $(GO) run build/lint-locale-usage/lint-locale-usage.go .PHONY: lint-md lint-md: node_modules @@ -537,9 +522,9 @@ watch-backend: go-check test: test-frontend test-backend .PHONY: test-backend -test-backend: | compute-go-test-packages +test-backend: @echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." - @TZ=UTC $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES) + @$(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES) .PHONY: test-remote-cacher test-remote-cacher: @@ -567,39 +552,20 @@ test-check: fi .PHONY: test\#% -test\#%: | compute-go-test-packages +test\#%: @echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." - @TZ=UTC $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES) + @$(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES) -coverage-merge: - rm -fr coverage/merged ; mkdir -p coverage/merged - $(GO) tool covdata merge -i `find coverage/data -name 'covmeta.*' | sed -e 's|/covmeta.*|,|' | tr -d '\n' | sed -e 's/,$$//'` -o coverage/merged +.PHONY: coverage +coverage: + grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' coverage.out > coverage-bodged.out + grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' integration.coverage.out > integration.coverage-bodged.out + $(GO) run build/gocovmerge.go integration.coverage-bodged.out coverage-bodged.out > coverage.all -coverage-convert: coverage-merge - $(GO) tool covdata textfmt -i=coverage/merged -o=coverage/textfmt.out - -coverage-show-html: coverage-convert - ( cd coverage ; $(GO) tool cover -html=textfmt.out -o coverage.html ) - xdg-open coverage/coverage.html - -coverage-show-percentage: coverage-convert - go tool cover -func=coverage/textfmt.out - -coverage-run: | compute-go-test-packages - contrib/coverage-helper.sh test_packages $(COVERAGE_TEST_PACKAGES) - -coverage-run-%: generate-ini-% | compute-migration-packages - # - # Migration tests go first - # - $(MAKE) GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/$*.ini COVERAGE_TEST_ARGS= COVERAGE_TEST_PACKAGES=forgejo.org/tests/integration/migration-test coverage-run - for pkg in $(MIGRATION_PACKAGES); do \ - $(MAKE) GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/$*.ini COVERAGE_TEST_DATABASE=$* COVERAGE_TEST_ARGS= COVERAGE_TEST_PACKAGES=$$pkg coverage-run ; \ - done - # - # All other integration tests follow - # - $(MAKE) GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/$*.ini COVERAGE_TEST_DATABASE=$* COVERAGE_TEST_PACKAGES=forgejo.org/tests/integration coverage-run +.PHONY: unit-test-coverage +unit-test-coverage: + @echo "Running unit-test-coverage $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." + @$(GOTEST) $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 .PHONY: tidy tidy: @@ -672,7 +638,6 @@ 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 @@ -719,7 +684,7 @@ test-e2e-mysql\#%: playwright e2e.mysql.test generate-ini-mysql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini $(GOTESTCOMPILEDRUNPREFIX) ./e2e.mysql.test $(GOTESTCOMPILEDRUNSUFFIX) -test.run TestE2e/$* .PHONY: test-e2e-pgsql -GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.initest-e2e-pgsql: playwright e2e.pgsql.test generate-ini-pgsql +test-e2e-pgsql: playwright e2e.pgsql.test generate-ini-pgsql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GOTESTCOMPILEDRUNPREFIX) ./e2e.pgsql.test $(GOTESTCOMPILEDRUNSUFFIX) -test.run TestE2e .PHONY: test-e2e-pgsql\#% @@ -742,6 +707,14 @@ bench-mysql: integrations.mysql.test generate-ini-mysql bench-pgsql: integrations.pgsql.test generate-ini-pgsql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench . +.PHONY: integration-test-coverage +integration-test-coverage: integrations.cover.test generate-ini-mysql + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out + +.PHONY: integration-test-coverage-sqlite +integration-test-coverage-sqlite: integrations.cover.sqlite.test generate-ini-sqlite + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.cover.sqlite.test -test.coverprofile=integration.coverage.out + integrations.mysql.test: git-check $(GO_SOURCES) $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration -o integrations.mysql.test @@ -751,6 +724,12 @@ integrations.pgsql.test: git-check $(GO_SOURCES) integrations.sqlite.test: git-check $(GO_SOURCES) $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration -o integrations.sqlite.test -tags '$(TEST_TAGS)' +integrations.cover.test: git-check $(GO_SOURCES) + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.test + +integrations.cover.sqlite.test: git-check $(GO_SOURCES) + $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.sqlite.test -tags '$(TEST_TAGS)' + .PHONY: migrations.mysql.test migrations.mysql.test: $(GO_SOURCES) generate-ini-mysql $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/integration/migration-test -o migrations.mysql.test @@ -767,7 +746,7 @@ migrations.sqlite.test: $(GO_SOURCES) generate-ini-sqlite GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTESTCOMPILEDRUNPREFIX) ./migrations.sqlite.test $(GOTESTCOMPILEDRUNSUFFIX) .PHONY: migrations.individual.mysql.test -migrations.individual.mysql.test: $(GO_SOURCES) | compute-migration-packages +migrations.individual.mysql.test: $(GO_SOURCES) for pkg in $(MIGRATION_PACKAGES); do \ GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' $$pkg || exit 1; \ done @@ -777,7 +756,7 @@ migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/migrations/$* .PHONY: migrations.individual.pgsql.test -migrations.individual.pgsql.test: $(GO_SOURCES) | compute-migration-packages +migrations.individual.pgsql.test: $(GO_SOURCES) for pkg in $(MIGRATION_PACKAGES); do \ GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' $$pkg || exit 1;\ done @@ -787,7 +766,7 @@ migrations.individual.pgsql.test\#%: $(GO_SOURCES) generate-ini-pgsql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/migrations/$* .PHONY: migrations.individual.sqlite.test -migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite | compute-migration-packages +migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite for pkg in $(MIGRATION_PACKAGES); do \ GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' $$pkg || exit 1; \ done @@ -961,7 +940,6 @@ 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/README.md b/README.md index e6f1b6c3d1..f95aebadeb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Hi there! Tired of big platforms playing monopoly? Providing Git hosting for your project, friends, company or community? **Forgejo** (/for'd͡ʒe.jo/ inspired by forĝejo – the Esperanto word for *forge*) has you covered with its intuitive interface, -light and easy hosting and a lot of built-in functionality. +light and easy hosting and a lot of builtin functionality. Forgejo was [created in 2022](https://forgejo.org/2022-12-15-hello-forgejo/) because we think that the project should be owned by an independent community. diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index c688045d1c..32f7b8c264 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](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). +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). ## 9.0.2 diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 494671f320..fb6c201a5e 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -34,141 +34,6 @@ "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/v11/act", - "path": "code.forgejo.org/forgejo/runner/v11/act/LICENSE", - "licenseText": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. \u003chttps://fsf.org/\u003e\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n Preamble\n\n The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works. By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users. We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors. You can apply it to\nyour programs, too.\n\n When we speak of free software, we are referring to freedom, not\nprice. Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights. Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received. You must make sure that they, too, receive\nor can get the source code. And you must show them these terms so they\nknow their rights.\n\n Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software. For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so. This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software. The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable. Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts. If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary. To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n The precise terms and conditions for copying, distribution and\nmodification follow.\n\n TERMS AND CONDITIONS\n\n 0. Definitions.\n\n \"This License\" refers to version 3 of the GNU General Public License.\n\n \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n \"The Program\" refers to any copyrightable work licensed under this\nLicense. Each licensee is addressed as \"you\". \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy. The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy. Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies. Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License. If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n 1. Source Code.\n\n The \"source code\" for a work means the preferred form of the work\nfor making modifications to it. \"Object code\" means any non-source\nform of a work.\n\n A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form. A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities. However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work. For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n The Corresponding Source for a work in source code form is that\nsame work.\n\n 2. Basic Permissions.\n\n All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met. This License explicitly affirms your unlimited\npermission to run the unmodified Program. The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work. This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force. You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright. Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n Conveying under any other circumstances is permitted solely under\nthe conditions stated below. Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n 4. Conveying Verbatim Copies.\n\n You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n 5. Conveying Modified Source Versions.\n\n You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n a) The work must carry prominent notices stating that you modified\n it, and giving a relevant date.\n\n b) The work must carry prominent notices stating that it is\n released under this License and any conditions added under section\n 7. This requirement modifies the requirement in section 4 to\n \"keep intact all notices\".\n\n c) You must license the entire work, as a whole, under this\n License to anyone who comes into possession of a copy. This\n License will therefore apply, along with any applicable section 7\n additional terms, to the whole of the work, and all its parts,\n regardless of how they are packaged. This License gives no\n permission to license the work in any other way, but it does not\n invalidate such permission if you have separately received it.\n\n d) If the work has interactive user interfaces, each must display\n Appropriate Legal Notices; however, if the Program has interactive\n interfaces that do not display Appropriate Legal Notices, your\n work need not make them do so.\n\n A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit. Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n 6. Conveying Non-Source Forms.\n\n You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n a) Convey the object code in, or embodied in, a physical product\n (including a physical distribution medium), accompanied by the\n Corresponding Source fixed on a durable physical medium\n customarily used for software interchange.\n\n b) Convey the object code in, or embodied in, a physical product\n (including a physical distribution medium), accompanied by a\n written offer, valid for at least three years and valid for as\n long as you offer spare parts or customer support for that product\n model, to give anyone who possesses the object code either (1) a\n copy of the Corresponding Source for all the software in the\n product that is covered by this License, on a durable physical\n medium customarily used for software interchange, for a price no\n more than your reasonable cost of physically performing this\n conveying of source, or (2) access to copy the\n Corresponding Source from a network server at no charge.\n\n c) Convey individual copies of the object code with a copy of the\n written offer to provide the Corresponding Source. This\n alternative is allowed only occasionally and noncommercially, and\n only if you received the object code with such an offer, in accord\n with subsection 6b.\n\n d) Convey the object code by offering access from a designated\n place (gratis or for a charge), and offer equivalent access to the\n Corresponding Source in the same way through the same place at no\n further charge. You need not require recipients to copy the\n Corresponding Source along with the object code. If the place to\n copy the object code is a network server, the Corresponding Source\n may be on a different server (operated by you or a third party)\n that supports equivalent copying facilities, provided you maintain\n clear directions next to the object code saying where to find the\n Corresponding Source. Regardless of what server hosts the\n Corresponding Source, you remain obligated to ensure that it is\n available for as long as needed to satisfy these requirements.\n\n e) Convey the object code using peer-to-peer transmission, provided\n you inform other peers where the object code and Corresponding\n Source of the work are being offered to the general public at no\n charge under subsection 6d.\n\n A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling. In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage. For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product. A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source. The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information. But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed. Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n 7. Additional Terms.\n\n \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law. If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit. (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.) You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n a) Disclaiming warranty or limiting liability differently from the\n terms of sections 15 and 16 of this License; or\n\n b) Requiring preservation of specified reasonable legal notices or\n author attributions in that material or in the Appropriate Legal\n Notices displayed by works containing it; or\n\n c) Prohibiting misrepresentation of the origin of that material, or\n requiring that modified versions of such material be marked in\n reasonable ways as different from the original version; or\n\n d) Limiting the use for publicity purposes of names of licensors or\n authors of the material; or\n\n e) Declining to grant rights under trademark law for use of some\n trade names, trademarks, or service marks; or\n\n f) Requiring indemnification of licensors and authors of that\n material by anyone who conveys the material (or modified versions of\n it) with contractual assumptions of liability to the recipient, for\n any liability that these contractual assumptions directly impose on\n those licensors and authors.\n\n All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10. If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term. If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n 8. Termination.\n\n You may not propagate or modify a covered work except as expressly\nprovided under this License. Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License. If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n 9. Acceptance Not Required for Having Copies.\n\n You are not required to accept this License in order to receive or\nrun a copy of the Program. Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance. However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work. These actions infringe copyright if you do\nnot accept this License. Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n 10. Automatic Licensing of Downstream Recipients.\n\n Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License. You are not responsible\nfor enforcing compliance by third parties with this License.\n\n An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations. If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License. For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n 11. Patents.\n\n A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based. The\nwork thus licensed is called the contributor's \"contributor version\".\n\n A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version. For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement). To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients. \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License. You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n 12. No Surrender of Others' Freedom.\n\n If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License. If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all. For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n 13. Use with the GNU Affero General Public License.\n\n Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work. The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n 14. Revised Versions of this License.\n\n The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time. Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n Each version is given a distinguishing version number. If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation. If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n Later license versions may give you additional or different\npermissions. However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n 15. Disclaimer of Warranty.\n\n THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n 16. Limitation of Liability.\n\n IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n 17. Interpretation of Sections 15 and 16.\n\n If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n END OF TERMS AND CONDITIONS\n\n How to Apply These Terms to Your New Programs\n\n If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n To do so, attach the following notices to the program. It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n \u003cone line to give the program's name and a brief idea of what it does.\u003e\n Copyright (C) \u003cyear\u003e \u003cname of author\u003e\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n \u003cprogram\u003e Copyright (C) \u003cyear\u003e \u003cname of author\u003e\n This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n This is free software, and you are welcome to redistribute it\n under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License. Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n\u003chttps://www.gnu.org/licenses/\u003e.\n\n The GNU General Public License does not permit incorporating your program\ninto proprietary programs. If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library. If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License. But first, please read\n\u003chttps://www.gnu.org/licenses/why-not-lgpl.html\u003e.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/lookpath", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/lookpath/LICENSE", - "licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n\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": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@actions/core", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@actions/core/LICENSE.md", - "licenseText": "The MIT License (MIT)\n\nCopyright 2019 GitHub\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@actions/github/node_modules/@actions/http-client", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@actions/github/node_modules/@actions/http-client/LICENSE", - "licenseText": "Actions Http Client for Node.js\n\nCopyright (c) GitHub, Inc.\n\nAll rights reserved.\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and\nassociated documentation files (the \"Software\"), to deal in the Software without restriction,\nincluding without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,\nand/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@actions/http-client", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@actions/http-client/LICENSE", - "licenseText": "Actions Http Client for Node.js\n\nCopyright (c) GitHub, Inc.\n\nAll rights reserved.\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and\nassociated documentation files (the \"Software\"), to deal in the Software without restriction,\nincluding without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,\nand/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\nLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/auth-token", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/auth-token/LICENSE", - "licenseText": "The MIT License\n\nCopyright (c) 2019 Octokit contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/core", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/core/LICENSE", - "licenseText": "The MIT License\n\nCopyright (c) 2019 Octokit contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/endpoint", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/endpoint/LICENSE", - "licenseText": "The MIT License\n\nCopyright (c) 2018 Octokit contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/graphql", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/graphql/LICENSE", - "licenseText": "The MIT License\n\nCopyright (c) 2018 Octokit contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/openapi-types", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/openapi-types/LICENSE", - "licenseText": "Copyright 2020 Gregor Martynus\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/plugin-paginate-rest", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/plugin-paginate-rest/LICENSE", - "licenseText": "MIT License Copyright (c) 2019 Octokit contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/plugin-rest-endpoint-methods", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/plugin-rest-endpoint-methods/LICENSE", - "licenseText": "MIT License Copyright (c) 2019 Octokit contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/request-error", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/request-error/LICENSE", - "licenseText": "The MIT License\n\nCopyright (c) 2019 Octokit contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/request", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/request/LICENSE", - "licenseText": "The MIT License\n\nCopyright (c) 2018 Octokit contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/types", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@octokit/types/LICENSE", - "licenseText": "MIT License Copyright (c) 2019 Octokit contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@vercel/ncc", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/@vercel/ncc/LICENSE", - "licenseText": "Copyright 2018 ZEIT, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/before-after-hook", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/before-after-hook/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 2018 Gregor Martynus and other contributors.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/deprecation", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/deprecation/LICENSE", - "licenseText": "The ISC License\n\nCopyright (c) Gregor Martynus and contributors\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR\nIN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/is-plain-object", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/is-plain-object/LICENSE", - "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014-2017, Jon Schlinkert.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/node-fetch", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/node-fetch/LICENSE.md", - "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 David Frank\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": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/once", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/once/LICENSE", - "licenseText": "The ISC License\n\nCopyright (c) Isaac Z. Schlueter and Contributors\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR\nIN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/tunnel", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/tunnel/LICENSE", - "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2012 Koichi Kobayashi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/universal-user-agent", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/universal-user-agent/LICENSE.md", - "licenseText": "# [ISC License](https://spdx.org/licenses/ISC)\n\nCopyright (c) 2018, Gregor Martynus (https://github.com/gr2m)\n\nPermission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/uuid", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/uuid/LICENSE.md", - "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2010-2020 Robert Kieffer and other contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/webidl-conversions", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/webidl-conversions/LICENSE.md", - "licenseText": "# The BSD 2-Clause License\n\nCopyright (c) 2014, Domenic Denicola\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/whatwg-url", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/whatwg-url/LICENSE.txt", - "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015–2016 Sebastian Mayr\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, - { - "name": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/wrappy", - "path": "code.forgejo.org/forgejo/runner/v11/act/act/runner/testdata/actions/node20/node_modules/wrappy/LICENSE", - "licenseText": "The ISC License\n\nCopyright (c) Isaac Z. Schlueter and Contributors\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR\nIN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n" - }, { "name": "code.forgejo.org/go-chi/binding", "path": "code.forgejo.org/go-chi/binding/LICENSE", @@ -249,11 +114,6 @@ "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", @@ -384,26 +244,6 @@ "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", @@ -724,21 +564,11 @@ "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", @@ -765,8 +595,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/inbucket/html2text", - "path": "github.com/inbucket/html2text/LICENSE", + "name": "github.com/jaytaylor/html2text", + "path": "github.com/jaytaylor/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" }, { @@ -874,11 +704,6 @@ "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", @@ -890,8 +715,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/archives", - "path": "github.com/mholt/archives/LICENSE", + "name": "github.com/mholt/archiver/v3", + "path": "github.com/mholt/archiver/v3/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." }, { @@ -904,11 +729,6 @@ "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", @@ -924,11 +744,6 @@ "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", @@ -949,26 +764,21 @@ "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/v2", - "path": "github.com/nwaples/rardecode/v2/LICENSE", + "name": "github.com/nwaples/rardecode", + "path": "github.com/nwaples/rardecode/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", @@ -1094,11 +904,6 @@ "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", @@ -1194,16 +999,6 @@ "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": "go.yaml.in/yaml/v3", - "path": "go.yaml.in/yaml/v3/LICENSE", - "licenseText": "\nThis project is covered by two different licenses: MIT and Apache.\n\n#### MIT License ####\n\nThe following files were ported to Go from C files of libyaml, and thus\nare still covered by their original MIT license, with the additional\ncopyright staring in 2011 when the project was ported over:\n\n apic.go emitterc.go parserc.go readerc.go scannerc.go\n writerc.go yamlh.go yamlprivateh.go\n\nCopyright (c) 2006-2010 Kirill Simonov\nCopyright (c) 2006-2011 Kirill Simonov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, 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### Apache License ###\n\nAll the remaining project files are covered by the Apache license:\n\nCopyright (c) 2011-2019 Canonical Ltd\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\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/build/lint-locale-usage/allowed-masked-usage.txt b/build/lint-locale-usage/allowed-masked-usage.txt deleted file mode 100644 index 06675530db..0000000000 --- a/build/lint-locale-usage/allowed-masked-usage.txt +++ /dev/null @@ -1,54 +0,0 @@ -# translation tooling test keys -meta.last_line -translation_meta.test - -# models/admin/task.go: instances of $TranslatableMessage.Format -# this also gets instantiated as a Messenger once -repo.migrate.migrating_failed.error - -# models/asymkey/gpg_key_object_verification.go: $ObjectVerification.Reason -# unfortunately, it is non-trivial to parse all the occurences -gpg.error.extract_sign -gpg.error.failed_retrieval_gpg_keys -gpg.error.generate_hash -gpg.error.no_committer_account - -# models/system/notice.go: func (n *Notice) TrStr() string -admin.notices.type_1 -admin.notices.type_2 - -# modules/setting/ui.go -themes.names. - -# services/context/context.go -relativetime. - -# templates/mail/issue/default.tmpl: $.locale.Tr -mail.issue.in_tree_path - -# templates/package/metadata/arch.tmpl: $.locale.Tr -packages.details.license - -# templates/repo/issue/view_content.tmpl: indirection via $closeTranslationKey -repo.issues.close -repo.pulls.close - -# templates/repo/issue/view_content/comments.tmpl: indirection via $refTr -repo.issues.ref_closing_from -repo.issues.ref_issue_from -repo.issues.ref_pull_from -repo.issues.ref_reopening_from - -# templates/repo/issue/view_content/comments.tmpl: ctx.Locale.Tr (printf "projects.type-%d.display_name" .OldProject.Type) -projects. -projects.type-1.display_name -projects.type-2.display_name -projects.type-3.display_name - -# templates/repo/settings/webhook/link_menu.tmpl, templates/webhook/new.tmpl: repo.settings.web_hook_name_ -# tests/integration/repo_archive_text_test.go -repo.settings. - -# services/migrations/migrate.go: messenger calls -# ToDo: give them a unique prefix -repo.migrate. diff --git a/build/lint-locale-usage/handle-go.go b/build/lint-locale-usage/handle-go.go deleted file mode 100644 index a91a210313..0000000000 --- a/build/lint-locale-usage/handle-go.go +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package main - -import ( - "fmt" - "go/ast" - goParser "go/parser" - "go/token" - "strconv" - "strings" -) - -func (handler Handler) handleGoTrBasicLit(fset *token.FileSet, argLit *ast.BasicLit, prefix string) { - if argLit.Kind == token.STRING { - // extract string content - arg, err := strconv.Unquote(argLit.Value) - if err != nil { - return - } - // found interesting strings - arg = prefix + arg - if strings.HasSuffix(arg, ".") || strings.HasSuffix(arg, "_") { - prep, trunc := PrepareMsgidPrefix(arg) - if trunc { - handler.OnWarning(fset, argLit.ValuePos, fmt.Sprintf("needed to truncate message id prefix: %s", arg)) - } - handler.OnMsgidPrefix(fset, argLit.ValuePos, prep, trunc) - } else { - handler.OnMsgid(fset, argLit.ValuePos, arg) - } - } -} - -func (handler Handler) handleGoTrArgument(fset *token.FileSet, n ast.Expr, prefix string) { - if argLit, ok := n.(*ast.BasicLit); ok { - handler.handleGoTrBasicLit(fset, argLit, prefix) - } else if argBinExpr, ok := n.(*ast.BinaryExpr); ok { - if argBinExpr.Op != token.ADD { - // pass - } else if argLit, ok := argBinExpr.X.(*ast.BasicLit); ok && argLit.Kind == token.STRING { - // extract string content - arg, err := strconv.Unquote(argLit.Value) - if err != nil { - return - } - // found interesting strings - arg = prefix + arg - prep, trunc := PrepareMsgidPrefix(arg) - if trunc { - handler.OnWarning(fset, argLit.ValuePos, fmt.Sprintf("needed to truncate message id prefix: %s", arg)) - } - handler.OnMsgidPrefix(fset, argLit.ValuePos, prep, trunc) - } - } -} - -func (handler Handler) handleGoCommentGroup(fset *token.FileSet, cg *ast.CommentGroup, commentPrefix string) *string { - if cg == nil { - return nil - } - var matches []token.Pos - matchInsPrefix := "" - commentPrefix = "//" + commentPrefix - for _, comment := range cg.List { - ctxt := strings.TrimSpace(comment.Text) - if ctxt == commentPrefix { - matches = append(matches, comment.Slash) - } else if after, found := strings.CutPrefix(ctxt, commentPrefix+"Suffix "); found { - matches = append(matches, comment.Slash) - matchInsPrefix = strings.TrimSpace(after) - } - } - switch len(matches) { - case 0: - return nil - case 1: - return &matchInsPrefix - default: - handler.OnWarning( - fset, - matches[0], - fmt.Sprintf("encountered multiple %s... directives, ignoring", strings.TrimSpace(commentPrefix)), - ) - return &matchInsPrefix - } -} - -// the `Handle*File` functions follow the following calling convention: -// * `fname` is the name of the input file -// * `src` is either `nil` (then the function invokes `ReadFile` to read the file) -// or the contents of the file as {`[]byte`, or a `string`} - -func (handler Handler) HandleGoFile(fname string, src any) error { - fset := token.NewFileSet() - node, err := goParser.ParseFile(fset, fname, src, goParser.SkipObjectResolution|goParser.ParseComments) - if err != nil { - return LocatedError{ - Location: fname, - Kind: "Go parser", - Err: err, - } - } - - ast.Inspect(node, func(n ast.Node) bool { - // search for function calls of the form `anything.Tr(any-string-lit, ...)` - - switch n2 := n.(type) { - case *ast.CallExpr: - if len(n2.Args) == 0 { - return true - } - funSel, ok := n2.Fun.(*ast.SelectorExpr) - if !ok { - return true - } - - ltf, ok := handler.LocaleTrFunctions[funSel.Sel.Name] - if !ok { - return true - } - - var gotUnexpectedInvoke *int - - for _, argNum := range ltf { - if len(n2.Args) <= int(argNum) { - argc := len(n2.Args) - gotUnexpectedInvoke = &argc - } else { - handler.handleGoTrArgument(fset, n2.Args[int(argNum)], "") - } - } - - if gotUnexpectedInvoke != nil { - handler.OnUnexpectedInvoke(fset, funSel.Sel.NamePos, funSel.Sel.Name, *gotUnexpectedInvoke) - } - case *ast.CompositeLit: - ident, ok := n2.Type.(*ast.Ident) - if !ok { - return true - } - - // special case: models/unit/unit.go - if strings.HasSuffix(fname, "unit.go") && ident.Name == "Unit" { - if len(n2.Elts) != 6 { - handler.OnWarning(fset, n2.Pos(), "unexpected initialization of 'Unit' (unexpected number of arguments)") - } - // NameKey has index 2 - // invoked like '{{ctx.Locale.Tr $unit.NameKey}}' - nameKey, ok := n2.Elts[2].(*ast.BasicLit) - if !ok || nameKey.Kind != token.STRING { - handler.OnWarning(fset, n2.Elts[2].Pos(), "unexpected initialization of 'Unit' (expected string literal as NameKey)") - return true - } - - // extract string content - arg, err := strconv.Unquote(nameKey.Value) - if err == nil { - // found interesting strings - handler.OnMsgid(fset, nameKey.ValuePos, arg) - } - } - case *ast.FuncDecl: - matchInsPrefix := handler.handleGoCommentGroup(fset, n2.Doc, "llu:returnsTrKey") - if matchInsPrefix == nil { - return true - } - results := n2.Type.Results.List - if len(results) != 1 { - handler.OnWarning(fset, n2.Type.Func, fmt.Sprintf("function %s has unexpected return type; expected single return value", n2.Name.Name)) - return true - } - - ast.Inspect(n2.Body, func(n ast.Node) bool { - // search for return stmts - // TODO: what about nested functions? - if ret, ok := n.(*ast.ReturnStmt); ok { - for _, res := range ret.Results { - ast.Inspect(res, func(n ast.Node) bool { - if expr, ok := n.(ast.Expr); ok { - handler.handleGoTrArgument(fset, expr, *matchInsPrefix) - } - return true - }) - } - return false - } - return true - }) - return true - case *ast.GenDecl: - if !(n2.Tok == token.CONST || n2.Tok == token.VAR) { - return true - } - matchInsPrefix := handler.handleGoCommentGroup(fset, n2.Doc, " llu:TrKeys") - if matchInsPrefix == nil { - return true - } - for _, spec := range n2.Specs { - // interpret all contained strings as message IDs - ast.Inspect(spec, func(n ast.Node) bool { - if argLit, ok := n.(*ast.BasicLit); ok { - handler.handleGoTrBasicLit(fset, argLit, *matchInsPrefix) - return false - } - return true - }) - } - } - - return true - }) - - return nil -} diff --git a/build/lint-locale-usage/handle-tmpl.go b/build/lint-locale-usage/handle-tmpl.go deleted file mode 100644 index a71d7d47e3..0000000000 --- a/build/lint-locale-usage/handle-tmpl.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package main - -import ( - "fmt" - "go/token" - "os" - "strings" - "text/template" - tmplParser "text/template/parse" - - fjTemplates "forgejo.org/modules/templates" - "forgejo.org/modules/util" -) - -// derived from source: modules/templates/scopedtmpl/scopedtmpl.go, L169-L213 -func (handler Handler) handleTemplateNode(fset *token.FileSet, node tmplParser.Node) { - switch node.Type() { - case tmplParser.NodeAction: - handler.handleTemplatePipeNode(fset, node.(*tmplParser.ActionNode).Pipe) - case tmplParser.NodeList: - nodeList := node.(*tmplParser.ListNode) - handler.handleTemplateFileNodes(fset, nodeList.Nodes) - case tmplParser.NodePipe: - handler.handleTemplatePipeNode(fset, node.(*tmplParser.PipeNode)) - case tmplParser.NodeTemplate: - handler.handleTemplatePipeNode(fset, node.(*tmplParser.TemplateNode).Pipe) - case tmplParser.NodeIf: - nodeIf := node.(*tmplParser.IfNode) - handler.handleTemplateBranchNode(fset, nodeIf.BranchNode) - case tmplParser.NodeRange: - nodeRange := node.(*tmplParser.RangeNode) - handler.handleTemplateBranchNode(fset, nodeRange.BranchNode) - case tmplParser.NodeWith: - nodeWith := node.(*tmplParser.WithNode) - handler.handleTemplateBranchNode(fset, nodeWith.BranchNode) - - case tmplParser.NodeCommand: - nodeCommand := node.(*tmplParser.CommandNode) - - handler.handleTemplateFileNodes(fset, nodeCommand.Args) - - if len(nodeCommand.Args) < 2 { - return - } - - funcname := "" - if nodeChain, ok := nodeCommand.Args[0].(*tmplParser.ChainNode); ok { - if nodeIdent, ok := nodeChain.Node.(*tmplParser.IdentifierNode); ok { - if nodeIdent.Ident != "ctx" || len(nodeChain.Field) != 2 || nodeChain.Field[0] != "Locale" { - return - } - funcname = nodeChain.Field[1] - } - } else if nodeField, ok := nodeCommand.Args[0].(*tmplParser.FieldNode); ok { - if len(nodeField.Ident) != 2 || !(nodeField.Ident[0] == "locale" || nodeField.Ident[0] == "Locale") { - return - } - funcname = nodeField.Ident[1] - } - - var gotUnexpectedInvoke *int - ltf, ok := handler.LocaleTrFunctions[funcname] - if !ok { - return - } - - for _, argNum := range ltf { - if len(nodeCommand.Args) >= int(argNum+2) { - handler.handleTemplateMsgid(fset, nodeCommand.Args[int(argNum+1)]) - } else { - argc := len(nodeCommand.Args) - 1 - gotUnexpectedInvoke = &argc - } - } - - if gotUnexpectedInvoke != nil { - handler.OnUnexpectedInvoke(fset, token.Pos(nodeCommand.Pos), funcname, *gotUnexpectedInvoke) - } - - default: - } -} - -func (handler Handler) handleTemplateMsgid(fset *token.FileSet, node tmplParser.Node) { - // the column numbers are a bit "off", but much better than nothing - pos := token.Pos(node.Position()) - - switch node.Type() { - case tmplParser.NodeString: - nodeString := node.(*tmplParser.StringNode) - // found interesting strings - handler.OnMsgid(fset, pos, nodeString.Text) - - case tmplParser.NodePipe: - nodePipe := node.(*tmplParser.PipeNode) - handler.handleTemplatePipeNode(fset, nodePipe) - - if len(nodePipe.Cmds) == 0 { - handler.OnWarning(fset, pos, fmt.Sprintf("unsupported invocation of locate function (no commands): %s", node.String())) - } else if len(nodePipe.Cmds) != 1 { - handler.OnWarning(fset, pos, fmt.Sprintf("unsupported invocation of locate function (too many commands): %s", node.String())) - return - } - nodeCommand := nodePipe.Cmds[0] - if len(nodeCommand.Args) < 2 { - handler.OnWarning(fset, pos, fmt.Sprintf("unsupported invocation of locate function (not enough arguments): %s", node.String())) - return - } - - nodeIdent, ok := nodeCommand.Args[0].(*tmplParser.IdentifierNode) - if !ok || (nodeIdent.Ident != "print" && nodeIdent.Ident != "printf") { - // handler.OnWarning(fset, pos, fmt.Sprintf("unsupported invocation of locate function (bad command): %s", node.String())) - return - } - - nodeString, ok := nodeCommand.Args[1].(*tmplParser.StringNode) - if !ok { - //handler.OnWarning( - // fset, - // pos, - // fmt.Sprintf("unsupported invocation of locate function (string should be first argument to %s): %s", nodeIdent.Ident, node.String()), - //) - return - } - - msgidPrefix := nodeString.Text - stringPos := token.Pos(nodeString.Pos) - - if len(nodeCommand.Args) == 2 { - // found interesting strings - handler.OnMsgid(fset, stringPos, msgidPrefix) - } else { - if nodeIdent.Ident == "printf" { - parts := strings.SplitN(msgidPrefix, "%", 2) - if len(parts) != 2 { - handler.OnWarning( - fset, - stringPos, - fmt.Sprintf("unsupported invocation of locate function (format string doesn't match \"prefix%%smth\" pattern): %s", nodeString.String()), - ) - return - } - msgidPrefix = parts[0] - } - - msgidPrefixFin, truncated := PrepareMsgidPrefix(msgidPrefix) - if truncated { - handler.OnWarning(fset, stringPos, fmt.Sprintf("needed to truncate message id prefix: %s", msgidPrefix)) - } - - // found interesting strings - handler.OnMsgidPrefix(fset, stringPos, msgidPrefixFin, truncated) - } - - default: - // handler.OnWarning(fset, pos, fmt.Sprintf("unknown invocation of locate function: %s", node.String())) - } -} - -func (handler Handler) handleTemplatePipeNode(fset *token.FileSet, pipeNode *tmplParser.PipeNode) { - if pipeNode == nil { - return - } - - // NOTE: we can't pass `pipeNode.Cmds` to handleTemplateFileNodes due to incompatible argument types - for _, node := range pipeNode.Cmds { - handler.handleTemplateNode(fset, node) - } -} - -func (handler Handler) handleTemplateBranchNode(fset *token.FileSet, branchNode tmplParser.BranchNode) { - handler.handleTemplatePipeNode(fset, branchNode.Pipe) - handler.handleTemplateFileNodes(fset, branchNode.List.Nodes) - if branchNode.ElseList != nil { - handler.handleTemplateFileNodes(fset, branchNode.ElseList.Nodes) - } -} - -func (handler Handler) handleTemplateFileNodes(fset *token.FileSet, nodes []tmplParser.Node) { - for _, node := range nodes { - handler.handleTemplateNode(fset, node) - } -} - -// the `Handle*File` functions follow the following calling convention: -// * `fname` is the name of the input file -// * `src` is either `nil` (then the function invokes `ReadFile` to read the file) -// or the contents of the file as {`[]byte`, or a `string`} - -func (handler Handler) HandleTemplateFile(fname string, src any) error { - var tmplContent []byte - switch src2 := src.(type) { - case nil: - var err error - tmplContent, err = os.ReadFile(fname) - if err != nil { - return LocatedError{ - Location: fname, - Kind: "ReadFile", - Err: err, - } - } - case []byte: - tmplContent = src2 - case string: - // SAFETY: we do not modify tmplContent below - tmplContent = util.UnsafeStringToBytes(src2) - default: - panic("invalid type for 'src'") - } - - fset := token.NewFileSet() - fset.AddFile(fname, 1, len(tmplContent)).SetLinesForContent(tmplContent) - // SAFETY: we do not modify tmplContent2 below - tmplContent2 := util.UnsafeBytesToString(tmplContent) - - tmpl := template.New(fname) - tmpl.Funcs(fjTemplates.NewFuncMap()) - tmplParsed, err := tmpl.Parse(tmplContent2) - if err != nil { - return LocatedError{ - Location: fname, - Kind: "Template parser", - Err: err, - } - } - handler.handleTemplateFileNodes(fset, tmplParsed.Root.Nodes) - return nil -} diff --git a/build/lint-locale-usage/lint-locale-usage.go b/build/lint-locale-usage/lint-locale-usage.go index 8c67a781e9..88375c1c36 100644 --- a/build/lint-locale-usage/lint-locale-usage.go +++ b/build/lint-locale-usage/lint-locale-usage.go @@ -5,19 +5,22 @@ package main import ( - "bufio" - "errors" - "flag" "fmt" + "go/ast" + goParser "go/parser" "go/token" "io/fs" "os" "path/filepath" - "sort" + "strconv" "strings" + "text/template" + tmplParser "text/template/parse" "forgejo.org/modules/container" + fjTemplates "forgejo.org/modules/templates" "forgejo.org/modules/translation/localeiter" + "forgejo.org/modules/util" ) // this works by first gathering all valid source string IDs from `en-US` reference files @@ -60,180 +63,241 @@ func InitLocaleTrFunctions() map[string][]uint { type Handler struct { OnMsgid func(fset *token.FileSet, pos token.Pos, msgid string) - OnMsgidPrefix func(fset *token.FileSet, pos token.Pos, msgidPrefix string, truncated bool) OnUnexpectedInvoke func(fset *token.FileSet, pos token.Pos, funcname string, argc int) - OnWarning func(fset *token.FileSet, pos token.Pos, msg string) LocaleTrFunctions map[string][]uint } -type StringTrie interface { - Matches(key []string) bool -} +// the `Handle*File` functions follow the following calling convention: +// * `fname` is the name of the input file +// * `src` is either `nil` (then the function invokes `ReadFile` to read the file) +// or the contents of the file as {`[]byte`, or a `string`} -type StringTrieMap map[string]StringTrie - -func (m StringTrieMap) Matches(key []string) bool { - if len(key) == 0 || m == nil { - return true - } - value, ok := m[key[0]] - if !ok { - return false - } - if value == nil { - return true - } - return value.Matches(key[1:]) -} - -func (m StringTrieMap) Insert(key []string) { - if m == nil { - return - } - - switch len(key) { - case 0: - return - - case 1: - m[key[0]] = nil - - default: - if value, ok := m[key[0]]; ok { - if value == nil { - return - } - } else { - m[key[0]] = make(StringTrieMap) - } - m[key[0]].(StringTrieMap).Insert(key[1:]) - } -} - -func ParseAllowedMaskedUsages(fname string, usedMsgids container.Set[string], allowedMaskedPrefixes StringTrieMap, chkMsgid func(msgid string) bool) error { - file, err := os.Open(fname) +func (handler Handler) HandleGoFile(fname string, src any) error { + fset := token.NewFileSet() + node, err := goParser.ParseFile(fset, fname, src, goParser.SkipObjectResolution) if err != nil { return LocatedError{ Location: fname, - Kind: "Open", + Kind: "Go parser", Err: err, } } - defer file.Close() - scanner := bufio.NewScanner(file) - lno := 0 - for scanner.Scan() { - lno++ - line := strings.TrimSpace(scanner.Text()) - if line == "" || strings.HasPrefix(line, "#") { - continue + ast.Inspect(node, func(n ast.Node) bool { + // search for function calls of the form `anything.Tr(any-string-lit, ...)` + + call, ok := n.(*ast.CallExpr) + if !ok || len(call.Args) < 1 { + return true } - if linePrefix, found := strings.CutSuffix(line, "."); found { - allowedMaskedPrefixes.Insert(strings.Split(linePrefix, ".")) - } else { - if !chkMsgid(line) { - return LocatedError{ - Location: fmt.Sprintf("%s: line %d", fname, lno), - Kind: "undefined msgid", - Err: errors.New(line), + + funSel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + + ltf, ok := handler.LocaleTrFunctions[funSel.Sel.Name] + if !ok { + return true + } + + var gotUnexpectedInvoke *int + + for _, argNum := range ltf { + if len(call.Args) >= int(argNum+1) { + argLit, ok := call.Args[int(argNum)].(*ast.BasicLit) + if !ok || argLit.Kind != token.STRING { + continue } + + // extract string content + arg, err := strconv.Unquote(argLit.Value) + if err == nil { + // found interesting strings + handler.OnMsgid(fset, argLit.ValuePos, arg) + } + } else { + argc := len(call.Args) + gotUnexpectedInvoke = &argc } - usedMsgids.Add(line) } - } - if err := scanner.Err(); err != nil { - return LocatedError{ - Location: fname, - Kind: "Scanner", - Err: err, + + if gotUnexpectedInvoke != nil { + handler.OnUnexpectedInvoke(fset, funSel.Sel.NamePos, funSel.Sel.Name, *gotUnexpectedInvoke) } - } + + return true + }) + return nil } -// Truncating a message id prefix to the last dot -func PrepareMsgidPrefix(s string) (string, bool) { - index := strings.LastIndexByte(s, 0x2e) - if index == -1 { - return "", true +// derived from source: modules/templates/scopedtmpl/scopedtmpl.go, L169-L213 +func (handler Handler) handleTemplateNode(fset *token.FileSet, node tmplParser.Node) { + switch node.Type() { + case tmplParser.NodeAction: + handler.handleTemplatePipeNode(fset, node.(*tmplParser.ActionNode).Pipe) + case tmplParser.NodeList: + nodeList := node.(*tmplParser.ListNode) + handler.handleTemplateFileNodes(fset, nodeList.Nodes) + case tmplParser.NodePipe: + handler.handleTemplatePipeNode(fset, node.(*tmplParser.PipeNode)) + case tmplParser.NodeTemplate: + handler.handleTemplatePipeNode(fset, node.(*tmplParser.TemplateNode).Pipe) + case tmplParser.NodeIf: + nodeIf := node.(*tmplParser.IfNode) + handler.handleTemplateBranchNode(fset, nodeIf.BranchNode) + case tmplParser.NodeRange: + nodeRange := node.(*tmplParser.RangeNode) + handler.handleTemplateBranchNode(fset, nodeRange.BranchNode) + case tmplParser.NodeWith: + nodeWith := node.(*tmplParser.WithNode) + handler.handleTemplateBranchNode(fset, nodeWith.BranchNode) + + case tmplParser.NodeCommand: + nodeCommand := node.(*tmplParser.CommandNode) + + handler.handleTemplateFileNodes(fset, nodeCommand.Args) + + if len(nodeCommand.Args) < 2 { + return + } + + nodeChain, ok := nodeCommand.Args[0].(*tmplParser.ChainNode) + if !ok { + return + } + + nodeIdent, ok := nodeChain.Node.(*tmplParser.IdentifierNode) + if !ok || nodeIdent.Ident != "ctx" || len(nodeChain.Field) != 2 || nodeChain.Field[0] != "Locale" { + return + } + + ltf, ok := handler.LocaleTrFunctions[nodeChain.Field[1]] + if !ok { + return + } + + var gotUnexpectedInvoke *int + + for _, argNum := range ltf { + if len(nodeCommand.Args) >= int(argNum+2) { + nodeString, ok := nodeCommand.Args[int(argNum+1)].(*tmplParser.StringNode) + if ok { + // found interesting strings + // the column numbers are a bit "off", but much better than nothing + handler.OnMsgid(fset, token.Pos(nodeString.Pos), nodeString.Text) + } + } else { + argc := len(nodeCommand.Args) - 1 + gotUnexpectedInvoke = &argc + } + } + + if gotUnexpectedInvoke != nil { + handler.OnUnexpectedInvoke(fset, token.Pos(nodeChain.Pos), nodeChain.Field[1], *gotUnexpectedInvoke) + } + + default: } - return s[:index], index != len(s)-1 } -func Usage() { - outp := flag.CommandLine.Output() - fmt.Fprintf(outp, "Usage of %s:\n", os.Args[0]) - flag.PrintDefaults() - - fmt.Fprintf(outp, "\nThis command assumes that it gets started from the project root directory.\n") - - fmt.Fprintf(outp, "\nExit codes:\n") - for _, i := range []string{ - "0\tsuccess, no issues found", - "1\tunable to walk directory tree", - "2\tunable to parse locale ini/json files", - "3\tunable to parse go or text/template files", - "4\tfound missing message IDs", - "5\tfound unused message IDs", - } { - fmt.Fprintf(outp, "\t%s\n", i) +func (handler Handler) handleTemplatePipeNode(fset *token.FileSet, pipeNode *tmplParser.PipeNode) { + if pipeNode == nil { + return } - fmt.Fprintf(outp, "\nSpecial Go doc comments:\n") - for _, i := range []string{ - "//llu:returnsTrKey", - "\tcan be used in front of functions to indicate", - "\tthat the function returns message IDs", - "\tWARNING: this currently doesn't support nested functions properly", - "", - "//llu:returnsTrKeySuffix prefix.", - "\tsimilar to llu:returnsTrKey, but the given prefix is prepended", - "\tto the found strings before interpreting them as msgids", - "", - "// llu:TrKeys", - "\tcan be used in front of 'const' and 'var' blocks", - "\tin order to mark all contained strings as message IDs", - "", - "// llu:TrKeysSuffix prefix.", - "\tlike llu:returnsTrKeySuffix, but for 'const' and 'var' blocks", - } { - if i == "" { - fmt.Fprintf(outp, "\n") - } else { - fmt.Fprintf(outp, "\t%s\n", i) + // NOTE: we can't pass `pipeNode.Cmds` to handleTemplateFileNodes due to incompatible argument types + for _, node := range pipeNode.Cmds { + handler.handleTemplateNode(fset, node) + } +} + +func (handler Handler) handleTemplateBranchNode(fset *token.FileSet, branchNode tmplParser.BranchNode) { + handler.handleTemplatePipeNode(fset, branchNode.Pipe) + handler.handleTemplateFileNodes(fset, branchNode.List.Nodes) + if branchNode.ElseList != nil { + handler.handleTemplateFileNodes(fset, branchNode.ElseList.Nodes) + } +} + +func (handler Handler) handleTemplateFileNodes(fset *token.FileSet, nodes []tmplParser.Node) { + for _, node := range nodes { + handler.handleTemplateNode(fset, node) + } +} + +func (handler Handler) HandleTemplateFile(fname string, src any) error { + var tmplContent []byte + switch src2 := src.(type) { + case nil: + var err error + tmplContent, err = os.ReadFile(fname) + if err != nil { + return LocatedError{ + Location: fname, + Kind: "ReadFile", + Err: err, + } + } + case []byte: + tmplContent = src2 + case string: + // SAFETY: we do not modify tmplContent below + tmplContent = util.UnsafeStringToBytes(src2) + default: + panic("invalid type for 'src'") + } + + fset := token.NewFileSet() + fset.AddFile(fname, 1, len(tmplContent)).SetLinesForContent(tmplContent) + // SAFETY: we do not modify tmplContent2 below + tmplContent2 := util.UnsafeBytesToString(tmplContent) + + tmpl := template.New(fname) + tmpl.Funcs(fjTemplates.NewFuncMap()) + tmplParsed, err := tmpl.Parse(tmplContent2) + if err != nil { + return LocatedError{ + Location: fname, + Kind: "Template parser", + Err: err, } } + handler.handleTemplateFileNodes(fset, tmplParsed.Root.Nodes) + return nil } +// This command assumes that we get started from the project root directory +// +// Possible command line flags: +// +// --allow-missing-msgids don't return an error code if missing message IDs are found +// +// EXIT CODES: +// +// 0 success, no issues found +// 1 unable to walk directory tree +// 2 unable to parse locale ini/json files +// 3 unable to parse go or text/template files +// 4 found missing message IDs +// //nolint:forbidigo func main() { allowMissingMsgids := false - allowUnusedMsgids := false - usedMsgids := make(container.Set[string]) - allowedMaskedPrefixes := make(StringTrieMap) - - // It's possible for execl to hand us an empty os.Args. - if len(os.Args) == 0 { - flag.CommandLine = flag.NewFlagSet("lint-locale-usage", flag.ExitOnError) - } else { - flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) + for _, arg := range os.Args[1:] { + if arg == "--allow-missing-msgids" { + allowMissingMsgids = true + } } - flag.CommandLine.Usage = Usage - flag.Usage = Usage - flag.BoolVar( - &allowMissingMsgids, - "allow-missing-msgids", - false, - "don't return an error code if missing message IDs are found", - ) - flag.BoolVar( - &allowUnusedMsgids, - "allow-unused-msgids", - false, - "don't return an error code if unused message IDs are found", - ) + onError := func(err error) { + if err == nil { + return + } + fmt.Println(err.Error()) + os.Exit(3) + } msgids := make(container.Set[string]) @@ -270,50 +334,17 @@ func main() { gotAnyMsgidError := false - flag.Func( - "allow-masked-usages-from", - "supply a file containing a newline-separated list of allowed masked usages", - func(argval string) error { - return ParseAllowedMaskedUsages(argval, usedMsgids, allowedMaskedPrefixes, func(msgid string) bool { - return msgids.Contains(msgid) - }) - }, - ) - flag.Parse() - - onError := func(err error) { - if err == nil { - return - } - fmt.Println(err.Error()) - os.Exit(3) - } - handler := Handler{ - OnMsgidPrefix: func(fset *token.FileSet, pos token.Pos, msgidPrefix string, truncated bool) { - msgidPrefixSplit := strings.Split(msgidPrefix, ".") - if !truncated { - allowedMaskedPrefixes.Insert(msgidPrefixSplit) - } else if !allowedMaskedPrefixes.Matches(msgidPrefixSplit) { - gotAnyMsgidError = true - fmt.Printf("%s:\tmissing msgid prefix: %s\n", fset.Position(pos).String(), msgidPrefix) - } - }, OnMsgid: func(fset *token.FileSet, pos token.Pos, msgid string) { if !msgids.Contains(msgid) { gotAnyMsgidError = true fmt.Printf("%s:\tmissing msgid: %s\n", fset.Position(pos).String(), msgid) - } else { - usedMsgids.Add(msgid) } }, OnUnexpectedInvoke: func(fset *token.FileSet, pos token.Pos, funcname string, argc int) { gotAnyMsgidError = true fmt.Printf("%s:\tunexpected invocation of %s with %d arguments\n", fset.Position(pos).String(), funcname, argc) }, - OnWarning: func(fset *token.FileSet, pos token.Pos, msg string) { - fmt.Printf("%s:\tWARNING: %s\n", fset.Position(pos).String(), msg) - }, LocaleTrFunctions: InitLocaleTrFunctions(), } @@ -346,27 +377,7 @@ func main() { os.Exit(1) } - unusedMsgids := []string{} - - for msgid := range msgids { - if !usedMsgids.Contains(msgid) && !allowedMaskedPrefixes.Matches(strings.Split(msgid, ".")) { - unusedMsgids = append(unusedMsgids, msgid) - } - } - - sort.Strings(unusedMsgids) - - if len(unusedMsgids) != 0 { - fmt.Printf("=== unused msgids (%d): ===\n", len(unusedMsgids)) - for _, msgid := range unusedMsgids { - fmt.Printf("- %s\n", msgid) - } - } - if !allowMissingMsgids && gotAnyMsgidError { os.Exit(4) } - if !allowUnusedMsgids && len(unusedMsgids) != 0 { - os.Exit(5) - } } diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index 91b344b1e9..cb95b3b3c8 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -17,15 +17,6 @@ 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", @@ -69,16 +60,6 @@ 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 9af6c331d3..997d6b3a16 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -14,6 +14,15 @@ 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{ @@ -175,6 +184,16 @@ 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 ef5d9116e3..abdcd5d48a 100644 --- a/cmd/admin_auth_oauth.go +++ b/cmd/admin_auth_oauth.go @@ -86,11 +86,6 @@ 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: "", @@ -125,10 +120,6 @@ 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", - }, } } @@ -136,7 +127,7 @@ func microcmdAuthAddOauth() *cli.Command { return &cli.Command{ Name: "add-oauth", Usage: "Add new Oauth authentication source", - Action: newAuthService().addOauth, + Action: runAddOauth, Flags: oauthCLIFlags(), } } @@ -145,7 +136,7 @@ func microcmdAuthUpdateOauth() *cli.Command { return &cli.Command{ Name: "update-oauth", Usage: "Update existing Oauth authentication source", - Action: newAuthService().updateOauth, + Action: runUpdateOauth, Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{idFlag()}, oauthCLIFlags()[1:]...)...), } } @@ -172,7 +163,6 @@ 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"), @@ -180,15 +170,14 @@ 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 (a *authService) addOauth(ctx context.Context, c *cli.Command) error { +func runAddOauth(ctx context.Context, c *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() - if err := a.initDB(ctx); err != nil { + if err := initDB(ctx); err != nil { return err } @@ -200,7 +189,7 @@ func (a *authService) addOauth(ctx context.Context, c *cli.Command) error { } } - return a.createAuthSource(ctx, &auth_model.Source{ + return auth_model.CreateSource(ctx, &auth_model.Source{ Type: auth_model.OAuth2, Name: c.String("name"), IsActive: true, @@ -208,7 +197,7 @@ func (a *authService) addOauth(ctx context.Context, c *cli.Command) error { }) } -func (a *authService) updateOauth(ctx context.Context, c *cli.Command) error { +func runUpdateOauth(ctx context.Context, c *cli.Command) error { if !c.IsSet("id") { return errors.New("--id flag is missing") } @@ -216,11 +205,11 @@ func (a *authService) updateOauth(ctx context.Context, c *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() - if err := a.initDB(ctx); err != nil { + if err := initDB(ctx); err != nil { return err } - source, err := a.getAuthSourceByID(ctx, c.Int64("id")) + source, err := auth_model.GetSourceByID(ctx, c.Int64("id")) if err != nil { return err } @@ -255,10 +244,6 @@ func (a *authService) updateOauth(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") } @@ -282,10 +267,6 @@ func (a *authService) updateOauth(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{} @@ -319,5 +300,5 @@ func (a *authService) updateOauth(ctx context.Context, c *cli.Command) error { oAuth2Config.CustomURLMapping = customURLMapping source.Cfg = oAuth2Config - return a.updateAuthSource(ctx, source) + return auth_model.UpdateSource(ctx, source) } diff --git a/cmd/admin_auth_oauth_test.go b/cmd/admin_auth_oauth_test.go deleted file mode 100644 index 3430ad1f56..0000000000 --- a/cmd/admin_auth_oauth_test.go +++ /dev/null @@ -1,706 +0,0 @@ -// 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 ea141291f5..cb01e74196 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -8,12 +8,11 @@ import ( "context" "errors" "fmt" - "io/fs" + "io" "os" "path" "path/filepath" "strings" - "sync" "time" "forgejo.org/models/db" @@ -24,43 +23,36 @@ import ( "forgejo.org/modules/util" "code.forgejo.org/go-chi/session" - "github.com/mholt/archives" + "github.com/mholt/archiver/v3" "github.com/urfave/cli/v3" ) -func addObject(archiveJobs chan archives.ArchiveAsyncJob, object fs.File, customName string, verbose bool) error { +func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error { if verbose { log.Info("Adding file %s", 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 - }, + return w.Write(archiver.File{ + FileInfo: archiver.FileInfo{ + FileInfo: info, + CustomName: customName, }, - Result: ch, - } - - return <-ch + ReadCloser: r, + }) } -func addFile(archiveJobs chan archives.ArchiveAsyncJob, filePath, absPath string, verbose bool) error { - file, err := os.Open(absPath) // Closed by archiver +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() if err != nil { return err } - return addObject(archiveJobs, file, filePath, verbose) + return addReader(w, file, fileInfo, filePath, verbose) } func isSubdir(upper, lower string) (bool, error) { @@ -109,54 +101,6 @@ 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{ @@ -310,185 +254,46 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error { return err } - archiveJobs := make(chan archives.ArchiveAsyncJob) - wg := sync.WaitGroup{} - archiver, err := getArchiverByType(outType) + var iface any + if fileName == "-" { + iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType)) + } else { + iface, err = archiver.ByExtension(fileName) + } 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) - wg.Add(1) - go dumpRepos(ctx, archiveJobs, &wg, absFileName, verbose) - } + if err := addRecursiveExclude(w, "repos", setting.RepoRootPath, []string{absFileName}, verbose); err != nil { + fatal("Failed to include repositories: %v", 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) + 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 } - }() - } - 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) - } - }() + return addReader(w, object, info, path.Join("data", "lfs", objPath), verbose) + }); err != nil { + fatal("Failed to dump LFS objects: %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-*") @@ -529,32 +334,139 @@ func dumpDatabase(ctx *cli.Command, archiveJobs chan archives.ArchiveAsyncJob, w fatal("Failed to dump database: %v", err) } - if err := addFile(archiveJobs, "forgejo-db.sql", dbDump.Name(), verbose); err != nil { + if err := addFile(w, "forgejo-db.sql", dbDump.Name(), verbose); err != nil { fatal("Failed to include forgejo-db.sql: %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 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) + } } - 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) + 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) }); err != nil { - fatal("Failed to dump LFS objects: %v", err) + 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 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 -// 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 { +func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeAbsPath []string, verbose bool) error { absPath, err := filepath.Abs(absPath) if err != nil { return err @@ -579,11 +491,10 @@ func addRecursiveExclude(archiveJobs chan archives.ArchiveAsyncJob, insidePath, } if file.IsDir() { - if err := addFile(archiveJobs, currentInsidePath, currentAbsPath, false); err != nil { + if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil { return err } - - if err := addRecursiveExclude(archiveJobs, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil { + if err = addRecursiveExclude(w, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil { return err } } else { @@ -601,7 +512,7 @@ func addRecursiveExclude(archiveJobs chan archives.ArchiveAsyncJob, insidePath, shouldAdd = targetStat.Mode().IsRegular() } if shouldAdd { - if err := addFile(archiveJobs, currentInsidePath, currentAbsPath, verbose); err != nil { + if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil { return err } } diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go index 7159d55e99..eb89273e7f 100644 --- a/cmd/dump_repo.go +++ b/cmd/dump_repo.go @@ -82,11 +82,6 @@ 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 3bdd9d68f8..459386318f 100644 --- a/cmd/dump_test.go +++ b/cmd/dump_test.go @@ -4,32 +4,40 @@ package cmd import ( + "io" "os" "testing" - "github.com/mholt/archives" + "github.com/mholt/archiver/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func mockArchiverAsync(ch chan archives.ArchiveAsyncJob, files *[]string) { - for job := range ch { - *files = append(*files, job.File.NameInArchive) - job.Result <- nil - } +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 TestAddRecursiveExclude(t *testing.T) { t.Run("Empty", func(t *testing.T) { - ch := make(chan archives.ArchiveAsyncJob) - var files []string - go mockArchiverAsync(ch, &files) - dir := t.TempDir() + archiver := &mockArchiver{} - err := addRecursiveExclude(ch, "", dir, []string{}, false) + err := addRecursiveExclude(archiver, "", dir, []string{}, false) require.NoError(t, err) - assert.Empty(t, files) + assert.Empty(t, archiver.addedFiles) }) t.Run("Single file", func(t *testing.T) { @@ -38,25 +46,20 @@ func TestAddRecursiveExclude(t *testing.T) { require.NoError(t, err) t.Run("No exclude", func(t *testing.T) { - ch := make(chan archives.ArchiveAsyncJob) - var files []string - go mockArchiverAsync(ch, &files) + archiver := &mockArchiver{} - err := addRecursiveExclude(ch, "", dir, nil, false) + err = addRecursiveExclude(archiver, "", dir, nil, false) require.NoError(t, err) - - assert.Len(t, files, 1) - assert.Contains(t, files, "example") + assert.Len(t, archiver.addedFiles, 1) + assert.Contains(t, archiver.addedFiles, "example") }) t.Run("With exclude", func(t *testing.T) { - ch := make(chan archives.ArchiveAsyncJob) - var files []string - go mockArchiverAsync(ch, &files) + archiver := &mockArchiver{} - err := addRecursiveExclude(ch, "", dir, []string{dir + "/example"}, false) + err = addRecursiveExclude(archiver, "", dir, []string{dir + "/example"}, false) require.NoError(t, err) - assert.Empty(t, files) + assert.Empty(t, archiver.addedFiles) }) }) @@ -70,57 +73,46 @@ func TestAddRecursiveExclude(t *testing.T) { require.NoError(t, err) t.Run("No exclude", func(t *testing.T) { - ch := make(chan archives.ArchiveAsyncJob) - var files []string - go mockArchiverAsync(ch, &files) + archiver := &mockArchiver{} - err := addRecursiveExclude(ch, "", dir, nil, false) + err = addRecursiveExclude(archiver, "", dir, nil, false) require.NoError(t, err) - 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") + 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") }) t.Run("Exclude first directory", func(t *testing.T) { - ch := make(chan archives.ArchiveAsyncJob) - var files []string - go mockArchiverAsync(ch, &files) + archiver := &mockArchiver{} - err := addRecursiveExclude(ch, "", dir, []string{dir + "/deep"}, false) + err = addRecursiveExclude(archiver, "", dir, []string{dir + "/deep"}, false) require.NoError(t, err) - assert.Empty(t, files) + assert.Empty(t, archiver.addedFiles) }) t.Run("Exclude nested directory", func(t *testing.T) { - ch := make(chan archives.ArchiveAsyncJob) - var files []string - go mockArchiverAsync(ch, &files) + archiver := &mockArchiver{} - err := addRecursiveExclude(ch, "", dir, []string{dir + "/deep/nested/folder"}, false) + err = addRecursiveExclude(archiver, "", dir, []string{dir + "/deep/nested/folder"}, false) require.NoError(t, err) - assert.Len(t, files, 2) - - assert.Contains(t, files, "deep") - assert.Contains(t, files, "deep/nested") + assert.Len(t, archiver.addedFiles, 2) + assert.Contains(t, archiver.addedFiles, "deep") + assert.Contains(t, archiver.addedFiles, "deep/nested") }) t.Run("Exclude file", func(t *testing.T) { - ch := make(chan archives.ArchiveAsyncJob) - var files []string - go mockArchiverAsync(ch, &files) + archiver := &mockArchiver{} - err := addRecursiveExclude(ch, "", dir, []string{dir + "/deep/nested/folder/example"}, false) + err = addRecursiveExclude(archiver, "", dir, []string{dir + "/deep/nested/folder/example"}, false) require.NoError(t, err) - 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") + 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") }) }) } diff --git a/cmd/hook.go b/cmd/hook.go index 7378dc21ad..909cdfdf84 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -231,6 +231,8 @@ Forgejo or set your environment appropriately.`, "") } } + supportProcReceive := git.CheckGitVersionAtLeast("2.29") == nil + for scanner.Scan() { // TODO: support news feeds for wiki if isWiki { @@ -248,25 +250,31 @@ Forgejo or set your environment appropriately.`, "") total++ lastline++ - // All references should be checked because permission check was delayed. - oldCommitIDs[count] = oldCommitID - newCommitIDs[count] = newCommitID - refFullNames[count] = refFullName - count++ - fmt.Fprint(out, "*") + // 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, "*") - 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) + 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 } - count = 0 - lastline = 0 + } else { + fmt.Fprint(out, ".") } if lastline >= hookBatchSize { fmt.Fprint(out, "\n") @@ -505,6 +513,10 @@ 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 c18bfa919b..c543afe872 100644 --- a/cmd/manager_logging.go +++ b/cmd/manager_logging.go @@ -44,11 +44,6 @@ 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"}, @@ -291,9 +286,6 @@ 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 0e0551d297..1fac2d13f5 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -88,14 +88,6 @@ var ( alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`) ) -func sshLog(ctx context.Context, level log.Level, message string) error { - if testing.Testing() || setting.InternalToken == "" { - return nil - } - - return private.SSHLog(ctx, level, message) -} - // fail prints message to stdout, it's mainly used for git serv and git hook commands. // The output will be passed to git client and shown to user. func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error { @@ -120,7 +112,10 @@ func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error logMsg = userMessage + ". " + logMsg } } - _ = sshLog(ctx, log.ERROR, logMsg) + // Don't send an log if this is done in a test and no InternalToken is set. + if !testing.Testing() || setting.InternalToken != "" { + _ = private.SSHLog(ctx, true, logMsg) + } } return cli.Exit("", 1) } @@ -198,10 +193,12 @@ func runServ(ctx context.Context, c *cli.Command) error { } if len(words) < 2 { - // for AGit Flow - if cmd == "ssh_info" { - fmt.Print(`{"type":"agit","version":1}`) - return nil + if git.CheckGitVersionAtLeast("2.29") == 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 be6314addb..03b3b9f0da 100644 --- a/cmd/web_acme.go +++ b/cmd/web_acme.go @@ -15,7 +15,6 @@ 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" @@ -77,12 +76,6 @@ 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/contrib/coverage-helper.sh b/contrib/coverage-helper.sh deleted file mode 100755 index ec9f5469ea..0000000000 --- a/contrib/coverage-helper.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -set -e -#set -x -PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: ' - -# -# Those must be explicitly required and are excluded from the full list of packages because they -# would interfere with the testing fixtures. -# -excluded+='forgejo.org/models/migrations|' # must be run before database specific tests -excluded+='forgejo.org/models/forgejo_migrations|' # must be run before database specific tests -excluded+='forgejo.org/tests/integration/migration-test|' # must be run before database specific tests -excluded+='forgejo.org/tests|' # only tests, no coverage to get there -excluded+='forgejo.org/tests/e2e|' # JavaScript is not in scope here and if it adds coverage it should not be counted -excluded+='FAKETERMINATOR' # do not modify - -: ${COVERAGEDIR:=$(pwd)/coverage/data} -: ${GO:=$(go env GOROOT)/bin/go} - -DEFAULT_TEST_PACKAGES=$($GO list ./... | grep -E -v "$excluded") - -COVERED_PACKAGES=$($GO list ./...) -COVERED_PACKAGES=$(echo $COVERED_PACKAGES | sed -e 's/ /,/g') - -function run_test() { - local package="$1" - if echo "$package" | grep --quiet --fixed-string ".."; then - echo "$package contains a suspicious .." - return 1 - fi - - local coverage="$COVERAGEDIR/$COVERAGE_TEST_DATABASE/$package" - rm -fr $coverage - mkdir -p $coverage - - # - # -race cannot be used because it requires -covermode atomic which is - # different from the end-to-end tests and would cause issues wen merging - # - $GO test -timeout=20m -tags='sqlite sqlite_unlock_notify' -cover $package -coverpkg $COVERED_PACKAGES $COVERAGE_TEST_ARGS -args -test.gocoverdir=$coverage |& grep -v 'warning: no packages being tested depend on matches for pattern' -} - -function test_packages() { - for package in ${@:-$DEFAULT_TEST_PACKAGES}; do - run_test $package - done -} - -"$@" diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 6c8c85b87c..1b8d4c6697 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -449,9 +449,6 @@ 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 ;; @@ -595,10 +592,13 @@ 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_SSH_MODE= ;; SSH logs from ssh git request +;logger.access.MODE= +;logger.router.MODE=, +;logger.xorm.MODE=, +;; +;; Collect SSH logs (Creates log from ssh git request) +;; +;ENABLE_SSH_LOG = false ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; @@ -631,7 +631,6 @@ LEVEL = Info ;LEVEL= ;FLAGS = stdflags or journald ;EXPRESSION = -;EXCLUSION = ;PREFIX = ;COLORIZE = false ;; @@ -1586,11 +1585,6 @@ 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] @@ -1773,9 +1767,6 @@ 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 ;; @@ -1828,9 +1819,6 @@ 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/eslint.config.mjs b/eslint.config.mjs index 83a6b6bee1..28cfa80089 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -153,7 +153,7 @@ export default tseslint.config( '@stylistic/quotes': [2, 'single', { avoidEscape: true, - allowTemplateLiterals: 'always', + allowTemplateLiterals: true, }], '@stylistic/rest-spread-spacing': [2, 'never'], @@ -799,7 +799,6 @@ export default tseslint.config( 'unicorn/prefer-array-some': [2], 'unicorn/prefer-at': [0], 'unicorn/prefer-blob-reading-methods': [2], - 'unicorn/prefer-classlist-toggle': [2], 'unicorn/prefer-code-point': [0], 'unicorn/prefer-date-now': [2], 'unicorn/prefer-default-parameters': [0], diff --git a/go.mod b/go.mod index fcb20601e4..bb2be827eb 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module forgejo.org -go 1.24.0 +go 1.24 -toolchain go1.24.7 +toolchain go1.24.4 require ( code.forgejo.org/f3/gof3/v3 v3.11.0 @@ -10,7 +10,6 @@ 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/v11 v11.0.0 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 @@ -25,11 +24,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.20.0 + github.com/alecthomas/chroma/v2 v2.18.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.24.0 + github.com/caddyserver/certmagic v0.23.0 github.com/chi-middleware/proxy v1.1.1 github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 @@ -41,44 +40,46 @@ require ( github.com/fsnotify/fsnotify v1.9.0 github.com/gliderlabs/ssh v0.3.8 github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 - github.com/go-ap/jsonld v0.0.0-20250905102310-8480b0fe24d9 - github.com/go-chi/chi/v5 v5.2.3 - github.com/go-chi/cors v1.2.2 + 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-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.4 + github.com/go-webauthn/webauthn v0.13.0 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 - github.com/golang-jwt/jwt/v5 v5.3.0 + github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/google/go-github/v64 v64.0.0 - github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.2.0 github.com/gorilla/sessions v1.4.0 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/inbucket/html2text v0.9.0 - github.com/jhillyerd/enmime/v2 v2.2.0 + github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 + github.com/jhillyerd/enmime/v2 v2.1.0 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/klauspost/compress v1.18.0 - github.com/klauspost/cpuid/v2 v2.2.11 + github.com/klauspost/cpuid/v2 v2.2.10 github.com/lib/pq v1.10.9 github.com/markbates/goth v1.80.0 github.com/mattn/go-isatty v0.0.20 - github.com/mattn/go-sqlite3 v1.14.32 - github.com/meilisearch/meilisearch-go v0.34.0 - github.com/mholt/archives v0.1.3 + github.com/mattn/go-sqlite3 v1.14.28 + github.com/meilisearch/meilisearch-go v0.31.0 + github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/minio/minio-go/v7 v7.0.95 + github.com/minio/minio-go/v7 v7.0.94 github.com/msteinert/pam/v2 v2.1.0 - github.com/niklasfasching/go-org v1.9.1 + github.com/nektos/act v0.2.52 + github.com/niklasfasching/go-org v1.8.0 github.com/olivere/elastic/v7 v7.0.32 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 @@ -88,27 +89,27 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/sergi/go-diff v1.4.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.0 - github.com/ulikunitz/xz v0.5.15 - github.com/urfave/cli/v3 v3.4.1 + github.com/ulikunitz/xz v0.5.12 + github.com/urfave/cli/v3 v3.3.3 github.com/valyala/fastjson v1.6.4 github.com/yohcop/openid-go v1.0.1 - github.com/yuin/goldmark v1.7.13 + github.com/yuin/goldmark v1.7.12 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc gitlab.com/gitlab-org/api/client-go v0.130.1 - go.uber.org/mock v0.6.0 - go.yaml.in/yaml/v3 v3.0.4 - golang.org/x/crypto v0.42.0 - golang.org/x/image v0.31.0 - golang.org/x/net v0.44.0 - golang.org/x/oauth2 v0.31.0 - golang.org/x/sync v0.17.0 - golang.org/x/sys v0.36.0 - golang.org/x/text v0.29.0 - google.golang.org/protobuf v1.36.9 + go.uber.org/mock v0.5.2 + golang.org/x/crypto v0.39.0 + golang.org/x/image v0.27.0 + golang.org/x/net v0.41.0 + golang.org/x/oauth2 v0.30.0 + golang.org/x/sync v0.15.0 + golang.org/x/sys v0.33.0 + golang.org/x/text v0.26.0 + google.golang.org/protobuf v1.36.4 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 + gopkg.in/yaml.v3 v3.0.1 mvdan.cc/xurls/v2 v2.5.0 xorm.io/builder v0.3.13 xorm.io/xorm v1.3.9 @@ -116,13 +117,12 @@ require ( require ( cloud.google.com/go/compute/metadata v0.6.0 // indirect - dario.cat/mergo v1.0.2 // indirect + dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect - github.com/STARRY-S/zip v0.2.1 // indirect - github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/aymerick/douceur v0.2.0 // indirect @@ -145,40 +145,35 @@ 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.4.1 // indirect + github.com/cyphar/filepath-securejoin v0.3.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dlclark/regexp2 v1.11.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.18.0 // indirect - github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-enry/go-oniguruma v1.2.1 // indirect 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.23 // indirect + github.com/go-webauthn/x v0.1.21 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect @@ -188,38 +183,31 @@ 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 // indirect + github.com/libdns/libdns v1.0.0-beta.1 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/markbates/going v1.0.3 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-colorable v0.1.13 // 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/mikelolasagasti/xz v1.0.1 // indirect - github.com/minio/crc64nvme v1.0.2 // indirect + github.com/minio/crc64nvme v1.0.1 // 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/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/nwaples/rardecode v1.1.3 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/philhofer/fwd v1.2.0 // indirect + github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -227,34 +215,35 @@ 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.7.7 // indirect + github.com/rhysd/actionlint v1.6.27 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/skeema/knownhosts v1.3.1 // indirect - github.com/sorairolake/lzip-go v0.3.5 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/tinylib/msgp v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/zeebo/assert v1.3.0 // indirect github.com/zeebo/blake3 v0.2.4 // indirect - go.etcd.io/bbolt v1.4.3 // indirect + go.etcd.io/bbolt v1.4.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect - go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/mod v0.27.0 // indirect - golang.org/x/time v0.13.0 // indirect - golang.org/x/tools v0.36.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/time v0.11.0 // indirect + golang.org/x/tools v0.34.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // 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 63af64f9ca..639880e2ce 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,13 @@ -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= @@ -28,8 +16,6 @@ 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/v11 v11.0.0 h1:PDChRbwPzoflwP05QHn8YyvRDvnU5yVq/X3sdZojXr8= -code.forgejo.org/forgejo/runner/v11 v11.0.0/go.mod h1:ZJokNf9nZItjsfSjC/9zHbUvf5LXYUi+On+1Bg/pO3g= 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= @@ -48,9 +34,8 @@ codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtf codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= 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.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= -dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 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= @@ -63,8 +48,6 @@ 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= @@ -74,22 +57,21 @@ 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.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= -github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= +github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4= +github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= -github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= -github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= -github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ= -github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +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/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= @@ -141,14 +123,6 @@ 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= @@ -160,11 +134,10 @@ 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.24.0 h1:EfXTWpxHAUKgDfOj6MHImJN8Jm4AMFfMT6ITuKhrDF0= -github.com/caddyserver/certmagic v0.24.0/go.mod h1:xPT7dC1DuHHnS2yuEQCEyks+b89sUkMENh8dJF+InLE= +github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= +github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/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= @@ -174,18 +147,14 @@ 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.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +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/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= @@ -204,6 +173,7 @@ 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= @@ -211,8 +181,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.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= -github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +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/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= @@ -222,10 +192,8 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTe github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/envoyproxy/go-control-plane v0.9.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/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -234,22 +202,21 @@ 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.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= +github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 h1:j2TrkUG/NATGi/EQS+MvEoF79CxiRUmT16ErFroNcKI= github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9/go.mod h1:cJ9Ye0ZNSMN7RzZDBRY3E+8M3Bpf/R1JX22Ir9yX6WI= github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 h1:I2nuhyVI/48VXoRCCZR2hYBgnSXa+EuDJf/VyX06TC0= github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7/go.mod h1:5x8a6P/dhmMGFxWLcyYlyOuJ2lRNaHGhRv+yu8BaTSI= +github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 h1:GMKIYXyXPGIp+hYiWOhfqK4A023HdgisDT4YGgf99mw= github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA= -github.com/go-ap/jsonld v0.0.0-20250905102310-8480b0fe24d9 h1:gaBrU/E+usPHIafDIC2EwvZbehvgAEuu78Jk0zjxw5w= -github.com/go-ap/jsonld v0.0.0-20250905102310-8480b0fe24d9/go.mod h1:4h93IBxgfnE/DEleMLgJ/XCeu/RtQ+MUh3ucANseeXA= github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= -github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -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-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= +github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= +github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= 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= @@ -264,10 +231,8 @@ 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.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-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-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= @@ -285,10 +250,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.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/go-webauthn/webauthn v0.13.0 h1:cJIL1/1l+22UekVhipziAaSgESJxokYkowUqAIsWs0Y= +github.com/go-webauthn/webauthn v0.13.0/go.mod h1:Oy9o2o79dbLKRPZWWgRIOdtBGAhKnDIaBp2PFkICRHs= +github.com/go-webauthn/x v0.1.21 h1:nFbckQxudvHEJn2uy1VEi713MeSpApoAv9eRqsb9AdQ= +github.com/go-webauthn/x v0.1.21/go.mod h1:sEYohtg1zL4An1TXIUIQ5csdmoO+WO0R4R2pGKaHYKA= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= @@ -300,26 +265,16 @@ 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.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +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/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/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/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/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= @@ -329,13 +284,11 @@ 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= @@ -353,20 +306,13 @@ 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-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/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/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= @@ -381,19 +327,12 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= -github.com/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= @@ -401,32 +340,30 @@ 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/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/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA= +github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jhillyerd/enmime/v2 v2.2.0 h1:Pe35MB96eZK5Q0XjlvPftOgWypQpd1gcbfJKAt7rsB8= -github.com/jhillyerd/enmime/v2 v2.2.0/go.mod h1:SOBXlCemjhiV2DvHhAKnJiWrtJGS/Ffuw4Iy7NjBTaI= +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/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.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= -github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -443,8 +380,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libdns/libdns v1.0.0 h1:IvYaz07JNz6jUQ4h/fv2R4sVnRnm77J/aOuC9B+TQTA= -github.com/libdns/libdns v1.0.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= +github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ= +github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= @@ -452,36 +389,30 @@ 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.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +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-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-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.34.0 h1:P+Ohdx4/PCxXaoI5wNi0LMwPkuiNrF/kGIzBrKYS4tw= -github.com/meilisearch/meilisearch-go v0.34.0/go.mod h1:cUVJZ2zMqTvvwIMEEAdsWH+zrHsrLpAw6gm8Lt1MXK0= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY= +github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g= github.com/mholt/acmez/v3 v3.1.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/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/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY= +github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.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/minio/minio-go/v7 v7.0.94 h1:1ZoksIKPyaSt64AVOyaQvhDOgVC3MfZsWM6mZXRUGtM= +github.com/minio/minio-go/v7 v7.0.94/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -497,19 +428,16 @@ 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.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/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/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/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/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 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= @@ -527,8 +455,9 @@ 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.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= -github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.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= @@ -543,7 +472,6 @@ 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= @@ -554,21 +482,19 @@ 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.7.7 h1:0KgkoNTrYY7vmOCs9BW2AHxLvvpoY9nEUzgBHiPUr0k= -github.com/rhysd/actionlint v1.7.7/go.mod h1:AE6I6vJEkNaIfWqC2GNE5spIJNhxf8NCtLEKU4NnUXg= +github.com/rhysd/actionlint v1.6.27 h1:xxwe8YmveBcC8lydW6GoHMGmB6H/MTqUU60F2p10wjw= +github.com/rhysd/actionlint v1.6.27/go.mod h1:m2nFUjAnOrxCMXuOMz9evYBRCLUsMnKY2IJl/N5umbk= 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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/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= @@ -577,10 +503,8 @@ 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.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/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= 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= @@ -595,23 +519,26 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= -github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -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/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/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= @@ -619,8 +546,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.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= -github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= +github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= @@ -631,32 +558,22 @@ 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.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= -go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= -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.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= 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= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -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= @@ -666,59 +583,23 @@ 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.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= -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/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/image v0.0.0-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.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA= -golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA= -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/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= +golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.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.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/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-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= @@ -731,21 +612,12 @@ 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.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -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.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= -golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-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= @@ -753,25 +625,15 @@ 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.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-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= @@ -783,6 +645,7 @@ 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= @@ -790,8 +653,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.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -801,12 +664,9 @@ 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.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -816,88 +676,31 @@ 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.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -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.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= -golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-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.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -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.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= -google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= 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= @@ -925,11 +728,6 @@ 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= @@ -952,9 +750,6 @@ 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 492e3f6cea..10cd3868a1 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -108,7 +108,6 @@ func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) erro type FindArtifactsOptions struct { db.ListOptions - ID int64 RepoID int64 RunID int64 ArtifactName string @@ -117,9 +116,6 @@ type FindArtifactsOptions struct { func (opts FindArtifactsOptions) ToConds() builder.Cond { cond := builder.NewCond() - if opts.ID > 0 { - cond = cond.And(builder.Eq{"id": opts.ID}) - } if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) } @@ -136,13 +132,6 @@ 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/main_test.go b/models/actions/main_test.go index f551d39671..2eb923d9d0 100644 --- a/models/actions/main_test.go +++ b/models/actions/main_test.go @@ -15,10 +15,6 @@ func TestMain(m *testing.M) { "action_runner.yml", "repository.yml", "action_runner_token.yml", - "user.yml", - "action_run.yml", - "action_run_job.yml", - "action_task.yml", }, }) } diff --git a/models/actions/run.go b/models/actions/run.go index b5f79a0cb3..55def805ed 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" - "code.forgejo.org/forgejo/runner/v11/act/jobparser" + "github.com/nektos/act/pkg/jobparser" "xorm.io/builder" ) @@ -284,10 +284,16 @@ func GetLatestRun(ctx context.Context, repoID int64) (*ActionRun, error) { return &run, nil } -func GetRunBefore(ctx context.Context, _ *ActionRun) (*ActionRun, error) { - // TODO return the most recent run related to the run given in argument - // see https://codeberg.org/forgejo/user-research/issues/63 for context - return nil, nil +// GetRunBefore returns the last run that completed a given timestamp (not inclusive). +func GetRunBefore(ctx context.Context, repoID int64, timestamp timeutil.TimeStamp) (*ActionRun, error) { + var run ActionRun + has, err := db.GetEngine(ctx).Where("repo_id=? AND stopped IS NOT NULL AND stopped 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 deleted file mode 100644 index 176ce9905d..0000000000 --- a/models/db/table_names_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// 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/error.go b/models/error.go index 99c8ded766..ebaa8a135d 100644 --- a/models/error.go +++ b/models/error.go @@ -121,7 +121,6 @@ type ErrInvalidCloneAddr struct { IsInvalidPath bool IsProtocolInvalid bool IsPermissionDenied bool - HasCredentials bool LocalPath bool } @@ -144,9 +143,6 @@ func (err *ErrInvalidCloneAddr) Error() string { if err.IsURLError { return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url is invalid", err.Host) } - if err.HasCredentials { - return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url contains credentials", err.Host) - } return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host) } diff --git a/models/fixtures/ModerationFeatures/abuse_report.yml b/models/fixtures/ModerationFeatures/abuse_report.yml deleted file mode 100644 index f2e371ee35..0000000000 --- a/models/fixtures/ModerationFeatures/abuse_report.yml +++ /dev/null @@ -1,21 +0,0 @@ -- - 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 deleted file mode 100644 index a4d41ad997..0000000000 --- a/models/fixtures/ModerationFeatures/comment.yml +++ /dev/null @@ -1,7 +0,0 @@ -- # 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 deleted file mode 100644 index 662c61a3e9..0000000000 --- a/models/fixtures/ModerationFeatures/user.yml +++ /dev/null @@ -1,22 +0,0 @@ -- # This user is a spammer and will create abusive content (for testing moderation functionalities). - id: 1002 - lower_name: alexsmith - name: alexsmith - full_name: Alex Smith - email: alexsmith@example.org - keep_email_private: false - passwd: passwdSalt:password - passwd_hash_algo: dummy - type: 0 - location: '@master@seo.net' - website: http://promote-your-business.biz - pronouns: SEO - salt: passwdSalt - description: I can help you promote your business online using SEO. - created_unix: 1752697800 # 2025-07-16 20:30:00 - is_active: true - is_admin: false - is_restricted: false - avatar: avatar-hash-1002 - avatar_email: alexsmith@example.org - use_custom_avatar: false diff --git a/models/fixtures/TestActivateUserEmail/email_address.yml b/models/fixtures/TestActivateUserEmail/email_address.yml deleted file mode 100644 index cf41ff8241..0000000000 --- a/models/fixtures/TestActivateUserEmail/email_address.yml +++ /dev/null @@ -1,7 +0,0 @@ -- - id: 1001 - uid: 1001 - email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net - lower_email: anothertestuserwithuppercaseemail@otto.splvs.net - is_activated: false - is_primary: true diff --git a/models/fixtures/TestActivateUserEmail/user.yml b/models/fixtures/TestActivateUserEmail/user.yml deleted file mode 100644 index 0a68e70a4a..0000000000 --- a/models/fixtures/TestActivateUserEmail/user.yml +++ /dev/null @@ -1,12 +0,0 @@ -- - id: 1001 - lower_name: user1001 - name: user1001 - full_name: User That loves Upper Cases - email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net - passwd: ZogKvWdyEx:password - passwd_hash_algo: dummy - avatar: '' - avatar_email: anothertestuserwithuppercaseemail@otto.splvs.net - login_name: user1 - created_unix: 1672578000 diff --git a/models/fixtures/TestAddTeamReviewRequest/issue.yml b/models/fixtures/TestAddTeamReviewRequest/issue.yml deleted file mode 100644 index a1bcf2921f..0000000000 --- a/models/fixtures/TestAddTeamReviewRequest/issue.yml +++ /dev/null @@ -1,16 +0,0 @@ -- - id: 23 - repo_id: 2 - index: 3 - poster_id: 2 - original_author_id: 0 - name: protected branch pull - content: pull request to a protected branch - milestone_id: 0 - priority: 0 - is_pull: true - is_closed: false - num_comments: 0 - created_unix: 1707270422 - updated_unix: 1707270422 - is_locked: false \ No newline at end of file diff --git a/models/fixtures/TestAddTeamReviewRequest/protected_branch.yml b/models/fixtures/TestAddTeamReviewRequest/protected_branch.yml deleted file mode 100644 index 93909bd991..0000000000 --- a/models/fixtures/TestAddTeamReviewRequest/protected_branch.yml +++ /dev/null @@ -1,28 +0,0 @@ -- id: 1 - repo_id: 2 - branch_name: protected-main - can_push: false - enable_whitelist: true - whitelist_user_i_ds: [1] - whitelist_team_i_ds: [] - enable_merge_whitelist: true - whitelist_deploy_keys: false - merge_whitelist_user_i_ds: [1] - merge_whitelist_team_i_ds: [] - enable_status_check: false - status_check_contexts: [] - enable_approvals_whitelist: true - approvals_whitelist_user_i_ds: [] - approvals_whitelist_team_i_ds: [1] - required_approvals: 1 - block_on_rejected_reviews: true - block_on_official_review_requests: true - block_on_outdated_branch: true - dismiss_stale_approvals: true - ignore_stale_approvals: false - require_signed_commits: false - protected_file_patterns: "" - unprotected_file_patterns: "" - apply_to_admins: true - created_unix: 1752513073 - updated_unix: 1752513073 \ No newline at end of file diff --git a/models/fixtures/TestAddTeamReviewRequest/pull_request.yml b/models/fixtures/TestAddTeamReviewRequest/pull_request.yml deleted file mode 100644 index 067bb01324..0000000000 --- a/models/fixtures/TestAddTeamReviewRequest/pull_request.yml +++ /dev/null @@ -1,12 +0,0 @@ -- - id: 11 - type: 0 # gitea pull request - status: 2 # mergeable - issue_id: 23 - index: 3 - head_repo_id: 2 - base_repo_id: 2 - head_branch: feature/protected-branch-pr - base_branch: protected-main - merge_base: 4a357436d925b5c974181ff12a994538ddc5a269 - has_merged: false \ No newline at end of file diff --git a/models/fixtures/action.yml b/models/fixtures/action.yml index f1592d4569..a97e94fbf4 100644 --- a/models/fixtures/action.yml +++ b/models/fixtures/action.yml @@ -59,14 +59,6 @@ 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_artifact.yml b/models/fixtures/action_artifact.yml index a591719aee..2c51c11ebd 100644 --- a/models/fixtures/action_artifact.yml +++ b/models/fixtures/action_artifact.yml @@ -57,7 +57,7 @@ run_id: 792 runner_id: 1 repo_id: 4 - owner_id: 5 + owner_id: 1 commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 storage_path: "27/5/1730330775594233150.chunk" file_size: 1024 diff --git a/models/fixtures/action_run_job.yml b/models/fixtures/action_run_job.yml index 911e99c076..702c6bc832 100644 --- a/models/fixtures/action_run_job.yml +++ b/models/fixtures/action_run_job.yml @@ -106,7 +106,7 @@ commit_sha: 985f0301dba5e7b34be866819cd15ad3d8f508ee is_fork_pull_request: 0 name: job_2 - attempt: 2 + attempt: 1 job_id: job_2 task_id: 47 status: 5 @@ -128,18 +128,3 @@ runs_on: '["fedora"]' started: 1683636528 stopped: 1683636626 -- - id: 396 - run_id: 794 - repo_id: 4 - owner_id: 1 - commit_sha: 985f0301dba5e7b34be866819cd15ad3d8f508ee - is_fork_pull_request: 0 - name: job_2 - attempt: 0 - job_id: job_2 - task_id: null - status: 5 - runs_on: '["fedora"]' - started: 1683636528 - stopped: 1683636626 diff --git a/models/fixtures/action_task.yml b/models/fixtures/action_task.yml index e5fa35f0b3..506a47d8a0 100644 --- a/models/fixtures/action_task.yml +++ b/models/fixtures/action_task.yml @@ -117,43 +117,3 @@ log_length: 707 log_size: 90179 log_expired: 0 -- - id: 52 - job_id: 192 - attempt: 1 - runner_id: 1 - status: 1 # success - started: 1683636528 - stopped: 1683636626 - repo_id: 4 - owner_id: 1 - commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 - is_fork_pull_request: 0 - token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784223 - token_salt: ffffffffff - token_last_eight: ffffffff - log_filename: artifact-test2/2f/47.log - log_in_storage: 1 - log_length: 707 - log_size: 90179 - log_expired: 0 -- - id: 53 - job_id: 192 - attempt: 2 - runner_id: 1 - status: 1 # success - started: 1683636528 - stopped: 1683636626 - repo_id: 4 - owner_id: 1 - commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 - is_fork_pull_request: 0 - token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784224 - token_salt: ffffffffff - token_last_eight: ffffffff - log_filename: artifact-test2/2f/47.log - log_in_storage: 1 - log_length: 707 - log_size: 90179 - log_expired: 0 diff --git a/models/fixtures/action_variable.yml b/models/fixtures/action_variable.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/action_variable.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/comment.yml b/models/fixtures/comment.yml index 6908d85dda..34407d6f81 100644 --- a/models/fixtures/comment.yml +++ b/models/fixtures/comment.yml @@ -186,46 +186,10 @@ type: 8 # milestone poster_id: 1 issue_id: 1 # in repo_id 1 - milestone_id: 10 # not existing milestone + milestone_id: 10 # not exsting milestone old_milestone_id: 0 created_unix: 946685080 -- - id: 2004 - type: 8 # milestone - poster_id: 1 - issue_id: 1 # in repo_id 1 - milestone_id: 1 - old_milestone_id: 10 # not existing (ghost) milestone - created_unix: 946685085 - -- - id: 2005 - type: 8 # milestone - poster_id: 1 - issue_id: 1 # in repo_id 1 - milestone_id: 10 # not existing (ghost) milestone - old_milestone_id: 1 - created_unix: 946685090 - -- - id: 2006 - type: 8 # milestone - poster_id: 1 - issue_id: 1 # in repo_id 1 - milestone_id: 11 # not existing (ghost) milestone - old_milestone_id: 10 # not existing (ghost) milestone - created_unix: 946685095 - -- - id: 2007 - type: 8 # milestone - poster_id: 1 - issue_id: 1 # in repo_id 1 - milestone_id: 0 - old_milestone_id: 11 # not existing (ghost) milestone - created_unix: 946685100 - - id: 2010 type: 30 # project diff --git a/models/fixtures/deploy_key.yml b/models/fixtures/deploy_key.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/deploy_key.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/external_login_user.yml b/models/fixtures/external_login_user.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/external_login_user.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/federated_user.yml b/models/fixtures/federated_user.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/federated_user.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/federation_host.yml b/models/fixtures/federation_host.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/federation_host.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/gpg_key_import.yml b/models/fixtures/gpg_key_import.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/gpg_key_import.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/label.yml b/models/fixtures/label.yml index 84c2a7f418..acfac74968 100644 --- a/models/fixtures/label.yml +++ b/models/fixtures/label.yml @@ -3,7 +3,6 @@ repo_id: 1 org_id: 0 name: label1 - description: 'First label' color: '#abcdef' exclusive: false num_issues: 2 @@ -108,26 +107,3 @@ 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 new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/login_source.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/protected_branch.yml b/models/fixtures/protected_branch.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/protected_branch.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/pull_auto_merge.yml b/models/fixtures/pull_auto_merge.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/pull_auto_merge.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/push_mirror.yml b/models/fixtures/push_mirror.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/push_mirror.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/repo_archiver.yml b/models/fixtures/repo_archiver.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/repo_archiver.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/repo_indexer_status.yml b/models/fixtures/repo_indexer_status.yml new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/repo_indexer_status.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/repo_redirect.yml b/models/fixtures/repo_redirect.yml index 82d365c600..8850c8d780 100644 --- a/models/fixtures/repo_redirect.yml +++ b/models/fixtures/repo_redirect.yml @@ -3,9 +3,3 @@ owner_id: 2 lower_name: oldrepo1 redirect_repo_id: 1 - -- - id: 2 - owner_id: 17 - lower_name: oldrepo24 - redirect_repo_id: 24 diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index 2f104eed65..c383fa43ac 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 new file mode 100644 index 0000000000..ca780a73aa --- /dev/null +++ b/models/fixtures/secret.yml @@ -0,0 +1 @@ +[] # empty diff --git a/models/fixtures/user_redirect.yml b/models/fixtures/user_redirect.yml index 2f7a523c0c..f471e94511 100644 --- a/models/fixtures/user_redirect.yml +++ b/models/fixtures/user_redirect.yml @@ -3,15 +3,3 @@ lower_name: olduser1 redirect_user_id: 1 created_unix: 1730000000 - -- - id: 2 - lower_name: oldorg22 - redirect_user_id: 22 - created_unix: 1730000000 - -- - id: 3 - lower_name: oldorg23 - redirect_user_id: 23 - created_unix: 1730000000 diff --git a/models/forgejo_migrations/main_test.go b/models/forgejo_migrations/main_test.go index 2246e327f0..031fe8090d 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 +package forgejo_migrations //nolint:revive import ( "testing" diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 71fcf16e7a..737350b019 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 +package forgejo_migrations //nolint:revive import ( "context" @@ -108,17 +108,7 @@ var migrations = []*Migration{ // v33 -> v34 NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun), // v34 -> v35 - NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), - // v35 -> v36 - NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission), - // 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), + NewMigration("Add index to `stopped` column in `action_run` table", AddIndexToActionRunStopped), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/migrate_test.go b/models/forgejo_migrations/migrate_test.go index 9d16c9fe1c..20653929a3 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 +package forgejo_migrations //nolint:revive import ( "testing" diff --git a/models/forgejo_migrations/v13.go b/models/forgejo_migrations/v13.go index ba4183885e..614f68249d 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v14.go b/models/forgejo_migrations/v14.go index 65b857d343..53f1ef2223 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 +package forgejo_migrations //nolint:revive import ( "forgejo.org/models/migrations/base" diff --git a/models/forgejo_migrations/v15.go b/models/forgejo_migrations/v15.go index a63199ab19..5e5588dd05 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 +package forgejo_migrations //nolint:revive import ( "time" diff --git a/models/forgejo_migrations/v16.go b/models/forgejo_migrations/v16.go index a7d4d5d590..f80bfc5268 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v17.go b/models/forgejo_migrations/v17.go index 8ef6f2c681..d6e2983d00 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v18.go b/models/forgejo_migrations/v18.go index e39b0cbf10..e6c1493f0e 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v19.go b/models/forgejo_migrations/v19.go index 43d279dcb0..69b7746eb1 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_20/v1.go b/models/forgejo_migrations/v1_20/v1.go index f0cb125557..72beaf23de 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 +package forgejo_v1_20 //nolint:revive import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v1_20/v2.go b/models/forgejo_migrations/v1_20/v2.go index 3f79ac3801..39f3b58924 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 +package forgejo_v1_20 //nolint:revive import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_20/v3.go b/models/forgejo_migrations/v1_20/v3.go index 49530df556..cce227e6eb 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 +package forgejo_v1_20 //nolint:revive 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 d6a5bdacee..03c4c5272c 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 +package v1_22 //nolint import ( "testing" diff --git a/models/forgejo_migrations/v1_22/v10.go b/models/forgejo_migrations/v1_22/v10.go index cf45abdd24..819800ae71 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v11.go b/models/forgejo_migrations/v1_22/v11.go index f0f92bd04c..17bb592379 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 +package v1_22 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v1_22/v12.go b/models/forgejo_migrations/v1_22/v12.go index 51354bd3c2..6822524705 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 +package v1_22 //nolint import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v4.go b/models/forgejo_migrations/v1_22/v4.go index 499d377bb4..f1195f5f66 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v5.go b/models/forgejo_migrations/v1_22/v5.go index 1671d3eed2..55f9fe1338 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v6.go b/models/forgejo_migrations/v1_22/v6.go index 072f8e6a15..1a4874872c 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v7.go b/models/forgejo_migrations/v1_22/v7.go index e7f6eb412b..b42dd1af67 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v8.go b/models/forgejo_migrations/v1_22/v8.go index f23b00d2ad..2d3c0c594b 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 +package v1_22 //nolint import ( "strings" diff --git a/models/forgejo_migrations/v1_22/v8_test.go b/models/forgejo_migrations/v1_22/v8_test.go index 5117dd2dfb..baaba7290f 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 +package v1_22 //nolint import ( "testing" diff --git a/models/forgejo_migrations/v1_22/v9.go b/models/forgejo_migrations/v1_22/v9.go index e3cdea97f2..34c2844c39 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 +package v1_22 //nolint import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v20.go b/models/forgejo_migrations/v20.go index 91c7b8e911..8ca9e91f73 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v21.go b/models/forgejo_migrations/v21.go index 61d7950c5a..53f141b2ab 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v22.go b/models/forgejo_migrations/v22.go index 8078591da6..eeb738799c 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v23.go b/models/forgejo_migrations/v23.go index a79a4f3d6e..20a916a716 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v24.go b/models/forgejo_migrations/v24.go index 084a57e1ce..ebfb5fc1c4 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v25.go b/models/forgejo_migrations/v25.go index 56cde499a3..8e3032a40c 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 +package forgejo_migrations //nolint:revive import ( "context" diff --git a/models/forgejo_migrations/v25_test.go b/models/forgejo_migrations/v25_test.go index 68e71da012..e7402fd021 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 +package forgejo_migrations //nolint:revive import ( "testing" diff --git a/models/forgejo_migrations/v26.go b/models/forgejo_migrations/v26.go index a0c47799c2..3292d93ffd 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v27.go b/models/forgejo_migrations/v27.go index 9cfbc64370..2efa3485a8 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 +package forgejo_migrations //nolint:revive import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v28.go b/models/forgejo_migrations/v28.go index 19f0dcd862..cba888d2ec 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v29.go b/models/forgejo_migrations/v29.go index 92eb05e8b3..d0c2f723ae 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 +package forgejo_migrations //nolint:revive import ( "database/sql" diff --git a/models/forgejo_migrations/v30.go b/models/forgejo_migrations/v30.go index 05a1dff898..6c41a55316 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 +package forgejo_migrations //nolint:revive import ( "time" diff --git a/models/forgejo_migrations/v30_test.go b/models/forgejo_migrations/v30_test.go index 152fddeb47..f826dab815 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 +package forgejo_migrations //nolint:revive import ( "testing" diff --git a/models/forgejo_migrations/v31.go b/models/forgejo_migrations/v31.go index 23397c7c13..fdcab21b1a 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 +package forgejo_migrations //nolint:revive import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v31_test.go b/models/forgejo_migrations/v31_test.go index 6d1690aae0..5b4aac2a60 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 +package forgejo_migrations //nolint:revive import ( "testing" diff --git a/models/forgejo_migrations/v32.go b/models/forgejo_migrations/v32.go index ce3f855694..bed335ab6b 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 +package forgejo_migrations //nolint:revive import ( "encoding/xml" @@ -12,7 +12,6 @@ import ( "strconv" "strings" - "forgejo.org/models/db" "forgejo.org/models/packages" "forgejo.org/modules/json" "forgejo.org/modules/log" @@ -53,50 +52,55 @@ 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 { - 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 - } + sess := x.NewSession() + defer sess.Close() - for _, id := range ownerIDs { - if err := fixMavenArtifactPerOwner(ctx, id); err != nil { - log.Error("owner %d migration failed: %v", id, err) - return err // rollback all - } - } + if err := sess.Begin(); err != nil { + return err + } - return nil - }) + // 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 sess.Commit() } -func fixMavenArtifactPerOwner(ctx context.Context, ownerID *int64) error { - results, err := getMavenPackageResultsToUpdate(ctx, ownerID) +func fixMavenArtifactPerOwner(sess *xorm.Session, ownerID *int64) error { + results, err := getMavenPackageResultsToUpdate(sess, ownerID) if err != nil { return err } - if err = resolvePackageCollisions(ctx, results); err != nil { + if err = resolvePackageCollisions(results, sess); err != nil { return err } - if err = processPackageVersions(ctx, results); err != nil { + if err = processPackageVersions(results, sess); err != nil { return err } - return processPackageFiles(ctx, results) + return processPackageFiles(results, sess) } // processPackageFiles updates Maven package files and versions in the database // Returns an error if any database or processing operation fails. -func processPackageFiles(ctx context.Context, results []*mavenPackageResult) error { +func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) error { processedVersion := make(map[string][]*mavenPackageResult) for _, r := range results { @@ -109,7 +113,7 @@ func processPackageFiles(ctx context.Context, results []*mavenPackageResult) err if r.PackageVersion.ID != r.PackageFile.VersionID { pattern := strings.TrimSuffix(r.PackageFile.Name, ".pom") + "%" // Per routers/api/packages/maven/maven.go:338, POM files already have the `IsLead`, so no update needed for this prop - if _, err := db.GetEngine(ctx).Exec("UPDATE package_file SET version_id = ? WHERE version_id = ? and name like ?", r.PackageVersion.ID, r.PackageFile.VersionID, pattern); err != nil { + 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 { return err } } @@ -124,14 +128,14 @@ func processPackageFiles(ctx context.Context, results []*mavenPackageResult) err rs := packageResults[0] - pf, md, err := parseMetadata(ctx, rs) + pf, md, err := parseMetadata(sess, rs) if err != nil { return err } if pf != nil && md != nil && md.GroupID == rs.GroupID && md.ArtifactID == rs.ArtifactID { if pf.VersionID != rs.PackageFile.VersionID { - if _, err := db.GetEngine(ctx).ID(pf.ID).Cols("version_id").Update(pf); err != nil { + if _, err := sess.ID(pf.ID).Cols("version_id").Update(pf); err != nil { return err } } @@ -146,9 +150,11 @@ func processPackageFiles(ctx context.Context, results []*mavenPackageResult) err // parseMetadata retrieves metadata for a Maven package file from the database and decodes it into a Metadata object. // Returns the associated PackageFile, Metadata, and any error encountered during processing. -func parseMetadata(ctx context.Context, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { +func parseMetadata(sess *xorm.Session, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { + ctx := context.Background() + var pf packages.PackageFile - found, err := db.GetEngine(ctx).Table(pf). + found, err := sess.Table(pf). Where("version_id = ?", snapshot.PackageFile.VersionID). // still the old id And("lower_name = ?", "maven-metadata.xml"). Get(&pf) @@ -177,7 +183,7 @@ func parseMetadata(ctx context.Context, 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(ctx context.Context, results []*mavenPackageResult) error { +func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) error { processedVersion := make(map[string]int64) for _, r := range results { @@ -190,14 +196,14 @@ func processPackageVersions(ctx context.Context, results []*mavenPackageResult) // for non collisions, just update the metadata if r.PackageVersion.PackageID == r.Package.ID { - if _, err := db.GetEngine(ctx).ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil { + if _, err := sess.ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil { return err } } else { log.Info("Create new maven package version for %s:%s", r.PackageName, r.PackageVersion.Version) r.PackageVersion.ID = 0 r.PackageVersion.PackageID = r.Package.ID - if _, err := db.GetEngine(ctx).Insert(r.PackageVersion); err != nil { + if _, err := sess.Insert(r.PackageVersion); err != nil { return err } } @@ -210,9 +216,10 @@ func processPackageVersions(ctx context.Context, results []*mavenPackageResult) // getMavenPackageResultsToUpdate retrieves Maven package results that need updates based on the owner ID. // It processes POM metadata, fixes package inconsistencies, and filters corrupted package versions. -func getMavenPackageResultsToUpdate(ctx context.Context, ownerID *int64) ([]*mavenPackageResult, error) { +func getMavenPackageResultsToUpdate(sess *xorm.Session, ownerID *int64) ([]*mavenPackageResult, error) { + ctx := context.Background() var candidates []*mavenPackageResult - if err := db.GetEngine(ctx). + if err := sess. Table("package_file"). Select("package_file.*, package_version.*, package.*"). Join("INNER", "package_version", "package_version.id = package_file.version_id"). @@ -258,7 +265,7 @@ func getMavenPackageResultsToUpdate(ctx context.Context, ownerID *int64) ([]*mav // resolvePackageCollisions handles name collisions by keeping the first existing record and inserting new Package records for subsequent collisions. // Returns a map from PackageName to its resolved Package.ID. -func resolvePackageCollisions(ctx context.Context, results []*mavenPackageResult) error { +func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) error { // Group new names by lowerName collisions := make(map[string][]string) for _, r := range results { @@ -285,7 +292,7 @@ func resolvePackageCollisions(ctx context.Context, results []*mavenPackageResult } else if list[0] == r.PackageName { pkgIDByName[r.PackageName] = r.Package.ID - if _, err = db.GetEngine(ctx).ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { + if _, err = sess.ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { return err } // create a new entry @@ -293,7 +300,7 @@ func resolvePackageCollisions(ctx context.Context, results []*mavenPackageResult log.Info("Create new maven package for %s", r.Package.Name) r.Package.ID = 0 - if _, err = db.GetEngine(ctx).Insert(r.Package); err != nil { + if _, err = sess.Insert(r.Package); err != nil { return err } diff --git a/models/forgejo_migrations/v32_test.go b/models/forgejo_migrations/v32_test.go index 24cda891bc..cd33de2608 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 +package forgejo_migrations //nolint:revive import ( "bytes" diff --git a/models/forgejo_migrations/v33.go b/models/forgejo_migrations/v33.go index b9ea8efe47..272035fc23 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 +package forgejo_migrations //nolint:revive import ( "fmt" diff --git a/models/forgejo_migrations/v33_test.go b/models/forgejo_migrations/v33_test.go index 1d3298da15..664c704bbc 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 +package forgejo_migrations //nolint:revive import ( "testing" diff --git a/models/forgejo_migrations/v34.go b/models/forgejo_migrations/v34.go index d193d799e7..9e958b934f 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 +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v35.go b/models/forgejo_migrations/v35.go index 9b389fcc12..0fb3b43e2c 100644 --- a/models/forgejo_migrations/v35.go +++ b/models/forgejo_migrations/v35.go @@ -1,13 +1,19 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations +package forgejo_migrations //nolint:revive import ( + "forgejo.org/modules/timeutil" + "xorm.io/xorm" ) -// see https://codeberg.org/forgejo/forgejo/issues/8373 -func NoopAddIndexToActionRunStopped(x *xorm.Engine) error { - return nil +func AddIndexToActionRunStopped(x *xorm.Engine) error { + type ActionRun struct { + ID int64 + Stopped timeutil.TimeStamp `xorm:"index"` + } + + return x.Sync(&ActionRun{}) } diff --git a/models/forgejo_migrations/v36.go b/models/forgejo_migrations/v36.go deleted file mode 100644 index 1a798147cf..0000000000 --- a/models/forgejo_migrations/v36.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "xorm.io/xorm" -) - -func FixWikiUnitDefaultPermission(x *xorm.Engine) error { - // Type is Unit's Type - type Type int - - // Enumerate all the unit types - const ( - TypeInvalid Type = iota // 0 invalid - TypeCode // 1 code - TypeIssues // 2 issues - TypePullRequests // 3 PRs - TypeReleases // 4 Releases - TypeWiki // 5 Wiki - TypeExternalWiki // 6 ExternalWiki - TypeExternalTracker // 7 ExternalTracker - TypeProjects // 8 Projects - TypePackages // 9 Packages - TypeActions // 10 Actions - ) - - // RepoUnitAccessMode specifies the users access mode to a repo unit - type UnitAccessMode int - - const ( - // UnitAccessModeUnset - no unit mode set - UnitAccessModeUnset UnitAccessMode = iota // 0 - // UnitAccessModeNone no access - UnitAccessModeNone // 1 - // UnitAccessModeRead read access - UnitAccessModeRead // 2 - // UnitAccessModeWrite write access - UnitAccessModeWrite // 3 - ) - _ = UnitAccessModeNone - _ = UnitAccessModeWrite - - type RepoUnit struct { - DefaultPermissions UnitAccessMode `xorm:"NOT NULL DEFAULT 0"` - } - _, err := x.Where("type = ?", TypeWiki). - Where("default_permissions = ?", UnitAccessModeRead). - Cols("default_permissions"). - Update(RepoUnit{ - DefaultPermissions: UnitAccessModeUnset, - }) - return err -} diff --git a/models/forgejo_migrations/v37.go b/models/forgejo_migrations/v37.go deleted file mode 100644 index 89358991af..0000000000 --- a/models/forgejo_migrations/v37.go +++ /dev/null @@ -1,16 +0,0 @@ -// 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 deleted file mode 100644 index 24240f15a0..0000000000 --- a/models/forgejo_migrations/v38.go +++ /dev/null @@ -1,19 +0,0 @@ -// 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 deleted file mode 100644 index 9af1c250b3..0000000000 --- a/models/forgejo_migrations/v39.go +++ /dev/null @@ -1,78 +0,0 @@ -// 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 deleted file mode 100644 index 42934d912f..0000000000 --- a/models/forgejo_migrations/v39_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// 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 deleted file mode 100644 index 11e8fbd85e..0000000000 --- a/models/forgejo_migrations/v40.go +++ /dev/null @@ -1,13 +0,0 @@ -// 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 c4632fd4dd..d3643adeef 100644 --- a/models/issues/action_aggregator.go +++ b/models/issues/action_aggregator.go @@ -4,7 +4,6 @@ package issues import ( - "context" "slices" "forgejo.org/models/organization" @@ -375,10 +374,3 @@ 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 deleted file mode 100644 index 1962596d2d..0000000000 --- a/models/issues/action_aggregator_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// 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 6afd1623f3..a81221caf4 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -795,15 +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() - gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, c.Issue.Repo) - if err != nil { - return err + c.Commits = git_model.ParseCommitsWithStatus(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs), c.Issue.Repo) + c.CommitsNum = int64(len(c.Commits)) } - defer closer.Close() - c.Commits = git_model.ParseCommitsWithStatus(ctx, gitRepo.GetCommitsFromIDs(data.CommitIDs, c.IsForcePush), c.Issue.Repo) - c.CommitsNum = int64(len(c.Commits)) return err } @@ -1155,7 +1156,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, true); err != nil { + if err := IfNeededCreateShadowCopyForComment(ctx, c); err != nil { return err } @@ -1196,7 +1197,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, false); err != nil { + if err := IfNeededCreateShadowCopyForComment(ctx, comment); err != nil { return err } diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index 9b502d1c91..7285e347b4 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -101,7 +101,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { return nil } - milestones := make(map[int64]*Milestone, len(milestoneIDs)) + milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) left := len(milestoneIDs) for left > 0 { limit := db.DefaultMaxInSize @@ -110,7 +110,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { } err := db.GetEngine(ctx). In("id", milestoneIDs[:limit]). - Find(&milestones) + Find(&milestoneMaps) if err != nil { return err } @@ -118,8 +118,8 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { milestoneIDs = milestoneIDs[limit:] } - for _, comment := range comments { - comment.Milestone = milestones[comment.MilestoneID] + for _, issue := range comments { + issue.Milestone = milestoneMaps[issue.MilestoneID] } return nil } @@ -140,7 +140,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { return nil } - milestones := make(map[int64]*Milestone, len(milestoneIDs)) + milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) left := len(milestoneIDs) for left > 0 { limit := db.DefaultMaxInSize @@ -149,7 +149,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { } err := db.GetEngine(ctx). In("id", milestoneIDs[:limit]). - Find(&milestones) + Find(&milestoneMaps) if err != nil { return err } @@ -157,8 +157,8 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { milestoneIDs = milestoneIDs[limit:] } - for _, comment := range comments { - comment.OldMilestone = milestones[comment.OldMilestoneID] + for _, issue := range comments { + issue.OldMilestone = milestoneMaps[issue.MilestoneID] } return nil } diff --git a/models/issues/issue.go b/models/issues/issue.go index 14848e4c98..5edebb4105 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -237,7 +237,7 @@ func (issue *Issue) LoadPullRequest(ctx context.Context) (err error) { return nil } -func (issue *Issue) LoadComments(ctx context.Context) (err error) { +func (issue *Issue) loadComments(ctx context.Context) (err error) { return issue.loadCommentsByType(ctx, CommentTypeUndefined) } @@ -341,7 +341,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) { return err } - if err = issue.LoadComments(ctx); err != nil { + if err = issue.loadComments(ctx); err != nil { return err } @@ -469,8 +469,6 @@ func (issue *Issue) GetLastEventTimestamp() timeutil.TimeStamp { } // GetLastEventLabel returns the localization label for the current issue. -// -//llu:returnsTrKey func (issue *Issue) GetLastEventLabel() string { if issue.IsClosed { if issue.IsPull && issue.PullRequest.HasMerged { @@ -496,8 +494,6 @@ func (issue *Issue) GetLastComment(ctx context.Context) (*Comment, error) { } // GetLastEventLabelFake returns the localization label for the current issue without providing a link in the username. -// -//llu:returnsTrKey func (issue *Issue) GetLastEventLabelFake() string { if issue.IsClosed { if issue.IsPull && issue.PullRequest.HasMerged { diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 529f0c15d4..91a69c26a7 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -48,9 +48,7 @@ type IssuesOptions struct { //nolint UpdatedBeforeUnix int64 // prioritize issues from this repo PriorityRepoID int64 - // 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] + 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 @@ -62,7 +60,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, priorityIssueIndex int64) { +func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { switch sortType { case "oldest": sess.Asc("issue.created_unix").Asc("issue.id") @@ -99,11 +97,8 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID, priorityIss case "priorityrepo": sess.OrderBy("CASE "+ "WHEN issue.repo_id = ? THEN 1 "+ - "ELSE 2 END ASC", priorityRepoID) - if priorityIssueIndex != 0 { - sess.OrderBy("issue.index = ? DESC", priorityIssueIndex) - } - sess.Desc("issue.created_unix"). + "ELSE 2 END ASC", priorityRepoID). + Desc("issue.created_unix"). Desc("issue.id") case "project-column-sorting": sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id") @@ -475,7 +470,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, opts.PriorityIssueIndex) + applySorts(sess, opts.SortType, opts.PriorityRepoID) issues := IssueList{} if err := sess.Find(&issues); err != nil { @@ -499,7 +494,7 @@ func IssueIDs(ctx context.Context, opts *IssuesOptions, otherConds ...builder.Co } applyLimit(sess, opts) - applySorts(sess, opts.SortType, opts.PriorityRepoID, opts.PriorityIssueIndex) + applySorts(sess, opts.SortType, opts.PriorityRepoID) 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 9afb711d65..635d295db0 100644 --- a/models/issues/moderation.go +++ b/models/issues/moderation.go @@ -5,7 +5,6 @@ package issues import ( "context" - "strconv" "forgejo.org/models/moderation" "forgejo.org/modules/json" @@ -25,21 +24,6 @@ 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 { @@ -47,8 +31,8 @@ func newIssueData(issue *Issue) IssueData { RepoID: issue.RepoID, Index: issue.Index, PosterID: issue.PosterID, - Title: issue.Title, Content: issue.Content, + Title: issue.Title, ContentVersion: issue.ContentVersion, CreatedUnix: issue.CreatedUnix, UpdatedUnix: issue.UpdatedUnix, @@ -66,19 +50,6 @@ 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 { @@ -116,19 +87,13 @@ 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, forUpdates bool) error { +func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment) 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 deleted file mode 100644 index adb07bd63a..0000000000 --- a/models/issues/moderation_test.go +++ /dev/null @@ -1,70 +0,0 @@ -// 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 ddb813cf44..8fc0491026 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, 0) + applySorts(findSession, opts.SortType, 0) findSession = db.SetSessionPagination(findSession, opts) prs := make([]*PullRequest, 0, opts.PageSize) found := findSession.Find(&prs) diff --git a/models/issues/review.go b/models/issues/review.go index 5370117a81..584704d3e8 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -781,6 +781,10 @@ func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organizat official, err := IsOfficialReviewerTeam(ctx, issue, reviewer) if err != nil { return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err) + } else if !official { + if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil { + return nil, fmt.Errorf("isOfficialReviewer(): %w", err) + } } if review, err = CreateReview(ctx, CreateReviewOptions{ @@ -793,6 +797,12 @@ func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organizat return nil, err } + if official { + if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil { + return nil, err + } + } + comment, err := CreateComment(ctx, &CreateCommentOptions{ Type: CommentTypeReviewRequest, Doer: doer, diff --git a/models/issues/review_test.go b/models/issues/review_test.go index 6e2f6ddfa8..33d131c225 100644 --- a/models/issues/review_test.go +++ b/models/issues/review_test.go @@ -8,7 +8,6 @@ import ( "forgejo.org/models/db" issues_model "forgejo.org/models/issues" - organization_model "forgejo.org/models/organization" repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" @@ -320,80 +319,3 @@ func TestAddReviewRequest(t *testing.T) { require.Error(t, err) assert.True(t, issues_model.IsErrReviewRequestOnClosedPR(err)) } - -func TestAddTeamReviewRequest(t *testing.T) { - defer unittest.OverrideFixtures("models/fixtures/TestAddTeamReviewRequest")() - require.NoError(t, unittest.PrepareTestDatabase()) - - setupForProtectedBranch := func() (*issues_model.Issue, *user_model.User) { - // From override models/fixtures/TestAddTeamReviewRequest/issue.yml; issue #23 is a PR into a protected branch - issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 23}) - require.NoError(t, issue.LoadRepo(db.DefaultContext)) - doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) - return issue, doer - } - - t.Run("Protected branch, not official team", func(t *testing.T) { - issue, doer := setupForProtectedBranch() - // Team 2 is not part of the whitelist for this protected branch - team := unittest.AssertExistsAndLoadBean(t, &organization_model.Team{ID: 2}) - - comment, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer) - require.NoError(t, err) - require.NotNil(t, comment) - - review, err := issues_model.GetTeamReviewerByIssueIDAndTeamID(db.DefaultContext, issue.ID, team.ID) - require.NoError(t, err) - require.NotNil(t, review) - assert.Equal(t, issues_model.ReviewTypeRequest, review.Type) - assert.Equal(t, team.ID, review.ReviewerTeamID) - // This review request should not be marked official because it is not a request for a team in the branch - // protection rule's whitelist... - assert.False(t, review.Official) - }) - - t.Run("Protected branch, official team", func(t *testing.T) { - issue, doer := setupForProtectedBranch() - // Team 1 is part of the whitelist for this protected branch - team := unittest.AssertExistsAndLoadBean(t, &organization_model.Team{ID: 1}) - - comment, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer) - require.NoError(t, err) - require.NotNil(t, comment) - - review, err := issues_model.GetTeamReviewerByIssueIDAndTeamID(db.DefaultContext, issue.ID, team.ID) - require.NoError(t, err) - require.NotNil(t, review) - assert.Equal(t, issues_model.ReviewTypeRequest, review.Type) - assert.Equal(t, team.ID, review.ReviewerTeamID) - // Expected to be considered official because team 1 is in the review whitelist for this protected branch - assert.True(t, review.Official) - }) - - t.Run("Unprotected branch, official team", func(t *testing.T) { - // Working on a PR into a branch that is not protected, issue #2 - issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) - require.NoError(t, issue.LoadRepo(db.DefaultContext)) - doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - // team is a team that has write perms against the repo - team := unittest.AssertExistsAndLoadBean(t, &organization_model.Team{ID: 1}) - - comment, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer) - require.NoError(t, err) - require.NotNil(t, comment) - - review, err := issues_model.GetTeamReviewerByIssueIDAndTeamID(db.DefaultContext, issue.ID, team.ID) - require.NoError(t, err) - require.NotNil(t, review) - assert.Equal(t, issues_model.ReviewTypeRequest, review.Type) - assert.Equal(t, team.ID, review.ReviewerTeamID) - // Will not be marked as official because PR #2 there's no branch protection rule that enables whitelist - // approvals (verifying logic in `IsOfficialReviewerTeam` indirectly) - assert.False(t, review.Official) - - // Adding the same team review request again should be a noop - comment, err = issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer) - require.NoError(t, err) - require.Nil(t, comment) - }) -} diff --git a/models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml b/models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml deleted file mode 100644 index 908b428321..0000000000 --- a/models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml +++ /dev/null @@ -1,14 +0,0 @@ -- - 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 6be3b3c2fc..c1f0caf19b 100644 --- a/models/migrations/test/tests.go +++ b/models/migrations/test/tests.go @@ -95,8 +95,7 @@ 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, - SkipCleanRegistedModels: true, + Dir: fixturesDir, }, 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 1742bea296..5d2fd8e244 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 +package v1_10 //nolint import ( "net/url" diff --git a/models/migrations/v1_10/v101.go b/models/migrations/v1_10/v101.go index 6c8dfe2486..f023a2a0e7 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 +package v1_10 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_10/v88.go b/models/migrations/v1_10/v88.go index eb8e81c19e..7e86ac364f 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 +package v1_10 //nolint import ( "crypto/sha1" diff --git a/models/migrations/v1_10/v89.go b/models/migrations/v1_10/v89.go index 0df2a6e17b..d5f27ffdc6 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 +package v1_10 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v90.go b/models/migrations/v1_10/v90.go index 5521a97e32..295d4b1c1b 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 +package v1_10 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v91.go b/models/migrations/v1_10/v91.go index 08db6c2742..48cac2de70 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 +package v1_10 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v92.go b/models/migrations/v1_10/v92.go index b6c04a9234..9080108594 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 +package v1_10 //nolint import ( "xorm.io/builder" diff --git a/models/migrations/v1_10/v93.go b/models/migrations/v1_10/v93.go index c131be9a8d..ee59a8db39 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 +package v1_10 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v94.go b/models/migrations/v1_10/v94.go index 13b7d7b303..c131af162b 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 +package v1_10 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v95.go b/models/migrations/v1_10/v95.go index 86b52026bf..3b1f67fd9c 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 +package v1_10 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v96.go b/models/migrations/v1_10/v96.go index bcbd618b49..3bfb770f24 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 +package v1_10 //nolint import ( "path/filepath" diff --git a/models/migrations/v1_10/v97.go b/models/migrations/v1_10/v97.go index 5872bb63e5..dee45b32e3 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 +package v1_10 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v98.go b/models/migrations/v1_10/v98.go index d21c326459..bdd9aed089 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 +package v1_10 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v99.go b/models/migrations/v1_10/v99.go index addae66be9..7f287b77aa 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 +package v1_10 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_11/v102.go b/models/migrations/v1_11/v102.go index 15f0c83c36..a585d9c423 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 +package v1_11 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_11/v103.go b/models/migrations/v1_11/v103.go index a515710160..53527dac58 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 +package v1_11 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v104.go b/models/migrations/v1_11/v104.go index 7461f0cda3..af3578ca4a 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 +package v1_11 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_11/v105.go b/models/migrations/v1_11/v105.go index d86973a0f6..b91340c30a 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 +package v1_11 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v106.go b/models/migrations/v1_11/v106.go index edffe18683..ecb11cdd1e 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 +package v1_11 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v107.go b/models/migrations/v1_11/v107.go index a158e3bb50..f0bfe5862c 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 +package v1_11 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v108.go b/models/migrations/v1_11/v108.go index 8f14504ceb..a85096234d 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 +package v1_11 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v109.go b/models/migrations/v1_11/v109.go index f7616aec7b..ea565ccda3 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 +package v1_11 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v110.go b/models/migrations/v1_11/v110.go index e94a738f67..fce9be847e 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 +package v1_11 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go index 6f531e4858..cc3dc0d545 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 +package v1_11 //nolint import ( "fmt" diff --git a/models/migrations/v1_11/v112.go b/models/migrations/v1_11/v112.go index 22054e6f68..6112ab51a5 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 +package v1_11 //nolint import ( "fmt" diff --git a/models/migrations/v1_11/v113.go b/models/migrations/v1_11/v113.go index a4d54f66fb..dea344a44f 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 +package v1_11 //nolint import ( "fmt" diff --git a/models/migrations/v1_11/v114.go b/models/migrations/v1_11/v114.go index 9467a8a90c..95adcee989 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 +package v1_11 //nolint import ( "net/url" diff --git a/models/migrations/v1_11/v115.go b/models/migrations/v1_11/v115.go index 65094df93d..3d4b41017b 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 +package v1_11 //nolint import ( "crypto/md5" diff --git a/models/migrations/v1_11/v116.go b/models/migrations/v1_11/v116.go index 729fbad18b..85aa76c1e0 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 +package v1_11 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v117.go b/models/migrations/v1_12/v117.go index 73b58ca34b..8eadcdef2b 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 +package v1_12 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v118.go b/models/migrations/v1_12/v118.go index e8b4249743..eb022dc5e4 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 +package v1_12 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v119.go b/models/migrations/v1_12/v119.go index b4bf29a935..60bfe6a57d 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 +package v1_12 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v120.go b/models/migrations/v1_12/v120.go index 14d515f5a7..3f7ed8d373 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 +package v1_12 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v121.go b/models/migrations/v1_12/v121.go index a28ae4e1c9..175ec9164d 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 +package v1_12 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_12/v122.go b/models/migrations/v1_12/v122.go index bc1b175f6a..6e31d863a1 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 +package v1_12 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v123.go b/models/migrations/v1_12/v123.go index 52b10bb850..b0c3af07a3 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 +package v1_12 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v124.go b/models/migrations/v1_12/v124.go index 9a93f436d4..d2ba03ffe0 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 +package v1_12 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v125.go b/models/migrations/v1_12/v125.go index 7f582ecff5..ec4ffaab25 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 +package v1_12 //nolint import ( "fmt" diff --git a/models/migrations/v1_12/v126.go b/models/migrations/v1_12/v126.go index 64fd7f7478..ca9ec3aa3f 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 +package v1_12 //nolint import ( "xorm.io/builder" diff --git a/models/migrations/v1_12/v127.go b/models/migrations/v1_12/v127.go index f686fa617c..11a4042973 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 +package v1_12 //nolint import ( "fmt" diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go index 8fca974616..6d7307f470 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 +package v1_12 //nolint import ( "fmt" diff --git a/models/migrations/v1_12/v129.go b/models/migrations/v1_12/v129.go index 3e4d3aca68..cf228242b9 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 +package v1_12 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v130.go b/models/migrations/v1_12/v130.go index 383ef47492..bfa856796a 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 +package v1_12 //nolint import ( "forgejo.org/modules/json" diff --git a/models/migrations/v1_12/v131.go b/models/migrations/v1_12/v131.go index 1266c2f185..5184bc3590 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 +package v1_12 //nolint import ( "fmt" diff --git a/models/migrations/v1_12/v132.go b/models/migrations/v1_12/v132.go index 8b1ae6db93..3b2b28f7ab 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 +package v1_12 //nolint import ( "fmt" diff --git a/models/migrations/v1_12/v133.go b/models/migrations/v1_12/v133.go index 69e20597d8..c9087fc8c1 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 +package v1_12 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go index 1fabdcae96..bba996fd40 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 +package v1_12 //nolint import ( "fmt" diff --git a/models/migrations/v1_12/v135.go b/models/migrations/v1_12/v135.go index 5df0ad7fc4..8898011df5 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 +package v1_12 //nolint import ( "fmt" diff --git a/models/migrations/v1_12/v136.go b/models/migrations/v1_12/v136.go index 7d246a82be..e2557ae002 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 +package v1_12 //nolint import ( "fmt" diff --git a/models/migrations/v1_12/v137.go b/models/migrations/v1_12/v137.go index 9d38483488..0d86b72010 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 +package v1_12 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v138.go b/models/migrations/v1_12/v138.go index 4485adeb2d..8c8d353f40 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 +package v1_12 //nolint import ( "fmt" diff --git a/models/migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go index 51e57b984a..cd7963524e 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 +package v1_12 //nolint import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_13/v140.go b/models/migrations/v1_13/v140.go index 5bb612c098..d74f808e9f 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 +package v1_13 //nolint import ( "fmt" diff --git a/models/migrations/v1_13/v141.go b/models/migrations/v1_13/v141.go index b54bc1727c..ae211e0e44 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 +package v1_13 //nolint import ( "fmt" diff --git a/models/migrations/v1_13/v142.go b/models/migrations/v1_13/v142.go index 8939f6f2f8..7490e0f3b4 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 +package v1_13 //nolint import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_13/v143.go b/models/migrations/v1_13/v143.go index 6a8da8b06d..1f9120e2ba 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 +package v1_13 //nolint import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_13/v144.go b/models/migrations/v1_13/v144.go index f138338514..7e801eab8a 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 +package v1_13 //nolint import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go index f7d3895c84..a01f577ed1 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 +package v1_13 //nolint import ( "fmt" diff --git a/models/migrations/v1_13/v146.go b/models/migrations/v1_13/v146.go index e6a476a288..a1b54ee3aa 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 +package v1_13 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_13/v147.go b/models/migrations/v1_13/v147.go index 831ef5842a..cc57504c74 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 +package v1_13 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_13/v148.go b/models/migrations/v1_13/v148.go index d276db3d61..7bb8ab700b 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 +package v1_13 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_13/v149.go b/models/migrations/v1_13/v149.go index c1bfe8b09e..3a0c5909d5 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 +package v1_13 //nolint import ( "fmt" diff --git a/models/migrations/v1_13/v150.go b/models/migrations/v1_13/v150.go index 471a531024..be14fd130c 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 +package v1_13 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go index 691b86062d..ff584fff67 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 +package v1_13 //nolint import ( "context" diff --git a/models/migrations/v1_13/v152.go b/models/migrations/v1_13/v152.go index 648e26446f..502c82a40d 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 +package v1_13 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_13/v153.go b/models/migrations/v1_13/v153.go index e5462fc162..0b2dd3eb62 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 +package v1_13 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_13/v154.go b/models/migrations/v1_13/v154.go index 89dc7821b2..cf31190781 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 +package v1_13 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_14/main_test.go b/models/migrations/v1_14/main_test.go index 57cf995be1..c01faedc35 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 +package v1_14 //nolint import ( "testing" diff --git a/models/migrations/v1_14/v155.go b/models/migrations/v1_14/v155.go index 505a9ae033..e814f59938 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v156.go b/models/migrations/v1_14/v156.go index 7bbd9f4c85..b6dc91a054 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v157.go b/models/migrations/v1_14/v157.go index ba69f71130..7187278d29 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 +package v1_14 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go index 2ab3c8a1f0..3fa27cfecd 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 +package v1_14 //nolint import ( "errors" diff --git a/models/migrations/v1_14/v159.go b/models/migrations/v1_14/v159.go index 4e921ea1c6..fdd7e12449 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 +package v1_14 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v160.go b/models/migrations/v1_14/v160.go index 73f3798954..4dea91b514 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 +package v1_14 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v161.go b/models/migrations/v1_14/v161.go index 9c850ad0c2..6e904cfab6 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 +package v1_14 //nolint import ( "context" diff --git a/models/migrations/v1_14/v162.go b/models/migrations/v1_14/v162.go index ead63f16f4..5d6d7c2e3f 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 +package v1_14 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v163.go b/models/migrations/v1_14/v163.go index 06ac36cbc7..60fc98c0a4 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 +package v1_14 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v164.go b/models/migrations/v1_14/v164.go index d2fd9b8464..54f6951427 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v165.go b/models/migrations/v1_14/v165.go index 90fd2b1e46..9315e44197 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 +package v1_14 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go index 4c106bd7da..e5731582fd 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 +package v1_14 //nolint import ( "crypto/sha256" diff --git a/models/migrations/v1_14/v167.go b/models/migrations/v1_14/v167.go index d77bbc401e..9d416f6a32 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v168.go b/models/migrations/v1_14/v168.go index aa93eec19b..a30a8859f7 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 +package v1_14 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_14/v169.go b/models/migrations/v1_14/v169.go index 4f9df0d96f..5b81bb58b1 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 +package v1_14 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v170.go b/models/migrations/v1_14/v170.go index a2ff4623e1..7b6498a3e9 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v171.go b/models/migrations/v1_14/v171.go index 7b200e960a..51a35a02ad 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v172.go b/models/migrations/v1_14/v172.go index c410d393f1..d49b70f5ad 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 +package v1_14 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_14/v173.go b/models/migrations/v1_14/v173.go index 7752fbe966..2d9eee9197 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v174.go b/models/migrations/v1_14/v174.go index 4049e43070..c839e15db8 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v175.go b/models/migrations/v1_14/v175.go index 49fa17d046..3cda5772a0 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v176.go b/models/migrations/v1_14/v176.go index ef5dce9a02..1ed49f75fa 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 +package v1_14 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v176_test.go b/models/migrations/v1_14/v176_test.go index d56b3e0470..d88ff207e7 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 +package v1_14 //nolint import ( "testing" diff --git a/models/migrations/v1_14/v177.go b/models/migrations/v1_14/v177.go index 96676bf8d9..6e1838f369 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 +package v1_14 //nolint import ( "fmt" diff --git a/models/migrations/v1_14/v177_test.go b/models/migrations/v1_14/v177_test.go index 0e0a67fd33..bffc6f92e3 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 +package v1_14 //nolint import ( "testing" diff --git a/models/migrations/v1_15/main_test.go b/models/migrations/v1_15/main_test.go index 4cf6d6f695..6c04d3f5ee 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 +package v1_15 //nolint import ( "testing" diff --git a/models/migrations/v1_15/v178.go b/models/migrations/v1_15/v178.go index ca3a5c262e..6d236eb049 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 +package v1_15 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v179.go b/models/migrations/v1_15/v179.go index ce514cc4a9..b990583303 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 +package v1_15 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_15/v180.go b/models/migrations/v1_15/v180.go index 0b68c3ceb7..02fbd57cdb 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 +package v1_15 //nolint import ( "forgejo.org/modules/json" diff --git a/models/migrations/v1_15/v181.go b/models/migrations/v1_15/v181.go index fb1d3d7a75..2185ed0213 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 +package v1_15 //nolint import ( "strings" diff --git a/models/migrations/v1_15/v181_test.go b/models/migrations/v1_15/v181_test.go index 8196f751e5..4154e0b1e9 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 +package v1_15 //nolint import ( "strings" diff --git a/models/migrations/v1_15/v182.go b/models/migrations/v1_15/v182.go index f53ff11df9..9ca500c0f9 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 +package v1_15 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v182_test.go b/models/migrations/v1_15/v182_test.go index 2baf90d06a..6865cafac4 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 +package v1_15 //nolint import ( "testing" diff --git a/models/migrations/v1_15/v183.go b/models/migrations/v1_15/v183.go index 5684e35699..aaad64c220 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 +package v1_15 //nolint import ( "fmt" diff --git a/models/migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go index fbe0dcd780..41b64d4743 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 +package v1_15 //nolint import ( "context" diff --git a/models/migrations/v1_15/v185.go b/models/migrations/v1_15/v185.go index 60af59edca..e5878ec193 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 +package v1_15 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v186.go b/models/migrations/v1_15/v186.go index 55d3199335..ad75822de5 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 +package v1_15 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_15/v187.go b/models/migrations/v1_15/v187.go index fabef14779..b573fc52ef 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 +package v1_15 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_15/v188.go b/models/migrations/v1_15/v188.go index 4494e6ff05..71e45cab0e 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 +package v1_15 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_16/main_test.go b/models/migrations/v1_16/main_test.go index 8c0a043be6..6f891f3e94 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 +package v1_16 //nolint import ( "testing" diff --git a/models/migrations/v1_16/v189.go b/models/migrations/v1_16/v189.go index 19bfcb2423..1ee72d9c39 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 +package v1_16 //nolint import ( "encoding/binary" diff --git a/models/migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go index 9d74462a92..90b721d5f1 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 +package v1_16 //nolint import ( "testing" diff --git a/models/migrations/v1_16/v190.go b/models/migrations/v1_16/v190.go index 1eb6b6ddb4..5953802849 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 +package v1_16 //nolint import ( "fmt" diff --git a/models/migrations/v1_16/v191.go b/models/migrations/v1_16/v191.go index 427476b70b..567f88d6d1 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 +package v1_16 //nolint import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_16/v192.go b/models/migrations/v1_16/v192.go index 31e8c36346..731b9fb43a 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 +package v1_16 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_16/v193.go b/models/migrations/v1_16/v193.go index a5af2de380..8d3ce7a558 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 +package v1_16 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go index bf8d8a7dc6..8260acf32d 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 +package v1_16 //nolint import ( "testing" diff --git a/models/migrations/v1_16/v194.go b/models/migrations/v1_16/v194.go index 2e4ed8340e..6aa13c50cf 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 +package v1_16 //nolint import ( "fmt" diff --git a/models/migrations/v1_16/v195.go b/models/migrations/v1_16/v195.go index 4fd42b7bd2..6d7e94141e 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 +package v1_16 //nolint import ( "fmt" diff --git a/models/migrations/v1_16/v195_test.go b/models/migrations/v1_16/v195_test.go index 1fc7b51f3c..71234a6fb3 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 +package v1_16 //nolint import ( "testing" diff --git a/models/migrations/v1_16/v196.go b/models/migrations/v1_16/v196.go index 6c9caa100f..7cbafc61e5 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 +package v1_16 //nolint import ( "fmt" diff --git a/models/migrations/v1_16/v197.go b/models/migrations/v1_16/v197.go index 862bdfdcbd..97888b2847 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 +package v1_16 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v198.go b/models/migrations/v1_16/v198.go index 5d3043eb46..8b3c73addc 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 +package v1_16 //nolint import ( "fmt" diff --git a/models/migrations/v1_16/v199.go b/models/migrations/v1_16/v199.go index 4020352f2b..6adcf890af 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 +package v1_16 //nolint // 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 de57fad8fe..c08c20e51d 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 +package v1_16 //nolint import ( "fmt" diff --git a/models/migrations/v1_16/v201.go b/models/migrations/v1_16/v201.go index 2c43698b0c..35e0c9f2fb 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 +package v1_16 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v202.go b/models/migrations/v1_16/v202.go index d8c8fdcadc..6ba36152f1 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 +package v1_16 //nolint import ( "fmt" diff --git a/models/migrations/v1_16/v203.go b/models/migrations/v1_16/v203.go index c3241cba57..e8e6b52453 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 +package v1_16 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v204.go b/models/migrations/v1_16/v204.go index 4d375307e7..ece03e1305 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 +package v1_16 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_16/v205.go b/models/migrations/v1_16/v205.go index cb452dfd7f..a064b9830d 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 +package v1_16 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_16/v206.go b/models/migrations/v1_16/v206.go index 01a9c386eb..581a7d76e9 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 +package v1_16 //nolint import ( "fmt" diff --git a/models/migrations/v1_16/v207.go b/models/migrations/v1_16/v207.go index 19126ead1f..91208f066c 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 +package v1_16 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v208.go b/models/migrations/v1_16/v208.go index fb643324f4..1a11ef096a 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 +package v1_16 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v209.go b/models/migrations/v1_16/v209.go index 230838647b..be3100e02a 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 +package v1_16 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go index f48ab11db6..375a008e18 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 +package v1_16 //nolint import ( "crypto/ecdh" diff --git a/models/migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go index 8454920aa0..f6423a5821 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 +package v1_16 //nolint import ( "encoding/hex" diff --git a/models/migrations/v1_17/main_test.go b/models/migrations/v1_17/main_test.go index 166860b3b1..0a8e05ab5f 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 +package v1_17 //nolint import ( "testing" diff --git a/models/migrations/v1_17/v211.go b/models/migrations/v1_17/v211.go index 517cf19388..9b72c8610b 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 +package v1_17 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v212.go b/models/migrations/v1_17/v212.go index 23868c0bb2..2337adcc80 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 +package v1_17 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_17/v213.go b/models/migrations/v1_17/v213.go index b2bbdf7279..bb3f466e52 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 +package v1_17 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v214.go b/models/migrations/v1_17/v214.go index 1925324f0f..2268164919 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 +package v1_17 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v215.go b/models/migrations/v1_17/v215.go index 431103c98e..5aae798562 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 +package v1_17 //nolint import ( "forgejo.org/models/pull" diff --git a/models/migrations/v1_17/v216.go b/models/migrations/v1_17/v216.go index 37aeacb6fc..268f472a42 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 +package v1_17 //nolint // 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 fef48b7a5b..5f096d4824 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 +package v1_17 //nolint import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_17/v218.go b/models/migrations/v1_17/v218.go index 412d124286..5e3dcd0841 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 +package v1_17 //nolint import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_17/v219.go b/models/migrations/v1_17/v219.go index 7ca6a26be6..e90656090f 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 +package v1_17 //nolint import ( "time" diff --git a/models/migrations/v1_17/v220.go b/models/migrations/v1_17/v220.go index 4e010e5b76..61bbf19725 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 +package v1_17 //nolint import ( packages_model "forgejo.org/models/packages" diff --git a/models/migrations/v1_17/v221.go b/models/migrations/v1_17/v221.go index 3ef34e3f06..84e9a238af 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 +package v1_17 //nolint import ( "encoding/base32" diff --git a/models/migrations/v1_17/v221_test.go b/models/migrations/v1_17/v221_test.go index a9c47136b2..02607d6b32 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 +package v1_17 //nolint import ( "encoding/base32" diff --git a/models/migrations/v1_17/v222.go b/models/migrations/v1_17/v222.go index 873769881e..ae910cbcb6 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 +package v1_17 //nolint import ( "context" diff --git a/models/migrations/v1_17/v223.go b/models/migrations/v1_17/v223.go index 4f5d34d841..7d92dcf5ae 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 +package v1_17 //nolint import ( "context" diff --git a/models/migrations/v1_18/main_test.go b/models/migrations/v1_18/main_test.go index 0c20934cea..33f5c51222 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 +package v1_18 //nolint import ( "testing" diff --git a/models/migrations/v1_18/v224.go b/models/migrations/v1_18/v224.go index 6dc12020ea..f3d522b91a 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 +package v1_18 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_18/v225.go b/models/migrations/v1_18/v225.go index 266eccfff8..86bcb1323d 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 +package v1_18 //nolint import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_18/v226.go b/models/migrations/v1_18/v226.go index 8ed9761476..f87e24b11d 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 +package v1_18 //nolint import ( "xorm.io/builder" diff --git a/models/migrations/v1_18/v227.go b/models/migrations/v1_18/v227.go index d39a010159..b6250fb76c 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 +package v1_18 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_18/v228.go b/models/migrations/v1_18/v228.go index 3f5b69734d..1161c8a4c9 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 +package v1_18 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_18/v229.go b/models/migrations/v1_18/v229.go index 00d794725f..f96dde9840 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 +package v1_18 //nolint import ( "fmt" diff --git a/models/migrations/v1_18/v229_test.go b/models/migrations/v1_18/v229_test.go index 903a60c851..ac5e726a79 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 +package v1_18 //nolint import ( "testing" diff --git a/models/migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go index 078fce7643..ea5b4d02e1 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 +package v1_18 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go index da31b0dc9b..7dd6675673 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 +package v1_18 //nolint import ( "testing" diff --git a/models/migrations/v1_19/main_test.go b/models/migrations/v1_19/main_test.go index 9d1c3a57ea..7c56926f4c 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 +package v1_19 //nolint import ( "testing" diff --git a/models/migrations/v1_19/v231.go b/models/migrations/v1_19/v231.go index 8ef1e4e743..79e46132f0 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 +package v1_19 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v232.go b/models/migrations/v1_19/v232.go index 2aab2cf830..7fb4a5ac8d 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 +package v1_19 //nolint import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_19/v233.go b/models/migrations/v1_19/v233.go index e62e8a9356..191afd4868 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 +package v1_19 //nolint import ( "fmt" diff --git a/models/migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go index 3d5eac9887..4dc35d1e27 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 +package v1_19 //nolint import ( "testing" diff --git a/models/migrations/v1_19/v234.go b/models/migrations/v1_19/v234.go index e00b1cc2b6..c610a423dd 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 +package v1_19 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_19/v235.go b/models/migrations/v1_19/v235.go index 297d90f65a..3715de3920 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 +package v1_19 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go index c453f95e04..fa01a6ab80 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 +package v1_19 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_19/v237.go b/models/migrations/v1_19/v237.go index cf30226ccd..b23c765aa5 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 +package v1_19 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v238.go b/models/migrations/v1_19/v238.go index b257315319..7c912a8341 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 +package v1_19 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go index 8f4a65be95..10076f2401 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 +package v1_19 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v240.go b/models/migrations/v1_19/v240.go index c49ce2f49a..4ca5becede 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 +package v1_19 //nolint import ( "forgejo.org/models/db" diff --git a/models/migrations/v1_19/v241.go b/models/migrations/v1_19/v241.go index e35801a057..a617d6fd2f 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 +package v1_19 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v242.go b/models/migrations/v1_19/v242.go index 87ca9cf214..bbf227ef77 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 +package v1_19 //nolint import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_19/v243.go b/models/migrations/v1_19/v243.go index 9c3f372594..55bbfafb2f 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 +package v1_19 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/main_test.go b/models/migrations/v1_20/main_test.go index ee5eec5ef6..f870dca429 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 +package v1_20 //nolint import ( "testing" diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go index 76cdccaca5..977566ad7d 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 +package v1_20 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go index 5e034568c4..7e6585388b 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 +package v1_20 //nolint import ( "context" diff --git a/models/migrations/v1_20/v246.go b/models/migrations/v1_20/v246.go index 22bf723404..e6340ef079 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 +package v1_20 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v247.go b/models/migrations/v1_20/v247.go index 056699d744..9ed810a623 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 +package v1_20 //nolint import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v248.go b/models/migrations/v1_20/v248.go index 4f2091e4bc..40555210e7 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 +package v1_20 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_20/v249.go b/models/migrations/v1_20/v249.go index 0aebb2a343..d2b096bf58 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 +package v1_20 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go index e12223691f..cfcde2fc9b 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 +package v1_20 //nolint import ( "strings" diff --git a/models/migrations/v1_20/v251.go b/models/migrations/v1_20/v251.go index 7d2d259df6..c8665ba7eb 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 +package v1_20 //nolint import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v252.go b/models/migrations/v1_20/v252.go index 435cce7ebe..bb85c78309 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 +package v1_20 //nolint import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v253.go b/models/migrations/v1_20/v253.go index 73354fd485..5f4057e9d9 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 +package v1_20 //nolint import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v254.go b/models/migrations/v1_20/v254.go index 9cdbfb3916..1e26979a5b 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 +package v1_20 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v255.go b/models/migrations/v1_20/v255.go index baa3c4b6d8..49b0ecf220 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 +package v1_20 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_20/v256.go b/models/migrations/v1_20/v256.go index 7b84c1e154..822153b93e 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 +package v1_20 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go index 8045909dba..70f229d73f 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 +package v1_20 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_20/v258.go b/models/migrations/v1_20/v258.go index 1d3faffdae..47174ce805 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 +package v1_20 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v259.go b/models/migrations/v1_20/v259.go index 9b2b68263e..f10b94fa9c 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 +package v1_20 //nolint import ( "fmt" diff --git a/models/migrations/v1_20/v259_test.go b/models/migrations/v1_20/v259_test.go index b41b6c7995..32e4aa3050 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 +package v1_20 //nolint import ( "sort" diff --git a/models/migrations/v1_21/main_test.go b/models/migrations/v1_21/main_test.go index 3f10a39a94..7104887afb 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 +package v1_21 //nolint import ( "testing" diff --git a/models/migrations/v1_21/v260.go b/models/migrations/v1_21/v260.go index b73b53bd61..245f3011ab 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 +package v1_21 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_21/v261.go b/models/migrations/v1_21/v261.go index 83a4927704..743bef152d 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 +package v1_21 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v262.go b/models/migrations/v1_21/v262.go index 6e88e29b9d..23e900572a 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v263.go b/models/migrations/v1_21/v263.go index 55c418bde0..2c7cbadf0d 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 +package v1_21 //nolint import ( "fmt" diff --git a/models/migrations/v1_21/v264.go b/models/migrations/v1_21/v264.go index acd2c9bb48..5615600072 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 +package v1_21 //nolint import ( "context" diff --git a/models/migrations/v1_21/v265.go b/models/migrations/v1_21/v265.go index b6892acc27..800eb95f72 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v266.go b/models/migrations/v1_21/v266.go index 440549e868..79a5f5e14c 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v267.go b/models/migrations/v1_21/v267.go index 13992d8776..f94696a22b 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 +package v1_21 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v268.go b/models/migrations/v1_21/v268.go index b677d2383e..332793ff07 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v269.go b/models/migrations/v1_21/v269.go index 042040927d..475ec02380 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v270.go b/models/migrations/v1_21/v270.go index ab7c5660ba..b9cc84d3ac 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v271.go b/models/migrations/v1_21/v271.go index e3ce2d4b74..f45c113c1f 100644 --- a/models/migrations/v1_21/v271.go +++ b/models/migrations/v1_21/v271.go @@ -1,8 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 - +package v1_21 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v272.go b/models/migrations/v1_21/v272.go index 14c1e0c4b0..a729c49f1b 100644 --- a/models/migrations/v1_21/v272.go +++ b/models/migrations/v1_21/v272.go @@ -1,8 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 - +package v1_21 //nolint import ( "xorm.io/xorm" ) diff --git a/models/migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go index d6ec80d3d5..1ec6ade566 100644 --- a/models/migrations/v1_21/v273.go +++ b/models/migrations/v1_21/v273.go @@ -1,8 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 - +package v1_21 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v274.go b/models/migrations/v1_21/v274.go index a1211d1fdd..b74e5fed51 100644 --- a/models/migrations/v1_21/v274.go +++ b/models/migrations/v1_21/v274.go @@ -1,8 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 - +package v1_21 //nolint import ( "time" diff --git a/models/migrations/v1_21/v275.go b/models/migrations/v1_21/v275.go index 2bfe5c72fa..78804a59d6 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go index 3b0bc23da7..0830c3bd92 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 +package v1_21 //nolint import ( repo_model "forgejo.org/models/repo" diff --git a/models/migrations/v1_21/v277.go b/models/migrations/v1_21/v277.go index 0c102eddde..12529160b7 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v278.go b/models/migrations/v1_21/v278.go index 846f228678..d6a462d1e7 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v279.go b/models/migrations/v1_21/v279.go index beb39effe1..2abd1bbe84 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 +package v1_21 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/main_test.go b/models/migrations/v1_22/main_test.go index 7b05993e09..dc991b78fe 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 +package v1_22 //nolint import ( "testing" diff --git a/models/migrations/v1_22/v280.go b/models/migrations/v1_22/v280.go index 2271cb6089..a8ee4a3bf7 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v281.go b/models/migrations/v1_22/v281.go index 2eeca9be82..5271c786be 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 +package v1_22 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_22/v282.go b/models/migrations/v1_22/v282.go index eed64c30f7..baad9e0916 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go index 33a2513069..86946d1c39 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v283_test.go b/models/migrations/v1_22/v283_test.go index 652d96ac16..d8e147a131 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 +package v1_22 //nolint import ( "testing" diff --git a/models/migrations/v1_22/v284.go b/models/migrations/v1_22/v284.go index 31b38f6aed..2b95078980 100644 --- a/models/migrations/v1_22/v284.go +++ b/models/migrations/v1_22/v284.go @@ -1,8 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 - +package v1_22 //nolint import ( "xorm.io/xorm" ) diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go index fed89f670e..a55cc17c04 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 +package v1_22 //nolint import ( "time" diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go index 05247bb436..d0489e7aeb 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 +package v1_22 //nolint import ( "fmt" diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go index 5bb3334df2..c63deef495 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 +package v1_22 //nolint import ( "testing" diff --git a/models/migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go index 5fd901f9de..c8b1593286 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v288.go b/models/migrations/v1_22/v288.go index 78be3b6ef2..44e4991851 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 +package v1_22 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go index 78689a4ffa..b9941aadd9 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 +package v1_22 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go index ebafab6567..594e417644 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 +package v1_22 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_22/v290_test.go b/models/migrations/v1_22/v290_test.go index a1907cf4d6..569d77bc16 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 +package v1_22 //nolint import ( "strconv" diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go index 823a644a95..74726fae96 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 +package v1_22 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go index 440f48ce80..beca556aee 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 +package v1_22 //nolint // 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 e9c9746b26..9f38c3db56 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 +package v1_22 //nolint import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go index 6b1931b761..444146737d 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 +package v1_22 //nolint import ( "testing" diff --git a/models/migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go index 6c52372306..314b4519f1 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 +package v1_22 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go index e87a4bc85f..ef7b67ca5b 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 +package v1_22 //nolint import ( "slices" @@ -45,8 +45,7 @@ func Test_AddUniqueIndexForProjectIssue(t *testing.T) { for _, index := range tables[0].Indexes { if index.Type == schemas.UniqueType { found = true - slices.Sort(index.Cols) - assert.Equal(t, []string{"issue_id", "project_id"}, index.Cols) + slices.Equal(index.Cols, []string{"project_id", "issue_id"}) break } } diff --git a/models/migrations/v1_22/v295.go b/models/migrations/v1_22/v295.go index 319b1a399b..17bdadb4ad 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 +package v1_22 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go index 75350f9f65..1ecacab95f 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 +package v1_22 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go index 7700173a00..b9f3b95ade 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 +package v1_22 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go index 5fb4fec999..0fd90a4a67 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 +package v1_23 //nolint import ( "testing" diff --git a/models/migrations/v1_23/v299.go b/models/migrations/v1_23/v299.go index 73ce19c875..f6db960c3b 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 +package v1_23 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v300.go b/models/migrations/v1_23/v300.go index 404d8dbea8..f1f1cccdbf 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 +package v1_23 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v301.go b/models/migrations/v1_23/v301.go index f2a4d8c559..b7797f6c6b 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 +package v1_23 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v302.go b/models/migrations/v1_23/v302.go index 1b056993bd..c8ed786d63 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 +package v1_23 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_23/v303.go b/models/migrations/v1_23/v303.go index 03197d2857..fae0131bdd 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 +package v1_23 //nolint 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 f2c764bae3..f105d11830 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 +package v1_23 //nolint import ( "testing" diff --git a/models/migrations/v1_6/v70.go b/models/migrations/v1_6/v70.go index eb669f57b6..ec6bd09bb5 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 +package v1_6 //nolint import ( "fmt" diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go index 42fe8cd1ba..3706ad4406 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 +package v1_6 //nolint import ( "fmt" diff --git a/models/migrations/v1_6/v72.go b/models/migrations/v1_6/v72.go index 7cd2331376..4df2a0f6e9 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 +package v1_6 //nolint import ( "fmt" diff --git a/models/migrations/v1_7/v73.go b/models/migrations/v1_7/v73.go index e0b7a28537..b5a748aae3 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 +package v1_7 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_7/v74.go b/models/migrations/v1_7/v74.go index 376be37a24..f0567e3c9b 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 +package v1_7 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_7/v75.go b/models/migrations/v1_7/v75.go index ef11575466..fa7430970c 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 +package v1_7 //nolint import ( "xorm.io/builder" diff --git a/models/migrations/v1_8/v76.go b/models/migrations/v1_8/v76.go index 8d47280b41..61ad006a47 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 +package v1_8 //nolint import ( "fmt" diff --git a/models/migrations/v1_8/v77.go b/models/migrations/v1_8/v77.go index 4fe5ebe635..8b19993924 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 +package v1_8 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_8/v78.go b/models/migrations/v1_8/v78.go index 840fc20d96..8102b19335 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 +package v1_8 //nolint import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_8/v79.go b/models/migrations/v1_8/v79.go index c8e0db531f..f7d2d68f96 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 +package v1_8 //nolint import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_8/v80.go b/models/migrations/v1_8/v80.go index 6f9df47a93..cebbbead28 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 +package v1_8 //nolint import "xorm.io/xorm" diff --git a/models/migrations/v1_8/v81.go b/models/migrations/v1_8/v81.go index 8152a47ad7..734fc24641 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 +package v1_8 //nolint import ( "fmt" diff --git a/models/migrations/v1_9/v82.go b/models/migrations/v1_9/v82.go index 235c73c504..78a90bdde9 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 +package v1_9 //nolint import ( "fmt" diff --git a/models/migrations/v1_9/v83.go b/models/migrations/v1_9/v83.go index 9640564a44..fa24a92d28 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 +package v1_9 //nolint import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_9/v84.go b/models/migrations/v1_9/v84.go index 423915ae57..c7155fe9cf 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 +package v1_9 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go index 9d5adc82dd..d8e9d91840 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 +package v1_9 //nolint import ( "fmt" diff --git a/models/migrations/v1_9/v86.go b/models/migrations/v1_9/v86.go index 9464ff0cf6..cf2725d158 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 +package v1_9 //nolint import ( "xorm.io/xorm" diff --git a/models/migrations/v1_9/v87.go b/models/migrations/v1_9/v87.go index 81a4ebf80d..fa01b6e5e3 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 +package v1_9 //nolint import ( "xorm.io/xorm" diff --git a/models/moderation/abuse_report.go b/models/moderation/abuse_report.go index 152b81bb51..dadd61a95e 100644 --- a/models/moderation/abuse_report.go +++ b/models/moderation/abuse_report.go @@ -8,7 +8,6 @@ import ( "database/sql" "errors" "slices" - "time" "forgejo.org/models/db" "forgejo.org/modules/log" @@ -48,22 +47,14 @@ const ( AbuseCategoryTypeIllegalContent // 4 ) -// llu:TrKeys -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, AbuseCategoriesTranslationKeys[AbuseCategoryTypeSpam]}, - {AbuseCategoryTypeMalware, AbuseCategoriesTranslationKeys[AbuseCategoryTypeMalware]}, - {AbuseCategoryTypeIllegalContent, AbuseCategoriesTranslationKeys[AbuseCategoryTypeIllegalContent]}, - {AbuseCategoryTypeOther, AbuseCategoriesTranslationKeys[AbuseCategoryTypeOther]}, + {AbuseCategoryTypeSpam, "moderation.abuse_category.spam"}, + {AbuseCategoryTypeMalware, "moderation.abuse_category.malware"}, + {AbuseCategoryTypeIllegalContent, "moderation.abuse_category.illegal_content"}, + {AbuseCategoryTypeOther, "moderation.abuse_category.other_violations"}, } } @@ -109,11 +100,10 @@ type AbuseReport struct { // The abuse category selected by the reporter. Category AbuseCategoryType `xorm:"INDEX NOT NULL"` // Remarks provided by the reporter. - Remarks string `xorm:"VARCHAR(500)"` + Remarks string // 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") @@ -164,25 +154,6 @@ 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 deleted file mode 100644 index 265d143709..0000000000 --- a/models/moderation/abuse_report_detailed.go +++ /dev/null @@ -1,135 +0,0 @@ -// 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 8abb32e8ec..cdd8f69c52 100644 --- a/models/moderation/shadow_copy.go +++ b/models/moderation/shadow_copy.go @@ -17,7 +17,7 @@ import ( type AbuseReportShadowCopy struct { ID int64 `xorm:"pk autoincr"` - RawValue string `xorm:"LONGTEXT NOT NULL"` // A JSON with relevant fields from user, repository, issue or comment table. + RawValue string `xorm:"NOT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` } @@ -26,22 +26,6 @@ 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 c4df5d4fe1..ff95261051 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -186,11 +186,6 @@ 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 371993cdee..e387936473 100644 --- a/models/organization/org_list.go +++ b/models/organization/org_list.go @@ -71,8 +71,11 @@ 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) ([]*Organization, error) { +func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) { schema, err := db.TableInfo(new(user_model.User)) if err != nil { return nil, err @@ -97,7 +100,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*Organizatio } columnsStr := selectColumns.String() - var orgs []*Organization + var orgs []*MinimalOrg if err := db.GetEngine(ctx).Select(columnsStr). Table("user"). Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))). @@ -135,7 +138,6 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*Organizatio 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 6e8c0bac26..170e2bf131 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) - 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) + 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) + } } 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.Organization) int { + isSorted := slices.IsSortedFunc(orgs, func(a, b *organization.MinimalOrg) 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 209471e013..c78eff39fb 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -1,6 +1,5 @@ -// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2018 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. +// Copyright 2016 The Gogs Authors. All rights reserved. // SPDX-License-Identifier: MIT package organization @@ -8,7 +7,6 @@ package organization import ( "context" "fmt" - "net/url" "strings" "forgejo.org/models/db" @@ -22,6 +20,13 @@ import ( "xorm.io/builder" ) +// ___________ +// \__ ___/___ _____ _____ +// | |_/ __ \\__ \ / \ +// | |\ ___/ / __ \| Y Y \ +// |____| \___ >____ /__|_| / +// \/ \/ \/ + // ErrTeamAlreadyExist represents a "TeamAlreadyExist" kind of error. type ErrTeamAlreadyExist struct { OrgID int64 @@ -188,27 +193,6 @@ 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 { @@ -309,22 +293,10 @@ 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: GhostTeamID, - Name: GhostTeamName, - LowerName: GhostTeamLowerName, + ID: -1, + Name: "Ghost team", + LowerName: "ghost team", } } - -// 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 768ccdf5be..60c500e7ec 100644 --- a/models/organization/team_test.go +++ b/models/organization/team_test.go @@ -1,5 +1,4 @@ // Copyright 2017 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package organization_test @@ -16,33 +15,14 @@ import ( "github.com/stretchr/testify/require" ) -func TestTeam(t *testing.T) { +func TestTeam_IsOwnerTeam(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - 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: 1}) + assert.True(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()) + team = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}) + assert.False(t, team.IsOwnerTeam()) } func TestTeam_IsMember(t *testing.T) { diff --git a/models/packages/package.go b/models/packages/package.go index c06dcf5eb3..bdd1c74cad 100644 --- a/models/packages/package.go +++ b/models/packages/package.go @@ -125,7 +125,7 @@ func (pt Type) Name() string { case TypeRpm: return "RPM" case TypeAlt: - return "ALT" + return "Alt" case TypeRubyGems: return "RubyGems" case TypeSwift: diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index f7daf38e5c..ce9963b83a 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -7,7 +7,6 @@ import ( "context" "fmt" - actions_model "forgejo.org/models/actions" "forgejo.org/models/db" "forgejo.org/models/organization" perm_model "forgejo.org/models/perm" @@ -137,33 +136,6 @@ 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 deleted file mode 100644 index 55bc975421..0000000000 --- a/models/perm/access/repo_permission_test.go +++ /dev/null @@ -1,78 +0,0 @@ -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/project/project.go b/models/project/project.go index 18c647c8ac..b9813fda91 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -182,8 +182,6 @@ func init() { } // GetCardConfig retrieves the types of configurations project column cards could have -// -//llu:returnsTrKey func GetCardConfig() []CardConfig { return []CardConfig{ {CardTypeTextOnly, "repo.projects.card_type.text_only"}, diff --git a/models/project/template.go b/models/project/template.go index 278cf5b781..06d5d2af14 100644 --- a/models/project/template.go +++ b/models/project/template.go @@ -26,8 +26,6 @@ const ( ) // GetTemplateConfigs retrieves the template configs of configurations project columns could have -// -//llu:returnsTrKey func GetTemplateConfigs() []TemplateConfig { return []TemplateConfig{ {TemplateTypeNone, "repo.projects.type.none"}, diff --git a/models/quota/default.go b/models/quota/default.go index 37b23739ad..9f655d7847 100644 --- a/models/quota/default.go +++ b/models/quota/default.go @@ -7,7 +7,7 @@ import ( "forgejo.org/modules/setting" ) -func EvaluateDefault(used Used, forSubject LimitSubject) bool { +func EvaluateDefault(used Used, forSubject LimitSubject) (bool, int64) { groups := GroupList{ &Group{ Name: "builtin-default-group", diff --git a/models/quota/group.go b/models/quota/group.go index a4ec8d0e14..7ddc20b2d6 100644 --- a/models/quota/group.go +++ b/models/quota/group.go @@ -5,6 +5,7 @@ package quota import ( "context" + "math" "forgejo.org/models/db" user_model "forgejo.org/models/user" @@ -178,40 +179,78 @@ func (g *Group) RemoveRuleByName(ctx context.Context, ruleName string) error { return committer.Commit() } -// Group.Evaluate returns whether the group contains a matching rule for the subject -// and if so, whether the group allows the action given the size used -func (g *Group) Evaluate(used Used, forSubject LimitSubject) (match, allow bool) { - for _, rule := range g.Rules { - ruleMatch, ruleAllow := rule.Evaluate(used, forSubject) - if ruleMatch { - // evaluation stops as soon as we find a matching rule that denies the action - if !ruleAllow { - return true, false - } +var affectsMap = map[LimitSubject]LimitSubjects{ + LimitSubjectSizeAll: { + LimitSubjectSizeReposAll, + LimitSubjectSizeGitLFS, + LimitSubjectSizeAssetsAll, + }, + LimitSubjectSizeReposAll: { + LimitSubjectSizeReposPublic, + LimitSubjectSizeReposPrivate, + }, + LimitSubjectSizeAssetsAll: { + LimitSubjectSizeAssetsAttachmentsAll, + LimitSubjectSizeAssetsArtifacts, + LimitSubjectSizeAssetsPackagesAll, + }, + LimitSubjectSizeAssetsAttachmentsAll: { + LimitSubjectSizeAssetsAttachmentsIssues, + LimitSubjectSizeAssetsAttachmentsReleases, + }, +} - match = true - allow = true +// Evaluate returns whether the size used is acceptable for the topic if a rule +// was found, and returns the smallest limit of all applicable rules or the +// first limit found to be unacceptable for the size used. +func (g *Group) Evaluate(used Used, forSubject LimitSubject) (bool, bool, int64) { + var found bool + foundLimit := int64(math.MaxInt64) + for _, rule := range g.Rules { + ok, has := rule.Evaluate(used, forSubject) + if has { + if !ok { + return false, true, rule.Limit + } + found = true + foundLimit = min(foundLimit, rule.Limit) } } - return match, allow + if !found { + // If Evaluation for forSubject did not succeed, try evaluating against + // subjects below + + for _, subject := range affectsMap[forSubject] { + ok, has, limit := g.Evaluate(used, subject) + if has { + if !ok { + return false, true, limit + } + found = true + foundLimit = min(foundLimit, limit) + } + } + } + + return true, found, foundLimit } -// GroupList.Evaluate returns whether the grouplist allows the action given the size used -func (gl *GroupList) Evaluate(used Used, forSubject LimitSubject) (pass bool) { +// Evaluate returns if the used size is acceptable for the subject and the +// lowest limit that is acceptable for the subject. +func (gl *GroupList) Evaluate(used Used, forSubject LimitSubject) (bool, int64) { // If there are no groups, use the configured defaults: if gl == nil || len(*gl) == 0 { return EvaluateDefault(used, forSubject) } for _, group := range *gl { - groupMatch, groupAllow := group.Evaluate(used, forSubject) - if groupMatch && groupAllow { - // evaluation stops as soon as we find a matching group that allows the action - return true + ok, has, limit := group.Evaluate(used, forSubject) + if has && ok { + return true, limit } } - return false + return false, 0 } func GetGroupByName(ctx context.Context, name string) (*Group, error) { diff --git a/models/quota/quota.go b/models/quota/quota.go index 9869e9acab..9f1c3ca949 100644 --- a/models/quota/quota.go +++ b/models/quota/quota.go @@ -32,6 +32,6 @@ func EvaluateForUser(ctx context.Context, userID int64, subject LimitSubject) (b return false, err } - allow := groups.Evaluate(*used, subject) - return allow, nil + acceptable, _ := groups.Evaluate(*used, subject) + return acceptable, nil } diff --git a/models/quota/quota_group_test.go b/models/quota/quota_group_test.go index 7085682bfe..7f693b391b 100644 --- a/models/quota/quota_group_test.go +++ b/models/quota/quota_group_test.go @@ -4,16 +4,15 @@ package quota_test import ( + "math" "testing" quota_model "forgejo.org/models/quota" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" "github.com/stretchr/testify/assert" ) -func TestQuotaGroupAllRulesMustAllow(t *testing.T) { +func TestQuotaGroupAllRulesMustPass(t *testing.T) { unlimitedRule := quota_model.Rule{ Limit: -1, Subjects: quota_model.LimitSubjects{ @@ -36,11 +35,12 @@ func TestQuotaGroupAllRulesMustAllow(t *testing.T) { used := quota_model.Used{} used.Size.Repos.Public = 1024 - // Within a group, *all* matching rules must allow. Thus, if we have a deny-all rule, - // and an unlimited rule, the deny rule wins. - match, allow := group.Evaluate(used, quota_model.LimitSubjectSizeAll) - assert.True(t, match) - assert.False(t, allow) + // Within a group, *all* rules must pass. Thus, if we have a deny-all rule, + // and an unlimited rule, that will always fail. + ok, has, limit := group.Evaluate(used, quota_model.LimitSubjectSizeAll) + assert.True(t, has) + assert.False(t, ok) + assert.EqualValues(t, 0, limit) } func TestQuotaGroupRuleScenario1(t *testing.T) { @@ -68,21 +68,25 @@ func TestQuotaGroupRuleScenario1(t *testing.T) { used.Size.Assets.Packages.All = 256 used.Size.Git.LFS = 16 - match, allow := group.Evaluate(used, quota_model.LimitSubjectSizeAssetsAttachmentsReleases) - assert.True(t, match, "size:assets:attachments:releases is covered") - assert.True(t, allow, "size:assets:attachments:releases is allowed") + ok, has, limit := group.Evaluate(used, quota_model.LimitSubjectSizeAssetsAttachmentsReleases) + assert.True(t, has, "size:assets:attachments:releases is covered") + assert.True(t, ok, "size:assets:attachments:releases passes") + assert.EqualValues(t, 1024, limit) - match, allow = group.Evaluate(used, quota_model.LimitSubjectSizeAssetsPackagesAll) - assert.True(t, match, "size:assets:packages:all is covered") - assert.True(t, allow, "size:assets:packages:all is allowed") + ok, has, limit = group.Evaluate(used, quota_model.LimitSubjectSizeAssetsPackagesAll) + assert.True(t, has, "size:assets:packages:all is covered") + assert.True(t, ok, "size:assets:packages:all passes") + assert.EqualValues(t, 1024, limit) - match, allow = group.Evaluate(used, quota_model.LimitSubjectSizeGitLFS) - assert.True(t, match, "size:git:lfs is covered") - assert.False(t, allow, "size:git:lfs is denied") + ok, has, limit = group.Evaluate(used, quota_model.LimitSubjectSizeGitLFS) + assert.True(t, has, "size:git:lfs is covered") + assert.False(t, ok, "size:git:lfs fails") + assert.EqualValues(t, 0, limit) - match, allow = group.Evaluate(used, quota_model.LimitSubjectSizeAll) - assert.False(t, match, "size:all is not covered") - assert.False(t, allow, "size:all is not allowed (not covered)") + ok, has, limit = group.Evaluate(used, quota_model.LimitSubjectSizeAll) + assert.True(t, has, "size:all is covered") + assert.False(t, ok, "size:all fails") + assert.EqualValues(t, 0, limit) } func TestQuotaGroupRuleCombination(t *testing.T) { @@ -110,23 +114,31 @@ func TestQuotaGroupRuleCombination(t *testing.T) { }, } - // Git LFS does not match any rule - match, allow := group.Evaluate(used, quota_model.LimitSubjectSizeGitLFS) - assert.False(t, match) - assert.False(t, allow) + // Git LFS isn't covered by any rule + _, has, limit := group.Evaluate(used, quota_model.LimitSubjectSizeGitLFS) + assert.False(t, has) + assert.EqualValues(t, math.MaxInt, limit) - // repos:all has a matching rule and is allowed - match, allow = group.Evaluate(used, quota_model.LimitSubjectSizeReposAll) - assert.True(t, match) - assert.True(t, allow) + // repos:all is covered, and is passing + ok, has, limit := group.Evaluate(used, quota_model.LimitSubjectSizeReposAll) + assert.True(t, has) + assert.True(t, ok) + assert.EqualValues(t, 4096, limit) - // packages:all has a matching rule and is denied - match, allow = group.Evaluate(used, quota_model.LimitSubjectSizeAssetsPackagesAll) - assert.True(t, match) - assert.False(t, allow) + // packages:all is covered, and is failing + ok, has, limit = group.Evaluate(used, quota_model.LimitSubjectSizeAssetsPackagesAll) + assert.True(t, has) + assert.False(t, ok) + assert.EqualValues(t, 0, limit) + + // size:all is covered, and is failing (due to packages:all being over quota) + ok, has, limit = group.Evaluate(used, quota_model.LimitSubjectSizeAll) + assert.True(t, has, "size:all should be covered") + assert.False(t, ok, "size:all should fail") + assert.EqualValues(t, 0, limit) } -func TestQuotaGroupListsRequireOnlyOneAllow(t *testing.T) { +func TestQuotaGroupListsRequireOnlyOnePassing(t *testing.T) { unlimitedRule := quota_model.Rule{ Limit: -1, Subjects: quota_model.LimitSubjects{ @@ -156,12 +168,13 @@ func TestQuotaGroupListsRequireOnlyOneAllow(t *testing.T) { used := quota_model.Used{} used.Size.Repos.Public = 1024 - // In a group list, an action is allowed if any group matches and allows it. - allow := groups.Evaluate(used, quota_model.LimitSubjectSizeAll) - assert.True(t, allow) + // In a group list, if any group passes, the entire evaluation passes. + ok, limit := groups.Evaluate(used, quota_model.LimitSubjectSizeAll) + assert.True(t, ok) + assert.EqualValues(t, -1, limit) } -func TestQuotaGroupListAllDeny(t *testing.T) { +func TestQuotaGroupListAllFailing(t *testing.T) { denyRule := quota_model.Rule{ Limit: 0, Subjects: quota_model.LimitSubjects{ @@ -191,38 +204,18 @@ func TestQuotaGroupListAllDeny(t *testing.T) { used := quota_model.Used{} used.Size.Repos.Public = 2048 - allow := groups.Evaluate(used, quota_model.LimitSubjectSizeAll) - assert.False(t, allow) + ok, limit := groups.Evaluate(used, quota_model.LimitSubjectSizeAll) + assert.False(t, ok) + assert.EqualValues(t, 0, limit) } -// An empty group list should result in the use of the built in Default -// group: size:all defaulting to unlimited -func TestQuotaDefaultGroup(t *testing.T) { +func TestQuotaGroupListEmpty(t *testing.T) { groups := quota_model.GroupList{} used := quota_model.Used{} used.Size.Repos.Public = 2048 - testSets := []struct { - name string - limit int64 - expectAllow bool - }{ - {"unlimited", -1, true}, - {"limit-allow", 1024 * 1024, true}, - {"limit-deny", 1024, false}, - } - - for _, testSet := range testSets { - t.Run(testSet.name, func(t *testing.T) { - defer test.MockVariableValue(&setting.Quota.Default.Total, testSet.limit)() - - for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ { - t.Run(subject.String(), func(t *testing.T) { - allow := groups.Evaluate(used, subject) - assert.Equal(t, testSet.expectAllow, allow) - }) - } - }) - } + ok, limit := groups.Evaluate(used, quota_model.LimitSubjectSizeAll) + assert.True(t, ok) + assert.EqualValues(t, -1, limit) } diff --git a/models/quota/quota_rule_test.go b/models/quota/quota_rule_test.go index c4605fd58e..59c05563f0 100644 --- a/models/quota/quota_rule_test.go +++ b/models/quota/quota_rule_test.go @@ -83,43 +83,28 @@ func assertEvaluation(t *testing.T, rule quota_model.Rule, used quota_model.Used t.Helper() t.Run(subject.String(), func(t *testing.T) { - match, allow := rule.Evaluate(used, subject) - assert.True(t, match) - assert.Equal(t, expected, allow) + ok, has := rule.Evaluate(used, subject) + assert.True(t, has) + assert.Equal(t, expected, ok) }) } -func TestQuotaRuleNoMatch(t *testing.T) { - testSets := []struct { - name string - limit int64 - }{ - {"unlimited", -1}, - {"limit-0", 0}, - {"limit-1k", 1024}, - {"limit-1M", 1024 * 1024}, +func TestQuotaRuleNoEvaluation(t *testing.T) { + rule := quota_model.Rule{ + Limit: 1024, + Subjects: quota_model.LimitSubjects{ + quota_model.LimitSubjectSizeAssetsAttachmentsAll, + }, } + used := quota_model.Used{} + used.Size.Repos.Public = 4096 - for _, testSet := range testSets { - t.Run(testSet.name, func(t *testing.T) { - rule := quota_model.Rule{ - Limit: testSet.limit, - Subjects: quota_model.LimitSubjects{ - quota_model.LimitSubjectSizeAssetsAttachmentsAll, - }, - } - used := quota_model.Used{} - used.Size.Repos.Public = 4096 + _, has := rule.Evaluate(used, quota_model.LimitSubjectSizeReposAll) - match, allow := rule.Evaluate(used, quota_model.LimitSubjectSizeReposAll) - - // We have a rule for "size:assets:attachments:all", and query for - // "size:repos:all". We don't cover that subject, so the rule does not match - // regardless of the limit. - assert.False(t, match) - assert.False(t, allow) - }) - } + // We have a rule for "size:assets:attachments:all", and query for + // "size:repos:all". We don't cover that subject, so the evaluation returns + // with no rules found. + assert.False(t, has) } func TestQuotaRuleDirectEvaluation(t *testing.T) { @@ -144,12 +129,13 @@ func TestQuotaRuleDirectEvaluation(t *testing.T) { } t.Run("limit:0", func(t *testing.T) { - // With limit:0, any usage will fail evaluation, including 0 + // With limit:0, nothing used is fine. t.Run("used:0", func(t *testing.T) { for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ { - runTest(t, subject, 0, 0, false) + runTest(t, subject, 0, 0, true) } }) + // With limit:0, any usage will fail evaluation t.Run("used:512", func(t *testing.T) { for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ { runTest(t, subject, 0, 512, false) @@ -184,6 +170,14 @@ func TestQuotaRuleDirectEvaluation(t *testing.T) { } func TestQuotaRuleCombined(t *testing.T) { + rule := quota_model.Rule{ + Limit: 1024, + Subjects: quota_model.LimitSubjects{ + quota_model.LimitSubjectSizeGitLFS, + quota_model.LimitSubjectSizeAssetsAttachmentsReleases, + quota_model.LimitSubjectSizeAssetsPackagesAll, + }, + } used := quota_model.Used{ Size: quota_model.UsedSize{ Repos: quota_model.UsedSizeRepos{ @@ -204,112 +198,107 @@ func TestQuotaRuleCombined(t *testing.T) { }, } - expectMatch := map[quota_model.LimitSubject]bool{ - quota_model.LimitSubjectSizeGitLFS: true, - quota_model.LimitSubjectSizeAssetsAttachmentsReleases: true, - quota_model.LimitSubjectSizeAssetsPackagesAll: true, + expectationMap := map[quota_model.LimitSubject]bool{ + quota_model.LimitSubjectSizeGitLFS: false, + quota_model.LimitSubjectSizeAssetsAttachmentsReleases: false, + quota_model.LimitSubjectSizeAssetsPackagesAll: false, } - testSets := []struct { - name string - limit int64 - expectAllow bool - }{ - {"unlimited", -1, true}, - {"limit-allow", 1024 * 1024, true}, - {"limit-deny", 1024, false}, - } + for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ { + t.Run(subject.String(), func(t *testing.T) { + evalOk, evalHas := rule.Evaluate(used, subject) + expected, expectedHas := expectationMap[subject] - for _, testSet := range testSets { - t.Run(testSet.name, func(t *testing.T) { - rule := quota_model.Rule{ - Limit: testSet.limit, - Subjects: quota_model.LimitSubjects{ - quota_model.LimitSubjectSizeGitLFS, - quota_model.LimitSubjectSizeAssetsAttachmentsReleases, - quota_model.LimitSubjectSizeAssetsPackagesAll, - }, - } - - for subject := quota_model.LimitSubjectFirst; subject <= quota_model.LimitSubjectLast; subject++ { - t.Run(subject.String(), func(t *testing.T) { - match, allow := rule.Evaluate(used, subject) - - assert.Equal(t, expectMatch[subject], match) - if expectMatch[subject] { - assert.Equal(t, testSet.expectAllow, allow) - } else { - assert.False(t, allow) - } - }) + assert.Equal(t, expectedHas, evalHas) + if expectedHas { + assert.Equal(t, expected, evalOk) } }) } } func TestQuotaRuleSizeAll(t *testing.T) { - type Test struct { - name string - limit int64 - expectAllow bool - } + runTests := func(t *testing.T, rule quota_model.Rule, expected bool) { + t.Helper() - usedSets := []struct { - name string - used quota_model.Used - testSets []Test - }{ - { - "empty", - quota_model.Used{}, - []Test{ - {"unlimited", -1, true}, - {"limit-1M", 1024 * 1024, true}, - {"limit-5k", 5 * 1024, true}, - {"limit-0", 0, false}, - }, - }, - { - "partial", - makePartiallyUsed(), - []Test{ - {"unlimited", -1, true}, - {"limit-1M", 1024 * 1024, true}, - {"limit-5k", 5 * 1024, true}, - {"limit-0", 0, false}, - }, - }, - { - "full", - makeFullyUsed(), - []Test{ - {"unlimited", -1, true}, - {"limit-1M", 1024 * 1024, true}, - {"limit-5k", 5 * 1024, false}, - {"limit-0", 0, false}, - }, - }, - } + subject := quota_model.LimitSubjectSizeAll - for _, usedSet := range usedSets { - t.Run(usedSet.name, func(t *testing.T) { - testSets := usedSet.testSets - used := usedSet.used + t.Run("used:0", func(t *testing.T) { + used := quota_model.Used{} - for _, testSet := range testSets { - t.Run(testSet.name, func(t *testing.T) { - rule := quota_model.Rule{ - Limit: testSet.limit, - Subjects: quota_model.LimitSubjects{ - quota_model.LimitSubjectSizeAll, - }, - } + assertEvaluation(t, rule, used, subject, true) + }) - match, allow := rule.Evaluate(used, quota_model.LimitSubjectSizeAll) - assert.True(t, match) - assert.Equal(t, testSet.expectAllow, allow) - }) - } + t.Run("used:some-each", func(t *testing.T) { + used := makeFullyUsed() + + assertEvaluation(t, rule, used, subject, expected) + }) + + t.Run("used:some", func(t *testing.T) { + used := makePartiallyUsed() + + assertEvaluation(t, rule, used, subject, expected) }) } + + // With all limits set to 0, evaluation always fails if usage > 0 + t.Run("rule:0", func(t *testing.T) { + rule := quota_model.Rule{ + Limit: 0, + Subjects: quota_model.LimitSubjects{ + quota_model.LimitSubjectSizeAll, + }, + } + + runTests(t, rule, false) + }) + + // With no limits, evaluation always succeeds + t.Run("rule:unlimited", func(t *testing.T) { + rule := quota_model.Rule{ + Limit: -1, + Subjects: quota_model.LimitSubjects{ + quota_model.LimitSubjectSizeAll, + }, + } + + runTests(t, rule, true) + }) + + // With a specific, very generous limit, evaluation succeeds if the limit isn't exhausted + t.Run("rule:generous", func(t *testing.T) { + rule := quota_model.Rule{ + Limit: 102400, + Subjects: quota_model.LimitSubjects{ + quota_model.LimitSubjectSizeAll, + }, + } + + runTests(t, rule, true) + + t.Run("limit exhaustion", func(t *testing.T) { + used := quota_model.Used{ + Size: quota_model.UsedSize{ + Repos: quota_model.UsedSizeRepos{ + Public: 204800, + }, + }, + } + + assertEvaluation(t, rule, used, quota_model.LimitSubjectSizeAll, false) + }) + }) + + // With a specific, small limit, evaluation fails + t.Run("rule:limited", func(t *testing.T) { + rule := quota_model.Rule{ + Limit: 512, + Subjects: quota_model.LimitSubjects{ + quota_model.LimitSubjectSizeAll, + }, + } + + runTests(t, rule, false) + }) } diff --git a/models/quota/rule.go b/models/quota/rule.go index 98959e0a91..89cb57cace 100644 --- a/models/quota/rule.go +++ b/models/quota/rule.go @@ -16,21 +16,6 @@ type Rule struct { Subjects LimitSubjects `json:"subjects,omitempty"` } -var subjectToParent = map[LimitSubject]LimitSubject{ - LimitSubjectSizeGitAll: LimitSubjectSizeAll, - LimitSubjectSizeGitLFS: LimitSubjectSizeGitAll, - LimitSubjectSizeReposAll: LimitSubjectSizeGitAll, - LimitSubjectSizeReposPublic: LimitSubjectSizeReposAll, - LimitSubjectSizeReposPrivate: LimitSubjectSizeReposAll, - LimitSubjectSizeAssetsAll: LimitSubjectSizeAll, - LimitSubjectSizeAssetsAttachmentsAll: LimitSubjectSizeAssetsAll, - LimitSubjectSizeAssetsAttachmentsIssues: LimitSubjectSizeAssetsAttachmentsAll, - LimitSubjectSizeAssetsAttachmentsReleases: LimitSubjectSizeAssetsAttachmentsAll, - LimitSubjectSizeAssetsArtifacts: LimitSubjectSizeAssetsAll, - LimitSubjectSizeAssetsPackagesAll: LimitSubjectSizeAssetsAll, - LimitSubjectSizeWiki: LimitSubjectSizeAssetsAll, -} - func (r *Rule) TableName() string { return "quota_rule" } @@ -51,25 +36,18 @@ func (r Rule) Sum(used Used) int64 { return sum } -func (r Rule) Evaluate(used Used, forSubject LimitSubject) (match, allow bool) { +func (r Rule) Evaluate(used Used, forSubject LimitSubject) (bool, bool) { + // If there's no limit, short circuit out + if r.Limit == -1 { + return true, true + } + + // If the rule does not cover forSubject, bail out early if !slices.Contains(r.Subjects, forSubject) { - // this rule does not match the subject being tested - parent := subjectToParent[forSubject] - if parent != LimitSubjectNone { - return r.Evaluate(used, parent) - } return false, false } - match = true - - if r.Limit == -1 { - // Unlimited, any value is allowed - allow = true - } else { - allow = r.Sum(used) < r.Limit - } - return match, allow + return r.Sum(used) <= r.Limit, true } func (r *Rule) Edit(ctx context.Context, limit *int64, subjects *LimitSubjects) (*Rule, error) { diff --git a/models/quota/used.go b/models/quota/used.go index 0d8f62ab78..22815165f6 100644 --- a/models/quota/used.go +++ b/models/quota/used.go @@ -25,7 +25,7 @@ type UsedSize struct { } func (u UsedSize) All() int64 { - return u.Git.All(u.Repos) + u.Assets.All() + return u.Repos.All() + u.Git.All(u.Repos) + u.Assets.All() } type UsedSizeRepos struct { diff --git a/models/quota/used_test.go b/models/quota/used_test.go index 0fed83342b..82cc5b9bcc 100644 --- a/models/quota/used_test.go +++ b/models/quota/used_test.go @@ -1,54 +1,23 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package quota_test +package quota import ( "testing" - quota_model "forgejo.org/models/quota" "forgejo.org/models/unittest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestQuotaUsedGetUsedForUser(t *testing.T) { +func TestGetUsedForUser(t *testing.T) { defer unittest.OverrideFixtures("models/fixtures/TestGetUsedForUser/")() require.NoError(t, unittest.PrepareTestDatabase()) - used, err := quota_model.GetUsedForUser(t.Context(), 5) + used, err := GetUsedForUser(t.Context(), 5) require.NoError(t, err) assert.EqualValues(t, 4096, used.Size.Assets.Artifacts) } - -func TestQuotaUsedTotals(t *testing.T) { - used := quota_model.Used{ - Size: quota_model.UsedSize{ - Repos: quota_model.UsedSizeRepos{ - Public: 2, - Private: 3, - }, - Git: quota_model.UsedSizeGit{ - LFS: 7, - }, - Assets: quota_model.UsedSizeAssets{ - Attachments: quota_model.UsedSizeAssetsAttachments{ - Issues: 11, - Releases: 13, - }, - Artifacts: 17, - Packages: quota_model.UsedSizeAssetsPackages{ - All: 19, - }, - }, - }, - } - - assert.EqualValues(t, 5, used.Size.Repos.All()) // repos public + repos private - assert.EqualValues(t, 12, used.Size.Git.All(used.Size.Repos)) // repos all + git lfs - assert.EqualValues(t, 24, used.Size.Assets.Attachments.All()) // issues + releases - assert.EqualValues(t, 60, used.Size.Assets.All()) // attachments all + artifacts + packages - assert.EqualValues(t, 72, used.Size.All()) // git all + assets all -} diff --git a/models/repo/git.go b/models/repo/git.go index 11f6452be5..692176c8f6 100644 --- a/models/repo/git.go +++ b/models/repo/git.go @@ -29,8 +29,6 @@ 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 0d2672227b..d7b87dffa0 100644 --- a/models/repo/moderation.go +++ b/models/repo/moderation.go @@ -5,8 +5,6 @@ package repo import ( "context" - "strconv" - "strings" "forgejo.org/models/moderation" "forgejo.org/modules/json" @@ -27,22 +25,6 @@ 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 deleted file mode 100644 index 9852db1b51..0000000000 --- a/models/repo/moderation_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// 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 e57897fb7e..d6d0d1135a 100644 --- a/models/repo/pushmirror.go +++ b/models/repo/pushmirror.go @@ -32,7 +32,6 @@ 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)"` @@ -123,11 +122,6 @@ 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 a7e063ff71..fbef835372 100644 --- a/models/repo/pushmirror_test.go +++ b/models/repo/pushmirror_test.go @@ -75,139 +75,3 @@ 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/redirect.go b/models/repo/redirect.go index e5239a3684..9c44a255d0 100644 --- a/models/repo/redirect.go +++ b/models/repo/redirect.go @@ -14,9 +14,8 @@ import ( // ErrRedirectNotExist represents a "RedirectNotExist" kind of error. type ErrRedirectNotExist struct { - OwnerID int64 - RepoName string - MissingPermission bool + OwnerID int64 + RepoName string } // IsErrRedirectNotExist check if an error is an ErrRepoRedirectNotExist. @@ -50,8 +49,8 @@ func init() { db.RegisterModel(new(Redirect)) } -// GetRedirect returns the redirect for a given pair of ownerID and repository name. -func GetRedirect(ctx context.Context, ownerID int64, repoName string) (int64, error) { +// LookupRedirect look up if a repository has a redirect name +func LookupRedirect(ctx context.Context, ownerID int64, repoName string) (int64, error) { repoName = strings.ToLower(repoName) redirect := &Redirect{OwnerID: ownerID, LowerName: repoName} if has, err := db.GetEngine(ctx).Get(redirect); err != nil { diff --git a/models/repo/redirect_test.go b/models/repo/redirect_test.go index 2f2210588f..d84cbbed54 100644 --- a/models/repo/redirect_test.go +++ b/models/repo/redirect_test.go @@ -10,9 +10,21 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestLookupRedirect(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + repoID, err := repo_model.LookupRedirect(db.DefaultContext, 2, "oldrepo1") + require.NoError(t, err) + assert.EqualValues(t, 1, repoID) + + _, err = repo_model.LookupRedirect(db.DefaultContext, unittest.NonexistentID, "doesnotexist") + assert.True(t, repo_model.IsErrRedirectNotExist(err)) +} + func TestNewRedirect(t *testing.T) { // redirect to a completely new name require.NoError(t, unittest.PrepareTestDatabase()) diff --git a/models/repo/release.go b/models/repo/release.go index b39a1de971..10e9bb259f 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:"INDEX VARCHAR(64)"` + Sha1 string `xorm:"VARCHAR(64)"` HideArchiveLinks bool `xorm:"NOT NULL DEFAULT false"` NumCommits int64 NumCommitsBehind int64 `xorm:"-"` @@ -618,17 +618,3 @@ 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 69f9333589..94dbd6d9d5 100644 --- a/models/repo/release_test.go +++ b/models/repo/release_test.go @@ -49,16 +49,3 @@ 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 9d586b8345..0ba50e6614 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 - dbCtx, committer, err := db.TxContext((ctx)) + ctx, committer, err := db.TxContext((ctx)) if err != nil { return err } defer committer.Close() - _, err = db.GetEngine(dbCtx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) + _, err = db.GetEngine(ctx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) if err != nil { return err } for _, followingRepo := range followingRepoList { - _, err = db.GetEngine(dbCtx).Insert(followingRepo) + _, err = db.GetEngine(ctx).Insert(followingRepo) if err != nil { return err } diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index aa6f2fa0ae..c11ad70627 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -41,30 +41,27 @@ func (err ErrUnitTypeNotExist) Unwrap() error { } // RepoUnitAccessMode specifies the users access mode to a repo unit -// Only UnitAccessModeWrite is used by the wiki, to mark it as instance-writable type UnitAccessMode int const ( // UnitAccessModeUnset - no unit mode set UnitAccessModeUnset UnitAccessMode = iota // 0 - // UnitAccessModeNone no access - // UnitAccessModeNone UnitAccessMode = 1 + UnitAccessModeNone // 1 // UnitAccessModeRead read access - // UnitAccessModeRead UnitAccessMode = 2 - + UnitAccessModeRead // 2 // UnitAccessModeWrite write access - UnitAccessModeWrite UnitAccessMode = 3 + UnitAccessModeWrite // 3 ) func (mode UnitAccessMode) ToAccessMode(modeIfUnset perm.AccessMode) perm.AccessMode { switch mode { case UnitAccessModeUnset: return modeIfUnset - // case UnitAccessModeNone: - // return perm.AccessModeNone - // case UnitAccessModeRead: - // return perm.AccessModeRead + case UnitAccessModeNone: + return perm.AccessModeNone + case UnitAccessModeRead: + return perm.AccessModeRead case UnitAccessModeWrite: return perm.AccessModeWrite default: @@ -336,8 +333,5 @@ 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) - if err != nil { - return fmt.Errorf("UpdateRepoUnit: %v", err) - } - return nil + return err } diff --git a/models/repo/repo_unit_test.go b/models/repo/repo_unit_test.go index 3d6d408fcb..a1964519bd 100644 --- a/models/repo/repo_unit_test.go +++ b/models/repo/repo_unit_test.go @@ -34,8 +34,8 @@ func TestActionsConfig(t *testing.T) { } func TestRepoUnitAccessMode(t *testing.T) { - // assert.Equal(t, perm.AccessModeNone, UnitAccessModeNone.ToAccessMode(perm.AccessModeAdmin)) - // assert.Equal(t, perm.AccessModeRead, UnitAccessModeRead.ToAccessMode(perm.AccessModeAdmin)) + assert.Equal(t, perm.AccessModeNone, UnitAccessModeNone.ToAccessMode(perm.AccessModeAdmin)) + assert.Equal(t, perm.AccessModeRead, UnitAccessModeRead.ToAccessMode(perm.AccessModeAdmin)) assert.Equal(t, perm.AccessModeWrite, UnitAccessModeWrite.ToAccessMode(perm.AccessModeAdmin)) assert.Equal(t, perm.AccessModeRead, UnitAccessModeUnset.ToAccessMode(perm.AccessModeRead)) } diff --git a/models/repo/topic.go b/models/repo/topic.go index 9086f17627..4a3bdc7d8c 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 rendering topics for a repo, it's better to sort them by name to get consistent results + orderBy = "topic.name" // when render topics for a repo, it's better to sort them by name, to get consistent result } if opts.PageSize > 0 { sess = db.SetSessionPagination(sess, opts) diff --git a/models/secret/main_test.go b/models/secret/main_test.go deleted file mode 100644 index 85bfec0c4f..0000000000 --- a/models/secret/main_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// 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 6f6867db52..7be7f454a1 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -11,8 +11,9 @@ 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" @@ -38,7 +39,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 []byte `xorm:"BLOB"` // encrypted data + Data string `xorm:"LONGTEXT"` // encrypted data CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` } @@ -66,21 +67,17 @@ 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.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 - }) + return secret, db.Insert(ctx, secret) } func init() { @@ -116,9 +113,21 @@ func (opts FindSecretsOptions) ToConds() builder.Cond { return cond } -func (s *Secret) SetSecret(data string) { - key := keying.DeriveKey(keying.ContextActionSecret) - s.Data = key.Encrypt([]byte(data), keying.ColumnAndID("data", s.ID)) +// 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 GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) { @@ -146,14 +155,13 @@ 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 := key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) + v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data) if err != nil { - log.Error("unable to decrypt secret[id=%d,name=%q]: %v", secret.ID, secret.Name, err) + log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err) return nil, err } - secrets[secret.Name] = string(v) + secrets[secret.Name] = v } return secrets, nil diff --git a/models/secret/secret_test.go b/models/secret/secret_test.go deleted file mode 100644 index 15142d207b..0000000000 --- a/models/secret/secret_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// 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/unit/unit.go b/models/unit/unit.go index 6b4f2765ee..a14f3ff364 100644 --- a/models/unit/unit.go +++ b/models/unit/unit.go @@ -271,6 +271,7 @@ type Unit struct { Name string NameKey string URI string + DescKey string Idx int MaxAccessMode perm.AccessMode // The max access mode of the unit. i.e. Read means this unit can only be read. } @@ -298,6 +299,7 @@ var ( "code", "repo.code", "/", + "repo.code.desc", 0, perm.AccessModeOwner, } @@ -307,6 +309,7 @@ var ( "issues", "repo.issues", "/issues", + "repo.issues.desc", 1, perm.AccessModeOwner, } @@ -316,6 +319,7 @@ var ( "ext_issues", "repo.ext_issues", "/issues", + "repo.ext_issues.desc", 1, perm.AccessModeRead, } @@ -325,6 +329,7 @@ var ( "pulls", "repo.pulls", "/pulls", + "repo.pulls.desc", 2, perm.AccessModeOwner, } @@ -334,6 +339,7 @@ var ( "releases", "repo.releases", "/releases", + "repo.releases.desc", 3, perm.AccessModeOwner, } @@ -343,6 +349,7 @@ var ( "wiki", "repo.wiki", "/wiki", + "repo.wiki.desc", 4, perm.AccessModeOwner, } @@ -352,6 +359,7 @@ var ( "ext_wiki", "repo.ext_wiki", "/wiki", + "repo.ext_wiki.desc", 4, perm.AccessModeRead, } @@ -361,6 +369,7 @@ var ( "projects", "repo.projects", "/projects", + "repo.projects.desc", 5, perm.AccessModeOwner, } @@ -370,6 +379,7 @@ var ( "packages", "repo.packages", "/packages", + "packages.desc", 6, perm.AccessModeRead, } @@ -379,6 +389,7 @@ var ( "actions", "repo.actions", "/actions", + "actions.unit.desc", 7, perm.AccessModeOwner, } diff --git a/models/unittest/fixture_loader.go b/models/unittest/fixture_loader.go index 0b4ab52c61..67ef1b28df 100644 --- a/models/unittest/fixture_loader.go +++ b/models/unittest/fixture_loader.go @@ -12,9 +12,7 @@ import ( "path/filepath" "strings" - "forgejo.org/modules/container" - - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) type insertSQL struct { @@ -34,15 +32,13 @@ type loader struct { fixtureFiles []*fixtureFile } -func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTableNames container.Set[string]) (*loader, error) { +func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string) (*loader, error) { l := &loader{ db: db, dialect: dialect, fixtureFiles: []*fixtureFile{}, } - tablesWithoutFixture := allTableNames - // Load fixtures for _, fixturePath := range fixturePaths { stat, err := os.Stat(fixturePath) @@ -64,7 +60,6 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTabl return nil, err } l.fixtureFiles = append(l.fixtureFiles, fixtureFile) - tablesWithoutFixture.Remove(fixtureFile.name) } } } else { @@ -76,14 +71,6 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTabl } } - // 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 } @@ -191,13 +178,13 @@ func (l *loader) Load() error { }() // Clean the table and re-insert the fixtures. - tableDeleted := make(container.Set[string]) + tableDeleted := map[string]struct{}{} for _, fixture := range l.fixtureFiles { - if !tableDeleted.Contains(fixture.name) { + if _, ok := tableDeleted[fixture.name]; !ok { 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.Add(fixture.name) + tableDeleted[fixture.name] = struct{}{} } for _, insertSQL := range fixture.insertSQLs { diff --git a/models/unittest/fixtures.go b/models/unittest/fixtures.go index 829cc16466..6dc5c8412d 100644 --- a/models/unittest/fixtures.go +++ b/models/unittest/fixtures.go @@ -7,12 +7,10 @@ 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" @@ -46,8 +44,6 @@ 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...) @@ -79,12 +75,7 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { panic("Unsupported RDBMS for test") } - var allTables container.Set[string] - if !opts.SkipCleanRegistedModels { - allTables = allTableNames().Clone() - } - - fixturesLoader, err = newFixtureLoader(e.DB().DB, dialect, fixturePaths, allTables) + fixturesLoader, err = newFixtureLoader(e.DB().DB, dialect, fixturePaths) if err != nil { return err } diff --git a/models/unittest/mock_http.go b/models/unittest/mock_http.go index c2c12e55ee..6064e07e9b 100644 --- a/models/unittest/mock_http.go +++ b/models/unittest/mock_http.go @@ -41,10 +41,6 @@ func NewMockWebServer(t *testing.T, liveServerBaseURL, testDataDir string, liveM log.Info("Mock HTTP Server: got request for path %s", r.URL.Path) // TODO check request method (support POST?) fixturePath := fmt.Sprintf("%s/%s_%s", testDataDir, r.Method, url.PathEscape(path)) - if strings.Contains(path, "test_repo.git") { - // We got a git clone request against our mock server - fixturePath = fmt.Sprintf("%s/%s", testDataDir, strings.TrimLeft(r.URL.Path, "/")) - } if liveMode { liveURL := fmt.Sprintf("%s%s", liveServerBaseURL, path) diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 29ec82c55f..d34c9e9a0a 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -217,10 +217,6 @@ 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 aabf2336fc..816fd8a098 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))) } -// KeyID returns the ID of the user's public key -func (u *User) KeyID() string { +// APActorKeyID returns the ID of the user's public key +func (u *User) APActorKeyID() string { return u.APActorID() + "#main-key" } diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go index 85f5b16c65..1801f57a23 100644 --- a/models/user/email_address_test.go +++ b/models/user/email_address_test.go @@ -181,20 +181,3 @@ func TestDeletePrimaryEmailAddressOfUser(t *testing.T) { assert.True(t, user_model.IsErrEmailAddressNotExist(err)) assert.Nil(t, email) } - -func TestActivateUserEmail(t *testing.T) { - defer unittest.OverrideFixtures("models/fixtures/TestActivateUserEmail")() - require.NoError(t, unittest.PrepareTestDatabase()) - - t.Run("Activate email", func(t *testing.T) { - require.NoError(t, user_model.ActivateUserEmail(t.Context(), 1001, "AnotherTestUserWithUpperCaseEmail@otto.splvs.net", true)) - - unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{UID: 1001}, "is_activated = true") - }) - - t.Run("Deactivate email", func(t *testing.T) { - require.NoError(t, user_model.ActivateUserEmail(t.Context(), 1001, "AnotherTestUserWithUpperCaseEmail@otto.splvs.net", false)) - - unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{UID: 1001}, "is_activated = false") - }) -} diff --git a/models/user/fixtures/login_source.yml b/models/user/fixtures/login_source.yml deleted file mode 100644 index 3950f85964..0000000000 --- a/models/user/fixtures/login_source.yml +++ /dev/null @@ -1,8 +0,0 @@ -- - 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 137064a368..b1892f331b 100644 --- a/models/user/fixtures/user.yml +++ b/models/user/fixtures/user.yml @@ -11,7 +11,6 @@ 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 8663b2a943..e32c226385 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 } - dbCtx, committer, err := db.TxContext(ctx) + ctx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if err = db.Insert(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if err = db.Insert(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(ctx, "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 } - dbCtx, committer, err := db.TxContext(ctx) + ctx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if _, err = db.DeleteByBean(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(ctx, "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 17901f84ec..afda497f02 100644 --- a/models/user/moderation.go +++ b/models/user/moderation.go @@ -37,26 +37,6 @@ 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 { @@ -93,20 +73,16 @@ 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, userID int64, unalteredUser *User, alteredCols ...string) error { +func IfNeededCreateShadowCopyForUser(ctx context.Context, user *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 columns are being changed + // for updates we need to go further only if certain column are being changed for _, colName := range userDataColumnNames() { if shouldCheckIfNeeded = slices.Contains(alteredCols, colName); shouldCheckIfNeeded { break @@ -118,23 +94,18 @@ func IfNeededCreateShadowCopyForUser(ctx context.Context, userID int64, unaltere return nil } - shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, userID) + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, user.ID) if err != nil { return err } if shadowCopyNeeded { - if unalteredUser == nil { - if unalteredUser, err = GetUserByID(ctx, userID); err != nil { - return err - } - } - userData := newUserData(unalteredUser) + userData := newUserData(user) content, err := json.Marshal(userData) if err != nil { return err } - return moderation.CreateShadowCopyForUser(ctx, userID, string(content)) + return moderation.CreateShadowCopyForUser(ctx, user.ID, string(content)) } return nil diff --git a/models/user/moderation_test.go b/models/user/moderation_test.go deleted file mode 100644 index f951e41e11..0000000000 --- a/models/user/moderation_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// 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/openid.go b/models/user/openid.go index b4d4f175b2..96b00255a3 100644 --- a/models/user/openid.go +++ b/models/user/openid.go @@ -40,8 +40,8 @@ func GetUserOpenIDs(ctx context.Context, uid int64) ([]*UserOpenID, error) { return openids, nil } -// IsOpenIDUsed returns true if the openid has been used. -func IsOpenIDUsed(ctx context.Context, uri string) (bool, error) { +// isOpenIDUsed returns true if the openid has been used. +func isOpenIDUsed(ctx context.Context, uri string) (bool, error) { if len(uri) == 0 { return true, nil } @@ -71,7 +71,7 @@ func (err ErrOpenIDAlreadyUsed) Unwrap() error { // AddUserOpenID adds an pre-verified/normalized OpenID URI to given user. // NOTE: make sure openid.URI is normalized already func AddUserOpenID(ctx context.Context, openid *UserOpenID) error { - used, err := IsOpenIDUsed(ctx, openid.URI) + used, err := isOpenIDUsed(ctx, openid.URI) if err != nil { return err } else if used { diff --git a/models/user/redirect.go b/models/user/redirect.go index bcb421d4a1..75876f17d2 100644 --- a/models/user/redirect.go +++ b/models/user/redirect.go @@ -21,8 +21,7 @@ import ( // ErrUserRedirectNotExist represents a "UserRedirectNotExist" kind of error. type ErrUserRedirectNotExist struct { - Name string - MissingPermission bool + Name string } // IsErrUserRedirectNotExist check if an error is an ErrUserRedirectNotExist. @@ -82,6 +81,15 @@ func GetUserRedirect(ctx context.Context, userName string) (*Redirect, error) { return redirect, nil } +// LookupUserRedirect look up userID if a user has a redirect name +func LookupUserRedirect(ctx context.Context, userName string) (int64, error) { + redirect, err := GetUserRedirect(ctx, userName) + if err != nil { + return 0, err + } + return redirect.RedirectUserID, nil +} + // NewUserRedirect create a new user redirect func NewUserRedirect(ctx context.Context, ID int64, oldUserName, newUserName string) error { oldUserName = strings.ToLower(oldUserName) diff --git a/models/user/redirect_test.go b/models/user/redirect_test.go new file mode 100644 index 0000000000..c598fb045f --- /dev/null +++ b/models/user/redirect_test.go @@ -0,0 +1,26 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package user_test + +import ( + "testing" + + "forgejo.org/models/db" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLookupUserRedirect(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + userID, err := user_model.LookupUserRedirect(db.DefaultContext, "olduser1") + require.NoError(t, err) + assert.EqualValues(t, 1, userID) + + _, err = user_model.LookupUserRedirect(db.DefaultContext, "doesnotexist") + assert.True(t, user_model.IsErrUserRedirectNotExist(err)) +} diff --git a/models/user/user.go b/models/user/user.go index 8cdecc06e4..eedd1db80e 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -182,11 +182,11 @@ func (u *User) BeforeUpdate() { u.MaxRepoCreation = -1 } - // Ensure AvatarEmail is set for non-organization users, because organization - // are not required to have a email set. + // Organization does not need email + u.Email = strings.ToLower(u.Email) if !u.IsOrganization() { if len(u.AvatarEmail) == 0 { - u.AvatarEmail = strings.ToLower(u.Email) + u.AvatarEmail = u.Email } } @@ -234,33 +234,6 @@ 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 @@ -323,9 +296,6 @@ 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" } @@ -334,25 +304,16 @@ 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) } @@ -966,9 +927,7 @@ 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. - // 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 { + if err := IfNeededCreateShadowCopyForUser(ctx, u, cols...); err != nil { return err } @@ -1202,8 +1161,8 @@ func GetUserByEmail(ctx context.Context, email string) (*User, error) { email = strings.ToLower(email) // Otherwise, check in alternative list for activated email addresses - emailAddress := &EmailAddress{} - has, err := db.GetEngine(ctx).Where("lower_email = ? AND is_activated = ?", email, true).Get(emailAddress) + emailAddress := &EmailAddress{LowerEmail: email, IsActivated: true} + has, err := db.GetEngine(ctx).Get(emailAddress) if err != nil { return nil, err } diff --git a/models/user/user_repository.go b/models/user/user_repository.go index df864746e8..3f24efb1fb 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 - txCtx, committer, err := db.TxContext(ctx) + ctx, 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(txCtx, user, &overwrite); err != nil { + if err := CreateUser(ctx, user, &overwrite); err != nil { return err } @@ -48,7 +48,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat return err } - _, err = db.GetEngine(txCtx).Insert(federatedUser) + _, err = db.GetEngine(ctx).Insert(federatedUser) if err != nil { return err } @@ -57,6 +57,14 @@ 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) @@ -70,7 +78,7 @@ func FindFederatedUser(ctx context.Context, externalID string, federationHostID if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) + return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -87,7 +95,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 not found (given externalId: %v, federationHostId: %v)", externalID, federationHostID) + return nil, nil, fmt.Errorf("FederatedUser for externalId = %v and federationHostId = %v does not exist", externalID, federationHostID) } return user, federatedUser, nil } @@ -99,13 +107,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("FederatedUser table does not contain entry for user ID: %v", federatedUser.UserID) + return nil, nil, fmt.Errorf("Federated user %v does not exist", 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("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) + return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -130,7 +138,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("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) + return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -211,6 +219,7 @@ 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 e4a94cbc57..fd9d05653f 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -46,28 +46,6 @@ 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{ @@ -172,7 +150,7 @@ func TestAPActorID_APActorID(t *testing.T) { func TestKeyID(t *testing.T) { user := user_model.User{ID: 1} - url := user.KeyID() + url := user.APActorKeyID() expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key" assert.Equal(t, expected, url) } @@ -637,145 +615,6 @@ 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})() @@ -996,25 +835,3 @@ func TestPronounsPrivacy(t *testing.T) { assert.Equal(t, "any", user.GetPronouns(true)) }) } - -func TestGetUserByEmail(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - t.Run("Normal", func(t *testing.T) { - u, err := user_model.GetUserByEmail(t.Context(), "user2@example.com") - require.NoError(t, err) - assert.EqualValues(t, 2, u.ID) - }) - - t.Run("Not activated", func(t *testing.T) { - u, err := user_model.GetUserByEmail(t.Context(), "user11@example.com") - require.ErrorIs(t, err, user_model.ErrUserNotExist{Name: "user11@example.com"}) - assert.Nil(t, u) - }) - - t.Run("Not primary", func(t *testing.T) { - u, err := user_model.GetUserByEmail(t.Context(), "user1-3@example.com") - require.NoError(t, err) - assert.EqualValues(t, 1, u.ID) - }) -} diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index c3960d140a..7ae4557ed6 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -13,11 +13,11 @@ import ( api "forgejo.org/modules/structs" webhook_module "forgejo.org/modules/webhook" - "code.forgejo.org/forgejo/runner/v11/act/jobparser" - "code.forgejo.org/forgejo/runner/v11/act/model" - "code.forgejo.org/forgejo/runner/v11/act/workflowpattern" "github.com/gobwas/glob" - "go.yaml.in/yaml/v3" + "github.com/nektos/act/pkg/jobparser" + "github.com/nektos/act/pkg/model" + "github.com/nektos/act/pkg/workflowpattern" + "gopkg.in/yaml.v3" ) type DetectedWorkflow struct { @@ -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), false) + workflow, err := model.ReadWorkflow(bytes.NewReader(content)) if err != nil { return nil, err } diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go index 11a2fd94c3..d015fb7bec 100644 --- a/modules/activitypub/client.go +++ b/modules/activitypub/client.go @@ -66,11 +66,6 @@ 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 { @@ -82,7 +77,7 @@ func NewClientFactoryWithTimeout(timeout time.Duration) (c *ClientFactory, err e Transport: &http.Transport{ Proxy: proxy.Proxy(), }, - Timeout: timeout, + Timeout: 5 * time.Second, }, algs: setting.HttpsigAlgs, digestAlg: httpsig.DigestAlgorithm(setting.Federation.DigestAlgorithm), @@ -94,7 +89,6 @@ func NewClientFactoryWithTimeout(timeout time.Duration) (c *ClientFactory, err e 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 @@ -109,8 +103,12 @@ type Client struct { } // NewRequest function -func (cf *ClientFactory) WithKeysDirect(ctx context.Context, privateKey, pubID string) (APClient, error) { - privPem, _ := pem.Decode([]byte(privateKey)) +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)) privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes) if err != nil { return nil, err @@ -128,14 +126,6 @@ func (cf *ClientFactory) WithKeysDirect(ctx context.Context, privateKey, pubID s 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) @@ -159,14 +149,12 @@ func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) { 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 - } + 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) @@ -179,15 +167,12 @@ 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 } - - 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 - } + 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 2041f28bb1..48c6728f43 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -56,7 +56,14 @@ 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...) - return &Layer{name: name, fs: os.DirFS(root), localPath: root} + 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} } // Bindata returns a new Layer with the given name, it serves files from the given bindata asset. @@ -73,7 +80,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: layers} + return &LayeredFS{layers: slices.DeleteFunc(layers, func(layer *Layer) bool { return layer == nil })} } // 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 76eeb61d83..87d1f92b00 100644 --- a/modules/assetfs/layered_test.go +++ b/modules/assetfs/layered_test.go @@ -1,5 +1,4 @@ // Copyright 2023 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package assetfs @@ -109,30 +108,3 @@ 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 e3a3ff4a23..fd6a7c2b77 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 d3719dc552..70f837bc66 100644 --- a/modules/container/set.go +++ b/modules/container/set.go @@ -74,8 +74,3 @@ 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 44e4847f6b..af5e9126ab 100644 --- a/modules/container/set_test.go +++ b/modules/container/set_test.go @@ -47,11 +47,4 @@ 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 8ba31d5f6f..bb0c1de2f7 100644 --- a/modules/forgefed/activity_follow_test.go +++ b/modules/forgefed/activity_follow_test.go @@ -9,7 +9,6 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" - "github.com/stretchr/testify/assert" ) func Test_NewForgeFollowValidation(t *testing.T) { @@ -18,13 +17,15 @@ func Test_NewForgeFollowValidation(t *testing.T) { sut.Actor = ap.IRI("example.org/alice") sut.Object = ap.IRI("example.org/bob") - valid, err := validation.IsValid(sut) - assert.True(t, valid, "sut is invalid: %v\n", err) + if err, _ := validation.IsValid(sut); !err { + t.Errorf("sut is invalid: %v\n", err) + } sut = ForgeFollow{} sut.Actor = ap.IRI("example.org/alice") sut.Object = ap.IRI("example.org/bob") - valid, err = validation.IsValid(sut) - assert.False(t, valid, "sut is valid: %v\n", err) + if err, _ := validation.IsValid(sut); err { + t.Errorf("sut is valid: %v\n", err) + } } diff --git a/modules/forgefed/activity_like_test.go b/modules/forgefed/activity_like_test.go index eef5563d8b..6b252d5960 100644 --- a/modules/forgefed/activity_like_test.go +++ b/modules/forgefed/activity_like_test.go @@ -13,8 +13,6 @@ 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) { @@ -24,14 +22,21 @@ 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) - 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()) + 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()) + } got, err := sut.MarshalJSON() - require.NoError(t, err, "MarshalJSON() error = %q", err) - assert.True(t, reflect.DeepEqual(got, want), "MarshalJSON()\n got: %q,\n want: %q", got, want) + if err != nil { + t.Errorf("MarshalJSON() error = \"%v\"", err) + return + } + if !reflect.DeepEqual(got, want) { + t.Errorf("MarshalJSON() got = %q, want %q", got, want) + } } func Test_LikeMarshalJSON(t *testing.T) { @@ -61,8 +66,13 @@ func Test_LikeMarshalJSON(t *testing.T) { for name, tt := range tests { t.Run(name, func(t *testing.T) { got, err := tt.item.MarshalJSON() - 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) + 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) + } }) } } @@ -79,8 +89,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{ - Type: "Like", Actor: ap.IRI("https://repo.prod.meissa.de/api/activitypub/user-id/1"), + Type: "Like", Object: ap.IRI("https://codeberg.org/api/activitypub/repository-id/1"), }, }, @@ -97,10 +107,12 @@ func Test_LikeUnmarshalJSON(t *testing.T) { t.Run(name, func(t *testing.T) { got := new(ForgeLike) err := got.UnmarshalJSON(test.item) - 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 (err != nil || test.wantErr != nil) && !strings.Contains(err.Error(), test.wantErr.Error()) { + t.Errorf("UnmarshalJSON() error = \"%v\", wantErr \"%v\"", err, test.wantErr) + return + } if !reflect.DeepEqual(got, test.want) { - assert.Errorf(t, err, "UnmarshalJSON() got = %q, want %q, err %q", got, test.want, err.Error()) + t.Errorf("UnmarshalJSON() got = %q, want %q, err %q", got, test.want, err.Error()) } }) } @@ -108,47 +120,46 @@ 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"}`)) - valid, _ := validation.IsValid(sut) - assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) + if res, _ := validation.IsValid(sut); !res { + t.Errorf("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"}`)) - 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]) + if err := validateAndCheckError(sut, "type should not be empty"); err != nil { + t.Error(err) + } 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"}`)) - 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]) + if err := validateAndCheckError(sut, "Field type contains the value bad-type, which is not in allowed subset [Like]"); err != nil { + t.Error(err) + } 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"}`)) - validate = sut.Validate() - assert.Len(t, validate, 1) - assert.Equal(t, - "StartTime was invalid.", - validate[0]) + if err := validateAndCheckError(sut, "StartTime was invalid."); err != nil { + t.Error(err) + } } func TestActivityValidation_Attack(t *testing.T) { sut := new(ForgeLike) sut.UnmarshalJSON([]byte(`{rubbish}`)) - assert.Len(t, sut.Validate(), 5) + if len(sut.Validate()) != 5 { + t.Errorf("5 validation errors expected but was: %v\n", len(sut.Validate())) + } } diff --git a/modules/forgefed/activity_undo_like_test.go b/modules/forgefed/activity_undo_like_test.go index 76358b1669..5867a84e7b 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, "Value type should not be empty"); err != nil { + if err := validateAndCheckError(sut, "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 107ae51204..9cb9f133b9 100644 --- a/modules/forgefed/activity_user_activity_test.go +++ b/modules/forgefed/activity_user_activity_test.go @@ -9,7 +9,6 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" - "github.com/stretchr/testify/assert" ) func Test_ForgeUserActivityValidation(t *testing.T) { @@ -35,6 +34,7 @@ func Test_ForgeUserActivityValidation(t *testing.T) { sut.Note = note - valid, _ := validation.IsValid(sut) - assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) + if res, _ := validation.IsValid(sut); !res { + t.Errorf("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 e4f1734a9d..f466ddb964 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: Value path should not be empty\npath: \"\" has to be a person specific api path") + require.EqualError(t, err, "Validation Error: forgefed.PersonID: path should not be empty\npath: \"\" has to be a person specific api path") sut = PersonID{} sut.ID = "1" @@ -166,28 +166,38 @@ func TestWebfingerId(t *testing.T) { } func TestShouldThrowErrorOnInvalidInput(t *testing.T) { - 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}, + var err any + _, err = NewPersonID("", "forgejo") + if err == nil { + t.Errorf("empty input should be invalid.") } - - 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) - } + _, 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) } } @@ -211,11 +221,14 @@ func Test_PersonUnmarshalJSON(t *testing.T) { } sut := new(ForgePerson) err := sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`)) - require.NoError(t, err, "UnmarshalJSON() unexpected error: %q", err) - + if err != nil { + t.Errorf("UnmarshalJSON() unexpected error: %v", err) + } x, _ := expected.MarshalJSON() y, _ := sut.MarshalJSON() - assert.True(t, reflect.DeepEqual(x, y), "UnmarshalJSON()\n got: %q,\n want: %q", x, y) + if !reflect.DeepEqual(x, y) { + t.Errorf("UnmarshalJSON() expected: %q got: %q", x, y) + } expectedStr := strings.ReplaceAll(strings.ReplaceAll(`{ "id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10", @@ -231,7 +244,9 @@ func Test_PersonUnmarshalJSON(t *testing.T) { "\n", ""), "\t", "") err = sut.UnmarshalJSON([]byte(expectedStr)) - require.NoError(t, err, "UnmarshalJSON() unexpected error: %q", err) + if err != nil { + t.Errorf("UnmarshalJSON() unexpected error: %v", err) + } result, _ := sut.MarshalJSON() assert.JSONEq(t, expectedStr, string(result), "Expected string is not equal") } @@ -239,8 +254,9 @@ func Test_PersonUnmarshalJSON(t *testing.T) { func TestForgePersonValidation(t *testing.T) { sut := new(ForgePerson) sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`)) - valid, _ := validation.IsValid(sut) - assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) + if res, _ := validation.IsValid(sut); !res { + t.Errorf("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 a32114616c..48d773c5b9 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, "Value ID should not be empty", result[0]) + assert.Equal(t, "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 02aebd58d3..20c3666bb1 100644 --- a/modules/forgefed/object_user_activity_note_test.go +++ b/modules/forgefed/object_user_activity_note_test.go @@ -9,7 +9,6 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" - "github.com/stretchr/testify/assert" ) func Test_UserActivityNoteValidation(t *testing.T) { @@ -23,6 +22,7 @@ func Test_UserActivityNoteValidation(t *testing.T) { } sut.URL = ap.IRI("example.org/user-id/57") - valid, _ := validation.IsValid(sut) - assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) + if res, _ := validation.IsValid(sut); !res { + t.Errorf("sut expected to be valid: %v\n", sut.Validate()) + } } diff --git a/modules/git/blame.go b/modules/git/blame.go index 868edab2b8..4ff347e31b 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 !bypassBlameIgnore { + if CheckGitVersionAtLeast("2.23") == nil && !bypassBlameIgnore { ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit) } diff --git a/modules/git/blob.go b/modules/git/blob.go index 4eef5f0e2a..8c5c275146 100644 --- a/modules/git/blob.go +++ b/modules/git/blob.go @@ -8,7 +8,6 @@ import ( "bufio" "bytes" "encoding/base64" - "fmt" "io" "forgejo.org/modules/log" @@ -173,43 +172,60 @@ func (b *Blob) GetBlobContent(limit int64) (string, error) { return string(buf), err } -type BlobTooLargeError struct { - Size, Limit int64 -} - -func (b BlobTooLargeError) Error() string { - return fmt.Sprintf("blob: content larger than limit (%d > %d)", b.Size, b.Limit) -} - -// 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, - } +// 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'} - rc, size, err := b.NewTruncatedReader(limit) + 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) + } +} + +// 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 rc.Close() + defer dataRc.Close() - encoding := base64.StdEncoding - buf := bytes.NewBuffer(make([]byte, 0, encoding.EncodedLen(int(size)))) + pr, pw := io.Pipe() + encoder := base64.NewEncoder(base64.StdEncoding, pw) - encoder := base64.NewEncoder(encoding, buf) + go func() { + _, err := io.Copy(encoder, dataRc) + _ = encoder.Close() - if _, err := io.Copy(encoder, rc); err != nil { + if err != nil { + _ = pw.CloseWithError(err) + } else { + _ = pw.Close() + } + }() + + out, err := io.ReadAll(pr) + if err != nil { return "", err } - if err := encoder.Close(); err != nil { - return "", err - } - - return buf.String(), nil + return string(out), nil } // GuessContentType guesses the content type of the blob. @@ -220,7 +236,7 @@ func (b *Blob) GuessContentType() (typesniffer.SniffedType, error) { } defer r.Close() - return typesniffer.DetectContentTypeFromReader(r, b.Name()) + return typesniffer.DetectContentTypeFromReader(r) } // GetBlob finds the blob object in the repository. diff --git a/modules/git/blob_test.go b/modules/git/blob_test.go index a4b8033941..54115013d3 100644 --- a/modules/git/blob_test.go +++ b/modules/git/blob_test.go @@ -63,24 +63,6 @@ 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 4fb13ecd4f..96831e3ae4 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -16,6 +16,8 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/util" + + "github.com/go-git/go-git/v5/config" ) // Commit represents a git commit. @@ -27,8 +29,8 @@ type Commit struct { CommitMessage string Signature *ObjectSignature - Parents []ObjectID // ID strings - submodules map[string]Submodule // submodule indexed by path + Parents []ObjectID // ID strings + submoduleCache *ObjectCache } // Message returns the commit message. Same as retrieving CommitMessage directly. @@ -350,9 +352,71 @@ 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", "--exclude", "refs/tags/*", "--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) + 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()) 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 6511a1689a..8d9142d362 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 - Submodule Submodule + Entry *TreeEntry + Commit *Commit + SubModuleFile *SubModuleFile } // GetCommitsInfo gets information of all commits that are corresponding to these entries @@ -71,18 +71,19 @@ 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() } - submodule, err := commit.GetSubmodule(fullPath, entry) + subModuleURL, err := commit.GetSubModule(fullPath) if err != nil { return nil, nil, err } - commitsInfo[i].Submodule = submodule + subModuleFile := NewSubModuleFile(commitsInfo[i].Commit, subModuleURL, entry.ID.String()) + commitsInfo[i].SubModuleFile = subModuleFile } } diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index ee57a735e6..484827149c 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -436,3 +436,33 @@ 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 deleted file mode 100644 index 0eba8cb541..0000000000 --- a/modules/git/diff_compare.go +++ /dev/null @@ -1,56 +0,0 @@ -// 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 deleted file mode 100644 index 433497b5c4..0000000000 --- a/modules/git/diff_compare_test.go +++ /dev/null @@ -1,421 +0,0 @@ -// 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 851b090b53..1dfd0b5134 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.34.1" +const RequiredVersion = "2.0.0" var ( // GitExecutable is the command name of git @@ -33,6 +33,7 @@ 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 @@ -112,7 +113,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 { + if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { format += ", Wire Protocol %s Enabled" args = append(args, "Version 2") // for focus color } @@ -171,13 +172,16 @@ func InitFull(ctx context.Context) (err error) { _ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg")) } - if setting.Git.EnableAutoGitWireProtocol { + // Since git wire protocol has been released from git v2.18 + if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2") } // Explicitly disable credential helper, otherwise Git credentials might leak - globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") - + if CheckGitVersionAtLeast("2.9") == nil { + globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") + } + SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil SupportHashSha256 = CheckGitVersionAtLeast("2.42") == nil SupportCheckAttrOnBare = CheckGitVersionAtLeast("2.40") == nil if SupportHashSha256 { @@ -191,6 +195,9 @@ 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=") } @@ -227,28 +234,38 @@ func syncGitConfig() (err error) { } } - // Set git some configurations - these must be set to these values for forgejo to work correctly + // Set git some configurations - these must be set to these values for gitea to work correctly if err := configSet("core.quotePath", "false"); err != nil { return err } - if err := configSet("receive.advertisePushOptions", "true"); err != nil { - return err + if CheckGitVersionAtLeast("2.10") == nil { + if err := configSet("receive.advertisePushOptions", "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 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 + } } - // set support for AGit flow - if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); 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 + } } // Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user @@ -267,6 +284,11 @@ 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. @@ -303,7 +325,8 @@ func syncGitConfig() (err error) { } } - if !setting.Git.DisablePartialClone { + // By default partial clones are disabled, enable them from git v2.22 + if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil { 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 38d4db169c..01200dba68 100644 --- a/modules/git/git_test.go +++ b/modules/git/git_test.go @@ -14,6 +14,7 @@ import ( "forgejo.org/modules/test" "forgejo.org/modules/util" + "github.com/hashicorp/go-version" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -104,6 +105,10 @@ 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) @@ -116,6 +121,13 @@ 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/grep.go b/modules/git/grep.go index b5471b8f6c..3703b13660 100644 --- a/modules/git/grep.go +++ b/modules/git/grep.go @@ -36,7 +36,6 @@ const ( RegExpGrepMode ) -// llu:TrKeysSuffix search. var GrepSearchOptions = [3]string{"exact", "union", "regexp"} type GrepOptions struct { diff --git a/modules/git/parse.go b/modules/git/parse.go index c7b84d7198..6bc32057a7 100644 --- a/modules/git/parse.go +++ b/modules/git/parse.go @@ -10,6 +10,8 @@ import ( "io" "strconv" "strings" + + "forgejo.org/modules/log" ) // ParseTreeEntries parses the output of a `git ls-tree -l` command. @@ -53,9 +55,19 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) { entry.sized = true } - entry.entryMode, err = parseMode(string(entryMode)) - if err != nil { - return nil, err + switch string(entryMode) { + case "100644": + entry.entryMode = EntryModeBlob + case "100755": + entry.entryMode = EntryModeExec + case "120000": + entry.entryMode = EntryModeSymlink + case "160000": + entry.entryMode = EntryModeCommit + case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons + entry.entryMode = EntryModeTree + default: + return nil, fmt.Errorf("unknown type: %v", string(entryMode)) } entry.ID, err = NewIDFromString(string(entryObjectID)) @@ -96,10 +108,23 @@ loop: sz -= int64(count) entry := new(TreeEntry) entry.ptree = ptree - entry.entryMode, err = parseMode(string(mode)) - if err != nil { - return nil, err + + switch string(mode) { + case "100644": + entry.entryMode = EntryModeBlob + case "100755": + entry.entryMode = EntryModeExec + case "120000": + entry.entryMode = EntryModeSymlink + case "160000": + entry.entryMode = EntryModeCommit + case "40000", "40755": // git uses 40000 for tree object, but some users may get 40755 for unknown reasons + entry.entryMode = EntryModeTree + default: + log.Debug("Unknown mode: %v", string(mode)) + return nil, fmt.Errorf("unknown mode: %v", string(mode)) } + entry.ID = objectFormat.MustID(sha) entry.name = string(fname) entries = append(entries, entry) @@ -110,31 +135,3 @@ loop: return entries, nil } - -// Parse the file mode, we cannot hardcode the modes that we expect for -// a variety of reasons (that is not known to us) the permissions bits -// of files can vary, usually the result because of tooling that uses Git in -// a funny way. So we have to parse the mode as a integer and do bit tricks. -func parseMode(modeStr string) (EntryMode, error) { - mode, err := strconv.ParseUint(modeStr, 8, 64) - if err != nil { - return 0, fmt.Errorf("cannot parse mode: %v", err) - } - - switch mode & 0o170000 { - case 0o040000: - return EntryModeTree, nil - case 0o120000: - return EntryModeSymlink, nil - case 0o160000: - return EntryModeCommit, nil - case 0o100000: - // Check for the permission bit on the owner. - if mode&0o100 == 0o100 { - return EntryModeExec, nil - } - return EntryModeBlob, nil - } - - return 0, fmt.Errorf("unknown mode: %o", mode) -} diff --git a/modules/git/parse_test.go b/modules/git/parse_test.go index 502adab4da..03f359f6c1 100644 --- a/modules/git/parse_test.go +++ b/modules/git/parse_test.go @@ -101,38 +101,3 @@ func TestParseTreeEntriesInvalid(t *testing.T) { require.Error(t, err) assert.Empty(t, entries) } - -func TestParseMode(t *testing.T) { - ok := func(t *testing.T, mode string, entry EntryMode) { - t.Helper() - actualEntry, err := parseMode(mode) - require.NoError(t, err) - assert.Equal(t, entry, actualEntry) - } - - fail := func(t *testing.T, mode string) { - t.Helper() - entry, err := parseMode(mode) - require.Error(t, err) - assert.Zero(t, entry) - } - - ok(t, "100644", EntryModeBlob) - ok(t, "100755", EntryModeExec) - ok(t, "100754", EntryModeExec) - ok(t, "100700", EntryModeExec) - ok(t, "100744", EntryModeExec) - ok(t, "120000", EntryModeSymlink) - ok(t, "120644", EntryModeSymlink) - ok(t, "160000", EntryModeCommit) - ok(t, "160644", EntryModeCommit) - ok(t, "040000", EntryModeTree) - ok(t, "040755", EntryModeTree) - ok(t, "040775", EntryModeTree) - ok(t, "040754", EntryModeTree) - - fail(t, "not-a-number") - fail(t, "000000") - fail(t, "400000") - fail(t, "111111") -} diff --git a/modules/git/pipeline/revlist.go b/modules/git/pipeline/revlist.go index 1ee8921854..f39b7113bb 100644 --- a/modules/git/pipeline/revlist.go +++ b/modules/git/pipeline/revlist.go @@ -16,6 +16,26 @@ 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 e96ba0a339..9709a8be79 100644 --- a/modules/git/pushoptions/pushoptions.go +++ b/modules/git/pushoptions/pushoptions.go @@ -4,7 +4,6 @@ package pushoptions import ( - "encoding/base64" "fmt" "os" "strconv" @@ -110,22 +109,5 @@ func (o gitPushOptions) GetBool(key Key, def bool) bool { func (o gitPushOptions) GetString(key Key) (string, bool) { val, ok := o[string(key)] - 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 + return val, ok } diff --git a/modules/git/pushoptions/pushoptions_test.go b/modules/git/pushoptions/pushoptions_test.go index d7c50649d0..1cb36d9d1e 100644 --- a/modules/git/pushoptions/pushoptions_test.go +++ b/modules/git/pushoptions/pushoptions_test.go @@ -4,7 +4,6 @@ package pushoptions import ( - "encoding/base64" "fmt" "testing" @@ -93,23 +92,6 @@ 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 83a02fe2be..fb66d76ff0 100644 --- a/modules/git/remote.go +++ b/modules/git/remote.go @@ -12,7 +12,14 @@ 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) { - result, _, err := NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName).RunStdString(&RunOpts{Dir: repoPath}) + 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}) if err != nil { return "", err } diff --git a/modules/git/repo.go b/modules/git/repo.go index 4f2b05bca5..23e9337615 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -18,7 +18,6 @@ import ( "strings" "time" - "forgejo.org/modules/log" "forgejo.org/modules/proxy" "forgejo.org/modules/setting" "forgejo.org/modules/util" @@ -161,89 +160,24 @@ func CloneWithArgs(ctx context.Context, args TrustedCmdArgs, from, to string, op if len(opts.Branch) > 0 { cmd.AddArguments("-b").AddDynamicArguments(opts.Branch) } + cmd.AddDashesAndList(from, to) - envs := os.Environ() - parsedFromURL, err := url.Parse(from) - if err == nil { - envs = proxy.EnvWithProxy(parsedFromURL) - } - - fromURL := from - sanitizedFrom := from - - // If the clone URL has credentials, sanitize it and store the credentials in - // a temporary file that git will access. if strings.Contains(from, "://") && strings.Contains(from, "@") { - sanitizedFrom = util.SanitizeCredentialURLs(from) - if parsedFromURL != nil { - if pwd, has := parsedFromURL.User.Password(); has { - parsedFromURL.User = url.User(parsedFromURL.User.Username()) - fromURL = parsedFromURL.String() - - credentialsFile, err := os.CreateTemp(os.TempDir(), "forgejo-clone-credentials") - if err != nil { - return err - } - credentialsPath := credentialsFile.Name() - - defer func() { - _ = credentialsFile.Close() - if err := util.Remove(credentialsPath); err != nil { - log.Warn("Unable to remove temporary file %q: %v", credentialsPath, err) - } - }() - - // Make it read-write. - if err := credentialsFile.Chmod(0o600); err != nil { - return err - } - - // Write the password. - if _, err := fmt.Fprint(credentialsFile, pwd); err != nil { - return err - } - - askpassFile, err := os.CreateTemp(os.TempDir(), "forgejo-askpass") - if err != nil { - return err - } - askpassPath := askpassFile.Name() - - defer func() { - _ = askpassFile.Close() - if err := util.Remove(askpassPath); err != nil { - log.Warn("Unable to remove temporary file %q: %v", askpassPath, err) - } - }() - - // Make it executable. - if err := askpassFile.Chmod(0o700); err != nil { - return err - } - - // Write the password script. - if _, err := fmt.Fprintf(askpassFile, "exec cat %s", credentialsPath); err != nil { - return err - } - - // Close it, so that Git can use it and no busy errors arise. - _ = askpassFile.Close() - _ = credentialsFile.Close() - - // Use environments to specify that git should ask for credentials, this - // takes precedences over anything else https://git-scm.com/docs/gitcredentials#_requesting_credentials. - envs = append(envs, "GIT_ASKPASS="+askpassPath) - } - } + cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.SanitizeCredentialURLs(from), to, opts.Shared, opts.Mirror, opts.Depth)) + } else { + cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, from, to, opts.Shared, opts.Mirror, opts.Depth)) } - cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, sanitizedFrom, to, opts.Shared, opts.Mirror, opts.Depth)) - cmd.AddDashesAndList(fromURL, to) - if opts.Timeout <= 0 { opts.Timeout = -1 } + envs := os.Environ() + u, err := url.Parse(from) + if err == nil { + envs = proxy.EnvWithProxy(u) + } + stderr := new(bytes.Buffer) if err = cmd.Run(&RunOpts{ Timeout: opts.Timeout, diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go index 3d2c845fa0..c69382e245 100644 --- a/modules/git/repo_attribute_test.go +++ b/modules/git/repo_attribute_test.go @@ -5,6 +5,7 @@ package git import ( "context" + "fmt" "io" "io/fs" "os" @@ -196,7 +197,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, os.CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo")))) + require.NoError(t, CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo")))) gitRepo, err := openRepositoryWithDefaultContext(path) require.NoError(t, err) @@ -323,3 +324,32 @@ 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 d812354af5..139cdd7be9 100644 --- a/modules/git/repo_blame.go +++ b/modules/git/repo_blame.go @@ -4,46 +4,20 @@ 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, file string, line uint64) (*Commit, error) { - res, _, gitErr := NewCommand(repo.Ctx, "blame"). +func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) { + res, _, err := NewCommand(repo.Ctx, "blame"). AddOptionFormat("-L %d,%d", line, line). AddOptionValues("-p", revision). - 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() + AddDashesAndList(file).RunStdString(&RunOpts{Dir: path}) if err != nil { return nil, err } - - objectIDLen := objectFormat.FullLength() - if len(res) < objectIDLen { - return nil, fmt.Errorf("output of blame is invalid, cannot contain commit ID: %s", res) + if len(res) < 40 { + return nil, fmt.Errorf("invalid result of blame: %s", res) } - - return repo.GetCommit(res[:objectIDLen]) + return repo.GetCommit(res[:40]) } diff --git a/modules/git/repo_blame_test.go b/modules/git/repo_blame_test.go deleted file mode 100644 index 126b95386d..0000000000 --- a/modules/git/repo_blame_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// 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 1e38bf2946..3a9aa3e4e6 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -19,9 +19,15 @@ 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 NewCommand(ctx, "show-ref", "--verify", "--quiet").AddDashesAndList(BranchPrefix+name).Run(&RunOpts{Dir: repoPath}) == nil + return IsReferenceExist(ctx, repoPath, BranchPrefix+name) } // Branch represents a Git branch. diff --git a/modules/git/repo_branch_test.go b/modules/git/repo_branch_test.go index e61ea6f5d7..1e0fea7cd4 100644 --- a/modules/git/repo_branch_test.go +++ b/modules/git/repo_branch_test.go @@ -1,5 +1,4 @@ // Copyright 2018 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package git @@ -196,17 +195,3 @@ 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 a650e597d2..4c8516f828 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -443,43 +443,53 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit, } func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) { - command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix) + if CheckGitVersionAtLeast("2.7.0") == nil { + 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) + 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 } - stdout, _, err := command.RunStdString(&RunOpts{Dir: repo.Path}) + stdout, _, err := NewCommand(repo.Ctx, "branch").AddOptionValues("--contains", commit.ID.String()).RunStdString(&RunOpts{Dir: repo.Path}) if err != nil { return nil, err } - branches := strings.Fields(stdout) + 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] + } return branches, nil } -// GetCommitsFromIDs get commits from commit IDs. If ignoreExistence is -// specified, then commits that no longer exists are still returned but -// without any information except the ID. -func (repo *Repository) GetCommitsFromIDs(commitIDs []string, ignoreExistence bool) []*Commit { +// GetCommitsFromIDs get commits from commit IDs +func (repo *Repository) GetCommitsFromIDs(commitIDs []string) []*Commit { commits := make([]*Commit, 0, len(commitIDs)) for _, commitID := range commitIDs { commit, err := repo.GetCommit(commitID) if err == nil && commit != nil { commits = append(commits, commit) - } else if ignoreExistence && IsErrNotExist(err) { - // It's entirely possible the commit no longer exists, we only care - // about the status and verification. Verification is no longer possible, - // but getting the status is still possible with just the ID. We do have - // to assumme the commitID is not shortened, we cannot recover the full - // commitID. - id, err := NewIDFromString(commitID) - if err == nil { - commits = append(commits, &Commit{ - ID: id, - }) - } } } diff --git a/modules/git/repo_commit_test.go b/modules/git/repo_commit_test.go index 5932f31e3c..53760b208e 100644 --- a/modules/git/repo_commit_test.go +++ b/modules/git/repo_commit_test.go @@ -199,40 +199,3 @@ func TestCommitsByFileAndRange(t *testing.T) { assert.Len(t, commits, testCase.ExpectedCommitCount, "file: '%s', page: %d", testCase.File, testCase.Page) } } - -func TestGetCommitsFromIDs(t *testing.T) { - bareRepo1, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare")) - require.NoError(t, err) - - commitIDs := []string{"2839944139e0de9737a044f78b0e4b40d989a9e3", "2839944139e0de9737a044f78b0e4b40d989a9e4"} - - t.Run("Normal", func(t *testing.T) { - commits := bareRepo1.GetCommitsFromIDs(commitIDs, false) - if assert.Len(t, commits, 1) { - assert.Equal(t, "2839944139e0de9737a044f78b0e4b40d989a9e3", commits[0].ID.String()) - assert.Equal(t, "Example User", commits[0].Author.Name) - } - }) - - t.Run("Ignore existence", func(t *testing.T) { - commits := bareRepo1.GetCommitsFromIDs(commitIDs, true) - if assert.Len(t, commits, 2) { - assert.Equal(t, "2839944139e0de9737a044f78b0e4b40d989a9e3", commits[0].ID.String()) - assert.Equal(t, "Example User", commits[0].Author.Name) - - assert.Equal(t, "2839944139e0de9737a044f78b0e4b40d989a9e4", commits[1].ID.String()) - assert.Nil(t, commits[1].Author) - } - }) - - t.Run("Not full commit ID", func(t *testing.T) { - commits := bareRepo1.GetCommitsFromIDs(append(commitIDs, "abba"), true) - if assert.Len(t, commits, 2) { - assert.Equal(t, "2839944139e0de9737a044f78b0e4b40d989a9e3", commits[0].ID.String()) - assert.Equal(t, "Example User", commits[0].Author.Name) - - assert.Equal(t, "2839944139e0de9737a044f78b0e4b40d989a9e4", commits[1].ID.String()) - assert.Nil(t, commits[1].Author) - } - }) -} diff --git a/modules/git/repo_commitgraph.go b/modules/git/repo_commitgraph.go index c3647bd894..492438be37 100644 --- a/modules/git/repo_commitgraph.go +++ b/modules/git/repo_commitgraph.go @@ -11,8 +11,10 @@ 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 _, _, 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 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) + } } return nil } diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go index 94f1911c4a..373b5befb5 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -183,17 +183,6 @@ 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 b1ebdf6177..86bd6855a7 100644 --- a/modules/git/repo_compare_test.go +++ b/modules/git/repo_compare_test.go @@ -1,5 +1,4 @@ // Copyright 2018 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package git @@ -163,83 +162,3 @@ 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/repo_test.go b/modules/git/repo_test.go index b07fc3732d..c3971b2a5f 100644 --- a/modules/git/repo_test.go +++ b/modules/git/repo_test.go @@ -4,15 +4,7 @@ package git import ( - "bytes" - "encoding/base64" - "io/fs" - "net/http" - "net/http/httptest" - "net/url" - "os" "path/filepath" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -62,80 +54,3 @@ func TestRepoGetDivergingCommits(t *testing.T) { Behind: 2, }, do) } - -func TestCloneCredentials(t *testing.T) { - calledWithoutPassword := false - askpassFile := "" - credentialsFile := "" - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if req.URL.Path != "/info/refs" { - return - } - - // Get basic authorization. - auth, ok := strings.CutPrefix(req.Header.Get("Authorization"), "Basic ") - if !ok { - w.Header().Set("WWW-Authenticate", `Basic realm="Forgejo"`) - http.Error(w, "require credentials", http.StatusUnauthorized) - return - } - - rawAuth, err := base64.StdEncoding.DecodeString(auth) - require.NoError(t, err) - - user, password, ok := bytes.Cut(rawAuth, []byte{':'}) - assert.True(t, ok) - - // First time around Git tries without password (that's specified in the clone URL). - // It demonstrates it doesn't immediately uses askpass. - if len(password) == 0 { - assert.EqualValues(t, "oauth2", user) - calledWithoutPassword = true - - w.Header().Set("WWW-Authenticate", `Basic realm="Forgejo"`) - http.Error(w, "require credentials", http.StatusUnauthorized) - return - } - - assert.EqualValues(t, "oauth2", user) - assert.EqualValues(t, "some_token", password) - - tmpDir := os.TempDir() - - // Verify that the askpass implementation was used. - files, err := fs.Glob(os.DirFS(tmpDir), "forgejo-askpass*") - require.NoError(t, err) - for _, fileName := range files { - fileContent, err := os.ReadFile(filepath.Join(tmpDir, fileName)) - require.NoError(t, err) - - credentialsPath, ok := bytes.CutPrefix(fileContent, []byte(`exec cat `)) - assert.True(t, ok) - - fileContent, err = os.ReadFile(string(credentialsPath)) - require.NoError(t, err) - assert.EqualValues(t, "some_token", fileContent) - - askpassFile = filepath.Join(tmpDir, fileName) - credentialsFile = string(credentialsPath) - } - })) - - serverURL, err := url.Parse(server.URL) - require.NoError(t, err) - - serverURL.User = url.UserPassword("oauth2", "some_token") - - require.NoError(t, Clone(t.Context(), serverURL.String(), t.TempDir(), CloneRepoOptions{})) - - assert.True(t, calledWithoutPassword) - assert.NotEmpty(t, askpassFile) - assert.NotEmpty(t, credentialsFile) - - // Check that the helper files are gone. - _, err = os.Stat(askpassFile) - require.ErrorIs(t, err, fs.ErrNotExist) - _, err = os.Stat(credentialsFile) - require.ErrorIs(t, err, fs.ErrNotExist) -} diff --git a/modules/git/submodule.go b/modules/git/submodule.go index 4ea97d66eb..b99c81582b 100644 --- a/modules/git/submodule.go +++ b/modules/git/submodule.go @@ -6,124 +6,38 @@ 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 ) -// 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._-]+):(.*)$`) +// 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, + } +} + func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { if refURL == "" { return "" @@ -139,7 +53,7 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { urlPrefix = strings.TrimSuffix(urlPrefix, "/") - // FIXME: Need to consider branch - which will require changes in parseSubmoduleSection + // FIXME: Need to consider branch - which will require changes in modules/git/commit.go:GetSubModules // Relative url prefix check (according to git submodule documentation) if strings.HasPrefix(refURI, "./") || strings.HasPrefix(refURI, "../") { return urlPrefix + path.Clean(path.Join("/", repoFullName, refURI)) @@ -193,3 +107,13 @@ 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 2d27f47456..a396e4ea0d 100644 --- a/modules/git/submodule_test.go +++ b/modules/git/submodule_test.go @@ -4,11 +4,9 @@ package git import ( - "strings" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestGetRefURL(t *testing.T) { @@ -42,74 +40,3 @@ 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 deleted file mode 100644 index a77fa514de..0000000000 --- a/modules/git/tests/repos/templates_repo/COMMIT_EDITMSG +++ /dev/null @@ -1 +0,0 @@ -Initial diff --git a/modules/git/tests/repos/templates_repo/HEAD b/modules/git/tests/repos/templates_repo/HEAD deleted file mode 100644 index cb089cd89a..0000000000 --- a/modules/git/tests/repos/templates_repo/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/master diff --git a/modules/git/tests/repos/templates_repo/config b/modules/git/tests/repos/templates_repo/config deleted file mode 100644 index a4ef456cbc..0000000000 --- a/modules/git/tests/repos/templates_repo/config +++ /dev/null @@ -1,5 +0,0 @@ -[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 deleted file mode 100644 index 498b267a8c..0000000000 --- a/modules/git/tests/repos/templates_repo/description +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 2b3f95a155..0000000000 Binary files a/modules/git/tests/repos/templates_repo/index and /dev/null differ diff --git a/modules/git/tests/repos/templates_repo/info/exclude b/modules/git/tests/repos/templates_repo/info/exclude deleted file mode 100644 index a5196d1be8..0000000000 --- a/modules/git/tests/repos/templates_repo/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# 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 deleted file mode 100644 index 8ee4ab3ff8..0000000000 --- a/modules/git/tests/repos/templates_repo/info/refs +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 8904092586..0000000000 Binary files a/modules/git/tests/repos/templates_repo/objects/info/commit-graph and /dev/null differ diff --git a/modules/git/tests/repos/templates_repo/objects/info/packs b/modules/git/tests/repos/templates_repo/objects/info/packs deleted file mode 100644 index 5833126b59..0000000000 --- a/modules/git/tests/repos/templates_repo/objects/info/packs +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 6bd87bf13c..0000000000 Binary files a/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.bitmap and /dev/null 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 deleted file mode 100644 index b530533e1b..0000000000 Binary files a/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.idx and /dev/null 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 deleted file mode 100644 index 6bbcbb6546..0000000000 Binary files a/modules/git/tests/repos/templates_repo/objects/pack/pack-abb44544ae19d590e95822e963f78d069d27ba9e.pack and /dev/null differ diff --git a/modules/git/tests/repos/templates_repo/packed-refs b/modules/git/tests/repos/templates_repo/packed-refs deleted file mode 100644 index eb6e7a8add..0000000000 --- a/modules/git/tests/repos/templates_repo/packed-refs +++ /dev/null @@ -1,2 +0,0 @@ -# pack-refs with: peeled fully-peeled sorted -45697427ce0595075c5c8efa42567f050208510d refs/heads/master diff --git a/modules/git/tests/repos/templates_repo/refs/tags/.gitkeep b/modules/git/tests/repos/templates_repo/refs/tags/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/modules/git/tree_blob.go b/modules/git/tree_blob.go index 5c1aa7753d..df339f64b1 100644 --- a/modules/git/tree_blob.go +++ b/modules/git/tree_blob.go @@ -17,6 +17,7 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) { ptree: t, ID: t.ID, name: "", + fullName: "", entryMode: EntryModeTree, }, nil } @@ -54,7 +55,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 8b6c4c467c..d51b7992fe 100644 --- a/modules/git/tree_entry.go +++ b/modules/git/tree_entry.go @@ -21,12 +21,16 @@ type TreeEntry struct { entryMode EntryMode name string - size int64 - sized bool + size int64 + sized bool + fullName string } // Name returns the name of the entry func (te *TreeEntry) Name() string { + if te.fullName != "" { + return te.fullName + } return te.name } @@ -64,8 +68,8 @@ func (te *TreeEntry) Size() int64 { return te.size } -// IsSubmodule if the entry is a submodule -func (te *TreeEntry) IsSubmodule() bool { +// IsSubModule if the entry is a sub module +func (te *TreeEntry) IsSubModule() bool { return te.entryMode == EntryModeCommit } @@ -112,37 +116,32 @@ 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 - lnk, err := te.LinkTarget() + r, err := te.Blob().DataAsync() 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 @@ -210,7 +209,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()) @@ -237,7 +236,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 0b2984a953..7c855ac64e 100644 --- a/modules/graceful/server_http.go +++ b/modules/graceful/server_http.go @@ -16,11 +16,6 @@ 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 d385ac21c9..c5f0658d4e 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, opts.Filename) + sniffedType := typesniffer.DetectContentType(mineBuf) // 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 4c8b5f2a86..c53b7a2e6d 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, update.Filename).IsText() { + } else if !typesniffer.DetectContentType(fileContents).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 9b11f56fb7..3903d77fe0 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, update.Filename).IsText() { + } else if !typesniffer.DetectContentType(fileContents).IsText() { // FIXME: UTF-16 files will probably fail here return nil, nil } diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go index 66c9497dab..adf51a76d7 100644 --- a/modules/indexer/code/search.go +++ b/modules/indexer/code/search.go @@ -35,7 +35,6 @@ type SearchResultLanguages = internal.SearchResultLanguages type SearchOptions = internal.SearchOptions -// llu:TrKeysSuffix search. var CodeSearchOptions = [2]string{"exact", "union"} type SearchMode = internal.CodeSearchMode @@ -98,7 +97,7 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges conv := hcd.ConvertToPlaceholders(string(hl)) convLines := strings.Split(conv, "\n") - // each highlightRange is of the form [line number, start byte offset, end byte offset] + // each highlightRange is of the form [line number, start pos, end pos] for _, highlightRange := range highlightRanges { ln, start, end := highlightRange[0], highlightRange[1], highlightRange[2] line := convLines[ln] @@ -106,18 +105,15 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges continue } - sr := strings.NewReader(line) sb := strings.Builder{} count := -1 isOpen := false - for r, size, err := sr.ReadRune(); err == nil; r, size, err = sr.ReadRune() { + for _, r := range line { if token, ok := hcd.PlaceholderTokenMap[r]; // token was not found - !ok { - count += size - } else if - // token was marked as used - token == "" || + !ok || + // token was marked as used + token == "" || // the token is not an valid html tag emitted by chroma !(len(token) > 6 && (token[0:5] == "= end: + switch count { + case end: // if tag is not open, no need to close if !isOpen { break } sb.WriteRune(endTag) isOpen = false - case count >= start: + case start: // if tag is open, do not open again if isOpen { break @@ -165,7 +161,7 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges highlightedLines := strings.Split(hcd.Recover(conv), "\n") // The lineNums outputted by highlight.Code might not match the original lineNums, because "highlight" removes the last `\n` lines := make([]ResultLine, min(len(highlightedLines), len(lineNums))) - for i := range len(lines) { + for i := 0; i < len(lines); i++ { lines[i].Num = lineNums[i] lines[i].FormattedContent = template.HTML(highlightedLines[i]) } diff --git a/modules/indexer/code/search_test.go b/modules/indexer/code/search_test.go deleted file mode 100644 index 2413ddec0b..0000000000 --- a/modules/indexer/code/search_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package code - -import ( - "html/template" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestHighlightSearchResultCode(t *testing.T) { - opts := []struct { - Title string - File string - Lines []int - Range [][3]int - Code string - Result []template.HTML - }{ - { - Title: "One Match Text", - File: "test.txt", - Range: [][3]int{{1, 5, 9}}, - Code: "First Line\nMark this only\nThe End", - Result: []template.HTML{ - "First Line", - "Mark this only", - "The End", - }, - }, - { - Title: "Two Match Text", - File: "test.txt", - Range: [][3]int{ - {1, 5, 9}, - {2, 5, 9}, - }, - Code: "First Line\nMark this only\nMark this too\nThe End", - Result: []template.HTML{ - "First Line", - "Mark this only", - "Mark this too", - "The End", - }, - }, - { - Title: "Unicode Before", - File: "test.txt", - Range: [][3]int{{1, 10, 14}}, - Code: "First Line\nMark 👉 this only\nThe End", - Result: []template.HTML{ - "First Line", - "Mark 👉 this only", - "The End", - }, - }, - { - Title: "Unicode Between", - File: "test.txt", - Range: [][3]int{{1, 5, 14}}, - Code: "First Line\nMark this 😊 only\nThe End", - Result: []template.HTML{ - "First Line", - "Mark this 😊 only", - "The End", - }, - }, - { - Title: "Unicode Before And Between", - File: "test.txt", - Range: [][3]int{{1, 10, 19}}, - Code: "First Line\nMark 👉 this 😊 only\nThe End", - Result: []template.HTML{ - "First Line", - "Mark 👉 this 😊 only", - "The End", - }, - }, - { - Title: "Golang", - File: "test.go", - Range: [][3]int{{1, 14, 23}}, - Code: "func main() {\n\tfmt.Println(\"mark this\")\n}", - Result: []template.HTML{ - "func main() {", - "\tfmt.Println("mark this")", - "}", - }, - }, - { - Title: "Golang Unicode", - File: "test.go", - Range: [][3]int{{1, 14, 28}}, - Code: "func main() {\n\tfmt.Println(\"mark this 😊\")\n}", - Result: []template.HTML{ - "func main() {", - "\tfmt.Println("mark this 😊")", - "}", - }, - }, - } - for _, o := range opts { - t.Run(o.Title, func(t *testing.T) { - lines := []int{} - for i := range strings.Count(strings.TrimSuffix(o.Code, "\n"), "\n") + 1 { - lines = append(lines, i+1) - } - res := HighlightSearchResultCode(o.File, lines, o.Range, o.Code) - assert.Len(t, res, len(o.Result)) - assert.Len(t, res, len(lines)) - - for i, r := range res { - require.Equal(t, lines[i], r.Num) - require.Equal(t, o.Result[i], r.FormattedContent) - } - }) - } -} diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index cb98f722c5..573d63a446 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -156,12 +156,11 @@ func (b *Indexer) Delete(_ context.Context, ids ...int64) error { func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (*internal.SearchResult, error) { var queries []query.Query - tokens, err := options.Tokens() - if err != nil { - return nil, err - } - - if len(tokens) > 0 { + if options.Keyword != "" { + tokens, err := options.Tokens() + if err != nil { + return nil, err + } q := bleve.NewBooleanQuery() for _, token := range tokens { innerQ := bleve.NewDisjunctionQuery( @@ -171,7 +170,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(20.0) + idQuery.SetBoost(5.0) innerQ.AddQuery(idQuery) } @@ -198,15 +197,6 @@ 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 5f42bce9a1..397daa3265 100644 --- a/modules/indexer/issues/db/db.go +++ b/modules/indexer/issues/db/db.go @@ -53,7 +53,6 @@ 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 { @@ -83,7 +82,6 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( builder.Eq{"`index`": issueID}, cond, ) - priorityIssueIndex = issueID } } @@ -91,7 +89,6 @@ 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 55a471fc8e..4411cc1c37 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -78,11 +78,6 @@ 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 311e92730e..9d2786e101 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -149,13 +149,12 @@ func (b *Indexer) Delete(ctx context.Context, ids ...int64) error { func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (*internal.SearchResult, error) { query := elastic.NewBoolQuery() - tokens, err := options.Tokens() - if err != nil { - return nil, err - } - - if len(tokens) > 0 { + if options.Keyword != "" { q := elastic.NewBoolQuery() + tokens, err := options.Tokens() + if err != nil { + return nil, err + } for _, token := range tokens { innerQ := elastic.NewMultiMatchQuery(token.Term, "content", "comments").FieldWithBoost("title", 2.0).TieBreaker(0.5) if token.Fuzzy { @@ -166,7 +165,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(20) + indexQ := elastic.NewTermQuery("index", issueID).Boost(15.0) eitherQ = elastic.NewDisMaxQuery().Query(indexQ).Query(innerQ).TieBreaker(0.5) } switch token.Kind { @@ -189,10 +188,6 @@ 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/indexer_test.go b/modules/indexer/issues/indexer_test.go index 21daea3d45..d3b3494672 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -84,7 +84,7 @@ func searchIssueWithKeyword(t *testing.T) { issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) - assert.Equal(t, test.expectedIDs, issueIDs, test.opts.Keyword) + assert.Equal(t, test.expectedIDs, issueIDs) } } diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index cdd113212d..6c55405179 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -75,9 +75,8 @@ 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 - PriorityRepoID optional.Option[int64] // issues from this repository will be prioritized when SortByScore + RepoIDs []int64 // repository IDs which the issues belong to + AllPublic bool // if include all public repositories 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/qstring.go b/modules/indexer/issues/internal/qstring.go index 348f7a564b..6b60b4c5f6 100644 --- a/modules/indexer/issues/internal/qstring.go +++ b/modules/indexer/issues/internal/qstring.go @@ -45,9 +45,12 @@ func (t *Tokenizer) next() (tk Token, err error) { // skip all leading white space for { - if r, _, err = t.in.ReadRune(); err != nil || r != ' ' { - break + if r, _, err = t.in.ReadRune(); err == nil && r == ' ' { + //nolint:staticcheck,wastedassign // SA4006 the variable is used after the loop + r, _, err = t.in.ReadRune() + continue } + break } if err != nil { return tk, err @@ -104,17 +107,11 @@ nextEnd: // Tokenize the keyword func (o *SearchOptions) Tokens() (tokens []Token, err error) { - if o.Keyword == "" { - return nil, nil - } - in := strings.NewReader(o.Keyword) it := Tokenizer{in: in} for token, err := it.next(); err == nil; token, err = it.next() { - if token.Term != "" { - tokens = append(tokens, token) - } + tokens = append(tokens, token) } if err != nil && err != io.EOF { return nil, err diff --git a/modules/indexer/issues/internal/qstring_test.go b/modules/indexer/issues/internal/qstring_test.go index eb4bdb306f..835491707c 100644 --- a/modules/indexer/issues/internal/qstring_test.go +++ b/modules/indexer/issues/internal/qstring_test.go @@ -41,36 +41,6 @@ var testOpts = []testIssueQueryStringOpt{ }, }, }, - { - Keyword: "Hello World", - Results: []Token{ - { - Term: "Hello", - Fuzzy: true, - Kind: BoolOptShould, - }, - { - Term: "World", - Fuzzy: true, - Kind: BoolOptShould, - }, - }, - }, - { - Keyword: " Hello World ", - Results: []Token{ - { - Term: "Hello", - Fuzzy: true, - Kind: BoolOptShould, - }, - { - Term: "World", - Fuzzy: true, - Kind: BoolOptShould, - }, - }, - }, { Keyword: "+Hello +World", Results: []Token{ @@ -186,68 +156,6 @@ var testOpts = []testIssueQueryStringOpt{ }, }, }, - { - Keyword: "\\", - Results: nil, - }, - { - Keyword: "\"", - Results: nil, - }, - { - Keyword: "Hello \\", - Results: []Token{ - { - Term: "Hello", - Fuzzy: true, - Kind: BoolOptShould, - }, - }, - }, - { - Keyword: "\"\"", - Results: nil, - }, - { - Keyword: "\" World \"", - Results: []Token{ - { - Term: " World ", - Fuzzy: false, - Kind: BoolOptShould, - }, - }, - }, - { - Keyword: "\"\" World \"\"", - Results: []Token{ - { - Term: "World", - Fuzzy: true, - Kind: BoolOptShould, - }, - }, - }, - { - Keyword: "Best \"Hello World\" Ever", - Results: []Token{ - { - Term: "Best", - Fuzzy: true, - Kind: BoolOptShould, - }, - { - Term: "Hello World", - Fuzzy: false, - Kind: BoolOptShould, - }, - { - Term: "Ever", - Fuzzy: true, - Kind: BoolOptShould, - }, - }, - }, } func TestIssueQueryString(t *testing.T) { diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 46014994a0..ef75955a14 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -87,44 +87,14 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) { } } -func allResults(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Len(t, result.Hits, len(data)) - assert.Equal(t, len(data), int(result.Total)) -} - var cases = []*testIndexerCase{ { Name: "default", SearchOptions: &internal.SearchOptions{}, - Expected: allResults, - }, - { - Name: "empty keyword", - SearchOptions: &internal.SearchOptions{ - Keyword: "", + Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { + assert.Len(t, result.Hits, len(data)) + assert.Equal(t, len(data), int(result.Total)) }, - Expected: allResults, - }, - { - Name: "whitespace keyword", - SearchOptions: &internal.SearchOptions{ - Keyword: " ", - }, - Expected: allResults, - }, - { - Name: "dangling slash in keyword", - SearchOptions: &internal.SearchOptions{ - Keyword: "\\", - }, - Expected: allResults, - }, - { - Name: "dangling quote in keyword", - SearchOptions: &internal.SearchOptions{ - Keyword: "\"", - }, - Expected: allResults, }, { Name: "empty", @@ -772,25 +742,6 @@ 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 9c14e4cbd3..17a8ba2452 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -100,7 +100,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, nil) + _, err := b.inner.Client.Index(b.inner.VersionedIndexName()).AddDocuments(issue) if err != nil { return err } @@ -303,9 +303,21 @@ func doubleQuoteKeyword(k string) string { } func convertHits(searchRes *meilisearch.SearchResponse) ([]internal.Match, error) { - hits := make([]internal.Match, 0) - if err := searchRes.Hits.DecodeInto(&hits); err != nil { - return nil, ErrMalformedResponse + 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 { + return nil, ErrMalformedResponse + } + + hits = append(hits, internal.Match{ + ID: int64(issueID), + }) } return hits, nil } diff --git a/modules/indexer/issues/meilisearch/meilisearch_test.go b/modules/indexer/issues/meilisearch/meilisearch_test.go index 810de77046..7637e8d6b4 100644 --- a/modules/indexer/issues/meilisearch/meilisearch_test.go +++ b/modules/indexer/issues/meilisearch/meilisearch_test.go @@ -46,34 +46,30 @@ func TestMeilisearchIndexer(t *testing.T) { } func TestConvertHits(t *testing.T) { - 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) - } + _, err := convertHits(&meilisearch.SearchResponse{ + Hits: []any{"aa", "bb", "cc", "dd"}, + }) + require.ErrorIs(t, err, ErrMalformedResponse) validResponse := &meilisearch.SearchResponse{ - 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\"]"), + 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"}, }, - meilisearch.Hit{ - "id": []byte("22"), - "title": []byte("\"Bowling as title\""), - "content": []byte("\"\""), - "comments": []byte("[]"), + map[string]any{ + "id": float64(22), + "title": "Bowling as title", + "content": "", + "comments": []any{}, }, - meilisearch.Hit{ - "id": []byte("33"), - "title": []byte("\"Bowl-ing as fuzzy match\""), - "content": []byte("\"\""), - "comments": []byte("[]"), + map[string]any{ + "id": float64(33), + "title": "Bowl-ing as fuzzy match", + "content": "", + "comments": []any{}, }, }, } diff --git a/modules/issue/template/unmarshal.go b/modules/issue/template/unmarshal.go index 0d80dae009..8df71a3299 100644 --- a/modules/issue/template/unmarshal.go +++ b/modules/issue/template/unmarshal.go @@ -15,7 +15,7 @@ import ( api "forgejo.org/modules/structs" "forgejo.org/modules/util" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) // CouldBe indicates a file with the filename could be a template, diff --git a/modules/keying/keying.go b/modules/keying/keying.go index f39e16aeed..c859e30e9f 100644 --- a/modules/keying/keying.go +++ b/modules/keying/keying.go @@ -58,8 +58,6 @@ 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/label/parser.go b/modules/label/parser.go index 12fc176967..558ae68def 100644 --- a/modules/label/parser.go +++ b/modules/label/parser.go @@ -10,7 +10,7 @@ import ( "forgejo.org/modules/options" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) type labelFile struct { diff --git a/modules/lfs/pointer_scanner.go b/modules/lfs/pointer_scanner.go index 80da8e5222..632ecd19ae 100644 --- a/modules/lfs/pointer_scanner.go +++ b/modules/lfs/pointer_scanner.go @@ -39,7 +39,16 @@ 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 - go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan) + 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) + } wg.Wait() close(pointerChan) diff --git a/modules/log/event_format.go b/modules/log/event_format.go index 6835a4ca5b..df6b083a92 100644 --- a/modules/log/event_format.go +++ b/modules/log/event_format.go @@ -13,9 +13,10 @@ import ( type Event struct { Time time.Time - Caller string - Filename string - Line int + GoroutinePid string + Caller string + Filename string + Line int Level Level @@ -224,6 +225,19 @@ 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 f6f9754300..0c6061eaea 100644 --- a/modules/log/event_format_test.go +++ b/modules/log/event_format_test.go @@ -24,45 +24,48 @@ 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, - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + GoroutinePid: "pid", + 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] msg format: arg0 arg1 + assert.Equal(t, `<3>[PREFIX] 2020/01/02 03:04:05.000000 filename:123:caller [E] [pid] 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, - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + GoroutinePid: "pid", + 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 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 [\x1b[93mpid\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, - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + GoroutinePid: "pid", + Level: ERROR, + Stacktrace: "stacktrace", }, "msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue), ) @@ -74,12 +77,13 @@ 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, - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + GoroutinePid: "pid", + Level: ERROR, + Stacktrace: "stacktrace", }, "msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue), ) @@ -92,12 +96,13 @@ 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, - Level: ERROR, - Stacktrace: "stacktrace", + Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC), + Caller: "caller", + Filename: "filename", + Line: 123, + GoroutinePid: "pid", + 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 32b5b582c5..4b77e488de 100644 --- a/modules/log/event_writer.go +++ b/modules/log/event_writer.go @@ -26,7 +26,6 @@ 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 4de2b953c7..9189ca4e90 100644 --- a/modules/log/event_writer_base.go +++ b/modules/log/event_writer_base.go @@ -68,14 +68,6 @@ 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 { @@ -103,13 +95,6 @@ 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 d1e37c3673..ba9455ba69 100644 --- a/modules/log/event_writer_buffer_test.go +++ b/modules/log/event_writer_buffer_test.go @@ -31,49 +31,3 @@ 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 1e4fe830c1..afce30680d 100644 --- a/modules/log/flags.go +++ b/modules/log/flags.go @@ -30,6 +30,7 @@ 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 @@ -56,6 +57,7 @@ var flagFromString = map[string]uint32{ "levelinitial": Llevelinitial, "level": Llevel, "levelprefix": Llevelprefix, + "gopid": Lgopid, "medfile": Lmedfile, "stdflags": LstdFlags, @@ -80,6 +82,7 @@ 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 f39fe440e2..1af9a58ed6 100644 --- a/modules/log/flags_test.go +++ b/modules/log/flags_test.go @@ -15,7 +15,9 @@ import ( func TestFlags(t *testing.T) { assert.Equal(t, Ldefault, Flags{}.Bits()) assert.EqualValues(t, 0, FlagsFromString("").Bits()) - assert.Equal(t, Ldate|Ltime, FlagsFromString("date,time").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, "stdflags", FlagsFromString("stdflags").String()) assert.Equal(t, "medfile", FlagsFromString("medfile").String()) diff --git a/modules/log/groutinelabel.go b/modules/log/groutinelabel.go new file mode 100644 index 0000000000..cd5fb96d52 --- /dev/null +++ b/modules/log/groutinelabel.go @@ -0,0 +1,21 @@ +//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 new file mode 100644 index 0000000000..e849811bc2 --- /dev/null +++ b/modules/log/groutinelabel_go1.24.go @@ -0,0 +1,38 @@ +//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 new file mode 100644 index 0000000000..98d7b4e129 --- /dev/null +++ b/modules/log/groutinelabel_test.go @@ -0,0 +1,33 @@ +// 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 76d7e6a821..b21e800f52 100644 --- a/modules/log/logger_impl.go +++ b/modules/log/logger_impl.go @@ -200,6 +200,11 @@ 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 99045b0f4f..6d6ceb69d7 100644 --- a/modules/log/logger_test.go +++ b/modules/log/logger_test.go @@ -143,19 +143,3 @@ 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/log/stack.go b/modules/log/stack.go index a8d26d1fc3..9b22e92867 100644 --- a/modules/log/stack.go +++ b/modules/log/stack.go @@ -32,7 +32,7 @@ func Stack(skip int) string { } // Print equivalent of debug.Stack() - _, _ = fmt.Fprintf(buf, "\t%s:%d (0x%x)\n", filename, lineNumber, programCounter) + _, _ = fmt.Fprintf(buf, "%s:%d (0x%x)\n", filename, lineNumber, programCounter) // Now try to print the offending line if filename != lastFilename { data, err := os.ReadFile(filename) @@ -44,7 +44,7 @@ func Stack(skip int) string { lines = bytes.Split(data, []byte{'\n'}) lastFilename = filename } - _, _ = fmt.Fprintf(buf, "\t\t%s: %s\n", functionName(programCounter), source(lines, lineNumber)) + _, _ = fmt.Fprintf(buf, "\t%s: %s\n", functionName(programCounter), source(lines, lineNumber)) } return buf.String() } diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index dab6057cf4..8b1442ed08 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -25,7 +25,7 @@ import ( ) // filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2" -var filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L?\d+)?)`) +var filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`) type FilePreview struct { fileContent []template.HTML @@ -78,17 +78,17 @@ func newFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca commitSha := node.Data[m[4]:m[5]] filePath := node.Data[m[6]:m[7]] - hash := node.Data[m[8]:m[9]] urlFullSource := urlFull if strings.HasSuffix(filePath, "?display=source") { filePath = strings.TrimSuffix(filePath, "?display=source") } else if Type(filePath) != "" { - urlFullSource = node.Data[m[0]:m[6]] + filePath + "?display=source#" + hash + urlFullSource = node.Data[m[0]:m[6]] + filePath + "?display=source#" + node.Data[m[8]:m[1]] } filePath, err := url.QueryUnescape(filePath) if err != nil { return nil } + hash := node.Data[m[8]:m[9]] preview.start = m[0] preview.end = m[1] diff --git a/modules/markup/html.go b/modules/markup/html.go index 58021f5cb5..7961c5c930 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -557,13 +557,9 @@ func createCodeLink(href, content, class string) *html.Node { a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class}) } - unescaped, err := url.QueryUnescape(content) - if err != nil { - unescaped = content - } text := &html.Node{ Type: html.TextNode, - Data: unescaped, + Data: content, } code := &html.Node{ @@ -1146,7 +1142,7 @@ func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) { converted := emoji.FromAlias(alias) if converted == nil { // check if this is a custom reaction - if setting.UI.CustomEmojisLookup.Contains(alias) { + if _, exist := setting.UI.CustomEmojisMap[alias]; exist { replaceContent(node, m[0], m[1], createCustomEmoji(alias)) node = node.NextSibling.NextSibling start = 0 diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 11a6290ca3..2bc929bb04 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -91,9 +91,6 @@ func TestRender_Commits(t *testing.T) { test(sha[:14]+".", `

`+expected14+`.

`) test(sha[:14]+",", `

`+expected14+`,

`) test("["+sha[:14]+"]", `

[`+expected14+`]

`) - - fileStrangeChars := util.URLJoin(repo, "src", "commit", "eeb243c3395e1921c5d90e73bd739827251fc99d", "path", "to", "file%20%23.txt") - test(fileStrangeChars, `

eeb243c339/path/to/file #.txt

`) } func TestRender_CrossReferences(t *testing.T) { @@ -361,7 +358,7 @@ func TestRender_emoji(t *testing.T) { test( ":custom-emoji:", `

:custom-emoji:

`) - setting.UI.CustomEmojisLookup.Add("custom-emoji") + setting.UI.CustomEmojisMap["custom-emoji"] = ":custom-emoji:" test( ":custom-emoji:", `

:custom-emoji:

`) @@ -746,11 +743,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -780,11 +777,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -859,11 +856,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -890,11 +887,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -923,11 +920,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -948,11 +945,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -979,11 +976,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1004,11 +1001,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1029,11 +1026,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1112,39 +1109,6 @@ func TestRender_FilePreview(t *testing.T) { ) }) - t.Run("rendered file with lines L1-2 instead of L1-L2", func(t *testing.T) { - testRender( - commitFileURL+"#L1-2", - `

`+ - `
`+ - `
`+ - `
`+ - `path/to/file.md`+ - `
`+ - ``+ - `Lines 1 to 2 in c991312`+ - ``+ - `
`+ - `
`+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - `
# A`+"\n"+`
B`+"\n"+`
`+ - `
`+ - `
`+ - `

`, - localMetas, - ) - }) - commitFileURL = util.URLJoin(markup.TestRepoURL, "src", "commit", "190d9492934af498c3f669d6a2431dc5459e5b20", "path", "to", "file.go") t.Run("normal file with ?display=source", func(t *testing.T) { @@ -1165,11 +1129,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ diff --git a/modules/markup/markdown/convertyaml.go b/modules/markup/markdown/convertyaml.go index 8e5a49d931..1675b68be2 100644 --- a/modules/markup/markdown/convertyaml.go +++ b/modules/markup/markdown/convertyaml.go @@ -6,7 +6,7 @@ package markdown import ( "github.com/yuin/goldmark/ast" east "github.com/yuin/goldmark/extension/ast" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) func nodeToTable(meta *yaml.Node) ast.Node { diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index 8a3da3b08f..d229afa8e3 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -9,8 +9,6 @@ import ( "strings" "forgejo.org/modules/markup" - "forgejo.org/modules/markup/common" - markdownutil "forgejo.org/modules/markup/markdown/util" "forgejo.org/modules/setting" "github.com/yuin/goldmark/ast" @@ -37,8 +35,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(markdownutil.RenderContextKey).(*markup.RenderContext) - rc := pc.Get(markdownutil.RenderConfigKey).(*RenderConfig) + ctx := pc.Get(renderContextKey).(*markup.RenderContext) + rc := pc.Get(renderConfigKey).(*RenderConfig) tocList := make([]markup.Header, 0, 20) if rc.yamlNode != nil { @@ -75,18 +73,6 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa } case *ast.CodeSpan: g.transformCodeSpan(ctx, v, reader) - case *common.Footnote: - if scope, found := ctx.Metas["scope"]; found { - v.Name = fmt.Appendf(v.Name, "-%s", scope) - } - case *common.FootnoteLink: - if scope, found := ctx.Metas["scope"]; found { - v.Name = fmt.Appendf(v.Name, "-%s", scope) - } - case *common.FootnoteBackLink: - if scope, found := ctx.Metas["scope"]; found { - v.Name = fmt.Appendf(v.Name, "-%s", scope) - } } return ast.WalkContinue, nil }) diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index 2b19e0f1c9..e811d29994 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -16,7 +16,6 @@ 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" @@ -35,6 +34,11 @@ var ( specMarkdownOnce sync.Once ) +var ( + renderContextKey = parser.NewContextKey() + renderConfigKey = parser.NewContextKey() +) + type limitWriter struct { w io.Writer sum int64 @@ -60,7 +64,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(markdownutil.RenderContextKey, ctx) + pc.Set(renderContextKey, ctx) return pc } @@ -188,7 +192,7 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) } rc.metaLength = metaLength - pc.Set(markdownutil.RenderConfigKey, rc) + pc.Set(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 7c13494a67..e229ee4c65 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -104,7 +104,7 @@ func TestRender_Images(t *testing.T) { test( "!["+title+"]("+url+")", - `

`+title+`

`) + `

`+title+`

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

`+title+`

`) + `

`+title+`

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

image1
-image2

+ expected := `

image1
+image2

` res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase) require.NoError(t, err) @@ -561,14 +561,6 @@ 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 { @@ -576,32 +568,6 @@ 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) { @@ -802,49 +768,6 @@ Citation needed[^0].`, } } -func TestFootnoteWithScope(t *testing.T) { - testcases := []struct { - testcase string - expected string - }{ - { - `Citation needed[^0]. -[^0]: Source`, - `

Citation needed1.

-
-
-
    -
  1. -

    Source ↩︎

    -
  2. -
-
-`, - }, { - `[^0]: Source - -Citation needed[^0].`, - `

Citation needed1.

-
-
-
    -
  1. -

    Source ↩︎

    -
  2. -
-
-`, - }, - } - - for _, test := range testcases { - metas := map[string]string{"scope": "comment-999"} - res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext, Metas: metas}, test.testcase) - require.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) - assert.Equal(t, test.expected, string(res), "Unexpected result in testcase %q", test.testcase) - } -} - func TestTaskList(t *testing.T) { testcases := []struct { testcase string @@ -882,27 +805,6 @@ foo: bar } } -func TestRenderCheckList(t *testing.T) { - input := `- [ ] a -- [x] b -1. [x] a -2. [ ] b -5. [ ] e` - expected := `
    -
  • a
  • -
  • b
  • -
-
    -
  1. a
  2. -
  3. b
  4. -
  5. e
  6. -
-` - res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, input) - require.NoError(t, err) - assert.Equal(t, template.HTML(expected), res) -} - func TestRenderLinks(t *testing.T) { input := ` space @mention-user${SPACE}${SPACE} /just/a/path.bin @@ -943,10 +845,10 @@ mail@domain.com remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
diff --git a/modules/markup/markdown/math/block_parser.go b/modules/markup/markdown/math/block_parser.go index b0fe1d588a..527df84975 100644 --- a/modules/markup/markdown/math/block_parser.go +++ b/modules/markup/markdown/math/block_parser.go @@ -6,9 +6,6 @@ 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" @@ -64,13 +61,6 @@ 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/meta.go b/modules/markup/markdown/meta.go index 5bdc680e9e..e76b253ecd 100644 --- a/modules/markup/markdown/meta.go +++ b/modules/markup/markdown/meta.go @@ -9,7 +9,7 @@ import ( "unicode" "unicode/utf8" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) func isYAMLSeparator(line []byte) bool { diff --git a/modules/markup/markdown/renderconfig.go b/modules/markup/markdown/renderconfig.go index 8c278137dc..5c3eb1beec 100644 --- a/modules/markup/markdown/renderconfig.go +++ b/modules/markup/markdown/renderconfig.go @@ -10,7 +10,7 @@ import ( "forgejo.org/modules/markup" "github.com/yuin/goldmark/ast" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) // RenderConfig represents rendering configuration for this file diff --git a/modules/markup/markdown/renderconfig_test.go b/modules/markup/markdown/renderconfig_test.go index 478f57443e..c53acdc77a 100644 --- a/modules/markup/markdown/renderconfig_test.go +++ b/modules/markup/markdown/renderconfig_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) func TestRenderConfig_UnmarshalYAML(t *testing.T) { diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go index b86c9e3d41..0f9c69cae6 100644 --- a/modules/markup/markdown/transform_image.go +++ b/modules/markup/markdown/transform_image.go @@ -44,7 +44,6 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image) for _, attr := range v.Attributes() { image.SetAttribute(attr.Name, attr.Value) } - image.SetAttributeString("loading", []byte("lazy")) for child := v.FirstChild(); child != nil; { next := child.NextSibling() image.AppendChild(image, child) diff --git a/modules/markup/markdown/util/text.go b/modules/markup/markdown/util/text.go index db6e432e79..8a42e5835b 100644 --- a/modules/markup/markdown/util/text.go +++ b/modules/markup/markdown/util/text.go @@ -7,7 +7,6 @@ import ( "bytes" "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/parser" ) func textOfChildren(n ast.Node, src []byte, b *bytes.Buffer) { @@ -25,8 +24,3 @@ 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 71157dc7c7..cdaa9f18ce 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/markup/renderer.go b/modules/markup/renderer.go index 08502e12ab..a622d75085 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -248,14 +248,15 @@ type nopCloser struct { func (nopCloser) Close() error { return nil } func renderIFrame(ctx *RenderContext, output io.Writer) error { - // set height="300", otherwise if the postMessage mechanism breaks, we are left with a 0-height iframe + // set height="0" ahead, otherwise the scrollHeight would be max(150, realHeight) // at the moment, only "allow-scripts" is allowed for sandbox mode. // "allow-same-origin" should never be used, it leads to XSS attack, and it makes the JS in iframe can access parent window's config and CSRF token // TODO: when using dark theme, if the rendered content doesn't have proper style, the default text color is black, which is not easy to read _, err := io.WriteString(output, fmt.Sprintf(` `, setting.AppSubURL, @@ -316,12 +317,6 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr if err1 := renderer.Render(ctx, input, pw); err1 != nil { return err1 } - - if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() { - // Append a short script to the iframe's contents, which will communicate the scroll height of the embedded document via postMessage, either once loaded (in case the containing page loads first) in response to a postMessage from external.js, in case the iframe loads first - // We use '*' as a target origin for postMessage, because can be certain we are embedded on the same domain, due to X-Frame-Options configured elsewhere. (Plus, the offsetHeight of an embedded document is likely not sensitive data anyway.) - _, _ = pw.Write([]byte("")) - } _ = pw.Close() wg.Wait() diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index aacc2536bf..384dd1fe94 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -108,9 +108,6 @@ func createDefaultPolicy() *bluemonday.Policy { // Allow classes for emojis policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img") - // Allow attributes for images - policy.AllowAttrs("loading").Matching(regexp.MustCompile(`^lazy$`)).OnElements("img") - // Allow icons, emojis, chroma syntax and keyword markup on span policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span") policy.AllowAttrs("data-alias").Matching(regexp.MustCompile(`^[a-zA-Z0-9-_+]+$`)).OnElements("span") diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go index a0faff0494..9805a34910 100644 --- a/modules/markup/sanitizer_test.go +++ b/modules/markup/sanitizer_test.go @@ -75,10 +75,6 @@ func Test_Sanitizer(t *testing.T) { // Emoji `THUMBS UP`, `THUMBS UP`, `THUMBS UP`, `THUMBS UP`, - - // Images lazy loading - `image1`, `image1`, - `image1`, `image1`, } for i := 0; i < len(testCases); i += 2 { diff --git a/modules/migration/file_format.go b/modules/migration/file_format.go index af5f806444..8851ad6de7 100644 --- a/modules/migration/file_format.go +++ b/modules/migration/file_format.go @@ -13,7 +13,7 @@ import ( "forgejo.org/modules/log" "github.com/santhosh-tekuri/jsonschema/v6" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) // Load project data from file, with optional validation diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go index 2e2ccd6786..86c1c97341 100644 --- a/modules/optional/serialization.go +++ b/modules/optional/serialization.go @@ -6,7 +6,7 @@ package optional import ( "forgejo.org/modules/json" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) func (o *Option[T]) UnmarshalJSON(data []byte) error { diff --git a/modules/optional/serialization_test.go b/modules/optional/serialization_test.go index 5da0923a78..14bf3b7814 100644 --- a/modules/optional/serialization_test.go +++ b/modules/optional/serialization_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) type testSerializationStruct struct { diff --git a/modules/packages/arch/metadata.go b/modules/packages/arch/metadata.go index ca352c828c..f967bd25a0 100644 --- a/modules/packages/arch/metadata.go +++ b/modules/packages/arch/metadata.go @@ -7,7 +7,6 @@ import ( "archive/tar" "bufio" "bytes" - "context" "encoding/hex" "errors" "fmt" @@ -20,7 +19,7 @@ import ( "forgejo.org/modules/util" "forgejo.org/modules/validation" - "github.com/mholt/archives" + "github.com/mholt/archiver/v3" ) // Arch Linux Packages @@ -110,61 +109,55 @@ func ParsePackage(r *packages.HashedBuffer) (*Package, error) { return nil, err } - var tarball archives.Extractor + var tarball archiver.Reader var tarballType string if bytes.Equal(header[:len(magicZSTD)], magicZSTD) { tarballType = "zst" - tarball = archives.CompressedArchive{ - Compression: archives.Zstd{}, - Extraction: archives.Tar{}, - } + tarball = archiver.NewTarZstd() } else if bytes.Equal(header[:len(magicXZ)], magicXZ) { tarballType = "xz" - tarball = archives.CompressedArchive{ - Compression: archives.Xz{}, - Extraction: archives.Tar{}, - } + tarball = archiver.NewTarXz() } else if bytes.Equal(header[:len(magicGZ)], magicGZ) { tarballType = "gz" - tarball = archives.CompressedArchive{ - Compression: archives.Gz{}, - Extraction: archives.Tar{}, - } + tarball = archiver.NewTarGz() } 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) - err = tarball.Extract(context.TODO(), r, func(ctx context.Context, file archives.FileInfo) error { + for { + f, err := tarball.Read() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } // ref:https://gitlab.archlinux.org/pacman/pacman/-/blob/91546004903eea5d5267d59898a6029ba1d64031/lib/libalpm/add.c#L529-L533 - if !strings.HasPrefix(file.Name(), ".") { - files = append(files, (file.Header.(*tar.Header)).Name) + if !strings.HasPrefix(f.Name(), ".") { + files = append(files, (f.Header.(*tar.Header)).Name) } - switch file.Name() { + switch f.Name() { case ".PKGINFO": - f, err := file.Open() - if err != nil { - return err - } - defer f.Close() - pkg, err = ParsePackageInfo(tarballType, f) if err != nil { - return err + _ = f.Close() + return nil, err } case ".MTREE": mTree = true } - - return nil - }) - if err != nil { - return nil, err + _ = f.Close() } if pkg == nil { diff --git a/modules/packages/arch/metadata_test.go b/modules/packages/arch/metadata_test.go index fbdc9e5ddc..16c1c1637d 100644 --- a/modules/packages/arch/metadata_test.go +++ b/modules/packages/arch/metadata_test.go @@ -5,7 +5,7 @@ package arch import ( "bytes" - "io/fs" + "errors" "os" "strings" "testing" @@ -14,7 +14,7 @@ import ( "forgejo.org/modules/packages" - "github.com/mholt/archives" + "github.com/mholt/archiver/v3" "github.com/stretchr/testify/require" ) @@ -25,7 +25,7 @@ pkgbase = b pkgver = 1-2 arch = x86_64 ` - mapfs := fstest.MapFS{ + fs := fstest.MapFS{ "pkginfo": &fstest.MapFile{ Data: []byte(PKGINFO), Mode: os.ModePerm, @@ -39,111 +39,65 @@ arch = x86_64 } // Test .PKGINFO file - pinf, err := mapfs.Stat("pkginfo") + 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") require.NoError(t, err) // Test .MTREE file - minf, err := mapfs.Stat("mtree") + minf, err := fs.Stat("mtree") require.NoError(t, err) - files := []archives.FileInfo{ - { - FileInfo: pinf, - NameInArchive: ".PKGINFO", - Open: func() (fs.File, error) { - return mapfs.Open("pkginfo") + mfile, err := fs.Open("mtree") + require.NoError(t, err) + + marcname, err := archiver.NameInArchive(minf, ".MTREE", ".MTREE") + require.NoError(t, err) + + t.Run("normal 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, }, - }, - { - FileInfo: minf, - NameInArchive: ".MTREE", - Open: func() (fs.File, error) { - return mapfs.Open("mtree") + ReadCloser: pfile, + }) + require.NoError(t, errors.Join(pfile.Close(), err)) + + err = archive.Write(archiver.File{ + FileInfo: archiver.FileInfo{ + FileInfo: minf, + CustomName: marcname, }, - }, - } - - t.Run("normal zst archive", func(t *testing.T) { - var buf bytes.Buffer - archive := archives.CompressedArchive{ - Archival: archives.Tar{}, - Compression: archives.Zstd{}, - } - archive.Archive(t.Context(), &buf, files) + ReadCloser: mfile, + }) + require.NoError(t, errors.Join(mfile.Close(), archive.Close(), err)) reader, err := packages.CreateHashedBufferFromReader(&buf) - require.NoError(t, err) - defer reader.Close() - 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{}, + if err != nil { + t.Fatal(err) } - 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) + _, err = ParsePackage(reader) require.NoError(t, err) }) t.Run("missing .PKGINFO", func(t *testing.T) { var buf bytes.Buffer - 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") - }, - }, - }) + + archive := archiver.NewTarZstd() + archive.Create(&buf) + require.NoError(t, archive.Close()) reader, err := packages.CreateHashedBufferFromReader(&buf) require.NoError(t, err) @@ -157,20 +111,21 @@ arch = x86_64 t.Run("missing .MTREE", func(t *testing.T) { var buf bytes.Buffer - 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") - }, - }, - }) + 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, + }, + 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 1729a2206e..e44801654b 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(?:[1-9]?[0-9]:)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?\z`) + versionPattern = regexp.MustCompile(`\A(?:[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 079b9c19c8..cfcbc57ee0 100644 --- a/modules/packages/debian/metadata_test.go +++ b/modules/packages/debian/metadata_test.go @@ -167,14 +167,6 @@ 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/packages/helm/metadata.go b/modules/packages/helm/metadata.go index 4d3eaaa40c..19a30c5ffa 100644 --- a/modules/packages/helm/metadata.go +++ b/modules/packages/helm/metadata.go @@ -13,7 +13,7 @@ import ( "forgejo.org/modules/validation" "github.com/hashicorp/go-version" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) var ( diff --git a/modules/packages/pub/metadata.go b/modules/packages/pub/metadata.go index e1d4286f97..f8afdf7218 100644 --- a/modules/packages/pub/metadata.go +++ b/modules/packages/pub/metadata.go @@ -14,7 +14,7 @@ import ( "forgejo.org/modules/validation" "github.com/hashicorp/go-version" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) var ( diff --git a/modules/packages/rpm/metadata.go b/modules/packages/rpm/metadata.go index 503b7b1a24..4af9af620f 100644 --- a/modules/packages/rpm/metadata.go +++ b/modules/packages/rpm/metadata.go @@ -232,10 +232,9 @@ func getEntries(h *rpmutils.RpmHeader, namesTag, versionsTag, flagsTag int, repo case "alt": for i := range names { e := &Entry{ - Name: names[i], AltFlags: uint32(flags[i]), - Version: versions[i], } + e.Version = versions[i] entries = append(entries, e) } } diff --git a/modules/packages/rubygems/metadata.go b/modules/packages/rubygems/metadata.go index 9d3fd18f12..6d021a17ab 100644 --- a/modules/packages/rubygems/metadata.go +++ b/modules/packages/rubygems/metadata.go @@ -13,7 +13,7 @@ import ( "forgejo.org/modules/util" "forgejo.org/modules/validation" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) var ( diff --git a/modules/private/hook.go b/modules/private/hook.go index 89d44119ea..2d64c1dec9 100644 --- a/modules/private/hook.go +++ b/modules/private/hook.go @@ -11,7 +11,6 @@ import ( "forgejo.org/modules/git" "forgejo.org/modules/git/pushoptions" - "forgejo.org/modules/log" "forgejo.org/modules/repository" "forgejo.org/modules/setting" ) @@ -47,7 +46,7 @@ func (o *HookOptions) GetGitPushOptions() pushoptions.Interface { // SSHLogOption ssh log options type SSHLogOption struct { - Level log.Level + IsError bool Message string } @@ -122,9 +121,9 @@ func SetDefaultBranch(ctx context.Context, ownerName, repoName, branch string) R } // SSHLog sends ssh error log response -func SSHLog(ctx context.Context, level log.Level, msg string) error { +func SSHLog(ctx context.Context, isErr bool, msg string) error { reqURL := setting.LocalURL + "api/internal/ssh/log" - req := newInternalRequest(ctx, reqURL, "POST", &SSHLogOption{Level: level, Message: msg}) + req := newInternalRequest(ctx, reqURL, "POST", &SSHLogOption{IsError: isErr, Message: msg}) _, extra := requestJSONResp(req, &ResponseText{}) return extra.Error } diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go index 6325199637..19f3b9008a 100644 --- a/modules/setting/config_provider.go +++ b/modules/setting/config_provider.go @@ -6,7 +6,6 @@ package setting import ( "errors" "fmt" - "math" "os" "path/filepath" "strconv" @@ -16,7 +15,6 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/util" - "github.com/dustin/go-humanize" "gopkg.in/ini.v1" //nolint:depguard ) @@ -322,16 +320,6 @@ func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) { } } -// mustBytes returns -1 on parse error, or value out of range 0 to math.MaxInt64 -func mustBytes(section ConfigSection, key string) int64 { - value := section.Key(key).String() - bytes, err := humanize.ParseBytes(value) - if err != nil || bytes > math.MaxInt64 { - return -1 - } - return int64(bytes) -} - // DeprecatedWarnings contains the warning message for various deprecations, including: setting option, file/folder, etc var DeprecatedWarnings []string @@ -343,14 +331,6 @@ 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/config_provider_test.go b/modules/setting/config_provider_test.go index 3588784b81..3b99911f38 100644 --- a/modules/setting/config_provider_test.go +++ b/modules/setting/config_provider_test.go @@ -155,24 +155,3 @@ func TestDisableSaving(t *testing.T) { require.NoError(t, err) assert.Equal(t, "k1 = a\nk2 = y\nk3 = z\n", string(bs)) } - -func TestMustBytes(t *testing.T) { - test := func(value string) int64 { - cfg, err := NewConfigProviderFromData("[test]") - require.NoError(t, err) - sec := cfg.Section("test") - sec.NewKey("VALUE", value) - - return mustBytes(sec, "VALUE") - } - - assert.EqualValues(t, -1, test("")) - assert.EqualValues(t, -1, test("-1")) - assert.EqualValues(t, 0, test("0")) - assert.EqualValues(t, 1, test("1")) - assert.EqualValues(t, 10000, test("10000")) - assert.EqualValues(t, 1000000, test("1 mb")) - assert.EqualValues(t, 1048576, test("1mib")) - assert.EqualValues(t, 1782579, test("1.7mib")) - assert.EqualValues(t, -1, test("1 yib")) // too large -} diff --git a/modules/setting/cron_test.go b/modules/setting/cron_test.go index ba73c4039d..cabfb3a325 100644 --- a/modules/setting/cron_test.go +++ b/modules/setting/cron_test.go @@ -42,56 +42,3 @@ EXTEND = true assert.Equal(t, "white rabbit", extended.Second) assert.True(t, extended.Extend) } - -// Test_getCronSettings2 tests that getCronSettings can not handle two levels of embedding -func Test_getCronSettings2(t *testing.T) { - type BaseStruct struct { - Enabled bool - RunAtStart bool - Schedule string - } - - type Extended struct { - BaseStruct - Extend bool - } - type Extended2 struct { - Extended - Third string - } - - iniStr := ` -[cron.test] -ENABLED = TRUE -RUN_AT_START = TRUE -SCHEDULE = @every 1h -EXTEND = true -THIRD = white rabbit -` - cfg, err := NewConfigProviderFromData(iniStr) - require.NoError(t, err) - - extended := &Extended2{ - Extended: Extended{ - BaseStruct: BaseStruct{ - Enabled: false, - RunAtStart: false, - Schedule: "@every 72h", - }, - Extend: false, - }, - Third: "black rabbit", - } - - _, err = getCronSettings(cfg, "test", extended) - require.NoError(t, err) - - // This confirms the first level of embedding works - assert.Equal(t, "white rabbit", extended.Third) - assert.True(t, extended.Extend) - - // This confirms 2 levels of embedding doesn't work - assert.False(t, extended.Enabled) - assert.False(t, extended.RunAtStart) - assert.Equal(t, "@every 72h", extended.Schedule) -} diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index a890a4a328..e592220de6 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -44,14 +44,9 @@ func loadIncomingEmailFrom(rootCfg ConfigProvider) { if sec.HasKey("USER") && !sec.HasKey("USERNAME") { IncomingEmail.Username = sec.Key("USER").String() } - if sec.HasKey("PASSWD") && !sec.HasKey("PASSWORD") { - sec.Key("PASSWORD").SetValue(sec.Key("PASSWD").String()) + IncomingEmail.Password = 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 4ea740bafd..6d181cae3c 100644 --- a/modules/setting/incoming_email_test.go +++ b/modules/setting/incoming_email_test.go @@ -4,8 +4,6 @@ package setting import ( - "os" - "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -37,22 +35,6 @@ 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 ecc591fd35..0747ac4dac 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -23,6 +23,8 @@ type LogGlobalConfig struct { StacktraceLogLevel log.Level BufferLen int + EnableSSHLog bool + AccessLogTemplate string RequestIDHeaders []string } @@ -45,6 +47,8 @@ func loadLogGlobalFrom(rootCfg ConfigProvider) { } Log.RootPath = util.FilePathJoinAbs(Log.RootPath) + Log.EnableSSHLog = sec.Key("ENABLE_SSH_LOG").MustBool(false) + Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString(accessLogTemplateDefault) Log.RequestIDHeaders = sec.Key("REQUEST_ID_HEADERS").Strings(",") } @@ -52,83 +56,41 @@ func loadLogGlobalFrom(rootCfg ConfigProvider) { func prepareLoggerConfig(rootCfg ConfigProvider) { sec := rootCfg.Section("log") - // 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 + if !sec.HasKey("logger.default.MODE") { + sec.Key("logger.default.MODE").MustString(",") } - // 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) + 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) } if sec.HasKey("ENABLE_ACCESS_LOG") && !sec.Key("ENABLE_ACCESS_LOG").MustBool() { - sec.Key("LOGGER_ACCESS_MODE").SetValue("") + sec.Key("logger.access.MODE").SetValue("") } - // 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 + 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) } - if val := sec.Key("ROUTER").String(); hasNoValue && val != "" { - sec.Key("LOGGER_ROUTER_MODE").SetValue(val) - hasNoValue = false + if !sec.HasKey("logger.router.MODE") { + sec.Key("logger.router.MODE").MustString(",") // use default logger } if sec.HasKey("DISABLE_ROUTER_LOG") && sec.Key("DISABLE_ROUTER_LOG").MustBool() { - sec.Key("LOGGER_ROUTER_MODE").SetValue("") - hasNoValue = false - } - if hasNoValue { - sec.Key("LOGGER_ROUTER_MODE").SetValue(",") // use default logger + sec.Key("logger.router.MODE").SetValue("") } - // 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 + 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) } - if val := sec.Key("XORM").String(); hasNoValue && val != "" { - sec.Key("LOGGER_XORM_MODE").SetValue(val) - hasNoValue = false + if !sec.HasKey("logger.xorm.MODE") { + sec.Key("logger.xorm.MODE").MustString(",") // use default logger } if sec.HasKey("ENABLE_XORM_LOG") && !sec.Key("ENABLE_XORM_LOG").MustBool() { - sec.Key("LOGGER_XORM_MODE").SetValue("") - hasNoValue = false - } - if hasNoValue { - sec.Key("LOGGER_XORM_MODE").SetValue(",") // use default logger - } - - // Priority: `LOGGER_SSH_MODE` -> `ENABLE_SSH_LOG` - deprecatedSettingWarning(rootCfg, "log", "ENABLE_SSH_LOG", "log", "LOGGER_SSH_MODE") - if !sec.HasKey("LOGGER_SSH_MODE") && sec.HasKey("ENABLE_SSH_LOG") { - if sec.Key("ENABLE_SSH_LOG").MustBool() { - sec.Key("LOGGER_SSH_MODE").SetValue(",") // use default logger - } else { - sec.Key("LOGGER_SSH_MODE").SetValue("") // disable ssh logger - } + sec.Key("logger.xorm.MODE").SetValue("") } } @@ -171,7 +133,6 @@ 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 { @@ -251,19 +212,18 @@ func initManagedLoggers(manager *log.LoggerManager, cfg ConfigProvider) { initLoggerByName(manager, cfg, "access") initLoggerByName(manager, cfg, "router") initLoggerByName(manager, cfg, "xorm") - initLoggerByName(manager, cfg, "ssh") } func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, loggerName string) { sec := rootCfg.Section("log") - key := "LOGGER_" + strings.ToUpper(loggerName) + "_MODE" + keyPrefix := "logger." + loggerName - disabled := sec.HasKey(key) && sec.Key(key).String() == "" + disabled := sec.HasKey(keyPrefix+".MODE") && sec.Key(keyPrefix+".MODE").String() == "" if disabled { return } - modeVal := sec.Key(key).String() + modeVal := sec.Key(keyPrefix + ".MODE").String() if modeVal == "," { modeVal = Log.Mode } diff --git a/modules/setting/log_test.go b/modules/setting/log_test.go index 1e523d50d7..eda6dc36af 100644 --- a/modules/setting/log_test.go +++ b/modules/setting/log_test.go @@ -10,13 +10,16 @@ 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()) { - defer test.MockVariableValue(&Log, LogGlobalConfig{})() + oldLogConfig := Log + Log = LogGlobalConfig{} + defer func() { + Log = oldLogConfig + }() cfg, err := NewConfigProviderFromData(config) require.NoError(t, err) @@ -26,17 +29,6 @@ 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) @@ -52,7 +44,6 @@ func TestLogConfigDefault(t *testing.T) { "BufferLen": 10000, "Colorize": false, "Expression": "", - "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -92,7 +83,6 @@ logger.xorm.MODE = "BufferLen": 10000, "Colorize": false, "Expression": "", - "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -131,7 +121,6 @@ MODE = console "BufferLen": 10000, "Colorize": false, "Expression": "", - "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -179,7 +168,6 @@ ACCESS = file "BufferLen": 10000, "Colorize": false, "Expression": "", - "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -203,7 +191,6 @@ ACCESS = file "BufferLen": 10000, "Colorize": false, "Expression": "", - "Exclusion": "", "Flags": "none", "Level": "info", "Prefix": "", @@ -251,8 +238,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 @@ -270,7 +257,6 @@ STDERR = true "BufferLen": 10000, "Colorize": false, "Expression": "", - "Exclusion": "", "Flags": "stdflags", "Level": "warn", "Prefix": "", @@ -284,7 +270,6 @@ STDERR = true "BufferLen": 10000, "Colorize": false, "Expression": "", - "Exclusion": "", "Flags": "stdflags", "Level": "error", "Prefix": "", @@ -302,7 +287,6 @@ STDERR = true "BufferLen": 10000, "Colorize": false, "Expression": "", - "Exclusion": "", "Flags": "none", "Level": "warn", "Prefix": "", @@ -339,7 +323,6 @@ MODE = file LEVEL = error STACKTRACE_LEVEL = fatal EXPRESSION = filter -EXCLUSION = not FLAGS = medfile PREFIX = "[Prefix] " FILE_NAME = file-xxx.log @@ -358,7 +341,6 @@ COMPRESSION_LEVEL = 4 "BufferLen": 10, "Colorize": false, "Expression": "", - "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -378,7 +360,6 @@ COMPRESSION_LEVEL = 4 "BufferLen": 10, "Colorize": false, "Expression": "filter", - "Exclusion": "not", "Flags": "medfile", "Level": "error", "Prefix": "[Prefix] ", @@ -403,216 +384,3 @@ 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", - }, - }) - }) - - t.Run("ssh", func(t *testing.T) { - runCases(t, "LOGGER_SSH_MODE", Cases{ - { - "uses default value for ssh logger", - "", - "", - }, - { - "deprecated config can enable logger", - `[log] -ENABLE_SSH_LOG = true -`, - ",", - }, - { - "check priority", - `[log] -LOGGER_SSH_MODE = file -ENABLE_SSH_LOG = true -`, - "file", - }, - }) - }) -} diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index b43484a90f..9c004c6ce0 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -147,10 +147,6 @@ 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 47eaf3ffbb..4523cc91dd 100644 --- a/modules/setting/mailer_test.go +++ b/modules/setting/mailer_test.go @@ -4,8 +4,6 @@ package setting import ( - "os" - "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -54,24 +52,6 @@ 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 799efed761..5f35a284d6 100644 --- a/modules/setting/moderation.go +++ b/modules/setting/moderation.go @@ -3,28 +3,13 @@ package setting -import ( - "fmt" - "time" -) - // Moderation settings var Moderation = struct { - Enabled bool `ini:"ENABLED"` - KeepResolvedReportsFor time.Duration `ini:"KEEP_RESOLVED_REPORTS_FOR"` + Enabled bool `ini:"ENABLED"` }{ Enabled: false, } -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 +func loadModerationFrom(rootCfg ConfigProvider) { + mustMapSetting(rootCfg, "moderation", &Moderation) } diff --git a/modules/setting/packages.go b/modules/setting/packages.go index ba4768c66b..87e41fb5a0 100644 --- a/modules/setting/packages.go +++ b/modules/setting/packages.go @@ -5,9 +5,12 @@ package setting import ( "fmt" + "math" "net/url" "os" "path/filepath" + + "github.com/dustin/go-humanize" ) // Package registry settings @@ -107,3 +110,17 @@ func loadPackagesFrom(rootCfg ConfigProvider) (err error) { Packages.LimitSizeAlt = mustBytes(sec, "LIMIT_SIZE_ALT") return nil } + +func mustBytes(section ConfigSection, key string) int64 { + const noLimit = "-1" + + value := section.Key(key).MustString(noLimit) + if value == noLimit { + return -1 + } + bytes, err := humanize.ParseBytes(value) + if err != nil || bytes > math.MaxInt64 { + return -1 + } + return int64(bytes) +} diff --git a/modules/setting/packages_test.go b/modules/setting/packages_test.go index a2cfdc6e35..85a4656da0 100644 --- a/modules/setting/packages_test.go +++ b/modules/setting/packages_test.go @@ -10,6 +10,27 @@ import ( "github.com/stretchr/testify/require" ) +func TestMustBytes(t *testing.T) { + test := func(value string) int64 { + cfg, err := NewConfigProviderFromData("[test]") + require.NoError(t, err) + sec := cfg.Section("test") + sec.NewKey("VALUE", value) + + return mustBytes(sec, "VALUE") + } + + assert.EqualValues(t, -1, test("")) + assert.EqualValues(t, -1, test("-1")) + assert.EqualValues(t, 0, test("0")) + assert.EqualValues(t, 1, test("1")) + assert.EqualValues(t, 10000, test("10000")) + assert.EqualValues(t, 1000000, test("1 mb")) + assert.EqualValues(t, 1048576, test("1mib")) + assert.EqualValues(t, 1782579, test("1.7mib")) + assert.EqualValues(t, -1, test("1 yib")) // too large +} + func Test_getStorageInheritNameSectionTypeForPackages(t *testing.T) { // packages storage inherits from storage if nothing configured iniStr := ` diff --git a/modules/setting/quota.go b/modules/setting/quota.go index 01b3f16bbd..05e14baa9c 100644 --- a/modules/setting/quota.go +++ b/modules/setting/quota.go @@ -23,7 +23,4 @@ var Quota = struct { func loadQuotaFrom(rootCfg ConfigProvider) { mustMapSetting(rootCfg, "quota", &Quota) - - sec := rootCfg.Section("quota.default") - Quota.Default.Total = mustBytes(sec, "TOTAL") } diff --git a/modules/setting/quota_test.go b/modules/setting/quota_test.go deleted file mode 100644 index baec9ccb4b..0000000000 --- a/modules/setting/quota_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package setting - -import ( - "fmt" - "testing" - - "forgejo.org/modules/test" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestConfigQuotaDefaultTotal(t *testing.T) { - iniStr := `` - cfg, err := NewConfigProviderFromData(iniStr) - require.NoError(t, err) - loadQuotaFrom(cfg) - - assert.False(t, Quota.Enabled) - assert.EqualValues(t, -1, Quota.Default.Total) - - testSets := []struct { - iniTotal string - expectTotal int64 - }{ - {"0", 0}, - {"5000", 5000}, - {"12,345,678", 12_345_678}, - {"2k", 2000}, - {"2MiB", 2 * 1024 * 1024}, - {"3G", 3_000_000_000}, - {"3GiB", 3 * 1024 * 1024 * 1024}, - {"9EB", 9_000_000_000_000_000_000}, - {"42EB", -1}, - {"-1", -1}, - {"-42", -1}, - {"-1MiB", -1}, - {"hello", -1}, - {"unlimited", -1}, - } - - for _, testSet := range testSets { - t.Run(testSet.iniTotal, func(t *testing.T) { - defer test.MockVariableValue(&Quota.Default.Total, -404)() - - iniStr := fmt.Sprintf(` -[quota] -ENABLED = true -[quota.default] -TOTAL = %s`, testSet.iniTotal) - - cfg, err := NewConfigProviderFromData(iniStr) - require.NoError(t, err) - loadQuotaFrom(cfg) - - assert.True(t, Quota.Enabled) - assert.Equal(t, testSet.expectTotal, Quota.Default.Total) - }) - } -} diff --git a/modules/setting/security.go b/modules/setting/security.go index c591a7c90a..c38d8dae79 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -20,7 +20,6 @@ var ( SecretKey string InternalToken string // internal access token LogInRememberDays int - GlobalTwoFactorRequirement TwoFactorRequirementType CookieRememberName string ReverseProxyAuthUser string ReverseProxyAuthEmail string @@ -36,7 +35,6 @@ var ( PasswordHashAlgo string PasswordCheckPwn bool SuccessfulTokensCacheSize int - DisableQueryAuthToken bool CSRFCookieName = "_csrf" CSRFCookieHTTPOnly = true ) @@ -114,8 +112,6 @@ 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") @@ -163,50 +159,4 @@ 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 3ff91d2cde..bff51f787d 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -16,8 +16,6 @@ import ( "forgejo.org/modules/json" "forgejo.org/modules/log" "forgejo.org/modules/util" - - "github.com/caddyserver/certmagic" ) // Scheme describes protocol types @@ -208,7 +206,7 @@ func loadServerFrom(rootCfg ConfigProvider) { EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) } if EnableAcme { - AcmeURL = sec.Key("ACME_URL").MustString(certmagic.LetsEncryptProductionCA) + AcmeURL = sec.Key("ACME_URL").MustString("") 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 9644d9b83b..75c24580b2 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -140,10 +140,6 @@ 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) @@ -225,6 +221,7 @@ func LoadSettings() { loadProjectFrom(CfgProvider) loadMimeTypeMapFrom(CfgProvider) loadF3From(CfgProvider) + loadModerationFrom(CfgProvider) } // LoadSettingsForInstall initializes the settings for install diff --git a/modules/setting/ui.go b/modules/setting/ui.go index 9dafe350eb..2e6a3df4c6 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -31,7 +31,7 @@ var UI = struct { Reactions []string ReactionsLookup container.Set[string] `ini:"-"` CustomEmojis []string - CustomEmojisLookup container.Set[string] `ini:"-"` + CustomEmojisMap map[string]string `ini:"-"` SearchRepoDescription bool OnlyShowRelevantRepos bool ExploreDefaultSort string `ini:"EXPLORE_PAGING_DEFAULT_SORT"` @@ -87,6 +87,7 @@ var UI = struct { Themes: []string{`forgejo-auto`, `forgejo-light`, `forgejo-dark`, `gitea-auto`, `gitea-light`, `gitea-dark`, `forgejo-auto-deuteranopia-protanopia`, `forgejo-light-deuteranopia-protanopia`, `forgejo-dark-deuteranopia-protanopia`, `forgejo-auto-tritanopia`, `forgejo-light-tritanopia`, `forgejo-dark-tritanopia`}, Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`, `forgejo`}, + CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:", "forgejo": ":forgejo:"}, ExploreDefaultSort: "recentupdate", PreferredTimestampTense: "mixed", @@ -162,6 +163,8 @@ func loadUIFrom(rootCfg ConfigProvider) { for _, reaction := range UI.Reactions { UI.ReactionsLookup.Add(reaction) } - UI.CustomEmojisLookup = make(container.Set[string]) - UI.CustomEmojisLookup.AddMultiple(UI.CustomEmojis...) + UI.CustomEmojisMap = make(map[string]string) + for _, emoji := range UI.CustomEmojis { + UI.CustomEmojisMap[emoji] = ":" + emoji + ":" + } } diff --git a/modules/ssh/init.go b/modules/ssh/init.go index 09b96a7d8a..1ccd95b18b 100644 --- a/modules/ssh/init.go +++ b/modules/ssh/init.go @@ -15,8 +15,6 @@ import ( "forgejo.org/modules/setting" ) -var logger = log.GetManager().GetLogger("ssh") - func Init() error { if setting.SSH.Disabled { builtinUnused() diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index 502fcd070f..19cac0b603 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -61,10 +61,10 @@ func sessionHandler(session ssh.Session) { command := session.RawCommand() - logger.Trace("SSH: Payload: %v", command) + log.Trace("SSH: Payload: %v", command) args := []string{"--config=" + setting.CustomConf, "serv", "key-" + keyID} - logger.Trace("SSH: Arguments: %v", args) + log.Trace("SSH: Arguments: %v", args) ctx, cancel := context.WithCancel(session.Context()) defer cancel() @@ -87,21 +87,21 @@ func sessionHandler(session ssh.Session) { stdout, err := cmd.StdoutPipe() if err != nil { - logger.Error("SSH: StdoutPipe: %v", err) + log.Error("SSH: StdoutPipe: %v", err) return } defer stdout.Close() stderr, err := cmd.StderrPipe() if err != nil { - logger.Error("SSH: StderrPipe: %v", err) + log.Error("SSH: StderrPipe: %v", err) return } defer stderr.Close() stdin, err := cmd.StdinPipe() if err != nil { - logger.Error("SSH: StdinPipe: %v", err) + log.Error("SSH: StdinPipe: %v", err) return } defer stdin.Close() @@ -112,14 +112,14 @@ func sessionHandler(session ssh.Session) { wg.Add(2) if err = cmd.Start(); err != nil { - logger.Error("SSH: Start: %v", err) + log.Error("SSH: Start: %v", err) return } go func() { defer stdin.Close() if _, err := io.Copy(stdin, session); err != nil { - logger.Error("Failed to write session to stdin. %s", err) + log.Error("Failed to write session to stdin. %s", err) } }() @@ -127,7 +127,7 @@ func sessionHandler(session ssh.Session) { defer wg.Done() defer stdout.Close() if _, err := io.Copy(session, stdout); err != nil { - logger.Error("Failed to write stdout to session. %s", err) + log.Error("Failed to write stdout to session. %s", err) } }() @@ -135,7 +135,7 @@ func sessionHandler(session ssh.Session) { defer wg.Done() defer stderr.Close() if _, err := io.Copy(session.Stderr(), stderr); err != nil { - logger.Error("Failed to write stderr to session. %s", err) + log.Error("Failed to write stderr to session. %s", err) } }() @@ -149,41 +149,41 @@ func sessionHandler(session ssh.Session) { // Cannot use errors.Is here because ExitError doesn't implement Is // Thus errors.Is will do equality test NOT type comparison if _, ok := err.(*exec.ExitError); !ok { - logger.Error("SSH: Wait: %v", err) + log.Error("SSH: Wait: %v", err) } } if err := session.Exit(getExitStatusFromError(err)); err != nil && !errors.Is(err, io.EOF) { - logger.Error("Session failed to exit. %s", err) + log.Error("Session failed to exit. %s", err) } } func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { - if logger.LevelEnabled(log.DEBUG) { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary - logger.Debug("Handle Public Key: Fingerprint: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr()) + if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary + log.Debug("Handle Public Key: Fingerprint: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr()) } if ctx.User() != setting.SSH.BuiltinServerUser { - logger.Warn("Invalid SSH username %s - must use %s for all git operations via ssh", ctx.User(), setting.SSH.BuiltinServerUser) - logger.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) + log.Warn("Invalid SSH username %s - must use %s for all git operations via ssh", ctx.User(), setting.SSH.BuiltinServerUser) + log.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) return false } // check if we have a certificate if cert, ok := key.(*gossh.Certificate); ok { - if logger.LevelEnabled(log.DEBUG) { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary - logger.Debug("Handle Certificate: %s Fingerprint: %s is a certificate", ctx.RemoteAddr(), gossh.FingerprintSHA256(key)) + if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary + log.Debug("Handle Certificate: %s Fingerprint: %s is a certificate", ctx.RemoteAddr(), gossh.FingerprintSHA256(key)) } if len(setting.SSH.TrustedUserCAKeys) == 0 { - logger.Warn("Certificate Rejected: No trusted certificate authorities for this server") - logger.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) + log.Warn("Certificate Rejected: No trusted certificate authorities for this server") + log.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) return false } if cert.CertType != gossh.UserCert { - logger.Warn("Certificate Rejected: Not a user certificate") - logger.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) + log.Warn("Certificate Rejected: Not a user certificate") + log.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) return false } @@ -193,10 +193,10 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { pkey, err := asymkey_model.SearchPublicKeyByContentExact(ctx, principal) if err != nil { if asymkey_model.IsErrKeyNotExist(err) { - logger.Debug("Principal Rejected: %s Unknown Principal: %s", ctx.RemoteAddr(), principal) + log.Debug("Principal Rejected: %s Unknown Principal: %s", ctx.RemoteAddr(), principal) continue principalLoop } - logger.Error("SearchPublicKeyByContentExact: %v", err) + log.Error("SearchPublicKeyByContentExact: %v", err) return false } @@ -215,8 +215,8 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { // check the CA of the cert if !c.IsUserAuthority(cert.SignatureKey) { - if logger.LevelEnabled(log.DEBUG) { - logger.Debug("Principal Rejected: %s Untrusted Authority Signature Fingerprint %s for Principal: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(cert.SignatureKey), principal) + if log.IsDebug() { + log.Debug("Principal Rejected: %s Untrusted Authority Signature Fingerprint %s for Principal: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(cert.SignatureKey), principal) } continue principalLoop } @@ -224,14 +224,14 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { // validate the cert for this principal if err := c.CheckCert(principal, cert); err != nil { // User is presenting an invalid certificate - STOP any further processing - logger.Error("Invalid Certificate KeyID %s with Signature Fingerprint %s presented for Principal: %s from %s", cert.KeyId, gossh.FingerprintSHA256(cert.SignatureKey), principal, ctx.RemoteAddr()) - logger.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) + log.Error("Invalid Certificate KeyID %s with Signature Fingerprint %s presented for Principal: %s from %s", cert.KeyId, gossh.FingerprintSHA256(cert.SignatureKey), principal, ctx.RemoteAddr()) + log.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) return false } - if logger.LevelEnabled(log.DEBUG) { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary - logger.Debug("Successfully authenticated: %s Certificate Fingerprint: %s Principal: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(key), principal) + if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary + log.Debug("Successfully authenticated: %s Certificate Fingerprint: %s Principal: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(key), principal) } if ctx.Permissions().Extensions == nil { ctx.Permissions().Extensions = map[string]string{} @@ -241,28 +241,28 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { return true } - logger.Warn("From %s Fingerprint: %s is a certificate, but no valid principals found", ctx.RemoteAddr(), gossh.FingerprintSHA256(key)) - logger.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) + log.Warn("From %s Fingerprint: %s is a certificate, but no valid principals found", ctx.RemoteAddr(), gossh.FingerprintSHA256(key)) + log.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) return false } - if logger.LevelEnabled(log.DEBUG) { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary - logger.Debug("Handle Public Key: %s Fingerprint: %s is not a certificate", ctx.RemoteAddr(), gossh.FingerprintSHA256(key)) + if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary + log.Debug("Handle Public Key: %s Fingerprint: %s is not a certificate", ctx.RemoteAddr(), gossh.FingerprintSHA256(key)) } pkey, err := asymkey_model.SearchPublicKeyByContent(ctx, strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key)))) if err != nil { if asymkey_model.IsErrKeyNotExist(err) { - logger.Warn("Unknown public key: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr()) - logger.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) + log.Warn("Unknown public key: %s from %s", gossh.FingerprintSHA256(key), ctx.RemoteAddr()) + log.Warn("Failed authentication attempt from %s", ctx.RemoteAddr()) return false } - logger.Error("SearchPublicKeyByContent: %v", err) + log.Error("SearchPublicKeyByContent: %v", err) return false } - if logger.LevelEnabled(log.DEBUG) { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary - logger.Debug("Successfully authenticated: %s Public Key Fingerprint: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(key)) + if log.IsDebug() { // <- FingerprintSHA256 is kinda expensive so only calculate it if necessary + log.Debug("Successfully authenticated: %s Public Key Fingerprint: %s", ctx.RemoteAddr(), gossh.FingerprintSHA256(key)) } if ctx.Permissions().Extensions == nil { ctx.Permissions().Extensions = map[string]string{} @@ -276,9 +276,9 @@ func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { // - this mainly exists to give a nice function name in logging func sshConnectionFailed(conn net.Conn, err error) { // Log the underlying error with a specific message - logger.Warn("Failed connection from %s with error: %v", conn.RemoteAddr(), err) + log.Warn("Failed connection from %s with error: %v", conn.RemoteAddr(), err) // Log with the standard failed authentication from message for simpler fail2ban configuration - logger.Warn("Failed authentication attempt from %s", conn.RemoteAddr()) + log.Warn("Failed authentication attempt from %s", conn.RemoteAddr()) } // Listen starts a SSH server listens on given port. @@ -317,22 +317,22 @@ func Listen(host string, port int, ciphers, keyExchanges, macs []string) { filePath := filepath.Dir(setting.SSH.ServerHostKeys[0]) if err := os.MkdirAll(filePath, os.ModePerm); err != nil { - logger.Error("Failed to create dir %s: %v", filePath, err) + log.Error("Failed to create dir %s: %v", filePath, err) } err := GenKeyPair(setting.SSH.ServerHostKeys[0]) if err != nil { log.Fatal("Failed to generate private key: %v", err) } - logger.Trace("New private key is generated: %s", setting.SSH.ServerHostKeys[0]) + log.Trace("New private key is generated: %s", setting.SSH.ServerHostKeys[0]) keys = append(keys, setting.SSH.ServerHostKeys[0]) } for _, key := range keys { - logger.Info("Adding SSH host key: %s", key) + log.Info("Adding SSH host key: %s", key) err := srv.SetOption(ssh.HostKeyFile(key)) if err != nil { - logger.Error("Failed to set Host Key. %s", err) + log.Error("Failed to set Host Key. %s", err) } } @@ -359,7 +359,7 @@ func GenKeyPair(keyPath string) error { } defer func() { if err = f.Close(); err != nil { - logger.Error("Close: %v", err) + log.Error("Close: %v", err) } }() @@ -380,7 +380,7 @@ func GenKeyPair(keyPath string) error { } defer func() { if err = p.Close(); err != nil { - logger.Error("Close: %v", err) + log.Error("Close: %v", err) } }() _, err = p.Write(public) diff --git a/modules/ssh/ssh_graceful.go b/modules/ssh/ssh_graceful.go index 98ddc18ae7..825313ab1c 100644 --- a/modules/ssh/ssh_graceful.go +++ b/modules/ssh/ssh_graceful.go @@ -20,12 +20,12 @@ func listen(server *ssh.Server) { if err != nil { select { case <-graceful.GetManager().IsShutdown(): - logger.Critical("Failed to start SSH server: %v", err) + log.Critical("Failed to start SSH server: %v", err) default: log.Fatal("Failed to start SSH server: %v", err) } } - logger.Info("SSH Listener: %s Closed", server.Addr) + log.Info("SSH Listener: %s Closed", server.Addr) } // builtinUnused informs our cleanup routine that we will not be using a ssh port diff --git a/modules/storage/minio.go b/modules/storage/minio.go index 8d4f9d6627..bf51a1642a 100644 --- a/modules/storage/minio.go +++ b/modules/storage/minio.go @@ -76,13 +76,8 @@ 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) @@ -117,7 +112,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(initCtx, minioClient, config.Bucket) + err = getBucketVersioning(ctx, minioClient, config.Bucket) if err != nil { errResp, ok := err.(minio.ErrorResponse) if !ok { @@ -130,13 +125,13 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, } // Check to see if we already own this bucket - exists, err := minioClient.BucketExists(initCtx, config.Bucket) + exists, err := minioClient.BucketExists(ctx, config.Bucket) if err != nil { return nil, convertMinioErr(err) } if !exists { - if err := minioClient.MakeBucket(initCtx, config.Bucket, minio.MakeBucketOptions{ + if err := minioClient.MakeBucket(ctx, 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 18fe91edfb..ec1b2fc77a 100644 --- a/modules/storage/minio_test.go +++ b/modules/storage/minio_test.go @@ -9,10 +9,8 @@ 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" @@ -219,41 +217,3 @@ 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 76589d941a..af3dd9520e 100644 --- a/modules/storage/storage_test.go +++ b/modules/storage/storage_test.go @@ -5,7 +5,6 @@ package storage import ( "bytes" - "io" "testing" "forgejo.org/modules/setting" @@ -14,39 +13,22 @@ 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 := []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}, + 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"}, } for _, f := range testFiles { - sc := &spyCloser{bytes.NewBufferString(f.content), 0} - _, err = l.Save(f.path, sc, f.size) + _, err = l.Save(f[0], bytes.NewBufferString(f[1]), -1) 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 0cc257ff95..117eb0bed2 100644 --- a/modules/structs/activitypub.go +++ b/modules/structs/activitypub.go @@ -1,5 +1,4 @@ // Copyright 2022 The Gitea Authors. All rights reserved. -// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package structs @@ -8,15 +7,3 @@ 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/attachment.go b/modules/structs/attachment.go index 746f618cf0..0a3d4140c2 100644 --- a/modules/structs/attachment.go +++ b/modules/structs/attachment.go @@ -22,12 +22,6 @@ type Attachment struct { Type string `json:"type"` } -// WebAttachment the generic attachment with mime type -type WebAttachment struct { - *Attachment - MimeType string `json:"mime_type"` -} - // EditAttachmentOptions options for editing attachments // swagger:model type EditAttachmentOptions struct { diff --git a/modules/structs/hook.go b/modules/structs/hook.go index 11372ca6e1..5adcad0881 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -53,7 +53,8 @@ type CreateHookOption struct { BranchFilter string `json:"branch_filter" binding:"GlobPattern"` AuthorizationHeader string `json:"authorization_header"` // default: false - Active bool `json:"active"` + Active bool `json:"active"` + IsSystemWebhook bool `json:"is_system_webhook"` } // EditHookOption options when modify one hook diff --git a/modules/structs/issue.go b/modules/structs/issue.go index 7b7397dc4b..a67bdcf50e 100644 --- a/modules/structs/issue.go +++ b/modules/structs/issue.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) // StateType issue state type diff --git a/modules/structs/issue_test.go b/modules/structs/issue_test.go index 7adb843206..2003e22e0a 100644 --- a/modules/structs/issue_test.go +++ b/modules/structs/issue_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) func TestIssueTemplate_Type(t *testing.T) { diff --git a/modules/structs/mirror.go b/modules/structs/mirror.go index 4909ae20ca..1b6566803a 100644 --- a/modules/structs/mirror.go +++ b/modules/structs/mirror.go @@ -13,7 +13,6 @@ 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 @@ -30,6 +29,4 @@ 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 01cbf26f61..c9cd729cf3 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -327,7 +327,6 @@ const ( GitBucketService // 7 gitbucket service CodebaseService // 8 codebase service ForgejoService // 9 forgejo service - PagureService // 10 pagure service ) // Name represents the service type's name @@ -355,8 +354,6 @@ func (gt GitServiceType) Title() string { return "Codebase" case ForgejoService: return "Forgejo" - case PagureService: - return "Pagure" case PlainGitService: return "Git" } @@ -415,7 +412,6 @@ 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 deleted file mode 100644 index d2b896391b..0000000000 --- a/modules/templates/context.go +++ /dev/null @@ -1,23 +0,0 @@ -// 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 deleted file mode 100644 index d854fbf0ff..0000000000 --- a/modules/templates/context_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// 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 848d4b4ad4..02b175e6f6 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -6,8 +6,6 @@ package templates import ( - "bytes" - "context" "fmt" "html" "html/template" @@ -31,23 +29,6 @@ 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, // ----------------------------------------------------------------- @@ -147,8 +128,8 @@ func NewFuncMap() template.FuncMap { "AllowedReactions": func() []string { return setting.UI.Reactions }, - "CustomEmojis": func() []string { - return setting.UI.CustomEmojis + "CustomEmojis": func() map[string]string { + return setting.UI.CustomEmojisMap }, "MetaAuthor": func() string { return setting.UI.Meta.Author @@ -207,7 +188,6 @@ 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 bec8d5f5e3..a4d7a82eea 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -7,7 +7,6 @@ import ( "context" "encoding/hex" "fmt" - "html" "html/template" "math" "net/url" @@ -16,18 +15,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 := html.EscapeString(msg) + cleanMsg := template.HTMLEscapeString(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{ @@ -64,7 +63,7 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlDefault string, Ctx: ctx, DefaultLink: urlDefault, Metas: metas, - }, html.EscapeString(msgLine)) + }, template.HTMLEscapeString(msgLine)) if err != nil { log.Error("RenderCommitMessageSubject: %v", err) return template.HTML("") @@ -89,7 +88,7 @@ func RenderCommitBody(ctx context.Context, msg string, metas map[string]string) renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{ Ctx: ctx, Metas: metas, - }, html.EscapeString(msgLine)) + }, template.HTMLEscapeString(msgLine)) if err != nil { log.Error("RenderCommitMessage: %v", err) return "" @@ -123,7 +122,7 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{ Ctx: ctx, Metas: metas, - }, html.EscapeString(text)) + }, template.HTMLEscapeString(text)) if err != nil { log.Error("RenderIssueTitle: %v", err) return template.HTML("") @@ -133,7 +132,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}, html.EscapeString(text)) + renderedText, err := markup.RenderRefIssueTitle(&markup.RenderContext{Ctx: ctx}, template.HTMLEscapeString(text)) if err != nil { log.Error("RenderRefIssueTitle: %v", err) return "" @@ -144,18 +143,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, label *issues_model.Label) template.HTML { +func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML { var ( archivedCSSClass string textColor = util.ContrastColor(label.Color) labelScope = label.ExclusiveScope() ) - description := emoji.ReplaceAliases(html.EscapeString(label.Description)) + description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) if label.IsArchived() { archivedCSSClass = "archived-label" - description = ctx.Locale.TrString("repo.issues.archived_label_description", description) + description = locale.TrString("repo.issues.archived_label_description", description) } if labelScope == "" { @@ -213,7 +212,7 @@ func RenderLabel(ctx *Context, label *issues_model.Label) template.HTML { // 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}, - html.EscapeString(text)) + template.HTMLEscapeString(text)) if err != nil { log.Error("RenderEmoji: %v", err) return template.HTML("") @@ -245,7 +244,7 @@ func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //n return output } -func RenderLabels(ctx *Context, labels []*issues_model.Label, repoLink string, isPull bool) template.HTML { +func RenderLabels(ctx context.Context, locale translation.Locale, 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 @@ -258,34 +257,16 @@ func RenderLabels(ctx *Context, labels []*issues_model.Label, repoLink string, i issuesOrPull = "pulls" } htmlCode += fmt.Sprintf("%s ", - repoLink, issuesOrPull, label.ID, RenderLabel(ctx, label)) + repoLink, issuesOrPull, label.ID, RenderLabel(ctx, locale, label)) } htmlCode += "" return template.HTML(htmlCode) } -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 { +func RenderReviewRequest(users []issues_model.RequestReviewTarget) template.HTML { usernames := make([]string, 0, len(users)) for _, user := range users { - 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()))) - } + usernames = append(usernames, template.HTMLEscapeString(user.Name())) } htmlCode := `` diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 3cfd572491..b75b061218 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -10,11 +10,7 @@ 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" @@ -196,8 +192,8 @@ func TestRenderMarkdownToHtml(t *testing.T) { remote link local link remote link -local image -remote image +local image +remote image 88fc37a3c0...12fc37a3c0 (hash) @@ -219,70 +215,9 @@ 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}) - 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>") + 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") } diff --git a/modules/test/distant_federation_server_mock.go b/modules/test/distant_federation_server_mock.go index ea8a69e9b4..9bd908e2b9 100644 --- a/modules/test/distant_federation_server_mock.go +++ b/modules/test/distant_federation_server_mock.go @@ -10,79 +10,56 @@ import ( "net/http/httptest" "strings" "testing" - - "forgejo.org/modules/util" ) type FederationServerMockPerson struct { - ID int64 - Name string - PubKey string - PrivKey string + ID int64 + Name string + PubKey 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: pub, - PrivKey: priv, + 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"`, } } -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/v1/activitypub/user-id/%[2]v",`+ + `"id":"http://%[1]v/api/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/v1/activitypub/user-id/%[2]v/inbox",`+ - `"outbox":"http://%[1]v/api/v1/activitypub/user-id/%[2]v/outbox",`+ + `"inbox":"http://%[1]v/api/activitypub/user-id/%[2]v/inbox",`+ + `"outbox":"http://%[1]v/api/activitypub/user-id/%[2]v/outbox",`+ `"preferredUsername":"%[3]v",`+ - `"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) + `"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) } func NewFederationServerMock() *FederationServerMock { return &FederationServerMock{ - ApActor: NewApActorMock(), Persons: []FederationServerMockPerson{ NewFederationServerMockPerson(15, "stargoose1"), NewFederationServerMockPerson(30, "stargoose2"), @@ -94,18 +71,8 @@ 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 @@ -120,28 +87,30 @@ 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)) }) - federatedRoutes.HandleFunc(fmt.Sprintf("POST /api/v1/activitypub/user-id/%v/inbox", person.ID), - func(res http.ResponseWriter, req *http.Request) { - mock.recordLastPost(t, req) - }) } - for _, repository := range mock.Repositories { - federatedRoutes.HandleFunc(fmt.Sprintf("POST /api/v1/activitypub/repository-id/%v/inbox", repository.ID), + federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox", repository.ID), func(res http.ResponseWriter, req *http.Request) { - mock.recordLastPost(t, req) + 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() }) } federatedRoutes.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { - t.Errorf("Unhandled %v request: %q", req.Method, req.URL.EscapedPath()) + t.Errorf("Unhandled request: %q", req.URL.EscapedPath()) }) federatedSrv := httptest.NewServer(federatedRoutes) return federatedSrv diff --git a/modules/translation/plural_rules.go b/modules/translation/plural_rules.go index 587ee48850..59665da255 100644 --- a/modules/translation/plural_rules.go +++ b/modules/translation/plural_rules.go @@ -2,7 +2,6 @@ // 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 @@ -17,7 +16,7 @@ import ( "forgejo.org/modules/translation/i18n" ) -// The constants refer to indices below in `PluralRules` and also in web_src/js/webcomponents/relative-time.js, keep them in sync! +// The constants refer to indices below in `PluralRules` and also in i18n.js, keep them in sync! const ( PluralRuleDefault = 0 PluralRuleBengali = 1 diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go index 8cb1513a88..262feb2b05 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, filename string) SniffedType { +func DetectContentType(data []byte) SniffedType { if len(data) == 0 { return SniffedType{"text/unknown"} } @@ -176,13 +176,6 @@ func DetectContentType(data []byte, filename string) 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")) { @@ -193,7 +186,7 @@ func DetectContentType(data []byte, filename string) SniffedType { } // DetectContentTypeFromReader guesses the content type contained in the reader. -func DetectContentTypeFromReader(r io.Reader, filename string) (SniffedType, error) { +func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) { buf := make([]byte, sniffLen) n, err := util.ReadAtMost(r, buf) if err != nil { @@ -201,5 +194,5 @@ func DetectContentTypeFromReader(r io.Reader, filename string) (SniffedType, err } buf = buf[:n] - return DetectContentType(buf, filename), nil + return DetectContentType(buf), nil } diff --git a/modules/typesniffer/typesniffer_test.go b/modules/typesniffer/typesniffer_test.go index d2b7ed4f21..176d3658bb 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,24 +158,10 @@ 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/util/shellquote_test.go b/modules/util/shellquote_test.go index 6c1b778a08..969998c592 100644 --- a/modules/util/shellquote_test.go +++ b/modules/util/shellquote_test.go @@ -3,11 +3,7 @@ package util -import ( - "testing" - - "github.com/stretchr/testify/assert" -) +import "testing" func TestShellEscape(t *testing.T) { tests := []struct { @@ -83,23 +79,13 @@ func TestShellEscape(t *testing.T) { "Single quotes don't need to escape except for '...", "~/ ${gitea} `gitea` (gitea) !gitea! \"gitea\" \\gitea\\ 'gitea'", "~/' ${gitea} `gitea` (gitea) !gitea! \"gitea\" \\gitea\\ '\\''gitea'\\'", - }, { - "Inline command", - "some`echo foo`thing", - "\"some\\`echo foo\\`thing\"", - }, { - "Substitution", - `;${HOME}`, - `";\${HOME}"`, - }, { - "ANSI Escape codes (not escaped)", - "\033[31;1;4mHello\033[0m", - "\"\x1b[31;1;4mHello\x1b[0m\"", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, ShellEscape(tt.toEscape)) + if got := ShellEscape(tt.toEscape); got != tt.want { + t.Errorf("ShellEscape(%q):\nGot: %s\nWanted: %s", tt.toEscape, got, tt.want) + } }) } } diff --git a/modules/util/string.go b/modules/util/string.go index ca3d43ec6e..cf50f591c6 100644 --- a/modules/util/string.go +++ b/modules/util/string.go @@ -95,25 +95,3 @@ func UnsafeBytesToString(b []byte) string { func UnsafeStringToBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s)) } - -// AsciiEqualFold is taken from Golang, but reimplemented here, since the original is not exposed to public -// Taken from: https://cs.opensource.google/go/go/+/refs/tags/go1.24.4:src/net/http/internal/ascii/print.go -func ASCIIEqualFold(s, t string) bool { - if len(s) != len(t) { - return false - } - for i := 0; i < len(s); i++ { - if ASCIILower(s[i]) != ASCIILower(t[i]) { - return false - } - } - return true -} - -// AsciiLower returns the ASCII lowercase version of b. -func ASCIILower(b byte) byte { - if 'A' <= b && b <= 'Z' { - return b + ('a' - 'A') - } - return b -} diff --git a/modules/util/string_test.go b/modules/util/string_test.go index 1012ab32a4..0a4a8bbcfb 100644 --- a/modules/util/string_test.go +++ b/modules/util/string_test.go @@ -45,29 +45,3 @@ func TestToSnakeCase(t *testing.T) { assert.Equal(t, expected, ToSnakeCase(input)) } } - -func TestASCIIEqualFold(t *testing.T) { - cases := map[string]struct { - First string - Second string - Expected bool - }{ - "Empty String": {First: "", Second: "", Expected: true}, - "Single Letter Ident": {First: "h", Second: "h", Expected: true}, - "Single Letter Equal": {First: "h", Second: "H", Expected: true}, - "Single Letter Unequal": {First: "h", Second: "g", Expected: false}, - "Simple Match Ident": {First: "someString", Second: "someString", Expected: true}, - "Simple Match Equal": {First: "someString", Second: "someSTRIng", Expected: true}, - "Simple Match Unequal": {First: "someString", Second: "sameString", Expected: false}, - "Different Length": {First: "abcdef", Second: "abcdefg", Expected: false}, - "Unicode Kelvin": {First: "ghijklm", Second: "GHIJ\u212ALM", Expected: false}, - } - - for name := range cases { - c := cases[name] - t.Run(name, func(t *testing.T) { - Actual := ASCIIEqualFold(c.First, c.Second) - assert.Equal(t, c.Expected, Actual) - }) - } -} diff --git a/modules/validation/email.go b/modules/validation/email.go index 7960a80a1f..8e1ffc203d 100644 --- a/modules/validation/email.go +++ b/modules/validation/email.go @@ -72,23 +72,16 @@ func validateEmailBasic(email string) error { } func validateEmailDomain(email string) error { - if _, ok := IsEmailDomainAllowed(email); !ok { + if !IsEmailDomainAllowed(email) { return ErrEmailInvalid{email} } return nil } -func IsEmailDomainAllowed(email string) (validEmail, ok bool) { - // Normalized the address. This strips for example comments which could be - // used to smuggle a different domain - parsedAddress, err := mail.ParseAddress(email) - if err != nil { - return false, false - } - - return true, isEmailDomainAllowedInternal( - parsedAddress.Address, +func IsEmailDomainAllowed(email string) bool { + return isEmailDomainAllowedInternal( + email, setting.Service.EmailDomainAllowList, setting.Service.EmailDomainBlockList) } diff --git a/modules/validation/email_test.go b/modules/validation/email_test.go index 28158cae53..b7ee766ddb 100644 --- a/modules/validation/email_test.go +++ b/modules/validation/email_test.go @@ -67,3 +67,8 @@ func TestEmailAddressValidate(t *testing.T) { }) } } + +func TestEmailDomainAllowList(t *testing.T) { + res := IsEmailDomainAllowed("someuser@localhost.localdomain") + assert.True(t, res) +} diff --git a/modules/validation/validatable.go b/modules/validation/validatable.go index 7bcca03bf8..4500f6e53d 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("Field %v must not be nil", name)} + return []string{fmt.Sprintf("%v should 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("Value %v should not be empty", name)} + return []string{fmt.Sprintf("%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 is longer than expected length %v", name, maxLen)} + return []string{fmt.Sprintf("Value %v was longer than %v", name, maxLen)} } return []string{} } diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 23351d9cbf..f4ac1a0e3d 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -23,7 +23,9 @@ explore = إكتشف return_to_forgejo = العودة إلى فورجيو write = اكتب webauthn_error_unknown = حدث خطأ غير معروف. من فضلك حاول مجدداً. +webauthn_reload = إعادة تحميل twofa = المصادقة الثنائية +account_settings = إعدادات الحساب version = الإصدار copy_success = تم النسخ! help = مساعدة @@ -48,7 +50,7 @@ concept_user_organization = المنظمة link_account = ربط الحساب rerun_all = أعِد تشغيل جميع الوظائف your_profile = الملف الشخصي -sign_out = سجّل الخروج +sign_out = سجل الخروج settings = الإعدادات locked = مقفول error = خطأ @@ -64,6 +66,7 @@ webauthn_unsupported_browser = متصفحك لا يدعم ويب آوثن حال copy = انسخ enabled = مُفَعَّل rerun = أعِد التشغيل +new_org = منظمة جديدة milestones = أهداف webauthn_error_insecure = ويب آوثن يدعم فقط الاتصالات الآمنة. للاختبار على HTTP، يمكنك استخدام "localhost" أو "127.0.0.1" show_timestamps = إظهار الطوابع الزمنية @@ -84,11 +87,14 @@ add_all = أضف الكل new_fork = اشتقاق جديد لمستودع new_project_column = عمود جديد add = أضف -active_stopwatch = متتبِّع وقت النشاط +active_stopwatch = تتبع وقت الإنجاز organization = منظمة +new_migrate = ترحيل جديد save = احفظ sign_in_with_provider = سجل الدخول بـ %s ok = وافق +manage_org = إدارة المنظمات +new_repo = مستودع جديد webauthn_error_unable_to_process = الخادم لا يمكنه معالجة طلبك. register = سجل mirror = مرآة @@ -108,7 +114,7 @@ twofa_scratch = الرمز الاحتياطي للمصادقة بعاملين home = الرئيسية email = عنوان البريد الإلكتروني issues = المسائل -error404 = الصفحة التي تحاول الوصول لها إما غير موجودو أو أنك غير مصرح لك بعرضها. +error404 = الصفحة التي تحاول الوصول لها إما لا توجد أو أنت لست مأذون لك بعرضها. powered_by = مدعوم بواسطة %s retry = أعد المحاولة tracked_time_summary = ملخص للتتبع الزمني وفقًا لنتائج تصفية قائمة المسائل @@ -121,8 +127,8 @@ toggle_menu = تبديل القائمة more_items = عناصر اضافية copy_generic = نسخ إلى الحافظة invalid_data = بيانات غير صالحة: %v -filter.clear = مسح عوامل التصفية -filter = عامل تصفية +filter.clear = مسح المرشحات +filter = مرشح filter.is_archived = مؤرشف filter.is_template = قوالب filter.not_mirror = ليست مرايا @@ -131,17 +137,13 @@ filter.is_mirror = مرايا filter.is_fork = الاشتقاقات filter.not_fork = ليست اشتقاقات filter.not_archived = ليس مؤرشف -filter.public = عام +filter.public = علني filter.private = خاص new_repo.title = مستودع جديد new_migrate.title = انتقال جديد new_org.title = منظمة جديدة new_repo.link = مستودع جديد new_migrate.link = انتقال جديد -copy_path = نسخ المسار -test = اختبار -new_org.link = منظمة جديدة -error413 = لقد استنفدت حصتك. [install] db_name = اسم قاعدة البيانات @@ -160,7 +162,7 @@ ssl_mode = SSL db_title = إعدادات قاعدة البيانات install = التثبيت allow_dots_in_usernames = السماح للمستخدمين بوضع نقاط في أسمائهم. لا يؤثر على الحسابات الموجودة. -enable_update_checker_helper_forgejo = يفحص دورياً لنسخ جديدة من فورجيو عن طريق التحقق من سجل TXT DNS عند release.forgejo.org. +enable_update_checker_helper_forgejo = يفحص دورياً لنسخ جديدة من فورجيو عن طريق التحقق من سجل DNS TXT عند release.forgejo.org. db_schema_helper = اتركه فارغاً لقاعدة البيانات الافتراضية ("عام"). db_type = نوع قاعدة البيانات reinstall_confirm_check_1 = البيانات المشفرة بواسطة SECRET_KEY في مِلَفّ app.ini قد تُفقد: قد لا يتمكن المستخدمون من تسجيل الدخول باستخدام المصادقة الثنائية (2FA/OTP) و المرايات قد لا تعمل بالشكل الصحيح. من خلال تحديد هذا المربع أنت تؤكد أن مِلَفّ app.ini الحالي يحتوي على الـSECRET_KEY الصحيح. @@ -168,7 +170,7 @@ reinstall_confirm_check_2 = وقد يلزم إعادة تزامن المستود run_user = شغّل عبر مستخدم err_admin_name_is_invalid = اسم مستخدم المدير غير صالح reinstall_confirm_check_3 = أنتِ تؤكد أنكِ متأكد تماماً من أن فورجيو يعمل مع مسار app.ini الصحيح وأنك متأكد من أنه يجب عليك إعادة تثبيته. أنت تُؤكّدُ بأنّك تُقرّ بالمخاطر السالفة الذكر. -repo_path = المسار الجذر للمستودع +repo_path = المسار الجذري للمستودع err_empty_admin_email = عنوان بريد المدير لا يمكن أن يكون فارغ. no_admin_and_disable_registration = لا يمكنك تعطيل التسجيل الذاتي للمستخدمين بدون إنشاء حساب إداري. err_admin_name_pattern_not_allowed = اسم مستخدم المدير غير صالح، هذا الأسم يطابق نمطا محجوز @@ -177,10 +179,10 @@ repo_path_helper = ستُحفظ كلّ مستودعات جِت البعيدة ف general_title = الإعدادات العامة lfs_path_helper = الملفات التي تم تعقبها بواسطة Git LFS ستُخزن في هذا الدليل. اتركه فارغًا لتعطيله. err_empty_db_path = طريق قاعدة بيانات SQLite3 لا يمكن أن يكون فارغا. -lfs_path = مسار جذر Git LFS -app_name_helper = أدخل اسم المثيل هنا. سيظهر هذا الاسم في كل الصفحات. +lfs_path = مسار جذر جِت LFS +app_name_helper = يمكنك إدخال اسم شركتك هنا. err_admin_name_is_reserved = اسم مستخدم المدير غير صالح، هذا الأسم محجوز -app_name = عنوان المثيل +app_name = عنوان الموقع log_root_path = مسار السجل log_root_path_helper = ستُكتب ملفات السجل في هذا الدليل. smtp_addr = مضيف SMTP @@ -188,7 +190,7 @@ smtp_port = منفذ SMTP mailer_password = كلمة مرور SMTP app_url_helper = العنوان الأساسي لاستنساخ عناوين URL HTTP(S) وإشعارات البريد الإلكتروني. mailer_user = اسم مستخدم SMTP -disable_gravatar.description = عطل Gravatar والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة. +disable_gravatar.description = عطل جرافاتار والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة. offline_mode.description = عطل خدمات توصيل المحتوى من الجهات الخارجية، واخدم كل المحتوى محلياً. run_user_helper = اسم مستخدم نظام التشغيل الذي يشغل فورجيو. ملاحظة: هذا المستخدم يجب أن يكون له حق الوصول إلى المسار الجذري للمستودع. domain = نطاق الخادم @@ -197,32 +199,32 @@ smtp_from = أرسل البريد الإلكتروني كـ federated_avatar_lookup = تفعيل الصور الرمزية الاتحادية optional_title = إعدادات اختيارية domain_helper = نطاق أو عنوان المضيف لخادمك. -mail_notify = فعّل التنبيهات عبر البريد الإلكتروني -app_url = الرابط الأساس +mail_notify = فعّل التنبيه عبر البريد الإلكتروني +app_url = الرابط الأساس لفورجيو smtp_from_helper = عنوان البريد الإلكتروني الذي سيستخدمه فورجيو. أدخل عنوان بريد إلكتروني عادي أو استخدم صيغة"Name" . ssh_port_helper = رقم المنفذ الذي يستمع له خادم SSH. اتركه فارغاً لتعطيله. -http_port_helper = المنفذ الذي سيستمع إليه خادم ويب Forgejo. -http_port = منفذ استماع HTTP +http_port_helper = المنفذ الذي سيستمع إليه خادم الويب لفورجيو. +http_port = منفذ استماع HTTP لفورجيو ssh_port = منفذ خادم SSH email_title = إعدادات البريد الإلكتروني offline_mode = فعل الوضع المحلي server_service_title = إعدادات الخادم وخدمات الجهات الخارجية register_confirm = الزم تأكيد البريد الإلكتروني للتسجيل -allow_only_external_registration.description = لن يتمكن المستخدمون من إنشاء حسابات جديدة إلا باستخدام خدمات خارجية مهيأة. +allow_only_external_registration.description = لا يسمح بالتسجيل إلا من خلال الخدمات الخارجية disable_registration = عطّل التسجيل الذاتي -federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام Libravatar. +federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام ليبرافاتار. openid_signup = فعّل التسجيل الذاتي عبر OpenID -disable_registration.description = سيتمكن مسؤولو المثيل فقط من إنشاء حسابات مستخدمين جديدة. يوصى بشدة بإبقاء التسجيل معطلاً إلا إذا كنت تنوي استضافة مثيل عام للجميع ومستعد للتعامل مع كميات كبيرة من الحسابات غير المرغوب بها. +disable_registration.description = عطل التسجيل الذاتي. المديرون فقط سيكونون قادرين على إنشاء حسابات جديدة للمستخدمين. openid_signin = فعّل تسجيل الدخول عبر OpenID openid_signin.description = فعّل تسجيل دخول المستخدمين عبر OpenID. enable_captcha = فعّل كابتشا التسجيل -enable_captcha.description = مطالبة المستخدمين باجتياز اختبار CAPTCHA من أجل إنشاء حسابات. +enable_captcha.description = الزم وجود كابتشا للتسجيل الذاتي للمستخدمين. openid_signup.description = فعّل التسجيل الذاتي للمستخدمين عبر OpenID. -require_sign_in_view = يتطلب تسجيل الدخول لعرض محتوى المثيل +require_sign_in_view = الزم تسجيل الدخول لعرض الصفحات require_sign_in_view.description = مكّن وصول الصفحات للمستخدمين فقط. لن يرى الزائرون سوى صفحات التسجيل والتسجيل. -admin_setting.description = إنشاء حساب إداري هو اختياري. أول مستخدم مُسجل سيصبح تلقائيا مديرًا. +admin_setting.description = إنشاء حساب إداري هو اختياري. أول مستخدم مُسجل سيصبح تلقائيا مديرا. admin_password = كلمة المرور -admin_email = البريد الإلكتروني +admin_email = عنوان البريد الإلكتروني install_btn_confirm = تثبت فورجيو secret_key_failed = لم يتم توليد مفتاح سري: %v save_config_failed = فشل في حفظ الإعداد: %s @@ -231,7 +233,7 @@ test_git_failed = يتعذر اختبار أمر جِت: %v confirm_password = أكّد كلمة المرور invalid_admin_setting = إعداد حساب المدير غير صالح: %v invalid_log_root_path = مسار السجل غير صالح: %v -default_enable_timetracking = فعّل التتبع الزمني افتراضيًا +default_enable_timetracking = فعّل تتبع الوقت مبدئيا env_config_keys_prompt = ستطبق المتغيرات البيئية التالية أيضاً على ملف الإعدادات: admin_title = إعدادات حساب المدير no_reply_address_helper = النطاق للمستخدمين بعنوان بريد إلكتروني مخفي. مثلاً، اسم المستخدم "sarah" سوف يسجل في جِت كـ"sarah@noreply.example.org" لو كان نطاق البريد الإلكتروني الخفي مدخل كـ"noreply.example.org". @@ -240,24 +242,20 @@ default_enable_timetracking.description = فعل تتبع الوقت للمست run_user_not_match = مستخدم التشغيل غير مطابق لأسم المستخدم الحالي: %s -> %s invalid_db_setting = إعدادات قاعدة البيانات غير صالحة: %v invalid_db_table = جدول قاعدة البيانات "%s" غير صالح: %v -default_keep_email_private.description = قم بتمكين إخفاء عنوان البريد الإلكتروني للمستخدمين الجدد افتراضيًا حتى لا يتم تسريب هذه المعلومات فور التسجيل. +default_keep_email_private.description = أخفِ عناوين البريد الإلكتروني للحسابات الجديدة مبدئيا. env_config_keys = إعدادات بيئية -default_allow_create_organization = اسمح بإنشاء المنظمات بشكل افتراضي +default_allow_create_organization = اسمح بإنشاء المنظمات مبدئيا invalid_app_data_path = مسار بيانات التطبيق غير صالح: %v +enable_update_checker_helper = يفحص لإيجاد اصدارات جديدة عن طريق الإتصال بسيرفرات فورجيو. invalid_repo_path = المسار الجزري للمستودع غير صالح: %v internal_token_failed = فشل توليد الرمز الداخلي: %v -no_reply_address = نطاق البريد الإلكتروني مخفي +no_reply_address = نطاقات البريد الإلكتروني المخفية default_keep_email_private = أخفِ عناوين البريد الإلكتروني مبدئيا admin_name = اسم مستخدم المدير -default_allow_create_organization.description = السماح للمستخدمين الجدد بإنشاء منتديات المجموعة بشكل افتراضي. عند تعطيل هذا الخيار، سيتعين على المسؤول منح إذن لإنشاء منتديات المجموعة للمستخدمين الجدد. +default_allow_create_organization.description = اسمح بحسابات المستخدمين الجديدة بإنشاء المنظمات مبدئيا. password_algorithm = خوارزمية تجزئة كلمة المرور invalid_password_algorithm = خوارزمية بصمة كلمة المرور غير صالحة -password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقيواها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. -app_slogan_helper = أدخل شعار المثيل الخاص بك هنا. اتركه فارغاً لتعطيله. -app_slogan = شعار المثيل -allow_only_external_registration = السماح بالتسجيل عبر الخدمات الخارجية فقط -config_location_hint = سيتم حفظ خيارات التهيئة هذه في: -smtp_from_invalid = عنوان "،بريد الإرسال كـ" غير صالح +password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقوتها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. [editor] buttons.list.ordered.tooltip = أضف قائمة مرقمة @@ -274,22 +272,10 @@ buttons.mention.tooltip = اذكر مستخدمًا أو فريقًا buttons.italic.tooltip = أضف نصًا مائلًا buttons.link.tooltip = اضف رابط buttons.disable_monospace_font = عطّل الخط الثابت العرض -buttons.unindent.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.url = Url -link_modal.description = الوصف -buttons.new_table.tooltip = إضافة جدول -link_modal.header = إضافة رابط -link_modal.paste_reminder = تلميح: باستخدام عنوان URL في حافظتك، يمكنك اللصق مباشرةً في المحرر لإنشاء رابط. [aria] navbar = شريط التنقل -footer.software = عن هذه البرمجية +footer.software = عن البرمجية footer.links = روابط footer = الذيل @@ -302,31 +288,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 = تعطيل المصادقة الثنائية سيجعل حسابك أقل أماناً. الاستمرار؟ -manage_themes = الموضوع الافتراضي -delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عن ذلك. -cancel = إلغاء -repos_none = لا تملك أية مستودع. +twofa_disable_desc = تعطيل الاستيثاق الثنائي سيجعل حسابك أقل أمانًا. أتريد الاستمرار؟ +manage_themes = اختر السمة المبدئية +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 = حُدِّثت السمة. @@ -353,17 +339,19 @@ 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 = استمر -confirm_delete_account = تأكيد الحذف +emails = عناوين البريد الإلكتروني +confirm_delete_account = أكُد الحذف change_password_success = حُدِّثت كلمة مرورك. سجّل الدخول بكلمة مرورك الجديدة من الآن فصاعدا. email_deletion_desc = سيُزال عنوان البريد هذا مع كل المعلومات المرتطبة به من حسابك. لكن ستبقى إيداعات Git المودعة به بلا تغيير. أتريد الاستمرار؟ or_enter_secret = أو أدخل السر: %s applications = التطبيقات +access_token_deletion_cancel_action = ألغِ location = الموقع الجغرافي password = كلمة المرور comment_type_group_title = العنوان @@ -371,17 +359,20 @@ location_placeholder = شارك مكانك التقريبي مع الآخرين appearance = المظهر repos = المستودعات change_username = تم تحديث اسم مستخدمك. +social = الحسابات الاجتماعية twofa = الاستيثاق الثنائي 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 = تم تحديث ملفك الشخصي. @@ -399,7 +390,7 @@ account = الحساب uploaded_avatar_is_too_big = حجم الملف المرفوع (%d كي‌ب) يتخطى الحجم الأقصى (%d كي‌ب). biography_placeholder = أخبرنا شيئا عن نفسك! (يمكنك استخدام ماركداون) comment_type_group_reference = الإشارات -orgs = المنظمات +orgs = إدارة المنظمات update_profile = حدِّث الملف الشخصي profile = الملف الشخصي comment_type_group_dependency = الاعتماديات @@ -409,7 +400,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 = أنشئ تطبيقا @@ -421,15 +412,16 @@ 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 +manage_openid = إدارة عناوين OpenID webauthn = استيثاق ثنائي (مفاتيح الأمان) comment_type_group_deadline = الموعد النهائي add_key = أضف مفتاح @@ -438,6 +430,7 @@ keep_activity_private = اخف النشاط من صفحة الملف الشخص profile_desc = تحكم في كيفية ظهور ملفك الشخصي للمستخدمين الآخرين. سيتم استخدام عنوان بريدك الإلكتروني الأساسي للإشعارات واستعادة كلمة المرور وعمليات Git المعتمدة على الويب. can_not_add_email_activations_pending = هناك تفعيل قيد الانتظار، حاول مجدداً خلال بضع دقائق إن أردت أضافه بريد إلكتروني جديد. gpg_key_id_used = هناك مفتاح GPG عام بنفس المعرف موجود بالفعل. +add_new_gpg_key = أضف مفتاح GPG manage_gpg_keys = إدارة مفاتيح GPG password_username_disabled = لا يمكن للمستخدمين غير المحليين أن يغيروا اسمهم. يُرجى التواصل مع مدير الموقع لتفاصيل أكثر. comment_type_group_issue_ref = مرجع المسألة @@ -445,16 +438,17 @@ gpg_desc = ترتبط مفاتيح GPG العامة هذه بحسابك. حاف manage_ssh_keys = إدارة مفاتيح SSH openid_desc = يتيح لك OpenID بتفويض الاستيثاق إلى مزوّد خارجي. key_content_ssh_placeholder = يبدأ بـ'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', أو 'sk-ssh-ed25519@openssh.com' +add_new_key = أضف مفتاح SSH hidden_comment_types_description = أنواع التعليق المُختارة هنا لن تظهر داخل صفحات المسائل. أختيار "تصنيف" مثلاً يمسح كل تعليقات "<مستخدم> أضاف/مسح <تصنيف>". 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 = لقد منحت حق الوصول إلى حسابك الشخصي في Forgejo لهذه التطبيقات الخارجية. يرجى إلغاء الوصول للتطبيقات التي لم تعد قيد الاستخدام. +authorized_oauth2_applications_description = لقد منحتَ إمكانية الوصول إلى حسابك الشخصي على فورجيو لهذه التطبيقات من تطبيقات خارجية. الرجاء إلغاء وصول التطبيقات التي لم تعد بحاجة إليها. 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 = تحقق @@ -464,13 +458,14 @@ last_used = استعمل آخر مرة في gpg_token_signature = توقيع GPG مصفح add_openid = اضف رابط OpenID add_gpg_key_success = تم إضافة مفتاح GPG "%s". +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 = اسم المستخدم القديم سوف يعاد توجيهه حتى يطالب به شخص آخر. @@ -478,9 +473,10 @@ 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 key_id = معرف المفتاح gpg_key_deletion_success = تم إزالة مفتاح GPG. verify_gpg_key_success = تم التحقق من مفتاح GPG "%s". @@ -491,7 +487,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 = المحتوى @@ -505,120 +501,7 @@ remove_oauth2_application_success = أُزيل التطبيق. update_oauth2_application_success = لقد حدّثت بنجاح تطبيق OAuth2. oauth2_redirect_uris = روابط إعادة التوجيه. نرجو وضع كل رابط في سطر وحده. remove_account_link = أزل الحساب المربوط -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 = الموسوعة +remove_account_link_success = أُزيل الحساب المربوط. [org] follow_blocked_user = لا يمكنك إتباع هذه المنظمة لأن هذه المنظمة حظرتك. @@ -709,8 +592,10 @@ settings.add_collaborator_blocked_our = لا يمكن إضافة المشترك commits.browse_further = تصفح أكثر settings.ignore_stale_approvals = تجاهل الطلبات الراكدة rss.must_be_on_branch = يجب أن تكون على فرع لتحصل على موجز RSS. +clone_in_vscodium = إستنسخ في VSCodium admin.enabled_flags = العلامات المفعلة لهذا المستودع: settings.new_owner_blocked_doer = المالك الجديد حظرك. +issues.comment.blocked_by_user = لا يمكنك أن ترسل تعليقاً على هذه المسألة لأنك محظور من قبل مالك المستودع أو مرسل المسألة. pulls.nothing_to_compare_have_tag = الفرع/الوسم المختارين متساويين. admin.update_flags = تحديث العلامات editor.invalid_commit_mail = البريد غير صالح لصنع إيداع. @@ -720,23 +605,24 @@ settings.add_collaborator_blocked_them = لا يمكن أن إضافة المش issues.blocked_by_user = لا يمكنك أن ترسل مسألة في هذا المستودع لأنك محظور من قبل مالك المستودع. mirror_sync = متزامن settings.archive.mirrors_unavailable = المرايا ليست متاحة إذا تم أرشفة المستودع. +migrate.forgejo.description = ترحيل المعلومات من كودبيرج أو خوادم فورجيو الأخرى. pulls.blocked_by_user = لا يمكنك أن ترسل طلب سحب في هذا المستودع لأنك محظور من قبل مالك المستودع. migrate.migrating_milestones = ترحيل الأهداف migrate_items_milestones = أهداف repo_size = حجم المستودع -object_format = تنسيق الكائنات +object_format = صيغة الكائنات use_template = استخدم هذا القالب migrate_items_merge_requests = طلبات الدمج repo_name = اسم المستودع template = القالب projects.modify = عدّل المشروع -tree_path_not_found.commit = المسار %[1]s غير موجود في الإيداع %[2]s +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 = احذف المشروع @@ -746,23 +632,23 @@ 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 = اختر ملف ترخيص. -tree_path_not_found.tag = المسار %[1]s غير موجود في الوسم %[2]s +tree_path_not_found_tag = المسار %[1]s غير موجود في الوسم %[2]s object_format_helper = صيغة كائنات المستودع. لا يمكن تغييرها بعد ذلك. SHA1 هي الأكثر توافقية. forks = الاشتقاقات migrate_items_pullrequests = طلبات السحب fork_to_different_account = اشتق إلى حساب مختلف -tree_path_not_found.branch = المسار %[1]s غير موجود في الفرع %[2]s +tree_path_not_found_branch = المسار %[1]s غير موجود في الفرع %[2]s projects.edit_success = حدِّث المشروع "%s". projects.create_success = تم إنشاء المشروع "%s". find_file.no_matching = لا يوجد ملف مطابق @@ -774,7 +660,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 = تصنيفات المسائل @@ -783,6 +669,7 @@ issues.remove_project_at = `أزال هذه المسألة من المشروع < issues.unlock.notice_1 = - يستطيع أي مستخدم عندئذٍ أن يعلّق على هذه المسألة من جديد. issues.remove_assignee_at = `ألغى تكليفه %s %s` branch.warning_rename_default_branch = إنك تغيّر اسم الفرع المبدئي. +trust_model_helper_default = المبدئي: اختر نموذج الثقة المبدئي لهذا الموقع tag.create_tag = أنشئ الوسم %s release.title_empty = لا يمكن ترك العنوان فارغا. tag.create_tag_operation = أنشئ وسمًا @@ -812,7 +699,7 @@ issues.filter_milestone_all = كل الأهداف issues.unlock.notice_2 = - يمكنك دوما إقفال هذه المسألة من جديد في المستقبل. issues.num_participants_few = %d متحاور release.title = عنوان الإصدار -issues.closed_at = `أغلق هذه المسألة %s` +issues.closed_at = `أغلق هذه المسألة %[2]s` issues.lock.title = إقفال التحاور في هذه المسألة. issues.new.no_label = بلا تصنيف issues.filter_sort.mostforks = الأعلى اشتقاقا @@ -869,9 +756,10 @@ milestones.deletion_desc = حذف هدف يحذفه من كل المسائل ا issues.desc = نظّم إبلاغات العلل، والمهام، والأهداف. issues.choose.ignore_invalid_templates = أُهمِلت القوالب التالفة branch.renamed = غُيّر اسم الفرع %s إلى %s. -delete_preexisting = حذف الملفات الموجودة مسبقاً +delete_preexisting = احذف الملفات الموجودة سابقا branch.included_desc = هذا الفرع جزء من الفرع المبدئي -issues.reopened_at = `أعاد فتح هذه المسألة %s` +trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع +issues.reopened_at = `أعاد فتح هذه المسألة %[2]s` issues.action_milestone = هدف issues.new.assignees = المكلَّفون release.tag_name_protected = اسم الوسم محمي. @@ -879,6 +767,7 @@ milestones.filter_sort.most_complete = الأكثر اكتمالا issues.filter_labels = تصفية التصنيفات issues.label.filter_sort.alphabetically = أبجديا settings.confirm_wiki_delete = احذف بيانات الموسوعة +branch.confirm_rename_branch = غيّر اسم الفرع issues.action_assignee_no_select = بلا مكلف release.publish = انشر الإصدار issues.lock_duplicate = لا يمكن إقفال مسألة مقفلة. @@ -890,6 +779,7 @@ milestones.filter_sort.least_complete = الأقل اكتمالا branch.create_branch = أنشئ الفرع %s issues.remove_self_assignment = `ألغى تكليف نفسه %s` issues.label_edit = عدّل +release.download_count = التنزيلات: %s wiki.welcome = أهلا بك في الموسوعة. find_file.go_to_file = انتقل إلى ملف issues.cancel = ألغِ @@ -940,7 +830,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 = الأهداف التامة @@ -948,6 +838,7 @@ issues.new.milestone = الأهداف branch.confirm_create_branch = أنشئ فرعًا issues.close = مسألة تامة issues.label_modify = عدّل التصنيف +issues.author_helper = هذا المستخدم هو منشئ المسألة. issues.delete_branch_at = `حُذِف الفرع %s %s` issues.new.open_projects = المشروعات الحالية issues.new_label = تصنيف جديد @@ -964,6 +855,7 @@ issues.filter_assginee_no_select = كل المكلفين issues.new.no_projects = بلا مشروع issues.new.labels = التصنيفات issues.role.member = عضو +trust_model_helper_collaborator = مشترك: ثق بتوقيعات المشترِكين issues.label_archived_filter = أظهر التصنيفات المؤرشفة issues.filter_milestone = هدف issues.remove_label = أزال التصنيف %s %s @@ -980,14 +872,16 @@ 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 = لا يمكن فك قفل مسألة غير مقفلة. milestones.filter_sort.most_issues = الأكثر مسائل issues.add_project_at = `أضاف هذه المسألة إلى المشروع %s %s` issues.context.reference_issue = أشر إليها في مسألة جديدة issues.context.quote_reply = اقتبس في رد issues.role.owner_helper = هذا المستخدم هو مالك المستودع. +trust_model_helper_committer = مودع: ثق بالتوقيعات الموافقة للمودعين issues.filter_project_none = بلا مشروع issues.lock_no_reason = "أقفلها وقيّد التحاور للمشتركين في المستودع %s" issue_labels_helper = اختر باقة تصنيفات للمسائل. @@ -1012,7 +906,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 = "عدّل" @@ -1024,7 +918,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 = تتبع إصدارات المشروع وتنزيلاته. @@ -1037,15 +931,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 = خطاطيف الويب @@ -1069,12 +963,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 = لقد رفض الخادوم التعديل بغير رسالة. نرجو مراجعة خطاطيف جت. @@ -1093,7 +987,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 = النطاق @@ -1122,11 +1016,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 = أزل خطاف الويب @@ -1147,6 +1041,7 @@ visibility_helper_forced = يفرض مدير موقعك أن تكون المست wiki.page_content = محتوى الصفحة settings.collaborator_deletion_desc = إزالة مشترك ستبطل وصوله إلى هذا المستودع. أتريد الاستمرار؟ settings.remove_collaborator_success = أُزيل المشترك. +settings.search_user_placeholder = ابحث عن مستخدم… settings.mirror_settings.pushed_repository = مستودع مدفوع settings.delete_collaborator = أزل settings.add_collaborator_success = أُضيف المشترك. @@ -1271,7 +1166,7 @@ pulls.status_checking = في انتظار بعض الفحوص pulls.status_checks_failure = بعض الفحوص فشلت pulls.status_checks_success = جميع الفحوص ناجحة pulls.status_checks_warning = بعض الفحوص تعطي تحذيرات -pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %s` +pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %[2]s` pulls.cmd_instruction_hint = `أظهر شرح استخدام سطر الأوامر.` pulls.cmd_instruction_checkout_title = اسحب pulls.cmd_instruction_checkout_desc = من مستودع مشروعك، اسحب (check out) فرعا جديدا واختبر التغييرات. @@ -1295,6 +1190,8 @@ milestones.close = تام milestones.new_subheader = تساعدك الأهداف في تنظيم المسائل وتتبع سيرها. milestones.completeness = %d%% مكتمل milestones.create = أنشئ هدفا +search.match.tooltip = لا تأت إلا بالنتائج التي تطابق كلمة البحث تماما +search.results = نتائج البحث عن "%s" في %s settings.collaboration = المشتركون settings.collaboration.admin = مدير settings.collaboration.write = تحرير @@ -1343,6 +1240,7 @@ settings.transfer_perform = أتم نقل الملكية settings.transfer_succeed = تم نقل ملكية المستودع. pulls.auto_merge_newly_scheduled_comment = `أمر بجدولة هذا الطلب للدمج آليا عند نجاح جميع الفحوص %[1]s pulls.auto_merge_canceled_schedule_comment = `ألغى الدمج الآلي لهذا الطلب عند نجاح جميع الفحوص %[1]s +ext_issues.desc = رابط متتبع المسائل الخارجي. projects.edit_subheader = تساعد المشروعات في تنظيم المسائل وتتبع سيرها. issues.tracker = متتبع الوقت issues.start_tracking_short = شغّل المؤقت @@ -1359,8 +1257,8 @@ pulls.status_checks_details = تفاصيل pulls.status_checks_hide_all = أخفِ كل الفحوص pulls.status_checks_show_all = أظهر كل الفحوص pulls.close = أغلق طلب الدمج -pulls.closed_at = `أغلق طلب الدمج %s` -pulls.reopened_at = `أعاد فتح طلب الدمج %s` +pulls.closed_at = `أغلق طلب الدمج %[2]s` +pulls.reopened_at = `أعاد فتح طلب الدمج %[2]s` milestones.title = العنوان milestones.desc = الوصف milestones.edit = عدّل الهدف @@ -1374,6 +1272,10 @@ settings.trust_model.collaborator.long = مشترك: ثق بتوقيعات ال wiki.file_revision = مراجعة الصفحة wiki.wiki_page_revisions = مراجعات صفحة الموسوعة wiki.back_to_wiki = عد إلى صفحة الموسوعة +search.type.tooltip = نوع البحث +search.fuzzy = تقريبي +search.fuzzy.tooltip = ائت بالنتائج القريبة من كلمة البحث +search.match = مطابق settings = الإعدادات settings.mirror_settings.push_mirror.remote_url = رابط مستودع جت البعيد settings.releases_desc = فعّل الإصدارات في المستودع @@ -1383,10 +1285,12 @@ settings.trust_model = نموذج الثقة في التوقيعات settings.trust_model.committer = مودِع settings.trust_model.collaboratorcommitter = مشترك+مودع settings.trust_model.collaboratorcommitter.long = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع +tagged_this = وسم هذا branches = الفروع tags = الوسوم issues = المسائل pulls = طلبات الدمج +project_board = المشروعات packages = الحزم actions = الإجراءات released_this = أصدر هذا @@ -1398,15 +1302,19 @@ issues.closed_by_fake = من %[2]s أُغلقت %[1]s issues.num_comments_1 = %d تعليق issues.num_comments = %d تعليقا issues.commented_at = `علّق %s` -issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %s` -issues.ref_issue_from = `أشار إلى هذه المسألة %[3]s %[1]s` -issues.ref_pull_from = `أشار إلى هذا الطلب %[3]s %[1]s` -issues.ref_closing_from = `أشار إلى طلب دمج %[3]s سيغلق هذه المسألة %[1]s` -issues.ref_reopening_from = `أشار إلى طلب دمج %[3]s سيعيد فتح هذه المسألة %[1]s` +issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %[2]s` +issues.ref_issue_from = `أشار إلى هذه المسألة %[4]s %[2]s` +issues.ref_pull_from = `أشار إلى هذا الطلب %[4]s %[2]s` +issues.ref_closing_from = `أشار إلى طلب دمج %[4]s سيغلق هذه المسألة %[2]s` +issues.ref_reopening_from = `أشار إلى طلب دمج %[4]s سيعيد فتح هذه المسألة %[2]s` +issues.ref_closed_from = `أغلق هذه المسألة %[4]s %[2]s` +issues.ref_reopened_from = `أعاد فتح هذه المسألة %[4]s %[2]s` issues.reference_issue.body = المحتوى +issues.reference_link = للإشارة: %s settings.actions_desc = فعّل الإجراءات في المستودع pulls.push_rejected = تعذر الدمج: تم رفض الدفع. راجع خطاطيف جت لهذا المستودع. contributors.contribution_type.additions = الإضافات +search = بحث pulls.require_signed_wont_sign = يطلب الفرع إيداعات موقّعة، لكن لن يكون هذا الدمج موقّعًا pulls.update_branch = تحديث الفرع بالدمج pulls.update_branch_rebase = تحديث الفرع بإعادة التأسيس @@ -1419,223 +1327,30 @@ 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.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 = لا يوجد وصف -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 التي يستند إليها هذا المستودع. اتصل بمسؤول هذا المثيل أو احذف هذا المستودع. -watch = شاهد -migrate.migrating_git = ترحيل بيانات Git -migrate.cancel_migrating_confirm = تريد إلغاء عملية الترحيل هذه؟ -migrate.permission_denied_blocked = لا يمكنك الاستيراد من مضيفين غير مسموح بهم، يُرجى الطلب من المسؤول التحقق من إعدادات ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. -migrate.migrating = الترحيل من %s … -migrate.migrating_failed.error = أخفق الترحيل: %s -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 = من فضلك اضغط هنا لإدارة هذا المستخدم من لوحة الإدارة. admin.new_user.subject = مستخدم جديد: %s سجل حالاً admin.new_user.user_info = معلومات المستخدم activate_account.text_1 = أهلا يا %[1]s، شكرا لك للتسجيل في %[2]s! -register_notify = أهلا بك في %s +register_notify = أهلا بك في فورجيو activate_account = نرجو تفعيل حسابك +activate_account.title = يا %s، نرجو منك تفعيل حسابك issue.x_mentioned_you = ذكرك @%s: +register_notify.title = مرحبا بك يا %[1]s في %[2]s issue.in_tree_path = في %s: -register_notify.text_3 = إذا قام شخص آخر بإنشاء هذا الحساب لك، فستحتاج أولاً إلى ضبط كلمة مرورك. -register_notify.text_2 = يمكنك الآن تسجيل الدخول إلى حسابك باسم المستخدم: %s +register_notify.text_3 = إذا أُنشئ هذا الحساب لك، فنرجو أولا ضبط كلمة مرورك. +register_notify.text_2 = يمكنك الآن تسجيل الدخول باسم المستخدم: %s. +reset_password.title = يا %s، لقد طلبت استعادة حسابك repo.transfer.to_you = أنت reset_password = استعادة حسابك -repo.collaborator.added.subject = %s أضافك إلى %s كمشترِك +repo.collaborator.added.subject = %s أضافك إلى %s team_invite.subject = لقد دعاك %[1]s للانضمام إلى منظمة %[2]s team_invite.text_2 = نرجو الضغط على الرابط التالي للانضمام إلى الفريق: team_invite.text_1 = لقد دعاك %[1]s للانضمام إلى فريق %[2]s في منظمة %[3]s. -repo.collaborator.added.text = لقد جُعلت مشترِكا إلى مستودع: +repo.collaborator.added.text = لقد جُعلت مشترِكا في مستودع: reply = أو رد على هذا البريد الإلكتروني مباشرة -link_not_working_do_paste = هل الرابط لا يعمل؟ جرّب نسخه ولصقه في شريط عنوان المتصفح لديك. +link_not_working_do_paste = لا تعمل؟ حاول أن النسخ واللصق إلى متصفحك. register_success = نجح التسجيل view_it_on = اعرضه على %s activate_account.text_2 = يرجى النقر على الرابط التالي لتفعيل حسابك في %s: @@ -1646,6 +1361,7 @@ activate_email.text = يرجى الضغط على الرابط الآتي لتأ reset_password.text = يرجى الضغط على الرابط الآتي لاستعادة الحساب في خلال %s: release.downloads = التنزيلات: activate_email = أكد عنوان بريدك الإلكتروني +activate_email.title = %s، يرجى تأكيد عنوان بريدك الإلكتروني release.note = ملاحظة: issue.action.close = @%[1]s أغلق #%[2]d. issue.action.merge = @%[1]s دمج #%[2]d مع %[3]s. @@ -1664,32 +1380,19 @@ issue.action.new = @%[1]s انشأ #%[2]d. issue_assigned.issue = @%[1]s عيّنك إلى مسألة %[2]s في مستودع %[3]s. issue.action.push_n = @%[1]s دفع %[3]d إيداعات إلى %[2]s release.new.subject = أُصدر %s في %s -repo.transfer.subject_to_you = %s يريد نقل ملكية مستودع "%s" إليك -repo.transfer.subject_to = %s يريد نقل ملكية مستودع "%s" إلى %s +repo.transfer.subject_to_you = %s يود نقل ملكية "%s" إليك +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 = تم تغيير كلمة مرورك -totp_disabled.subject = تم تعطيل TOTP -totp_disabled.text_1 = تم تعطيل كلمة المرور لمرة واحدة المستندة إلى الوقت (TOTP) على حسابك للتو. -totp_enrolled.text_1.has_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يمكنك استخدام TOTP كطريقة للمصادقة الثنائية ، أو استخدام أي من مفاتيح الأمان الخاصة بك. -totp_enrolled.subject = لقد قمت بتشيط TOTP كطريقة 2FA -removed_security_key.subject = تمت إزالة مفتاح الأمان -removed_security_key.text_1 = تم إزالة مفتاح الأمان ”%[1] s“ للتو من حسابك. -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.text_1.no_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يجب عليك استخدام TOTP كطريقة للمصادقة الثنائية. [error] not_found = تعذر العثور على الهدف. report_message = إن كنت متيقِّنًا أن هذه علة في فورجيو، رجاءً ابحث في كودبيرج أو افتح مسأله جديدة إذا لزم الأمر. network_error = خطأ في الشبكة +invalid_csrf = طلب سيئ: رمز CSRF غير صالح occurred = حدث خطأ +missing_csrf = طلب سيئ: لا يوجد رمز CSRF server_internal = خطأ داخلي في الخادم [startpage] @@ -1717,7 +1420,7 @@ joined_on = انضم في %s user_bio = السيرة الذاتية repositories = المستودعات activity = النشاط العام -projects = المشاريع +projects = مشاريع unfollow = إلغِ المتابعة settings = إعدادات المستخدم following_few = %d يتابع @@ -1725,25 +1428,15 @@ follow = تابع followers_few = %d متابعين form.name_reserved = اسم المستخدم "%s" محجوز. email_visibility.limited = عنوان بريدك الإلكتروني ظاهر لكل المستخدمين المُستَوثَقين -code = الكود +code = البرمجية overview = نظرة عامة watched = المستودعات المشاهدة disabled_public_activity = هذا المستخدم عطّل الظهور العام للنشاط. show_on_map = اعرض هذا المكان على الخريطة +email_visibility.private = عنوان بريدك الإلكتروني ظاهر لك وللمديرين فقط starred = المستودعات المميّزة بنجمة form.name_chars_not_allowed = اسم المستخدم "%s" يحتوي على رموز غير صالحة. form.name_pattern_not_allowed = النمط "s%" غير مسموح به في إسم المستخدم. -followers.title.one = متابِع -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_profile = نشاطك مرئي لك ولسُعاة المثيل فقط لأن ملفك الشخصي خاص. تعديل الإعدادات. [auth] change_unconfirmed_email_error = تعذر تغيير البريد الإلكتروني: %v @@ -1754,21 +1447,26 @@ disable_register_mail = تم تعطيل تأكيد البريد للتسجيل. sign_up_successful = أٌنشئ الحساب بنجاح. مرحباً! forgot_password = نسيت كلمة المرور؟ allow_password_change = ألزم المستخدم بتغيير كلمة المرور (يُنصح به) +sign_up_now = تحتاج إلى حساب؟ سجل الآن. forgot_password_title = نسيت كلمة المرور account_activated = تم تفعيل الحساب +social_register_helper_msg = هل لديك حساب بالفعل؟ اربطه الآن! create_new_account = سجل حساب disable_register_prompt = التسجيل مغلق حالياً. يرجى الاتصال بالمدير. active_your_account = فعّل حسابك +register_helper_msg = هل لديك حساب بالفعل؟ سجل الدخول! manual_activation_only = تواصل مع مدير موقعك لإكمال التفعيل. must_change_password = حدّث كلمة المرور الخاصة بك -send_reset_mail = أرسل بريد الاستعادة +send_reset_mail = أرسل رسالة استعادة حساب resend_mail = اضغط هنا لإعادة إرسالة رسالة تفعيل حسابك has_unconfirmed_mail = أهلا يا %s، لديك عنوان بريد إلكتروني غير مؤكَّد (%s). إن لم تستلم رسالة تأكيد أو تريد إرسال واحدة جديدة، فنرجو الضغط على الزر الذي بالأسفل. -reset_password = استعادة الحساب +email_not_associate = عنوان البريد هذا غير مرتبط بأي حساب. +reset_password = استعادة حساب oauth_signin_tab = أربط بحساب موجود invalid_password = كلمة المرور الخاصة بك لا تطابق كلمة المرور التي استخدمت لتسجيل الحساب. oauth_signin_title = سجّل الدخول لتأذن للحساب المربوط reset_password_helper = إعادة الحساب +tab_openid = تسجيل دخول بـOpenID openid_connect_submit = اتصل oauth_signup_tab = سجل حساب جديد oauth.signin.error.temporarily_unavailable = فشل طلب الإذن لأن خادم التوثيق غير متاح مؤقتا. حاول مرة أخرى لاحقاً. @@ -1784,13 +1482,13 @@ reset_password_wrong_user = أنت مُسجل كـ %s، لكن رابط أعاد openid_connect_title = اتصل بحساب موجود confirmation_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى %s. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية التسجيل. إذا كان عنوان البريد خاطئ، يمكنك تسجيل الدخول وطلب بريد تأكيد جديد يُرسل إلى عنوان آخر. scratch_code = رمز الخدش -invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهت صلاحيته. اضغط هنا للإعادة. +invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهى اضغط هنا للإعادة. openid_register_title = أنشئ حسابًا جديدًا verify = تحقق twofa_scratch_used = لقد استخدمت رمز الخدش الخاص بك. لقد تم إعادة توجيهك إلى إعدادات المصادقة الثنائية حتى يمكنك إزالة تسجيل جهازك أو توليد رمز خدش جديد. oauth_signup_submit = أكمل الحساب oauth.signin.error = كان هناك خطأ في تجهيز طلب الإذن إذا استمر هذا الخطأ، يرجى الاتصال بالمدير. -invalid_code = رمز تأكيدك غير صحيح أو انتهت صلاحيته. +invalid_code = رمز تأكيدك غير صحيح أو انتهى. oauth_signup_title = أكمل حساب جديد resent_limit_prompt = لقد طلبت بالفعل بريداً إلكترونياً للتفعيل مؤخراً من فضلك انتظر 3 دقائق وحاول مرة أخرى. reset_password_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى %s. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية استعادة الحساب. @@ -1799,26 +1497,21 @@ authorize_application_description = إذا منحت حق الوصول، فسيك authorize_application_created_by = أُنشئ هذا التطبيق بواسطة %s. email_domain_blacklisted = لا يمكنك التسجيل باستخدام عنوان بريدك الإلكتروني. authorize_title = هل تريد أن تأذن لـ "%s" بالوصول إلى حسابك؟ -prohibit_login = هذا الحساب معلق -prohibit_login_desc = تم تعليق حسابك من التفاعل مع هذه النسخة. تواصل مع مسؤول النسخة لاستعادة الوصول. +prohibit_login = تسجيل الدخول ممنوع +prohibit_login_desc = حسابك ممنوع من تسجيل الدخول، يرجى التواصل مع مدير الموقع. disable_forgot_password_mail_admin = استرداد الحساب متاح فقط عند إعداد البريد الإلكتروني. يُرجى إعداد البريد الإلكتروني لتفعيل استرداد الحساب. password_pwned_err = تعذر الوصول إلى HaveIBeenPwned password_pwned = الكلمة المرور المُختارة هي على قائمة كلمات مرور مسروقة تم كشفها في تسريبات عامة للبيانات. يُرجى المحاولة مرة أخرى بكلمة مرور أخرى، وضع في اعتبارك تغيير تلك الكلمة في الأماكن الأخرى. authorization_failed = فشل الإذن authorize_redirect_notice = ستتم إعادة توجيهك إلى %s إذا أذنت للتطبيق. authorize_application = ائذن للتطبيق +sspi_auth_failed = فشلت عملية استيثاق SSPI openid_connect_desc = مسار الـOpenID المختار مجهول. اربطه مع حساب جديد هنا. openid_signin_desc = أدخل مسار الـOpenID الخاص بك. مثلاً: alice.openid.example.org أو https://openid.example.org/alice. openid_register_desc = مسار الـOpenID المختار مجهول. اربطه مع حساب جديد هنا. remember_me = تذكر هذا الجهاز +remember_me.compromised = رمز الاحتفاظ بتسجيل الدخول لم يعد صالحا، مما قد يعني اختراق الحساب. نرجو مراجعة حسابك لرؤية أي نشاط غير مألوف. authorization_failed_desc = فشل التفويض لأننا اكتشفنا طلبًا غير صالح. يرجى الاتصال بمشرف التطبيق الذي حاولت ترخيصه. -sign_in_openid = المتابعة باستخدام OpenID -hint_login = لديك حساب بالفعل؟ سجّل الدخول الآن! -hint_register = يلزمك حساب ؟ سجِّل الآن. -sign_up_button = سجِّل الآن. -back_to_sign_in = العودة إلى تسجيل الدخول -use_onetime_code = استخدم رمزًا لمرة واحدة -unauthorized_credentials = بيانات الاعتماد غير صحيحة أو انتهت صلاحيتها. أعد محاولة تنفيذ الأمر أو راجع %s لمزيد من المعلومات [packages] rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة. @@ -1862,9 +1555,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 = المساهمات [admin] self_check.database_fix_mysql = لمستخدمين ميسكول/ماريا دي بي، يمكنك استخدام أمر "forgejo doctor convert" لإصلاح مشاكل التجمّع، أو يمكنك أيضاً إصلاح المشكلة عن طريق تعديل السيكول يدوياً. @@ -1936,6 +1626,7 @@ packages.repository = المستودع orgs.teams = الفِرق packages.size = الحجم users.max_repo_creation = العدد الأقصى للمستودعات +repos.forks = الاشتقاقات orgs.new_orga = منظمة جديدة users.allow_import_local = يستطيع استيراد مستودعات محلية repos.name = الاسم @@ -2015,7 +1706,7 @@ enterred_invalid_org_name = اسم المنظمة التي أدخلته خطأ. lang_select_error = اختر لغة من القائمة. alpha_dash_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_").` alpha_dash_dot_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_") والنقطة (".").` -repo_name_been_taken = اسم المستودع مستخدم بالفعل. +repo_name_been_taken = اسم المستودع مستعمل بالفعل. Email = البريد الإلكتروني auth_failed = فشل الاستيثاق: %v email_error = ` ليس عنوان بريد إلكتروني صالح.` @@ -2028,30 +1719,35 @@ email_invalid = عنوان البريد غير صالح. CommitSummary = خلاصة الإيداع team_not_exist = الفريق غير موجود. TreeName = مسار الملف +SSPIDefaultLanguage = اللغة المبدئية password_complexity = كلمة المرور ليست بالتعقيد المطلوب: password_not_match = لا تتطابق كلمتا المرور. still_has_org = "حسابك عضو في منظمة أو أكثر؛ غادرهم أولا." repository_files_already_exist.adopt_or_delete = الملفات موجودة بالفعل لهذا المستودع. إما اعتمادها أو حذفها. repository_files_already_exist.delete = الملفات موجودة بالفعل لهذا المستودع. يجب عليك حذفها. repository_files_already_exist.adopt = الملفات موجودة بالفعل لهذا المستودع ويمكن اعتمادها فقط. -repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. اتصل بمدير النظام. +repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. تواصل مع مدير النظام. TeamName = اسم الفريق -username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسمائهم. +username_has_not_been_changed = لم يتم تغيير اسم المستخدم +username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسماؤهم. captcha_incorrect = الكابتشا خاطئة. AdminEmail = عنوان البريد الإلكتروني للمدير team_no_units_error = اسمح بالوصول إلى قسم واحد على الأقل في المستودعات. must_use_public_key = المفتاح الذي قدمته هو مفتاح خاص. من فضلك لا ترفع مفتاحك الخاص في أي مكان. استخدم مفتاحك العام بدلاً من ذلك. -unable_verify_ssh_key = تعذر التحقق من مفتاح الـSSH، تأكد منه مجدداً. +unable_verify_ssh_key = "تعذر التحقق من مفتاح الـSSH، تأكد منه مجدداً." invalid_gpg_key = فشل تحقق مفتاح الـGPG: %s still_own_packages = "حسابك يملك حزمة واحدة أو اكثر، احذفهم أولاً." -still_own_repo = حسابك يملك مستودع واحد أو اكثر، احذفهم أو حولهم أولاً. +still_own_repo = "حسابك يملك مستودع واحد أو اكثر، احذفهم أو حولهم أولاً." +SSHTitle = اسم مفتاح SSH 2fa_auth_required = الزيارة الخارجية الزمت استيثاق ثنائي. target_branch_not_exist = الفرع المستهدف ليس موجود. PayloadUrl = رابط الحمولة org_still_own_packages = "المنظمة تزال تملك حزمة واحدة أو اكثر، احذفهم أولاً." last_org_owner = لا يمكنك إزالة آخر مستخدم من فريق "المالكين". يجب أن يكون هناك على الأقل مالك واحد للمنظمة. +HttpsUrl = رابط HTTPS invalid_ssh_key = فشل تحقق مفتاح الـSSH: %s AuthName = اسم الأذن +SSPISeparatorReplacement = الفاصلة openid_been_used = عنوان الـOpenID "%s" مُستخدم بالفعل. git_ref_name_error = `يجب أن يكون اسمًا مرجعيًا جيدًا لـ Git.` include_error = ` يجب أن يحتوي على سلسلة فرعية "%s".` @@ -2060,56 +1756,52 @@ glob_pattern_error = `النمط الشامل غير صالح: %s.` CommitChoice = إختيار الإداع regex_pattern_error = ` نمط التعبير النمطي غير صالح: %s.` username_error = ` يُمكنه أن يحتوي على حروف إنجليزية وأرقام وشرطة ("-") وشرطة سفلية ("_") و نقطة (".") فقط. ويمكنه ان يبدأ وينتهي بحرف او برقم.` -Biography = النبذة -Website = موقع الويب -To = اسم الفرع -AccessToken = رمز الوصول -repository_force_private = وضع الخاص الإجباري مفعّل: لا يمكن تحويل المستودعات الخاصة إلى عامة. -FullName = الاسم الكامل -Description = الوصف -Pronouns = الضمائر -username_claiming_cooldown = لا يمكن المطالبة باسم المستخدم، لأن فترة تباطؤه لم تنتهِ بعد. يمكن المطالبة به عند %[1]s. -Location = الموقع -invalid_group_team_map_error = ` التعيين غير صالح: %s ` -visit_rate_limit = تناولت الزيارة عن بُعد الحد من معدلها. -email_domain_is_not_allowed = نطاق البريد الإلكتروني للمستخدم %s يتعارض مع قائمة النطاقات المسموحة ، أو الممنوعة. يرجى التأكد من إدخال عنوان البريد الإلكتروني بشكل صحيح. -unset_password = المستخدم المسجل لم يقم بتعيين كلمة مرور. -unsupported_login_type = نوع تسجيل الدخول غير مدعوم لحذف الحساب. -invalid_ssh_principal = أصل غير صالح: %s -required_prefix = المُدخل يجب أن يبدأ مع "%s" [home] filter = تصفيات أخرى show_archived = مؤرشف -my_orgs = المنظمات +search_repos = العثور على مستودع… +my_orgs = مُنظّماتي uname_holder = اسم المستخدم أو عنوان البريد الإلكتروني my_repos = المستودعات show_both_archived_unarchived = يُعرض المؤرشف وغير المؤرشف feed_of = موجز "%s" issues.in_your_repos = في مستودعاتك -switch_dashboard_context = بدّل سياق لوحة التحكم +switch_dashboard_context = تغيير إطار لوحة التحكم show_both_private_public = يُعرض العام والخاص filter_by_team_repositories = تصفية حسب مستودعات الفريق show_only_private = يُعرض الخاص فقط show_private = خاص +password_holder = كلمة المرور +show_more_repos = إظهار المزيد من المستودعات… show_only_public = يُعرض العام فقط +collaborative_repos = المستودعات التعاونية show_only_unarchived = يُعرض غير المؤرشف فقط +my_mirrors = مراياي show_only_archived = يُعرض المؤرشف فقط view_home = عرض %s [explore] +search.match.tooltip = إشمل فقط النتائج التي تطابق البحث كلياً +search.type.tooltip = نوع البحث +code_search_results = نتائج البحث عن "%s" go_to = إذهب إلى repos = المستودعات users = المستخدمين +code_search_unavailable = البحث البرمجي غير متوفر حالياً. من فضلك راسل مدير الموقع. +search = البحث +user_no_results = لا يوجد مستخدمون متطابقون. +org_no_results = لا توجد منظمات متطابقة. code = نص برمجي +search.match = مطابق +search.fuzzy.tooltip = إشمل النتائج التي تطابق البحث تقريباً +search.fuzzy = تطابق غامض organizations = المنظمات +repo_no_results = لا توجد مستودعات مطابقة. +code_no_results = لم يتم العثور على برمجية تطابق البحث. relevant_repositories_tooltip = تم أخفاء المستودعات التي هي مشتقات وأيضاً التي ليس لها موضوع، ولا أيقونة، ولا يوجد وصف. relevant_repositories = يتم اظهار المستودعات المتعلقة فقط. أظهر النتائج غير المصفاة. code_last_indexed_at = فُهرس آخر مرة %s -stars_few = %d نجوم -forks_one = %d نسخة -forks_few = %d نُسَخ -stars_one = %d نجمة [actions] variables.none = لا توجد متغيرات بعد. @@ -2143,6 +1835,7 @@ runners.labels = التصنيفات runners.status.unspecified = مجهول runs.commit = إيداع status.success = "نجح" +runs.no_workflows.documentation =لمعرفة المزيد عن إجراءات فورجيو، برجاء رؤية التوثيق. runs.empty_commit_message = (رسالة إيداع فارغة) status.cancelled = "ملغي" runs.status_no_select = كل الحالات @@ -2280,28 +1973,21 @@ component_failed_to_load = حدث خطأ غير متوقع. [search] -org_kind = بحث في المنظمات… +org_kind = بحث في المنظمات... code_search_unavailable = البحث في الكود غير متوفر حاليًا. يرجى الاتصال بمدير الموقع. -search = البحث… +search = ابحث... type_tooltip = نوع البحث fuzzy = أجعد fuzzy_tooltip = قم بتضمين النتائج التي تتطابق أيضًا مع مصطلح البحث بشكل وثيق -repo_kind = بحث في المستودعات… -user_kind = بحث عن المستخدمين… -team_kind = بحث عن الفرق… -code_kind = بحث ضمن الكود… -project_kind = البحث ضمن المشاريع… -branch_kind = البحث ضمن الفروع… +match = تتناسب +match_tooltip = قم بتضمين النتائج التي تطابق مصطلح البحث المحدد فقط +repo_kind = بحث في المستودعات... +user_kind = بحث عن المستخدمين... +team_kind = بحث عن الفرق ... +code_kind = بحث في الكود... +project_kind = البحث ضمن المشاريع... +branch_kind = البحث ضمن الفروع... no_results = لا توجد نتائج مطابقة. -issue_kind = البحث ضمن الأعطال… -pull_kind = البحث ضمن طلبات السحب… +issue_kind = البحث ضمن الأعطال... +pull_kind = البحث ضمن طلبات السحب... keyword_search_unavailable = البحث من خلال الكلمات المفتاحية ليس متوفر حالياً. رجاءاً تواصل مع مشرف الموقع. -package_kind = البحث ضمن الحزم… -regexp_tooltip = تعامل مع عبارة البحث على أنها تعبير نمطي -commit_kind = البحث ضمن الإيداعات… -union = مطابقة عامة -runner_kind = البحث ضمن المشغِّلات… -exact = مطابق -exact_tooltip = عرض النتائج التي تطابق مصطلح البحث بالضبط فقط -regexp = RegExp -union_tooltip = عرض النتائج التي تطابق أي من الكلمات المفتاحية المفصولة بمسافات diff --git a/options/locale/locale_be.ini b/options/locale/locale_be.ini index 25aff3019c..fe04dadc3e 100644 --- a/options/locale/locale_be.ini +++ b/options/locale/locale_be.ini @@ -7,20 +7,10 @@ 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 -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 +sign_in_with_provider = Увайсці з %s \ No newline at end of file diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 720c4fa1e5..abce4f1133 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -40,6 +40,7 @@ new_mirror = Ново огледално re_type = Потвърдете паролата copy = Копиране enabled = Включено +new_org = Нова организация milestones = Етапи rss_feed = RSS емисия never = Никога @@ -53,9 +54,12 @@ add_all = Добавяне на всичко new_project_column = Нова колона add = Добавяне organization = Организация +new_migrate = Нова миграция save = Запазване sign_in_with_provider = Влизане с %s ok = Добре +manage_org = Управление на организациите +new_repo = Ново хранилище register = Регистрация mirror = Огледално username = Потребителско име @@ -70,6 +74,7 @@ issues = Задачи retry = Повторен опит remove = Премахване admin_panel = Управление на сайта +account_settings = Настройки на акаунта powered_by = Осъществено от %s pull_requests = Заявки за сливане collaborative = Съвместни @@ -136,7 +141,6 @@ webauthn_sign_in = Натиснете бутона на вашия ключ за webauthn_error = Неуспешно прочитане на вашия ключ за сигурност. webauthn_unsupported_browser = Вашият браузър в момента не поддържа WebAuthn. webauthn_error_duplicated = Ключът за сигурност не е разрешен за тази заявка. Моля, уверете се, че ключът не е вече регистриран. -tracked_time_summary = Обобщение на проследеното време въз основа на филтрите в списъка със задачи [settings] ui = Тема @@ -151,9 +155,11 @@ oauth2_application_edit = Редактиране repos = Хранилища can_write_info = Писане delete = Изтриване на акаунта +social = Социални акаунти twofa = Двуфакторно удостоверяване (TOTP) update_theme = Промяна на темата can_read_info = Четене +access_token_deletion_confirm_action = Изтриване website = Уебсайт cancel = Отказ delete_token = Изтриване @@ -163,16 +169,19 @@ save_application = Запазване privacy = Поверителност avatar = Профилна снимка add_key = Добавяне на ключ +account_link = Свързани акаунти delete_email = Премахване update_language = Промяна на езика organization = Организации link_account = Свързване на акаунт +add_new_gpg_key = Добавяне на GPG ключ manage_gpg_keys = Управление на GPG ключовете manage_ssh_keys = Управление на SSH ключовете old_password = Текуща парола public_profile = Публичен профил full_name = Пълно име security = Сигурност +add_new_key = Добавяне на SSH ключ account = Акаунт update_avatar = Обновяване на профилната снимка ssh_gpg_keys = SSH / GPG ключове @@ -184,6 +193,7 @@ biography_placeholder = Разкажете на другите малко за orgs = Организации continue = Продължаване blocked_users = Блокирани потребители +emails = Адреси на ел. поща update_profile = Обновяване на профила profile = Профил change_password = Промяна на паролата @@ -218,6 +228,7 @@ comment_type_group_title = Заглавие comment_type_group_label = Етикет change_username_prompt = Бележка: Промяната на потребителското ви име променя също URL на вашия акаунт. update_language_not_found = Езикът „%s“ не е наличен. +keep_activity_private_popup = Вашата дейност ще бъде видима само за вас и администраторите на сайта uploaded_avatar_not_a_image = Каченият файл не е изображение. uploaded_avatar_is_too_big = Размерът на качения файл (%d KiB) надвишава максималния размер (%d KiB). change_password_success = Паролата ви е обновена. Отсега нататък използвайте новата си парола, за да влезете. @@ -267,10 +278,13 @@ confirm_delete_account = Потвърждаване на изтриването email_notifications.onmention = Ел. писмо само при споменаване pronouns_unspecified = Непосочени pronouns = Местоимения +gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig language.title = Език по подразбиране language.localization_project = Помогнете ни да преведем Forgejo на вашия език! Научете повече. language.description = Този език ще бъде запазен във вашия акаунт и ще се използва като език по подразбиране, след като влезете. +pronouns_custom = Персонализирани visibility.limited_tooltip = Видим само за влезли потребители +pronouns_custom_label = Персонализирани местоимения comment_type_group_review_request = Искане за рецензия ssh_key_been_used = Този SSH ключ вече е добавен към сървъра. create_oauth2_application = Създаване на ново OAuth2 приложение @@ -310,7 +324,7 @@ permissions_list = Разрешения: edit_oauth2_application = Редактиране на OAuth2 приложение remove_oauth2_application = Премахване на OAuth2 приложение twofa_recovery_tip = Ако загубите устройството си, ще можете да използвате ключ за еднократно възстановяване, за да си върнете достъпа до акаунта. -visibility.private_tooltip = Видим само за участници в организации, в които участвате +visibility.private_tooltip = Видим само за членове на организации, в които участвате quota.applies_to_user = Следните правила за квота се прилагат за вашия акаунт quota.rule.no_limit = Неограничена hints = Подсказки @@ -357,7 +371,7 @@ add_email_confirmation_sent = Изпратено е ел. писмо за пот additional_repo_units_hint_description = Показване на подсказка „Включване на повече“ за хранилища, които нямат включени всички налични елементи. email_notifications.submit = Задаване на предпочит. за ел. поща email_notifications.andyourown = И вашите собствени известия -email_deletion_desc = Този адрес за ел. поща и свързаната информация ще бъдат премахнати от вашия акаунт. Git подаванията от този адрес за ел. поща ще останат непроменени. Продължаване? +email_deletion_desc = Адресът за ел. поща и свързаната информация ще бъдат премахнати от вашия акаунт. Git подаванията от този адрес за ел. поща ще останат непроменени. Продължаване? add_email_success = Новият адрес за ел. поща е добавен. remove_account_link = Премахване на свързан акаунт webauthn_alternative_tip = Може да искате да конфигурирате допълнителен метод за удостоверяване. @@ -366,19 +380,6 @@ hidden_comment_types = Скрити типове коментари comment_type_group_lock = Състояние на заключване can_not_add_email_activations_pending = Има чакаща активация, опитайте отново след няколко минути, ако искате да добавите нова ел. поща. storage_overview = Преглед на съхранението -webauthn = Двуфакторно удостоверяване (Ключове за сигурност) -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.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 = Стойност @@ -437,112 +438,6 @@ details.documentation_site = Уебсайт на документацията arch.version.conflicts = В конфликт alpine.repository.branches = Клонове arch.pacman.repo.multi.item = Конфигурация за %s -container.multi_arch = ОС / Архитектура -rpm.repository = Информация за хранилището -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.setup = Добавете хранилище към списъка със свързани хранилища (изберете необходимата архитектура вместо „_arch_“): -alt.repository = Информация за хранилището -owner.settings.cargo.initialize.error = Неуспешно инициализиране на индекса на Cargo: %v -owner.settings.cargo.initialize = Инициализиране на индекс -settings.delete.description = Изтриването на пакет е трайно и не може да бъде отменено. -alt.repository.multiple_groups = Този пакет е наличен в няколко групи. -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 = и изпълнете следната команда: -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.success = Връзката на хранилището беше успешно обновена. -owner.settings.cleanuprules.pattern_full_match = Прилагане на шаблона към пълното име на пакета -owner.settings.cleanuprules.keep.title = Версиите, които съответстват на тези правила, се запазват, дори ако съответстват на правило за премахване по-долу. -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 часа @@ -613,6 +508,7 @@ release.source_code = Програмен код settings = Настройки forks = Разклонения editor.or = или +search = Търсене issues.new_label_desc_placeholder = Описание watch_guest_user = Влезте, за да наблюдавате това хранилище. migrate_items_milestones = Етапи @@ -655,6 +551,7 @@ pulls.made_using_agit = AGit issues.num_comments = %d коментара issues.filter_sort.leastcomment = Най-малко коментирани issues.filter_sort.mostcomment = Най-много коментирани +issues.keyword_search_unavailable = В момента търсенето по ключова дума не е налично. Моля, свържете се с вашия администратор на сайта. repo_desc_helper = Въведете кратко описание (опционално) mirror_address = Клониране от URL owner_helper = Някои организации може да не се показват в падащото меню поради ограничение за максимален брой хранилища. @@ -671,6 +568,7 @@ readme = README migrate.clone_address = Мигриране / Клониране от URL migrated_from_fake = Мигрирано от %[1]s migrate.migrate = Мигриране от %s +settings.search_user_placeholder = Потърсете потребител… issues.new_label = Нов етикет issues.new_label_placeholder = Име на етикета issues.label_count = %d етикета @@ -770,6 +668,7 @@ activity.title.issues_n = %d задачи wiki.pages = Страници activity.git_stats_author_1 = %d автор activity.git_stats_and_deletions = и +project_board = Проекти wiki.save_page = Запазване на страницата activity.git_stats_author_n = %d автори wiki.delete_page_button = Изтриване на страницата @@ -832,6 +731,8 @@ repo_size = Размер на хранилището settings.danger_zone = Опасна зона issues.closed_by = от %[3]s бе затворена %[1]s issues.delete_comment_confirm = Сигурни ли сте, че искате да изтриете този коментар? +issues.author_helper = Този потребител е авторът. +issues.ref_closed_from = `затвори тази задача %[4]s %[2]s` milestones.due_date = Краен срок (опционално) settings.wiki_delete_notices_1 = - Това ще изтрие перманентно и ще деактивира уикито на хранилището %s. settings.wiki_deletion_success = Данните на уикито на хранилището са изтрити. @@ -848,7 +749,7 @@ settings.admin_settings = Администраторски настройки issues.role.owner = Притежател settings.transfer.title = Прехвърляне на притежанието issues.author = Автор -issues.closed_at = `затвори тази задача %s` +issues.closed_at = `затвори тази задача %[2]s` settings.collaborator_deletion_desc = Премахването на сътрудник ще отнеме достъпа му до това хранилище. Продължаване? commits.message = Съобщение issues.due_date_not_set = Няма зададен краен срок. @@ -872,17 +773,19 @@ issues.filter_type.all_issues = Всички задачи issues.filter_poster_no_select = Всички автори issues.opened_by = отворена %[1]s от %[3]s issues.action_open = Отваряне -pulls.closed_at = `затвори тази заявка за сливане %s` -pulls.reopened_at = `отвори наново тази заявка за сливане %s` -issues.reopened_at = `отвори наново тази задача %s` +pulls.closed_at = `затвори тази заявка за сливане %[2]s` +pulls.reopened_at = `отвори наново тази заявка за сливане %[2]s` +issues.reopened_at = `отвори наново тази задача %[2]s` projects.column.edit = Редактиране на колоната issues.close = Затваряне на задачата +issues.ref_reopened_from = `отвори наново тази задача %[4]s %[2]s` projects.deletion = Изтриване на проекта projects.edit_success = Проектът „%s“ е обновен. projects.deletion_success = Проектът е изтрит. issues.create_comment = Коментиране unescape_control_characters = Отекраниране editor.file_delete_success = Файлът „%s“ е изтрит. +projects.type.uncategorized = Некатегоризирано projects.column.set_default = Задаване по подразбиране projects.column.assigned_to = Възложено на issues.reopen_comment_issue = Отваряне наново с коментар @@ -967,12 +870,14 @@ editor.filename_cannot_be_empty = Името на файла не може да release.title_empty = Заглавието не може да бъде празно. settings.webhook.payload = Съдържание settings.deploy_key_content = Съдържание +clone_in_vsc = Клониране във VS Code use_template = Използване на този шаблон download_file = Изтегляне на файла editor.add_file = Добавяне на файл editor.edit_file = Редактиране на файла editor.this_file_locked = Файлът е заключен editor.delete_this_file = Изтриване на файла +clone_in_vscodium = Клониране във VSCodium download_zip = Изтегляне на ZIP download_tar = Изтегляне на TAR.GZ desc.public = Публично @@ -1035,9 +940,9 @@ editor.no_changes_to_show = Няма промени за показване. issues.choose.get_started = Първи стъпки issues.change_milestone_at = `промени етапа от %s на %s %s` issues.change_project_at = `промени проекта от %s на %s %s` -issues.self_assign_at = `си самовъзложи това %s` +issues.self_assign_at = `си само-възложи това %s` issues.remove_assignee_at = `е премахнат като изпълнител от %s %s` -issues.remove_self_assignment = `се самопремахна като изпълнител %s` +issues.remove_self_assignment = `се само-премахна като изпълнител %s` issues.add_assignee_at = `му бе възложено това от %s %s` pulls.merged_by = от %[3]s бе слята %[1]s pulls.merged_by_fake = от %[2]s бе слята %[1]s @@ -1053,6 +958,7 @@ issues.lock.reason = Причина за заключването pulls.create = Създаване на заявка за сливане issues.label.filter_sort.reverse_by_size = Най-голям размер issues.unlock = Отключване на обсъждането +issues.due_date_form_add = Добавяне на краен срок release.save_draft = Запазване на чернова release.add_tag = Създаване на маркер release.publish = Публикуване на издание @@ -1105,7 +1011,9 @@ editor.fail_to_update_file_summary = Съобщение за грешка: editor.fail_to_update_file = Неуспешно обновяване/създаване на файл „%s“. editor.add_subdir = Добавяне на директория… commits.commits = Подавания +commits.find = Търсене commits.search_all = Всички клонове +commits.search = Потърсете подавания… commit.operations = Операции issues.deleted_milestone = `(изтрит)` issues.deleted_project = `(изтрит)` @@ -1134,11 +1042,13 @@ activity.git_stats_push_to_all_branches = към всички клонове. release.deletion_tag_success = Маркерът е изтрит. release.cancel = Отказ release.deletion = Изтриване на изданието +release.download_count = Изтегляния: %s release.tag_name_invalid = Името на маркера не е валидно. diff.stats_desc = %d променени файла с %d добавяния и %d изтривания release.tag_name_already_exist = Вече съществува издание с това име на маркер. branch.branch_already_exists = Клонът „%s“ вече съществува в това хранилище. diff.download_patch = Изтегляне на файл-кръпка +diff.show_diff_stats = Показване на статистика diff.commit = подаване diff.download_diff = Изтегляне на файл-разлики diff.whitespace_show_everything = Показване на всички промени @@ -1153,11 +1063,12 @@ issues.push_commit_1 = добави %d подаване %s fork_visibility_helper = Видимостта на разклонено хранилище не може да бъде променена. language_other = Други stars_remove_warning = Това ще премахне всички звезди от това хранилище. -tree_path_not_found.tag = Пътят %[1]s не съществува в маркер %[2]s -tree_path_not_found.commit = Пътят %[1]s не съществува в подаване %[2]s -tree_path_not_found.branch = Пътят %[1]s не съществува в клон %[2]s +tree_path_not_found_tag = Пътят %[1]s не съществува в маркер %[2]s +tree_path_not_found_commit = Пътят %[1]s не съществува в подаване %[2]s +tree_path_not_found_branch = Пътят %[1]s не съществува в клон %[2]s transfer.accept = Приемане на прехвърлянето transfer.reject = Отхвърляне на прехвърлянето +archive.issue.nocomment = Това хранилище е архивирано. Не можете да коментирате в задачите. forked_from = разклонено от issues.delete_branch_at = `изтри клона %s %s` pulls.has_viewed_file = Прегледано @@ -1193,6 +1104,7 @@ branch.delete_html = Изтриване на клона tag.create_success = Маркерът „%s“ е създаден. branch.new_branch_from = Създаване на нов клон от „%s“ branch.new_branch = Създаване на нов клон +branch.confirm_rename_branch = Преименуване на клона branch.create_from = от „%s“ settings.add_team_duplicate = Екипът вече разполага с това хранилище settings.slack_domain = Домейн @@ -1218,6 +1130,7 @@ branch.deletion_failed = Неуспешно изтриване на клона branch.rename_branch_to = Преименуване от „%s“ на: settings.web_hook_name_msteams = Microsoft Teams settings.web_hook_name_dingtalk = DingTalk +issues.error_removing_due_date = Неуспешно премахване на крайния срок. branch.renamed = Клонът %s е преименуван на %s. settings.teams = Екипи settings.add_team = Добавяне на екип @@ -1231,6 +1144,8 @@ branch.download = Изтегляне на клона „%s“ branch.rename = Преименуване на клона „%s“ empty_message = В това хранилище няма съдържание. open_with_editor = Отваряне с %s +search.search_repo = Търсене в хранилището +search.results = Резултати от търсенето на "%s" в %s object_format = Формат на обектите release.releases_for = Издания за %s release.tags_for = Маркери за %s @@ -1238,10 +1153,12 @@ pulls.cmd_instruction_hint = Вижте инструкциите за коман pulls.showing_only_single_commit = Показани са само промените в подаване %[1]s issues.lock_no_reason = заключи и ограничи обсъждането до сътрудници %s pulls.expand_files = Разгъване на всички файлове +pulls.title_desc_few = иска да слее %[1]d подавания от %[2]s в %[3]s issues.content_history.deleted = изтрито activity.git_stats_exclude_merges = С изключение на сливанията, activity.navbar.pulse = Последна дейност activity.no_git_activity = Не е имало никаква дейност с подавания през този период. +pulls.merged_title_desc_few = сля %[1]d подавания от %[2]s в %[3]s %[4]s diff.stats_desc_file = %d промени: %d добавяния и %d изтривания issues.content_history.created = създадено pulls.status_checks_success = Всички проверки са успешни @@ -1256,7 +1173,9 @@ pulls.collapse_files = Свиване на всички файлове pulls.show_all_commits = Показване на всички подавания diff.whitespace_button = Празни знаци issues.content_history.edited = редактирано +pulls.title_desc_one = иска да слее %[1]d подаване от %[2]s в %[3]s pulls.showing_specified_commit_range = Показани са само промените между %[1]s..%[2]s +pulls.merged_title_desc_one = сля %[1]d подаване от %[2]s в %[3]s %[4]s pulls.no_merge_access = Не сте упълномощени да слеете тази заявка за сливане. activity.navbar.code_frequency = Честота на промените activity.git_stats_pushed_1 = е изтласкал @@ -1286,7 +1205,7 @@ issues.dependency.cancel = Отказ issues.dependency.add_error_dep_exists = Зависимостта вече съществува. issues.dependency.add_error_dep_not_exist = Зависимостта не съществува. issues.remove_ref_at = `премахна препратката %s %s` -issues.ref_pull_from = `спомена тази заявка за сливане %[3]s %[1]s` +issues.ref_pull_from = `спомена тази заявка за сливане %[4]s %[2]s` issues.dependency.pr_no_dependencies = Няма зададени зависимости. issues.dependency.remove_info = Премахване на тази зависимост issues.dependency.removed_dependency = `премахна зависимостта %s` @@ -1297,6 +1216,7 @@ issues.dependency.issue_close_blocked = Трябва да затворите в issues.dependency.blocks_short = Блокира issues.dependency.remove_header = Премахване на зависимост issues.dependency.issue_remove_text = Това ще премахне зависимостта от тази задача. Продължаване? +issues.reference_link = Препратка: %s pulls.closed = Заявката за сливане е затворена pulls.merged_success = Заявката за сливане е успешно слята и затворена branch.confirm_create_branch = Създаване на клон @@ -1310,11 +1230,11 @@ issues.dependency.title = Зависимости issues.dependency.issue_no_dependencies = Няма зададени зависимости. issues.dependency.pr_close_blocked = Трябва да затворите всички задачи, блокиращи тази заявка за сливане, преди да можете да я слеете. issues.dependency.pr_close_blocks = Тази заявка за сливане блокира затварянето на следните задачи -issues.ref_issue_from = `спомена тази задача %[3]s %[1]s` -issues.commit_ref_at = `спомена тази задача в подаване %s` +issues.ref_issue_from = `спомена тази задача %[4]s %[2]s` +issues.commit_ref_at = `спомена тази задача в подаване %[2]s` issues.add_ref_at = `добави препратка %s %s` pulls.merged_info_text = Клонът %s вече може да бъде изтрит. -pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %s` +pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %[2]s` issues.change_ref_at = `промени препратката от %s на %s %s` diff.review.reject = Поискване на промени diff.bin_not_shown = Двоичният файл не е показан. @@ -1352,7 +1272,7 @@ issues.review.show_resolved = Показване на решено issues.review.hide_resolved = Скриване на решено issues.review.resolve_conversation = Решаване на обсъждането diff.comment.markdown_info = Поддържа се стилизиране с Маркдаун. -diff.file_suppressed = Разликите във файла са потиснати, защото са твърде много +diff.file_suppressed = Разликите не са показани, защото са твърде много pulls.reject_count_n = %d поискани промени settings.pulls.default_allow_edits_from_maintainers = Позволяване на редакции от поддържащите по подразбиране fork_branch = Клон за клониране в разклонението @@ -1366,6 +1286,7 @@ issues.new.no_reviewers = Няма рецензенти issues.filter_reviewers = Филтриране на рецензент issues.filter_type.reviewed_by_you = Рецензирани от вас issues.filter_type.review_requested = Поискани рецензии +issues.review.review = Рецензия issues.review.comment = рецензира %s branch.deleted_by = Изтрит от %s branch.restore = Възстановяване на клона „%s“ @@ -1378,9 +1299,9 @@ branch.create_new_branch = Създаване на клон от клон: pulls.status_checks_show_all = Показване на всички проверки size_format = %[1]s: %[2]s; %[3]s: %[4]s pulls.filter_changes_by_commit = Филтриране по подаване -issues.ref_closing_from = `спомена тази задача в заявка за сливане %[3]s, която ще я затвори, %[1]s` +issues.ref_closing_from = `спомена тази задача в заявка за сливане %[4]s, която ще я затвори, %[2]s` issues.ref_from = `от %[1]s` -issues.ref_reopening_from = `спомена тази задача в заявка за сливане %[3]s, която ще я отвори наново , %[1]s` +issues.ref_reopening_from = `спомена тази задача в заявка за сливане %[4]s, която ще я отвори наново , %[2]s` issues.draft_title = Чернова pulls.reopen_to_merge = Моля, отворете наново тази заявка за сливане, за да извършите сливане. pulls.cant_reopen_deleted_branch = Тази заявка за сливане не може да бъде отворена наново, защото клонът е изтрит. @@ -1426,7 +1347,7 @@ settings.default_branch_desc = Изберете стандартен клон з settings.transfer.button = Прехвърляне на притежанието settings.transfer.modal.title = Прехвърляне на притежанието ambiguous_runes_line = `Този ред съдържа двусмислени Уникод знаци` -ambiguous_character = `%[1]c [U+%04[1]X] може да бъде объркан със %[2]c [U+%04[2]X]` +ambiguous_character = `%[1]c [U+%04[1]X] може да бъде объркан с %[2]c [U+%04[2]X]` invisible_runes_header = `Този файл съдържа невидими Уникод знаци` issues.all_title = Общо issues.new.assign_to_me = Възлагане на мен @@ -1525,7 +1446,7 @@ generated_from = генерирано от clear_ref = `Изчистване на текущата препратка` file_follow = Последване на символната връзка commitstatus.failure = Неуспех -issues.filter_label_exclude = Използвайте Alt + Click, за да изключите етикети +issues.filter_label_exclude = `Използвайте alt + click/enter, за да изключите етикети` migrate.migrating_failed = Мигрирането от %s е неуспешно. migrate.migrating_issues = Мигриране на задачи mirror_from = огледално на @@ -1549,16 +1470,20 @@ migrate.migrating_topics = Мигриране на теми projects.desc = Управлявайте задачи и заявки за сливане в проектни табла. issues.choose.invalid_templates = %v невалидни шаблона са намерени pulls.edit.already_changed = Неуспешно запазване на промените в заявката за сливане. Изглежда съдържанието вече е променено от друг потребител. Моля, презаредете страницата и опитайте да редактирате отново, за да избегнете презаписването на техните промени +migrate.gitbucket.description = Мигриране на данни от GitBucket инстанции. migrate.migrating_git = Мигриране на Git данни commits.newer = По-нови issues.choose.blank_about = Създаване на задача от стандартен шаблон. issues.filter_no_results = Няма резултати issues.filter_no_results_placeholder = Опитайте да коригирате филтрите си за търсене. archive.nocomment = Коментирането не е възможно, тъй като хранилището е архивирано. +migrate.gitlab.description = Мигриране на данни от gitlab.com или други GitLab инстанции. transfer.no_permission_to_accept = Нямате разрешение да приемете това прехвърляне. transfer.no_permission_to_reject = Нямате разрешение да отхвърлите това прехвърляне. editor.file_changed_while_editing = Съдържанието на файла е променено, откакто сте го отворили. Щракнете тук, за да го видите, или Подайте промените отново, за да ги презапишете. sync_fork.button = Синхронизиране +migrate.onedev.description = Мигриране на данни от code.onedev.io или други OneDev инстанции. +migrate.codebase.description = Мигриране на данни от codebasehq.com. migrate.migrating_labels = Мигриране на етикети migrate.migrating_releases = Мигриране на издания editor.push_rejected_no_message = Промяната беше отхвърлена от сървъра без съобщение. Моля, проверете Git куките. @@ -1579,6 +1504,7 @@ editor.cannot_commit_to_protected_branch = Не може да се подава editor.no_commit_to_branch = Не може да се подава директно в клона, защото: editor.push_rejected = Промяната беше отхвърлена от сървъра. Моля, проверете Git куките. cite_this_repo = Цитиране на това хранилище +migrate.gitea.description = Мигриране на данни от gitea.com или други Gitea инстанции. editor.push_rejected_summary = Пълно съобщение на отхвърлянето: sync_fork.branch_behind_one = Този клон е %[1]d подаване зад %[2]s sync_fork.branch_behind_few = Този клон е %[1]d подавания зад %[2]s @@ -1587,6 +1513,9 @@ editor.commit_id_not_matching = Файлът е променен, докато editor.user_no_push_to_branch = Потребителят не може да изтласква в клона archive.pull.noreview = Това хранилище е архивирано. Не можете да рецензирате заявки за сливане. migrate.migrating_failed.error = Неуспешно мигриране: %s +migrate.github.description = Мигриране на данни от github.com или GitHub Enterprise сървър. +migrate.forgejo.description = Мигриране на данни от codeberg.org или други Forgejo инстанции. +migrate.gogs.description = Мигриране на данни от notabug.org или други Gogs инстанции. migrate.migrating_milestones = Мигриране на етапи migrate.failed = Мигрирането е неуспешно: %v pulls.nothing_to_compare_and_allow_empty_pr = Тези клонове са равни. Тази заявка за сливане ще бъде празна. @@ -1608,14 +1537,15 @@ pulls.required_status_check_missing = Някои задължителни про pulls.change_target_branch_at = `промени целевия клон от %s на %s %s` issues.time_spent_total = Общо изразходвано време issues.del_time_history = `изтри изразходваното време %s` -pulls.nothing_to_compare_have_tag = Избраните клонове/маркери са равни. +pulls.nothing_to_compare_have_tag = Избраните клон/маркер са равни. pulls.cannot_auto_merge_desc = Тази заявка за сливане не може да бъде слята автоматично поради конфликти. issues.tracker_auto_close = Таймерът ще бъде спрян автоматично, когато тази задача бъде затворена -issues.force_push_codes = `изтласка принудително %[1]s от %[2]s %[8]s към %[4]s %[9]s %[6]s` +issues.force_push_codes = `изтласка принудително %[1]s от %[2]s към %[4]s %[6]s` pulls.blocked_by_official_review_requests = Тази заявка за сливане е блокирана, защото липсва одобрение от един или повече официални рецензенти. issues.tracker = Проследяване на времето issues.add_time_history = `добави изразходвано време %s` migrate.repo_desc_helper = Оставете празно, за да внесете съществуващото описание +migrate.git.description = Мигриране само на хранилище от всяка Git услуга. mirror_sync = синхронизирано migrate_repo = Мигриране на хранилище migrate_options = Опции за мигрирането @@ -1645,176 +1575,6 @@ migrate.migrating_failed_no_addr = Мигрирането е неуспешно. issues.force_push_compare = Сравняване pulls.status_checking = Някои проверки са в очакване pulls.nothing_to_compare = Тези клонове са равни. Не е нужно да създавате заявка за сливане. -admin.flags_replaced = Флаговете на хранилището са заменени -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 = Искате ли да откажете тази миграция? -issues.choose.invalid_config = Конфигурацията на задачите съдържа грешки: -unit_disabled = Администраторът на сайта е изключил тази секция на хранилището. -issues.blocked_by_user = Не можете да създавате задачи в това хранилище, защото сте блокирани от притежателя на хранилището. -commits.signed_by = Подписано от -commits.signed_by_untrusted_user = Подписано от недоверен потребител -commits.signed_by_untrusted_user_unmatched = Подписано от недоверен потребител, който не съвпада с подаващия -issues.lock.notice_1 = - Други потребители не могат да добавят нови коментари към тази задача. -issues.unlock.notice_2 = - Винаги можете да заключите тази задача отново в бъдеще. -issues.unlock.title = Отключване на обсъждането по тази задача. -issues.dependency.no_permission_1 = Нямате разрешение да прочетете %d зависимост -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.no_merge_desc = Тази заявка за сливане не може да бъде слята, защото всички опции за сливане в хранилището са изключени. -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.no_merge_helper = Включете опциите за сливане в настройките на хранилището или слейте заявката за сливане ръчно. -pulls.review_only_possible_for_full_diff = Рецензирането е възможно само при преглед на пълните разлики -pulls.push_rejected = Изтласкването е неуспешно: Изтласкването е отхвърлено. Прегледайте Git куките за това хранилище. -pulls.auto_merge_canceled_schedule = Автоматичното сливане е отменено за тази заявка за сливане. -pulls.allow_edits_from_maintainers_err = Обновяването е неуспешно -pulls.auto_merge_cancel_schedule = Отмяна на автоматичното сливане -pulls.auto_merge_not_scheduled = Тази заявка за сливане не е насрочена за автоматично сливане. -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 = Наистина ли искате да изтриете тази заявка за сливане? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) -pulls.auto_merge_has_pending_schedule = %[1]s насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[2]s. -pulls.auto_merge_when_succeed = Автоматично сливане, когато всички проверки са успешни -error.csv.invalid_field_count = Не може да се визуализира този файл, защото има грешен брой полета на ред %d. -diff.bin = ДВОИЧЕН -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_approve = Авторите на заявки за сливане не могат да одобряват собствените си заявки -release.tag_name_protected = Името на маркера е защитено. -branch.warning_rename_default_branch = Преименувате стандартния клон. -find_file.no_matching = Не е намерен съвпадащ файл -issues.role.member_helper = Този потребител е участник в организацията, притежаваща това хранилище. -diff.image.overlay = Наслагване -diff.image.swipe = Плъзгане -branch.included = Включен -diff.file_suppressed_line_too_long = Разликите във файла са потиснати, защото един или повече редове са твърде дълги -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 = Потвърждаване @@ -1825,7 +1585,7 @@ yes = Да [editor] buttons.list.ordered.tooltip = Добавяне на номериран списък -buttons.bold.tooltip = Добавяне на удебелен текст (Ctrl+B / ⌘B) +buttons.bold.tooltip = Добавяне на удебелен текст buttons.quote.tooltip = Цитиран текст buttons.code.tooltip = Добавяне на код buttons.list.unordered.tooltip = Добавяне на неномериран списък @@ -1834,7 +1594,7 @@ buttons.switch_to_legacy.tooltip = Използване на стария ред buttons.list.task.tooltip = Добавяне на списък със задачи buttons.enable_monospace_font = Включване на равноширок шрифт buttons.mention.tooltip = Споменаване на потребител или екип -buttons.italic.tooltip = Добавяне на курсив текст (Ctrl+I / ⌘I) +buttons.italic.tooltip = Добавяне на курсив текст buttons.link.tooltip = Добавяне на връзка buttons.disable_monospace_font = Изключване на равноширокия шрифт buttons.ref.tooltip = Препратка към задача или заявка за сливане @@ -1903,16 +1663,18 @@ follow_blocked_user = Не можете да следвате тази орга settings.delete_prompt = Организацията ще бъде премахната завинаги. Това НЕ МОЖЕ да бъде отменено! settings.labels_desc = Добавете етикети, които могат да се използват за задачи за всички хранилища в тази организация. teams.none_access = Без достъп -teams.members.none = Няма участници в този екип. +teams.members.none = Няма членове в този екип. repo_updated = Обновено %s teams.delete_team_success = Екипът е изтрит. +teams.search_repo_placeholder = Потърсете хранилище… teams.delete_team_title = Изтриване на екипа -teams.add_team_member = Добавяне на участник в екипа +teams.add_team_member = Добавяне на член на екипа +teams.read_access_helper = Членовете могат да преглеждат и клонират хранилищата на екипа. teams.invite.description = Моля, щракнете върху бутона по-долу, за да се присъедините към екипа. teams.invite.title = Поканени сте да се присъедините към екип %s в организация %s. team_permission_desc = Разрешение members.public_helper = Да е скрит -teams.members = Участници в екипа +teams.members = Членове на екипа teams.delete_team = Изтриване на екипа members.owner = Притежател members.member_role = Роля на участника: @@ -1922,37 +1684,6 @@ teams.no_desc = Този екип няма описание settings.delete_org_desc = Тази организация ще бъде изтрита перманентно. Продължаване? open_dashboard = Отваряне на таблото settings.change_orgname_prompt = Бележка: Промяната на името на организацията ще промени и URL адреса на вашата организация и ще освободи старото име. -teams.add_duplicate_users = Потребителят вече е участник в екипа. -team_unit_disabled = (Изключено) -form.name_reserved = Името на организацията „%s“ е резервирано. -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 = Видим -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] admin_password = Парола @@ -2021,6 +1752,7 @@ issue.action.review = @%[1]s коментира в тази заявка issue.action.reopen = @%[1]s отвори наново #%[2]d. issue.action.approve = @%[1]s одобри тази заявка за сливане. issue.action.reject = @%[1]s поиска промени в тази заявка за сливане. +register_notify.title = %[1]s, добре дошли в %[2]s link_not_working_do_paste = Ако връзката не работи, опитайте да я копирате и поставите в URL лентата на вашия браузър. activate_account = Моля, активирайте своя акаунт admin.new_user.subject = Нов потребител %s току-що се регистрира @@ -2080,6 +1812,7 @@ block_user = Блокиране на потребителя change_avatar = Променете профилната си снимка… email_visibility.limited = Вашият адрес за ел. поща е видим за всички удостоверени потребители disabled_public_activity = Този потребител е изключил публичната видимост на дейността. +email_visibility.private = Вашият адрес на ел. поща е видим само за вас и администраторите show_on_map = Показване на това място на картата followers_one = %d последовател following_one = %d следван @@ -2104,6 +1837,7 @@ block_user.detail_1 = Ще спрете да се следвате един др [home] filter = Други филтри show_archived = Архивирани +search_repos = Намиране на хранилище… my_orgs = Организации uname_holder = Потребителско име или ел. поща my_repos = Хранилища @@ -2113,9 +1847,13 @@ issues.in_your_repos = Във вашите хранилища show_both_private_public = Показване на и публични и частни show_only_private = Показване само на частни show_private = Частни +password_holder = Парола +show_more_repos = Показване на повече хранилища… show_only_unarchived = Показване само на неархивирани +my_mirrors = Моите огледала show_only_archived = Показване само на архивирани view_home = Преглед на %s +collaborative_repos = Съвместни хранилища switch_dashboard_context = Превключване на контекста на таблото show_only_public = Показване само на публични filter_by_team_repositories = Филтриране по хранилища на екипа @@ -2128,6 +1866,8 @@ dashboard = Табло repositories = Хранилища users.name = Потребителско име organizations = Организации +repos.forks = Разклонения +repos.stars = Звезди config.mailer_name = Име repos.name = Име orgs.name = Име @@ -2141,6 +1881,7 @@ notices.type_1 = Хранилище config.domain = Домейн на сървъра users.max_repo_creation = Максимален брой хранилища defaulthooks = Уеб-куки по подразбиране +auths.sspi_default_language = Потребителски език по подразбиране hooks = Уеб-куки systemhooks = Системни уеб-куки orgs.new_orga = Нова организация @@ -2209,12 +1950,14 @@ Email = Адрес за ел. поща Password = Парола RepoName = Име на хранилището username_been_taken = Потребителското име вече е заето. +SSPIDefaultLanguage = Език по подразбиране password_not_match = Паролите не съвпадат. captcha_incorrect = CAPTCHA кодът е неправилен. username_change_not_local_user = Потребители, които не са локални не могат да променят потребителското си име. username_password_incorrect = Неправилно потребителско име или парола. user_not_exist = Потребителят не съществува. lang_select_error = Изберете език от списъка. +HttpsUrl = HTTPS URL require_error = ` не може да бъде празно.` Retype = Потвърдете паролата url_error = `„%s“ не е валиден URL.` @@ -2223,6 +1966,7 @@ team_not_exist = Екипът не съществува. TeamName = Име на екипа email_error = ` не е валиден адрес за ел. поща.` email_invalid = Адресът за ел. поща е невалиден. +SSHTitle = Име на SSH ключ repo_name_been_taken = Името на хранилището вече е използвано. team_name_been_taken = Името на екипа вече е заето. org_name_been_taken = Името на организацията вече е заето. @@ -2234,14 +1978,14 @@ Pronouns = Местоимения Biography = Биография Website = Уебсайт Location = Местоположение -cannot_add_org_to_team = Организация не може да бъде добавена като участник в екип. +cannot_add_org_to_team = Организация не може да бъде добавена като член на екип. auth_failed = Неуспешно удостоверяване: %v team_no_units_error = Разрешете достъп до поне една секция на хранилището. password_uppercase_one = Поне един голям знак CommitSummary = Обобщение на подаването username_error = ` може да съдържа само буквено-цифрови знаци („0-9“, „a-z“, „A-Z“), тире („-“), долна черта („_“) и точка („.“). Не може да започва или завършва с не-буквено-цифрови знаци, като също така са забранени и последователни не-буквено-цифрови знаци.` username_error_no_dots = ` може да съдържа само буквено-цифрови знаци („0-9“, „a-z“, „A-Z“), тире („-“) и долна черта („_“). Не може да започва или завършва с не-буквено-цифрови знаци, като също така са забранени и последователни не-буквено-цифрови знаци.` -duplicate_invite_to_team = Потребителят вече е поканен като участник в екипа. +duplicate_invite_to_team = Потребителят вече е поканен като член на екипа. must_use_public_key = Ключът, който предоставихте, е частен ключ. Моля, не качвайте частния си ключ никъде. Вместо това използвайте публичния си ключ. org_still_own_packages = Тази организация все още притежава един или повече пакети, първо ги изтрийте. admin_cannot_delete_self = Не можете да изтриете себе си, когато сте администратор. Моля, първо премахнете администраторските си привилегии. @@ -2262,7 +2006,7 @@ enterred_invalid_repo_name = Името на хранилището, което enterred_invalid_org_name = Името на организацията, което въведохте, е неправилно. enterred_invalid_password = Паролата, която въведохте, е неправилна. organization_leave_success = Успешно напуснахте организацията %s. -still_has_org = Вашият акаунт е участник в една или повече организации, първо ги напуснете. +still_has_org = Вашият акаунт е член на една или повече организации, първо ги напуснете. org_still_own_repo = Тази организация все още притежава едно или повече хранилища, първо ги изтрийте или прехвърлете. target_branch_not_exist = Целевият клон не съществува. glob_pattern_error = ` glob шаблонът е невалиден: %s.` @@ -2272,17 +2016,6 @@ 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 = Име на новия клон -invalid_ssh_key = Не може да се потвърди вашият SSH ключ: %s -required_prefix = Въведеният текст трябва да започва с „%s“ -regex_pattern_error = ` шаблонът на регулярния израз е невалиден: %s.` -repository_files_already_exist = Вече съществуват файлове за това хранилище. Свържете се със системния администратор. -repository_files_already_exist.delete = Вече съществуват файлове за това хранилище. Трябва да ги изтриете. -invalid_gpg_key = Не може да се потвърди вашият GPG ключ: %s -git_ref_name_error = ` трябва да е правилно форматирано име на Git препратка.` -last_org_owner = Не можете да премахнете последния потребител от екипа на „притежателите“. Трябва да има поне един притежател за организация. [action] close_issue = `затвори задача %[3]s#%[2]s` @@ -2302,7 +2035,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 @@ -2311,23 +2044,22 @@ 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 от огледало -review_dismissed = `отхвърли рецензия от %[4]s за %[3]s#%[2]s` -mirror_sync_delete = синхронизира и изтри препратка %[2]s на %[3]s от огледало [auth] +tab_openid = OpenID openid_connect_submit = Свързване sign_up_successful = Акаунтът е създаден успешно. Добре дошли! login_userpass = Влизане forgot_password = Забравена парола? +sign_up_now = Нуждаете се от акаунт? Регистрирайте се сега. forgot_password_title = Забравена парола openid_register_title = Създаване на нов акаунт account_activated = Акаунтът е активиран +social_register_helper_msg = Вече имате акаунт? Свържете го сега! verify = Потвърждаване create_new_account = Регистриране на акаунт active_your_account = Активирайте акаунта си +register_helper_msg = Вече имате акаунт? Влезте сега! reset_password = Възстановяване на акаунта disable_register_prompt = Регистрирането е изключено. Моля, свържете се с вашия администратор на сайта. remember_me = Запомни ме @@ -2336,6 +2068,8 @@ disable_register_mail = Потвърждението по ел. поща за р manual_activation_only = Свържете се с вашия администратор на сайта, за да завършите активирането. must_change_password = Обновете паролата си password_too_short = Дължината на паролата не може да бъде по-малка от %d знака. +tab_signin = Влизане +tab_signup = Регистриране password_pwned = Паролата, която сте избрали, е в списък с откраднати пароли, разкрити преди това при публични пробиви на данни. Моля, опитайте отново с различна парола. confirmation_mail_sent_prompt = Ново ел. писмо за потвърждение е изпратено до %s. За да завършите процеса на регистрация, моля, проверете входящата си кутия и последвайте предоставената връзка в рамките на следващите %s. Ако адресът за ел. поща е неправилен, можете да влезете и да поискате друго ел. писмо за потвърждение да бъде изпратено на различен адрес. hint_login = Вече имате акаунт? Влезте! @@ -2403,12 +2137,17 @@ mark_all_as_read = Отбелязване на всички като проче pin = Закачване на известието [explore] +code_search_results = Резултати от търсенето на "%s" go_to = Отиване към repos = Хранилища users = Потребители +search = Търсене code = Код organizations = Организации code_last_indexed_at = Последно индексиран %s +repo_no_results = Няма намерени съответстващи хранилища. +user_no_results = Няма намерени съответстващи потребители. +org_no_results = Няма намерени съответстващи организации. stars_one = %d звезда stars_few = %d звезди forks_one = %d разклонение @@ -2438,14 +2177,6 @@ variables.management = Управление на променливи variables.not_found = Променливата не е открита. variables.id_not_exist = Променлива с идентификатор %d не съществува. runners.owner_type = Тип -status.cancelled = Отменено -status.running = Изпълнява се -status.success = Успешно -status.waiting = Изчаква се -status.unknown = Неизвестно -status.failure = Неуспешно -status.skipped = Пропуснато -unit.desc = Управление на интегрирани CI/CD pipelines с Forgejo Actions. [heatmap] less = По-малко @@ -2527,33 +2258,3 @@ eib = ЕиБ [translation_meta] test = окей - - -[gpg] -default_key = Подписано с ключ по подразбиране -error.no_gpg_keys_found = Не е намерен известен ключ за този подпис в базата данни -error.not_signed_commit = Не е подписано подаване -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] -error.no_unit_allowed_repo = Нямате разрешение за достъп до никоя секция на това хранилище. -unit = Елемент -error.unit_not_allowed = Нямате разрешение за достъп до тази секция на хранилището. \ No newline at end of file diff --git a/options/locale/locale_ca.ini b/options/locale/locale_ca.ini index 6a7e074618..9cb7d5e50c 100644 --- a/options/locale/locale_ca.ini +++ b/options/locale/locale_ca.ini @@ -39,7 +39,7 @@ twofa_scratch = Codi de rascar de doble-factor passcode = Codi de pas webauthn_insert_key = Inseriu la vostra clau de seguretat webauthn_sign_in = Premeu el botó a la vostra clau de seguretat. Si no en té, torneu-la a inserir. -webauthn_press_button = Si us plau, premeu el botó a la vostra clau de seguretat… +webauthn_press_button = Siusplau, premeu el botó a la vostra clau de seguretat… webauthn_use_twofa = Utilitza un codi de doble factor des del teu mòbil webauthn_error = No s'ha pogut llegir la clau de seguretat. webauthn_unsupported_browser = El teu navegador no suprta WebAuthn. @@ -48,11 +48,15 @@ webauthn_error_insecure = WebAuthn només suporta connexions segures. Per provar webauthn_error_unable_to_process = El servidor no ha pogut processar la vostra petició. webauthn_error_duplicated = La clau de seguretat no és permesa per aquesta petició. Si us plau, assegureu-vos que la clau encara no ha estat registrada. webauthn_error_empty = S'ha d'anomenar aquesta clau. +webauthn_reload = Recarrega repository = Repositori organization = Organització mirror = Mirall +new_repo = Nou repositori +new_migrate = Nova migració new_mirror = Nou mirall new_fork = Nou fork d'un repositori +new_org = Nova organització new_project = Nou projecte new_project_column = Nova columna admin_panel = Administració del lloc @@ -144,29 +148,31 @@ new_migrate.link = Nova migració new_org.link = Nova organització [search] +milestone_kind = Cerca fites... fuzzy = Difusa search = Cerca... type_tooltip = Tipus de cerca fuzzy_tooltip = Inclou resultats que s'assemblen al terme de la cerca -repo_kind = Cerca repos… -user_kind = Cerca usuaris… +repo_kind = Cerca repos... +user_kind = Cerca usuaris... code_search_unavailable = La cerca de codi no està disponible actualment. Si us plau concteu amb l'administrador del lloc. -package_kind = Cerca paquets… -project_kind = Cerca projectes… -branch_kind = Cerca branques… -commit_kind = Cerca commits… -runner_kind = Cerca executors… +code_search_by_git_grep = Els resultats actuals de la cerca de codi són proporcionats per "git grep". Podríen haver-hi millors resultats si l'administrador del lloc habilita l'indexador de codi. +package_kind = Cerca paquets... +project_kind = Cerca projectes... +branch_kind = Cerca branques... +commit_kind = Cerca commits... +runner_kind = Cerca executors... no_results = Cap resultat coincident trobat. keyword_search_unavailable = La cerca per paraula clau no està disponible ara mateix. Si us plau contacteu amb l'administrador del lloc. union = Paraules clau union_tooltip = Inclou resultats que encaixen amb qualsevol paraula clau separada per espais -org_kind = Cerca organitzacions… -team_kind = Cerca teams… -code_kind = Cerca codi… -pull_kind = Cerca "pulls"… +org_kind = Cerca organitzacions... +team_kind = Cerca teams... +code_kind = Cerca codi... +pull_kind = Cerca "pulls"... exact = Exacte exact_tooltip = Inclou només resultats que són exactament el terme de cerca -issue_kind = Cerca problemes… +issue_kind = Cerca problemes... regexp = RegExp regexp_tooltip = Interpreta el terme de cerca com una expressió regular @@ -188,6 +194,8 @@ occurred = Hi ha hagut un error report_message = Si creus que això es un bug de Forgejo, si us plau cerca problemes a Codeberg i obre'n un de nou si cal. not_found = L'objectiu no s'ha pogut trobar. server_internal = Error intern del servidor +missing_csrf = Petició Dolenta: falta el testimoni CSRF +invalid_csrf = Petició Dolenta: testimoni CSRF invàlid network_error = Error de xarxa [install] @@ -436,6 +444,7 @@ link_modal.paste_reminder = Pista: Amb un enllaç en el teu porta-retalls, pots [home] my_orgs = Organitzacions +show_more_repos = Mostra més repositoris… show_both_archived_unarchived = Mostrant ambdós arxivats i no-arxivats show_only_public = Mostrant només publics issues.in_your_repos = En els teus repositoris @@ -445,8 +454,10 @@ show_both_private_public = Mostrant amdós publics i privats show_only_private = Mostrant només privats filter_by_team_repositories = Filtra per respostirois d'equip feed_of = Canal de "%s" +collaborative_repos = Respositoris coŀlaboratius show_archived = Arxivat view_home = Veure %s +password_holder = Contrasenya switch_dashboard_context = Commuta el contexte del tauler my_repos = Repositoris show_only_archived = Mostrant només arxivats @@ -472,290 +483,4 @@ admin.new_user.user_info = Informació d'usuari admin.new_user.subject = Nou usuari %s s'acaba d'enregistrar activate_email.text = Si us plau, cliqueu el següent enllaç per verificar la vostra adreça de correu electrònic en %s activate_email = Verifica la teva adreça de correu electrònic -activate_account.text_2 = Si us plau, cliqueu l'enllaç següent per activar el vostre compte en %s: -issue.action.merge = @%[1]s ha fusionat #%[2]d a %[3]s. -release.download.targz = Codi font (TAR.GZ) -repo.transfer.body = Per acceptar-ho o rebutjar-ho, aneu a %s o simplement ignoreu-ho. -team_invite.text_1 = %[1]s us convida a unir-vos a l'equip %[2]s de l'organització %[3]s. -team_invite.text_2 = Si us plau, feu clic al següent enllaç per unir-vos a l'equip: -issue.action.close = @%[1]s ha tancat #%[2]d. -team_invite.text_3 = Nota: aquesta invitació estava destinada a %[1]s. Si no estàveu esperant-la, podeu ignorar aquest correu electrònic. -team_invite.subject = %[1]s us convida a unir-vos a l'organització %[2]s -release.title = Títol: %s -release.downloads = Baixades: -release.download.zip = Codi font (ZIP) -repo.transfer.subject_to_you = %s us vol transferir el repositori "%s" -repo.collaborator.added.subject = %s us ha afegit a %s com a col·laborador -repo.collaborator.added.text = Us han afegit com a col·laborador al repositori: -issue_assigned.issue = @%[1]s us ha assignat l'incidència %[2]s del repositori %[3]s. -issue.x_mentioned_you = @%s us ha mencionat: -issue.action.new = @%[1]s ha creat #%[2]d. -repo.transfer.subject_to = %s vol transferir el repositori "%s" a %s - -[modal] -yes = Sí -no = No -cancel = Cancel·lar -confirm = Confirmar - -[form] -FullName = Nom complet -Description = Descripció -Pronouns = Pronoms -UserName = Nom d'usuari -username_change_not_local_user = No es permet canviar el nom d'usuari als usuaris no locals. -repo_name_been_taken = El nom del repositori ja està en ús. -repository_files_already_exist.adopt = Aquest repositori ja té fitxers i només pot ser Adoptat. -Password = Contrasenya -Retype = Confirma la contrasenya -captcha_incorrect = El codi CAPTCHA és incorrecte. -email_error = ` no és una adreça de correu electrònic vàlida.` -unable_verify_ssh_key = No es pot verificar la clau SSH, reviseu si conté errors. -auth_failed = Ha fallat l'autenticació: %v -still_own_packages = El vostre compte té un o més paquets, elimineu-los abans. -org_still_own_repo = Aquesta organització encara té un o més repositoris, elimineu-los o transferiu-los abans. -admin_cannot_delete_self = No us podeu eliminar si sou administradors. Si us plau, elimineu els vostres privilegis d'administrador abans. -repository_files_already_exist = Aquest repositori ja té fitxers. Contacteu l'administrador del sistema. -password_uppercase_one = Com a mínim un caràcter en majúscules -team_not_exist = L'equip no existeix. -team_name_been_taken = El nom de l'equip ja està en ús. -last_org_owner = No podeu eliminar l'últim usuari de l'equip "propietaris". Una organització ha de tenir un propietari com a mínim. -duplicate_invite_to_team = L'usuari ja ha estat convidat com a membre de l'equip. -Biography = Biografia -RepoName = Nom del repositori -TeamName = Nom de l' equip -To = Nom de la branca -NewBranchName = Nom de la nova branca -Content = Contingut -url_error = `"%s" no és una URL vàlida.` -unknown_error = Error desconegut: -password_not_match = Les contrasenyes no coincideixen. -lang_select_error = Seleccioneu un idioma de la llista. -username_been_taken = El nom d'usuari ja està en ús. -repository_files_already_exist.adopt_or_delete = Aquest repositori ja té fitxers. Adopteu-los o elimineu-los. -org_name_been_taken = El nom de l'organització ja està en ús. -email_been_used = L'adreça de correu electrònic ja està en ús. -email_invalid = L'adreça de correu electrònic no es vàlida. -password_complexity = La contrasenya no supera els requisits de complexitat: -password_digit_one = Com a mínim un dígit -password_special_one = Com a mínim un caràcter especial (puntuació, claudàtors, cometes, etc.) -enterred_invalid_repo_name = El nom del repositori que heu introduït és incorrecte. -enterred_invalid_owner_name = El nom del nou propietari no és vàlid. -enterred_invalid_password = La contrasenya que heu introduït no és correcta. -user_not_exist = L'usuari no existeix. -cannot_add_org_to_team = No es pot afegir una organització com a membre d'un equip. -invalid_ssh_key = No es pot verificar la vostra clau SSH: %s -invalid_gpg_key = No es pot verificar la vostra clau GPG: %s -still_has_org = El vostre compte és membre d'una o més organitzacions, abandoneu-les abans. -still_own_repo = El vostre compte té un o més repositoris, elimineu-los o transferiu-los abans. -target_branch_not_exist = La branca de destí no existeix. -AuthName = Nom d'autorització -enterred_invalid_org_name = El nom d'organització que heu introduït és incorrecte. -repository_files_already_exist.delete = Aquest repositori ja té fitxers. Els heu d'eliminar. -organization_leave_success = Heu deixat l'organització %s amb èxit. -openid_been_used = L'adreça OpenID "%s" ja està en ús. -username_password_incorrect = Nom d'usuari o contrasenya incorrecte. -password_lowercase_one = Com a mínim un caràcter en minúscules -must_use_public_key = La clau que heu proporcionat és una clau privada. Si us plau, no pugueu la vostra clau privada enlloc. Feu servir la vostra clau pública. -org_still_own_packages = Aquesta organització encara té un o més paquets, elimineu-los abans. - -[settings] -pronouns = Pronoms -change_username_prompt = Nota: canviar el vostre nom d'usuari també canvia l'URL del vostre compte. -comment_type_group_time_tracking = Seguiment del temps -comment_type_group_deadline = Data límit -keep_activity_private = Oculta l'activitat de la pàgina de perfil -repos = Repositoris -applications = Aplicacions -location_placeholder = Compartiu la vostra ubicació aproximada amb altres -security = Seguretat -profile = Perfil -account = Compte -appearance = Aparença -blocked_users = Usuaris bloquejats -cancel = Cancel·lar -language = Idioma -comment_type_group_reference = Referència -comment_type_group_dependency = Dependència -saved_successfully = La vostra configuració s'ha desat amb èxit. -ssh_gpg_keys = Claus SSH / GPG -public_profile = Perfil públic -ui = Tema -hints = Consells -hidden_comment_types.issue_ref_tooltip = Comentaris on l'usuari canvia la branca/etiqueta associada amb aquesta incidència -hidden_comment_types = Tipus de comentaris ocults -orgs = Organitzacions -organization = Organitzacions -full_name = Nom complet -website = Pàgina web -location = Ubicació -pronouns_unspecified = Sense especificar -update_theme = Canviar el tema -update_language_not_found = L'idioma "%s" no està disponible. -update_language_success = S'ha actualitzat l'idioma. -update_profile_success = El vostre perfil s'ha actualitzat. -change_username_redirect_prompt = El nom d'usuari antic redirigirà fins que algú altre el reclami. -additional_repo_units_hint = Suggereix habilitar unitats de repositori addicionals -update_hints = Actualitza els suggeriments -update_hints_success = S'han actualitzat els suggeriments. -comment_type_group_label = Etiqueta -comment_type_group_title = Títol -comment_type_group_branch = Branca -comment_type_group_issue_ref = Referència d'incidència -hidden_comment_types_description = Els tipus de comentaris marcats aquí no es mostraran a les pàgines de les incidències. Marcar "Etiqueta", per exemple, eliminarà tots els comentaris del tipus "