diff --git a/.air.toml b/.air.toml index 65872e7382..af182697fb 100644 --- a/.air.toml +++ b/.air.toml @@ -11,7 +11,7 @@ include_file = ["main.go"] include_dir = ["cmd", "models", "modules", "options", "routers", "services"] exclude_dir = [ "models/fixtures", - "models/gitea_migrations/fixtures", + "models/migrations/fixtures", "modules/avatar/identicon/testdata", "modules/avatar/testdata", "modules/git/tests", diff --git a/.deadcode-out b/.deadcode-out index 9c7176e7b1..61c5bcb055 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -13,30 +13,34 @@ forgejo.org/models IsErrSHANotFound IsErrMergeDivergingFastForwardOnly +forgejo.org/models/activities + GetActivityByID + NewFederatedUserActivity + CreateUserActivity + GetFollowingFeeds + FederatedUserActivity.loadActor + forgejo.org/models/auth WebAuthnCredentials forgejo.org/models/db TruncateBeans - TruncateBeansCascade InTransaction DumpTables - GetTableNames - extendBeansForCascade forgejo.org/models/dbfs file.renameTo Create Rename +forgejo.org/models/forgefed + GetFederationHost + forgejo.org/models/forgejo/semver GetVersion SetVersionString SetVersion -forgejo.org/models/forgejo_migrations - resetMigrations - forgejo.org/models/git RemoveDeletedBranchByID @@ -57,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 @@ -91,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 @@ -210,6 +231,7 @@ forgejo.org/modules/util/filebuffer forgejo.org/modules/validation IsErrNotValid + ValidateIDExists forgejo.org/modules/web RouteMock @@ -226,15 +248,6 @@ forgejo.org/routers/web/org forgejo.org/services/context GetPrivateContext -forgejo.org/services/federation - FollowRemoteActor - -forgejo.org/services/mailer - ActionCloseIssueByCommit.isActionAdditionalData - -forgejo.org/services/notify - UnregisterNotifier - forgejo.org/services/repository IsErrForkAlreadyExist diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 72fb06c69a..28fa9e4555 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,12 +1,12 @@ { "name": "Gitea DevContainer", - "image": "mcr.microsoft.com/devcontainers/go:2.0-bullseye", + "image": "mcr.microsoft.com/devcontainers/go:1.24-bullseye", "features": { // installs nodejs into container "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-composite/setup-cache-go/action.yaml b/.forgejo/workflows-composite/setup-cache-go/action.yaml index fec166fc35..f2818a7635 100644 --- a/.forgejo/workflows-composite/setup-cache-go/action.yaml +++ b/.forgejo/workflows-composite/setup-cache-go/action.yaml @@ -17,7 +17,7 @@ runs: apt-get -q install -qq -y zstd - name: "Set up Go using setup-go" - uses: https://data.forgejo.org/actions/setup-go@v6 + uses: https://data.forgejo.org/actions/setup-go@v5 id: go-version with: go-version-file: "go.mod" diff --git a/.forgejo/workflows/build-release-integration.yml b/.forgejo/workflows/build-release-integration.yml index bb11f083ba..a5f9991d45 100644 --- a/.forgejo/workflows/build-release-integration.yml +++ b/.forgejo/workflows/build-release-integration.yml @@ -25,10 +25,10 @@ jobs: if: vars.ROLE == 'forgejo-coding' runs-on: lxc-bookworm steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 - id: forgejo - uses: https://data.forgejo.org/actions/setup-forgejo@v3.0.4 + uses: https://data.forgejo.org/actions/setup-forgejo@v3.0.1 with: user: root password: admin1234 diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index 042a981881..ba83304275 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -33,7 +33,7 @@ jobs: # root is used for testing, allow it if: vars.ROLE == 'forgejo-integration' || github.repository_owner == 'root' steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 with: fetch-depth: 0 @@ -43,11 +43,11 @@ jobs: repository="${{ github.repository }}" echo "value=${repository##*/}" >> "$GITHUB_OUTPUT" - - uses: https://data.forgejo.org/actions/setup-node@v6 + - uses: https://data.forgejo.org/actions/setup-node@v4 with: node-version: 22 - - uses: https://data.forgejo.org/actions/setup-go@v6 + - uses: https://data.forgejo.org/actions/setup-go@v5 with: go-version-file: "go.mod" @@ -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 c481c80497..7c8c56de13 100644 --- a/.forgejo/workflows/cascade-setup-end-to-end.yml +++ b/.forgejo/workflows/cascade-setup-end-to-end.yml @@ -37,11 +37,11 @@ jobs: container: image: data.forgejo.org/oci/node:22-bookworm steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 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 a835efd9a5..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.6-scratch - options: --tmpfs /data:noatime - steps: - - uses: https://data.forgejo.org/actions/checkout@v5 - 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 a583756656..1c63868938 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -41,7 +41,7 @@ jobs: runs-on: lxc-bookworm if: vars.DOER != '' && vars.FORGEJO != '' && vars.TO_OWNER != '' && vars.FROM_OWNER != '' && secrets.TOKEN != '' steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 - name: copy & sign uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.4.1 @@ -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.2.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 76799b06dc..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@v5 + - 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 6fde710d84..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: @@ -17,7 +17,7 @@ jobs: container: image: 'data.forgejo.org/oci/node:22-bookworm' steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 - name: event run: | @@ -28,7 +28,7 @@ jobs: ${{ toJSON(github.event) }} EOF - - uses: https://data.forgejo.org/actions/setup-go@v6 + - uses: https://data.forgejo.org/actions/setup-go@v5 with: go-version-file: "go.mod" cache: false diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index 7a06a3177a..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.146.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 f98f92e317..bfb5952116 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -34,10 +34,13 @@ jobs: image: 'data.forgejo.org/oci/node:22-bookworm' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - 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' @@ -53,10 +56,13 @@ jobs: image: 'data.forgejo.org/oci/node:22-bookworm' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - 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 +72,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@v5 - - 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 49213b064f..7a93bb66a8 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -21,7 +21,7 @@ jobs: cat <<'EOF' ${{ toJSON(github) }} EOF - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env - run: su forgejo -c 'make deps-backend deps-tools' - run: su forgejo -c 'make --always-make -j$(nproc) lint-backend tidy-check swagger-check lint-swagger fmt-check swagger-validate' # ensure the "go-licenses" make target runs @@ -33,15 +33,11 @@ jobs: image: 'data.forgejo.org/oci/node:22-bookworm' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 - 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 @@ -77,7 +73,7 @@ jobs: MINIO_ROOT_USER: 123456 MINIO_ROOT_PASSWORD: 12345678 steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env - name: install git >= 2.42 uses: ./.forgejo/workflows-composite/apt-install-from @@ -104,7 +100,7 @@ jobs: image: 'data.forgejo.org/oci/playwright:latest' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 with: fetch-depth: 20 - uses: ./.forgejo/workflows-composite/setup-env @@ -126,7 +122,7 @@ jobs: echo "all=1" >> "$GITHUB_OUTPUT" - name: Get changed files id: changed-files - uses: https://data.forgejo.org/tj-actions/changed-files@v47 + uses: https://data.forgejo.org/tj-actions/changed-files@v46 with: separator: '\n' - run: | @@ -172,7 +168,7 @@ jobs: image: ${{ matrix.cacher.image }} options: ${{ matrix.cacher.options }} steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env - name: install git >= 2.42 uses: ./.forgejo/workflows-composite/apt-install-from @@ -205,7 +201,7 @@ jobs: 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 steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - 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 @@ -232,8 +228,7 @@ jobs: MINIO_ROOT_PASSWORD: 12345678 options: --tmpfs /bitnami/minio/data ldap: - image: data.forgejo.org/oci/forgejo-test-openldap:1 - options: --memory 500M + image: data.forgejo.org/oci/test-openldap:latest pgsql: image: data.forgejo.org/oci/bitnami/postgresql:16 env: @@ -243,7 +238,7 @@ jobs: POSTGRESQL_EXTRA_FLAGS: -c full_page_writes=off options: --tmpfs /bitnami/postgresql steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - 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 @@ -265,7 +260,7 @@ jobs: image: 'data.forgejo.org/oci/node:22-bookworm' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - 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 @@ -293,7 +288,7 @@ jobs: image: 'data.forgejo.org/oci/node:22-bookworm' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v5 + - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env - run: su forgejo -c 'make deps-backend deps-tools' - run: su forgejo -c 'make security-check' 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 e41a40fe5a..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: @@ -94,7 +86,6 @@ linters: - all testifylint: disable: - - error-is-as - go-require exclusions: generated: lax @@ -120,7 +111,7 @@ linters: - errcheck - gocyclo - gosec - path: models/gitea_migrations/v + path: models/migrations/v - linters: - forbidigo path: cmd diff --git a/.release-notes-assistant.yaml b/.release-notes-assistant.yaml index f8264c0897..b3e5a8e665 100644 --- a/.release-notes-assistant.yaml +++ b/.release-notes-assistant.yaml @@ -5,7 +5,6 @@ branch-find-version: 'v(?P\d+\.\d+)/forgejo' branch-to-version: '${version}.0' branch-from-version: 'v%[1]d.%[2]d/forgejo' tag-from-version: 'v%[1]d.%[2]d.%[3]d' -supported-release-count: 3 branch-known: - 'v7.0/forgejo' cleanup-line: 'sed -Ee "s/^(feat|fix):\s*//g" -e "s/^\[WIP\] //" -e "s/^WIP: //" -e "s;\[(UI|BUG|FEAT|v.*?/forgejo)\]\s*;;g"' diff --git a/CODEOWNERS b/CODEOWNERS index fa332c6d71..34cdceca09 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -38,10 +38,5 @@ routers/.* @gusted options/locale/.* @0ko options/locale_next/.* @0ko -# lint-locale-usage -build/lint-locale-usage/.* @fogti -models/unit/.* @fogti -services/migrations/lint-locale-usage/.* @fogti - # Personal interest .*/webhook.* @oliverpool diff --git a/Makefile b/Makefile index 76e58c784f..e770f2a989 100644 --- a/Makefile +++ b/Makefile @@ -37,17 +37,17 @@ endif XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go -EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.4.1 # renovate: datasource=go -GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.1 # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.5.0 # renovate: datasource=go -GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15 # renovate: datasource=go -SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.33.1 # renovate: datasource=go +EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.3.0 # renovate: datasource=go +GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 # renovate: datasource=go +GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go +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.38.0 # renovate: datasource=go -GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.6.0 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@41.146.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/gitea_migrations/...) $(shell $(GO) list forgejo.org/models/forgejo_migrations_legacy/...) $(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/gitea_migrations/... forgejo.org/models/forgejo_migrations_legacy/... forgejo.org/models/forgejo_migrations/...)) -endif - ### # Check system and environment requirements ### @@ -432,7 +417,7 @@ lint: lint-frontend lint-backend lint-fix: lint-frontend-fix lint-backend-fix .PHONY: lint-frontend -lint-frontend: lint-js tsc lint-css +lint-frontend: lint-js lint-css .PHONY: lint-frontend-fix lint-frontend-fix: lint-js-fix lint-css-fix @@ -475,7 +460,7 @@ lint-locale: .PHONY: lint-locale-usage lint-locale-usage: - $(GO) run ./build/lint-locale-usage/bin --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 @@ -514,11 +499,7 @@ lint-disposable-emails-fix: .PHONY: security-check security-check: - $(GO) run $(GOVULNCHECK_PACKAGE) -show color ./... - -.PHONY: tsc -tsc: node_modules - npx tsc --noEmit + go run $(GOVULNCHECK_PACKAGE) -show color ./... ### # Development and testing targets @@ -541,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: @@ -571,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: @@ -676,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 @@ -723,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\#% @@ -746,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 @@ -755,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 @@ -771,34 +746,34 @@ 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 .PHONY: migrations.individual.sqlite.test\#% migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite - GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/gitea_migrations/$* + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/migrations/$* .PHONY: migrations.individual.pgsql.test -migrations.individual.pgsql.test: $(GO_SOURCES) | 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 .PHONY: migrations.individual.pgsql.test\#% migrations.individual.pgsql.test\#%: $(GO_SOURCES) generate-ini-pgsql - GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/gitea_migrations/$* + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/migrations/$* .PHONY: migrations.individual.sqlite.test -migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite | 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 .PHONY: migrations.individual.sqlite.test\#% migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite - GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/gitea_migrations/$* + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GOTEST) $(GOTESTFLAGS) -tags '$(TEST_TAGS)' forgejo.org/models/migrations/$* e2e.mysql.test: $(GO_SOURCES) $(GOTEST) $(GOTESTFLAGS) -c forgejo.org/tests/e2e -o e2e.mysql.test @@ -965,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 a0a8889352..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 @@ -130,10 +130,6 @@ A [companion blog post](https://forgejo.org/2024-07-release-v8-0/) provides addi - [PR](https://codeberg.org/forgejo/forgejo/pulls/3334): added support for the [`workflow_dispatch` trigger](https://forgejo.org/docs/v8.0/user/actions/#onworkflow_dispatch) in Forgejo Actions. - [PR](https://codeberg.org/forgejo/forgejo/pulls/3307): support [Proof Key for Code Exchange (PKCE - RFC7636)](https://www.rfc-editor.org/rfc/rfc7636) for external login using the OpenID Connect authentication source. - [PR](https://codeberg.org/forgejo/forgejo/pulls/3139): allow hiding auto generated release archives. - - [PR](https://codeberg.org/forgejo/forgejo/pulls/3952): Update of Chroma from v2.13.0: to v2.14.0: - - [`1e983e7`](https://github.com/alecthomas/chroma/commit/1e983e7) lexers/cue: support CUE attributes ([#​961](https://github.com/alecthomas/chroma/issues/961)) - - [`9347b55`](https://github.com/alecthomas/chroma/commit/9347b55) Add Gleam syntax highlighting ([#​959](https://github.com/alecthomas/chroma/issues/959)) - - [`2580aaa`](https://github.com/alecthomas/chroma/commit/2580aaa) Add Bazel bzlmod support into Python lexer ([#​947](https://github.com/alecthomas/chroma/issues/947)) - **Bug fixes** - [PR](https://codeberg.org/forgejo/forgejo/pulls/4732) ([backported from](https://codeberg.org/forgejo/forgejo/pulls/4715)): Show the AGit label on merged pull requests. - [PR](https://codeberg.org/forgejo/forgejo/pulls/4689) ([backported from](https://codeberg.org/forgejo/forgejo/pulls/4687)): Fixed: issue state change via the API is not idempotent. @@ -150,9 +146,6 @@ A [companion blog post](https://forgejo.org/2024-07-release-v8-0/) provides addi - [PR](https://codeberg.org/forgejo/forgejo/pulls/3442): Fixed: it is not possible to remove attachments from an empty comment. - [PR](https://codeberg.org/forgejo/forgejo/pulls/3430): Fixed: the `/api/v1/repos/{owner}/{repo}/wiki` API endpoints is using a hardcoded "master" branch for the wiki, rather than the branch they really use. - [PR](https://codeberg.org/forgejo/forgejo/pulls/3379): Fixed: using the API to search for users, the results are not paged by default an the default paging limits are not respected. - - [PR](https://codeberg.org/forgejo/forgejo/pulls/3952): Update of Chroma from v2.13.0: to v2.14.0: - - [`736c0ea`](https://github.com/alecthomas/chroma/commit/736c0ea) Typescript: Several fixes ([#​952](https://github.com/alecthomas/chroma/issues/952)) - - [`e5c25d0`](https://github.com/alecthomas/chroma/commit/e5c25d0) Org: Keep all newlines ([#​951](https://github.com/alecthomas/chroma/issues/951)) - **Localization** - [PR](https://codeberg.org/forgejo/forgejo/pulls/4661) ([backported from](https://codeberg.org/forgejo/forgejo/pulls/4568)): 24 July updates - [PR](https://codeberg.org/forgejo/forgejo/pulls/4565) ([backported from](https://codeberg.org/forgejo/forgejo/pulls/4451)): 19 July updates diff --git a/assets/go-licenses.json b/assets/go-licenses.json index aa1a65db12..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", @@ -199,16 +64,6 @@ "path": "code.gitea.io/sdk/gitea/LICENSE", "licenseText": "Copyright (c) 2016 The Gitea Authors\nCopyright (c) 2014 The Gogs Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" }, - { - "name": "code.superseriousbusiness.org/go-jpeg-image-structure/v2", - "path": "code.superseriousbusiness.org/go-jpeg-image-structure/v2/LICENSE", - "licenseText": "MIT LICENSE\n\nCopyright 2020 Dustin Oprea\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.superseriousbusiness.org/go-png-image-structure/v2", - "path": "code.superseriousbusiness.org/go-png-image-structure/v2/LICENSE", - "licenseText": "MIT LICENSE\n\nCopyright 2020 Dustin Oprea\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": "codeberg.org/gusted/mcaptcha", "path": "codeberg.org/gusted/mcaptcha/LICENSE", @@ -217,7 +72,7 @@ { "name": "connectrpc.com/connect", "path": "connectrpc.com/connect/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 2021-2025 The Connect 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" + "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 2021-2024 The Connect 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" }, { "name": "dario.cat/mergo", @@ -259,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", @@ -394,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", @@ -494,31 +324,6 @@ "path": "github.com/dsnet/compress/LICENSE.md", "licenseText": "Copyright © 2015, Joe Tsai and 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 met:\n\n* Redistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer.\n* Redistributions in binary form must reproduce the above copyright notice,\nthis list of conditions and the following disclaimer in the documentation and/or\nother materials provided with the distribution.\n* Neither the copyright holder nor the names of its contributors may be used to\nendorse or promote products derived from this software without specific prior\nwritten permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, - { - "name": "github.com/dsoprea/go-exif/v3", - "path": "github.com/dsoprea/go-exif/v3/LICENSE", - "licenseText": "MIT LICENSE\n\nCopyright 2019 Dustin Oprea\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": "github.com/dsoprea/go-iptc", - "path": "github.com/dsoprea/go-iptc/LICENSE", - "licenseText": "MIT License\n\nCopyright (c) 2020 Dustin Oprea\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/dsoprea/go-logging", - "path": "github.com/dsoprea/go-logging/LICENSE", - "licenseText": "MIT LICENSE\n\nCopyright 2020 Dustin Oprea\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": "github.com/dsoprea/go-photoshop-info-format", - "path": "github.com/dsoprea/go-photoshop-info-format/LICENSE", - "licenseText": "MIT License\n\nCopyright (c) 2020 Dustin Oprea\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/dsoprea/go-utility/v2", - "path": "github.com/dsoprea/go-utility/v2/LICENSE", - "licenseText": "Copyright 2019 Random Ingenuity InformationWorks\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": "github.com/dustin/go-humanize", "path": "github.com/dustin/go-humanize/LICENSE", @@ -609,11 +414,6 @@ "path": "github.com/go-enry/go-enry/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 [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/go-errors/errors", - "path": "github.com/go-errors/errors/LICENSE.MIT", - "licenseText": "Copyright (c) 2015 Conrad Irwin \u003cconrad@bugsnag.com\u003e\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": "github.com/go-fed/httpsig", "path": "github.com/go-fed/httpsig/LICENSE", @@ -659,11 +459,6 @@ "path": "github.com/go-webauthn/x/revoke/LICENSE", "licenseText": "Copyright (c) 2014 CloudFlare Inc.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\nRedistributions of source code must retain the above copyright notice,\nthis list of conditions and the following disclaimer.\n\nRedistributions in binary form must reproduce the above copyright notice,\nthis list of conditions and the following disclaimer in the documentation\nand/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, - { - "name": "github.com/go-xmlfmt/xmlfmt", - "path": "github.com/go-xmlfmt/xmlfmt/LICENSE", - "licenseText": "MIT License\n\nCopyright (c) 2016 go-xmlfmt\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/gobwas/glob", "path": "github.com/gobwas/glob/LICENSE", @@ -694,11 +489,6 @@ "path": "github.com/golang-jwt/jwt/v5/LICENSE", "licenseText": "Copyright (c) 2012 Dave Grijalva\nCopyright (c) 2021 golang-jwt maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n" }, - { - "name": "github.com/golang/geo", - "path": "github.com/golang/geo/LICENSE", - "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" - }, { "name": "github.com/golang/groupcache/lru", "path": "github.com/golang/groupcache/lru/LICENSE", @@ -805,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" }, { @@ -914,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", @@ -930,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." }, { @@ -944,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", @@ -964,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", @@ -989,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", @@ -1134,16 +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-APACHE", - "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/spf13/afero", - "path": "github.com/spf13/afero/LICENSE.txt", - "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" - }, { "name": "github.com/ssor/bom", "path": "github.com/ssor/bom/LICENSE", @@ -1239,21 +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": "go.yaml.in/yaml/v4", - "path": "go.yaml.in/yaml/v4/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/readerutil", - "path": "go4.org/readerutil/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", @@ -1319,11 +1064,6 @@ "path": "gopkg.in/warnings.v0/LICENSE", "licenseText": "Copyright (c) 2016 Péter Surányi.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n\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": "gopkg.in/yaml.v2", - "path": "gopkg.in/yaml.v2/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": "gopkg.in/yaml.v3", "path": "gopkg.in/yaml.v3/LICENSE", diff --git a/build/code-batch-process.go b/build/code-batch-process.go index ede43c6c46..516736b65c 100644 --- a/build/code-batch-process.go +++ b/build/code-batch-process.go @@ -74,7 +74,7 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/fixtures`)) - co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/gitea_migrations/fixtures`)) + co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/migrations/fixtures`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`services/gitdiff/testdata`)) } @@ -181,7 +181,7 @@ func parseArgs() (mainOptions map[string]string, subCmd string, subArgs []string break } } - return mainOptions, subCmd, subArgs + return } func showUsage() { 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 cfab25a5fd..0000000000 --- a/build/lint-locale-usage/allowed-masked-usage.txt +++ /dev/null @@ -1,44 +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/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. diff --git a/build/lint-locale-usage/bin/handle-go.go b/build/lint-locale-usage/bin/handle-go.go deleted file mode 100644 index b1757fa0fc..0000000000 --- a/build/lint-locale-usage/bin/handle-go.go +++ /dev/null @@ -1,177 +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" - "reflect" - "slices" - "strconv" - "strings" - - llu "forgejo.org/build/lint-locale-usage" - lluUnit "forgejo.org/models/unit/lint-locale-usage" - lluMigrate "forgejo.org/services/migrations/lint-locale-usage" -) - -// 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 HandleGoFile(handler llu.Handler, fname string, src any) error { - fset := token.NewFileSet() - node, err := goParser.ParseFile(fset, fname, src, goParser.SkipObjectResolution|goParser.ParseComments) - if err != nil { - return llu.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: - if strings.HasSuffix(fname, "models/unit/unit.go") { - lluUnit.HandleCompositeUnit(handler, fset, n2) - } - - case *ast.FuncDecl: - matchInsPrefix := handler.HandleGoCommentGroup(fset, n2.Doc, "llu:returnsTrKey") - if matchInsPrefix != nil { - 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 - }) - } - - if strings.HasSuffix(fname, "services/migrations/migrate.go") { - lluMigrate.HandleMessengerInFunc(handler, fset, n2) - } - return true - case *ast.GenDecl: - switch n2.Tok { - case token.CONST, token.VAR: - 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 - }) - } - - case token.TYPE: - // modules/web/middleware/binding.go:Validate uses the convention that structs - // entries can have tags. - // In particular, `locale:$msgid` should be handled; any fields with `form:-` shouldn't. - // Problem: we don't know which structs are forms, actually. - - for _, spec := range n2.Specs { - tspec := spec.(*ast.TypeSpec) - structNode, ok := tspec.Type.(*ast.StructType) - if !ok || !(strings.HasSuffix(tspec.Name.Name, "Form") || - (tspec.Doc != nil && - slices.ContainsFunc(tspec.Doc.List, func(c *ast.Comment) bool { - return c.Text == "// swagger:model" - }))) { - continue - } - for _, field := range structNode.Fields.List { - if field.Names == nil { - continue - } - if len(field.Names) != 1 { - handler.OnWarning(fset, field.Type.Pos(), "unsupported multiple field names") - continue - } - msgidPos := field.Names[0].NamePos - msgid := "form." + field.Names[0].Name - if field.Tag != nil && field.Tag.Kind == token.STRING { - rawTag, err := strconv.Unquote(field.Tag.Value) - if err != nil { - handler.OnWarning(fset, field.Tag.ValuePos, "invalid tag value encountered") - continue - } - tag := reflect.StructTag(rawTag) - if tag.Get("form") == "-" { - continue - } - tmp := tag.Get("locale") - if len(tmp) != 0 { - msgidPos = field.Tag.ValuePos - msgid = tmp - } - } - handler.OnMsgid(fset, msgidPos, msgid, true) - } - } - } - } - - return true - }) - - return nil -} diff --git a/build/lint-locale-usage/bin/lint-locale-usage.go b/build/lint-locale-usage/bin/lint-locale-usage.go deleted file mode 100644 index d9cdf9f321..0000000000 --- a/build/lint-locale-usage/bin/lint-locale-usage.go +++ /dev/null @@ -1,345 +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 ( - "bufio" - "errors" - "flag" - "fmt" - "go/token" - "io/fs" - "os" - "path/filepath" - "sort" - "strings" - - llu "forgejo.org/build/lint-locale-usage" - "forgejo.org/modules/container" - "forgejo.org/modules/translation/localeiter" -) - -// this works by first gathering all valid source string IDs from `en-US` reference files -// and then checking if all used source strings are actually defined - -func InitLocaleTrFunctions() map[string][]uint { - ret := make(map[string][]uint) - - f0 := []uint{0} - ret["Tr"] = f0 - ret["TrString"] = f0 - ret["TrHTML"] = f0 - - ret["TrPluralString"] = []uint{1} - ret["TrN"] = []uint{1, 2} - - return ret -} - -type StringTrie interface { - Matches(key []string) bool -} - -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) - if err != nil { - return llu.LocatedError{ - Location: fname, - Kind: "Open", - 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 - } - if linePrefix, found := strings.CutSuffix(line, "."); found { - allowedMaskedPrefixes.Insert(strings.Split(linePrefix, ".")) - } else { - if !chkMsgid(line) { - return llu.LocatedError{ - Location: fmt.Sprintf("%s: line %d", fname, lno), - Kind: "undefined msgid", - Err: errors.New(line), - } - } - usedMsgids.Add(line) - } - } - if err := scanner.Err(); err != nil { - return llu.LocatedError{ - Location: fname, - Kind: "Scanner", - Err: err, - } - } - return nil -} - -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) - } - - 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) - } - } -} - -//nolint:forbidigo -func main() { - allowMissingMsgids := false - allowUnusedMsgids := false - allowWeakMissingMsgids := true - 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) - } - 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( - &allowWeakMissingMsgids, - "allow-weak-missing-msgids", - true, - "Don't return an error code if missing 'weak' (e.g. \"form.$msgid\") message IDs are found", - ) - flag.BoolVar( - &allowUnusedMsgids, - "allow-unused-msgids", - false, - "don't return an error code if unused message IDs are found", - ) - - msgids := make(container.Set[string]) - - localeFile := filepath.Join(filepath.Join("options", "locale"), "locale_en-US.ini") - localeContent, err := os.ReadFile(localeFile) - if err != nil { - fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) - os.Exit(2) - } - - if err = localeiter.IterateMessagesContent(localeContent, func(trKey, trValue string) error { - msgids[trKey] = struct{}{} - return nil - }); err != nil { - fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) - os.Exit(2) - } - - localeFile = filepath.Join(filepath.Join("options", "locale_next"), "locale_en-US.json") - localeContent, err = os.ReadFile(localeFile) - if err != nil { - fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) - os.Exit(2) - } - - if err := localeiter.IterateMessagesNextContent(localeContent, func(trKey, pluralForm, trValue string) error { - // ignore plural form - msgids[trKey] = struct{}{} - return nil - }); err != nil { - fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) - os.Exit(2) - } - - gotAnyMsgidError := false - - 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 := llu.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, weak bool) { - if !msgids.Contains(msgid) { - if weak && allowWeakMissingMsgids { - return - } - 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: llu.InitLocaleTrFunctions(), - } - - if err := filepath.WalkDir(".", func(fpath string, d fs.DirEntry, err error) error { - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - name := d.Name() - if d.IsDir() { - if name == "docker" || name == ".git" || name == "node_modules" { - return fs.SkipDir - } - } else if name == "bindata.go" || fpath == "modules/translation/i18n/i18n_test.go" { - // skip false positives - } else if strings.HasSuffix(name, ".go") { - onError(HandleGoFile(handler, fpath, nil)) - } else if strings.HasSuffix(name, ".tmpl") { - if strings.HasPrefix(fpath, "tests") && strings.HasSuffix(name, ".ini.tmpl") { - // skip false positives - } else { - onError(handler.HandleTemplateFile(fpath, nil)) - } - } - return nil - }); err != nil { - fmt.Printf("walkdir ERROR: %s\n", err.Error()) - 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/build/lint-locale-usage/handle-go.go b/build/lint-locale-usage/handle-go.go deleted file mode 100644 index 44229e52f7..0000000000 --- a/build/lint-locale-usage/handle-go.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package lintLocaleUsage - -import ( - "fmt" - "go/ast" - "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, false) - } - } -} - -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 - } -} diff --git a/build/lint-locale-usage/handle-tmpl.go b/build/lint-locale-usage/handle-tmpl.go deleted file mode 100644 index 8d03291205..0000000000 --- a/build/lint-locale-usage/handle-tmpl.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package lintLocaleUsage - -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 := "" - switch nodeCommand.Args[0].Type() { - case tmplParser.NodeChain: - nodeChain := nodeCommand.Args[0].(*tmplParser.ChainNode) - 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] - } - - case tmplParser.NodeField: - nodeField := nodeCommand.Args[0].(*tmplParser.FieldNode) - if len(nodeField.Ident) != 2 || !(nodeField.Ident[0] == "locale" || nodeField.Ident[0] == "Locale") { - return - } - funcname = nodeField.Ident[1] - - case tmplParser.NodeVariable: - nodeVar := nodeCommand.Args[0].(*tmplParser.VariableNode) - if len(nodeVar.Ident) != 3 || !(nodeVar.Ident[0] == "$" && nodeVar.Ident[1] == "locale") { - return - } - funcname = nodeVar.Ident[2] - } - - 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, false) - - 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, false) - } 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/handler.go b/build/lint-locale-usage/handler.go deleted file mode 100644 index 6673ac3a4d..0000000000 --- a/build/lint-locale-usage/handler.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package lintLocaleUsage - -import ( - "go/token" - "strings" -) - -type LocatedError struct { - Location string - Kind string - Err error -} - -func (e LocatedError) Error() string { - var sb strings.Builder - - sb.WriteString(e.Location) - sb.WriteString(":\t") - if e.Kind != "" { - sb.WriteString(e.Kind) - sb.WriteString(": ") - } - sb.WriteString("ERROR: ") - sb.WriteString(e.Err.Error()) - - return sb.String() -} - -func InitLocaleTrFunctions() map[string][]uint { - ret := make(map[string][]uint) - - f0 := []uint{0} - ret["Tr"] = f0 - ret["TrString"] = f0 - ret["TrHTML"] = f0 - - ret["TrPluralString"] = []uint{1} - ret["TrN"] = []uint{1, 2} - - return ret -} - -type Handler struct { - OnMsgid func(fset *token.FileSet, pos token.Pos, msgid string, weak bool) - 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 -} - -// 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 - } - return s[:index], index != len(s)-1 -} diff --git a/build/lint-locale-usage/lint-locale-usage.go b/build/lint-locale-usage/lint-locale-usage.go new file mode 100644 index 0000000000..88375c1c36 --- /dev/null +++ b/build/lint-locale-usage/lint-locale-usage.go @@ -0,0 +1,383 @@ +// 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" + "io/fs" + "os" + "path/filepath" + "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 +// and then checking if all used source strings are actually defined + +type LocatedError struct { + Location string + Kind string + Err error +} + +func (e LocatedError) Error() string { + var sb strings.Builder + + sb.WriteString(e.Location) + sb.WriteString(":\t") + if e.Kind != "" { + sb.WriteString(e.Kind) + sb.WriteString(": ") + } + sb.WriteString("ERROR: ") + sb.WriteString(e.Err.Error()) + + return sb.String() +} + +func InitLocaleTrFunctions() map[string][]uint { + ret := make(map[string][]uint) + + f0 := []uint{0} + ret["Tr"] = f0 + ret["TrString"] = f0 + ret["TrHTML"] = f0 + + ret["TrPluralString"] = []uint{1} + ret["TrN"] = []uint{1, 2} + + return ret +} + +type Handler struct { + OnMsgid func(fset *token.FileSet, pos token.Pos, msgid string) + OnUnexpectedInvoke func(fset *token.FileSet, pos token.Pos, funcname string, argc int) + LocaleTrFunctions map[string][]uint +} + +// the `Handle*File` functions follow the following calling convention: +// * `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) + 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, ...)` + + call, ok := n.(*ast.CallExpr) + if !ok || len(call.Args) < 1 { + return true + } + + 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 + } + } + + if gotUnexpectedInvoke != nil { + handler.OnUnexpectedInvoke(fset, funSel.Sel.NamePos, funSel.Sel.Name, *gotUnexpectedInvoke) + } + + return true + }) + + return nil +} + +// 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: + } +} + +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) + } +} + +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 + for _, arg := range os.Args[1:] { + if arg == "--allow-missing-msgids" { + allowMissingMsgids = true + } + } + + onError := func(err error) { + if err == nil { + return + } + fmt.Println(err.Error()) + os.Exit(3) + } + + msgids := make(container.Set[string]) + + localeFile := filepath.Join(filepath.Join("options", "locale"), "locale_en-US.ini") + localeContent, err := os.ReadFile(localeFile) + if err != nil { + fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) + os.Exit(2) + } + + if err = localeiter.IterateMessagesContent(localeContent, func(trKey, trValue string) error { + msgids[trKey] = struct{}{} + return nil + }); err != nil { + fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) + os.Exit(2) + } + + localeFile = filepath.Join(filepath.Join("options", "locale_next"), "locale_en-US.json") + localeContent, err = os.ReadFile(localeFile) + if err != nil { + fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) + os.Exit(2) + } + + if err := localeiter.IterateMessagesNextContent(localeContent, func(trKey, pluralForm, trValue string) error { + // ignore plural form + msgids[trKey] = struct{}{} + return nil + }); err != nil { + fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error()) + os.Exit(2) + } + + gotAnyMsgidError := false + + handler := Handler{ + OnMsgid: func(fset *token.FileSet, pos token.Pos, msgid string) { + if !msgids.Contains(msgid) { + gotAnyMsgidError = true + fmt.Printf("%s:\tmissing msgid: %s\n", fset.Position(pos).String(), msgid) + } + }, + OnUnexpectedInvoke: func(fset *token.FileSet, pos token.Pos, funcname string, argc int) { + gotAnyMsgidError = true + fmt.Printf("%s:\tunexpected invocation of %s with %d arguments\n", fset.Position(pos).String(), funcname, argc) + }, + LocaleTrFunctions: InitLocaleTrFunctions(), + } + + if err := filepath.WalkDir(".", func(fpath string, d fs.DirEntry, err error) error { + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + name := d.Name() + if d.IsDir() { + if name == "docker" || name == ".git" || name == "node_modules" { + return fs.SkipDir + } + } else if name == "bindata.go" || fpath == "modules/translation/i18n/i18n_test.go" { + // skip false positives + } else if strings.HasSuffix(name, ".go") { + onError(handler.HandleGoFile(fpath, nil)) + } else if strings.HasSuffix(name, ".tmpl") { + if strings.HasPrefix(fpath, "tests") && strings.HasSuffix(name, ".ini.tmpl") { + // skip false positives + } else { + onError(handler.HandleTemplateFile(fpath, nil)) + } + } + return nil + }); err != nil { + fmt.Printf("walkdir ERROR: %s\n", err.Error()) + os.Exit(1) + } + + if !allowMissingMsgids && gotAnyMsgidError { + os.Exit(4) + } +} diff --git a/build/lint-locale-usage/bin/lint-locale-usage_test.go b/build/lint-locale-usage/lint-locale-usage_test.go similarity index 83% rename from build/lint-locale-usage/bin/lint-locale-usage_test.go rename to build/lint-locale-usage/lint-locale-usage_test.go index 69fc5accc6..81ca12c6db 100644 --- a/build/lint-locale-usage/bin/lint-locale-usage_test.go +++ b/build/lint-locale-usage/lint-locale-usage_test.go @@ -7,26 +7,24 @@ import ( "go/token" "testing" - llu "forgejo.org/build/lint-locale-usage" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func buildHandler(ret *[]string) llu.Handler { - return llu.Handler{ - OnMsgid: func(fset *token.FileSet, pos token.Pos, msgid string, weak bool) { +func buildHandler(ret *[]string) Handler { + return Handler{ + OnMsgid: func(fset *token.FileSet, pos token.Pos, msgid string) { *ret = append(*ret, msgid) }, OnUnexpectedInvoke: func(fset *token.FileSet, pos token.Pos, funcname string, argc int) {}, - LocaleTrFunctions: llu.InitLocaleTrFunctions(), + LocaleTrFunctions: InitLocaleTrFunctions(), } } func HandleGoFileWrapped(t *testing.T, fname, src string) []string { var ret []string handler := buildHandler(&ret) - require.NoError(t, HandleGoFile(handler, fname, src)) + require.NoError(t, handler.HandleGoFile(fname, src)) return ret } diff --git a/cmd/actions.go b/cmd/actions.go index 7a440d0290..12af2c8e86 100644 --- a/cmd/actions.go +++ b/cmd/actions.go @@ -28,7 +28,6 @@ func subcmdActionsGenRunnerToken() *cli.Command { return &cli.Command{ Name: "generate-runner-token", Usage: "Generate a new token for a runner to use to register with the server", - Before: noDanglingArgs, Action: runGenerateActionsRunnerToken, Aliases: []string{"grt"}, Flags: []cli.Flag{ diff --git a/cmd/admin.go b/cmd/admin.go index 90157e2d5a..7e06a99cda 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -37,7 +37,6 @@ func subcmdRepoSyncReleases() *cli.Command { return &cli.Command{ Name: "repo-sync-releases", Usage: "Synchronize repository releases with tags", - Before: noDanglingArgs, Action: runRepoSyncReleases, } } @@ -76,7 +75,6 @@ func subcmdSendMail() *cli.Command { return &cli.Command{ Name: "sendmail", Usage: "Send a message to all users", - Before: noDanglingArgs, Action: runSendMail, Flags: []cli.Flag{ &cli.StringFlag{ @@ -105,7 +103,7 @@ func idFlag() *cli.Int64Flag { } } -func runRepoSyncReleases(ctx context.Context, c *cli.Command) error { +func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index 0cfe7da12e..cb95b3b3c8 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -17,21 +17,11 @@ 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", Usage: "Delete specific auth source", Flags: []cli.Flag{idFlag()}, - Before: noDanglingArgs, Action: runDeleteAuth, } } @@ -40,7 +30,6 @@ func microcmdAuthList() *cli.Command { return &cli.Command{ Name: "list", Usage: "List auth sources", - Before: noDanglingArgs, Action: runListAuth, Flags: []cli.Flag{ &cli.IntFlag{ @@ -71,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 0ed7613a7e..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{ @@ -133,9 +142,8 @@ func ldapSimpleAuthCLIFlags() []cli.Flag { func microcmdAuthAddLdapBindDn() *cli.Command { return &cli.Command{ - Name: "add-ldap", - Usage: "Add new LDAP (via Bind DN) authentication source", - Before: noDanglingArgs, + Name: "add-ldap", + Usage: "Add new LDAP (via Bind DN) authentication source", Action: func(ctx context.Context, cli *cli.Command) error { return newAuthService().addLdapBindDn(ctx, cli) }, @@ -145,9 +153,8 @@ func microcmdAuthAddLdapBindDn() *cli.Command { func microcmdAuthUpdateLdapBindDn() *cli.Command { return &cli.Command{ - Name: "update-ldap", - Usage: "Update existing LDAP (via Bind DN) authentication source", - Before: noDanglingArgs, + Name: "update-ldap", + Usage: "Update existing LDAP (via Bind DN) authentication source", Action: func(ctx context.Context, cli *cli.Command) error { return newAuthService().updateLdapBindDn(ctx, cli) }, @@ -157,9 +164,8 @@ func microcmdAuthUpdateLdapBindDn() *cli.Command { func microcmdAuthAddLdapSimpleAuth() *cli.Command { return &cli.Command{ - Name: "add-ldap-simple", - Usage: "Add new LDAP (simple auth) authentication source", - Before: noDanglingArgs, + Name: "add-ldap-simple", + Usage: "Add new LDAP (simple auth) authentication source", Action: func(ctx context.Context, cli *cli.Command) error { return newAuthService().addLdapSimpleAuth(ctx, cli) }, @@ -169,9 +175,8 @@ func microcmdAuthAddLdapSimpleAuth() *cli.Command { func microcmdAuthUpdateLdapSimpleAuth() *cli.Command { return &cli.Command{ - Name: "update-ldap-simple", - Usage: "Update existing LDAP (simple auth) authentication source", - Before: noDanglingArgs, + Name: "update-ldap-simple", + Usage: "Update existing LDAP (simple auth) authentication source", Action: func(ctx context.Context, cli *cli.Command) error { return newAuthService().updateLdapSimpleAuth(ctx, cli) }, @@ -179,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 0fd8650f37..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,8 +127,7 @@ func microcmdAuthAddOauth() *cli.Command { return &cli.Command{ Name: "add-oauth", Usage: "Add new Oauth authentication source", - Before: noDanglingArgs, - Action: newAuthService().addOauth, + Action: runAddOauth, Flags: oauthCLIFlags(), } } @@ -146,8 +136,7 @@ func microcmdAuthUpdateOauth() *cli.Command { return &cli.Command{ Name: "update-oauth", Usage: "Update existing Oauth authentication source", - Before: noDanglingArgs, - Action: newAuthService().updateOauth, + Action: runUpdateOauth, Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{idFlag()}, oauthCLIFlags()[1:]...)...), } } @@ -174,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"), @@ -182,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 } @@ -202,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, @@ -210,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") } @@ -218,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 } @@ -257,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") } @@ -284,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{} @@ -321,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/admin_auth_stmp.go b/cmd/admin_auth_stmp.go index 970b613638..48b3adaac3 100644 --- a/cmd/admin_auth_stmp.go +++ b/cmd/admin_auth_stmp.go @@ -78,7 +78,6 @@ func microcmdAuthAddSMTP() *cli.Command { return &cli.Command{ Name: "add-smtp", Usage: "Add new SMTP authentication source", - Before: noDanglingArgs, Action: runAddSMTP, Flags: smtpCLIFlags(), } @@ -88,7 +87,6 @@ func microcmdAuthUpdateSMTP() *cli.Command { return &cli.Command{ Name: "update-smtp", Usage: "Update existing SMTP authentication source", - Before: noDanglingArgs, Action: runUpdateSMTP, Flags: append(smtpCLIFlags()[:1], append([]cli.Flag{idFlag()}, smtpCLIFlags()[1:]...)...), } diff --git a/cmd/admin_regenerate.go b/cmd/admin_regenerate.go index 4d14df317d..7bfd12f8f4 100644 --- a/cmd/admin_regenerate.go +++ b/cmd/admin_regenerate.go @@ -17,19 +17,17 @@ var ( microcmdRegenHooks = &cli.Command{ Name: "hooks", Usage: "Regenerate git-hooks", - Before: noDanglingArgs, Action: runRegenerateHooks, } microcmdRegenKeys = &cli.Command{ Name: "keys", Usage: "Regenerate authorized_keys file", - Before: noDanglingArgs, Action: runRegenerateKeys, } ) -func runRegenerateHooks(ctx context.Context, c *cli.Command) error { +func runRegenerateHooks(ctx context.Context, _ *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() @@ -39,7 +37,7 @@ func runRegenerateHooks(ctx context.Context, c *cli.Command) error { return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext()) } -func runRegenerateKeys(ctx context.Context, c *cli.Command) error { +func runRegenerateKeys(ctx context.Context, _ *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() diff --git a/cmd/admin_user_change_password.go b/cmd/admin_user_change_password.go index cd52a8378c..dd8c9d378a 100644 --- a/cmd/admin_user_change_password.go +++ b/cmd/admin_user_change_password.go @@ -21,7 +21,6 @@ func microcmdUserChangePassword() *cli.Command { return &cli.Command{ Name: "change-password", Usage: "Change a user's password", - Before: noDanglingArgs, Action: runChangePassword, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index e3800bdb59..96431412f6 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -23,7 +23,6 @@ func microcmdUserCreate() *cli.Command { return &cli.Command{ Name: "create", Usage: "Create a new user in database", - Before: noDanglingArgs, Action: runCreateUser, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/admin_user_delete.go b/cmd/admin_user_delete.go index affd76247f..3382c53e5f 100644 --- a/cmd/admin_user_delete.go +++ b/cmd/admin_user_delete.go @@ -40,7 +40,6 @@ func microcmdUserDelete() *cli.Command { Usage: "Purge user, all their repositories, organizations and comments", }, }, - Before: noDanglingArgs, Action: runDeleteUser, } } diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go index 0a3a7fa89d..d0f2878297 100644 --- a/cmd/admin_user_generate_access_token.go +++ b/cmd/admin_user_generate_access_token.go @@ -40,7 +40,6 @@ func microcmdUserGenerateAccessToken() *cli.Command { Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`, }, }, - Before: noDanglingArgs, Action: runGenerateAccessToken, } } diff --git a/cmd/admin_user_list.go b/cmd/admin_user_list.go index 8e1323bcf8..ccc4b8c917 100644 --- a/cmd/admin_user_list.go +++ b/cmd/admin_user_list.go @@ -18,7 +18,6 @@ func microcmdUserList() *cli.Command { return &cli.Command{ Name: "list", Usage: "List users", - Before: noDanglingArgs, Action: runListUsers, Flags: []cli.Flag{ &cli.BoolFlag{ diff --git a/cmd/admin_user_reset_mfa.go b/cmd/admin_user_reset_mfa.go index 504331100c..8107fd97bf 100644 --- a/cmd/admin_user_reset_mfa.go +++ b/cmd/admin_user_reset_mfa.go @@ -17,7 +17,6 @@ func microcmdUserResetMFA() *cli.Command { return &cli.Command{ Name: "reset-mfa", Usage: "Remove all two-factor authentication configurations for a user", - Before: noDanglingArgs, Action: runResetMFA, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/cert.go b/cmd/cert.go index baadcbda85..f9e3a16f3e 100644 --- a/cmd/cert.go +++ b/cmd/cert.go @@ -31,7 +31,6 @@ func cmdCert() *cli.Command { Usage: "Generate self-signed certificate", Description: `Generate a self-signed X.509 certificate for a TLS server. Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`, - Before: noDanglingArgs, Action: runCert, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/cmd.go b/cmd/cmd.go index 379609d294..85a482b78c 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -12,7 +12,6 @@ import ( "io" "os" "os/signal" - "slices" "strings" "syscall" @@ -41,19 +40,6 @@ func argsSet(c *cli.Command, args ...string) error { return nil } -// When a CLI command is intended to be used only with flags and no other arbitrary args, noDanglingArgs will validate -// the end-user's usage. -func noDanglingArgs(ctx context.Context, c *cli.Command) (context.Context, error) { - if c.Args().Len() != 0 { - args := c.Args().Slice() - if slices.Contains(args, "false") { - println("Hint: boolean false must be specified as a single arg, eg. '--restricted=false', not '--restricted false'") - } - return nil, fmt.Errorf("unexpected arguments: %s", strings.Join(c.Args().Slice(), ", ")) - } - return nil, nil -} - // confirm waits for user input which confirms an action func confirm() (bool, error) { var response string @@ -149,18 +135,3 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(ctx context.Context, return ctx, nil } } - -func multipleBefore(beforeFuncs ...cli.BeforeFunc) cli.BeforeFunc { - return func(ctx context.Context, cli *cli.Command) (context.Context, error) { - for _, beforeFunc := range beforeFuncs { - bctx, err := beforeFunc(ctx, cli) - if err != nil { - return bctx, err - } - if bctx != nil { - ctx = bctx - } - } - return ctx, nil - } -} diff --git a/cmd/doctor.go b/cmd/doctor.go index f557481110..681794f094 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -6,8 +6,6 @@ package cmd import ( "context" "fmt" - "image" - "io" golog "log" "os" "path/filepath" @@ -15,17 +13,13 @@ import ( "text/tabwriter" "forgejo.org/models/db" - "forgejo.org/models/gitea_migrations" - migrate_base "forgejo.org/models/gitea_migrations/base" - repo_model "forgejo.org/models/repo" - user_model "forgejo.org/models/user" + "forgejo.org/models/migrations" + migrate_base "forgejo.org/models/migrations/base" "forgejo.org/modules/container" "forgejo.org/modules/log" "forgejo.org/modules/setting" - "forgejo.org/modules/storage" "forgejo.org/services/doctor" - exif_terminator "code.superseriousbusiness.org/exif-terminator" "github.com/urfave/cli/v3" ) @@ -40,7 +34,6 @@ func cmdDoctor() *cli.Command { cmdDoctorCheck(), cmdRecreateTable(), cmdDoctorConvert(), - cmdAvatarStripExif(), }, } } @@ -50,7 +43,6 @@ func cmdDoctorCheck() *cli.Command { Name: "check", Usage: "Diagnose and optionally fix problems", Description: "A command to diagnose problems with the current Forgejo instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.", - Before: noDanglingArgs, Action: runDoctorCheck, Flags: []cli.Flag{ &cli.BoolFlag{ @@ -106,15 +98,6 @@ You should back-up your database before doing this and ensure that your database } } -func cmdAvatarStripExif() *cli.Command { - return &cli.Command{ - Name: "avatar-strip-exif", - Usage: "Strip EXIF metadata from all images in the avatar storage", - Before: noDanglingArgs, - Action: runAvatarStripExif, - } -} - func runRecreateTable(stdCtx context.Context, ctx *cli.Command) error { stdCtx, cancel := installSignals(stdCtx) defer cancel() @@ -159,7 +142,7 @@ func runRecreateTable(stdCtx context.Context, ctx *cli.Command) error { return err } - if err := gitea_migrations.EnsureUpToDate(engine); err != nil { + if err := migrations.EnsureUpToDate(engine); err != nil { return err } @@ -247,78 +230,3 @@ func runDoctorCheck(stdCtx context.Context, ctx *cli.Command) error { } return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks) } - -func runAvatarStripExif(ctx context.Context, c *cli.Command) error { - ctx, cancel := installSignals(ctx) - defer cancel() - - if err := initDB(ctx); err != nil { - return err - } - if err := storage.Init(); err != nil { - return err - } - - type HasCustomAvatarRelativePath interface { - CustomAvatarRelativePath() string - } - - doExifStrip := func(obj HasCustomAvatarRelativePath, name string, target_storage storage.ObjectStorage) error { - if obj.CustomAvatarRelativePath() == "" { - return nil - } - - log.Info("Stripping avatar for %s...", name) - - avatarFile, err := target_storage.Open(obj.CustomAvatarRelativePath()) - if err != nil { - return fmt.Errorf("storage.Avatars.Open: %w", err) - } - _, imgType, err := image.DecodeConfig(avatarFile) - if err != nil { - return fmt.Errorf("image.DecodeConfig: %w", err) - } - - // reset io.Reader for exif termination scan - _, err = avatarFile.Seek(0, io.SeekStart) - if err != nil { - return fmt.Errorf("avatarFile.Seek: %w", err) - } - - cleanedData, err := exif_terminator.Terminate(avatarFile, imgType) - if err != nil && strings.Contains(err.Error(), "cannot be processed") { - // expected error for an image type that isn't supported by exif_terminator - log.Info("... image type %s is not supported by exif_terminator, skipping.", imgType) - return nil - } else if err != nil { - return fmt.Errorf("error cleaning exif data: %w", err) - } - - if err := storage.SaveFrom(target_storage, obj.CustomAvatarRelativePath(), func(w io.Writer) error { - _, err := io.Copy(w, cleanedData) - return err - }); err != nil { - return fmt.Errorf("Failed to create dir %s: %w", obj.CustomAvatarRelativePath(), err) - } - - log.Info("... completed %s.", name) - - return nil - } - - err := db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error { - return doExifStrip(user, fmt.Sprintf("user %s", user.Name), storage.Avatars) - }) - if err != nil { - return err - } - - err = db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error { - return doExifStrip(repo, fmt.Sprintf("repo %s", repo.Name), storage.RepoAvatars) - }) - if err != nil { - return err - } - - return nil -} diff --git a/cmd/doctor_convert.go b/cmd/doctor_convert.go index 7bc70163dd..44bebae154 100644 --- a/cmd/doctor_convert.go +++ b/cmd/doctor_convert.go @@ -20,7 +20,6 @@ func cmdDoctorConvert() *cli.Command { Name: "convert", Usage: "Convert the database", Description: "A command to convert an existing MySQL database from utf8 to utf8mb4", - Before: noDanglingArgs, Action: runDoctorConvert, } } diff --git a/cmd/dump.go b/cmd/dump.go index b94277e529..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{ @@ -164,7 +108,6 @@ func cmdDump() *cli.Command { Usage: "Dump Forgejo files and database", Description: `Dump compresses all related files and database into zip file. It can be used for backup and capture Forgejo server image to send to maintainer`, - Before: noDanglingArgs, Action: runDump, Flags: []cli.Flag{ &cli.StringFlag{ @@ -311,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-*") @@ -530,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 @@ -580,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 { @@ -602,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 8e0ef0311f..eb89273e7f 100644 --- a/cmd/dump_repo.go +++ b/cmd/dump_repo.go @@ -28,7 +28,6 @@ func cmdDumpRepository() *cli.Command { Name: "dump-repo", Usage: "Dump the repository from git/github/gitea/gitlab", Description: "This is a command for dumping the repository data.", - Before: noDanglingArgs, Action: runDumpRepository, Flags: []cli.Flag{ &cli.StringFlag{ @@ -83,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/generate.go b/cmd/generate.go index a37d689dc2..7076ae541f 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -42,7 +42,6 @@ func microcmdGenerateInternalToken() *cli.Command { return &cli.Command{ Name: "INTERNAL_TOKEN", Usage: "Generate a new INTERNAL_TOKEN", - Before: noDanglingArgs, Action: runGenerateInternalToken, } } @@ -52,7 +51,6 @@ func microcmdGenerateLfsJwtSecret() *cli.Command { Name: "JWT_SECRET", Aliases: []string{"LFS_JWT_SECRET"}, Usage: "Generate a new JWT_SECRET", - Before: noDanglingArgs, Action: runGenerateLfsJwtSecret, } } @@ -61,7 +59,6 @@ func microcmdGenerateSecretKey() *cli.Command { return &cli.Command{ Name: "SECRET_KEY", Usage: "Generate a new SECRET_KEY", - Before: noDanglingArgs, Action: runGenerateSecretKey, } } 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/keys.go b/cmd/keys.go index 31be86d99b..00901903f4 100644 --- a/cmd/keys.go +++ b/cmd/keys.go @@ -21,7 +21,7 @@ func cmdKeys() *cli.Command { Name: "keys", Usage: "(internal) Should only be called by SSH server", Description: "Queries the Forgejo database to get the authorized command for a given ssh key fingerprint", - Before: multipleBefore(noDanglingArgs, PrepareConsoleLoggerLevel(log.FATAL)), + Before: PrepareConsoleLoggerLevel(log.FATAL), Action: runKeys, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/cmd/manager.go b/cmd/manager.go index 87634705ee..029329b44e 100644 --- a/cmd/manager.go +++ b/cmd/manager.go @@ -39,7 +39,6 @@ func subcmdShutdown() *cli.Command { Name: "debug", }, }, - Before: noDanglingArgs, Action: runShutdown, } } @@ -53,7 +52,6 @@ func subcmdRestart() *cli.Command { Name: "debug", }, }, - Before: noDanglingArgs, Action: runRestart, } } @@ -67,7 +65,6 @@ func subcmdReloadTemplates() *cli.Command { Name: "debug", }, }, - Before: noDanglingArgs, Action: runReloadTemplates, } } @@ -76,7 +73,6 @@ func subcmdFlushQueues() *cli.Command { return &cli.Command{ Name: "flush-queues", Usage: "Flush queues in the running process", - Before: noDanglingArgs, Action: runFlushQueues, Flags: []cli.Flag{ &cli.DurationFlag{ @@ -99,7 +95,6 @@ func subCmdProcesses() *cli.Command { return &cli.Command{ Name: "processes", Usage: "Display running processes within the current process", - Before: noDanglingArgs, Action: runProcesses, Flags: []cli.Flag{ &cli.BoolFlag{ diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go index 02c924f4c5..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"}, @@ -77,7 +72,6 @@ func subcmdLogging() *cli.Command { Name: "debug", }, }, - Before: noDanglingArgs, Action: runPauseLogging, }, { Name: "resume", @@ -87,7 +81,6 @@ func subcmdLogging() *cli.Command { Name: "debug", }, }, - Before: noDanglingArgs, Action: runResumeLogging, }, { Name: "release-and-reopen", @@ -97,7 +90,6 @@ func subcmdLogging() *cli.Command { Name: "debug", }, }, - Before: noDanglingArgs, Action: runReleaseReopenLogging, }, { Name: "remove", @@ -159,7 +151,6 @@ func subcmdLogging() *cli.Command { Usage: "Compression level to use", }, }...), - Before: noDanglingArgs, Action: runAddFileLogger, }, { Name: "conn", @@ -186,7 +177,6 @@ func subcmdLogging() *cli.Command { Usage: "Host address and port to connect to (defaults to :7020)", }, }...), - Before: noDanglingArgs, Action: runAddConnLogger, }, }, @@ -202,7 +192,6 @@ func subcmdLogging() *cli.Command { Usage: "Switch off SQL logging", }, }, - Before: noDanglingArgs, Action: runSetLogSQL, }, }, @@ -297,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/migrate.go b/cmd/migrate.go index af450d6139..5a485d17f9 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -7,7 +7,7 @@ import ( "context" "forgejo.org/models/db" - "forgejo.org/models/gitea_migrations" + "forgejo.org/models/migrations" "forgejo.org/modules/log" "forgejo.org/modules/setting" @@ -20,7 +20,6 @@ func cmdMigrate() *cli.Command { Name: "migrate", Usage: "Migrate the database", Description: "This is a command for migrating the database, so that you can run 'forgejo admin user create' before starting the server.", - Before: noDanglingArgs, Action: runMigrate, } } @@ -44,7 +43,7 @@ func runMigrate(stdCtx context.Context, ctx *cli.Command) error { if err != nil { return err } - return gitea_migrations.Migrate(masterEngine) + return migrations.Migrate(masterEngine) }); err != nil { log.Fatal("Failed to initialize ORM engine: %v", err) return err diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go index 14ce222c9b..d741a883e3 100644 --- a/cmd/migrate_storage.go +++ b/cmd/migrate_storage.go @@ -13,7 +13,7 @@ import ( actions_model "forgejo.org/models/actions" "forgejo.org/models/db" git_model "forgejo.org/models/git" - "forgejo.org/models/gitea_migrations" + "forgejo.org/models/migrations" packages_model "forgejo.org/models/packages" repo_model "forgejo.org/models/repo" user_model "forgejo.org/models/user" @@ -32,7 +32,6 @@ func cmdMigrateStorage() *cli.Command { Name: "migrate-storage", Usage: "Migrate the storage", Description: "Copies stored files from storage configured in app.ini to parameter-configured storage", - Before: noDanglingArgs, Action: runMigrateStorage, Flags: []cli.Flag{ &cli.StringFlag{ @@ -200,7 +199,7 @@ func runMigrateStorage(stdCtx context.Context, ctx *cli.Command) error { log.Info("Configuration file: %s", setting.CustomConf) if err := db.InitEngineWithMigration(context.Background(), func(e db.Engine) error { - return gitea_migrations.Migrate(e.(*xorm.Engine)) + return migrations.Migrate(e.(*xorm.Engine)) }); err != nil { log.Fatal("Failed to initialize ORM engine: %v", err) return err diff --git a/cmd/restore_repo.go b/cmd/restore_repo.go index 6c9bb218d4..0e9f0bb50a 100644 --- a/cmd/restore_repo.go +++ b/cmd/restore_repo.go @@ -19,7 +19,6 @@ func cmdRestoreRepository() *cli.Command { Name: "restore-repo", Usage: "Restore the repository from disk", Description: "This is a command for restoring the repository data.", - Before: noDanglingArgs, Action: runRestoreRepository, Flags: []cli.Flag{ &cli.StringFlag{ 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.go b/cmd/web.go index 12a8cac797..87965a7c1e 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -39,7 +39,7 @@ func cmdWeb() *cli.Command { Usage: "Start the Forgejo web server", Description: `The Forgejo web server is the only thing you need to run, and it takes care of all the other things for you`, - Before: multipleBefore(noDanglingArgs, PrepareConsoleLoggerLevel(log.INFO)), + Before: PrepareConsoleLoggerLevel(log.INFO), Action: runWeb, Flags: []cli.Flag{ &cli.StringFlag{ diff --git a/contrib/coverage-helper.sh b/contrib/coverage-helper.sh deleted file mode 100755 index 3c37e03e05..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/gitea_migrations|' # must be run before database specific tests -excluded+='forgejo.org/models/forgejo_migrations_legacy|' # 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 c68d2ebcc4..1b8d4c6697 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -12,10 +12,10 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; These values are environment-dependent but form the basis of a lot of values. They will be -;; reported as part of the default configuration when running `forgejo help` or on start-up. The order they are emitted there is slightly different but we will list them here in the order they are set-up. +;; reported as part of the default configuration when running `gitea help` or on start-up. The order they are emitted there is slightly different but we will list them here in the order they are set-up. ;; -;; - _`AppPath`_: This is the absolute path of the running Forgejo binary. -;; - _`AppWorkPath`_: This refers to "working path" of the `forgejo` binary. It is determined by using the first set thing in the following hierarchy: +;; - _`AppPath`_: This is the absolute path of the running gitea binary. +;; - _`AppWorkPath`_: This refers to "working path" of the `gitea` binary. It is determined by using the first set thing in the following hierarchy: ;; - The "WORK_PATH" option in "app.ini" file ;; - The `--work-path` flag passed to the binary ;; - The environment variable `$GITEA_WORK_DIR` @@ -54,7 +54,7 @@ APP_NAME = ; Forgejo: Beyond coding. We Forge. RUN_USER = ; git ;; ;; Application run mode, affects performance and debugging: "dev" or "prod", default is "prod" -;; Mode "dev" makes Forgejo easier to develop and debug, values other than "dev" are treated as "prod" which is for production use. +;; Mode "dev" makes Gitea easier to develop and debug, values other than "dev" are treated as "prod" which is for production use. ;RUN_MODE = prod ;; ;; The working directory, see the comment of AppWorkPath above @@ -127,7 +127,7 @@ RUN_USER = ; git ;; Permission for unix socket ;UNIX_SOCKET_PERMISSION = 666 ;; -;; Local (DMZ) URL for Forgejo workers (such as SSH update) accessing web service. In +;; Local (DMZ) URL for Gitea workers (such as SSH update) accessing web service. In ;; most cases you do not need to change the default value. Alter it only if ;; your SSH server node is not the same as HTTP node. For different protocol, the default ;; values are different. If `PROTOCOL` is `http+unix`, the default value is `http://unix/`. @@ -169,11 +169,11 @@ RUN_USER = ; git ;; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'. ;SSH_ROOT_PATH = ;; -;; Forgejo will create a authorized_keys file by default when it is not using the internal ssh server +;; Gitea will create a authorized_keys file by default when it is not using the internal ssh server ;; If you intend to use the AuthorizedKeysCommand functionality then you should turn this off. ;SSH_CREATE_AUTHORIZED_KEYS_FILE = true ;; -;; Forgejo will create a authorized_principals file by default when it is not using the internal ssh server +;; Gitea will create a authorized_principals file by default when it is not using the internal ssh server ;; If you intend to use the AuthorizedPrincipalsCommand functionality then you should turn this off. ;SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE = true ;; @@ -198,7 +198,7 @@ RUN_USER = ; git ;; default is the system temporary directory. ;SSH_KEY_TEST_PATH = ;; -;; Use `ssh-keygen` to parse public SSH keys. The value is passed to the shell. By default, Forgejo does the parsing itself. +;; Use `ssh-keygen` to parse public SSH keys. The value is passed to the shell. By default, Gitea does the parsing itself. ;SSH_KEYGEN_PATH = ;; ;; Enable SSH Authorized Key Backup when rewriting all keys, default is false @@ -220,9 +220,9 @@ RUN_USER = ; git ;; E.g."ssh- ". or "ssh- , ssh- ". ;; For more information see "TrustedUserCAKeys" in the sshd config manpages. ;SSH_TRUSTED_USER_CA_KEYS = -;; Absolute path of the `TrustedUserCaKeys` file Forgejo will manage. +;; Absolute path of the `TrustedUserCaKeys` file gitea will manage. ;; Default this `RUN_USER`/.ssh/gitea-trusted-user-ca-keys.pem -;; If you're running your own ssh server and you want to use the Forgejo managed file you'll also need to modify your +;; If you're running your own ssh server and you want to use the gitea managed file you'll also need to modify your ;; sshd_config to point to this file. The official docker image will automatically work without further configuration. ;SSH_TRUSTED_USER_CA_KEYS_FILENAME = ;; @@ -277,7 +277,7 @@ RUN_USER = ; git ;; Manual TLS settings: (Only applicable if ENABLE_ACME=false) ;; ;; Generate steps: -;; $ ./forgejo cert -ca=true -duration=8760h0m0s -host=myhost.example.com +;; $ ./gitea cert -ca=true -duration=8760h0m0s -host=myhost.example.com ;; ;; Or from a .pfx file exported from the Windows certificate store (do ;; not forget to export the private key): @@ -288,7 +288,7 @@ RUN_USER = ; git ;KEY_FILE = https/key.pem ;; ;; Root directory containing templates and static files. -;; default is the path where Forgejo is executed +;; default is the path where Gitea is executed ;STATIC_ROOT_PATH = ; Will default to the built-in value _`StaticRootPath`_ ;; ;; Default path for App data @@ -302,7 +302,7 @@ RUN_USER = ; git ;; For "serve" command it dumps to disk at PPROF_DATA_PATH as (cpuprofile|memprofile)__ ;ENABLE_PPROF = false ;; -;; PPROF_DATA_PATH, use an absolute path when you start Forgejo as service +;; PPROF_DATA_PATH, use an absolute path when you start gitea as service ;PPROF_DATA_PATH = data/tmp/pprof ; Path is relative to _`AppWorkPath`_ ;; ;; Landing page, can be "home", "explore", "organizations", "login", or any URL such as "/org/repo" or even "https://anotherwebsite.com" @@ -374,7 +374,7 @@ DB_TYPE = sqlite3 ;USER = root ;PASSWD = ;Use PASSWD = `your password` for quoting if you use special characters in the password. ;SSL_MODE = false ; either "false" (default), "true", or "skip-verify" -;CHARSET_COLLATION = ; Empty as default, Forgejo will try to find a case-sensitive collation. Don't change it unless you clearly know what you need. +;CHARSET_COLLATION = ; Empty as default, Gitea will try to find a case-sensitive collation. Don't change it unless you clearly know what you need. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; @@ -440,7 +440,7 @@ SECRET_KEY = ;; This key is VERY IMPORTANT. If you lose it, the data encrypted by it (like 2FA secret) can't be decrypted anymore. ;SECRET_KEY_URI = file:/etc/gitea/secret_key ;; -;; Secret used to validate communication within Forgejo binary. +;; Secret used to validate communication within Gitea binary. INTERNAL_TOKEN = ;; ;; Alternative location to specify internal token, instead of this file; you cannot specify both this and INTERNAL_TOKEN, and must pick one @@ -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 ;; @@ -474,9 +471,9 @@ INTERNAL_TOKEN = ;; ;; Set to false to allow users with git hook privileges to create custom git hooks. ;; Custom git hooks can be used to perform arbitrary code execution on the host operating system. -;; This enables the users to access and modify this config file and the Forgejo database and interrupt the Forgejo service. -;; By modifying the Forgejo database, users can gain Forgejo administrator privileges. -;; It also enables them to access other resources available to the user on the operating system that is running the Forgejo instance and perform arbitrary actions in the name of the Forgejo OS user. +;; This enables the users to access and modify this config file and the Gitea database and interrupt the Gitea service. +;; By modifying the Gitea database, users can gain Gitea administrator privileges. +;; It also enables them to access other resources available to the user on the operating system that is running the Gitea instance and perform arbitrary actions in the name of the Gitea OS user. ;; WARNING: This maybe harmful to you website or your operating system. ;; WARNING: Setting this to true does not change existing hooks in git repos; adjust it before if necessary. ;DISABLE_GIT_HOOKS = true @@ -484,7 +481,7 @@ INTERNAL_TOKEN = ;; Set to true to disable webhooks feature. ;DISABLE_WEBHOOKS = false ;; -;; Set to false to allow pushes to Forgejo repositories despite having an incomplete environment - NOT RECOMMENDED +;; Set to false to allow pushes to gitea repositories despite having an incomplete environment - NOT RECOMMENDED ;ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET = true ;; ;;Comma separated list of character classes required to pass minimum complexity. @@ -545,7 +542,7 @@ ENABLED = true ;; The file must contain a RSA or ECDSA private key in the PKCS8 format. If no key exists a 4096 bit key will be created for you. ;JWT_SIGNING_PRIVATE_KEY_FILE = jwt/private.pem ;; -;; OAuth2 authentication secret for access and refresh tokens, change this yourself to a unique string. CLI generate option is helpful in this case. https://forgejo.org/docs/latest/admin/command-line/#generate-secret +;; OAuth2 authentication secret for access and refresh tokens, change this yourself to a unique string. CLI generate option is helpful in this case. https://docs.gitea.io/en-us/command-line/#generate ;; This setting is only needed if JWT_SIGNING_ALGORITHM is set to HS256, HS384 or HS512. ;JWT_SECRET = ;; @@ -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 ;; @@ -670,7 +669,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; The path of git executable. If empty, Forgejo searches through the PATH environment. +;; The path of git executable. If empty, Gitea searches through the PATH environment. ;PATH = ;; ;; The HOME directory for Git @@ -755,17 +754,17 @@ LEVEL = Info ;; Whether a new user needs to be confirmed manually after registration. (Requires `REGISTER_EMAIL_CONFIRM` to be disabled.) ;REGISTER_MANUAL_CONFIRM = false ;; -;; List of domain names that are allowed to be used to register on a Forgejo instance, wildcard is supported -;; eg: forgejo.org,example.com,*.mydomain.com +;; List of domain names that are allowed to be used to register on a Gitea instance, wildcard is supported +;; eg: gitea.io,example.com,*.mydomain.com ;EMAIL_DOMAIN_ALLOWLIST = ;; -;; Comma-separated list of domain names that are not allowed to be used to register on a Forgejo instance, wildcard is supported +;; Comma-separated list of domain names that are not allowed to be used to register on a Gitea instance, wildcard is supported ;EMAIL_DOMAIN_BLOCKLIST = ;; ;; Disallow registration, only allow admins to create accounts. ;DISABLE_REGISTRATION = false ;; -;; Allow registration only using Forgejo itself, it works only when DISABLE_REGISTRATION is false +;; Allow registration only using gitea itself, it works only when DISABLE_REGISTRATION is false ;ALLOW_ONLY_INTERNAL_REGISTRATION = false ;; ;; Allow registration only using third-party services, it works only when DISABLE_REGISTRATION is false @@ -777,7 +776,7 @@ LEVEL = Info ;; Mail notification ;ENABLE_NOTIFY_MAIL = false ;; -;; This setting enables Forgejo to be signed in with HTTP BASIC Authentication using the user's password +;; This setting enables gitea to be signed in with HTTP BASIC Authentication using the user's password ;; If you set this to false you will not be able to access the tokens endpoints on the API with your password ;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token ;ENABLE_BASIC_AUTHENTICATION = true @@ -986,10 +985,7 @@ LEVEL = Info ;; Default private when using push-to-create ;DEFAULT_PUSH_CREATE_PRIVATE = true ;; -;; Global maximum creation limit of repositories for each user and organization; `-1` means no limit. If regular users -;; can create organizations (see `DISABLE_REGULAR_ORG_CREATION`) then they can bypass this by creating new organizations. -;; The global limit can be overridden by an administrator on each user and repository through their respective -;; configuration UIs. +;; Global limit of repositories per user, applied at creation time. -1 means no limit ;MAX_CREATION_LIMIT = -1 ;; ;; Preferred Licenses to place at the top of the List @@ -1012,7 +1008,7 @@ LEVEL = Info ;; Close issues as long as a commit on any branch marks it as fixed ;DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false ;; -;; Allow users to push local repositories to Forgejo and have them automatically created for a user or an org +;; Allow users to push local repositories to Gitea and have them automatically created for a user or an org ;ENABLE_PUSH_CREATE_USER = false ;ENABLE_PUSH_CREATE_ORG = false ;; @@ -1076,7 +1072,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; Path for local repository copy. Defaults to `tmp/local-repo` (content gets deleted on Forgejo restart) +;; Path for local repository copy. Defaults to `tmp/local-repo` (content gets deleted on gitea restart) ;LOCAL_COPY_PATH = tmp/local-repo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1088,7 +1084,7 @@ LEVEL = Info ;; Whether repository file uploads are enabled. Defaults to `true` ;ENABLED = true ;; -;; Path for uploads. Defaults to `data/tmp/uploads` (content gets deleted on Forgejo restart) +;; Path for uploads. Defaults to `data/tmp/uploads` (content gets deleted on gitea restart) ;TEMP_PATH = data/tmp/uploads ;; ;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types. @@ -1187,7 +1183,7 @@ LEVEL = Info ;; Sets the default trust model for repositories. Options are: collaborator, committer, collaboratorcommitter ;DEFAULT_TRUST_MODEL = collaborator ;; -;; Determines when Forgejo should sign the initial commit when creating a repository +;; Determines when gitea should sign the initial commit when creating a repository ;; Either: ;; - never ;; - pubkey: only sign if the user has a pubkey @@ -1301,7 +1297,7 @@ LEVEL = Info ;; Whether the email of the user should be shown in the Explore Users page ;SHOW_USER_EMAIL = true ;; -;; Set the default theme for the Forgejo install +;; Set the default theme for the Gitea install ;DEFAULT_THEME = forgejo-auto ;; ;; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`. @@ -1338,6 +1334,10 @@ LEVEL = Info ;; Change the sort type of the explore pages. ;; Default is "recentupdate", but you also have "alphabetically", "reverselastlogin", "newest", "oldest". ;EXPLORE_PAGING_DEFAULT_SORT = recentupdate +;; +;; The tense all timestamps should be rendered in. Possible values are `absolute` time (i.e. 1970-01-01, 11:59) and `mixed`. +;; `mixed` means most timestamps are rendered in relative time (i.e. 2 days ago). +;PREFERRED_TIMESTAMP_TENSE = mixed ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1585,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] @@ -1706,10 +1701,10 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; NOTICE: this section is for Forgejo 1.18 and later. If you are using Forgejo 1.17 or older, +;; NOTICE: this section is for Gitea 1.18 and later. If you are using Gitea 1.17 or older, ;; please refer to -;; https://codeberg.org/forgejo/forgejo/src/commit/8769df117d6cc2f4ab00d6e1d54ef4241d063f11/custom/conf/app.example.ini -;; https://codeberg.org/forgejo/forgejo/src/commit/8769df117d6cc2f4ab00d6e1d54ef4241d063f11/docs/content/doc/advanced/config-cheat-sheet.en-us.md +;; https://github.com/go-gitea/gitea/blob/release/v1.17/custom/conf/app.example.ini +;; https://github.com/go-gitea/gitea/blob/release/v1.17/docs/content/doc/advanced/config-cheat-sheet.en-us.md ;; ;ENABLED = false ;; @@ -1762,7 +1757,7 @@ LEVEL = Info ;; Sometimes it is helpful to use a different address on the envelope. Set this to use ENVELOPE_FROM as the from on the envelope. Set to `<>` to send an empty address. ;ENVELOPE_FROM = ;; -;; If Forgejo sends mails on behave of users, it will just use the name also displayed in the WebUI. If you want e.g. `John Doe (by AppName) `, +;; If gitea sends mails on behave of users, it will just use the name also displayed in the WebUI. If you want e.g. `Mister X (by CodeIt) `, ;; set it to `{{ .DisplayName }} (by {{ .AppName }})`. Available Variables: `.DisplayName`, `.AppName` and `.Domain`. ;FROM_DISPLAY_NAME_FORMAT = {{ .DisplayName }} ;; @@ -1772,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 ;; @@ -1827,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 ;; @@ -1923,7 +1912,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; How Forgejo deals with missing repository avatars +;; How Gitea deals with missing repository avatars ;; none = no avatar will be displayed; random = random avatar will be displayed; image = default image will be used ;REPOSITORY_AVATAR_FALLBACK = none ;REPOSITORY_AVATAR_FALLBACK_IMAGE = /img/repo_default.png @@ -2041,7 +2030,7 @@ LEVEL = Info ;; ;; Setting this to true will enable all cron tasks periodically with default settings. ;ENABLED = false -;; Setting this to true will run all enabled cron tasks when Forgejo starts. +;; Setting this to true will run all enabled cron tasks when Gitea starts. ;RUN_AT_START = false ;; ;; Note: ``SCHEDULE`` accept formats @@ -2081,7 +2070,7 @@ LEVEL = Info ;SCHEDULE = @every 10m ;; Enable running Update mirrors task periodically. ;ENABLED = true -;; Run Update mirrors task when Forgejo starts. +;; Run Update mirrors task when Gitea starts. ;RUN_AT_START = false ;; Notice if not success ;NOTICE_ON_SUCCESS = false @@ -2102,7 +2091,7 @@ LEVEL = Info ;SCHEDULE = @midnight ;; Enable running Repository health check task periodically. ;ENABLED = true -;; Run Repository health check task when Forgejo starts. +;; Run Repository health check task when Gitea starts. ;RUN_AT_START = false ;; Notice if not success ;NOTICE_ON_SUCCESS = false @@ -2120,7 +2109,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Enable running check repository statistics task periodically. ;ENABLED = true -;; Run check repository statistics task when Forgejo starts. +;; Run check repository statistics task when Gitea starts. ;RUN_AT_START = true ;; Notice if not success ;NOTICE_ON_SUCCESS = false @@ -2275,7 +2264,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Update the '.ssh/authorized_keys' file with Forgejo SSH keys +;; Update the '.ssh/authorized_keys' file with Gitea SSH keys ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[cron.resync_all_sshkeys] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2348,7 +2337,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Check for new Forgejo versions +;; Check for new Gitea versions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[cron.update_checker] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2432,7 +2421,6 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The first locale will be used as the default if user browser's language doesn't match any locale in the list. -;; en-US/English is the base language used for translations and must be in the list, even if not in first position. ;LANGS = en-US,zh-CN,zh-HK,zh-TW,da,de-DE,nds,fr-FR,nl-NL,lv-LV,ru-RU,uk-UA,ja-JP,es-ES,pt-BR,pt-PT,pl-PL,bg,it-IT,fi-FI,fil,eo,tr-TR,cs-CZ,sl,sv-SE,ko-KR,el-GR,fa-IR,hu-HU,id-ID ;NAMES = English,简体中文,繁體中文(香港),繁體中文(台灣),Dansk,Deutsch,Plattdüütsch,Français,Nederlands,Latviešu,Русский,Українська,日本語,Español,Português do Brasil,Português de Portugal,Polski,Български,Italiano,Suomi,Filipino,Esperanto,Türkçe,Čeština,Slovenščina,Svenska,한국어,Ελληνικά,فارسی,Magyar nyelv,Bahasa Indonesia @@ -2449,7 +2437,7 @@ LEVEL = Info ;[other] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Show version information about Forgejo and Go in the footer +;; Show version information about Gitea and Go in the footer ;SHOW_FOOTER_VERSION = true ;; Show template execution time in the footer ;SHOW_FOOTER_TEMPLATE_LOAD_TIME = true @@ -2696,7 +2684,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; settings for Forgejo's LFS client (eg: mirroring an upstream lfs endpoint) +;; settings for Gitea's LFS client (eg: mirroring an upstream lfs endpoint) ;; ;[lfs_client] ;; Limit the number of pointers in each batch request to this number @@ -2782,10 +2770,6 @@ LEVEL = Info ;SKIP_WORKFLOW_STRINGS = [skip ci],[ci skip],[no ci],[skip actions],[actions skip] ;; Limit on inputs for manual / workflow_dispatch triggers, default is 10 ;LIMIT_DISPATCH_INPUTS = 10 -;; Support queuing workflow jobs, by setting `concurrency.group` & `concurrency.cancel-in-progress: false`, can increase -;; server and database workload due to more complex database queries and more frequent server task querying; this -;; feature can be disabled to reduce performance impact -;CONCURRENCY_GROUP_QUEUE_ENABLED = true ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/eslint.config.mjs b/eslint.config.mjs index 61987aef14..28cfa80089 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -58,14 +58,7 @@ export default tseslint.config( sourceType: 'module', }, rules: { - '@typescript-eslint/no-unused-vars': [2, { - args: 'all', - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', - caughtErrorsIgnorePattern: '^_', - destructuredArrayIgnorePattern: '^_', - ignoreRestSiblings: false, - }], + '@typescript-eslint/no-unused-vars': 'off', // TODO: enable this rule again '@eslint-community/eslint-comments/disable-enable-pair': [2], '@eslint-community/eslint-comments/no-aggregating-enable': [2], @@ -160,7 +153,7 @@ export default tseslint.config( '@stylistic/quotes': [2, 'single', { avoidEscape: true, - allowTemplateLiterals: 'always', + allowTemplateLiterals: true, }], '@stylistic/rest-spread-spacing': [2, 'never'], @@ -546,7 +539,14 @@ export default tseslint.config( 'no-unused-labels': [2], 'no-unused-private-class-members': [2], - 'no-unused-vars': [0], + 'no-unused-vars': [2, { + args: 'all', + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + destructuredArrayIgnorePattern: '^_', + ignoreRestSiblings: false, + }], 'no-use-before-define': [2, { functions: false, @@ -703,6 +703,7 @@ export default tseslint.config( 'sonarjs/no-inverted-boolean-check': [2], 'sonarjs/no-nested-switch': [0], 'sonarjs/no-nested-template-literals': [0], + 'sonarjs/no-one-iteration-loop': [2], 'sonarjs/no-redundant-boolean': [2], 'sonarjs/no-redundant-jump': [2], 'sonarjs/no-same-line-conditional': [2], @@ -798,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], @@ -1082,7 +1082,6 @@ export default tseslint.config( '@vitest/no-standalone-expect': [0], '@vitest/no-test-prefixes': [0], '@vitest/no-test-return-statement': [0], - '@vitest/prefer-called-exactly-once-with': [2], '@vitest/prefer-called-with': [0], '@vitest/prefer-comparison-matcher': [0], '@vitest/prefer-each': [0], @@ -1090,7 +1089,6 @@ export default tseslint.config( '@vitest/prefer-expect-resolves': [0], '@vitest/prefer-hooks-in-order': [0], '@vitest/prefer-hooks-on-top': [2], - '@vitest/prefer-import-in-mock': [0], '@vitest/prefer-lowercase-title': [0], '@vitest/prefer-mock-promise-shorthand': [0], '@vitest/prefer-snapshot-hint': [0], diff --git a/go.mod b/go.mod index d70f517653..dc547bccca 100644 --- a/go.mod +++ b/go.mod @@ -1,42 +1,38 @@ module forgejo.org -go 1.24.0 +go 1.24 -toolchain go1.25.3 +toolchain go1.24.6 require ( - code.forgejo.org/f3/gof3/v3 v3.11.1 + code.forgejo.org/f3/gof3/v3 v3.11.0 code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 code.forgejo.org/forgejo/go-rpmutils v1.0.0 code.forgejo.org/forgejo/levelqueue v1.0.0 code.forgejo.org/forgejo/reply v1.0.2 - code.forgejo.org/forgejo/runner/v11 v11.2.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 code.forgejo.org/go-chi/session v1.0.2 code.gitea.io/actions-proto-go v0.4.0 code.gitea.io/sdk/gitea v0.21.0 - code.superseriousbusiness.org/exif-terminator v0.11.0 - code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 - connectrpc.com/connect v1.19.1 + connectrpc.com/connect v1.18.1 github.com/42wim/httpsig v1.2.3 github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/ProtonMail/go-crypto v1.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 github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 - github.com/dsoprea/go-exif/v3 v3.0.1 github.com/dustin/go-humanize v1.0.1 github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 github.com/emersion/go-imap v1.2.1 @@ -44,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.22.0 + github.com/go-openapi/spec v0.21.0 github.com/go-sql-driver/mysql v1.9.3 - github.com/go-webauthn/webauthn v0.14.0 + 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-20250923004556-9e5a51aed1e8 + 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.5 + 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 @@ -91,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/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.143.2 - go.uber.org/mock v0.6.0 - go.yaml.in/yaml/v3 v3.0.4 - golang.org/x/crypto v0.43.0 - golang.org/x/image v0.32.0 - golang.org/x/net v0.46.0 - golang.org/x/oauth2 v0.32.0 - golang.org/x/sync v0.17.0 - golang.org/x/sys v0.37.0 - golang.org/x/text v0.30.0 - google.golang.org/protobuf v1.36.10 + gitlab.com/gitlab-org/api/client-go v0.130.1 + go.uber.org/mock v0.5.2 + golang.org/x/crypto v0.39.0 + golang.org/x/image v0.27.0 + golang.org/x/net v0.41.0 + golang.org/x/oauth2 v0.30.0 + golang.org/x/sync v0.15.0 + golang.org/x/sys v0.33.0 + golang.org/x/text v0.26.0 + google.golang.org/protobuf v1.36.4 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 + 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 @@ -119,14 +117,12 @@ require ( require ( cloud.google.com/go/compute/metadata v0.6.0 // indirect - code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.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.3 // indirect - github.com/andybalholm/brotli v1.2.0 // 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 @@ -149,53 +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.9.1 // indirect - github.com/bodgit/plumbing v1.3.0 // indirect - github.com/bodgit/sevenzip v1.6.1 // 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/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb // indirect - github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect - github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c // indirect - github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // 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-errors/errors v1.4.2 // 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.3 // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-openapi/jsonpointer v0.22.1 // indirect - github.com/go-openapi/jsonreference v0.21.2 // indirect - github.com/go-openapi/swag/conv v0.25.1 // indirect - github.com/go-openapi/swag/jsonname v0.25.1 // indirect - github.com/go-openapi/swag/jsonutils v0.25.1 // indirect - github.com/go-openapi/swag/loading v0.25.1 // indirect - github.com/go-openapi/swag/stringutils v0.25.1 // indirect - github.com/go-openapi/swag/typeutils v0.25.1 // indirect - github.com/go-openapi/swag/yamlutils v0.25.1 // indirect - github.com/go-webauthn/x v0.1.25 // indirect - github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect + github.com/go-openapi/jsonpointer v0.21.1 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect + github.com/go-webauthn/x v0.1.21 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // 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 @@ -206,77 +184,68 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-retryablehttp v0.7.8 // 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-runewidth v0.0.17 // indirect - github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mholt/acmez/v3 v3.1.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.1 // 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.2.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/pierrec/lz4/v4 v4.1.22 // indirect + github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/rhysd/actionlint v1.7.8 // 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.8 // indirect - github.com/spf13/afero v1.15.0 // 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 - go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect - go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.38.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.v2 v2.4.0 // 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 replace git.sr.ht/~mariusor/go-xsd-duration => code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078 - -replace xorm.io/xorm v1.3.9 => code.forgejo.org/xorm/xorm v1.3.9-forgejo.3 diff --git a/go.sum b/go.sum index 31a84a4cd0..d1630b622d 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.1 h1:c0vE8XvqpbXuSv8gzttn96k5T2FQi0u9bYnux46qSAs= -code.forgejo.org/f3/gof3/v3 v3.11.1/go.mod h1:1p2UKrqZiwxKneQF2DKrMnc403YIgR/lfcfvadZtmDs= +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.2.0 h1:juvrGBxQG3bNDKDc+3SorYY0sx9sgPcta122K/l+Ids= -code.forgejo.org/forgejo/runner/v11 v11.2.0/go.mod h1:hCiUsUtsvV7wICou9Gk2aV0kGn87NhhirCcx7WRqfZw= 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= @@ -40,25 +26,16 @@ code.forgejo.org/go-chi/captcha v1.0.2 h1:vyHDPXkpjDv8bLO9NqtWzZayzstD/WpJ5xwEkA code.forgejo.org/go-chi/captcha v1.0.2/go.mod h1:lxiPLcJ76UCZHoH31/Wbum4GUi2NgjfFZLrJkKv1lLE= code.forgejo.org/go-chi/session v1.0.2 h1:pG+AXre9L9VXJmTaADXkmeEPuRalhmBXyv6tG2Rvjcc= code.forgejo.org/go-chi/session v1.0.2/go.mod h1:HnEGyBny7WPzCiVLP2vzL5ssma+3gCSl/vLpuVNYrqc= -code.forgejo.org/xorm/xorm v1.3.9-forgejo.3 h1:7KW1+3cr2YIZCS85TM+imdsUe8OdNsfV3F8iP2t36rM= -code.forgejo.org/xorm/xorm v1.3.9-forgejo.3/go.mod h1:5ouTxqMcalQUvlBpQynRpzu/44GwaMpkA1nU+encsDE= code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU= code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4= code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA= -code.superseriousbusiness.org/exif-terminator v0.11.0 h1:Hof0MCcsa+1fS17gf86fTTZ8AQnMY9h9kzcc+2C6mVg= -code.superseriousbusiness.org/exif-terminator v0.11.0/go.mod h1:9sutT1axa/kSdlPLlRFjCNKmyo/KNx8eX3XZvWBlAEY= -code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0 h1:r9uq8StaSHYKJ8DklR9Xy+E9c40G1Z8yj5TRGi8L6+4= -code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0/go.mod h1:IK1OlR6APjVB3E9tuYGvf0qXMrwP+TrzcHS5rf4wffQ= -code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 h1:I512jiIeXDC4//2BeSPrRM2ZS4wpBKUaPeTPxakMNGA= -code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0/go.mod h1:SNHomXNW88o1pFfLHpD4KsCZLfcr4z5dm+xcX5SV10A= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= -connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= -connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= -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= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 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= @@ -71,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= @@ -82,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.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4= -github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk= 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.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= -github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +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= @@ -149,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.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= -github.com/bmatcuk/doublestar/v4 v4.9.1/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.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4= -github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8= -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= @@ -168,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= @@ -182,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= @@ -212,37 +173,16 @@ 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= -github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= -github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8= -github.com/dsoprea/go-exif/v3 v3.0.0-20210428042052-dca55bf8ca15/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= -github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk= -github.com/dsoprea/go-exif/v3 v3.0.0-20221003160559-cf5cd88aa559/go.mod h1:rW6DMEv25U9zCtE5ukC7ttBRllXj7g7TAHl7tQrT5No= -github.com/dsoprea/go-exif/v3 v3.0.0-20221003171958-de6cb6e380a8/go.mod h1:akyZEJZ/k5bmbC9gA612ZLQkcED8enS9vuTiuAkENr0= -github.com/dsoprea/go-exif/v3 v3.0.1 h1:/IE4iW7gvY7BablV1XY0unqhMv26EYpOquVMwoBo/wc= -github.com/dsoprea/go-exif/v3 v3.0.1/go.mod h1:10HkA1Wz3h398cDP66L+Is9kKDmlqlIJGPv8pk4EWvc= -github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb h1:gwjJjUr6FY7zAWVEueFPrcRHhd9+IK81TcItbqw2du4= -github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM= -github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA= -github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8= -github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg= -github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8= -github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c h1:7j5aWACOzROpr+dvMtu8GnI97g9ShLWD72XIELMgn+c= -github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c/go.mod h1:pqKB+ijp27cEcrHxhXVgUUMlSDRuGJJp1E+20Lj5H0E= -github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8= -github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU= -github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LVjRU0RNUuMDqkPTxcALio0LWPFPXxxFCvVGVAwEpFc= -github.com/dsoprea/go-utility/v2 v2.0.0-20221003160719-7bc88537c05e/go.mod h1:VZ7cB0pTjm1ADBWhJUOHESu4ZYy9JN+ZPqjfiW09EPU= -github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje0z+3UQ5YjYiSRRzVdtamFpvBQXKwMglWqw= -github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs= 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= @@ -252,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= @@ -264,33 +202,27 @@ 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= github.com/go-enry/go-enry/v2 v2.9.2/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8= github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= -github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -299,47 +231,29 @@ 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.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8= -github.com/go-git/go-git/v5 v5.16.3/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= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= -github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= -github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= -github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU= -github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ= -github.com/go-openapi/spec v0.22.0 h1:xT/EsX4frL3U09QviRIZXvkh80yibxQmtoEvyqug0Tw= -github.com/go-openapi/spec v0.22.0/go.mod h1:K0FhKxkez8YNS94XzF8YKEMULbFrRw4m15i2YUht4L0= -github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0= -github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs= -github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= -github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= -github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8= -github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1 h1:DSQGcdB6G0N9c/KhtpYc71PzzGEIc/fZ1no35x4/XBY= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1/go.mod h1:kjmweouyPwRUEYMSrbAidoLMGeJ5p6zdHi9BgZiqmsg= -github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw= -github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc= -github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw= -github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg= -github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA= -github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8= -github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk= -github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= +github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= +github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-webauthn/webauthn v0.14.0 h1:ZLNPUgPcDlAeoxe+5umWG/tEeCoQIDr7gE2Zx2QnhL0= -github.com/go-webauthn/webauthn v0.14.0/go.mod h1:QZzPFH3LJ48u5uEPAu+8/nWJImoLBWM7iAH/kSVSo6k= -github.com/go-webauthn/x v0.1.25 h1:g/0noooIGcz/yCVqebcFgNnGIgBlJIccS+LYAa+0Z88= -github.com/go-webauthn/x v0.1.25/go.mod h1:ieblaPY1/BVCV0oQTsA/VAo08/TWayQuJuo5Q+XxmTY= -github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo= -github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= +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= @@ -351,30 +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/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= -github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= -github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo= -github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= -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= @@ -384,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= @@ -408,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-20250923004556-9e5a51aed1e8 h1:ZI8gCoCjGzPsum4L21jHdQs8shFBIQih1TM9Rd/c+EQ= -github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= -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= @@ -440,10 +331,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= -github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= -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/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/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= @@ -451,34 +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/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -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= @@ -495,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= @@ -504,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.17 h1:78v8ZlW0bP43XfmAfPsdXcoNCelfMHsDmd/pkENfrjQ= -github.com/mattn/go-runewidth v0.0.17/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-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY= +github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g= github.com/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.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ= -github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4= 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.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A= -github.com/minio/minlz v1.0.1/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= @@ -549,21 +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/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -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.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A= -github.com/nwaples/rardecode/v2 v2.2.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= @@ -581,10 +455,11 @@ 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/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= -github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +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= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -597,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= @@ -606,23 +480,21 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rhysd/actionlint v1.7.8 h1:3d+N9ourgAxVYG4z2IFxFIk/YiT6V+VnKASfXGwT60E= -github.com/rhysd/actionlint v1.7.8/go.mod h1:3kiS6egcbXG+vQsJIhFxTz+UKaF1JprsE0SKrpCZKvU= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rhysd/actionlint v1.6.27 h1:xxwe8YmveBcC8lydW6GoHMGmB6H/MTqUU60F2p10wjw= +github.com/rhysd/actionlint v1.6.27/go.mod h1:m2nFUjAnOrxCMXuOMz9evYBRCLUsMnKY2IJl/N5umbk= github.com/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= @@ -631,19 +503,13 @@ 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.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik= -github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU= -github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= -github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +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= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -653,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.9/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/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= @@ -677,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= @@ -687,36 +556,24 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -gitlab.com/gitlab-org/api/client-go v0.143.2 h1:tfmUW8u+G/DGKOB/FDR0c06f0RVUAEe0ym8WpLoiHXI= -gitlab.com/gitlab-org/api/client-go v0.143.2/go.mod h1:gJn5yLx9vYGXr73Yv0ueHWCVl+fL8iUOgJFxC7qV+iM= -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= +gitlab.com/gitlab-org/api/client-go v0.130.1 h1:1xF5C5Zq3sFeNg3PzS2z63oqrxifne3n/OnbI7nptRc= +gitlab.com/gitlab-org/api/client-go v0.130.1/go.mod h1:ZhSxLAWadqP6J9lMh40IAZOlOxBLPRh7yFOXR/bMJWM= +go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.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= -go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s= -go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= -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= @@ -726,69 +583,28 @@ 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.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -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.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ= -golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc= -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/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.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.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= -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-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= @@ -796,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.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= -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.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= -golang.org/x/oauth2 v0.32.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= @@ -818,39 +625,27 @@ 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= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/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= @@ -858,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.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.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= @@ -869,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.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= -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= @@ -884,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.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -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.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.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.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +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.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +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= @@ -986,32 +721,36 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A= -modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I= -modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= -modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= -modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.39.1 h1:H+/wGFzuSCIEVCvXYVHX5RQglwhMOvtHSv+VtidL2r4= -modernc.org/sqlite v1.39.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= +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= +modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= +modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= +modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= +modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= +modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= +modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE= +modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= +modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +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= +xorm.io/xorm v1.3.9/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw= diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 492e3f6cea..95c4bead8f 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}) } 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 2d83806b98..69592120e9 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -21,27 +21,15 @@ 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" ) -type ConcurrencyMode int - -const ( - // Don't enforce concurrency control. Note that you won't find `UnlimitedConcurrency` implemented directly in the - // code; setting it on an `ActionRun` prevents the other limiting behaviors. - UnlimitedConcurrency ConcurrencyMode = iota - // Queue behind other jobs with the same concurrency group - QueueBehind - // Cancel other jobs with the same concurrency group - CancelInProgress -) - // ActionRun represents a run of a workflow file type ActionRun struct { ID int64 Title string - RepoID int64 `xorm:"index unique(repo_index) index(concurrency)"` + RepoID int64 `xorm:"index unique(repo_index)"` Repo *repo_model.Repository `xorm:"-"` OwnerID int64 `xorm:"index"` WorkflowID string `xorm:"index"` // the name of workflow file @@ -68,11 +56,6 @@ type ActionRun struct { Created timeutil.TimeStamp `xorm:"created"` Updated timeutil.TimeStamp `xorm:"updated"` NotifyEmail bool - - ConcurrencyGroup string `xorm:"'concurrency_group' index(concurrency)"` - ConcurrencyType ConcurrencyMode - - PreExecutionError string `xorm:"LONGTEXT"` // used to report errors that blocked execution of a workflow } func init() { @@ -180,24 +163,6 @@ func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, err return nil, fmt.Errorf("event %s is not a pull request event", run.Event) } -func (run *ActionRun) SetConcurrencyGroup(concurrencyGroup string) { - // Concurrency groups are case insensitive identifiers, implemented by collapsing case here. Unfortunately the - // `ConcurrencyGroup` field can't be made a private field because xorm doesn't map those fields -- using - // `SetConcurrencyGroup` is required for consistency but not enforced at compile-time. - run.ConcurrencyGroup = strings.ToLower(concurrencyGroup) -} - -func (run *ActionRun) SetDefaultConcurrencyGroup() { - // Before ConcurrencyGroups were supported, Forgejo would automatically cancel runs with matching git refs, workflow - // IDs, and trigger events. For backwards compatibility we emulate that behavior: - run.SetConcurrencyGroup(fmt.Sprintf( - "%s_%s_%s__auto", - run.Ref, - run.WorkflowID, - run.TriggerEvent, - )) -} - func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error { _, err := db.GetEngine(ctx).ID(repo.ID). SetExpr("num_action_runs", @@ -218,7 +183,6 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err ), ), ). - Cols("num_action_runs", "num_closed_action_runs"). Update(repo) return err } @@ -345,26 +309,15 @@ func GetLatestRunForBranchAndWorkflow(ctx context.Context, repoID int64, branch, } func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) { - run, has, err := GetRunByIDWithHas(ctx, id) + var run ActionRun + has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run) if err != nil { return nil, err } else if !has { return nil, fmt.Errorf("run with id %d: %w", id, util.ErrNotExist) } - return run, nil -} - -func GetRunByIDWithHas(ctx context.Context, id int64) (*ActionRun, bool, error) { - var run ActionRun - has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run) - if err != nil { - return nil, false, err - } else if !has { - return nil, false, nil - } - - return &run, true, nil + return &run, nil } func GetRunByIndex(ctx context.Context, repoID, index int64) (*ActionRun, error) { diff --git a/models/actions/run_job.go b/models/actions/run_job.go index edd1170072..1fadb4b7c7 100644 --- a/models/actions/run_job.go +++ b/models/actions/run_job.go @@ -44,38 +44,6 @@ func init() { db.RegisterModel(new(ActionRunJob)) } -func (job *ActionRunJob) HTMLURL(ctx context.Context) (string, error) { - if job.Run == nil || job.Run.Repo == nil { - return "", fmt.Errorf("action_run_job: load run and repo before accessing HTMLURL") - } - - // Find the "index" of the currently selected job... kinda ugly that the URL uses the index rather than some other - // unique identifier of the job which could actually be stored upon it. But hard to change that now. - allJobs, err := GetRunJobsByRunID(ctx, job.RunID) - if err != nil { - return "", err - } - jobIndex := -1 - for i, otherJob := range allJobs { - if job.ID == otherJob.ID { - jobIndex = i - break - } - } - if jobIndex == -1 { - return "", fmt.Errorf("action_run_job: unable to find job on run: %d", job.ID) - } - - attempt := job.Attempt - // If a job has never been fetched by a runner yet, it will have attempt 0 -- but this attempt will never have a - // valid UI since attempt is incremented to 1 if it is picked up by a runner. - if attempt == 0 { - attempt = 1 - } - - return fmt.Sprintf("%s/actions/runs/%d/jobs/%d/attempt/%d", job.Run.Repo.HTMLURL(), job.Run.Index, jobIndex, attempt), nil -} - func (job *ActionRunJob) Duration() time.Duration { return calculateDuration(job.Started, job.Stopped, job.Status) } diff --git a/models/actions/run_job_test.go b/models/actions/run_job_test.go index 6abdb2bf5c..50a4ba10d8 100644 --- a/models/actions/run_job_test.go +++ b/models/actions/run_job_test.go @@ -3,14 +3,9 @@ package actions import ( - "fmt" "testing" - "forgejo.org/models/db" - "forgejo.org/models/unittest" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestActionRunJob_ItRunsOn(t *testing.T) { @@ -32,41 +27,3 @@ func TestActionRunJob_ItRunsOn(t *testing.T) { assert.False(t, actionJob.ItRunsOn(agentLabels)) } - -func TestActionRunJob_HTMLURL(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - tests := []struct { - id int64 - expected string - }{ - { - id: 192, - expected: "https://try.gitea.io/user5/repo4/actions/runs/187/jobs/0/attempt/1", - }, - { - id: 393, - expected: "https://try.gitea.io/user2/repo1/actions/runs/187/jobs/1/attempt/1", - }, - { - id: 394, - expected: "https://try.gitea.io/user2/repo1/actions/runs/187/jobs/2/attempt/2", - }, - } - - for _, tt := range tests { - t.Run(fmt.Sprintf("id=%d", tt.id), func(t *testing.T) { - var job ActionRunJob - has, err := db.GetEngine(t.Context()).Where("id=?", tt.id).Get(&job) - require.NoError(t, err) - require.True(t, has, "load ActionRunJob from fixture") - - err = job.LoadAttributes(t.Context()) - require.NoError(t, err) - - url, err := job.HTMLURL(t.Context()) - require.NoError(t, err) - assert.Equal(t, tt.expected, url) - }) - } -} diff --git a/models/actions/run_list.go b/models/actions/run_list.go index 174d2aa70c..92be510569 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -5,7 +5,6 @@ package actions import ( "context" - "strings" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" @@ -65,15 +64,14 @@ func (runs RunList) LoadRepos(ctx context.Context) error { type FindRunOptions struct { db.ListOptions - RepoID int64 - OwnerID int64 - WorkflowID string - Ref string // the commit/tag/… that caused this workflow - TriggerUserID int64 - TriggerEvent webhook_module.HookEventType - Approved bool // not util.OptionalBool, it works only when it's true - Status []Status - ConcurrencyGroup string + RepoID int64 + OwnerID int64 + WorkflowID string + Ref string // the commit/tag/… that caused this workflow + TriggerUserID int64 + TriggerEvent webhook_module.HookEventType + Approved bool // not util.OptionalBool, it works only when it's true + Status []Status } func (opts FindRunOptions) ToConds() builder.Cond { @@ -102,9 +100,6 @@ func (opts FindRunOptions) ToConds() builder.Cond { if opts.TriggerEvent != "" { cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent}) } - if opts.ConcurrencyGroup != "" { - cond = cond.And(builder.Eq{"concurrency_group": strings.ToLower(opts.ConcurrencyGroup)}) - } return cond } diff --git a/models/actions/run_test.go b/models/actions/run_test.go index 834d1595ae..c9a552a2b2 100644 --- a/models/actions/run_test.go +++ b/models/actions/run_test.go @@ -5,84 +5,7 @@ package actions import ( "testing" - - repo_model "forgejo.org/models/repo" - "forgejo.org/models/unittest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestGetRunBefore(t *testing.T) { } - -func TestSetConcurrencyGroup(t *testing.T) { - run := ActionRun{} - run.SetConcurrencyGroup("abc123") - assert.Equal(t, "abc123", run.ConcurrencyGroup) - run.SetConcurrencyGroup("ABC123") // case should collapse in SetConcurrencyGroup - assert.Equal(t, "abc123", run.ConcurrencyGroup) -} - -func TestSetDefaultConcurrencyGroup(t *testing.T) { - run := ActionRun{ - Ref: "refs/heads/main", - WorkflowID: "testing", - TriggerEvent: "pull_request", - } - run.SetDefaultConcurrencyGroup() - assert.Equal(t, "refs/heads/main_testing_pull_request__auto", run.ConcurrencyGroup) - run = ActionRun{ - Ref: "refs/heads/main", - WorkflowID: "TESTING", // case should collapse in SetDefaultConcurrencyGroup - TriggerEvent: "pull_request", - } - run.SetDefaultConcurrencyGroup() - assert.Equal(t, "refs/heads/main_testing_pull_request__auto", run.ConcurrencyGroup) -} - -func TestUpdateRepoRunsNumbers(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - t.Run("Normal", func(t *testing.T) { - t.Run("Repo 1", func(t *testing.T) { - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - - require.NoError(t, updateRepoRunsNumbers(t.Context(), repo)) - - repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - assert.Equal(t, 1, repo.NumActionRuns) - assert.Equal(t, 1, repo.NumClosedActionRuns) - }) - - t.Run("Repo 4", func(t *testing.T) { - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) - - require.NoError(t, updateRepoRunsNumbers(t.Context(), repo)) - - repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) - assert.Equal(t, 4, repo.NumActionRuns) - assert.Equal(t, 4, repo.NumClosedActionRuns) - }) - - t.Run("Repo 63", func(t *testing.T) { - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 63}) - - require.NoError(t, updateRepoRunsNumbers(t.Context(), repo)) - - repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 63}) - assert.Equal(t, 3, repo.NumActionRuns) - assert.Equal(t, 2, repo.NumClosedActionRuns) - }) - }) - - t.Run("Columns specifc", func(t *testing.T) { - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - repo.Name = "ishouldnotbeupdated" - - require.NoError(t, updateRepoRunsNumbers(t.Context(), repo)) - - repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - assert.Equal(t, "repo1", repo.Name) - }) -} diff --git a/models/actions/task.go b/models/actions/task.go index 569fc2bb33..93369db7e8 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -17,8 +17,8 @@ import ( "forgejo.org/modules/timeutil" "forgejo.org/modules/util" - "code.forgejo.org/forgejo/runner/v11/act/jobparser" lru "github.com/hashicorp/golang-lru/v2" + "github.com/nektos/act/pkg/jobparser" "xorm.io/builder" ) @@ -148,21 +148,6 @@ func (task *ActionTask) GenerateToken() (err error) { return err } -// Retrieve all the attempts from the same job as the target `ActionTask`. Limited fields are queried to avoid loading -// the LogIndexes blob when not needed. -func (task *ActionTask) GetAllAttempts(ctx context.Context) ([]*ActionTask, error) { - var attempts []*ActionTask - err := db.GetEngine(ctx). - Cols("id", "attempt", "status", "started"). - Where("job_id=?", task.JobID). - Desc("attempt"). - Find(&attempts) - if err != nil { - return nil, err - } - return attempts, nil -} - func GetTaskByID(ctx context.Context, id int64) (*ActionTask, error) { var task ActionTask has, err := db.GetEngine(ctx).Where("id=?", id).Get(&task) @@ -175,18 +160,6 @@ func GetTaskByID(ctx context.Context, id int64) (*ActionTask, error) { return &task, nil } -func GetTaskByJobAttempt(ctx context.Context, jobID, attempt int64) (*ActionTask, error) { - var task ActionTask - has, err := db.GetEngine(ctx).Where("job_id=?", jobID).Where("attempt=?", attempt).Get(&task) - if err != nil { - return nil, err - } else if !has { - return nil, fmt.Errorf("task with job_id %d and attempt %d: %w", jobID, attempt, util.ErrNotExist) - } - - return &task, nil -} - func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, error) { errNotExist := fmt.Errorf("task with token %q: %w", token, util.ErrNotExist) if token == "" { @@ -239,88 +212,6 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro return nil, errNotExist } -func getConcurrencyCondition() builder.Cond { - concurrencyCond := builder.NewCond() - - // OK to pick if there's no concurrency_group on the run - concurrencyCond = concurrencyCond.Or(builder.Eq{"concurrency_group": ""}) - concurrencyCond = concurrencyCond.Or(builder.IsNull{"concurrency_group"}) - - // OK to pick if it's not a "QueueBehind" concurrency type - concurrencyCond = concurrencyCond.Or(builder.Neq{"concurrency_type": QueueBehind}) - - // subQuery ends up representing all the runs that would block a run from executing: - subQuery := builder.Select("id").From("action_run", "inner_run"). - // A run can't block itself, so exclude it from this search - Where(builder.Neq{"inner_run.id": builder.Expr("outer_run.id")}). - // Blocking runs must be from the same repo & concurrency group - And(builder.Eq{"inner_run.repo_id": builder.Expr("outer_run.repo_id")}). - And(builder.Eq{"inner_run.concurrency_group": builder.Expr("outer_run.concurrency_group")}). - And( - // Ideally the logic here would be that a blocking run is "not done", and "younger", which allows each run - // to be blocked on the previous runs in the concurrency group and therefore execute in order from oldest to - // newest. - // - // But it's possible for runs to be required to run out-of-order -- for example, if a younger run has - // already completed but then it is re-run. If we only used "not done" and "younger" as logic, then the - // re-run would not be blocked, and therefore would violate the concurrency group's single-run goal. - // - // So we use two conditions to meet both needs: - // - // Blocking runs have a running status... - builder.Eq{"inner_run.status": StatusRunning}.Or( - // Blocking runs don't have a IsDone status & are younger than the outer_run - builder.NotIn("inner_run.status", []Status{StatusSuccess, StatusFailure, StatusCancelled, StatusSkipped}). - And(builder.Lt{"inner_run.`index`": builder.Expr("outer_run.`index`")}))) - - // OK to pick if there are no blocking runs - concurrencyCond = concurrencyCond.Or(builder.NotExists(subQuery)) - - return concurrencyCond -} - -// Returns all the available jobs that could be executed on `runner`, before label filtering is applied. Note that -// only a single job can actually be run from this result for any given invocation, as multiple runs (in order) from any -// single concurrency group could be returned. -func GetAvailableJobsForRunner(e db.Engine, runner *ActionRunner) ([]*ActionRunJob, error) { - jobCond := builder.NewCond() - if runner.RepoID != 0 { - jobCond = builder.Eq{"repo_id": runner.RepoID} - } else if runner.OwnerID != 0 { - jobCond = builder.In("repo_id", builder.Select("`repository`.id").From("repository"). - Join("INNER", "repo_unit", "`repository`.id = `repo_unit`.repo_id"). - Where(builder.Eq{"`repository`.owner_id": runner.OwnerID, "`repo_unit`.type": unit.TypeActions})) - } - // Concurrency group checks for queuing one run behind the last run in the concurrency group are more - // computationally expensive on the database. To manage the risk that this might have on large-scale deployments - // When this feature is initially released, it can be disabled in the ini file by setting - // `CONCURRENCY_GROUP_QUEUE_ENABLED = false` in the `[actions]` section. If disabled, then actions with a - // concurrency group and `cancel-in-progress: false` will run simultaneously rather than being queued. - if setting.Actions.ConcurrencyGroupQueueEnabled { - jobCond = jobCond.And(getConcurrencyCondition()) - } - if jobCond.IsValid() { - // It is *likely* more efficient to use an EXISTS query here rather than an IN clause, as that allows the - // database's query optimizer to perform partial computation of the subquery rather than complete computation. - // However, database engines can be fickle and difficult to predict. We'll retain the original IN clause - // implementation when ConcurrencyGroupQueueEnabled is disabled, which should maintain the same performance - // characteristics. When ConcurrencyGroupQueueEnabled is enabled, it will switch to the EXISTS clause. - if setting.Actions.ConcurrencyGroupQueueEnabled { - jobCond = builder.Exists(builder.Select("id").From("action_run", "outer_run"). - Where(builder.Eq{"outer_run.id": builder.Expr("action_run_job.run_id")}). - And(jobCond)) - } else { - jobCond = builder.In("run_id", builder.Select("id").From("action_run", "outer_run").Where(jobCond)) - } - } - - var jobs []*ActionRunJob - if err := e.Where("task_id=? AND status=?", 0, StatusWaiting).And(jobCond).Asc("updated", "id").Find(&jobs); err != nil { - return nil, err - } - return jobs, nil -} - func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) { ctx, commiter, err := db.TxContext(ctx) if err != nil { @@ -330,8 +221,20 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask e := db.GetEngine(ctx) - jobs, err := GetAvailableJobsForRunner(e, runner) - if err != nil { + jobCond := builder.NewCond() + if runner.RepoID != 0 { + jobCond = builder.Eq{"repo_id": runner.RepoID} + } else if runner.OwnerID != 0 { + jobCond = builder.In("repo_id", builder.Select("`repository`.id").From("repository"). + Join("INNER", "repo_unit", "`repository`.id = `repo_unit`.repo_id"). + Where(builder.Eq{"`repository`.owner_id": runner.OwnerID, "`repo_unit`.type": unit.TypeActions})) + } + if jobCond.IsValid() { + jobCond = builder.In("run_id", builder.Select("id").From("action_run").Where(jobCond)) + } + + var jobs []*ActionRunJob + if err := e.Where("task_id=? AND status=?", 0, StatusWaiting).And(jobCond).Asc("updated", "id").Find(&jobs); err != nil { return nil, false, err } @@ -372,7 +275,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask } var workflowJob *jobparser.Job - if gots, err := jobparser.Parse(job.WorkflowPayload, false); err != nil { + if gots, err := jobparser.Parse(job.WorkflowPayload); err != nil { return nil, false, fmt.Errorf("parse workflow of job %d: %w", job.ID, err) } else if len(gots) != 1 { return nil, false, fmt.Errorf("workflow of job %d: not single workflow", job.ID) diff --git a/models/actions/task_test.go b/models/actions/task_test.go deleted file mode 100644 index 73aff17a85..0000000000 --- a/models/actions/task_test.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package actions - -import ( - "testing" - - "forgejo.org/models/db" - "forgejo.org/models/unittest" - "forgejo.org/modules/timeutil" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestActionTask_GetAllAttempts(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - var task ActionTask - has, err := db.GetEngine(t.Context()).Where("id=?", 47).Get(&task) - require.NoError(t, err) - require.True(t, has, "load ActionTask from fixture") - - allAttempts, err := task.GetAllAttempts(t.Context()) - require.NoError(t, err) - require.Len(t, allAttempts, 3) - assert.EqualValues(t, 47, allAttempts[0].ID, "ordered by attempt, 1") - assert.EqualValues(t, 53, allAttempts[1].ID, "ordered by attempt, 2") - assert.EqualValues(t, 52, allAttempts[2].ID, "ordered by attempt, 3") - - // GetAllAttempts doesn't populate all fields; so check expected fields from one of the records - assert.EqualValues(t, 3, allAttempts[0].Attempt, "read Attempt field") - assert.Equal(t, StatusRunning, allAttempts[0].Status, "read Status field") - assert.Equal(t, timeutil.TimeStamp(1683636528), allAttempts[0].Started, "read Started field") -} - -func TestActionTask_GetTaskByJobAttempt(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - task, err := GetTaskByJobAttempt(t.Context(), 192, 2) - require.NoError(t, err) - assert.EqualValues(t, 192, task.JobID) - assert.EqualValues(t, 2, task.Attempt) - - _, err = GetTaskByJobAttempt(t.Context(), 192, 100) - assert.ErrorContains(t, err, "task with job_id 192 and attempt 100: resource does not exist") -} diff --git a/models/activities/action.go b/models/activities/action.go index 5067109545..f928ad6784 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -26,7 +26,6 @@ import ( "forgejo.org/modules/base" "forgejo.org/modules/container" "forgejo.org/modules/git" - "forgejo.org/modules/json" "forgejo.org/modules/log" "forgejo.org/modules/setting" "forgejo.org/modules/structs" @@ -387,21 +386,8 @@ func (a *Action) IsIssueEvent() bool { // GetIssueInfos returns a list of associated information with the action. func (a *Action) GetIssueInfos() []string { - // Previously multiple pieces of data used to be encoded into a.Content by pipe-separating them, but this doesn't - // work well if some of the user-entered pieces of content (issue titles, comments, etc.) contain pipes. The newer - // storage format is to json-encode a string array, which we check for and prefer... then fallback to assuming old. - var ret []string - if strings.HasPrefix(a.Content, "[") && strings.HasSuffix(a.Content, "]") { - ret = make([]string, 0, 3) - err := json.Unmarshal([]byte(a.Content), &ret) - if err != nil { - log.Error("GetIssueInfos json decoding error: %v", err) - } - } else { - ret = strings.SplitN(a.Content, "|", 3) - } - // make sure it always returns 3 elements, because there are some access to the a[1] and a[2] without checking the length + ret := strings.SplitN(a.Content, "|", 3) for len(ret) < 3 { ret = append(ret, "") } @@ -784,9 +770,7 @@ func DeleteIssueActions(ctx context.Context, repoID, issueID, issueIndex int64) _, err := e.Where("repo_id = ?", repoID). In("op_type", ActionCreateIssue, ActionCreatePullRequest). - Where(builder.Or( - builder.Like{"content", strconv.FormatInt(issueIndex, 10) + "|%"}, // "IssueIndex|content..." - builder.Like{"content", "[\"" + strconv.FormatInt(issueIndex, 10) + "\"%"})). // JSON, ["IssueIndex"... + Where("content LIKE ?", strconv.FormatInt(issueIndex, 10)+"|%"). // "IssueIndex|content..." Delete(&Action{}) return err } diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 6911733a09..161d05bbfa 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -308,69 +308,14 @@ func TestDeleteIssueActions(t *testing.T) { }) require.NoError(t, err) err = db.Insert(db.DefaultContext, &activities_model.Action{ - OpType: activities_model.ActionCreateIssue, - RepoID: issue.RepoID, - // Older Content format... + OpType: activities_model.ActionCreateIssue, + RepoID: issue.RepoID, Content: fmt.Sprintf("%d|content...", issue.Index), }) require.NoError(t, err) - err = db.Insert(db.DefaultContext, &activities_model.Action{ - OpType: activities_model.ActionCreateIssue, - RepoID: issue.RepoID, - // JSON-encoded Content format... - Content: fmt.Sprintf("[\"%d\",\"content...\"]", issue.Index), - }) - require.NoError(t, err) // assert that the actions exist, then delete them - unittest.AssertCount(t, &activities_model.Action{}, 3) + unittest.AssertCount(t, &activities_model.Action{}, 2) require.NoError(t, activities_model.DeleteIssueActions(db.DefaultContext, issue.RepoID, issue.ID, issue.Index)) unittest.AssertCount(t, &activities_model.Action{}, 0) } - -func TestGetIssueInfos(t *testing.T) { - tt := []struct { - content string - field1 string - field2 string - field3 string - }{ - { - content: "4|", - field1: "4", - }, - { - content: "2|docs: Add README w/ template sections", - field1: "2", - field2: "docs: Add README w/ template sections", - }, - { - content: "2|docs: Add README w/ template sections|Some comment...", - field1: "2", - field2: "docs: Add README w/ template sections", - field3: "Some comment...", - }, - { - content: "[\"4\"]", - field1: "4", - }, - { - content: "[\"2\", \"docs: Add README w/ | template sections\"]", - field1: "2", - field2: "docs: Add README w/ | template sections", - }, - { - content: "[\"2\", \"docs: Add README w/ | template sections\", \"Some | comment...\"]", - field1: "2", - field2: "docs: Add README w/ | template sections", - field3: "Some | comment...", - }, - } - for _, test := range tt { - action := &activities_model.Action{Content: test.content} - issueInfos := action.GetIssueInfos() - assert.Equal(t, test.field1, issueInfos[0]) - assert.Equal(t, test.field2, issueInfos[1]) - assert.Equal(t, test.field3, issueInfos[2]) - } -} diff --git a/models/asymkey/gpg_key_object_verification.go b/models/asymkey/gpg_key_object_verification.go index 10567dcd4f..745ed04869 100644 --- a/models/asymkey/gpg_key_object_verification.go +++ b/models/asymkey/gpg_key_object_verification.go @@ -36,7 +36,6 @@ type ObjectVerification struct { TrustStatus string } -// llu:TrKeys const ( // BadSignature is used as the reason when the signature has a KeyID that is in the db // but no key that has that ID verifies the signature. This is a suspicious failure. diff --git a/models/asymkey/main_test.go b/models/asymkey/main_test.go index aa13a93f0a..316e8f1d54 100644 --- a/models/asymkey/main_test.go +++ b/models/asymkey/main_test.go @@ -15,6 +15,8 @@ func TestMain(m *testing.M) { "gpg_key.yml", "public_key.yml", "TestParseCommitWithSSHSignature/public_key.yml", + "deploy_key.yml", + "gpg_key_import.yml", "user.yml", "email_address.yml", }, diff --git a/models/auth/access_token.go b/models/auth/access_token.go index 2bdb5357c5..695702b7a0 100644 --- a/models/auth/access_token.go +++ b/models/auth/access_token.go @@ -127,13 +127,6 @@ func (t *AccessToken) DisplayPublicOnly() bool { return publicOnly } -// UpdateLastUsed updates the time this token was last used to now. -func (t *AccessToken) UpdateLastUsed(ctx context.Context) error { - t.UpdatedUnix = timeutil.TimeStampNow() - _, err := db.GetEngine(ctx).ID(t.ID).Cols("updated_unix").NoAutoTime().Update(t) - return err -} - func getAccessTokenIDFromCache(token string) int64 { if successfulAccessTokenCache == nil { return 0 @@ -227,6 +220,12 @@ func (opts ListAccessTokensOptions) ToOrders() string { return "created_unix DESC" } +// UpdateAccessToken updates information of access token. +func UpdateAccessToken(ctx context.Context, t *AccessToken) error { + _, err := db.GetEngine(ctx).ID(t.ID).AllCols().Update(t) + return err +} + // DeleteAccessTokenByID deletes access token by given ID. func DeleteAccessTokenByID(ctx context.Context, id, userID int64) error { cnt, err := db.GetEngine(ctx).ID(id).Delete(&AccessToken{ @@ -259,6 +258,5 @@ func RegenerateAccessTokenByID(ctx context.Context, id, userID int64) (*AccessTo // Reset the creation time, token is unused t.UpdatedUnix = timeutil.TimeStampNow() - _, err = db.GetEngine(ctx).ID(t.ID).Cols("token_salt", "token", "token_hash", "token_last_eight", "updated_unix").NoAutoTime().Update(t) - return t, err + return t, UpdateAccessToken(ctx, t) } diff --git a/models/auth/access_token_test.go b/models/auth/access_token_test.go index 18e0fd3342..913118433c 100644 --- a/models/auth/access_token_test.go +++ b/models/auth/access_token_test.go @@ -5,12 +5,10 @@ package auth_test import ( "testing" - "time" auth_model "forgejo.org/models/auth" "forgejo.org/models/db" "forgejo.org/models/unittest" - "forgejo.org/modules/timeutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -109,17 +107,14 @@ func TestListAccessTokens(t *testing.T) { assert.Empty(t, tokens) } -func TestUpdateLastUsed(t *testing.T) { - timeutil.MockSet(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)) - defer timeutil.MockUnset() - +func TestUpdateAccessToken(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - token := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 2}) + token, err := auth_model.GetAccessTokenBySHA(db.DefaultContext, "4c6f36e6cf498e2a448662f915d932c09c5a146c") + require.NoError(t, err) + token.Name = "Token Z" - require.NoError(t, token.UpdateLastUsed(t.Context())) - - token = unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 2}) - assert.Equal(t, timeutil.TimeStampNow(), token.UpdatedUnix) + require.NoError(t, auth_model.UpdateAccessToken(db.DefaultContext, token)) + unittest.AssertExistsAndLoadBean(t, token) } func TestDeleteAccessTokenByID(t *testing.T) { diff --git a/models/auth/two_factor_test.go b/models/auth/two_factor_test.go index 3787f6ca95..36e0404ae2 100644 --- a/models/auth/two_factor_test.go +++ b/models/auth/two_factor_test.go @@ -7,7 +7,6 @@ import ( "forgejo.org/models/unittest" - "github.com/pquerna/otp/totp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -33,30 +32,3 @@ func TestHasTwoFactorByUID(t *testing.T) { assert.True(t, ok) }) } - -func TestNewTwoFactor(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - otpKey, err := totp.Generate(totp.GenerateOpts{ - SecretSize: 40, - Issuer: "forgejo-test", - AccountName: "user2", - }) - require.NoError(t, err) - - t.Run("Transaction failed", func(t *testing.T) { - reset := unittest.SetFaultInjector(2) - require.ErrorIs(t, NewTwoFactor(t.Context(), &TwoFactor{UID: 44}, otpKey.Secret()), unittest.ErrFaultInjected) - reset() - - unittest.AssertExistsIf(t, false, &TwoFactor{UID: 44}) - }) - - t.Run("Normal", func(t *testing.T) { - reset := unittest.SetFaultInjector(4) - require.NoError(t, NewTwoFactor(t.Context(), &TwoFactor{UID: 44}, otpKey.Secret())) - reset() - - unittest.AssertExistsIf(t, true, &TwoFactor{UID: 44}) - }) -} diff --git a/models/db/context.go b/models/db/context.go index 34736ddbef..3e035cd733 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -298,31 +298,6 @@ func TruncateBeans(ctx context.Context, beans ...any) (err error) { return nil } -// TruncateBeansCascade deletes all given beans. Beans MUST NOT contain delete conditions, as tables related by foreign -// keys will also be truncated. -func TruncateBeansCascade(ctx context.Context, beans ...any) (err error) { - // Expand the list of beans to any other table with a foreign key reference to the beans - cascadeTables, err := extendBeansForCascade(beans) - if err != nil { - return err - } - - // Sort the beans in inverse foreign key delete order - cascadeSorted, err := sortBeans(cascadeTables, foreignKeySortDelete) - if err != nil { - return err - } - - // Execute the truncate - e := GetEngine(ctx) - for i := range cascadeSorted { - if _, err = e.Truncate(cascadeSorted[i]); err != nil { - return err - } - } - return nil -} - // CountByBean counts the number of database records according non-empty fields of the bean as conditions. func CountByBean(ctx context.Context, bean any) (int64, error) { return GetEngine(ctx).Count(bean) diff --git a/models/db/engine.go b/models/db/engine.go index 71197153e5..05a119b08d 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -15,7 +15,6 @@ import ( "strings" "time" - "forgejo.org/modules/container" "forgejo.org/modules/log" "forgejo.org/modules/setting" @@ -161,13 +160,9 @@ func (w engineGroupWrapper) AddHook(hook contexts.Hook) bool { // SyncAllTables sync the schemas of all tables func SyncAllTables() error { - sortedTables, err := sortBeans(tables, foreignKeySortInsert) - if err != nil { - return err - } - _, err = x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{ + _, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{ WarnIfDatabaseColumnMissed: true, - }, sortedTables...) + }, tables...) return err } @@ -390,15 +385,6 @@ func (TracingHook) BeforeProcess(c *contexts.ContextHook) (context.Context, erro } func (TracingHook) AfterProcess(c *contexts.ContextHook) error { - if c.Result != nil { - if rowsAffected, err := c.Result.RowsAffected(); err == nil { - trace.Logf(c.Ctx, "rows affected", "%d", rowsAffected) - } - if lastID, err := c.Result.LastInsertId(); err == nil { - trace.Logf(c.Ctx, "last insert id", "%d", lastID) - } - } - c.Ctx.Value(sqlTask{}).(*trace.Task).End() return nil } @@ -452,12 +438,3 @@ func GetMasterEngine(x Engine) (*xorm.Engine, error) { return engine, nil } - -// GetTableNames returns the table name of all registered models. -func GetTableNames() container.Set[string] { - names := make(container.Set[string]) - for _, table := range tables { - names.Add(x.TableName(table)) - } - return names -} diff --git a/models/db/foreign_keys.go b/models/db/foreign_keys.go deleted file mode 100644 index 7af0b92f73..0000000000 --- a/models/db/foreign_keys.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package db - -import ( - "cmp" - "fmt" - "slices" - "sync" - - "forgejo.org/modules/container" - - "xorm.io/xorm/schemas" -) - -type schemaWithDefaultBean struct { - schema *schemas.Table - bean any -} - -var ( - cachedForeignKeyOrderedTables = sync.OnceValues(foreignKeyOrderedTables) - cachedTableNameLookupOrder = sync.OnceValues(tableNameLookupOrder) - // Slice of all registered tables, including their bean from `RegisterModel()` and their schemas.Table reference. - cachedSchemaTables = sync.OnceValues(func() ([]schemaWithDefaultBean, error) { - schemaTables := make([]schemaWithDefaultBean, 0, len(tables)) - for _, bean := range tables { - table, err := TableInfo(bean) - if err != nil { - return nil, fmt.Errorf("cachedSchemaTables: failure to fetch schema table for bean %#v: %w", bean, err) - } - schemaTables = append(schemaTables, schemaWithDefaultBean{ - schema: table, - bean: bean, - }) - } - return schemaTables, nil - }) - // Lookup map from table name -> {schemas.Table, bean}. The bean is the empty bean from `RegisterModel()`. - cachedTableMap = sync.OnceValues(func() (map[string]schemaWithDefaultBean, error) { - schemaTables, err := cachedSchemaTables() - if err != nil { - return nil, err - } - retval := make(map[string]schemaWithDefaultBean, len(schemaTables)) - for _, table := range schemaTables { - retval[table.schema.Name] = table - } - return retval, nil - }) - // Table A has foreign keys to [B, C], this is a map of A -> {B, C}. - cachedReferencingTables = sync.OnceValues(func() (map[string][]string, error) { - schemaTables, err := cachedSchemaTables() - if err != nil { - return nil, err - } - return calculateReferencingTables(schemaTables), nil - }) - // Table A has foreign keys to [B, C], this is a map of B -> {A}, C -> {A}. - cachedReferencedTables = sync.OnceValues(func() (map[string][]string, error) { - referencingTables, err := cachedReferencingTables() - if err != nil { - return nil, err - } - referencedTables := make(map[string][]string) - for referencingTable, targetTables := range referencingTables { - for _, targetTable := range targetTables { - referencedTables[targetTable] = append(referencedTables[targetTable], referencingTable) - } - } - return referencedTables, nil - }) -) - -// Create a map for each schema table which contains a slice of all the tables that reference it (with a foreign key). -func calculateReferencingTables(tables []schemaWithDefaultBean) map[string][]string { - referencingTables := make(map[string][]string, len(tables)) - for _, table := range tables { - tableName := table.schema.Name - for _, fk := range table.schema.ForeignKeys { - referencingTables[tableName] = append(referencingTables[tableName], fk.TargetTableName) - } - } - return referencingTables -} - -// Create a list of database tables in their "foreign key order". This order specifies the safe insertion order for -// records into tables, where earlier tables in the list are referenced by foreign keys that exist in tables later in -// the list. This order can be used in reverse as a safe deletion order as well. -// -// An ordered list of tables is incompatible with tables that have self-referencing foreign keys and circular referenced -// foreign keys; however neither of those cases are in-use in Forgejo. -func calculateTableForeignKeyOrder(tables []schemaWithDefaultBean) ([]schemaWithDefaultBean, error) { - remainingTables := slices.Clone(tables) - - referencingTables := calculateReferencingTables(remainingTables) - orderedTables := make([]schemaWithDefaultBean, 0, len(remainingTables)) - - for len(remainingTables) > 0 { - nextGroup := make([]schemaWithDefaultBean, 0, len(remainingTables)) - - for _, targetTable := range remainingTables { - // Skip if this targetTable has foreign keys and the target table hasn't been created. - slice, ok := referencingTables[targetTable.schema.Name] - if ok && len(slice) > 0 { // This table is still referencing an uncreated table - continue - } - // This table's references are satisfied or it had none - nextGroup = append(nextGroup, targetTable) - } - - if len(nextGroup) == 0 { - return nil, fmt.Errorf("calculateTableForeignKeyOrder: unable to figure out next table from remainingTables = %#v", remainingTables) - } - - orderedTables = append(orderedTables, nextGroup...) - - // Cleanup between loops: remove each table in nextGroup from remainingTables, and remove their table names from - // referencingTables as well. - for _, doneTable := range nextGroup { - remainingTables = slices.DeleteFunc(remainingTables, func(remainingTable schemaWithDefaultBean) bool { - return remainingTable.schema.Name == doneTable.schema.Name - }) - for referencingTable, referencedTables := range referencingTables { - referencingTables[referencingTable] = slices.DeleteFunc(referencedTables, func(tableName string) bool { - return tableName == doneTable.schema.Name - }) - } - } - } - - return orderedTables, nil -} - -// Create a list of registered database tables in their "foreign key order", per calculateTableForeignKeyOrder. -func foreignKeyOrderedTables() ([]schemaWithDefaultBean, error) { - schemaTables, err := cachedSchemaTables() - if err != nil { - return nil, err - } - - orderedTables, err := calculateTableForeignKeyOrder(schemaTables) - if err != nil { - return nil, err - } - - return orderedTables, nil -} - -// Create a map from each registered database table's name to its order in "foreign key order", per -// calculateTableForeignKeyOrder. -func tableNameLookupOrder() (map[string]int, error) { - tables, err := cachedForeignKeyOrderedTables() - if err != nil { - return nil, err - } - - lookupMap := make(map[string]int, len(tables)) - for i, table := range tables { - lookupMap[table.schema.Name] = i - } - - return lookupMap, nil -} - -// When used as a comparator function in `slices.SortFunc`, can sort a slice into the safe insertion order for records -// in tables, where earlier tables in the list are referenced by foreign keys that exist in tables later in the list. -func TableNameInsertionOrderSortFunc(table1, table2 string) int { - lookupMap, err := cachedTableNameLookupOrder() - if err != nil { - panic(fmt.Sprintf("cachedTableNameLookupOrder failed: %#v", err)) - } - - // Since this is typically used by `slices.SortFunc` it can't return an error. If a table is referenced that isn't - // a registered model then it will be sorted at the beginning -- this case is used in models/gitea_migrations/test. - val1, ok := lookupMap[table1] - if !ok { - val1 = -1 - } - val2, ok := lookupMap[table2] - if !ok { - val2 = -1 - } - - return cmp.Compare(val1, val2) -} - -// In "Insert" order, tables that have a foreign key will be sorted after the tables that the foreign key points to, so -// that records can be safely inserted in this order. "Delete" order is the opposite, and allows records to be safely -// deleted in this order. -type foreignKeySortOrder int8 - -const ( - foreignKeySortInsert foreignKeySortOrder = iota - foreignKeySortDelete -) - -// Sort the provided beans in the provided foreign-key sort order. -func sortBeans(beans []any, sortOrder foreignKeySortOrder) ([]any, error) { - type beanWithTableName struct { - bean any - tableName string - } - - beansWithTableNames := make([]beanWithTableName, 0, len(beans)) - for _, bean := range beans { - table, err := TableInfo(bean) - if err != nil { - return nil, fmt.Errorf("sortBeans: failure to fetch schema table for bean %#v: %w", bean, err) - } - beansWithTableNames = append(beansWithTableNames, beanWithTableName{bean: bean, tableName: table.Name}) - } - - slices.SortFunc(beansWithTableNames, func(a, b beanWithTableName) int { - if sortOrder == foreignKeySortInsert { - return TableNameInsertionOrderSortFunc(a.tableName, b.tableName) - } - return TableNameInsertionOrderSortFunc(b.tableName, a.tableName) - }) - - beanRetval := make([]any, len(beans)) - for i, beanWithTableName := range beansWithTableNames { - beanRetval[i] = beanWithTableName.bean - } - return beanRetval, nil -} - -// A database operation on `beans` may need to affect additional tables based upon foreign keys to those beans. -// extendBeansForCascade returns a new list of beans which includes all the referencing tables that link to `beans`' -// tables. For example, provided a `&User{}`, it will return `[&User{}, &Stopwatch{}, ...]` where `Stopwatch` is a table -// that references `User`. The additional beans returned will be default structs that were provided to -// `db.RegisterModel`. -func extendBeansForCascade(beans []any) ([]any, error) { - referencedTables, err := cachedReferencedTables() - if err != nil { - return nil, err - } - tableMap, err := cachedTableMap() - if err != nil { - return nil, err - } - - deduplicateTables := make(container.Set[string], len(beans)) - - finalBeans := slices.Clone(beans) - newBeans := beans - nextBeanSet := make([]any, 0) - - for { - for _, bean := range newBeans { - schema, err := TableInfo(bean) - if err != nil { - return nil, fmt.Errorf("cascadeDeleteTables: failure to fetch schema table for bean %#v: %w", bean, err) - } - if deduplicateTables.Contains(schema.Name) { - continue - } - deduplicateTables.Add(schema.Name) - for _, referencingTable := range referencedTables[schema.Name] { - table := tableMap[referencingTable] - finalBeans = append(finalBeans, table.bean) - nextBeanSet = append(nextBeanSet, table.bean) - } - } - - if len(nextBeanSet) == 0 { - break - } - newBeans = nextBeanSet - nextBeanSet = nextBeanSet[:0] // set len 0, keep allocation for reuse - } - - return finalBeans, nil -} diff --git a/models/db/index.go b/models/db/index.go index c069f0febd..4c15dbe8a1 100644 --- a/models/db/index.go +++ b/models/db/index.go @@ -72,19 +72,14 @@ func postgresGetNextResourceIndex(ctx context.Context, tableName string, groupID } func mysqlGetNextResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) { - res, err := GetEngine(ctx).Query(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ - "VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1 /*M!100500 RETURNING max_index */", - tableName), groupID) - if err != nil { + if _, err := GetEngine(ctx).Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ + "VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", + tableName), groupID); err != nil { return 0, err } - if len(res) > 0 { - return strconv.ParseInt(string(res[0]["max_index"]), 10, 64) - } - var idx int64 - _, err = GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&idx) + _, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&idx) if err != nil { return 0, err } diff --git a/models/db/iterate.go b/models/db/iterate.go index 5e30b5e8bc..450c7d3389 100644 --- a/models/db/iterate.go +++ b/models/db/iterate.go @@ -5,82 +5,39 @@ package db import ( "context" - "fmt" - "reflect" "forgejo.org/modules/setting" "xorm.io/builder" ) -// Iterate iterate all the Bean object. The table being iterated must have a single-column primary key. +// Iterate iterate all the Bean object func Iterate[Bean any](ctx context.Context, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error { - var dummy Bean + var start int batchSize := setting.Database.IterateBufferSize - - table, err := TableInfo(&dummy) - if err != nil { - return fmt.Errorf("unable to fetch table info for bean %v: %w", dummy, err) - } - if len(table.PrimaryKeys) != 1 { - return fmt.Errorf("iterate only supported on a table with 1 primary key field, but table %s had %d", table.Name, len(table.PrimaryKeys)) - } - - pkDbName := table.PrimaryKeys[0] - var pkStructFieldName string - - for _, c := range table.Columns() { - if c.Name == pkDbName { - pkStructFieldName = c.FieldName - break - } - } - if pkStructFieldName == "" { - return fmt.Errorf("iterate unable to identify struct field for primary key %s", pkDbName) - } - - var lastPK any - + sess := GetEngine(ctx) for { select { case <-ctx.Done(): return ctx.Err() default: beans := make([]*Bean, 0, batchSize) - - sess := GetEngine(ctx) - sess = sess.OrderBy(pkDbName) if cond != nil { sess = sess.Where(cond) } - if lastPK != nil { - sess = sess.Where(builder.Gt{pkDbName: lastPK}) - } - - if err := sess.Limit(batchSize).Find(&beans); err != nil { + if err := sess.Limit(batchSize, start).Find(&beans); err != nil { return err } if len(beans) == 0 { return nil } + start += len(beans) for _, bean := range beans { if err := f(ctx, bean); err != nil { return err } } - - lastBean := beans[len(beans)-1] - lastPK = extractFieldValue(lastBean, pkStructFieldName) } } } - -func extractFieldValue(bean any, fieldName string) any { - v := reflect.ValueOf(bean) - if v.Kind() == reflect.Ptr { - v = v.Elem() - } - field := v.FieldByName(fieldName) - return field.Interface() -} diff --git a/models/db/iterate_test.go b/models/db/iterate_test.go index 405db84866..bdeaa876d5 100644 --- a/models/db/iterate_test.go +++ b/models/db/iterate_test.go @@ -5,113 +5,42 @@ package db_test import ( "context" - "fmt" - "slices" "testing" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "xorm.io/builder" ) func TestIterate(t *testing.T) { - db.SetLogSQL(t.Context(), true) - defer test.MockVariableValue(&setting.Database.IterateBufferSize, 50)() + require.NoError(t, unittest.PrepareTestDatabase()) + xe, err := unittest.GetXORMEngine() + require.NoError(t, err) + require.NoError(t, xe.Sync(&repo_model.RepoUnit{})) - t.Run("No Modifications", func(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - xe, err := unittest.GetXORMEngine() - require.NoError(t, err) - require.NoError(t, xe.Sync(&repo_model.RepoUnit{})) + cnt, err := db.GetEngine(db.DefaultContext).Count(&repo_model.RepoUnit{}) + require.NoError(t, err) - // Fetch all the repo unit IDs... - var remainingRepoIDs []int64 - db.GetEngine(t.Context()).Table(&repo_model.RepoUnit{}).Cols("id").Find(&remainingRepoIDs) - - // Ensure that every repo unit ID is found when doing iterate: - err = db.Iterate(t.Context(), nil, func(ctx context.Context, repo *repo_model.RepoUnit) error { - remainingRepoIDs = slices.DeleteFunc(remainingRepoIDs, func(n int64) bool { - return repo.ID == n - }) - return nil - }) - require.NoError(t, err) - assert.Empty(t, remainingRepoIDs) + var repoUnitCnt int + err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repo *repo_model.RepoUnit) error { + repoUnitCnt++ + return nil }) + require.NoError(t, err) + assert.EqualValues(t, cnt, repoUnitCnt) - t.Run("Concurrent Delete", func(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - xe, err := unittest.GetXORMEngine() - require.NoError(t, err) - require.NoError(t, xe.Sync(&repo_model.RepoUnit{})) - - // Fetch all the repo unit IDs... - var remainingRepoIDs []int64 - db.GetEngine(t.Context()).Table(&repo_model.RepoUnit{}).Cols("id").Find(&remainingRepoIDs) - - // Ensure that every repo unit ID is found, even if someone else performs a DELETE on the table while we're - // iterating. In real-world usage the deleted record may or may not be returned, but the important - // subject-under-test is that no *other* record is skipped. - didDelete := false - err = db.Iterate(t.Context(), nil, func(ctx context.Context, repo *repo_model.RepoUnit) error { - // While on page 2 (assuming ID ordering, 50 record buffer size)... - if repo.ID == 51 { - // Delete a record that would have been on page 1. - affected, err := db.GetEngine(t.Context()).ID(25).Delete(&repo_model.RepoUnit{}) - if err != nil { - return err - } else if affected != 1 { - return fmt.Errorf("expected to delete 1 record, but affected %d records", affected) - } - didDelete = true - } - remainingRepoIDs = slices.DeleteFunc(remainingRepoIDs, func(n int64) bool { - return repo.ID == n - }) - return nil - }) - require.NoError(t, err) - assert.True(t, didDelete, "didDelete") - assert.Empty(t, remainingRepoIDs) - }) - - t.Run("Verify cond applied", func(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - xe, err := unittest.GetXORMEngine() - require.NoError(t, err) - require.NoError(t, xe.Sync(&repo_model.RepoUnit{})) - - // Fetch all the repo unit IDs... - var remainingRepoIDs []int64 - db.GetEngine(t.Context()).Table(&repo_model.RepoUnit{}).Cols("id").Find(&remainingRepoIDs) - - // Remove those that we're not expecting to find based upon `Iterate`'s condition. We'll trim the front few - // records and last few records, which will confirm that cond is applied on all pages. - remainingRepoIDs = slices.DeleteFunc(remainingRepoIDs, func(n int64) bool { - return n <= 15 || n > 1000 - }) - err = db.Iterate(t.Context(), builder.Gt{"id": 15}.And(builder.Lt{"id": 1000}), func(ctx context.Context, repo *repo_model.RepoUnit) error { - removedRecord := false - // Remove the record from remainingRepoIDs, but track to make sure we did actually remove a record - remainingRepoIDs = slices.DeleteFunc(remainingRepoIDs, func(n int64) bool { - if repo.ID == n { - removedRecord = true - return true - } - return false - }) - if !removedRecord { - return fmt.Errorf("unable to find record in remainingRepoIDs for repo %d, indicating a cond application failure", repo.ID) - } - return nil - }) - require.NoError(t, err) - assert.Empty(t, remainingRepoIDs) + err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repoUnit *repo_model.RepoUnit) error { + has, err := db.ExistByID[repo_model.RepoUnit](ctx, repoUnit.ID) + if err != nil { + return err + } + if !has { + return db.ErrNotExist{Resource: "repo_unit", ID: repoUnit.ID} + } + return nil }) + require.NoError(t, 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/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.yml b/models/fixtures/action_run.yml index 4e5af28166..5b6f89ae0e 100644 --- a/models/fixtures/action_run.yml +++ b/models/fixtures/action_run.yml @@ -525,7 +525,6 @@ ref: "refs/heads/main" commit_sha: "97f29ee599c373c729132a5c46a046978311e0ee" event: "workflow_dispatch" - trigger_event: "workflow_dispatch" is_fork_pull_request: 0 status: 6 # running started: 1683636528 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/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/repository.yml b/models/fixtures/repository.yml index f537ee02d5..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 @@ -349,7 +349,7 @@ is_archived: false is_mirror: false status: 0 - is_fork: true + is_fork: false fork_id: 10 is_template: false template_id: 0 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/tracked_time.yml b/models/fixtures/tracked_time.yml index f006576c45..768af38d9e 100644 --- a/models/fixtures/tracked_time.yml +++ b/models/fixtures/tracked_time.yml @@ -24,6 +24,7 @@ - id: 4 + user_id: -1 issue_id: 4 time: 1 created_unix: 946684803 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 9e9cb2c1a6..00aa182540 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -1562,41 +1562,3 @@ theme: "" keep_activity_private: false created_unix: 1672578400 - -- - id: 42 - lower_name: federated-example.net - name: federated-example.net - full_name: federated - email: f73240e82-c061-41ef-b7d6-4376cb6f2e1c@example.com - keep_email_private: false - email_notifications_preference: enabled - passwd: ZogKvWdyEx:password - passwd_hash_algo: dummy - must_change_password: false - login_source: 0 - login_name: federated-example.net - type: 5 - salt: ZogKvWdyEx - max_repo_creation: -1 - is_active: false - is_admin: false - is_restricted: false - allow_git_hook: false - allow_import_local: false - allow_create_organization: false - prohibit_login: false - avatar: "" - avatar_email: f73240e82-c061-41ef-b7d6-4376cb6f2e1c@example.com - use_custom_avatar: false - num_followers: 0 - num_following: 0 - num_stars: 0 - num_repos: 0 - num_teams: 0 - num_members: 0 - visibility: 0 - repo_admin_change_team_access: false - theme: "" - keep_activity_private: false - created_unix: 1759086716 diff --git a/models/forgejo_migrations/README.md b/models/forgejo_migrations/README.md deleted file mode 100644 index 578b75c8a0..0000000000 --- a/models/forgejo_migrations/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# forgejo_migrations - -Forgejo has three database migration mechanisms: - -- `models/gitea_migrations` - - Original database schema migrations from Forgejo's legacy as a fork of Gitea. - - A linear set of migrations referenced in `models/gitea_migrations/migrations.go`, each represented by a number (eg. migration 304). - - The current version is recorded in the database in the table `version`. -- `models/forgejo_migrations_legacy` - - The next 50-ish database schema migrations reflecting change in Forgejo's database structure between Forgejo v7.0 and v14.0 - - A linear set of migrations referenced in `models/forgejo_migrations_legacy/migrate.go`, each represented by a number (eg. migration 43). - - The current version is recorded in the database in the table `forgejo_version`. -- `models/forgejo_migrations` - - The most recent database schema migrations, reflecting change in the v14.0 release cycle and onwards into the future. - - Each migration is identified by the filename it is stored in. - - The applied migrations are recorded in the database in the table `forgejo_migration`. - -`forgejo_migrations` is designed to reduce code conflicts when multiple developers may be making schema migrations in close succession, which it does by avoiding having one code file with a long array of migrations. Instead, each file in `models/forgejo_migrations` registers itself as a migration, and its filename indicates the order that migration will be applied. - -Files in `forgejo_migrations` must: -- Define an `init` function which registers a function to be invoked for the migration. -- Follow the naming convention: - - The letter `v` - - A number, representing the development cycle that the migration was created in - - A letter, indicating any required migration ordering - - The character `_` (underscore) - - A short descriptive identifier for the migration - -For example, valid migration file names would look like this: -- `v14a_add-threaded-comments.go` -- `v14a_add-federated-emojis.go` -- `v14b_fix-threaded-comments-index.go` - - -## Migration Ordering - -Forgejo executes registered migrations in `forgejo_migrations` in the `strings.Compare()` ordering of their filename. - -There are edge cases where migrations may not be executed in this exact order: -- If a schema change is backported to an earlier Forgejo release. For example, if a bugfix during the v15 development cycle was backported into a v14 patch release, then a migration labeled `v15a_fix-unusual-data-corruption.go` could be applied during a v14 software upgrade. In the future when a v15 software release occurs, that migration will be identified as already applied and will be skipped. -- If a developer working on Forgejo switches between different branches with different schema migrations. -- If the contents of the `forgejo_migrations` database table are changed outside of Forgejo modifying it. - - -## Creating a new Migration - -First, determine the filename for your migration. In general, you create a new migration by starting a file with the same prefix as the most recent migration present. If `v14a_add-forgejo-migrations-table.go` was the last file, most of the time you can create your migration with the same `v14a_...` prefix. - -There are two exceptions: -- After the release branch is cut for a release, increment the version in the migration. If v14 was cut, you would start `v15a_...` as the next migration. -- If your migration requires that an earlier migration is complete first, you would increment the letter in the prefix. If you were modifying the table created by `v14a_add-forgejo-migrations-table.go`, then you would name your migration `v14b_...`. - -Once you've determined the migration filename, then you can copy this template into the file: - -```go -// 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 init() { - registerMigration(&Migration{ - Description: "short sentence describing this migration", - Upgrade: myMigrationFunction, // rename - }) -} - -func myMigrationFunction(x *xorm.Engine) error { - // add migration logic here - // - // to prevent `make watch` from recording this migration as done when it - // isn't authored yet, returh an error until the implementation is done - return errors.New("not implemented yet") -} -``` - -And now it's up to you to write the contents of your migration function. - - -## Development Notes - -Once migrations are executed, a record of their execution is stored in the database table `forgejo_migration`. - -```sql -=> SELECT * FROM forgejo_migration; - id | created_unix ------------------------------------+-------------- - v14a_add-forgejo-migrations-table | 1760402451 - v14a_example-other-migration | 1760402453 - v14b_another-example | 1760402455 - v15a_add-something-cool | 1760402456 - v15a_another-example-again | 1760402457 -``` - -If your migration successfully executes once, it will be recorded in this table and it will never execute again, even if you change the migration code. It is common during development to need to re-run a migration, in which case you can delete the record that you're working on developing. The migration will be re-run as soon as the Forgejo server is restarted: - -```sql -=> DELETE FROM forgejo_migration WHERE id = 'v15a_another-example-again'; -``` diff --git a/models/forgejo_migrations/foreign_key_utils.go b/models/forgejo_migrations/foreign_key_utils.go deleted file mode 100644 index fc2680e43e..0000000000 --- a/models/forgejo_migrations/foreign_key_utils.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "errors" - - "forgejo.org/modules/log" - - "xorm.io/xorm" -) - -func syncDoctorForeignKey(x *xorm.Engine, beans []any) error { - for _, bean := range beans { - // Sync() drops indexes by default, which will cause unnecessary rebuilding of indexes when syncDoctorForeignKey - // is used with partial bean definitions; so we disable that option - _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, bean) - if err != nil { - if errors.Is(err, xorm.ErrForeignKeyViolation) { - tableName := x.TableName(bean) - log.Error( - "Foreign key creation on table %s failed. Run `forgejo doctor check --all` to identify the orphaned records preventing this foreign key from being created. Error was: %v", - tableName, err) - return err - } - return err - } - } - return nil -} diff --git a/models/forgejo_migrations/main_test.go b/models/forgejo_migrations/main_test.go index a38afed10d..031fe8090d 100644 --- a/models/forgejo_migrations/main_test.go +++ b/models/forgejo_migrations/main_test.go @@ -1,12 +1,12 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2023 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT -package forgejo_migrations +package forgejo_migrations //nolint:revive import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 53ab95d216..94469b7371 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -1,222 +1,221 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2023 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT -package forgejo_migrations +package forgejo_migrations //nolint:revive import ( + "context" + "errors" "fmt" "os" - "regexp" - "runtime" - "slices" - "strings" - "forgejo.org/modules/container" + "forgejo.org/models/forgejo/semver" + forgejo_v1_20 "forgejo.org/models/forgejo_migrations/v1_20" + forgejo_v1_22 "forgejo.org/models/forgejo_migrations/v1_22" + "forgejo.org/modules/git" "forgejo.org/modules/log" "forgejo.org/modules/setting" - "forgejo.org/modules/timeutil" "xorm.io/xorm" "xorm.io/xorm/names" ) -// ForgejoMigration table contains a record of migrations applied to the database. (Note that there are older -// migrations in the forgejo_version table from before this table was introduced, and the `version` table from Gitea -// migrations). Each record in this table represents one successfully completed migration which was completed at the -// `CreatedUnix` time. -type ForgejoMigration struct { - ID string `xorm:"pk"` - CreatedUnix timeutil.TimeStamp `xorm:"created"` +// ForgejoVersion describes the Forgejo version table. Should have only one row with id = 1. +type ForgejoVersion struct { + ID int64 `xorm:"pk autoincr"` + Version int64 } type Migration struct { - Description string // short plaintext explanation of the migration - Upgrade func(*xorm.Engine) error // perform the migration - - id string // unique migration identifier + description string + migrate func(*xorm.Engine) error } -var ( - rawMigrations []*Migration - migrationFilenameRegex = regexp.MustCompile(`/(?Pv[0-9]+[a-z])_(?P[^/]+)\.go$`) -) - -var getMigrationFilename = func() string { - _, migrationFilename, _, _ := runtime.Caller(2) - return migrationFilename +// NewMigration creates a new migration. +func NewMigration(desc string, fn func(*xorm.Engine) error) *Migration { + return &Migration{desc, fn} } -func registerMigration(migration *Migration) { - migrationFilename := getMigrationFilename() +// This is a sequence of additional Forgejo migrations. +// Add new migrations to the bottom of the list. +var migrations = []*Migration{ + // v0 -> v1 + NewMigration("Create the `forgejo_blocked_user` table", forgejo_v1_20.AddForgejoBlockedUser), + // v1 -> v2 + NewMigration("Create the `forgejo_sem_ver` table", forgejo_v1_20.CreateSemVerTable), + // v2 -> v3 + NewMigration("Create the `forgejo_auth_token` table", forgejo_v1_20.CreateAuthorizationTokenTable), + // v3 -> v4 + NewMigration("Add the `default_permissions` column to the `repo_unit` table", forgejo_v1_22.AddDefaultPermissionsToRepoUnit), + // v4 -> v5 + NewMigration("Create the `forgejo_repo_flag` table", forgejo_v1_22.CreateRepoFlagTable), + // v5 -> v6 + NewMigration("Add the `wiki_branch` column to the `repository` table", forgejo_v1_22.AddWikiBranchToRepository), + // v6 -> v7 + NewMigration("Add the `enable_repo_unit_hints` column to the `user` table", forgejo_v1_22.AddUserRepoUnitHintsSetting), + // v7 -> v8 + NewMigration("Modify the `release`.`note` content to remove SSH signatures", forgejo_v1_22.RemoveSSHSignaturesFromReleaseNotes), + // v8 -> v9 + NewMigration("Add the `apply_to_admins` column to the `protected_branch` table", forgejo_v1_22.AddApplyToAdminsSetting), + // v9 -> v10 + NewMigration("Add pronouns to user", forgejo_v1_22.AddPronounsToUser), + // v10 -> v11 + NewMigration("Add the `created` column to the `issue` table", forgejo_v1_22.AddCreatedToIssue), + // v11 -> v12 + NewMigration("Add repo_archive_download_count table", forgejo_v1_22.AddRepoArchiveDownloadCount), + // v12 -> v13 + NewMigration("Add `hide_archive_links` column to `release` table", AddHideArchiveLinksToRelease), + // v13 -> v14 + NewMigration("Remove Gitea-specific columns from the repository and badge tables", RemoveGiteaSpecificColumnsFromRepositoryAndBadge), + // v14 -> v15 + NewMigration("Create the `federation_host` table", CreateFederationHostTable), + // v15 -> v16 + NewMigration("Create the `federated_user` table", CreateFederatedUserTable), + // v16 -> v17 + NewMigration("Add `normalized_federated_uri` column to `user` table", AddNormalizedFederatedURIToUser), + // v17 -> v18 + NewMigration("Create the `following_repo` table", CreateFollowingRepoTable), + // v18 -> v19 + NewMigration("Add external_url to attachment table", AddExternalURLColumnToAttachmentTable), + // v19 -> v20 + NewMigration("Creating Quota-related tables", CreateQuotaTables), + // v20 -> v21 + NewMigration("Add SSH keypair to `pull_mirror` table", AddSSHKeypairToPushMirror), + // v21 -> v22 + NewMigration("Add `legacy` to `web_authn_credential` table", AddLegacyToWebAuthnCredential), + // v22 -> v23 + NewMigration("Add `delete_branch_after_merge` to `auto_merge` table", AddDeleteBranchAfterMergeToAutoMerge), + // v23 -> v24 + NewMigration("Add `purpose` column to `forgejo_auth_token` table", AddPurposeToForgejoAuthToken), + // v24 -> v25 + NewMigration("Migrate `secret` column to store keying material", MigrateTwoFactorToKeying), + // v25 -> v26 + NewMigration("Add `hash_blake2b` column to `package_blob` table", AddHashBlake2bToPackageBlob), + // v26 -> v27 + NewMigration("Add `created_unix` column to `user_redirect` table", AddCreatedUnixToRedirect), + // v27 -> v28 + NewMigration("Add pronoun privacy settings to user", AddHidePronounsOptionToUser), + // v28 -> v29 + NewMigration("Add public key information to `FederatedUser` and `FederationHost`", AddPublicKeyInformationForFederation), + // v29 -> v30 + NewMigration("Migrate `User.NormalizedFederatedURI` column to extract port & schema into FederatedHost", MigrateNormalizedFederatedURI), + // v30 -> v31 + NewMigration("Normalize repository.topics to empty slice instead of null", SetTopicsAsEmptySlice), + // v31 -> v32 + NewMigration("Migrate maven package name concatenation", ChangeMavenArtifactConcatenation), + // v32 -> v33 + NewMigration("Add federated user activity tables, update the `federated_user` table & add indexes", FederatedUserActivityMigration), + // v33 -> v34 + NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun), + // v34 -> v35 + NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), + // v35 -> v36 + NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission), +} - if migrationResolutionComplete { - panic(fmt.Sprintf("attempted to register migration from %s after migration resolution is already complete", migrationFilename)) +// GetCurrentDBVersion returns the current Forgejo database version. +func GetCurrentDBVersion(x *xorm.Engine) (int64, error) { + if err := x.Sync(new(ForgejoVersion)); err != nil { + return -1, fmt.Errorf("sync: %w", err) } - matches := migrationFilenameRegex.FindStringSubmatch(migrationFilename) - if len(matches) == 0 { - panic(fmt.Sprintf("registerMigration must be invoked from a file matching migrationFilenameRegex, but was invoked from %q", migrationFilename)) - } - migration.id = fmt.Sprintf("%s_%s", matches[1], matches[2]) // this just rebuilds the filename, but guarantees that the regex applied for consistent naming - - rawMigrations = append(rawMigrations, migration) -} - -// For testing only -func resetMigrations() { - rawMigrations = nil - orderedMigrations = nil - migrationResolutionComplete = false - inMemoryMigrationIDs = nil -} - -var ( - migrationResolutionComplete = false - inMemoryMigrationIDs container.Set[string] - orderedMigrations []*Migration -) - -func resolveMigrations() { - if migrationResolutionComplete { - return - } - - inMemoryMigrationIDs = make(container.Set[string]) - for _, m := range rawMigrations { - if inMemoryMigrationIDs.Contains(m.id) { - // With the filename-based migration ID this shouldn't be possible, but a bit of a sanity check.. - panic(fmt.Sprintf("migration id is duplicated: %q", m.id)) - } - inMemoryMigrationIDs.Add(m.id) - } - - orderedMigrations = slices.Clone(rawMigrations) - slices.SortFunc(orderedMigrations, func(m1, m2 *Migration) int { - return strings.Compare(m1.id, m2.id) - }) - - migrationResolutionComplete = true -} - -func getInDBMigrationIDs(x *xorm.Engine) (container.Set[string], error) { - var inDBMigrations []ForgejoMigration - err := x.Find(&inDBMigrations) + currentVersion := &ForgejoVersion{ID: 1} + has, err := x.Get(currentVersion) if err != nil { - return nil, err + return -1, fmt.Errorf("get: %w", err) } - - inDBMigrationIDs := make(container.Set[string], len(inDBMigrations)) - for _, inDB := range inDBMigrations { - inDBMigrationIDs.Add(inDB.ID) + if !has { + return -1, nil } + return currentVersion.Version, nil +} - return inDBMigrationIDs, nil +// ExpectedVersion returns the expected Forgejo database version. +func ExpectedVersion() int64 { + return int64(len(migrations)) } // EnsureUpToDate will check if the Forgejo database is at the correct version. func EnsureUpToDate(x *xorm.Engine) error { - resolveMigrations() - - inDBMigrationIDs, err := getInDBMigrationIDs(x) + currentDB, err := GetCurrentDBVersion(x) if err != nil { return err } - // invalidMigrations are those that are in the database, but aren't registered. - invalidMigrations := inDBMigrationIDs.Difference(inMemoryMigrationIDs) - if len(invalidMigrations) > 0 { - return fmt.Errorf("current Forgejo database has migration(s) %s applied, which are not registered migrations", strings.Join(invalidMigrations.Slice(), ", ")) + if currentDB < 0 { + return errors.New("database has not been initialized") } - // unappliedMigrations are those that haven't yet been applied, but seem valid - unappliedMigrations := inMemoryMigrationIDs.Difference(inDBMigrationIDs) - if len(unappliedMigrations) > 0 { - return fmt.Errorf(`current Forgejo database is missing migration(s) %s. Please run "forgejo [--config /path/to/app.ini] migrate" to update the database version`, strings.Join(unappliedMigrations.Slice(), ", ")) + expected := ExpectedVersion() + + if currentDB != expected { + return fmt.Errorf(`current Forgejo database version %d is not equal to the expected version %d. Please run "forgejo [--config /path/to/app.ini] migrate" to update the database version`, currentDB, expected) } return nil } -func recordMigrationComplete(x *xorm.Engine, migration *Migration) error { - affected, err := x.Insert(&ForgejoMigration{ID: migration.id}) - if err != nil { - return err - } else if affected != 1 { - return fmt.Errorf("migration[%s]: failed to insert into DB, %d records affected", migration.id, affected) - } - return nil -} - // Migrate Forgejo database to current version. -func Migrate(x *xorm.Engine, freshDB bool) error { - resolveMigrations() - +func Migrate(x *xorm.Engine) error { // Set a new clean the default mapper to GonicMapper as that is the default for . x.SetMapper(names.GonicMapper{}) - if err := x.Sync(new(ForgejoMigration)); err != nil { + if err := x.Sync(new(ForgejoVersion)); err != nil { return fmt.Errorf("sync: %w", err) } - inDBMigrationIDs, err := getInDBMigrationIDs(x) + currentVersion := &ForgejoVersion{ID: 1} + has, err := x.Get(currentVersion) if err != nil { - return err - } else if len(inDBMigrationIDs) == 0 && freshDB { - // During startup on a new, empty database, and during integration tests, we rely only on `SyncAllTables` to - // create the DB schema. No migrations can be applied because `SyncAllTables` occurs later in the - // initialization cycle. We mark all migrations as complete up to this point and only run future migrations. - for _, migration := range orderedMigrations { - err := recordMigrationComplete(x, migration) - if err != nil { - return err - } + return fmt.Errorf("get: %w", err) + } else if !has { + // If the version record does not exist we think + // it is a fresh installation and we can skip all migrations. + currentVersion.ID = 0 + currentVersion.Version = ExpectedVersion() + + if _, err = x.InsertOne(currentVersion); err != nil { + return fmt.Errorf("insert: %w", err) } - inDBMigrationIDs, err = getInDBMigrationIDs(x) - if err != nil { - return err - } - } else if freshDB { - return fmt.Errorf("unexpected state: migrator called with freshDB=true, but existing migrations in DB %#v", inDBMigrationIDs) } - // invalidMigrations are those that are in the database, but aren't registered. - invalidMigrations := inDBMigrationIDs.Difference(inMemoryMigrationIDs) - if len(invalidMigrations) > 0 { - // Downgrading Forgejo's database version not supported - msg := fmt.Sprintf("Your Forgejo database has %d migration(s) (%s) for a newer version of Forgejo, you cannot use the newer database for this old Forgejo release.", len(invalidMigrations), strings.Join(invalidMigrations.Slice(), ", ")) + v := currentVersion.Version + + // Downgrading Forgejo's database version not supported + if v > ExpectedVersion() { + msg := fmt.Sprintf("Your Forgejo database (migration version: %d) is for a newer version of Forgejo, you cannot use the newer database for this old Forgejo release (%d).", v, ExpectedVersion()) msg += "\nForgejo will exit to keep your database safe and unchanged. Please use the correct Forgejo release, do not change the migration version manually (incorrect manual operation may cause data loss)." if !setting.IsProd { - msg += "\nIf you are in development and know what you're doing, you can remove the migration records from the forgejo_migration table. The affect of those migrations will still be present." - quoted := slices.Clone(invalidMigrations.Slice()) - for i, s := range quoted { - quoted[i] = "'" + s + "'" - } - msg += fmt.Sprintf("\n DELETE FROM forgejo_migration WHERE id IN (%s)", strings.Join(quoted, ", ")) + msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE forgejo_version SET version=%d WHERE id=1;", ExpectedVersion()) } _, _ = fmt.Fprintln(os.Stderr, msg) log.Fatal(msg) return nil } - // unappliedMigrations are those that are registered but haven't been applied. - unappliedMigrations := inMemoryMigrationIDs.Difference(inDBMigrationIDs) - for _, migration := range orderedMigrations { - if !unappliedMigrations.Contains(migration.id) { - continue - } - - log.Info("Migration[%s]: %s", migration.id, migration.Description) - - // Reset the mapper between each migration - migrations are not supposed to depend on each other - x.SetMapper(names.GonicMapper{}) - if err = migration.Upgrade(x); err != nil { - return fmt.Errorf("migration[%s]: %s failed: %w", migration.id, migration.Description, err) - } - - err := recordMigrationComplete(x, migration) - if err != nil { + // Some migration tasks depend on the git command + if git.DefaultContext == nil { + if err = git.InitSimple(context.Background()); err != nil { return err } } - return nil + // Migrate + for i, m := range migrations[v:] { + log.Info("Migration[%d]: %s", v+int64(i), m.description) + // Reset the mapper between each migration - migrations are not supposed to depend on each other + x.SetMapper(names.GonicMapper{}) + if err = m.migrate(x); err != nil { + return fmt.Errorf("migration[%d]: %s failed: %w", v+int64(i), m.description, err) + } + currentVersion.Version = v + int64(i) + 1 + if _, err = x.ID(1).Update(currentVersion); err != nil { + return err + } + } + + if err := x.Sync(new(semver.ForgejoSemVer)); err != nil { + return fmt.Errorf("sync: %w", err) + } + + return semver.SetVersionStringWithEngine(x, setting.ForgejoVersion) } diff --git a/models/forgejo_migrations/migrate_test.go b/models/forgejo_migrations/migrate_test.go index 360f6a16d5..20653929a3 100644 --- a/models/forgejo_migrations/migrate_test.go +++ b/models/forgejo_migrations/migrate_test.go @@ -1,314 +1,39 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2023 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT -package forgejo_migrations +package forgejo_migrations //nolint:revive import ( - "fmt" "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" - "forgejo.org/modules/test" + migration_tests "forgejo.org/models/migrations/test" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "xorm.io/xorm" ) -func noOpMigration(x *xorm.Engine) error { - return nil -} - -func nilMigration() *Migration { - return &Migration{ - Description: "nothing", - Upgrade: noOpMigration, - } -} - -func TestRegisterMigration(t *testing.T) { - resetMigrations() - - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v99b_neat_migration.go" - })() - - t.Run("migrationResolutionComplete", func(t *testing.T) { - defer test.MockVariableValue(&migrationResolutionComplete, true)() - assert.PanicsWithValue(t, "attempted to register migration from some-path/v99b_neat_migration.go after migration resolution is already complete", func() { - registerMigration(nilMigration()) - }) - }) - - for _, fn := range []string{ - "v99b_neat_migration.go", // no leading path - "vb_neat_migration.go", // no version number - "v12_neat_migration.go", // no migration group letter - "v12a-neat-migration.go", // no undescore - "v12a.go", // no descriptive identifier - } { - t.Run(fmt.Sprintf("bad name - %s", fn), func(t *testing.T) { - defer test.MockVariableValue(&getMigrationFilename, func() string { - return fn - })() - assert.PanicsWithValue(t, fmt.Sprintf("registerMigration must be invoked from a file matching migrationFilenameRegex, but was invoked from %q", fn), func() { - registerMigration(nilMigration()) - }) - }) - } - - registerMigration(nilMigration()) - found := false - for _, m := range rawMigrations { - if m.id == "v99b_neat_migration" { - found = true - } - } - require.True(t, found, "found registered migration") -} - -func TestResolveMigrations(t *testing.T) { - t.Run("duplicate migration IDs", func(t *testing.T) { - resetMigrations() - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v99b_neat_migration.go" - })() - registerMigration(nilMigration()) - registerMigration(nilMigration()) - - assert.PanicsWithValue(t, "migration id is duplicated: \"v99b_neat_migration\"", func() { - resolveMigrations() - }) - }) - - t.Run("success", func(t *testing.T) { - resetMigrations() - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v99b_neat_migration.go" - })() - registerMigration(nilMigration()) - - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v77a_neat_migration.go" - })() - registerMigration(nilMigration()) - - resolveMigrations() - - assert.True(t, migrationResolutionComplete, "migration resolution complete") - assert.Contains(t, inMemoryMigrationIDs, "v77a_neat_migration") - assert.Contains(t, inMemoryMigrationIDs, "v99b_neat_migration") - require.Len(t, orderedMigrations, 2) - assert.Equal(t, "v77a_neat_migration", orderedMigrations[0].id) - assert.Equal(t, "v99b_neat_migration", orderedMigrations[1].id) - }) -} - -func TestGetInDBMigrationIDs(t *testing.T) { - x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ForgejoMigration)) - defer deferable() - require.NotNil(t, x) - - migrationIDs, err := getInDBMigrationIDs(x) - require.NoError(t, err) - require.NotNil(t, migrationIDs) - assert.Empty(t, migrationIDs) - - _, err = x.Insert(&ForgejoMigration{ID: "v77a_neat_migration"}) - require.NoError(t, err) - _, err = x.Insert(&ForgejoMigration{ID: "v99b_neat_migration"}) - require.NoError(t, err) - - migrationIDs, err = getInDBMigrationIDs(x) - require.NoError(t, err) - require.NotNil(t, migrationIDs) - assert.Len(t, migrationIDs, 2) - assert.Contains(t, migrationIDs, "v77a_neat_migration") - assert.Contains(t, migrationIDs, "v99b_neat_migration") -} - +// TestEnsureUpToDate tests the behavior of EnsureUpToDate. func TestEnsureUpToDate(t *testing.T) { - tests := []struct { - desc string - inMemory []string - inDB []string - err string - }{ - { - desc: "up-to-date", - inMemory: []string{"v77a_neat_migration"}, - inDB: []string{"v77a_neat_migration"}, - }, - { - desc: "invalid-migration", - inMemory: []string{}, - inDB: []string{"v77a_neat_migration"}, - err: "current Forgejo database has migration(s) v77a_neat_migration applied, which are not registered migrations", - }, - { - desc: "missing-migration", - inMemory: []string{"v77a_neat_migration"}, - inDB: []string{}, - err: "current Forgejo database is missing migration(s) v77a_neat_migration", - }, + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ForgejoVersion)) + defer deferable() + if x == nil || t.Failed() { + return } - for _, tc := range tests { - t.Run(tc.desc, func(t *testing.T) { - resetMigrations() - x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ForgejoMigration)) - defer deferable() - require.NotNil(t, x) + // Ensure error if there's no row in Forgejo Version. + err := EnsureUpToDate(x) + require.Error(t, err) - for _, inMemory := range tc.inMemory { - defer test.MockVariableValue(&getMigrationFilename, func() string { - return fmt.Sprintf("some-path/%s.go", inMemory) - })() - registerMigration(nilMigration()) - } - for _, inDB := range tc.inDB { - x.Insert(&ForgejoMigration{ID: inDB}) - } + // Insert 'good' Forgejo Version row. + _, err = x.InsertOne(&ForgejoVersion{ID: 1, Version: ExpectedVersion()}) + require.NoError(t, err) - err := EnsureUpToDate(x) - if tc.err == "" { - assert.NoError(t, err) - } else { - assert.ErrorContains(t, err, tc.err) - } - }) - } -} - -func TestMigrate(t *testing.T) { - resetMigrations() - x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ForgejoMigration)) - defer deferable() - require.NotNil(t, x) - - v77aRun := false - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v77a_neat_migration.go" - })() - registerMigration(&Migration{ - Description: "nothing", - Upgrade: func(x *xorm.Engine) error { - v77aRun = true - return nil - }, - }) - // v77a_neat_migration will already be marked as already run - _, err := x.Insert(&ForgejoMigration{ID: "v77a_neat_migration"}) - require.NoError(t, err) - - v99bRun := false - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v99b_neat_migration.go" - })() - registerMigration(&Migration{ - Description: "nothing", - Upgrade: func(x *xorm.Engine) error { - v99bRun = true - type ForgejoMagicFunctionality struct { - ID int64 `xorm:"pk autoincr"` - Name string - } - return x.Sync(new(ForgejoMagicFunctionality)) - }, - }) - - v99cRun := false - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v99c_neat_migration.go" - })() - registerMigration(&Migration{ - Description: "nothing", - Upgrade: func(x *xorm.Engine) error { - v99cRun = true - type ForgejoMagicFunctionality struct { - NewField string - } - return x.Sync(new(ForgejoMagicFunctionality)) - }, - }) - - err = Migrate(x, false) - require.NoError(t, err) - - assert.False(t, v77aRun, "v77aRun") // was already marked as run in the DB so shouldn't have run again - assert.True(t, v99bRun, "v99bRun") - assert.True(t, v99cRun, "v99cRun") - migrationIDs, err := getInDBMigrationIDs(x) - require.NoError(t, err) - assert.Contains(t, migrationIDs, "v77a_neat_migration") - assert.Contains(t, migrationIDs, "v99b_neat_migration") - assert.Contains(t, migrationIDs, "v99c_neat_migration") - - // should be able to query all three of the fields from this table created, verifying both migrations creating the - // table and adding a column were run - rec := make([]map[string]any, 0) - err = x.Cols("id", "name", "new_field").Table("forgejo_magic_functionality").Find(&rec) - assert.NoError(t, err) -} - -func TestMigrateFreshDB(t *testing.T) { - resetMigrations() - x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ForgejoMigration)) - defer deferable() - require.NotNil(t, x) - - v77aRun := false - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v77a_neat_migration.go" - })() - registerMigration(&Migration{ - Description: "nothing", - Upgrade: func(x *xorm.Engine) error { - v77aRun = true - return nil - }, - }) - - v99bRun := false - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v99b_neat_migration.go" - })() - registerMigration(&Migration{ - Description: "nothing", - Upgrade: func(x *xorm.Engine) error { - v99bRun = true - type ForgejoMagicFunctionality struct { - ID int64 `xorm:"pk autoincr"` - Name string - } - return x.Sync(new(ForgejoMagicFunctionality)) - }, - }) - - v99cRun := false - defer test.MockVariableValue(&getMigrationFilename, func() string { - return "some-path/v99c_neat_migration.go" - })() - registerMigration(&Migration{ - Description: "nothing", - Upgrade: func(x *xorm.Engine) error { - v99cRun = true - type ForgejoMagicFunctionality struct { - NewField string - } - return x.Sync(new(ForgejoMagicFunctionality)) - }, - }) - - err := Migrate(x, true) - require.NoError(t, err) - - assert.False(t, v77aRun, "v77aRun") // none should be run due to freshDB flag - assert.False(t, v99bRun, "v99bRun") - assert.False(t, v99cRun, "v99cRun") - migrationIDs, err := getInDBMigrationIDs(x) - require.NoError(t, err) - assert.Contains(t, migrationIDs, "v77a_neat_migration") - assert.Contains(t, migrationIDs, "v99b_neat_migration") - assert.Contains(t, migrationIDs, "v99c_neat_migration") + err = EnsureUpToDate(x) + require.NoError(t, err) + + // Modify forgejo version to have a lower version. + _, err = x.Exec("UPDATE `forgejo_version` SET version = ? WHERE id = 1", ExpectedVersion()-1) + require.NoError(t, err) + + err = EnsureUpToDate(x) + require.Error(t, err) } diff --git a/models/forgejo_migrations_legacy/v13.go b/models/forgejo_migrations/v13.go similarity index 88% rename from models/forgejo_migrations_legacy/v13.go rename to models/forgejo_migrations/v13.go index 6c216b3732..614f68249d 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v14.go b/models/forgejo_migrations/v14.go similarity index 91% rename from models/forgejo_migrations_legacy/v14.go rename to models/forgejo_migrations/v14.go index 7085fb0722..53f1ef2223 100644 --- a/models/forgejo_migrations_legacy/v14.go +++ b/models/forgejo_migrations/v14.go @@ -1,10 +1,10 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations_legacy +package forgejo_migrations //nolint:revive import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/forgejo_migrations/v14a_add-foreign-keys-collaboration.go b/models/forgejo_migrations/v14a_add-foreign-keys-collaboration.go deleted file mode 100644 index 849c2e5ea9..0000000000 --- a/models/forgejo_migrations/v14a_add-foreign-keys-collaboration.go +++ /dev/null @@ -1,25 +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 init() { - registerMigration(&Migration{ - Description: "add forgejo_migration table", - Upgrade: addForeignKeysCollaboration, - }) -} - -func addForeignKeysCollaboration(x *xorm.Engine) error { - type Collaboration struct { - RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL REFERENCES(repository, id)"` - UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL REFERENCES(user, id)"` - } - return syncDoctorForeignKey(x, []any{ - new(Collaboration), - }) -} diff --git a/models/forgejo_migrations/v14a_add-forgejo-migrations-table.go b/models/forgejo_migrations/v14a_add-forgejo-migrations-table.go deleted file mode 100644 index f78eec8789..0000000000 --- a/models/forgejo_migrations/v14a_add-forgejo-migrations-table.go +++ /dev/null @@ -1,25 +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 init() { - registerMigration(&Migration{ - Description: "add forgejo_migration table", - Upgrade: addForgejoMigration, - }) -} - -func addForgejoMigration(x *xorm.Engine) error { - type ForgejoMigration struct { - ID string `xorm:"pk"` - CreatedUnix timeutil.TimeStamp `xorm:"created"` - } - return x.Sync(new(ForgejoMigration)) -} diff --git a/models/forgejo_migrations_legacy/v15.go b/models/forgejo_migrations/v15.go similarity index 94% rename from models/forgejo_migrations_legacy/v15.go rename to models/forgejo_migrations/v15.go index 13e1651fd3..5e5588dd05 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import ( "time" diff --git a/models/forgejo_migrations_legacy/v16.go b/models/forgejo_migrations/v16.go similarity index 91% rename from models/forgejo_migrations_legacy/v16.go rename to models/forgejo_migrations/v16.go index 7d18751262..f80bfc5268 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v17.go b/models/forgejo_migrations/v17.go similarity index 87% rename from models/forgejo_migrations_legacy/v17.go rename to models/forgejo_migrations/v17.go index 403351e221..d6e2983d00 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v18.go b/models/forgejo_migrations/v18.go similarity index 92% rename from models/forgejo_migrations_legacy/v18.go rename to models/forgejo_migrations/v18.go index e2d2937bfa..e6c1493f0e 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v19.go b/models/forgejo_migrations/v19.go similarity index 87% rename from models/forgejo_migrations_legacy/v19.go rename to models/forgejo_migrations/v19.go index e0a48a388d..69b7746eb1 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v1_20/v1.go b/models/forgejo_migrations/v1_20/v1.go similarity index 92% rename from models/forgejo_migrations_legacy/v1_20/v1.go rename to models/forgejo_migrations/v1_20/v1.go index f0cb125557..72beaf23de 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_20/v2.go b/models/forgejo_migrations/v1_20/v2.go similarity index 83% rename from models/forgejo_migrations_legacy/v1_20/v2.go rename to models/forgejo_migrations/v1_20/v2.go index 3f79ac3801..39f3b58924 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_20/v3.go b/models/forgejo_migrations/v1_20/v3.go similarity index 93% rename from models/forgejo_migrations_legacy/v1_20/v3.go rename to models/forgejo_migrations/v1_20/v3.go index 49530df556..cce227e6eb 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_22/main_test.go b/models/forgejo_migrations/v1_22/main_test.go similarity index 69% rename from models/forgejo_migrations_legacy/v1_22/main_test.go rename to models/forgejo_migrations/v1_22/main_test.go index 26930f8e70..03c4c5272c 100644 --- a/models/forgejo_migrations_legacy/v1_22/main_test.go +++ b/models/forgejo_migrations/v1_22/main_test.go @@ -1,12 +1,12 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 +package v1_22 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/forgejo_migrations_legacy/v1_22/v10.go b/models/forgejo_migrations/v1_22/v10.go similarity index 92% rename from models/forgejo_migrations_legacy/v1_22/v10.go rename to models/forgejo_migrations/v1_22/v10.go index cf45abdd24..819800ae71 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_22/v11.go b/models/forgejo_migrations/v1_22/v11.go similarity index 93% rename from models/forgejo_migrations_legacy/v1_22/v11.go rename to models/forgejo_migrations/v1_22/v11.go index f0f92bd04c..17bb592379 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_22/v12.go b/models/forgejo_migrations/v1_22/v12.go similarity index 95% rename from models/forgejo_migrations_legacy/v1_22/v12.go rename to models/forgejo_migrations/v1_22/v12.go index 51354bd3c2..6822524705 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_22/v4.go b/models/forgejo_migrations/v1_22/v4.go similarity index 93% rename from models/forgejo_migrations_legacy/v1_22/v4.go rename to models/forgejo_migrations/v1_22/v4.go index 499d377bb4..f1195f5f66 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_22/v5.go b/models/forgejo_migrations/v1_22/v5.go similarity index 94% rename from models/forgejo_migrations_legacy/v1_22/v5.go rename to models/forgejo_migrations/v1_22/v5.go index 1671d3eed2..55f9fe1338 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_22/v6.go b/models/forgejo_migrations/v1_22/v6.go similarity index 96% rename from models/forgejo_migrations_legacy/v1_22/v6.go rename to models/forgejo_migrations/v1_22/v6.go index 072f8e6a15..1a4874872c 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_22/v7.go b/models/forgejo_migrations/v1_22/v7.go similarity index 93% rename from models/forgejo_migrations_legacy/v1_22/v7.go rename to models/forgejo_migrations/v1_22/v7.go index e7f6eb412b..b42dd1af67 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_22/v8.go b/models/forgejo_migrations/v1_22/v8.go similarity index 97% rename from models/forgejo_migrations_legacy/v1_22/v8.go rename to models/forgejo_migrations/v1_22/v8.go index f23b00d2ad..2d3c0c594b 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v1_22/v8_test.go b/models/forgejo_migrations/v1_22/v8_test.go similarity index 91% rename from models/forgejo_migrations_legacy/v1_22/v8_test.go rename to models/forgejo_migrations/v1_22/v8_test.go index 3ef4a05f9f..baaba7290f 100644 --- a/models/forgejo_migrations_legacy/v1_22/v8_test.go +++ b/models/forgejo_migrations/v1_22/v8_test.go @@ -1,12 +1,12 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 +package v1_22 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/forgejo_migrations_legacy/v1_22/v9.go b/models/forgejo_migrations/v1_22/v9.go similarity index 93% rename from models/forgejo_migrations_legacy/v1_22/v9.go rename to models/forgejo_migrations/v1_22/v9.go index e3cdea97f2..34c2844c39 100644 --- a/models/forgejo_migrations_legacy/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_legacy/v20.go b/models/forgejo_migrations/v20.go similarity index 96% rename from models/forgejo_migrations_legacy/v20.go rename to models/forgejo_migrations/v20.go index 4bd0be6047..8ca9e91f73 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v21.go b/models/forgejo_migrations/v21.go similarity index 89% rename from models/forgejo_migrations_legacy/v21.go rename to models/forgejo_migrations/v21.go index cc7da2772c..53f141b2ab 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v22.go b/models/forgejo_migrations/v22.go similarity index 91% rename from models/forgejo_migrations_legacy/v22.go rename to models/forgejo_migrations/v22.go index 892a5ce851..eeb738799c 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v23.go b/models/forgejo_migrations/v23.go similarity index 91% rename from models/forgejo_migrations_legacy/v23.go rename to models/forgejo_migrations/v23.go index 48aac74204..20a916a716 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v24.go b/models/forgejo_migrations/v24.go similarity index 92% rename from models/forgejo_migrations_legacy/v24.go rename to models/forgejo_migrations/v24.go index bf3b7b2220..ebfb5fc1c4 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v25.go b/models/forgejo_migrations/v25.go similarity index 96% rename from models/forgejo_migrations_legacy/v25.go rename to models/forgejo_migrations/v25.go index 8c4b623c84..8e3032a40c 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import ( "context" @@ -23,7 +23,7 @@ func MigrateTwoFactorToKeying(x *xorm.Engine) error { var err error // When upgrading from Forgejo v9 to v10, this migration will already be - // called from models/gitea_migrations/migrations.go migration 304 and must not + // called from models/migrations/migrations.go migration 304 and must not // be run twice. var version int _, err = x.Table("version").Where("`id` = 1").Select("version").Get(&version) diff --git a/models/forgejo_migrations_legacy/v25_test.go b/models/forgejo_migrations/v25_test.go similarity index 94% rename from models/forgejo_migrations_legacy/v25_test.go rename to models/forgejo_migrations/v25_test.go index 86286b2ab1..e7402fd021 100644 --- a/models/forgejo_migrations_legacy/v25_test.go +++ b/models/forgejo_migrations/v25_test.go @@ -1,13 +1,13 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations_legacy +package forgejo_migrations //nolint:revive import ( "testing" "forgejo.org/models/auth" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/modules/keying" "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations_legacy/v26.go b/models/forgejo_migrations/v26.go similarity index 89% rename from models/forgejo_migrations_legacy/v26.go rename to models/forgejo_migrations/v26.go index 03d05ad96d..3292d93ffd 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v27.go b/models/forgejo_migrations/v27.go similarity index 90% rename from models/forgejo_migrations_legacy/v27.go rename to models/forgejo_migrations/v27.go index 531170d06f..2efa3485a8 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations_legacy/v28.go b/models/forgejo_migrations/v28.go similarity index 88% rename from models/forgejo_migrations_legacy/v28.go rename to models/forgejo_migrations/v28.go index 443c3f5d66..cba888d2ec 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v29.go b/models/forgejo_migrations/v29.go similarity index 93% rename from models/forgejo_migrations_legacy/v29.go rename to models/forgejo_migrations/v29.go index 9b79139fcc..d0c2f723ae 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import ( "database/sql" diff --git a/models/forgejo_migrations_legacy/v30.go b/models/forgejo_migrations/v30.go similarity index 97% rename from models/forgejo_migrations_legacy/v30.go rename to models/forgejo_migrations/v30.go index 2ffafd3fe7..6c41a55316 100644 --- a/models/forgejo_migrations_legacy/v30.go +++ b/models/forgejo_migrations/v30.go @@ -1,12 +1,12 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations_legacy +package forgejo_migrations //nolint:revive import ( "time" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations_legacy/v30_test.go b/models/forgejo_migrations/v30_test.go similarity index 95% rename from models/forgejo_migrations_legacy/v30_test.go rename to models/forgejo_migrations/v30_test.go index 73bfefac90..f826dab815 100644 --- a/models/forgejo_migrations_legacy/v30_test.go +++ b/models/forgejo_migrations/v30_test.go @@ -1,13 +1,13 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations_legacy +package forgejo_migrations //nolint:revive import ( "testing" "time" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/modules/timeutil" "github.com/stretchr/testify/require" diff --git a/models/forgejo_migrations_legacy/v31.go b/models/forgejo_migrations/v31.go similarity index 97% rename from models/forgejo_migrations_legacy/v31.go rename to models/forgejo_migrations/v31.go index f500ffa231..fdcab21b1a 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v31_test.go b/models/forgejo_migrations/v31_test.go similarity index 89% rename from models/forgejo_migrations_legacy/v31_test.go rename to models/forgejo_migrations/v31_test.go index 0adc9cc69b..5b4aac2a60 100644 --- a/models/forgejo_migrations_legacy/v31_test.go +++ b/models/forgejo_migrations/v31_test.go @@ -1,12 +1,12 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations_legacy +package forgejo_migrations //nolint:revive import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/require" ) diff --git a/models/forgejo_migrations_legacy/v32.go b/models/forgejo_migrations/v32.go similarity index 99% rename from models/forgejo_migrations_legacy/v32.go rename to models/forgejo_migrations/v32.go index bd5176db1e..2460003597 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import ( "encoding/xml" diff --git a/models/forgejo_migrations_legacy/v32_test.go b/models/forgejo_migrations/v32_test.go similarity index 99% rename from models/forgejo_migrations_legacy/v32_test.go rename to models/forgejo_migrations/v32_test.go index 6abb5f9004..cd33de2608 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import ( "bytes" @@ -11,7 +11,7 @@ import ( "strings" "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/models/packages" "github.com/stretchr/testify/assert" diff --git a/models/forgejo_migrations_legacy/v33.go b/models/forgejo_migrations/v33.go similarity index 98% rename from models/forgejo_migrations_legacy/v33.go rename to models/forgejo_migrations/v33.go index ce220d8179..272035fc23 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import ( "fmt" diff --git a/models/forgejo_migrations_legacy/v33_test.go b/models/forgejo_migrations/v33_test.go similarity index 92% rename from models/forgejo_migrations_legacy/v33_test.go rename to models/forgejo_migrations/v33_test.go index a77dfed0c2..664c704bbc 100644 --- a/models/forgejo_migrations_legacy/v33_test.go +++ b/models/forgejo_migrations/v33_test.go @@ -1,13 +1,13 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations_legacy +package forgejo_migrations //nolint:revive import ( "testing" "time" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/modules/log" ft "forgejo.org/modules/test" diff --git a/models/forgejo_migrations_legacy/v34.go b/models/forgejo_migrations/v34.go similarity index 86% rename from models/forgejo_migrations_legacy/v34.go rename to models/forgejo_migrations/v34.go index c1a1bfc8d6..9e958b934f 100644 --- a/models/forgejo_migrations_legacy/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_legacy +package forgejo_migrations //nolint:revive import "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v35.go b/models/forgejo_migrations/v35.go similarity index 86% rename from models/forgejo_migrations_legacy/v35.go rename to models/forgejo_migrations/v35.go index ea3765fce5..ca412d7951 100644 --- a/models/forgejo_migrations_legacy/v35.go +++ b/models/forgejo_migrations/v35.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations_legacy +package forgejo_migrations //nolint:revive import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/v36.go b/models/forgejo_migrations/v36.go similarity index 97% rename from models/forgejo_migrations_legacy/v36.go rename to models/forgejo_migrations/v36.go index 5c13f3878e..019c177a08 100644 --- a/models/forgejo_migrations_legacy/v36.go +++ b/models/forgejo_migrations/v36.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations_legacy +package forgejo_migrations //nolint:revive import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations_legacy/main_test.go b/models/forgejo_migrations_legacy/main_test.go deleted file mode 100644 index 2622af8e22..0000000000 --- a/models/forgejo_migrations_legacy/main_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2023 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package forgejo_migrations_legacy - -import ( - "testing" - - migration_tests "forgejo.org/models/gitea_migrations/test" -) - -func TestMain(m *testing.M) { - migration_tests.MainTest(m) -} diff --git a/models/forgejo_migrations_legacy/migrate.go b/models/forgejo_migrations_legacy/migrate.go deleted file mode 100644 index 72bbf66bdc..0000000000 --- a/models/forgejo_migrations_legacy/migrate.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2023 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package forgejo_migrations_legacy - -import ( - "context" - "errors" - "fmt" - "os" - - "forgejo.org/models/forgejo/semver" - "forgejo.org/models/forgejo_migrations" - forgejo_v1_20 "forgejo.org/models/forgejo_migrations_legacy/v1_20" - forgejo_v1_22 "forgejo.org/models/forgejo_migrations_legacy/v1_22" - "forgejo.org/modules/git" - "forgejo.org/modules/log" - "forgejo.org/modules/setting" - - "xorm.io/xorm" - "xorm.io/xorm/names" -) - -// ForgejoVersion describes the Forgejo version table. Should have only one row with id = 1. -type ForgejoVersion struct { - ID int64 `xorm:"pk autoincr"` - Version int64 -} - -type Migration struct { - description string - migrate func(*xorm.Engine) error -} - -// NewMigration creates a new migration. -func NewMigration(desc string, fn func(*xorm.Engine) error) *Migration { - return &Migration{desc, fn} -} - -// This is a sequence of additional Forgejo migrations. -// Add new migrations to the bottom of the list. -var migrations = []*Migration{ - // v0 -> v1 - NewMigration("Create the `forgejo_blocked_user` table", forgejo_v1_20.AddForgejoBlockedUser), - // v1 -> v2 - NewMigration("Create the `forgejo_sem_ver` table", forgejo_v1_20.CreateSemVerTable), - // v2 -> v3 - NewMigration("Create the `forgejo_auth_token` table", forgejo_v1_20.CreateAuthorizationTokenTable), - // v3 -> v4 - NewMigration("Add the `default_permissions` column to the `repo_unit` table", forgejo_v1_22.AddDefaultPermissionsToRepoUnit), - // v4 -> v5 - NewMigration("Create the `forgejo_repo_flag` table", forgejo_v1_22.CreateRepoFlagTable), - // v5 -> v6 - NewMigration("Add the `wiki_branch` column to the `repository` table", forgejo_v1_22.AddWikiBranchToRepository), - // v6 -> v7 - NewMigration("Add the `enable_repo_unit_hints` column to the `user` table", forgejo_v1_22.AddUserRepoUnitHintsSetting), - // v7 -> v8 - NewMigration("Modify the `release`.`note` content to remove SSH signatures", forgejo_v1_22.RemoveSSHSignaturesFromReleaseNotes), - // v8 -> v9 - NewMigration("Add the `apply_to_admins` column to the `protected_branch` table", forgejo_v1_22.AddApplyToAdminsSetting), - // v9 -> v10 - NewMigration("Add pronouns to user", forgejo_v1_22.AddPronounsToUser), - // v10 -> v11 - NewMigration("Add the `created` column to the `issue` table", forgejo_v1_22.AddCreatedToIssue), - // v11 -> v12 - NewMigration("Add repo_archive_download_count table", forgejo_v1_22.AddRepoArchiveDownloadCount), - // v12 -> v13 - NewMigration("Add `hide_archive_links` column to `release` table", AddHideArchiveLinksToRelease), - // v13 -> v14 - NewMigration("Remove Gitea-specific columns from the repository and badge tables", RemoveGiteaSpecificColumnsFromRepositoryAndBadge), - // v14 -> v15 - NewMigration("Create the `federation_host` table", CreateFederationHostTable), - // v15 -> v16 - NewMigration("Create the `federated_user` table", CreateFederatedUserTable), - // v16 -> v17 - NewMigration("Add `normalized_federated_uri` column to `user` table", AddNormalizedFederatedURIToUser), - // v17 -> v18 - NewMigration("Create the `following_repo` table", CreateFollowingRepoTable), - // v18 -> v19 - NewMigration("Add external_url to attachment table", AddExternalURLColumnToAttachmentTable), - // v19 -> v20 - NewMigration("Creating Quota-related tables", CreateQuotaTables), - // v20 -> v21 - NewMigration("Add SSH keypair to `pull_mirror` table", AddSSHKeypairToPushMirror), - // v21 -> v22 - NewMigration("Add `legacy` to `web_authn_credential` table", AddLegacyToWebAuthnCredential), - // v22 -> v23 - NewMigration("Add `delete_branch_after_merge` to `auto_merge` table", AddDeleteBranchAfterMergeToAutoMerge), - // v23 -> v24 - NewMigration("Add `purpose` column to `forgejo_auth_token` table", AddPurposeToForgejoAuthToken), - // v24 -> v25 - NewMigration("Migrate `secret` column to store keying material", MigrateTwoFactorToKeying), - // v25 -> v26 - NewMigration("Add `hash_blake2b` column to `package_blob` table", AddHashBlake2bToPackageBlob), - // v26 -> v27 - NewMigration("Add `created_unix` column to `user_redirect` table", AddCreatedUnixToRedirect), - // v27 -> v28 - NewMigration("Add pronoun privacy settings to user", AddHidePronounsOptionToUser), - // v28 -> v29 - NewMigration("Add public key information to `FederatedUser` and `FederationHost`", AddPublicKeyInformationForFederation), - // v29 -> v30 - NewMigration("Migrate `User.NormalizedFederatedURI` column to extract port & schema into FederatedHost", MigrateNormalizedFederatedURI), - // v30 -> v31 - NewMigration("Normalize repository.topics to empty slice instead of null", SetTopicsAsEmptySlice), - // v31 -> v32 - NewMigration("Migrate maven package name concatenation", ChangeMavenArtifactConcatenation), - // v32 -> v33 - NewMigration("Add federated user activity tables, update the `federated_user` table & add indexes", FederatedUserActivityMigration), - // v33 -> v34 - NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun), - // v34 -> v35 - NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), - // v35 -> v36 - NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission), - // 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), - // v40 -> v41 - NewMigration("Add foreign keys to stopwatch & tracked_time", AddForeignKeysStopwatchTrackedTime), - // v41 -> v42 - NewMigration("Add action_run concurrency fields", AddActionRunConcurrency), - // v42 -> v43 - NewMigration("Add action_run pre_execution_error field", AddActionRunPreExecutionError), - // v43 -> v44 - NewMigration("Add foreign keys to access", AddForeignKeysAccess), -} - -// GetCurrentDBVersion returns the current Forgejo database version. -func GetCurrentDBVersion(x *xorm.Engine) (int64, error) { - if err := x.Sync(new(ForgejoVersion)); err != nil { - return -1, fmt.Errorf("sync: %w", err) - } - - currentVersion := &ForgejoVersion{ID: 1} - has, err := x.Get(currentVersion) - if err != nil { - return -1, fmt.Errorf("get: %w", err) - } - if !has { - return -1, nil - } - return currentVersion.Version, nil -} - -// ExpectedVersion returns the expected Forgejo database version. -func ExpectedVersion() int64 { - return int64(len(migrations)) -} - -// EnsureUpToDate will check if the Forgejo database is at the correct version. -func EnsureUpToDate(x *xorm.Engine) error { - currentDB, err := GetCurrentDBVersion(x) - if err != nil { - return err - } - - if currentDB < 0 { - return errors.New("database has not been initialized") - } - - expected := ExpectedVersion() - - if currentDB != expected { - return fmt.Errorf(`current Forgejo database version %d is not equal to the expected version %d. Please run "forgejo [--config /path/to/app.ini] migrate" to update the database version`, currentDB, expected) - } - - return forgejoMigrationsEnsureUpToDate(x) -} - -var forgejoMigrationsEnsureUpToDate = forgejo_migrations.EnsureUpToDate - -// Migrate Forgejo database to current version. -func Migrate(x *xorm.Engine) error { - // Set a new clean the default mapper to GonicMapper as that is the default for . - x.SetMapper(names.GonicMapper{}) - if err := x.Sync(new(ForgejoVersion)); err != nil { - return fmt.Errorf("sync: %w", err) - } - - currentVersion := &ForgejoVersion{ID: 1} - has, err := x.Get(currentVersion) - freshDB := false - if err != nil { - return fmt.Errorf("get: %w", err) - } else if !has { - // If the version record does not exist we think - // it is a fresh installation and we can skip all migrations. - currentVersion.ID = 0 - currentVersion.Version = ExpectedVersion() - freshDB = true - - if _, err = x.InsertOne(currentVersion); err != nil { - return fmt.Errorf("insert: %w", err) - } - } - - v := currentVersion.Version - - // Downgrading Forgejo's database version not supported - if v > ExpectedVersion() { - msg := fmt.Sprintf("Your Forgejo database (migration version: %d) is for a newer version of Forgejo, you cannot use the newer database for this old Forgejo release (%d).", v, ExpectedVersion()) - msg += "\nForgejo will exit to keep your database safe and unchanged. Please use the correct Forgejo release, do not change the migration version manually (incorrect manual operation may cause data loss)." - if !setting.IsProd { - msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE forgejo_version SET version=%d WHERE id=1;", ExpectedVersion()) - } - _, _ = fmt.Fprintln(os.Stderr, msg) - log.Fatal(msg) - return nil - } - - // Some migration tasks depend on the git command - if git.DefaultContext == nil { - if err = git.InitSimple(context.Background()); err != nil { - return err - } - } - - // Migrate - for i, m := range migrations[v:] { - log.Info("Migration[%d]: %s", v+int64(i), m.description) - // Reset the mapper between each migration - migrations are not supposed to depend on each other - x.SetMapper(names.GonicMapper{}) - if err = m.migrate(x); err != nil { - return fmt.Errorf("migration[%d]: %s failed: %w", v+int64(i), m.description, err) - } - currentVersion.Version = v + int64(i) + 1 - if _, err = x.ID(1).Update(currentVersion); err != nil { - return err - } - } - - if err := x.Sync(new(semver.ForgejoSemVer)); err != nil { - return fmt.Errorf("sync: %w", err) - } - - if err := semver.SetVersionStringWithEngine(x, setting.ForgejoVersion); err != nil { - return fmt.Errorf("SetVersionStringWithEngine: %w", err) - } - - return forgejo_migrations.Migrate(x, freshDB) -} diff --git a/models/forgejo_migrations_legacy/migrate_test.go b/models/forgejo_migrations_legacy/migrate_test.go deleted file mode 100644 index 1a0fa14671..0000000000 --- a/models/forgejo_migrations_legacy/migrate_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2023 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package forgejo_migrations_legacy - -import ( - "testing" - - migration_tests "forgejo.org/models/gitea_migrations/test" - "forgejo.org/modules/test" - - "github.com/stretchr/testify/require" - "xorm.io/xorm" -) - -// TestEnsureUpToDate tests the behavior of EnsureUpToDate. -func TestEnsureUpToDate(t *testing.T) { - defer test.MockVariableValue(&forgejoMigrationsEnsureUpToDate, func(x *xorm.Engine) error { - return nil - })() - - x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ForgejoVersion)) - defer deferable() - if x == nil || t.Failed() { - return - } - - // Ensure error if there's no row in Forgejo Version. - err := EnsureUpToDate(x) - require.Error(t, err) - - // Insert 'good' Forgejo Version row. - _, err = x.InsertOne(&ForgejoVersion{ID: 1, Version: ExpectedVersion()}) - require.NoError(t, err) - - err = EnsureUpToDate(x) - require.NoError(t, err) - - // Modify forgejo version to have a lower version. - _, err = x.Exec("UPDATE `forgejo_version` SET version = ? WHERE id = 1", ExpectedVersion()-1) - require.NoError(t, err) - - err = EnsureUpToDate(x) - require.Error(t, err) -} diff --git a/models/forgejo_migrations_legacy/v37.go b/models/forgejo_migrations_legacy/v37.go deleted file mode 100644 index 508f798055..0000000000 --- a/models/forgejo_migrations_legacy/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_legacy - -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_legacy/v38.go b/models/forgejo_migrations_legacy/v38.go deleted file mode 100644 index 93cff2cabb..0000000000 --- a/models/forgejo_migrations_legacy/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_legacy - -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_legacy/v39.go b/models/forgejo_migrations_legacy/v39.go deleted file mode 100644 index 3f0d94b6aa..0000000000 --- a/models/forgejo_migrations_legacy/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_legacy - -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_legacy/v39_test.go b/models/forgejo_migrations_legacy/v39_test.go deleted file mode 100644 index 6c82f7180f..0000000000 --- a/models/forgejo_migrations_legacy/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_legacy - -import ( - "testing" - - migration_tests "forgejo.org/models/gitea_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_legacy/v40.go b/models/forgejo_migrations_legacy/v40.go deleted file mode 100644 index 50dccd393c..0000000000 --- a/models/forgejo_migrations_legacy/v40.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2024 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package forgejo_migrations_legacy - -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/forgejo_migrations_legacy/v41.go b/models/forgejo_migrations_legacy/v41.go deleted file mode 100644 index 4b3306debd..0000000000 --- a/models/forgejo_migrations_legacy/v41.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations_legacy - -import ( - "errors" - "fmt" - - "forgejo.org/modules/log" - - "xorm.io/builder" - "xorm.io/xorm" -) - -func syncDoctorForeignKey(x *xorm.Engine, beans []any) error { - for _, bean := range beans { - // Sync() drops indexes by default, which will cause unnecessary rebuilding of indexes when syncDoctorForeignKey - // is used with partial bean definitions; so we disable that option - _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, bean) - if err != nil { - if errors.Is(err, xorm.ErrForeignKeyViolation) { - tableName := x.TableName(bean) - log.Error( - "Foreign key creation on table %s failed. Run `forgejo doctor check --all` to identify the orphaned records preventing this foreign key from being created. Error was: %v", - tableName, err) - return err - } - return err - } - } - return nil -} - -func AddForeignKeysStopwatchTrackedTime(x *xorm.Engine) error { - type Stopwatch struct { - IssueID int64 `xorm:"INDEX REFERENCES(issue, id)"` - UserID int64 `xorm:"INDEX REFERENCES(user, id)"` - } - type TrackedTime struct { - ID int64 `xorm:"pk autoincr"` - IssueID int64 `xorm:"INDEX REFERENCES(issue, id)"` - UserID int64 `xorm:"INDEX REFERENCES(user, id)"` - } - - // TrackedTime.UserID used to be an intentionally dangling reference if a user was deleted, in order to maintain the - // time that was tracked against an issue. With the addition of a foreign key, we set UserID to NULL where the user - // doesn't exist instead of leaving it pointing to an invalid record: - var trackedTime []TrackedTime - err := x.Table("tracked_time"). - Join("LEFT", "`user`", "`tracked_time`.user_id = `user`.id"). - Where(builder.IsNull{"`user`.id"}). - Find(&trackedTime) - if err != nil { - return err - } - for _, tt := range trackedTime { - affected, err := x.Table(&TrackedTime{}).Where("id = ?", tt.ID).Update(map[string]any{"user_id": nil}) - if err != nil { - return err - } else if affected != 1 { - return fmt.Errorf("expected to update 1 tracked_time record with ID %d, but actually affected %d records", tt.ID, affected) - } - } - - return syncDoctorForeignKey(x, []any{ - new(Stopwatch), - new(TrackedTime), - }) -} diff --git a/models/forgejo_migrations_legacy/v42.go b/models/forgejo_migrations_legacy/v42.go deleted file mode 100644 index 940c270699..0000000000 --- a/models/forgejo_migrations_legacy/v42.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations_legacy - -import "xorm.io/xorm" - -func AddActionRunConcurrency(x *xorm.Engine) error { - type ActionRun struct { - RepoID int64 `xorm:"index unique(repo_index) index(concurrency)"` - Index int64 `xorm:"index unique(repo_index)"` - ConcurrencyGroup string `xorm:"index(concurrency)"` - ConcurrencyType int - } - _, err := x.SyncWithOptions(xorm.SyncOptions{ - // Sync drops indexes by default, and this local ActionRun doesn't have all the indexes -- so disable that. - IgnoreDropIndices: true, - }, new(ActionRun)) - return err -} diff --git a/models/forgejo_migrations_legacy/v43.go b/models/forgejo_migrations_legacy/v43.go deleted file mode 100644 index 215299031c..0000000000 --- a/models/forgejo_migrations_legacy/v43.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations_legacy - -import "xorm.io/xorm" - -func AddActionRunPreExecutionError(x *xorm.Engine) error { - type ActionRun struct { - PreExecutionError string `xorm:"LONGTEXT"` - } - _, err := x.SyncWithOptions(xorm.SyncOptions{ - // Sync drops indexes by default, and this local ActionRun doesn't have all the indexes -- so disable that. - IgnoreDropIndices: true, - }, new(ActionRun)) - return err -} diff --git a/models/forgejo_migrations_legacy/v44.go b/models/forgejo_migrations_legacy/v44.go deleted file mode 100644 index 459c7ac29d..0000000000 --- a/models/forgejo_migrations_legacy/v44.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations_legacy - -import ( - "xorm.io/xorm" -) - -func AddForeignKeysAccess(x *xorm.Engine) error { - type Access struct { - UserID int64 `xorm:"UNIQUE(s) REFERENCES(user, id)"` - RepoID int64 `xorm:"UNIQUE(s) REFERENCES(repository, id)"` - } - return syncDoctorForeignKey(x, []any{ - new(Access), - }) -} diff --git a/models/gitea_migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml b/models/gitea_migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml deleted file mode 100644 index 908b428321..0000000000 --- a/models/gitea_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/gitea_migrations/v1_18/main_test.go b/models/gitea_migrations/v1_18/main_test.go deleted file mode 100644 index 73e634ca35..0000000000 --- a/models/gitea_migrations/v1_18/main_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_18 - -import ( - "testing" - - migration_tests "forgejo.org/models/gitea_migrations/test" -) - -func TestMain(m *testing.M) { - migration_tests.MainTest(m) -} diff --git a/models/gitea_migrations/v1_19/main_test.go b/models/gitea_migrations/v1_19/main_test.go deleted file mode 100644 index 6bf5ec4cc9..0000000000 --- a/models/gitea_migrations/v1_19/main_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_19 - -import ( - "testing" - - migration_tests "forgejo.org/models/gitea_migrations/test" -) - -func TestMain(m *testing.M) { - migration_tests.MainTest(m) -} diff --git a/models/issues/TestGetUIDsAndStopwatch/stopwatch.yml b/models/issues/TestGetUIDsAndStopwatch/stopwatch.yml index 231d142dc4..f564e4b389 100644 --- a/models/issues/TestGetUIDsAndStopwatch/stopwatch.yml +++ b/models/issues/TestGetUIDsAndStopwatch/stopwatch.yml @@ -3,3 +3,9 @@ user_id: 1 issue_id: 2 created_unix: 1500988004 + +- + id: 4 + user_id: 3 + issue_id: 0 + created_unix: 1500988003 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..523a6ba9b9 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 } diff --git a/models/issues/issue.go b/models/issues/issue.go index b1966ea50f..5edebb4105 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -116,7 +116,7 @@ type Issue struct { DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"` - Created timeutil.TimeStampNano // more precise Created, but may not be populated for older issues + Created timeutil.TimeStampNano CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` @@ -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 fbfcd3529a..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") @@ -460,7 +455,7 @@ func applySubscribedCondition(sess *xorm.Session, subscriberID int64) { ), builder.Eq{"issue.poster_id": subscriberID}, builder.In("issue.repo_id", builder. - Select("repo_id"). + Select("id"). From("watch"). Where(builder.And(builder.Eq{"user_id": subscriberID}, builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))), @@ -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/issue_test.go b/models/issues/issue_test.go index 21f483b391..8f40d3bdd9 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -211,30 +211,6 @@ func TestIssues(t *testing.T) { }, []int64{2}, }, - { - issues_model.IssuesOptions{ - SubscriberID: 11, - }, - []int64{11, 5, 9, 8, 3, 2, 1}, - }, - { - issues_model.IssuesOptions{ - SubscriberID: 4, - }, - []int64{11, 5, 7, 4, 3, 2, 1}, - }, - { - issues_model.IssuesOptions{ - SubscriberID: 1, - }, - []int64{11, 6, 5, 3, 2, 1}, - }, - { - issues_model.IssuesOptions{ - SubscriberID: 8, - }, - []int64{}, - }, } { issues, err := issues_model.Issues(db.DefaultContext, &test.Opts) require.NoError(t, err) diff --git a/models/issues/moderation.go b/models/issues/moderation.go index 9afb711d65..921f770d4d 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 { 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/stopwatch.go b/models/issues/stopwatch.go index 90a637e993..2ff2a17d92 100644 --- a/models/issues/stopwatch.go +++ b/models/issues/stopwatch.go @@ -32,8 +32,8 @@ func (err ErrIssueStopwatchNotExist) Unwrap() error { // Stopwatch represents a stopwatch for time tracking. type Stopwatch struct { ID int64 `xorm:"pk autoincr"` - IssueID int64 `xorm:"INDEX REFERENCES(issue, id)"` - UserID int64 `xorm:"INDEX REFERENCES(user, id)"` + IssueID int64 `xorm:"INDEX"` + UserID int64 `xorm:"INDEX"` CreatedUnix timeutil.TimeStamp `xorm:"created"` } @@ -63,7 +63,7 @@ func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, ex // GetUIDsAndNotificationCounts between the two provided times func GetUIDsAndStopwatch(ctx context.Context) (map[int64][]*Stopwatch, error) { sws := []*Stopwatch{} - if err := db.GetEngine(ctx).Find(&sws); err != nil { + if err := db.GetEngine(ctx).Where("issue_id != 0").Find(&sws); err != nil { return nil, err } res := map[int64][]*Stopwatch{} diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go index d229d83470..05d7b15815 100644 --- a/models/issues/tracked_time.go +++ b/models/issues/tracked_time.go @@ -22,9 +22,9 @@ import ( // TrackedTime represents a time that was spent for a specific issue. type TrackedTime struct { ID int64 `xorm:"pk autoincr"` - IssueID int64 `xorm:"INDEX REFERENCES(issue, id)"` + IssueID int64 `xorm:"INDEX"` Issue *Issue `xorm:"-"` - UserID int64 `xorm:"INDEX REFERENCES(user, id)"` + UserID int64 `xorm:"INDEX"` User *user_model.User `xorm:"-"` Created time.Time `xorm:"-"` CreatedUnix int64 `xorm:"created"` diff --git a/models/gitea_migrations/base/db.go b/models/migrations/base/db.go similarity index 59% rename from models/gitea_migrations/base/db.go rename to models/migrations/base/db.go index 2f70fc0806..7f7edda53b 100644 --- a/models/gitea_migrations/base/db.go +++ b/models/migrations/base/db.go @@ -8,10 +8,8 @@ import ( "fmt" "reflect" "regexp" - "slices" "strings" - "forgejo.org/models/db" "forgejo.org/modules/log" "forgejo.org/modules/setting" @@ -19,12 +17,7 @@ import ( "xorm.io/xorm/schemas" ) -// RecreateTables returns a function that will recreate the tables for the provided beans using the newly provided bean -// definition, move all data to the new tables, and then replace the original tables with a drop and rename. -// -// If any 'base' table is requested to be rebuilt where one-or-more 'satellite' tables exists that references it through -// a foreign key, you must rebuild the satellite tables as well or you will receive an error 'incomplete table set'. -// +// RecreateTables will recreate the tables for the provided beans using the newly provided bean definition and move all data to that new table // WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION func RecreateTables(beans ...any) func(*xorm.Engine) error { return func(x *xorm.Engine) error { @@ -34,179 +27,34 @@ func RecreateTables(beans ...any) func(*xorm.Engine) error { return err } sess = sess.StoreEngine("InnoDB") - - tableNames := make(map[any]string, len(beans)) - tempTableNames := make(map[any]string, len(beans)) - tempTableNamesByOriginalName := make(map[string]string, len(beans)) for _, bean := range beans { - tableName := sess.Engine().TableName(bean) - tableNames[bean] = tableName - tempTableName := fmt.Sprintf("tmp_recreate__%s", tableName) - tempTableNames[bean] = tempTableName - tempTableNamesByOriginalName[tableName] = tempTableName - } - - // Create a set of temp tables. - for _, bean := range beans { - log.Info("Creating temp table: %s for Bean: %s", tempTableNames[bean], reflect.Indirect(reflect.ValueOf(bean)).Type().Name()) - if err := createTempTable(sess, bean, tempTableNames[bean]); err != nil { + log.Info("Recreating Table: %s for Bean: %s", x.TableName(bean), reflect.Indirect(reflect.ValueOf(bean)).Type().Name()) + if err := RecreateTable(sess, bean); err != nil { return err } } - - // Our new temp tables tables will have foreign keys that point to the original tables we are recreating. - // Before we put data into these tables, we need to drop those foreign keys and add new foreign keys that point - // to the temp tables. - tableSchemas := make(map[any]*schemas.Table, len(beans)) - for _, bean := range beans { - tableSchema, err := sess.Engine().TableInfo(bean) - if err != nil { - log.Error("Unable to get table info. Error: %v", err) - return err - } - tableSchemas[bean] = tableSchema - modifications := make([]schemas.TableModification, 0, len(tableSchema.ForeignKeys)*2) - for _, fk := range tableSchema.ForeignKeys { - targetTempTableName, ok := tempTableNamesByOriginalName[fk.TargetTableName] - if !ok { - return fmt.Errorf("incomplete table set: Found a foreign key reference to table %s, but it is not included in RecreateTables", fk.TargetTableName) - } - fkName := fk.Name - if setting.Database.Type.IsMySQL() { - // See MySQL explanation in createTempTable. - fkName = "_" + fkName - } - modifications = append(modifications, schemas.DropForeignKey{ForeignKey: schemas.ForeignKey{ - Name: fkName, - SourceFieldName: fk.SourceFieldName, - TargetTableName: fk.TargetTableName, - TargetFieldName: fk.TargetFieldName, - }}) - modifications = append(modifications, schemas.AddForeignKey{ForeignKey: schemas.ForeignKey{ - Name: fkName, - SourceFieldName: fk.SourceFieldName, - TargetTableName: targetTempTableName, // FK changed to new temp table - TargetFieldName: fk.TargetFieldName, - }}) - } - - if len(modifications) != 0 { - log.Info("Modifying temp table %s foreign keys to point to other temp tables", tempTableNames[bean]) - if err := sess.Table(tempTableNames[bean]).AlterTable(bean, modifications...); err != nil { - return fmt.Errorf("alter table failed: while rewriting foreign keys to temp tables, error occurred: %w", err) - } - } - } - - // Insert into the set of temp tables in the right order, starting with base tables, working outwards to - // satellite tables. - orderedBeans := slices.Clone(beans) - slices.SortFunc(orderedBeans, func(b1, b2 any) int { - return db.TableNameInsertionOrderSortFunc(tableNames[b1], tableNames[b2]) - }) - for _, bean := range orderedBeans { - log.Info("Copying table %s to temp table %s", tableNames[bean], tempTableNames[bean]) - if err := copyData(sess, bean, tableNames[bean], tempTableNames[bean]); err != nil { - // copyData does its own logging - return err - } - } - - // Drop all the old tables in the right order, starting with satellite tables working inwards to base tables, - // and rename all the temp tables to the final tables. The database will automatically update the foreign key - // references during the rename from temp to final tables. - for i := len(orderedBeans) - 1; i >= 0; i-- { - bean := orderedBeans[i] - log.Info("Dropping existing table %s, and renaming temp table %s in its place", tableNames[bean], tempTableNames[bean]) - if err := renameTable(sess, bean, tableNames[bean], tempTableNames[bean], tableSchemas[bean]); err != nil { - // renameTable does its own logging - return err - } - } - return sess.Commit() } } -// LegacyRecreateTable will recreate the table using the newly provided bean definition and move all data to that new -// table. -// +// RecreateTable will recreate the table using the newly provided bean definition and move all data to that new table // WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION -// // WARNING: YOU MUST COMMIT THE SESSION AT THE END -// -// Deprecated: LegacyRecreateTable exists for historical migrations and should not be used in current code -- tt does -// not support foreign key management. Use RecreateTables instead which provides foreign key support. -func LegacyRecreateTable(sess *xorm.Session, bean any) error { +func RecreateTable(sess *xorm.Session, bean any) error { + // TODO: This will not work if there are foreign keys + tableName := sess.Engine().TableName(bean) tempTableName := fmt.Sprintf("tmp_recreate__%s", tableName) - tableSchema, err := sess.Engine().TableInfo(bean) - if err != nil { - log.Error("Unable to get table info. Error: %v", err) - return err - } - // We need to move the old table away and create a new one with the correct columns // We will need to do this in stages to prevent data loss // // First create the temporary table - if err := createTempTable(sess, bean, tempTableName); err != nil { - // createTempTable does its own logging + if err := sess.Table(tempTableName).CreateTable(bean); err != nil { + log.Error("Unable to create table %s. Error: %v", tempTableName, err) return err } - if err := copyData(sess, bean, tableName, tempTableName); err != nil { - // copyData does its own logging - return err - } - - if err := renameTable(sess, bean, tableName, tempTableName, tableSchema); err != nil { - // renameTable does its own logging - return err - } - - return nil -} - -func createTempTable(sess *xorm.Session, bean any, tempTableName string) error { - if setting.Database.Type.IsMySQL() { - // Can't have identical foreign key names in MySQL, and Table(tempTableName) only affects the table name and not - // the schema definition generated from the bean, so, we do a little adjusting by appending a `_` at the - // beginning of each foreign key name on the temp table. We'll remove this by renaming the constraint after we - // drop the original table, in renameTable. - originalTableSchema, err := sess.Engine().TableInfo(bean) - if err != nil { - log.Error("Unable to get table info. Error: %v", err) - return err - } - - // `TableInfo()` will return a `*schema.Table` that is stored in a shared cache. We don't want to mutate that - // object as it will stick around and affect other things. Make a mostly-shallow clone, with a new slice for - // what we're changing. - tableSchema := *originalTableSchema - tableSchema.ForeignKeys = slices.Clone(originalTableSchema.ForeignKeys) - for i := range tableSchema.ForeignKeys { - tableSchema.ForeignKeys[i].Name = "_" + tableSchema.ForeignKeys[i].Name - } - - sql, _, err := sess.Engine().Dialect().CreateTableSQL(&tableSchema, tempTableName) - if err != nil { - log.Error("Unable to generate CREATE TABLE query. Error: %v", err) - return err - } - _, err = sess.Exec(sql) - if err != nil { - log.Error("Unable to create table %s. Error: %v", tempTableName, err) - return err - } - } else { - if err := sess.Table(tempTableName).CreateTable(bean); err != nil { - log.Error("Unable to create table %s. Error: %v", tempTableName, err) - return err - } - } - if err := sess.Table(tempTableName).CreateUniques(bean); err != nil { log.Error("Unable to create uniques for table %s. Error: %v", tempTableName, err) return err @@ -217,14 +65,11 @@ func createTempTable(sess *xorm.Session, bean any, tempTableName string) error { return err } - return nil -} - -func copyData(sess *xorm.Session, bean any, tableName, tempTableName string) error { // Work out the column names from the bean - these are the columns to select from the old table and install into the new table table, err := sess.Engine().TableInfo(bean) if err != nil { log.Error("Unable to get table info. Error: %v", err) + return err } newTableColumns := table.Columns() @@ -283,12 +128,9 @@ func copyData(sess *xorm.Session, bean any, tableName, tempTableName string) err return err } - return nil -} - -func renameTable(sess *xorm.Session, bean any, tableName, tempTableName string, tableSchema *schemas.Table) error { switch { case setting.Database.Type.IsSQLite3(): + // SQLite will drop all the constraints on the old table if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { log.Error("Unable to drop old table %s. Error: %v", tableName, err) return err @@ -315,44 +157,32 @@ func renameTable(sess *xorm.Session, bean any, tableName, tempTableName string, } case setting.Database.Type.IsMySQL(): + // MySQL will drop all the constraints on the old table if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { log.Error("Unable to drop old table %s. Error: %v", tableName, err) return err } - // MySQL will move all the constraints that reference this table from the temporary table to the new table + if err := sess.Table(tempTableName).DropIndexes(bean); err != nil { + log.Error("Unable to drop indexes on temporary table %s. Error: %v", tempTableName, err) + return err + } + + // SQLite and MySQL will move all the constraints from the temporary table to the new table if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` RENAME TO `%s`", tempTableName, tableName)); err != nil { log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) return err } - // In `RecreateTables` the foreign keys were renamed with a '_' prefix to avoid conflicting on the original - // table's constraint names. Now that table has been dropped, so we can rename them back to leave the table in - // the right state. Unfortunately this will cause a recheck of the constraint's validity against the target - // table which will be slow for large tables, but it's unavoidable without the ability to rename constraints - // in-place. Awkwardly these FKs are still a reference to the tmp_recreate target table since we drop in reverse - // FK order -- the ALTER TABLE ... RENAME .. on those tmp tables will correct the FKs later. - modifications := make([]schemas.TableModification, 0, len(tableSchema.ForeignKeys)*2) - for _, fk := range tableSchema.ForeignKeys { - modifications = append(modifications, schemas.DropForeignKey{ForeignKey: schemas.ForeignKey{ - Name: "_" + fk.Name, - SourceFieldName: fk.SourceFieldName, - TargetTableName: fmt.Sprintf("tmp_recreate__%s", fk.TargetTableName), - TargetFieldName: fk.TargetFieldName, - }}) - modifications = append(modifications, schemas.AddForeignKey{ForeignKey: schemas.ForeignKey{ - Name: fk.Name, - SourceFieldName: fk.SourceFieldName, - TargetTableName: fmt.Sprintf("tmp_recreate__%s", fk.TargetTableName), - TargetFieldName: fk.TargetFieldName, - }}) - } - if len(modifications) != 0 { - if err := sess.Table(tableName).AlterTable(bean, modifications...); err != nil { - return fmt.Errorf("alter table failed: while rewriting foreign keys to original names, error occurred: %w", err) - } + if err := sess.Table(tableName).CreateIndexes(bean); err != nil { + log.Error("Unable to recreate indexes on table %s. Error: %v", tableName, err) + return err } + if err := sess.Table(tableName).CreateUniques(bean); err != nil { + log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err) + return err + } case setting.Database.Type.IsPostgreSQL(): var originalSequences []string type sequenceData struct { @@ -363,7 +193,7 @@ func renameTable(sess *xorm.Session, bean any, tableName, tempTableName string, schema := sess.Engine().Dialect().URI().Schema sess.Engine().SetSchema("") - if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE ? || '_id_seq' AND sequence_catalog = ?", tableName, setting.Database.Name).Find(&originalSequences); err != nil { + if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE ? || '_%' AND sequence_catalog = ?", tableName, setting.Database.Name).Find(&originalSequences); err != nil { log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) return err } @@ -408,7 +238,7 @@ func renameTable(sess *xorm.Session, bean any, tableName, tempTableName string, var sequences []string sess.Engine().SetSchema("") - if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE 'tmp_recreate__' || ? || '_id_seq' AND sequence_catalog = ?", tableName, setting.Database.Name).Find(&sequences); err != nil { + if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE 'tmp_recreate__' || ? || '_%' AND sequence_catalog = ?", tableName, setting.Database.Name).Find(&sequences); err != nil { log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) return err } @@ -445,7 +275,6 @@ func renameTable(sess *xorm.Session, bean any, tableName, tempTableName string, default: log.Fatal("Unrecognized DB") } - return nil } diff --git a/models/gitea_migrations/base/db_test.go b/models/migrations/base/db_test.go similarity index 97% rename from models/gitea_migrations/base/db_test.go rename to models/migrations/base/db_test.go index 4633c7de10..4a610e065d 100644 --- a/models/gitea_migrations/base/db_test.go +++ b/models/migrations/base/db_test.go @@ -6,7 +6,7 @@ package base import ( "testing" - migrations_tests "forgejo.org/models/gitea_migrations/test" + migrations_tests "forgejo.org/models/migrations/test" "forgejo.org/modules/timeutil" "xorm.io/xorm/names" diff --git a/models/gitea_migrations/base/hash.go b/models/migrations/base/hash.go similarity index 100% rename from models/gitea_migrations/base/hash.go rename to models/migrations/base/hash.go diff --git a/models/gitea_migrations/base/main_test.go b/models/migrations/base/main_test.go similarity index 75% rename from models/gitea_migrations/base/main_test.go rename to models/migrations/base/main_test.go index 73bc7a3521..2b3889441a 100644 --- a/models/gitea_migrations/base/main_test.go +++ b/models/migrations/base/main_test.go @@ -6,7 +6,7 @@ package base import ( "testing" - migrations_tests "forgejo.org/models/gitea_migrations/test" + migrations_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/gitea_migrations/fixtures/Test_AddCombinedIndexToIssueUser/issue_user.yml b/models/migrations/fixtures/Test_AddCombinedIndexToIssueUser/issue_user.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddCombinedIndexToIssueUser/issue_user.yml rename to models/migrations/fixtures/Test_AddCombinedIndexToIssueUser/issue_user.yml diff --git a/models/gitea_migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml b/models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml rename to models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml diff --git a/models/gitea_migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/expected_webhook.yml b/models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/expected_webhook.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/expected_webhook.yml rename to models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/expected_webhook.yml diff --git a/models/gitea_migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/hook_task.yml b/models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/hook_task.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/hook_task.yml rename to models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/hook_task.yml diff --git a/models/gitea_migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/webhook.yml b/models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/webhook.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/webhook.yml rename to models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/webhook.yml diff --git a/models/gitea_migrations/fixtures/Test_AddIssueResourceIndexTable/issue.yml b/models/migrations/fixtures/Test_AddIssueResourceIndexTable/issue.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddIssueResourceIndexTable/issue.yml rename to models/migrations/fixtures/Test_AddIssueResourceIndexTable/issue.yml diff --git a/models/gitea_migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task.yml b/models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task.yml rename to models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task.yml diff --git a/models/gitea_migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task_migrated.yml b/models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task_migrated.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task_migrated.yml rename to models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task_migrated.yml diff --git a/models/gitea_migrations/fixtures/Test_AddRepoIDForAttachment/attachment.yml b/models/migrations/fixtures/Test_AddRepoIDForAttachment/attachment.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddRepoIDForAttachment/attachment.yml rename to models/migrations/fixtures/Test_AddRepoIDForAttachment/attachment.yml diff --git a/models/gitea_migrations/fixtures/Test_AddRepoIDForAttachment/issue.yml b/models/migrations/fixtures/Test_AddRepoIDForAttachment/issue.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddRepoIDForAttachment/issue.yml rename to models/migrations/fixtures/Test_AddRepoIDForAttachment/issue.yml diff --git a/models/gitea_migrations/fixtures/Test_AddRepoIDForAttachment/release.yml b/models/migrations/fixtures/Test_AddRepoIDForAttachment/release.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddRepoIDForAttachment/release.yml rename to models/migrations/fixtures/Test_AddRepoIDForAttachment/release.yml diff --git a/models/gitea_migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml b/models/migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml rename to models/migrations/fixtures/Test_AddUniqueIndexForProjectIssue/project_issue.yml diff --git a/models/gitea_migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package.yml b/models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package.yml rename to models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package.yml diff --git a/models/gitea_migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_blob.yml b/models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_blob.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_blob.yml rename to models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_blob.yml diff --git a/models/gitea_migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_file.yml b/models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_file.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_file.yml rename to models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_file.yml diff --git a/models/gitea_migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_version.yml b/models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_version.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_version.yml rename to models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_version.yml diff --git a/models/gitea_migrations/fixtures/Test_CheckProjectColumnsConsistency/project.yml b/models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_CheckProjectColumnsConsistency/project.yml rename to models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project.yml diff --git a/models/gitea_migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml b/models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml rename to models/migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml diff --git a/models/gitea_migrations/fixtures/Test_DeleteOrphanedIssueLabels/issue_label.yml b/models/migrations/fixtures/Test_DeleteOrphanedIssueLabels/issue_label.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_DeleteOrphanedIssueLabels/issue_label.yml rename to models/migrations/fixtures/Test_DeleteOrphanedIssueLabels/issue_label.yml diff --git a/models/gitea_migrations/fixtures/Test_DeleteOrphanedIssueLabels/label.yml b/models/migrations/fixtures/Test_DeleteOrphanedIssueLabels/label.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_DeleteOrphanedIssueLabels/label.yml rename to models/migrations/fixtures/Test_DeleteOrphanedIssueLabels/label.yml diff --git a/models/gitea_migrations/fixtures/Test_MigrateNormalizedFederatedURI/federated_user.yaml b/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federated_user.yaml similarity index 100% rename from models/gitea_migrations/fixtures/Test_MigrateNormalizedFederatedURI/federated_user.yaml rename to models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federated_user.yaml diff --git a/models/gitea_migrations/fixtures/Test_MigrateNormalizedFederatedURI/federation_host.yaml b/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federation_host.yaml similarity index 100% rename from models/gitea_migrations/fixtures/Test_MigrateNormalizedFederatedURI/federation_host.yaml rename to models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federation_host.yaml diff --git a/models/gitea_migrations/fixtures/Test_MigrateNormalizedFederatedURI/user.yaml b/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/user.yaml similarity index 100% rename from models/gitea_migrations/fixtures/Test_MigrateNormalizedFederatedURI/user.yaml rename to models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/user.yaml diff --git a/models/gitea_migrations/fixtures/Test_MigrateTwoFactorToKeying/two_factor.yml b/models/migrations/fixtures/Test_MigrateTwoFactorToKeying/two_factor.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_MigrateTwoFactorToKeying/two_factor.yml rename to models/migrations/fixtures/Test_MigrateTwoFactorToKeying/two_factor.yml diff --git a/models/gitea_migrations/fixtures/Test_RemigrateU2FCredentials/expected_webauthn_credential.yml b/models/migrations/fixtures/Test_RemigrateU2FCredentials/expected_webauthn_credential.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RemigrateU2FCredentials/expected_webauthn_credential.yml rename to models/migrations/fixtures/Test_RemigrateU2FCredentials/expected_webauthn_credential.yml diff --git a/models/gitea_migrations/fixtures/Test_RemigrateU2FCredentials/u2f_registration.yml b/models/migrations/fixtures/Test_RemigrateU2FCredentials/u2f_registration.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RemigrateU2FCredentials/u2f_registration.yml rename to models/migrations/fixtures/Test_RemigrateU2FCredentials/u2f_registration.yml diff --git a/models/gitea_migrations/fixtures/Test_RemigrateU2FCredentials/webauthn_credential.yml b/models/migrations/fixtures/Test_RemigrateU2FCredentials/webauthn_credential.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RemigrateU2FCredentials/webauthn_credential.yml rename to models/migrations/fixtures/Test_RemigrateU2FCredentials/webauthn_credential.yml diff --git a/models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/comment.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/comment.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/comment.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/comment.yml diff --git a/models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/issue.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/issue.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/issue.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/issue.yml diff --git a/models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/issue_label.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/issue_label.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/issue_label.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/issue_label.yml diff --git a/models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/label.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/label.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/label.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/label.yml diff --git a/models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/repository.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/repository.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RemoveInvalidLabels/repository.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/repository.yml diff --git a/models/gitea_migrations/fixtures/Test_RemoveSSHSignaturesFromReleaseNotes/release.yml b/models/migrations/fixtures/Test_RemoveSSHSignaturesFromReleaseNotes/release.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RemoveSSHSignaturesFromReleaseNotes/release.yml rename to models/migrations/fixtures/Test_RemoveSSHSignaturesFromReleaseNotes/release.yml diff --git a/models/gitea_migrations/fixtures/Test_RepositoryFormat/comment.yml b/models/migrations/fixtures/Test_RepositoryFormat/comment.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RepositoryFormat/comment.yml rename to models/migrations/fixtures/Test_RepositoryFormat/comment.yml diff --git a/models/gitea_migrations/fixtures/Test_RepositoryFormat/commit_status.yml b/models/migrations/fixtures/Test_RepositoryFormat/commit_status.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RepositoryFormat/commit_status.yml rename to models/migrations/fixtures/Test_RepositoryFormat/commit_status.yml diff --git a/models/gitea_migrations/fixtures/Test_RepositoryFormat/pull_request.yml b/models/migrations/fixtures/Test_RepositoryFormat/pull_request.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RepositoryFormat/pull_request.yml rename to models/migrations/fixtures/Test_RepositoryFormat/pull_request.yml diff --git a/models/gitea_migrations/fixtures/Test_RepositoryFormat/release.yml b/models/migrations/fixtures/Test_RepositoryFormat/release.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RepositoryFormat/release.yml rename to models/migrations/fixtures/Test_RepositoryFormat/release.yml diff --git a/models/gitea_migrations/fixtures/Test_RepositoryFormat/repo_archiver.yml b/models/migrations/fixtures/Test_RepositoryFormat/repo_archiver.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RepositoryFormat/repo_archiver.yml rename to models/migrations/fixtures/Test_RepositoryFormat/repo_archiver.yml diff --git a/models/gitea_migrations/fixtures/Test_RepositoryFormat/repo_indexer_status.yml b/models/migrations/fixtures/Test_RepositoryFormat/repo_indexer_status.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RepositoryFormat/repo_indexer_status.yml rename to models/migrations/fixtures/Test_RepositoryFormat/repo_indexer_status.yml diff --git a/models/gitea_migrations/fixtures/Test_RepositoryFormat/repository.yml b/models/migrations/fixtures/Test_RepositoryFormat/repository.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RepositoryFormat/repository.yml rename to models/migrations/fixtures/Test_RepositoryFormat/repository.yml diff --git a/models/gitea_migrations/fixtures/Test_RepositoryFormat/review_state.yml b/models/migrations/fixtures/Test_RepositoryFormat/review_state.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_RepositoryFormat/review_state.yml rename to models/migrations/fixtures/Test_RepositoryFormat/review_state.yml diff --git a/models/gitea_migrations/fixtures/Test_SetTopicsAsEmptySlice/repository.yml b/models/migrations/fixtures/Test_SetTopicsAsEmptySlice/repository.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_SetTopicsAsEmptySlice/repository.yml rename to models/migrations/fixtures/Test_SetTopicsAsEmptySlice/repository.yml diff --git a/models/gitea_migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml b/models/migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml rename to models/migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml diff --git a/models/gitea_migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/webauthn_credential.yml b/models/migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/webauthn_credential.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/webauthn_credential.yml rename to models/migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/webauthn_credential.yml diff --git a/models/gitea_migrations/fixtures/Test_UnwrapLDAPSourceCfg/login_source.yml b/models/migrations/fixtures/Test_UnwrapLDAPSourceCfg/login_source.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_UnwrapLDAPSourceCfg/login_source.yml rename to models/migrations/fixtures/Test_UnwrapLDAPSourceCfg/login_source.yml diff --git a/models/gitea_migrations/fixtures/Test_UpdateBadgeColName/badge.yml b/models/migrations/fixtures/Test_UpdateBadgeColName/badge.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_UpdateBadgeColName/badge.yml rename to models/migrations/fixtures/Test_UpdateBadgeColName/badge.yml diff --git a/models/gitea_migrations/fixtures/Test_UpdateOpenMilestoneCounts/expected_milestone.yml b/models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/expected_milestone.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_UpdateOpenMilestoneCounts/expected_milestone.yml rename to models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/expected_milestone.yml diff --git a/models/gitea_migrations/fixtures/Test_UpdateOpenMilestoneCounts/issue.yml b/models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/issue.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_UpdateOpenMilestoneCounts/issue.yml rename to models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/issue.yml diff --git a/models/gitea_migrations/fixtures/Test_UpdateOpenMilestoneCounts/milestone.yml b/models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/milestone.yml similarity index 100% rename from models/gitea_migrations/fixtures/Test_UpdateOpenMilestoneCounts/milestone.yml rename to models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/milestone.yml diff --git a/models/gitea_migrations/migrations.go b/models/migrations/migrations.go similarity index 96% rename from models/gitea_migrations/migrations.go rename to models/migrations/migrations.go index 7dae8bfcb4..2a5b97f519 100644 --- a/models/gitea_migrations/migrations.go +++ b/models/migrations/migrations.go @@ -2,7 +2,7 @@ // Copyright 2017 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package gitea_migrations +package migrations import ( "context" @@ -10,25 +10,25 @@ import ( "fmt" "forgejo.org/models/db" - "forgejo.org/models/forgejo_migrations_legacy" - "forgejo.org/models/gitea_migrations/v1_10" - "forgejo.org/models/gitea_migrations/v1_11" - "forgejo.org/models/gitea_migrations/v1_12" - "forgejo.org/models/gitea_migrations/v1_13" - "forgejo.org/models/gitea_migrations/v1_14" - "forgejo.org/models/gitea_migrations/v1_15" - "forgejo.org/models/gitea_migrations/v1_16" - "forgejo.org/models/gitea_migrations/v1_17" - "forgejo.org/models/gitea_migrations/v1_18" - "forgejo.org/models/gitea_migrations/v1_19" - "forgejo.org/models/gitea_migrations/v1_20" - "forgejo.org/models/gitea_migrations/v1_21" - "forgejo.org/models/gitea_migrations/v1_22" - "forgejo.org/models/gitea_migrations/v1_23" - "forgejo.org/models/gitea_migrations/v1_6" - "forgejo.org/models/gitea_migrations/v1_7" - "forgejo.org/models/gitea_migrations/v1_8" - "forgejo.org/models/gitea_migrations/v1_9" + "forgejo.org/models/forgejo_migrations" + "forgejo.org/models/migrations/v1_10" + "forgejo.org/models/migrations/v1_11" + "forgejo.org/models/migrations/v1_12" + "forgejo.org/models/migrations/v1_13" + "forgejo.org/models/migrations/v1_14" + "forgejo.org/models/migrations/v1_15" + "forgejo.org/models/migrations/v1_16" + "forgejo.org/models/migrations/v1_17" + "forgejo.org/models/migrations/v1_18" + "forgejo.org/models/migrations/v1_19" + "forgejo.org/models/migrations/v1_20" + "forgejo.org/models/migrations/v1_21" + "forgejo.org/models/migrations/v1_22" + "forgejo.org/models/migrations/v1_23" + "forgejo.org/models/migrations/v1_6" + "forgejo.org/models/migrations/v1_7" + "forgejo.org/models/migrations/v1_8" + "forgejo.org/models/migrations/v1_9" "forgejo.org/modules/git" "forgejo.org/modules/log" "forgejo.org/modules/setting" @@ -367,7 +367,7 @@ func prepareMigrationTasks() []*migration { // Migration to Forgejo v10 newMigration(303, "Gitea last drop", v1_23.GiteaLastDrop), - newMigration(304, "Migrate `secret` column to store keying material", forgejo_migrations_legacy.MigrateTwoFactorToKeying), + newMigration(304, "Migrate `secret` column to store keying material", forgejo_migrations.MigrateTwoFactorToKeying), } return preparedMigrations } @@ -426,7 +426,7 @@ func EnsureUpToDate(x *xorm.Engine) error { return fmt.Errorf(`current database version %d is not equal to the expected version %d. Please run "forgejo [--config /path/to/app.ini] migrate" to update the database version`, currentDB, expectedDB) } - return forgejo_migrations_legacy.EnsureUpToDate(x) + return forgejo_migrations.EnsureUpToDate(x) } func getPendingMigrations(curDBVer int64, migrations []*migration) []*migration { @@ -510,7 +510,7 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t } // Execute Forgejo specific migrations. - return forgejo_migrations_legacy.Migrate(x) + return forgejo_migrations.Migrate(x) } // WrapperMigrate is a wrapper for Migrate to be called in diagnostics diff --git a/models/gitea_migrations/migrations_test.go b/models/migrations/migrations_test.go similarity index 96% rename from models/gitea_migrations/migrations_test.go rename to models/migrations/migrations_test.go index a2491dd65a..468c918c93 100644 --- a/models/gitea_migrations/migrations_test.go +++ b/models/migrations/migrations_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package gitea_migrations +package migrations import ( "testing" diff --git a/models/gitea_migrations/test/tests.go b/models/migrations/test/tests.go similarity index 97% rename from models/gitea_migrations/test/tests.go rename to models/migrations/test/tests.go index 6a4865194d..c1f0caf19b 100644 --- a/models/gitea_migrations/test/tests.go +++ b/models/migrations/test/tests.go @@ -31,7 +31,7 @@ import ( // PrepareTestEnv prepares the test environment and reset the database. The skip parameter should usually be 0. // Provide models to be sync'd with the database - in particular any models you expect fixtures to be loaded from. // -// fixtures in `models/gitea_migrations/fixtures/` will be loaded automatically +// fixtures in `models/migrations/fixtures/` will be loaded automatically func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, func()) { t.Helper() ourSkip := 2 @@ -89,14 +89,13 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu } } - fixturesDir := filepath.Join(filepath.Dir(setting.AppPath), "models", "gitea_migrations", "fixtures", t.Name()) + fixturesDir := filepath.Join(filepath.Dir(setting.AppPath), "models", "migrations", "fixtures", t.Name()) if _, err := os.Stat(fixturesDir); err == nil { t.Logf("initializing fixtures from: %s", fixturesDir) if err := unittest.InitFixtures( unittest.FixturesOptions{ - Dir: fixturesDir, - OnlyAffectModels: syncModels, + Dir: fixturesDir, }, x); err != nil { t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err) return x, deferFn diff --git a/models/gitea_migrations/v1_10/v100.go b/models/migrations/v1_10/v100.go similarity index 98% rename from models/gitea_migrations/v1_10/v100.go rename to models/migrations/v1_10/v100.go index 1742bea296..5d2fd8e244 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v101.go b/models/migrations/v1_10/v101.go similarity index 94% rename from models/gitea_migrations/v1_10/v101.go rename to models/migrations/v1_10/v101.go index 6c8dfe2486..f023a2a0e7 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v88.go b/models/migrations/v1_10/v88.go similarity index 98% rename from models/gitea_migrations/v1_10/v88.go rename to models/migrations/v1_10/v88.go index eb8e81c19e..7e86ac364f 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v89.go b/models/migrations/v1_10/v89.go similarity index 96% rename from models/gitea_migrations/v1_10/v89.go rename to models/migrations/v1_10/v89.go index 0df2a6e17b..d5f27ffdc6 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v90.go b/models/migrations/v1_10/v90.go similarity index 94% rename from models/gitea_migrations/v1_10/v90.go rename to models/migrations/v1_10/v90.go index 5521a97e32..295d4b1c1b 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v91.go b/models/migrations/v1_10/v91.go similarity index 95% rename from models/gitea_migrations/v1_10/v91.go rename to models/migrations/v1_10/v91.go index 08db6c2742..48cac2de70 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v92.go b/models/migrations/v1_10/v92.go similarity index 93% rename from models/gitea_migrations/v1_10/v92.go rename to models/migrations/v1_10/v92.go index b6c04a9234..9080108594 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v93.go b/models/migrations/v1_10/v93.go similarity index 93% rename from models/gitea_migrations/v1_10/v93.go rename to models/migrations/v1_10/v93.go index c131be9a8d..ee59a8db39 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v94.go b/models/migrations/v1_10/v94.go similarity index 96% rename from models/gitea_migrations/v1_10/v94.go rename to models/migrations/v1_10/v94.go index 13b7d7b303..c131af162b 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v95.go b/models/migrations/v1_10/v95.go similarity index 94% rename from models/gitea_migrations/v1_10/v95.go rename to models/migrations/v1_10/v95.go index 86b52026bf..3b1f67fd9c 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v96.go b/models/migrations/v1_10/v96.go similarity index 98% rename from models/gitea_migrations/v1_10/v96.go rename to models/migrations/v1_10/v96.go index bcbd618b49..3bfb770f24 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v97.go b/models/migrations/v1_10/v97.go similarity index 92% rename from models/gitea_migrations/v1_10/v97.go rename to models/migrations/v1_10/v97.go index 5872bb63e5..dee45b32e3 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v98.go b/models/migrations/v1_10/v98.go similarity index 93% rename from models/gitea_migrations/v1_10/v98.go rename to models/migrations/v1_10/v98.go index d21c326459..bdd9aed089 100644 --- a/models/gitea_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/gitea_migrations/v1_10/v99.go b/models/migrations/v1_10/v99.go similarity index 97% rename from models/gitea_migrations/v1_10/v99.go rename to models/migrations/v1_10/v99.go index addae66be9..7f287b77aa 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v102.go b/models/migrations/v1_11/v102.go similarity index 87% rename from models/gitea_migrations/v1_11/v102.go rename to models/migrations/v1_11/v102.go index 53288f18ec..a585d9c423 100644 --- a/models/gitea_migrations/v1_11/v102.go +++ b/models/migrations/v1_11/v102.go @@ -1,10 +1,10 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 +package v1_11 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/gitea_migrations/v1_11/v103.go b/models/migrations/v1_11/v103.go similarity index 93% rename from models/gitea_migrations/v1_11/v103.go rename to models/migrations/v1_11/v103.go index a515710160..53527dac58 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v104.go b/models/migrations/v1_11/v104.go similarity index 91% rename from models/gitea_migrations/v1_11/v104.go rename to models/migrations/v1_11/v104.go index 47cf320359..af3578ca4a 100644 --- a/models/gitea_migrations/v1_11/v104.go +++ b/models/migrations/v1_11/v104.go @@ -1,10 +1,10 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 +package v1_11 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/gitea_migrations/v1_11/v105.go b/models/migrations/v1_11/v105.go similarity index 95% rename from models/gitea_migrations/v1_11/v105.go rename to models/migrations/v1_11/v105.go index d86973a0f6..b91340c30a 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v106.go b/models/migrations/v1_11/v106.go similarity index 96% rename from models/gitea_migrations/v1_11/v106.go rename to models/migrations/v1_11/v106.go index edffe18683..ecb11cdd1e 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v107.go b/models/migrations/v1_11/v107.go similarity index 93% rename from models/gitea_migrations/v1_11/v107.go rename to models/migrations/v1_11/v107.go index a158e3bb50..f0bfe5862c 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v108.go b/models/migrations/v1_11/v108.go similarity index 92% rename from models/gitea_migrations/v1_11/v108.go rename to models/migrations/v1_11/v108.go index 8f14504ceb..a85096234d 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v109.go b/models/migrations/v1_11/v109.go similarity index 92% rename from models/gitea_migrations/v1_11/v109.go rename to models/migrations/v1_11/v109.go index f7616aec7b..ea565ccda3 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v110.go b/models/migrations/v1_11/v110.go similarity index 83% rename from models/gitea_migrations/v1_11/v110.go rename to models/migrations/v1_11/v110.go index 0468dcd1ea..fce9be847e 100644 --- a/models/gitea_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" @@ -13,6 +13,9 @@ func ChangeReviewContentToText(x *xorm.Engine) error { case schemas.MYSQL: _, err := x.Exec("ALTER TABLE review MODIFY COLUMN content TEXT") return err + case schemas.ORACLE: + _, err := x.Exec("ALTER TABLE review MODIFY content TEXT") + return err case schemas.POSTGRES: _, err := x.Exec("ALTER TABLE review ALTER COLUMN content TYPE TEXT") return err diff --git a/models/gitea_migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go similarity index 99% rename from models/gitea_migrations/v1_11/v111.go rename to models/migrations/v1_11/v111.go index 6f531e4858..cc3dc0d545 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v112.go b/models/migrations/v1_11/v112.go similarity index 97% rename from models/gitea_migrations/v1_11/v112.go rename to models/migrations/v1_11/v112.go index 22054e6f68..6112ab51a5 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v113.go b/models/migrations/v1_11/v113.go similarity index 93% rename from models/gitea_migrations/v1_11/v113.go rename to models/migrations/v1_11/v113.go index a4d54f66fb..dea344a44f 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v114.go b/models/migrations/v1_11/v114.go similarity index 97% rename from models/gitea_migrations/v1_11/v114.go rename to models/migrations/v1_11/v114.go index 9467a8a90c..95adcee989 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v115.go b/models/migrations/v1_11/v115.go similarity index 99% rename from models/gitea_migrations/v1_11/v115.go rename to models/migrations/v1_11/v115.go index 65094df93d..3d4b41017b 100644 --- a/models/gitea_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/gitea_migrations/v1_11/v116.go b/models/migrations/v1_11/v116.go similarity index 96% rename from models/gitea_migrations/v1_11/v116.go rename to models/migrations/v1_11/v116.go index 729fbad18b..85aa76c1e0 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v117.go b/models/migrations/v1_12/v117.go similarity index 93% rename from models/gitea_migrations/v1_12/v117.go rename to models/migrations/v1_12/v117.go index 73b58ca34b..8eadcdef2b 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v118.go b/models/migrations/v1_12/v118.go similarity index 95% rename from models/gitea_migrations/v1_12/v118.go rename to models/migrations/v1_12/v118.go index e8b4249743..eb022dc5e4 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v119.go b/models/migrations/v1_12/v119.go similarity index 94% rename from models/gitea_migrations/v1_12/v119.go rename to models/migrations/v1_12/v119.go index b4bf29a935..60bfe6a57d 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v120.go b/models/migrations/v1_12/v120.go similarity index 94% rename from models/gitea_migrations/v1_12/v120.go rename to models/migrations/v1_12/v120.go index 14d515f5a7..3f7ed8d373 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v121.go b/models/migrations/v1_12/v121.go similarity index 93% rename from models/gitea_migrations/v1_12/v121.go rename to models/migrations/v1_12/v121.go index a28ae4e1c9..175ec9164d 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v122.go b/models/migrations/v1_12/v122.go similarity index 93% rename from models/gitea_migrations/v1_12/v122.go rename to models/migrations/v1_12/v122.go index bc1b175f6a..6e31d863a1 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v123.go b/models/migrations/v1_12/v123.go similarity index 93% rename from models/gitea_migrations/v1_12/v123.go rename to models/migrations/v1_12/v123.go index 52b10bb850..b0c3af07a3 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v124.go b/models/migrations/v1_12/v124.go similarity index 95% rename from models/gitea_migrations/v1_12/v124.go rename to models/migrations/v1_12/v124.go index 9a93f436d4..d2ba03ffe0 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v125.go b/models/migrations/v1_12/v125.go similarity index 93% rename from models/gitea_migrations/v1_12/v125.go rename to models/migrations/v1_12/v125.go index 7f582ecff5..ec4ffaab25 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v126.go b/models/migrations/v1_12/v126.go similarity index 96% rename from models/gitea_migrations/v1_12/v126.go rename to models/migrations/v1_12/v126.go index 64fd7f7478..ca9ec3aa3f 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v127.go b/models/migrations/v1_12/v127.go similarity index 98% rename from models/gitea_migrations/v1_12/v127.go rename to models/migrations/v1_12/v127.go index f686fa617c..11a4042973 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go similarity index 99% rename from models/gitea_migrations/v1_12/v128.go rename to models/migrations/v1_12/v128.go index 8fca974616..6d7307f470 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v129.go b/models/migrations/v1_12/v129.go similarity index 94% rename from models/gitea_migrations/v1_12/v129.go rename to models/migrations/v1_12/v129.go index 3e4d3aca68..cf228242b9 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v130.go b/models/migrations/v1_12/v130.go similarity index 99% rename from models/gitea_migrations/v1_12/v130.go rename to models/migrations/v1_12/v130.go index 383ef47492..bfa856796a 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v131.go b/models/migrations/v1_12/v131.go similarity index 94% rename from models/gitea_migrations/v1_12/v131.go rename to models/migrations/v1_12/v131.go index 1266c2f185..5184bc3590 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v132.go b/models/migrations/v1_12/v132.go similarity index 94% rename from models/gitea_migrations/v1_12/v132.go rename to models/migrations/v1_12/v132.go index 8b1ae6db93..3b2b28f7ab 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v133.go b/models/migrations/v1_12/v133.go similarity index 93% rename from models/gitea_migrations/v1_12/v133.go rename to models/migrations/v1_12/v133.go index 69e20597d8..c9087fc8c1 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go similarity index 99% rename from models/gitea_migrations/v1_12/v134.go rename to models/migrations/v1_12/v134.go index 1fabdcae96..bba996fd40 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v135.go b/models/migrations/v1_12/v135.go similarity index 93% rename from models/gitea_migrations/v1_12/v135.go rename to models/migrations/v1_12/v135.go index 5df0ad7fc4..8898011df5 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v136.go b/models/migrations/v1_12/v136.go similarity index 99% rename from models/gitea_migrations/v1_12/v136.go rename to models/migrations/v1_12/v136.go index 7d246a82be..e2557ae002 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v137.go b/models/migrations/v1_12/v137.go similarity index 93% rename from models/gitea_migrations/v1_12/v137.go rename to models/migrations/v1_12/v137.go index 9d38483488..0d86b72010 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v138.go b/models/migrations/v1_12/v138.go similarity index 93% rename from models/gitea_migrations/v1_12/v138.go rename to models/migrations/v1_12/v138.go index 4485adeb2d..8c8d353f40 100644 --- a/models/gitea_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/gitea_migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go similarity index 96% rename from models/gitea_migrations/v1_12/v139.go rename to models/migrations/v1_12/v139.go index 51e57b984a..cd7963524e 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v140.go b/models/migrations/v1_13/v140.go similarity index 95% rename from models/gitea_migrations/v1_13/v140.go rename to models/migrations/v1_13/v140.go index c9213a3f54..d74f808e9f 100644 --- a/models/gitea_migrations/v1_13/v140.go +++ b/models/migrations/v1_13/v140.go @@ -1,12 +1,12 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 +package v1_13 //nolint import ( "fmt" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/setting" "xorm.io/xorm" diff --git a/models/gitea_migrations/v1_13/v141.go b/models/migrations/v1_13/v141.go similarity index 94% rename from models/gitea_migrations/v1_13/v141.go rename to models/migrations/v1_13/v141.go index b54bc1727c..ae211e0e44 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v142.go b/models/migrations/v1_13/v142.go similarity index 95% rename from models/gitea_migrations/v1_13/v142.go rename to models/migrations/v1_13/v142.go index 8939f6f2f8..7490e0f3b4 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v143.go b/models/migrations/v1_13/v143.go similarity index 97% rename from models/gitea_migrations/v1_13/v143.go rename to models/migrations/v1_13/v143.go index 6a8da8b06d..1f9120e2ba 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v144.go b/models/migrations/v1_13/v144.go similarity index 96% rename from models/gitea_migrations/v1_13/v144.go rename to models/migrations/v1_13/v144.go index f138338514..7e801eab8a 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go similarity index 98% rename from models/gitea_migrations/v1_13/v145.go rename to models/migrations/v1_13/v145.go index f7d3895c84..a01f577ed1 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v146.go b/models/migrations/v1_13/v146.go similarity index 98% rename from models/gitea_migrations/v1_13/v146.go rename to models/migrations/v1_13/v146.go index e6a476a288..a1b54ee3aa 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v147.go b/models/migrations/v1_13/v147.go similarity index 99% rename from models/gitea_migrations/v1_13/v147.go rename to models/migrations/v1_13/v147.go index 831ef5842a..cc57504c74 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v148.go b/models/migrations/v1_13/v148.go similarity index 93% rename from models/gitea_migrations/v1_13/v148.go rename to models/migrations/v1_13/v148.go index d276db3d61..7bb8ab700b 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v149.go b/models/migrations/v1_13/v149.go similarity index 95% rename from models/gitea_migrations/v1_13/v149.go rename to models/migrations/v1_13/v149.go index c1bfe8b09e..3a0c5909d5 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v150.go b/models/migrations/v1_13/v150.go similarity index 79% rename from models/gitea_migrations/v1_13/v150.go rename to models/migrations/v1_13/v150.go index 0f92712327..be14fd130c 100644 --- a/models/gitea_migrations/v1_13/v150.go +++ b/models/migrations/v1_13/v150.go @@ -1,10 +1,10 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 +package v1_13 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/timeutil" "xorm.io/xorm" @@ -32,8 +32,8 @@ func AddPrimaryKeyToRepoTopic(x *xorm.Engine) error { return err } - base.LegacyRecreateTable(sess, &Topic{}) //nolint:staticcheck - base.LegacyRecreateTable(sess, &RepoTopic{}) //nolint:staticcheck + base.RecreateTable(sess, &Topic{}) + base.RecreateTable(sess, &RepoTopic{}) return sess.Commit() } diff --git a/models/gitea_migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go similarity index 97% rename from models/gitea_migrations/v1_13/v151.go rename to models/migrations/v1_13/v151.go index a464e6e7a7..ff584fff67 100644 --- a/models/gitea_migrations/v1_13/v151.go +++ b/models/migrations/v1_13/v151.go @@ -1,9 +1,10 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 +package v1_13 //nolint import ( + "context" "errors" "fmt" "strings" @@ -60,7 +61,7 @@ func SetDefaultPasswordToArgon2(x *xorm.Engine) error { tempTableName := "tmp_recreate__user" column.Default = "'argon2'" - createTableSQL, _, err := x.Dialect().CreateTableSQL(table, tempTableName) + createTableSQL, _, err := x.Dialect().CreateTableSQL(context.Background(), x.DB(), table, tempTableName) if err != nil { return err } diff --git a/models/gitea_migrations/v1_13/v152.go b/models/migrations/v1_13/v152.go similarity index 91% rename from models/gitea_migrations/v1_13/v152.go rename to models/migrations/v1_13/v152.go index 648e26446f..502c82a40d 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v153.go b/models/migrations/v1_13/v153.go similarity index 94% rename from models/gitea_migrations/v1_13/v153.go rename to models/migrations/v1_13/v153.go index e5462fc162..0b2dd3eb62 100644 --- a/models/gitea_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/gitea_migrations/v1_13/v154.go b/models/migrations/v1_13/v154.go similarity index 98% rename from models/gitea_migrations/v1_13/v154.go rename to models/migrations/v1_13/v154.go index 89dc7821b2..cf31190781 100644 --- a/models/gitea_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/gitea_migrations/v1_15/main_test.go b/models/migrations/v1_14/main_test.go similarity index 69% rename from models/gitea_migrations/v1_15/main_test.go rename to models/migrations/v1_14/main_test.go index 4811142c43..c01faedc35 100644 --- a/models/gitea_migrations/v1_15/main_test.go +++ b/models/migrations/v1_14/main_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 +package v1_14 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/gitea_migrations/v1_14/v155.go b/models/migrations/v1_14/v155.go similarity index 94% rename from models/gitea_migrations/v1_14/v155.go rename to models/migrations/v1_14/v155.go index 505a9ae033..e814f59938 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v156.go b/models/migrations/v1_14/v156.go similarity index 99% rename from models/gitea_migrations/v1_14/v156.go rename to models/migrations/v1_14/v156.go index 7bbd9f4c85..b6dc91a054 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v157.go b/models/migrations/v1_14/v157.go similarity index 98% rename from models/gitea_migrations/v1_14/v157.go rename to models/migrations/v1_14/v157.go index ba69f71130..7187278d29 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go similarity index 99% rename from models/gitea_migrations/v1_14/v158.go rename to models/migrations/v1_14/v158.go index 2ab3c8a1f0..3fa27cfecd 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v159.go b/models/migrations/v1_14/v159.go similarity index 86% rename from models/gitea_migrations/v1_14/v159.go rename to models/migrations/v1_14/v159.go index d8193d25bc..fdd7e12449 100644 --- a/models/gitea_migrations/v1_14/v159.go +++ b/models/migrations/v1_14/v159.go @@ -1,10 +1,10 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 +package v1_14 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/timeutil" "xorm.io/xorm" @@ -30,7 +30,7 @@ func UpdateReactionConstraint(x *xorm.Engine) error { return err } - if err := base.LegacyRecreateTable(sess, &Reaction{}); err != nil { //nolint:staticcheck + if err := base.RecreateTable(sess, &Reaction{}); err != nil { return err } diff --git a/models/gitea_migrations/v1_14/v160.go b/models/migrations/v1_14/v160.go similarity index 93% rename from models/gitea_migrations/v1_14/v160.go rename to models/migrations/v1_14/v160.go index 73f3798954..4dea91b514 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v161.go b/models/migrations/v1_14/v161.go similarity index 95% rename from models/gitea_migrations/v1_14/v161.go rename to models/migrations/v1_14/v161.go index b689a75a05..6e904cfab6 100644 --- a/models/gitea_migrations/v1_14/v161.go +++ b/models/migrations/v1_14/v161.go @@ -1,12 +1,12 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 +package v1_14 //nolint import ( "context" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/gitea_migrations/v1_14/v162.go b/models/migrations/v1_14/v162.go similarity index 94% rename from models/gitea_migrations/v1_14/v162.go rename to models/migrations/v1_14/v162.go index 69016f3f72..5d6d7c2e3f 100644 --- a/models/gitea_migrations/v1_14/v162.go +++ b/models/migrations/v1_14/v162.go @@ -1,10 +1,10 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 +package v1_14 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/gitea_migrations/v1_14/v163.go b/models/migrations/v1_14/v163.go similarity index 80% rename from models/gitea_migrations/v1_14/v163.go rename to models/migrations/v1_14/v163.go index 4c838df865..60fc98c0a4 100644 --- a/models/gitea_migrations/v1_14/v163.go +++ b/models/migrations/v1_14/v163.go @@ -1,10 +1,10 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 +package v1_14 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) @@ -27,7 +27,7 @@ func ConvertTopicNameFrom25To50(x *xorm.Engine) error { if err := sess.Begin(); err != nil { return err } - if err := base.LegacyRecreateTable(sess, new(Topic)); err != nil { //nolint:staticcheck + if err := base.RecreateTable(sess, new(Topic)); err != nil { return err } diff --git a/models/gitea_migrations/v1_14/v164.go b/models/migrations/v1_14/v164.go similarity index 97% rename from models/gitea_migrations/v1_14/v164.go rename to models/migrations/v1_14/v164.go index d2fd9b8464..54f6951427 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v165.go b/models/migrations/v1_14/v165.go similarity index 95% rename from models/gitea_migrations/v1_14/v165.go rename to models/migrations/v1_14/v165.go index 11b46e5742..9315e44197 100644 --- a/models/gitea_migrations/v1_14/v165.go +++ b/models/migrations/v1_14/v165.go @@ -1,10 +1,10 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 +package v1_14 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" "xorm.io/xorm/schemas" diff --git a/models/gitea_migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go similarity index 99% rename from models/gitea_migrations/v1_14/v166.go rename to models/migrations/v1_14/v166.go index 4c106bd7da..e5731582fd 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v167.go b/models/migrations/v1_14/v167.go similarity index 95% rename from models/gitea_migrations/v1_14/v167.go rename to models/migrations/v1_14/v167.go index d77bbc401e..9d416f6a32 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v168.go b/models/migrations/v1_14/v168.go similarity index 89% rename from models/gitea_migrations/v1_14/v168.go rename to models/migrations/v1_14/v168.go index aa93eec19b..a30a8859f7 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v169.go b/models/migrations/v1_14/v169.go similarity index 92% rename from models/gitea_migrations/v1_14/v169.go rename to models/migrations/v1_14/v169.go index 4f9df0d96f..5b81bb58b1 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v170.go b/models/migrations/v1_14/v170.go similarity index 93% rename from models/gitea_migrations/v1_14/v170.go rename to models/migrations/v1_14/v170.go index a2ff4623e1..7b6498a3e9 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v171.go b/models/migrations/v1_14/v171.go similarity index 94% rename from models/gitea_migrations/v1_14/v171.go rename to models/migrations/v1_14/v171.go index 7b200e960a..51a35a02ad 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v172.go b/models/migrations/v1_14/v172.go similarity index 93% rename from models/gitea_migrations/v1_14/v172.go rename to models/migrations/v1_14/v172.go index c410d393f1..d49b70f5ad 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v173.go b/models/migrations/v1_14/v173.go similarity index 93% rename from models/gitea_migrations/v1_14/v173.go rename to models/migrations/v1_14/v173.go index 7752fbe966..2d9eee9197 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v174.go b/models/migrations/v1_14/v174.go similarity index 96% rename from models/gitea_migrations/v1_14/v174.go rename to models/migrations/v1_14/v174.go index 4049e43070..c839e15db8 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v175.go b/models/migrations/v1_14/v175.go similarity index 98% rename from models/gitea_migrations/v1_14/v175.go rename to models/migrations/v1_14/v175.go index 49fa17d046..3cda5772a0 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v176.go b/models/migrations/v1_14/v176.go similarity index 98% rename from models/gitea_migrations/v1_14/v176.go rename to models/migrations/v1_14/v176.go index ef5dce9a02..1ed49f75fa 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v176_test.go b/models/migrations/v1_14/v176_test.go similarity index 97% rename from models/gitea_migrations/v1_14/v176_test.go rename to models/migrations/v1_14/v176_test.go index 60b555b520..d88ff207e7 100644 --- a/models/gitea_migrations/v1_14/v176_test.go +++ b/models/migrations/v1_14/v176_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 +package v1_14 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" ) diff --git a/models/gitea_migrations/v1_14/v177.go b/models/migrations/v1_14/v177.go similarity index 97% rename from models/gitea_migrations/v1_14/v177.go rename to models/migrations/v1_14/v177.go index 96676bf8d9..6e1838f369 100644 --- a/models/gitea_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/gitea_migrations/v1_14/v177_test.go b/models/migrations/v1_14/v177_test.go similarity index 96% rename from models/gitea_migrations/v1_14/v177_test.go rename to models/migrations/v1_14/v177_test.go index ee69dbad53..bffc6f92e3 100644 --- a/models/gitea_migrations/v1_14/v177_test.go +++ b/models/migrations/v1_14/v177_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 +package v1_14 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/modules/timeutil" "github.com/stretchr/testify/assert" diff --git a/models/gitea_migrations/v1_16/main_test.go b/models/migrations/v1_15/main_test.go similarity index 69% rename from models/gitea_migrations/v1_16/main_test.go rename to models/migrations/v1_15/main_test.go index 5b7cdb0032..6c04d3f5ee 100644 --- a/models/gitea_migrations/v1_16/main_test.go +++ b/models/migrations/v1_15/main_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 +package v1_15 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/gitea_migrations/v1_15/v178.go b/models/migrations/v1_15/v178.go similarity index 93% rename from models/gitea_migrations/v1_15/v178.go rename to models/migrations/v1_15/v178.go index ca3a5c262e..6d236eb049 100644 --- a/models/gitea_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/gitea_migrations/v1_15/v179.go b/models/migrations/v1_15/v179.go similarity index 91% rename from models/gitea_migrations/v1_15/v179.go rename to models/migrations/v1_15/v179.go index eb87702475..b990583303 100644 --- a/models/gitea_migrations/v1_15/v179.go +++ b/models/migrations/v1_15/v179.go @@ -1,10 +1,10 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 +package v1_15 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" "xorm.io/xorm/schemas" diff --git a/models/gitea_migrations/v1_15/v180.go b/models/migrations/v1_15/v180.go similarity index 99% rename from models/gitea_migrations/v1_15/v180.go rename to models/migrations/v1_15/v180.go index 0b68c3ceb7..02fbd57cdb 100644 --- a/models/gitea_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/gitea_migrations/v1_15/v181.go b/models/migrations/v1_15/v181.go similarity index 98% rename from models/gitea_migrations/v1_15/v181.go rename to models/migrations/v1_15/v181.go index fb1d3d7a75..2185ed0213 100644 --- a/models/gitea_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/gitea_migrations/v1_15/v181_test.go b/models/migrations/v1_15/v181_test.go similarity index 94% rename from models/gitea_migrations/v1_15/v181_test.go rename to models/migrations/v1_15/v181_test.go index 130bb35cc7..4154e0b1e9 100644 --- a/models/gitea_migrations/v1_15/v181_test.go +++ b/models/migrations/v1_15/v181_test.go @@ -1,13 +1,13 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 +package v1_15 //nolint import ( "strings" "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/gitea_migrations/v1_15/v182.go b/models/migrations/v1_15/v182.go similarity index 97% rename from models/gitea_migrations/v1_15/v182.go rename to models/migrations/v1_15/v182.go index f53ff11df9..9ca500c0f9 100644 --- a/models/gitea_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/gitea_migrations/v1_15/v182_test.go b/models/migrations/v1_15/v182_test.go similarity index 94% rename from models/gitea_migrations/v1_15/v182_test.go rename to models/migrations/v1_15/v182_test.go index f67fc6586a..6865cafac4 100644 --- a/models/gitea_migrations/v1_15/v182_test.go +++ b/models/migrations/v1_15/v182_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 +package v1_15 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/gitea_migrations/v1_15/v183.go b/models/migrations/v1_15/v183.go similarity index 97% rename from models/gitea_migrations/v1_15/v183.go rename to models/migrations/v1_15/v183.go index 5684e35699..aaad64c220 100644 --- a/models/gitea_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/gitea_migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go similarity index 95% rename from models/gitea_migrations/v1_15/v184.go rename to models/migrations/v1_15/v184.go index 58cf99af43..41b64d4743 100644 --- a/models/gitea_migrations/v1_15/v184.go +++ b/models/migrations/v1_15/v184.go @@ -1,13 +1,13 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 +package v1_15 //nolint import ( "context" "fmt" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/setting" "xorm.io/xorm" diff --git a/models/gitea_migrations/v1_15/v185.go b/models/migrations/v1_15/v185.go similarity index 95% rename from models/gitea_migrations/v1_15/v185.go rename to models/migrations/v1_15/v185.go index 60af59edca..e5878ec193 100644 --- a/models/gitea_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/gitea_migrations/v1_15/v186.go b/models/migrations/v1_15/v186.go similarity index 96% rename from models/gitea_migrations/v1_15/v186.go rename to models/migrations/v1_15/v186.go index 55d3199335..ad75822de5 100644 --- a/models/gitea_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/gitea_migrations/v1_15/v187.go b/models/migrations/v1_15/v187.go similarity index 94% rename from models/gitea_migrations/v1_15/v187.go rename to models/migrations/v1_15/v187.go index 76c29755f1..b573fc52ef 100644 --- a/models/gitea_migrations/v1_15/v187.go +++ b/models/migrations/v1_15/v187.go @@ -1,10 +1,10 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 +package v1_15 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/gitea_migrations/v1_15/v188.go b/models/migrations/v1_15/v188.go similarity index 92% rename from models/gitea_migrations/v1_15/v188.go rename to models/migrations/v1_15/v188.go index 4494e6ff05..71e45cab0e 100644 --- a/models/gitea_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/gitea_migrations/v1_17/main_test.go b/models/migrations/v1_16/main_test.go similarity index 69% rename from models/gitea_migrations/v1_17/main_test.go rename to models/migrations/v1_16/main_test.go index e54e2a9029..6f891f3e94 100644 --- a/models/gitea_migrations/v1_17/main_test.go +++ b/models/migrations/v1_16/main_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 +package v1_16 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/gitea_migrations/v1_16/v189.go b/models/migrations/v1_16/v189.go similarity index 97% rename from models/gitea_migrations/v1_16/v189.go rename to models/migrations/v1_16/v189.go index a2ba3a0c64..1ee72d9c39 100644 --- a/models/gitea_migrations/v1_16/v189.go +++ b/models/migrations/v1_16/v189.go @@ -1,13 +1,13 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 +package v1_16 //nolint import ( "encoding/binary" "fmt" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/json" "xorm.io/xorm" diff --git a/models/gitea_migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go similarity index 96% rename from models/gitea_migrations/v1_16/v189_test.go rename to models/migrations/v1_16/v189_test.go index 7b50cac3d8..90b721d5f1 100644 --- a/models/gitea_migrations/v1_16/v189_test.go +++ b/models/migrations/v1_16/v189_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 +package v1_16 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/modules/json" "github.com/stretchr/testify/assert" diff --git a/models/gitea_migrations/v1_16/v190.go b/models/migrations/v1_16/v190.go similarity index 94% rename from models/gitea_migrations/v1_16/v190.go rename to models/migrations/v1_16/v190.go index 1eb6b6ddb4..5953802849 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v191.go b/models/migrations/v1_16/v191.go similarity index 96% rename from models/gitea_migrations/v1_16/v191.go rename to models/migrations/v1_16/v191.go index 427476b70b..567f88d6d1 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v192.go b/models/migrations/v1_16/v192.go similarity index 84% rename from models/gitea_migrations/v1_16/v192.go rename to models/migrations/v1_16/v192.go index 0e2fd522f9..731b9fb43a 100644 --- a/models/gitea_migrations/v1_16/v192.go +++ b/models/migrations/v1_16/v192.go @@ -1,10 +1,10 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 +package v1_16 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/gitea_migrations/v1_16/v193.go b/models/migrations/v1_16/v193.go similarity index 97% rename from models/gitea_migrations/v1_16/v193.go rename to models/migrations/v1_16/v193.go index a5af2de380..8d3ce7a558 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go similarity index 96% rename from models/gitea_migrations/v1_16/v193_test.go rename to models/migrations/v1_16/v193_test.go index 73159118ab..8260acf32d 100644 --- a/models/gitea_migrations/v1_16/v193_test.go +++ b/models/migrations/v1_16/v193_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 +package v1_16 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/gitea_migrations/v1_16/v194.go b/models/migrations/v1_16/v194.go similarity index 94% rename from models/gitea_migrations/v1_16/v194.go rename to models/migrations/v1_16/v194.go index 2e4ed8340e..6aa13c50cf 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v195.go b/models/migrations/v1_16/v195.go similarity index 98% rename from models/gitea_migrations/v1_16/v195.go rename to models/migrations/v1_16/v195.go index 4fd42b7bd2..6d7e94141e 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v195_test.go b/models/migrations/v1_16/v195_test.go similarity index 95% rename from models/gitea_migrations/v1_16/v195_test.go rename to models/migrations/v1_16/v195_test.go index d3e4ffc56e..71234a6fb3 100644 --- a/models/gitea_migrations/v1_16/v195_test.go +++ b/models/migrations/v1_16/v195_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 +package v1_16 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/gitea_migrations/v1_16/v196.go b/models/migrations/v1_16/v196.go similarity index 93% rename from models/gitea_migrations/v1_16/v196.go rename to models/migrations/v1_16/v196.go index 6c9caa100f..7cbafc61e5 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v197.go b/models/migrations/v1_16/v197.go similarity index 94% rename from models/gitea_migrations/v1_16/v197.go rename to models/migrations/v1_16/v197.go index 862bdfdcbd..97888b2847 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v198.go b/models/migrations/v1_16/v198.go similarity index 97% rename from models/gitea_migrations/v1_16/v198.go rename to models/migrations/v1_16/v198.go index 5d3043eb46..8b3c73addc 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v199.go b/models/migrations/v1_16/v199.go similarity index 90% rename from models/gitea_migrations/v1_16/v199.go rename to models/migrations/v1_16/v199.go index 4020352f2b..6adcf890af 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v200.go b/models/migrations/v1_16/v200.go similarity index 94% rename from models/gitea_migrations/v1_16/v200.go rename to models/migrations/v1_16/v200.go index de57fad8fe..c08c20e51d 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v201.go b/models/migrations/v1_16/v201.go similarity index 93% rename from models/gitea_migrations/v1_16/v201.go rename to models/migrations/v1_16/v201.go index 2c43698b0c..35e0c9f2fb 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v202.go b/models/migrations/v1_16/v202.go similarity index 96% rename from models/gitea_migrations/v1_16/v202.go rename to models/migrations/v1_16/v202.go index d8c8fdcadc..6ba36152f1 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v203.go b/models/migrations/v1_16/v203.go similarity index 93% rename from models/gitea_migrations/v1_16/v203.go rename to models/migrations/v1_16/v203.go index c3241cba57..e8e6b52453 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v204.go b/models/migrations/v1_16/v204.go similarity index 92% rename from models/gitea_migrations/v1_16/v204.go rename to models/migrations/v1_16/v204.go index 4d375307e7..ece03e1305 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v205.go b/models/migrations/v1_16/v205.go similarity index 92% rename from models/gitea_migrations/v1_16/v205.go rename to models/migrations/v1_16/v205.go index a89edfdef4..a064b9830d 100644 --- a/models/gitea_migrations/v1_16/v205.go +++ b/models/migrations/v1_16/v205.go @@ -1,10 +1,10 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 +package v1_16 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" "xorm.io/xorm/schemas" diff --git a/models/gitea_migrations/v1_16/v206.go b/models/migrations/v1_16/v206.go similarity index 96% rename from models/gitea_migrations/v1_16/v206.go rename to models/migrations/v1_16/v206.go index 01a9c386eb..581a7d76e9 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v207.go b/models/migrations/v1_16/v207.go similarity index 90% rename from models/gitea_migrations/v1_16/v207.go rename to models/migrations/v1_16/v207.go index 19126ead1f..91208f066c 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v208.go b/models/migrations/v1_16/v208.go similarity index 90% rename from models/gitea_migrations/v1_16/v208.go rename to models/migrations/v1_16/v208.go index fb643324f4..1a11ef096a 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v209.go b/models/migrations/v1_16/v209.go similarity index 92% rename from models/gitea_migrations/v1_16/v209.go rename to models/migrations/v1_16/v209.go index 230838647b..be3100e02a 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go similarity index 99% rename from models/gitea_migrations/v1_16/v210.go rename to models/migrations/v1_16/v210.go index f48ab11db6..375a008e18 100644 --- a/models/gitea_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/gitea_migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go similarity index 98% rename from models/gitea_migrations/v1_16/v210_test.go rename to models/migrations/v1_16/v210_test.go index 7972c191f0..f6423a5821 100644 --- a/models/gitea_migrations/v1_16/v210_test.go +++ b/models/migrations/v1_16/v210_test.go @@ -1,13 +1,13 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 +package v1_16 //nolint import ( "encoding/hex" "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/modules/timeutil" "github.com/stretchr/testify/assert" diff --git a/models/gitea_migrations/v1_14/main_test.go b/models/migrations/v1_17/main_test.go similarity index 69% rename from models/gitea_migrations/v1_14/main_test.go rename to models/migrations/v1_17/main_test.go index 129e66f032..0a8e05ab5f 100644 --- a/models/gitea_migrations/v1_14/main_test.go +++ b/models/migrations/v1_17/main_test.go @@ -1,12 +1,12 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 +package v1_17 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/gitea_migrations/v1_17/v211.go b/models/migrations/v1_17/v211.go similarity index 91% rename from models/gitea_migrations/v1_17/v211.go rename to models/migrations/v1_17/v211.go index 517cf19388..9b72c8610b 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v212.go b/models/migrations/v1_17/v212.go similarity index 99% rename from models/gitea_migrations/v1_17/v212.go rename to models/migrations/v1_17/v212.go index 23868c0bb2..2337adcc80 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v213.go b/models/migrations/v1_17/v213.go similarity index 94% rename from models/gitea_migrations/v1_17/v213.go rename to models/migrations/v1_17/v213.go index b2bbdf7279..bb3f466e52 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v214.go b/models/migrations/v1_17/v214.go similarity index 95% rename from models/gitea_migrations/v1_17/v214.go rename to models/migrations/v1_17/v214.go index 1925324f0f..2268164919 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v215.go b/models/migrations/v1_17/v215.go similarity index 97% rename from models/gitea_migrations/v1_17/v215.go rename to models/migrations/v1_17/v215.go index 431103c98e..5aae798562 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v216.go b/models/migrations/v1_17/v216.go similarity index 91% rename from models/gitea_migrations/v1_17/v216.go rename to models/migrations/v1_17/v216.go index 37aeacb6fc..268f472a42 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v217.go b/models/migrations/v1_17/v217.go similarity index 96% rename from models/gitea_migrations/v1_17/v217.go rename to models/migrations/v1_17/v217.go index fef48b7a5b..5f096d4824 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v218.go b/models/migrations/v1_17/v218.go similarity index 98% rename from models/gitea_migrations/v1_17/v218.go rename to models/migrations/v1_17/v218.go index 412d124286..5e3dcd0841 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v219.go b/models/migrations/v1_17/v219.go similarity index 96% rename from models/gitea_migrations/v1_17/v219.go rename to models/migrations/v1_17/v219.go index 7ca6a26be6..e90656090f 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v220.go b/models/migrations/v1_17/v220.go similarity index 97% rename from models/gitea_migrations/v1_17/v220.go rename to models/migrations/v1_17/v220.go index 4e010e5b76..61bbf19725 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v221.go b/models/migrations/v1_17/v221.go similarity index 98% rename from models/gitea_migrations/v1_17/v221.go rename to models/migrations/v1_17/v221.go index 3ef34e3f06..84e9a238af 100644 --- a/models/gitea_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/gitea_migrations/v1_17/v221_test.go b/models/migrations/v1_17/v221_test.go similarity index 96% rename from models/gitea_migrations/v1_17/v221_test.go rename to models/migrations/v1_17/v221_test.go index 592fd15f5e..02607d6b32 100644 --- a/models/gitea_migrations/v1_17/v221_test.go +++ b/models/migrations/v1_17/v221_test.go @@ -1,13 +1,13 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 +package v1_17 //nolint import ( "encoding/base32" "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/gitea_migrations/v1_17/v222.go b/models/migrations/v1_17/v222.go similarity index 97% rename from models/gitea_migrations/v1_17/v222.go rename to models/migrations/v1_17/v222.go index 77095f4e7e..ae910cbcb6 100644 --- a/models/gitea_migrations/v1_17/v222.go +++ b/models/migrations/v1_17/v222.go @@ -1,14 +1,14 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 +package v1_17 //nolint import ( "context" "errors" "fmt" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/timeutil" "xorm.io/xorm" diff --git a/models/gitea_migrations/v1_17/v223.go b/models/migrations/v1_17/v223.go similarity index 97% rename from models/gitea_migrations/v1_17/v223.go rename to models/migrations/v1_17/v223.go index 4cc91c4f46..7d92dcf5ae 100644 --- a/models/gitea_migrations/v1_17/v223.go +++ b/models/migrations/v1_17/v223.go @@ -1,13 +1,13 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 +package v1_17 //nolint import ( "context" "fmt" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/setting" "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_18/main_test.go b/models/migrations/v1_18/main_test.go new file mode 100644 index 0000000000..33f5c51222 --- /dev/null +++ b/models/migrations/v1_18/main_test.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_18 //nolint + +import ( + "testing" + + migration_tests "forgejo.org/models/migrations/test" +) + +func TestMain(m *testing.M) { + migration_tests.MainTest(m) +} diff --git a/models/gitea_migrations/v1_18/v224.go b/models/migrations/v1_18/v224.go similarity index 95% rename from models/gitea_migrations/v1_18/v224.go rename to models/migrations/v1_18/v224.go index 6dc12020ea..f3d522b91a 100644 --- a/models/gitea_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/gitea_migrations/v1_18/v225.go b/models/migrations/v1_18/v225.go similarity index 96% rename from models/gitea_migrations/v1_18/v225.go rename to models/migrations/v1_18/v225.go index 266eccfff8..86bcb1323d 100644 --- a/models/gitea_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/gitea_migrations/v1_18/v226.go b/models/migrations/v1_18/v226.go similarity index 93% rename from models/gitea_migrations/v1_18/v226.go rename to models/migrations/v1_18/v226.go index 8ed9761476..f87e24b11d 100644 --- a/models/gitea_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/gitea_migrations/v1_18/v227.go b/models/migrations/v1_18/v227.go similarity index 96% rename from models/gitea_migrations/v1_18/v227.go rename to models/migrations/v1_18/v227.go index d39a010159..b6250fb76c 100644 --- a/models/gitea_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/gitea_migrations/v1_18/v228.go b/models/migrations/v1_18/v228.go similarity index 97% rename from models/gitea_migrations/v1_18/v228.go rename to models/migrations/v1_18/v228.go index 3f5b69734d..1161c8a4c9 100644 --- a/models/gitea_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/gitea_migrations/v1_18/v229.go b/models/migrations/v1_18/v229.go similarity index 98% rename from models/gitea_migrations/v1_18/v229.go rename to models/migrations/v1_18/v229.go index 00d794725f..f96dde9840 100644 --- a/models/gitea_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/gitea_migrations/v1_18/v229_test.go b/models/migrations/v1_18/v229_test.go similarity index 93% rename from models/gitea_migrations/v1_18/v229_test.go rename to models/migrations/v1_18/v229_test.go index deea44ab86..ac5e726a79 100644 --- a/models/gitea_migrations/v1_18/v229_test.go +++ b/models/migrations/v1_18/v229_test.go @@ -1,13 +1,13 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 +package v1_18 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" "forgejo.org/models/issues" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/gitea_migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go similarity index 95% rename from models/gitea_migrations/v1_18/v230.go rename to models/migrations/v1_18/v230.go index 078fce7643..ea5b4d02e1 100644 --- a/models/gitea_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/gitea_migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go similarity index 92% rename from models/gitea_migrations/v1_18/v230_test.go rename to models/migrations/v1_18/v230_test.go index 0db7025838..7dd6675673 100644 --- a/models/gitea_migrations/v1_18/v230_test.go +++ b/models/migrations/v1_18/v230_test.go @@ -1,12 +1,12 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 +package v1_18 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/migrations/v1_19/main_test.go b/models/migrations/v1_19/main_test.go new file mode 100644 index 0000000000..7c56926f4c --- /dev/null +++ b/models/migrations/v1_19/main_test.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_19 //nolint + +import ( + "testing" + + migration_tests "forgejo.org/models/migrations/test" +) + +func TestMain(m *testing.M) { + migration_tests.MainTest(m) +} diff --git a/models/gitea_migrations/v1_19/v231.go b/models/migrations/v1_19/v231.go similarity index 93% rename from models/gitea_migrations/v1_19/v231.go rename to models/migrations/v1_19/v231.go index 8ef1e4e743..79e46132f0 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v232.go b/models/migrations/v1_19/v232.go similarity index 95% rename from models/gitea_migrations/v1_19/v232.go rename to models/migrations/v1_19/v232.go index 2aab2cf830..7fb4a5ac8d 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v233.go b/models/migrations/v1_19/v233.go similarity index 99% rename from models/gitea_migrations/v1_19/v233.go rename to models/migrations/v1_19/v233.go index e62e8a9356..191afd4868 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go similarity index 96% rename from models/gitea_migrations/v1_19/v233_test.go rename to models/migrations/v1_19/v233_test.go index 72770d9544..4dc35d1e27 100644 --- a/models/gitea_migrations/v1_19/v233_test.go +++ b/models/migrations/v1_19/v233_test.go @@ -1,12 +1,12 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 +package v1_19 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/modules/json" "forgejo.org/modules/secret" "forgejo.org/modules/setting" diff --git a/models/gitea_migrations/v1_19/v234.go b/models/migrations/v1_19/v234.go similarity index 97% rename from models/gitea_migrations/v1_19/v234.go rename to models/migrations/v1_19/v234.go index e00b1cc2b6..c610a423dd 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v235.go b/models/migrations/v1_19/v235.go similarity index 92% rename from models/gitea_migrations/v1_19/v235.go rename to models/migrations/v1_19/v235.go index 297d90f65a..3715de3920 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go similarity index 96% rename from models/gitea_migrations/v1_19/v236.go rename to models/migrations/v1_19/v236.go index c453f95e04..fa01a6ab80 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v237.go b/models/migrations/v1_19/v237.go similarity index 95% rename from models/gitea_migrations/v1_19/v237.go rename to models/migrations/v1_19/v237.go index cf30226ccd..b23c765aa5 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v238.go b/models/migrations/v1_19/v238.go similarity index 97% rename from models/gitea_migrations/v1_19/v238.go rename to models/migrations/v1_19/v238.go index b257315319..7c912a8341 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go similarity index 95% rename from models/gitea_migrations/v1_19/v239.go rename to models/migrations/v1_19/v239.go index 8f4a65be95..10076f2401 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v240.go b/models/migrations/v1_19/v240.go similarity index 99% rename from models/gitea_migrations/v1_19/v240.go rename to models/migrations/v1_19/v240.go index c49ce2f49a..4ca5becede 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v241.go b/models/migrations/v1_19/v241.go similarity index 94% rename from models/gitea_migrations/v1_19/v241.go rename to models/migrations/v1_19/v241.go index e35801a057..a617d6fd2f 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v242.go b/models/migrations/v1_19/v242.go similarity index 96% rename from models/gitea_migrations/v1_19/v242.go rename to models/migrations/v1_19/v242.go index 87ca9cf214..bbf227ef77 100644 --- a/models/gitea_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/gitea_migrations/v1_19/v243.go b/models/migrations/v1_19/v243.go similarity index 91% rename from models/gitea_migrations/v1_19/v243.go rename to models/migrations/v1_19/v243.go index 9c3f372594..55bbfafb2f 100644 --- a/models/gitea_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/gitea_migrations/v1_20/main_test.go b/models/migrations/v1_20/main_test.go similarity index 69% rename from models/gitea_migrations/v1_20/main_test.go rename to models/migrations/v1_20/main_test.go index dd71e73804..f870dca429 100644 --- a/models/gitea_migrations/v1_20/main_test.go +++ b/models/migrations/v1_20/main_test.go @@ -1,12 +1,12 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 +package v1_20 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/gitea_migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go similarity index 95% rename from models/gitea_migrations/v1_20/v244.go rename to models/migrations/v1_20/v244.go index 76cdccaca5..977566ad7d 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go similarity index 95% rename from models/gitea_migrations/v1_20/v245.go rename to models/migrations/v1_20/v245.go index 730af95ad0..7e6585388b 100644 --- a/models/gitea_migrations/v1_20/v245.go +++ b/models/migrations/v1_20/v245.go @@ -1,13 +1,13 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 +package v1_20 //nolint import ( "context" "fmt" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/setting" "xorm.io/xorm" diff --git a/models/gitea_migrations/v1_20/v246.go b/models/migrations/v1_20/v246.go similarity index 91% rename from models/gitea_migrations/v1_20/v246.go rename to models/migrations/v1_20/v246.go index 22bf723404..e6340ef079 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v247.go b/models/migrations/v1_20/v247.go similarity index 97% rename from models/gitea_migrations/v1_20/v247.go rename to models/migrations/v1_20/v247.go index 056699d744..9ed810a623 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v248.go b/models/migrations/v1_20/v248.go similarity index 92% rename from models/gitea_migrations/v1_20/v248.go rename to models/migrations/v1_20/v248.go index 4f2091e4bc..40555210e7 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v249.go b/models/migrations/v1_20/v249.go similarity index 98% rename from models/gitea_migrations/v1_20/v249.go rename to models/migrations/v1_20/v249.go index 0aebb2a343..d2b096bf58 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go similarity index 99% rename from models/gitea_migrations/v1_20/v250.go rename to models/migrations/v1_20/v250.go index e12223691f..cfcde2fc9b 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v251.go b/models/migrations/v1_20/v251.go similarity index 97% rename from models/gitea_migrations/v1_20/v251.go rename to models/migrations/v1_20/v251.go index 7d2d259df6..c8665ba7eb 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v252.go b/models/migrations/v1_20/v252.go similarity index 97% rename from models/gitea_migrations/v1_20/v252.go rename to models/migrations/v1_20/v252.go index 435cce7ebe..bb85c78309 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v253.go b/models/migrations/v1_20/v253.go similarity index 97% rename from models/gitea_migrations/v1_20/v253.go rename to models/migrations/v1_20/v253.go index 73354fd485..5f4057e9d9 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v254.go b/models/migrations/v1_20/v254.go similarity index 95% rename from models/gitea_migrations/v1_20/v254.go rename to models/migrations/v1_20/v254.go index 9cdbfb3916..1e26979a5b 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v255.go b/models/migrations/v1_20/v255.go similarity index 95% rename from models/gitea_migrations/v1_20/v255.go rename to models/migrations/v1_20/v255.go index baa3c4b6d8..49b0ecf220 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v256.go b/models/migrations/v1_20/v256.go similarity index 96% rename from models/gitea_migrations/v1_20/v256.go rename to models/migrations/v1_20/v256.go index 7b84c1e154..822153b93e 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go similarity index 98% rename from models/gitea_migrations/v1_20/v257.go rename to models/migrations/v1_20/v257.go index 8045909dba..70f229d73f 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v258.go b/models/migrations/v1_20/v258.go similarity index 91% rename from models/gitea_migrations/v1_20/v258.go rename to models/migrations/v1_20/v258.go index 1d3faffdae..47174ce805 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v259.go b/models/migrations/v1_20/v259.go similarity index 99% rename from models/gitea_migrations/v1_20/v259.go rename to models/migrations/v1_20/v259.go index 9b2b68263e..f10b94fa9c 100644 --- a/models/gitea_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/gitea_migrations/v1_20/v259_test.go b/models/migrations/v1_20/v259_test.go similarity index 97% rename from models/gitea_migrations/v1_20/v259_test.go rename to models/migrations/v1_20/v259_test.go index 6805eb31ce..32e4aa3050 100644 --- a/models/gitea_migrations/v1_20/v259_test.go +++ b/models/migrations/v1_20/v259_test.go @@ -1,14 +1,14 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 +package v1_20 //nolint import ( "sort" "strings" "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/gitea_migrations/v1_21/main_test.go b/models/migrations/v1_21/main_test.go similarity index 69% rename from models/gitea_migrations/v1_21/main_test.go rename to models/migrations/v1_21/main_test.go index e467c66a91..7104887afb 100644 --- a/models/gitea_migrations/v1_21/main_test.go +++ b/models/migrations/v1_21/main_test.go @@ -1,12 +1,12 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 +package v1_21 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/gitea_migrations/v1_21/v260.go b/models/migrations/v1_21/v260.go similarity index 87% rename from models/gitea_migrations/v1_21/v260.go rename to models/migrations/v1_21/v260.go index 31f9c5b161..245f3011ab 100644 --- a/models/gitea_migrations/v1_21/v260.go +++ b/models/migrations/v1_21/v260.go @@ -1,10 +1,10 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 +package v1_21 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/gitea_migrations/v1_21/v261.go b/models/migrations/v1_21/v261.go similarity index 96% rename from models/gitea_migrations/v1_21/v261.go rename to models/migrations/v1_21/v261.go index 83a4927704..743bef152d 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v262.go b/models/migrations/v1_21/v262.go similarity index 91% rename from models/gitea_migrations/v1_21/v262.go rename to models/migrations/v1_21/v262.go index 6e88e29b9d..23e900572a 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v263.go b/models/migrations/v1_21/v263.go similarity index 98% rename from models/gitea_migrations/v1_21/v263.go rename to models/migrations/v1_21/v263.go index 55c418bde0..2c7cbadf0d 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v264.go b/models/migrations/v1_21/v264.go similarity index 98% rename from models/gitea_migrations/v1_21/v264.go rename to models/migrations/v1_21/v264.go index acd2c9bb48..5615600072 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v265.go b/models/migrations/v1_21/v265.go similarity index 96% rename from models/gitea_migrations/v1_21/v265.go rename to models/migrations/v1_21/v265.go index b6892acc27..800eb95f72 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v266.go b/models/migrations/v1_21/v266.go similarity index 94% rename from models/gitea_migrations/v1_21/v266.go rename to models/migrations/v1_21/v266.go index 440549e868..79a5f5e14c 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v267.go b/models/migrations/v1_21/v267.go similarity index 96% rename from models/gitea_migrations/v1_21/v267.go rename to models/migrations/v1_21/v267.go index 13992d8776..f94696a22b 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v268.go b/models/migrations/v1_21/v268.go similarity index 94% rename from models/gitea_migrations/v1_21/v268.go rename to models/migrations/v1_21/v268.go index b677d2383e..332793ff07 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v269.go b/models/migrations/v1_21/v269.go similarity index 90% rename from models/gitea_migrations/v1_21/v269.go rename to models/migrations/v1_21/v269.go index 042040927d..475ec02380 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v270.go b/models/migrations/v1_21/v270.go similarity index 96% rename from models/gitea_migrations/v1_21/v270.go rename to models/migrations/v1_21/v270.go index ab7c5660ba..b9cc84d3ac 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v271.go b/models/migrations/v1_21/v271.go similarity index 93% rename from models/gitea_migrations/v1_21/v271.go rename to models/migrations/v1_21/v271.go index e3ce2d4b74..f45c113c1f 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v272.go b/models/migrations/v1_21/v272.go similarity index 92% rename from models/gitea_migrations/v1_21/v272.go rename to models/migrations/v1_21/v272.go index 14c1e0c4b0..a729c49f1b 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go similarity index 97% rename from models/gitea_migrations/v1_21/v273.go rename to models/migrations/v1_21/v273.go index d6ec80d3d5..1ec6ade566 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v274.go b/models/migrations/v1_21/v274.go similarity index 97% rename from models/gitea_migrations/v1_21/v274.go rename to models/migrations/v1_21/v274.go index a1211d1fdd..b74e5fed51 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v275.go b/models/migrations/v1_21/v275.go similarity index 91% rename from models/gitea_migrations/v1_21/v275.go rename to models/migrations/v1_21/v275.go index 2bfe5c72fa..78804a59d6 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go similarity index 99% rename from models/gitea_migrations/v1_21/v276.go rename to models/migrations/v1_21/v276.go index 3b0bc23da7..0830c3bd92 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v277.go b/models/migrations/v1_21/v277.go similarity index 92% rename from models/gitea_migrations/v1_21/v277.go rename to models/migrations/v1_21/v277.go index 0c102eddde..12529160b7 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v278.go b/models/migrations/v1_21/v278.go similarity index 92% rename from models/gitea_migrations/v1_21/v278.go rename to models/migrations/v1_21/v278.go index 846f228678..d6a462d1e7 100644 --- a/models/gitea_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/gitea_migrations/v1_21/v279.go b/models/migrations/v1_21/v279.go similarity index 93% rename from models/gitea_migrations/v1_21/v279.go rename to models/migrations/v1_21/v279.go index beb39effe1..2abd1bbe84 100644 --- a/models/gitea_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/gitea_migrations/v1_22/main_test.go b/models/migrations/v1_22/main_test.go similarity index 69% rename from models/gitea_migrations/v1_22/main_test.go rename to models/migrations/v1_22/main_test.go index 81e4664b90..dc991b78fe 100644 --- a/models/gitea_migrations/v1_22/main_test.go +++ b/models/migrations/v1_22/main_test.go @@ -1,12 +1,12 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 +package v1_22 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/gitea_migrations/v1_22/v280.go b/models/migrations/v1_22/v280.go similarity index 96% rename from models/gitea_migrations/v1_22/v280.go rename to models/migrations/v1_22/v280.go index 2271cb6089..a8ee4a3bf7 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v281.go b/models/migrations/v1_22/v281.go similarity index 94% rename from models/gitea_migrations/v1_22/v281.go rename to models/migrations/v1_22/v281.go index 2eeca9be82..5271c786be 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v282.go b/models/migrations/v1_22/v282.go similarity index 92% rename from models/gitea_migrations/v1_22/v282.go rename to models/migrations/v1_22/v282.go index eed64c30f7..baad9e0916 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go similarity index 97% rename from models/gitea_migrations/v1_22/v283.go rename to models/migrations/v1_22/v283.go index 33a2513069..86946d1c39 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v283_test.go b/models/migrations/v1_22/v283_test.go similarity index 88% rename from models/gitea_migrations/v1_22/v283_test.go rename to models/migrations/v1_22/v283_test.go index 647555ca9e..d8e147a131 100644 --- a/models/gitea_migrations/v1_22/v283_test.go +++ b/models/migrations/v1_22/v283_test.go @@ -1,12 +1,12 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 +package v1_22 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/require" ) diff --git a/models/gitea_migrations/v1_22/v284.go b/models/migrations/v1_22/v284.go similarity index 94% rename from models/gitea_migrations/v1_22/v284.go rename to models/migrations/v1_22/v284.go index 31b38f6aed..2b95078980 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go similarity index 94% rename from models/gitea_migrations/v1_22/v285.go rename to models/migrations/v1_22/v285.go index fed89f670e..a55cc17c04 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go similarity index 98% rename from models/gitea_migrations/v1_22/v286.go rename to models/migrations/v1_22/v286.go index 05247bb436..d0489e7aeb 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go similarity index 96% rename from models/gitea_migrations/v1_22/v286_test.go rename to models/migrations/v1_22/v286_test.go index fd630508ec..c63deef495 100644 --- a/models/gitea_migrations/v1_22/v286_test.go +++ b/models/migrations/v1_22/v286_test.go @@ -1,12 +1,12 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 +package v1_22 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/models/gitea_migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go similarity index 96% rename from models/gitea_migrations/v1_22/v287.go rename to models/migrations/v1_22/v287.go index 5fd901f9de..c8b1593286 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v288.go b/models/migrations/v1_22/v288.go similarity index 95% rename from models/gitea_migrations/v1_22/v288.go rename to models/migrations/v1_22/v288.go index 78be3b6ef2..44e4991851 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go similarity index 96% rename from models/gitea_migrations/v1_22/v289.go rename to models/migrations/v1_22/v289.go index 78689a4ffa..b9941aadd9 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go similarity index 98% rename from models/gitea_migrations/v1_22/v290.go rename to models/migrations/v1_22/v290.go index ebafab6567..594e417644 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v290_test.go b/models/migrations/v1_22/v290_test.go similarity index 95% rename from models/gitea_migrations/v1_22/v290_test.go rename to models/migrations/v1_22/v290_test.go index a1c303cd28..569d77bc16 100644 --- a/models/gitea_migrations/v1_22/v290_test.go +++ b/models/migrations/v1_22/v290_test.go @@ -1,13 +1,13 @@ // 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" "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/modules/timeutil" webhook_module "forgejo.org/modules/webhook" diff --git a/models/gitea_migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go similarity index 94% rename from models/gitea_migrations/v1_22/v291.go rename to models/migrations/v1_22/v291.go index 823a644a95..74726fae96 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go similarity index 93% rename from models/gitea_migrations/v1_22/v292.go rename to models/migrations/v1_22/v292.go index 440f48ce80..beca556aee 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v293.go b/models/migrations/v1_22/v293.go similarity index 99% rename from models/gitea_migrations/v1_22/v293.go rename to models/migrations/v1_22/v293.go index e9c9746b26..9f38c3db56 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go similarity index 94% rename from models/gitea_migrations/v1_22/v293_test.go rename to models/migrations/v1_22/v293_test.go index 902e20fbc2..444146737d 100644 --- a/models/gitea_migrations/v1_22/v293_test.go +++ b/models/migrations/v1_22/v293_test.go @@ -1,13 +1,13 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 +package v1_22 //nolint import ( "testing" "forgejo.org/models/db" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "forgejo.org/models/project" "github.com/stretchr/testify/assert" diff --git a/models/gitea_migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go similarity index 98% rename from models/gitea_migrations/v1_22/v294.go rename to models/migrations/v1_22/v294.go index 6c52372306..314b4519f1 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go similarity index 87% rename from models/gitea_migrations/v1_22/v294_test.go rename to models/migrations/v1_22/v294_test.go index 197fada2a0..ef7b67ca5b 100644 --- a/models/gitea_migrations/v1_22/v294_test.go +++ b/models/migrations/v1_22/v294_test.go @@ -1,13 +1,13 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 +package v1_22 //nolint import ( "slices" "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -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/gitea_migrations/v1_22/v295.go b/models/migrations/v1_22/v295.go similarity index 96% rename from models/gitea_migrations/v1_22/v295.go rename to models/migrations/v1_22/v295.go index 319b1a399b..17bdadb4ad 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go similarity index 95% rename from models/gitea_migrations/v1_22/v296.go rename to models/migrations/v1_22/v296.go index 75350f9f65..1ecacab95f 100644 --- a/models/gitea_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/gitea_migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go similarity index 90% rename from models/gitea_migrations/v1_22/v298.go rename to models/migrations/v1_22/v298.go index 7700173a00..b9f3b95ade 100644 --- a/models/gitea_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/gitea_migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go similarity index 69% rename from models/gitea_migrations/v1_23/main_test.go rename to models/migrations/v1_23/main_test.go index f8337ab2ee..0fd90a4a67 100644 --- a/models/gitea_migrations/v1_23/main_test.go +++ b/models/migrations/v1_23/main_test.go @@ -1,12 +1,12 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 +package v1_23 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" ) func TestMain(m *testing.M) { diff --git a/models/gitea_migrations/v1_23/v299.go b/models/migrations/v1_23/v299.go similarity index 94% rename from models/gitea_migrations/v1_23/v299.go rename to models/migrations/v1_23/v299.go index 73ce19c875..f6db960c3b 100644 --- a/models/gitea_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/gitea_migrations/v1_23/v300.go b/models/migrations/v1_23/v300.go similarity index 96% rename from models/gitea_migrations/v1_23/v300.go rename to models/migrations/v1_23/v300.go index 404d8dbea8..f1f1cccdbf 100644 --- a/models/gitea_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/gitea_migrations/v1_23/v301.go b/models/migrations/v1_23/v301.go similarity index 95% rename from models/gitea_migrations/v1_23/v301.go rename to models/migrations/v1_23/v301.go index f2a4d8c559..b7797f6c6b 100644 --- a/models/gitea_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/gitea_migrations/v1_23/v302.go b/models/migrations/v1_23/v302.go similarity index 94% rename from models/gitea_migrations/v1_23/v302.go rename to models/migrations/v1_23/v302.go index 1b056993bd..c8ed786d63 100644 --- a/models/gitea_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/gitea_migrations/v1_23/v303.go b/models/migrations/v1_23/v303.go similarity index 95% rename from models/gitea_migrations/v1_23/v303.go rename to models/migrations/v1_23/v303.go index f6cbf7d859..fae0131bdd 100644 --- a/models/gitea_migrations/v1_23/v303.go +++ b/models/migrations/v1_23/v303.go @@ -1,10 +1,10 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package v1_23 +package v1_23 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" "xorm.io/xorm/schemas" diff --git a/models/gitea_migrations/v1_23/v303_test.go b/models/migrations/v1_23/v303_test.go similarity index 91% rename from models/gitea_migrations/v1_23/v303_test.go rename to models/migrations/v1_23/v303_test.go index 5eee15c47a..f105d11830 100644 --- a/models/gitea_migrations/v1_23/v303_test.go +++ b/models/migrations/v1_23/v303_test.go @@ -1,12 +1,12 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package v1_23 +package v1_23 //nolint import ( "testing" - migration_tests "forgejo.org/models/gitea_migrations/test" + migration_tests "forgejo.org/models/migrations/test" "github.com/stretchr/testify/require" "xorm.io/xorm/schemas" diff --git a/models/gitea_migrations/v1_6/v70.go b/models/migrations/v1_6/v70.go similarity index 99% rename from models/gitea_migrations/v1_6/v70.go rename to models/migrations/v1_6/v70.go index eb669f57b6..ec6bd09bb5 100644 --- a/models/gitea_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/gitea_migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go similarity index 96% rename from models/gitea_migrations/v1_6/v71.go rename to models/migrations/v1_6/v71.go index c9e96980d3..3706ad4406 100644 --- a/models/gitea_migrations/v1_6/v71.go +++ b/models/migrations/v1_6/v71.go @@ -1,12 +1,12 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 +package v1_6 //nolint import ( "fmt" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" diff --git a/models/gitea_migrations/v1_6/v72.go b/models/migrations/v1_6/v72.go similarity index 96% rename from models/gitea_migrations/v1_6/v72.go rename to models/migrations/v1_6/v72.go index 7cd2331376..4df2a0f6e9 100644 --- a/models/gitea_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/gitea_migrations/v1_7/v73.go b/models/migrations/v1_7/v73.go similarity index 94% rename from models/gitea_migrations/v1_7/v73.go rename to models/migrations/v1_7/v73.go index e0b7a28537..b5a748aae3 100644 --- a/models/gitea_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/gitea_migrations/v1_7/v74.go b/models/migrations/v1_7/v74.go similarity index 95% rename from models/gitea_migrations/v1_7/v74.go rename to models/migrations/v1_7/v74.go index 376be37a24..f0567e3c9b 100644 --- a/models/gitea_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/gitea_migrations/v1_7/v75.go b/models/migrations/v1_7/v75.go similarity index 97% rename from models/gitea_migrations/v1_7/v75.go rename to models/migrations/v1_7/v75.go index ef11575466..fa7430970c 100644 --- a/models/gitea_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/gitea_migrations/v1_8/v76.go b/models/migrations/v1_8/v76.go similarity index 98% rename from models/gitea_migrations/v1_8/v76.go rename to models/migrations/v1_8/v76.go index 8d47280b41..61ad006a47 100644 --- a/models/gitea_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/gitea_migrations/v1_8/v77.go b/models/migrations/v1_8/v77.go similarity index 92% rename from models/gitea_migrations/v1_8/v77.go rename to models/migrations/v1_8/v77.go index 4fe5ebe635..8b19993924 100644 --- a/models/gitea_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/gitea_migrations/v1_8/v78.go b/models/migrations/v1_8/v78.go similarity index 92% rename from models/gitea_migrations/v1_8/v78.go rename to models/migrations/v1_8/v78.go index 829435ead6..8102b19335 100644 --- a/models/gitea_migrations/v1_8/v78.go +++ b/models/migrations/v1_8/v78.go @@ -1,10 +1,10 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 +package v1_8 //nolint import ( - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "xorm.io/xorm" ) diff --git a/models/gitea_migrations/v1_8/v79.go b/models/migrations/v1_8/v79.go similarity index 96% rename from models/gitea_migrations/v1_8/v79.go rename to models/migrations/v1_8/v79.go index c8e0db531f..f7d2d68f96 100644 --- a/models/gitea_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/gitea_migrations/v1_8/v80.go b/models/migrations/v1_8/v80.go similarity index 93% rename from models/gitea_migrations/v1_8/v80.go rename to models/migrations/v1_8/v80.go index 6f9df47a93..cebbbead28 100644 --- a/models/gitea_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/gitea_migrations/v1_8/v81.go b/models/migrations/v1_8/v81.go similarity index 96% rename from models/gitea_migrations/v1_8/v81.go rename to models/migrations/v1_8/v81.go index 8152a47ad7..734fc24641 100644 --- a/models/gitea_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/gitea_migrations/v1_9/v82.go b/models/migrations/v1_9/v82.go similarity index 99% rename from models/gitea_migrations/v1_9/v82.go rename to models/migrations/v1_9/v82.go index 235c73c504..78a90bdde9 100644 --- a/models/gitea_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/gitea_migrations/v1_9/v83.go b/models/migrations/v1_9/v83.go similarity index 96% rename from models/gitea_migrations/v1_9/v83.go rename to models/migrations/v1_9/v83.go index 9640564a44..fa24a92d28 100644 --- a/models/gitea_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/gitea_migrations/v1_9/v84.go b/models/migrations/v1_9/v84.go similarity index 93% rename from models/gitea_migrations/v1_9/v84.go rename to models/migrations/v1_9/v84.go index 423915ae57..c7155fe9cf 100644 --- a/models/gitea_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/gitea_migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go similarity index 98% rename from models/gitea_migrations/v1_9/v85.go rename to models/migrations/v1_9/v85.go index c54a006f4c..d8e9d91840 100644 --- a/models/gitea_migrations/v1_9/v85.go +++ b/models/migrations/v1_9/v85.go @@ -1,12 +1,12 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 +package v1_9 //nolint import ( "fmt" - "forgejo.org/models/gitea_migrations/base" + "forgejo.org/models/migrations/base" "forgejo.org/modules/log" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" diff --git a/models/gitea_migrations/v1_9/v86.go b/models/migrations/v1_9/v86.go similarity index 92% rename from models/gitea_migrations/v1_9/v86.go rename to models/migrations/v1_9/v86.go index 9464ff0cf6..cf2725d158 100644 --- a/models/gitea_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/gitea_migrations/v1_9/v87.go b/models/migrations/v1_9/v87.go similarity index 93% rename from models/gitea_migrations/v1_9/v87.go rename to models/migrations/v1_9/v87.go index 81a4ebf80d..fa01b6e5e3 100644 --- a/models/gitea_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..3a6244ef4c 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"}, } } @@ -113,7 +104,6 @@ type AbuseReport struct { // The ID of the corresponding shadow-copied content when exists; otherwise null. ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` - ResolvedUnix timeutil.TimeStamp `xorm:"DEFAULT NULL"` } var ErrSelfReporting = errors.New("reporting yourself is not allowed") @@ -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..d363610a48 100644 --- a/models/moderation/shadow_copy.go +++ b/models/moderation/shadow_copy.go @@ -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/perm/access/access.go b/models/perm/access/access.go index 54526d0f5c..87ee600a15 100644 --- a/models/perm/access/access.go +++ b/models/perm/access/access.go @@ -22,8 +22,8 @@ import ( // repository, the members of the owners team are in this table. type Access struct { ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"UNIQUE(s) REFERENCES(user, id)"` - RepoID int64 `xorm:"UNIQUE(s) REFERENCES(repository, id)"` + UserID int64 `xorm:"UNIQUE(s)"` + RepoID int64 `xorm:"UNIQUE(s)"` Mode perm.AccessMode } 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.go b/models/repo.go index da3d33cf4a..6f7ae25615 100644 --- a/models/repo.go +++ b/models/repo.go @@ -292,10 +292,42 @@ func UpdateRepoStats(ctx context.Context, id int64) error { return nil } +func updateUserStarNumbers(ctx context.Context, users []user_model.User) error { + ctx, committer, err := db.TxContext(ctx) + if err != nil { + return err + } + defer committer.Close() + + for _, user := range users { + if _, err = db.Exec(ctx, "UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=?) WHERE id=?", user.ID, user.ID); err != nil { + return err + } + } + + return committer.Commit() +} + // DoctorUserStarNum recalculate Stars number for all user func DoctorUserStarNum(ctx context.Context) (err error) { - _, err = db.Exec(ctx, "UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=`user`.id) WHERE type = 0") + const batchSize = 100 + + for start := 0; ; start += batchSize { + users := make([]user_model.User, 0, batchSize) + if err = db.GetEngine(ctx).Limit(batchSize, start).Where("type = ?", 0).Cols("id").Find(&users); err != nil { + return err + } + if len(users) == 0 { + break + } + + if err = updateUserStarNumbers(ctx, users); err != nil { + return err + } + } + log.Debug("recalculate Stars number for all user finished") + return err } diff --git a/models/repo/TestGetUserForkLax/repository.yml b/models/repo/TestGetUserForkLax/repository.yml deleted file mode 100644 index c91ba18678..0000000000 --- a/models/repo/TestGetUserForkLax/repository.yml +++ /dev/null @@ -1,32 +0,0 @@ -- - id: 64 - owner_id: 15 - owner_name: user15 - lower_name: repo10 - name: repo10 - default_branch: master - num_watches: 0 - num_stars: 0 - num_forks: 0 - num_issues: 0 - num_closed_issues: 0 - num_pulls: 0 - num_closed_pulls: 0 - num_milestones: 0 - num_closed_milestones: 0 - num_projects: 0 - num_closed_projects: 0 - is_private: false - is_empty: false - is_archived: false - is_mirror: false - status: 0 - is_fork: true - fork_id: 10 - is_template: false - template_id: 0 - size: 0 - is_fsck_enabled: true - close_issues_via_commit_in_any_branch: false - topics: '[]' - diff --git a/models/repo/TestGetUserForkLaxWithTwoChoices/repository.yml b/models/repo/TestGetUserForkLaxWithTwoChoices/repository.yml deleted file mode 100644 index 859e76db88..0000000000 --- a/models/repo/TestGetUserForkLaxWithTwoChoices/repository.yml +++ /dev/null @@ -1,63 +0,0 @@ -- - id: 64 - owner_id: 15 - owner_name: user15 - lower_name: repo10 - name: repo10 - default_branch: master - num_watches: 0 - num_stars: 0 - num_forks: 0 - num_issues: 0 - num_closed_issues: 0 - num_pulls: 0 - num_closed_pulls: 0 - num_milestones: 0 - num_closed_milestones: 0 - num_projects: 0 - num_closed_projects: 0 - is_private: false - is_empty: false - is_archived: false - is_mirror: false - status: 0 - is_fork: true - fork_id: 10 - is_template: false - template_id: 0 - size: 0 - is_fsck_enabled: true - close_issues_via_commit_in_any_branch: false - topics: '[]' -- - id: 65 - owner_id: 15 - owner_name: user15 - lower_name: repo11 - name: repo11 - default_branch: master - num_watches: 0 - num_stars: 0 - num_forks: 0 - num_issues: 0 - num_closed_issues: 0 - num_pulls: 0 - num_closed_pulls: 0 - num_milestones: 0 - num_closed_milestones: 0 - num_projects: 0 - num_closed_projects: 0 - is_private: false - is_empty: false - is_archived: false - is_mirror: false - status: 0 - is_fork: true - fork_id: 11 - is_template: false - template_id: 0 - size: 0 - is_fsck_enabled: true - close_issues_via_commit_in_any_branch: false - topics: '[]' - diff --git a/models/repo/collaboration.go b/models/repo/collaboration.go index eec03b92b5..16d10d38b6 100644 --- a/models/repo/collaboration.go +++ b/models/repo/collaboration.go @@ -19,8 +19,8 @@ import ( // Collaboration represent the relation between an individual and a repository. type Collaboration struct { ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL REFERENCES(repository, id)"` - UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL REFERENCES(user, id)"` + RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"` + UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"` Mode perm.AccessMode `xorm:"DEFAULT 2 NOT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` diff --git a/models/repo/fork.go b/models/repo/fork.go index f96f4cf117..ed8b488738 100644 --- a/models/repo/fork.go +++ b/models/repo/fork.go @@ -5,7 +5,6 @@ package repo import ( "context" - "fmt" "forgejo.org/models/db" "forgejo.org/models/unit" @@ -43,22 +42,6 @@ func HasForkedRepo(ctx context.Context, ownerID, repoID int64) bool { return has } -// HasForkedRepoLax checks if given user has already forked a repository with given ID, -// or if it the target repository is itself a fork, whether the user has a fork of its base -// (as that can also be used to make a PR). -func HasForkedRepoLax(ctx context.Context, ownerID int64, baseRepo *Repository) bool { - query := db.GetEngine(ctx). - Table("repository"). - Where("owner_id=?", ownerID) - if baseRepo.IsFork { - query = query.And("fork_id=? OR fork_id=?", baseRepo.ID, baseRepo.ForkID) - } else { - query = query.And("fork_id=?", baseRepo.ID) - } - has, _ := query.Exist() - return has -} - // GetUserFork return user forked repository from this repository, if not forked return nil func GetUserFork(ctx context.Context, repoID, userID int64) (*Repository, error) { var forkedRepo Repository @@ -72,31 +55,6 @@ func GetUserFork(ctx context.Context, repoID, userID int64) (*Repository, error) return &forkedRepo, nil } -// GetUserForkLax returns the user forked repository from this repository. -// If the passed repository is itself a fork and we have a fork of its base, it will be used as -// a fall-back. Otherwise return nil. -func GetUserForkLax(ctx context.Context, baseRepo *Repository, userID int64) (*Repository, error) { - var forkedRepo Repository - query := db.GetEngine(ctx).Where("owner_id = ?", userID) - if baseRepo.IsFork { - query = query.And("fork_id = ? OR fork_id = ?", baseRepo.ID, baseRepo.ForkID) - // prefer any repository that is marked as an exact fork of the target - // (ordering by a boolean means returning the rows where the condition is false first, - // hence the counter-intuitive condition) - query.OrderBy(fmt.Sprintf("fork_id != %d", baseRepo.ID)) - } else { - query = query.And("fork_id = ?", baseRepo.ID) - } - has, err := query.Get(&forkedRepo) - if err != nil { - return nil, err - } - if !has { - return nil, nil - } - return &forkedRepo, nil -} - // GetForks returns all the forks of the repository that are visible to the user. func GetForks(ctx context.Context, repo *Repository, user *user_model.User, listOptions db.ListOptions) ([]*Repository, int64, error) { sess := db.GetEngine(ctx).Where(AccessibleRepositoryCondition(user, unit.TypeInvalid)) diff --git a/models/repo/fork_test.go b/models/repo/fork_test.go index fe18bbb423..d567081ee6 100644 --- a/models/repo/fork_test.go +++ b/models/repo/fork_test.go @@ -32,80 +32,3 @@ func TestGetUserFork(t *testing.T) { require.NoError(t, err) assert.Nil(t, repo) } - -func TestGetUserForkLax(t *testing.T) { - defer unittest.OverrideFixtures("models/repo/TestGetUserForkLax")() - require.NoError(t, unittest.PrepareTestDatabase()) - - // User13 has repo 11 forked from repo10 - repo10, err := repo_model.GetRepositoryByID(db.DefaultContext, 10) - require.NoError(t, err) - assert.NotNil(t, repo10) - require.True(t, repo_model.HasForkedRepoLax(db.DefaultContext, 13, repo10)) - repo11, err := repo_model.GetUserForkLax(db.DefaultContext, repo10, 13) - require.NoError(t, err) - assert.NotNil(t, repo11) - assert.Equal(t, int64(11), repo11.ID) - assert.Equal(t, int64(10), repo11.ForkID) - - // user13 does not have a fork of repo9 - repo9, err := repo_model.GetRepositoryByID(db.DefaultContext, 9) - require.NoError(t, err) - assert.NotNil(t, repo9) - require.False(t, repo_model.HasForkedRepoLax(db.DefaultContext, 13, repo9)) - fork, err := repo_model.GetUserForkLax(db.DefaultContext, repo9, 13) - require.NoError(t, err) - assert.Nil(t, fork) - - // User15 has repo id 64 forked from repo10, which counts as a fork of repo11 since they have a common base - require.False(t, repo_model.HasForkedRepo(db.DefaultContext, 15, repo11.ID)) - require.True(t, repo_model.HasForkedRepoLax(db.DefaultContext, 15, repo11)) - fork, err = repo_model.GetUserForkLax(db.DefaultContext, repo11, 15) - require.NoError(t, err) - assert.NotNil(t, fork) - assert.Equal(t, int64(64), fork.ID) - assert.Equal(t, int64(10), fork.ForkID) -} - -func TestGetUserForkLaxWithTwoChoices(t *testing.T) { - defer unittest.OverrideFixtures("models/repo/TestGetUserForkLaxWithTwoChoices")() - require.NoError(t, unittest.PrepareTestDatabase()) - - // Test scenario: - // - // - repo10 - // - forked by user15 as repo64 - // - forked by user13 as repo11 - // - forked by user15 as repo65 - // - // In this scenario, both repo64 and repo65 can be used as forks of repo11 for user15, - // but we prefer to use repo65 because it's specifically marked as a fork of repo11 - - repo10, err := repo_model.GetRepositoryByID(db.DefaultContext, 10) - require.NoError(t, err) - assert.NotNil(t, repo10) - - // User15 has repo64 forked from repo10 - require.True(t, repo_model.HasForkedRepoLax(db.DefaultContext, 15, repo10)) - repo64, err := repo_model.GetUserForkLax(db.DefaultContext, repo10, 15) - require.NoError(t, err) - assert.NotNil(t, repo64) - assert.Equal(t, int64(64), repo64.ID) - assert.Equal(t, int64(10), repo64.ForkID) - - // User13 has repo11 forked from repo10 - require.True(t, repo_model.HasForkedRepoLax(db.DefaultContext, 13, repo10)) - repo11, err := repo_model.GetUserForkLax(db.DefaultContext, repo10, 13) - require.NoError(t, err) - assert.NotNil(t, repo11) - assert.Equal(t, int64(11), repo11.ID) - assert.Equal(t, int64(10), repo11.ForkID) - - // User15 has repo65 forked from repo11 - require.True(t, repo_model.HasForkedRepoLax(db.DefaultContext, 15, repo11)) - repo65, err := repo_model.GetUserForkLax(db.DefaultContext, repo11, 15) - require.NoError(t, err) - assert.NotNil(t, repo65) - assert.Equal(t, int64(65), repo65.ID) - assert.Equal(t, int64(11), repo65.ForkID) -} 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/release.go b/models/repo/release.go index 2310de7cb9..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:"-"` @@ -180,11 +180,7 @@ func (r *Release) HTMLURL() string { } // APIUploadURL the api url to upload assets to a release. release must have attributes loaded -// If `githubFormat` is true, then `{?name,label}` is added to match the Github API. -func (r *Release) APIUploadURL(githubFormat bool) string { - if githubFormat { - return r.APIURL() + "/assets{?name,label}" - } +func (r *Release) APIUploadURL() string { return r.APIURL() + "/assets" } @@ -622,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/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/repo/update.go b/models/repo/update.go index 2c7a6b2b0b..0222d09de5 100644 --- a/models/repo/update.go +++ b/models/repo/update.go @@ -10,6 +10,7 @@ import ( "forgejo.org/models/db" user_model "forgejo.org/models/user" + "forgejo.org/modules/log" "forgejo.org/modules/util" ) @@ -104,9 +105,9 @@ func (err ErrRepoFilesAlreadyExist) Unwrap() error { return util.ErrAlreadyExist } -// CheckCreateRepository check if a repository can be created -func CheckCreateRepository(ctx context.Context, doer, u *user_model.User, name string) error { - if !doer.IsAdmin && !u.CanCreateRepo() { +// CheckCreateRepository check if could created a repository +func CheckCreateRepository(ctx context.Context, doer, u *user_model.User, name string, overwriteOrAdopt bool) error { + if !doer.CanCreateRepo() { return ErrReachLimitOfRepo{u.MaxRepoCreation} } @@ -121,6 +122,15 @@ func CheckCreateRepository(ctx context.Context, doer, u *user_model.User, name s return ErrRepoAlreadyExist{u.Name, name} } + repoPath := RepoPath(u.Name, name) + isExist, err := util.IsExist(repoPath) + if err != nil { + log.Error("Unable to check if %s exists. Error: %v", repoPath, err) + return err + } + if !overwriteOrAdopt && isExist { + return ErrRepoFilesAlreadyExist{u.Name, name} + } return nil } diff --git a/models/repo/update_test.go b/models/repo/update_test.go deleted file mode 100644 index cf74e2d4f0..0000000000 --- a/models/repo/update_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package repo_test - -import ( - "testing" - - "forgejo.org/models/db" - repo_model "forgejo.org/models/repo" - "forgejo.org/models/unittest" - user_model "forgejo.org/models/user" - "forgejo.org/modules/util" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestCheckCreateRepository(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - t.Run("Success", func(t *testing.T) { - user := &user_model.User{MaxRepoCreation: 1} - - err := repo_model.CheckCreateRepository(db.DefaultContext, user, user, "testName") - - require.NoError(t, err) - }) - - t.Run("AdminIgnoresRepoLimit", func(t *testing.T) { - user := &user_model.User{MaxRepoCreation: 0, IsAdmin: true} - - err := repo_model.CheckCreateRepository(db.DefaultContext, user, user, "testName") - - require.NoError(t, err) - }) - - t.Run("RepoLimitReached", func(t *testing.T) { - user := &user_model.User{MaxRepoCreation: 0} - - err := repo_model.CheckCreateRepository(db.DefaultContext, user, user, "testName") - - require.ErrorIs(t, err, repo_model.ErrReachLimitOfRepo{}) - }) - - t.Run("UnusableRepoName", func(t *testing.T) { - user := &user_model.User{MaxRepoCreation: 1} - - err := repo_model.CheckCreateRepository(db.DefaultContext, user, user, "testName/") - - require.ErrorIs(t, err, db.ErrNameCharsNotAllowed{Name: "testName/"}) - }) - - t.Run("RepoAlreadyExists", func(t *testing.T) { - unittest.AssertExistsIf(t, true, &repo_model.Repository{Name: "repo1"}) - user := &user_model.User{MaxRepoCreation: 2} - - err := repo_model.CheckCreateRepository(db.DefaultContext, user, user, "repo1") - - require.ErrorIs(t, err, repo_model.ErrRepoAlreadyExist{Name: "repo1"}) - }) - - t.Run("RepoDirAlreadyExists", func(t *testing.T) { - db.DeleteByBean(db.DefaultContext, &repo_model.Repository{Name: "repo1"}) - user := &user_model.User{MaxRepoCreation: 2, Name: "user2"} - - exists, _ := util.IsExist(repo_model.RepoPath("user2", "repo1")) - assert.True(t, exists) - - err := repo_model.CheckCreateRepository(db.DefaultContext, user, user, "repo1") - - require.ErrorIs(t, err, repo_model.ErrRepoAlreadyExist{Name: "repo1", Uname: "user2"}) - }) -} diff --git a/models/repo/upload.go b/models/repo/upload.go index a213cb1986..49152db7fd 100644 --- a/models/repo/upload.go +++ b/models/repo/upload.go @@ -104,6 +104,17 @@ func GetUploadByUUID(ctx context.Context, uuid string) (*Upload, error) { return upload, nil } +// GetUploadsByUUIDs returns multiple uploads by UUIDS +func GetUploadsByUUIDs(ctx context.Context, uuids []string) ([]*Upload, error) { + if len(uuids) == 0 { + return []*Upload{}, nil + } + + // Silently drop invalid uuids. + uploads := make([]*Upload, 0, len(uuids)) + return uploads, db.GetEngine(ctx).In("uuid", uuids).Find(&uploads) +} + // DeleteUploads deletes multiple uploads func DeleteUploads(ctx context.Context, uploads ...*Upload) (err error) { if len(uploads) == 0 { 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/lint-locale-usage/llu.go b/models/unit/lint-locale-usage/llu.go deleted file mode 100644 index 51e120dba2..0000000000 --- a/models/unit/lint-locale-usage/llu.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package lintLocaleUsage - -import ( - "go/ast" - "go/token" - "strconv" - - llu "forgejo.org/build/lint-locale-usage" -) - -func HandleCompositeUnit(handler llu.Handler, fset *token.FileSet, n *ast.CompositeLit) { - ident, ok := n.Type.(*ast.Ident) - if !ok || ident.Name != "Unit" { - return - } - - if len(n.Elts) != 6 { - handler.OnWarning(fset, n.Pos(), "unexpected initialization of 'Unit' (unexpected number of arguments)") - return - } - // NameKey has index 2 - // invoked like '{{ctx.Locale.Tr $unit.NameKey}}' - nameKey, ok := n.Elts[2].(*ast.BasicLit) - if !ok || nameKey.Kind != token.STRING { - handler.OnWarning(fset, n.Elts[2].Pos(), "unexpected initialization of 'Unit' (expected string literal as NameKey)") - return - } - - // extract string content - arg, err := strconv.Unquote(nameKey.Value) - if err == nil { - // found interesting strings - handler.OnMsgid(fset, nameKey.ValuePos, arg, false) - } -} 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/fault_injector.go b/models/unittest/fault_injector.go deleted file mode 100644 index 20a40cce45..0000000000 --- a/models/unittest/fault_injector.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later -package unittest - -import ( - "context" - "errors" - - "xorm.io/xorm/contexts" -) - -var ( - faultInjectorCount int64 - faultInjectorNumQueries int64 = -1 - ErrFaultInjected = errors.New("nobody expects a fault injection") -) - -type faultInjectorHook struct{} - -var _ contexts.Hook = &faultInjectorHook{} - -func (faultInjectorHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { - if faultInjectorNumQueries == -1 { - return c.Ctx, nil - } - - // Always allow ROLLBACK, we always want to allow for transactions to get cancelled. - if faultInjectorCount == faultInjectorNumQueries && c.SQL != "ROLLBACK" { - return c.Ctx, ErrFaultInjected - } - - faultInjectorCount++ - - return c.Ctx, nil -} - -func (faultInjectorHook) AfterProcess(*contexts.ContextHook) error { - return nil -} - -// Allow `numQueries` before all database queries will fail until the -// returning function is executed. -func SetFaultInjector(numQueries int64) func() { - faultInjectorNumQueries = numQueries - - return func() { - faultInjectorNumQueries = -1 - faultInjectorCount = 0 - } -} diff --git a/models/unittest/fault_injector_test.go b/models/unittest/fault_injector_test.go deleted file mode 100644 index 7b1e601d51..0000000000 --- a/models/unittest/fault_injector_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later -package unittest - -import ( - "testing" - - "github.com/stretchr/testify/require" - "xorm.io/xorm/contexts" -) - -func TestFaultInjector(t *testing.T) { - faultInjector := faultInjectorHook{} - c := &contexts.ContextHook{ - Ctx: t.Context(), - SQL: "Hello, 世界", // We don't check for valid SQL anyway. - } - - t.Run("Should not block", func(t *testing.T) { - // Currently no fault injection is set, so this should go through. - for range 100 { - _, err := faultInjector.BeforeProcess(c) - require.NoError(t, err) - } - }) - - t.Run("Reset", func(t *testing.T) { - // Okay only allow one query to go through. - reset := SetFaultInjector(1) - - // Do the only query. - _, err := faultInjector.BeforeProcess(c) - require.NoError(t, err) - - // Now we reset, we don't check the blocking behavior yet. We first - // must know that we can safely reset. - reset() - - // This should go through. - for range 100 { - _, err := faultInjector.BeforeProcess(c) - require.NoError(t, err) - } - }) - - t.Run("Blocking", func(t *testing.T) { - // Okay only allow one query to go through. - reset := SetFaultInjector(1) - - // Do the only query. - _, err := faultInjector.BeforeProcess(c) - require.NoError(t, err) - - // Any query now will return a error. - for range 100 { - _, err := faultInjector.BeforeProcess(c) - require.ErrorIs(t, err, ErrFaultInjected) - } - - // Ah but there's a exemption for `ROLLBACK`. - _, err = faultInjector.BeforeProcess(&contexts.ContextHook{Ctx: t.Context(), SQL: "ROLLBACK"}) - require.NoError(t, err) - - reset() - }) - - t.Run("Number of queries", func(t *testing.T) { - // For funsies lets test a bunch of max numbers of queries. - for i := range int64(1024) { - // Allow i queries - reset := SetFaultInjector(i) - - // Make i queries. - for range i { - _, err := faultInjector.BeforeProcess(c) - require.NoError(t, err) - } - - // After i'th query it returns a error. - _, err := faultInjector.BeforeProcess(c) - require.ErrorIs(t, err, ErrFaultInjected) - - reset() - } - }) -} diff --git a/models/unittest/fixture_loader.go b/models/unittest/fixture_loader.go index 5aea06550c..67ef1b28df 100644 --- a/models/unittest/fixture_loader.go +++ b/models/unittest/fixture_loader.go @@ -10,13 +10,9 @@ import ( "fmt" "os" "path/filepath" - "slices" "strings" - "forgejo.org/models/db" - "forgejo.org/modules/container" - - "go.yaml.in/yaml/v3" + "gopkg.in/yaml.v3" ) type insertSQL struct { @@ -36,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.Clone() - // Load fixtures for _, fixturePath := range fixturePaths { stat, err := os.Stat(fixturePath) @@ -65,10 +59,7 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTabl if err != nil { return nil, err } - if allTableNames.Contains(fixtureFile.name) { - l.fixtureFiles = append(l.fixtureFiles, fixtureFile) - tablesWithoutFixture.Remove(fixtureFile.name) - } + l.fixtureFiles = append(l.fixtureFiles, fixtureFile) } } } else { @@ -76,23 +67,10 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTabl if err != nil { return nil, err } - if allTableNames.Contains(fixtureFile.name) { - l.fixtureFiles = append(l.fixtureFiles, fixtureFile) - tablesWithoutFixture.Remove(fixtureFile.name) - } + l.fixtureFiles = append(l.fixtureFiles, fixtureFile) } } - // 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, - }) - } - - l.orderFixtures() - return l, nil } @@ -188,14 +166,6 @@ func (l *loader) buildFixtureFile(fixturePath string) (*fixtureFile, error) { return fixture, nil } -// Reorganize `fixtureFiles` based upon the order that they'll need to be inserted into the database to satisfy foreign -// key constraints. -func (l *loader) orderFixtures() { - slices.SortFunc(l.fixtureFiles, func(v1, v2 *fixtureFile) int { - return db.TableNameInsertionOrderSortFunc(v1.name, v2.name) - }) -} - func (l *loader) Load() error { // Start transaction. tx, err := l.db.Begin() @@ -208,19 +178,15 @@ func (l *loader) Load() error { }() // Clean the table and re-insert the fixtures. - tableDeleted := make(container.Set[string]) - - // Issue deletes first, in reverse of insertion order, to maintain foreign key constraints. - for i := len(l.fixtureFiles) - 1; i >= 0; i-- { - fixture := l.fixtureFiles[i] - if !tableDeleted.Contains(fixture.name) { + tableDeleted := map[string]struct{}{} + for _, fixture := range l.fixtureFiles { + 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 _, fixture := range l.fixtureFiles { + for _, insertSQL := range fixture.insertSQLs { if _, err := tx.Exec(insertSQL.statement, insertSQL.values...); err != nil { return fmt.Errorf("cannot insert %q with values %q: %w", insertSQL.statement, insertSQL.values, err) diff --git a/models/unittest/fixtures.go b/models/unittest/fixtures.go index 0019ae147b..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,17 +75,7 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { panic("Unsupported RDBMS for test") } - var allTables container.Set[string] - if opts.OnlyAffectModels == nil { - allTables = allTableNames().Clone() - } else { - allTables = make(container.Set[string]) - for _, bean := range opts.OnlyAffectModels { - allTables.Add(e.TableName(bean)) - } - } - - 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/testdb.go b/models/unittest/testdb.go index 0fbf7ff6bb..d34c9e9a0a 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -217,9 +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. When OnlyAffectModels is not-nil, - // cleaning registered models will be skipped and only these models with fixtures are considered. - OnlyAffectModels []any } // CreateTestEngine creates a memory database and loads the fixture data from fixturesDir @@ -232,7 +229,6 @@ func CreateTestEngine(opts FixturesOptions) error { return err } x.SetMapper(names.GonicMapper{}) - x.AddHook(faultInjectorHook{}) db.SetDefaultEngine(context.Background(), x) if err = db.SyncAllTables(); err != nil { 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/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..f9c16a17b3 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 { 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/search.go b/models/user/search.go index 08cf6a14a3..b30422e269 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -38,7 +38,6 @@ type SearchUserOptions struct { IsRestricted optional.Option[bool] IsTwoFactorEnabled optional.Option[bool] IsProhibitLogin optional.Option[bool] - AccountType optional.Option[UserType] IncludeReserved bool Load2FAStatus bool @@ -124,10 +123,6 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.Value()}) } - if opts.AccountType.Has() { - cond = cond.And(builder.Eq{"type": opts.AccountType.Value()}) - } - e := db.GetEngine(ctx) if !opts.IsTwoFactorEnabled.Has() { return e.Where(cond) diff --git a/models/user/user.go b/models/user/user.go index 819199607d..bfd7e6063f 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -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) } @@ -1194,9 +1155,7 @@ func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) [] return newCommits } -// GetUserByEmail returns the user associated with the email, if it exists -// and is activated. If the email is a no-reply address, then the user -// associated with that no-reply address is returned. +// GetUserByEmail returns the user object by given e-mail if exists. func GetUserByEmail(ctx context.Context, email string) (*User, error) { if len(email) == 0 { return nil, ErrUserNotExist{Name: email} @@ -1229,26 +1188,6 @@ func GetUserByEmail(ctx context.Context, email string) (*User, error) { return nil, ErrUserNotExist{Name: email} } -// GetUserByEmailSimple returns the user associated with the email, if it exists. -// -// NOTE: You likely should use `GetUserByEmail`, which handles the no-reply -// address and only uses activated emails to get the user. -func GetUserByEmailSimple(ctx context.Context, email string) (*User, error) { - if len(email) == 0 { - return nil, ErrUserNotExist{Name: email} - } - - emailAddress := &EmailAddress{} - has, err := db.GetEngine(ctx).Where("lower_email = ?", strings.ToLower(email)).Get(emailAddress) - if err != nil { - return nil, err - } else if !has { - return nil, ErrUserNotExist{Name: email} - } - - return GetUserByID(ctx, emailAddress.UID) -} - // GetUser checks if a user already exists func GetUser(ctx context.Context, user *User) (bool, error) { return db.GetEngine(ctx).Get(user) 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 0a1802ba6a..288a45105b 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) } @@ -219,10 +197,10 @@ func TestSearchUsers(t *testing.T) { } testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}}, - []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40, 42, 1041}) + []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40, 1041}) testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)}, - []int64{42, 9}) + []int64{9}) testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40, 1041}) @@ -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})() @@ -999,7 +838,6 @@ func TestPronounsPrivacy(t *testing.T) { func TestGetUserByEmail(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - defer test.MockVariableValue(&setting.Service.NoReplyAddress, "noreply.example.org")() t.Run("Normal", func(t *testing.T) { u, err := user_model.GetUserByEmail(t.Context(), "user2@example.com") @@ -1018,33 +856,4 @@ func TestGetUserByEmail(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, 1, u.ID) }) - - t.Run("No-reply", func(t *testing.T) { - u, err := user_model.GetUserByEmail(t.Context(), "user1@noreply.example.org") - require.NoError(t, err) - assert.EqualValues(t, 1, u.ID) - }) -} - -func TestGetUserByEmailSimple(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - defer test.MockVariableValue(&setting.Service.NoReplyAddress, "noreply.example.org")() - - t.Run("Normal", func(t *testing.T) { - u, err := user_model.GetUserByEmailSimple(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.GetUserByEmailSimple(t.Context(), "user11@example.com") - require.NoError(t, err) - assert.EqualValues(t, 11, u.ID) - }) - - t.Run("No-reply", func(t *testing.T) { - u, err := user_model.GetUserByEmailSimple(t.Context(), "user1@noreply.example.org") - require.ErrorIs(t, err, user_model.ErrUserNotExist{Name: "user1@noreply.example.org"}) - assert.Nil(t, u) - }) } diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index f50e5f8289..7ae4557ed6 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -13,18 +13,17 @@ 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 { - EntryName string - TriggerEvent *jobparser.Event - Content []byte - EventDetectionError error + EntryName string + TriggerEvent *jobparser.Event + Content []byte } func init() { @@ -87,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 } @@ -128,8 +127,7 @@ func DetectWorkflows( TriggerEvent: &jobparser.Event{ Name: triggedEvent.Event(), }, - Content: content, - EventDetectionError: err, + Content: content, } workflows = append(workflows, dwf) continue 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/avatar/avatar.go b/modules/avatar/avatar.go index 5ab872f953..33af60a3b8 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -10,7 +10,6 @@ import ( "image" "image/color" "image/png" - "io" _ "image/gif" // for processing gif images _ "image/jpeg" // for processing jpeg images @@ -18,7 +17,6 @@ import ( "forgejo.org/modules/avatar/identicon" "forgejo.org/modules/setting" - exif_terminator "code.superseriousbusiness.org/exif-terminator" "golang.org/x/image/draw" _ "golang.org/x/image/webp" // for processing webp images @@ -68,29 +66,15 @@ func processAvatarImage(data []byte, maxOriginSize int64) ([]byte, error) { return nil, fmt.Errorf("image height is too large: %d > %d", imgCfg.Height, setting.Avatar.MaxHeight) } - var cleanedBytes []byte - if imgType != "gif" { // "gif" is the only imgType supported above, but not supported by exif_terminator - cleanedData, err := exif_terminator.Terminate(bytes.NewReader(data), imgType) - if err != nil { - return nil, fmt.Errorf("error cleaning exif data: %w", err) - } - cleanedBytes, err = io.ReadAll(cleanedData) - if err != nil { - return nil, fmt.Errorf("error reading cleaned data: %w", err) - } - } else { // gif - cleanedBytes = data - } - // If the origin is small enough, just use it, then APNG could be supported, // otherwise, if the image is processed later, APNG loses animation. // And one more thing, webp is not fully supported, for animated webp, image.DecodeConfig works but Decode fails. // So for animated webp, if the uploaded file is smaller than maxOriginSize, it will be used, if it's larger, there will be an error. if len(data) < int(maxOriginSize) { - return cleanedBytes, nil + return data, nil } - img, _, err := image.Decode(bytes.NewReader(cleanedBytes)) + img, _, err := image.Decode(bytes.NewReader(data)) if err != nil { return nil, fmt.Errorf("image.Decode: %w", err) } @@ -110,7 +94,7 @@ func processAvatarImage(data []byte, maxOriginSize int64) ([]byte, error) { // usually the png compression is not good enough, use the original image (no cropping/resizing) if the origin is smaller if len(data) <= len(resized) { - return cleanedBytes, nil + return data, nil } return resized, nil diff --git a/modules/avatar/avatar_test.go b/modules/avatar/avatar_test.go index 7a395c49cc..5c21ad5824 100644 --- a/modules/avatar/avatar_test.go +++ b/modules/avatar/avatar_test.go @@ -11,10 +11,7 @@ import ( "testing" "forgejo.org/modules/setting" - "forgejo.org/modules/test" - jpegstructure "code.superseriousbusiness.org/go-jpeg-image-structure/v2" - "github.com/dsoprea/go-exif/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -33,8 +30,8 @@ func Test_RandomImage(t *testing.T) { } func Test_ProcessAvatarPNG(t *testing.T) { - defer test.MockVariableValue(&setting.Avatar.MaxWidth, 4096)() - defer test.MockVariableValue(&setting.Avatar.MaxHeight, 4096)() + setting.Avatar.MaxWidth = 4096 + setting.Avatar.MaxHeight = 4096 data, err := os.ReadFile("testdata/avatar.png") require.NoError(t, err) @@ -44,8 +41,8 @@ func Test_ProcessAvatarPNG(t *testing.T) { } func Test_ProcessAvatarJPEG(t *testing.T) { - defer test.MockVariableValue(&setting.Avatar.MaxWidth, 4096)() - defer test.MockVariableValue(&setting.Avatar.MaxHeight, 4096)() + setting.Avatar.MaxWidth = 4096 + setting.Avatar.MaxHeight = 4096 data, err := os.ReadFile("testdata/avatar.jpeg") require.NoError(t, err) @@ -54,28 +51,17 @@ func Test_ProcessAvatarJPEG(t *testing.T) { require.NoError(t, err) } -func Test_ProcessAvatarGIF(t *testing.T) { - defer test.MockVariableValue(&setting.Avatar.MaxWidth, 4096)() - defer test.MockVariableValue(&setting.Avatar.MaxHeight, 4096)() - - data, err := os.ReadFile("testdata/avatar.gif") - require.NoError(t, err) - - _, err = processAvatarImage(data, 262144) - require.NoError(t, err) -} - func Test_ProcessAvatarInvalidData(t *testing.T) { - defer test.MockVariableValue(&setting.Avatar.MaxWidth, 5)() - defer test.MockVariableValue(&setting.Avatar.MaxHeight, 5)() + setting.Avatar.MaxWidth = 5 + setting.Avatar.MaxHeight = 5 _, err := processAvatarImage([]byte{}, 12800) assert.EqualError(t, err, "image.DecodeConfig: image: unknown format") } func Test_ProcessAvatarInvalidImageSize(t *testing.T) { - defer test.MockVariableValue(&setting.Avatar.MaxWidth, 5)() - defer test.MockVariableValue(&setting.Avatar.MaxHeight, 5)() + setting.Avatar.MaxWidth = 5 + setting.Avatar.MaxHeight = 5 data, err := os.ReadFile("testdata/avatar.png") require.NoError(t, err) @@ -85,8 +71,8 @@ func Test_ProcessAvatarInvalidImageSize(t *testing.T) { } func Test_ProcessAvatarImage(t *testing.T) { - defer test.MockVariableValue(&setting.Avatar.MaxWidth, 4096)() - defer test.MockVariableValue(&setting.Avatar.MaxHeight, 4096)() + setting.Avatar.MaxWidth = 4096 + setting.Avatar.MaxHeight = 4096 scaledSize := DefaultAvatarSize * setting.Avatar.RenderedSizeFactor newImgData := func(size int, optHeight ...int) []byte { @@ -149,40 +135,3 @@ func Test_ProcessAvatarImage(t *testing.T) { _, err = processAvatarImage(origin, 262144) require.ErrorContains(t, err, "image width is too large: 10 > 5") } - -func safeExifJpeg(t *testing.T, jpeg []byte) { - t.Helper() - - parser := jpegstructure.NewJpegMediaParser() - mediaContext, err := parser.ParseBytes(jpeg) - require.NoError(t, err) - - sl := mediaContext.(*jpegstructure.SegmentList) - - rootIfd, _, err := sl.Exif() - require.NoError(t, err) - err = rootIfd.EnumerateTagsRecursively(func(ifd *exif.Ifd, ite *exif.IfdTagEntry) error { - assert.Equal(t, "Orientation", ite.TagName(), "only Orientation EXIF tag expected") - return nil - }) - require.NoError(t, err) -} - -func Test_ProcessAvatarExif(t *testing.T) { - t.Run("greater than max origin size", func(t *testing.T) { - data, err := os.ReadFile("testdata/exif.jpg") - require.NoError(t, err) - - processedData, err := processAvatarImage(data, 12800) - require.NoError(t, err) - safeExifJpeg(t, processedData) - }) - t.Run("smaller than max origin size", func(t *testing.T) { - data, err := os.ReadFile("testdata/exif.jpg") - require.NoError(t, err) - - processedData, err := processAvatarImage(data, 128000) - require.NoError(t, err) - safeExifJpeg(t, processedData) - }) -} diff --git a/modules/avatar/testdata/avatar.gif b/modules/avatar/testdata/avatar.gif deleted file mode 100644 index a70fbacd25..0000000000 Binary files a/modules/avatar/testdata/avatar.gif and /dev/null differ diff --git a/modules/avatar/testdata/exif.jpg b/modules/avatar/testdata/exif.jpg deleted file mode 100644 index 7e71d9b8f4..0000000000 Binary files a/modules/avatar/testdata/exif.jpg and /dev/null differ 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 6535d1e4bd..70f837bc66 100644 --- a/modules/container/set.go +++ b/modules/container/set.go @@ -74,27 +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) -} - -// Computes the elements that are in this set, that aren't in the other set. -func (s Set[T]) Difference(other Set[T]) Set[T] { - result := make(Set[T]) - for key := range s { - if !other.Contains(key) { - result.Add(key) - } - } - return result -} - -func (s Set[T]) Slice() []T { - retval := make([]T, 0, len(s)) - for key := range s { - retval = append(retval, key) - } - return retval -} 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_reader.go b/modules/git/commit_reader.go index 37b0c8c606..ec8989f5a7 100644 --- a/modules/git/commit_reader.go +++ b/modules/git/commit_reader.go @@ -24,7 +24,6 @@ func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader) payloadSB := new(strings.Builder) signatureSB := new(strings.Builder) messageSB := new(strings.Builder) - firstLine := true message := false pgpsig := false @@ -84,25 +83,21 @@ readLoop: commit.Committer = &Signature{} commit.Committer.Decode(data) _, _ = payloadSB.Write(line) + case "encoding": + _, _ = payloadSB.Write(line) + case "change-id": // jj-vcs specific header. + _, _ = payloadSB.Write(line) case "gpgsig": fallthrough case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present. _, _ = signatureSB.Write(data) _ = signatureSB.WriteByte('\n') pgpsig = true - default: - // If the first line is not any of the known headers, then it is probably the prefix added when git cat-file is called with --batch, and that is not part of the payload - if !firstLine { - // Every subsequent header field is added to the payload - _, _ = payloadSB.Write(line) - } } } else { _, _ = messageSB.Write(line) _, _ = payloadSB.Write(line) } - - firstLine = false } commit.CommitMessage = messageSB.String() commit.Signature = &ObjectSignature{ diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index cd6b17d0d7..484827149c 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -239,77 +239,6 @@ January where the year starts on a Monday :)`, commitFromReader.Signature.Payloa assert.Equal(t, "Nicole Patricia Mazzuca ", commitFromReader.Author.String()) } -func TestGitbutlerCustomHeaderFields(t *testing.T) { - // example from: https://github.com/go-gitea/gitea/issues/34529#issuecomment-2908481092 - commitString := `tree a29321bf9e3ec433ed9e47b1cbbac6906c71fc60 -parent c0d83043ade7fa3ca10659608799477e9daa670b -author Sebastian Thiel 1747920681 +0200 -committer Sebastian Thiel 1748010747 +0200 -gitbutler-headers-version 2 -gitbutler-change-id 1063f7ea-d841-43b3-903a-01747681c40d -gpgsig -----BEGIN PGP SIGNATURE----- - - iQIzBAABCAAdFiEE6vnM/NCHZAjyl8YKnLXueJXoJosFAmgwhvsACgkQnLXueJXo - JovXUxAAq0WKJILCUAxyhwh5tRdxJTB2NjiCLf+ggLfjyrWPtMWPi/YUt7iGPB2H - Wbv9U7l5t+54fPX8TQtBKZ79YaDMfYdjlfDSijmPruf8/MXB4G0rAaIajtCr0usZ - kJDOgmmYS7bVMybDe6guwFZappiuSS2dCEYgeJun+q7Y6IYsfvdAluJmGubQIkPT - rrEffqoQz3URmDYnAKW3sTRUVwCkYIJDxpl/W0Rvc0jmELdkHu7JYX7XvZBYSUDq - FWgzCPjyErtkKk8AqoeWtTCpL+9ozzNIXNRKjGCOL2LG4H/uuNFdM46HB+KW/7+B - wMGcpZk8T/zN9Cf348M+y+o09QX1OWavDS6LgvWJaDtG/swgxV96KKR5lEtdd1IU - JHuXfPUfGp4r378FIrbPK+Thu5bn9Yq8qGvdZOpTqDxHPU9/o9wLpJghcWJZ5O3X - MpK4HdN+bME2zgBd08QsOjANogbJIz9MVaMGRFlCO5iOiz2DxG+v2KkO8IRwGXaO - OKKQ7BD04fS2wFma862BaTtB9M9f9UTWV4e2mgRpSDJWTQKrj+HkJ63gAFQYFnfp - ppgqZLkmzH1Ta2U56JSMMfOoKVFgjuxRx1d+tzdC+TpQyo06NI1KkNMepK1rhFBW - p8hej6n/7Bl9LL/W+DKsNqW9jQbTYu66JqKs3Kg7xga6w/ss0iw= - =VG6I - -----END PGP SIGNATURE----- - -asdf -asdf -asdf -` - - sha := &Sha1Hash{0xe6, 0x69, 0x11, 0x91, 0x44, 0x14, 0xb0, 0xda, 0xa8, 0x5d, 0x4a, 0x42, 0x8c, 0x8d, 0x60, 0x7b, 0x9b, 0x24, 0x9a, 0x2c} - gitRepo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare")) - require.NoError(t, err) - assert.NotNil(t, gitRepo) - defer gitRepo.Close() - - commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString)) - require.NoError(t, err) - require.NotNil(t, commitFromReader) - assert.EqualValues(t, sha, commitFromReader.ID) - assert.Equal(t, `-----BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEE6vnM/NCHZAjyl8YKnLXueJXoJosFAmgwhvsACgkQnLXueJXo -JovXUxAAq0WKJILCUAxyhwh5tRdxJTB2NjiCLf+ggLfjyrWPtMWPi/YUt7iGPB2H -Wbv9U7l5t+54fPX8TQtBKZ79YaDMfYdjlfDSijmPruf8/MXB4G0rAaIajtCr0usZ -kJDOgmmYS7bVMybDe6guwFZappiuSS2dCEYgeJun+q7Y6IYsfvdAluJmGubQIkPT -rrEffqoQz3URmDYnAKW3sTRUVwCkYIJDxpl/W0Rvc0jmELdkHu7JYX7XvZBYSUDq -FWgzCPjyErtkKk8AqoeWtTCpL+9ozzNIXNRKjGCOL2LG4H/uuNFdM46HB+KW/7+B -wMGcpZk8T/zN9Cf348M+y+o09QX1OWavDS6LgvWJaDtG/swgxV96KKR5lEtdd1IU -JHuXfPUfGp4r378FIrbPK+Thu5bn9Yq8qGvdZOpTqDxHPU9/o9wLpJghcWJZ5O3X -MpK4HdN+bME2zgBd08QsOjANogbJIz9MVaMGRFlCO5iOiz2DxG+v2KkO8IRwGXaO -OKKQ7BD04fS2wFma862BaTtB9M9f9UTWV4e2mgRpSDJWTQKrj+HkJ63gAFQYFnfp -ppgqZLkmzH1Ta2U56JSMMfOoKVFgjuxRx1d+tzdC+TpQyo06NI1KkNMepK1rhFBW -p8hej6n/7Bl9LL/W+DKsNqW9jQbTYu66JqKs3Kg7xga6w/ss0iw= -=VG6I ------END PGP SIGNATURE----- -`, commitFromReader.Signature.Signature) - assert.Equal(t, `tree a29321bf9e3ec433ed9e47b1cbbac6906c71fc60 -parent c0d83043ade7fa3ca10659608799477e9daa670b -author Sebastian Thiel 1747920681 +0200 -committer Sebastian Thiel 1748010747 +0200 -gitbutler-headers-version 2 -gitbutler-change-id 1063f7ea-d841-43b3-903a-01747681c40d - -asdf -asdf -asdf -`, commitFromReader.Signature.Payload) - assert.Equal(t, "Sebastian Thiel ", commitFromReader.Author.String()) -} - func TestHasPreviousCommit(t *testing.T) { bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") @@ -507,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/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_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 98a1930ac2..94f1911c4a 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -30,12 +30,6 @@ type CompareInfo struct { NumFiles int } -// GetMergeBaseSimple returns the merge base of base and head. -func (repo *Repository) GetMergeBaseSimple(base, head string) (string, error) { - stdout, _, err := NewCommand(repo.Ctx, "merge-base").AddDashesAndList(base, head).RunStdString(&RunOpts{Dir: repo.Path}) - return strings.TrimSpace(stdout), err -} - // GetMergeBase checks and returns merge base of two branches and the reference used as base. func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, string, error) { if tmpRemote == "" { @@ -180,36 +174,16 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis return w.numLines, nil } -var ( - ErrNoMergebaseFound = errors.New("no merge base found") - ErrMultipleMergebasesFound = errors.New("multiple merge bases found") -) - -// GetShortStat returns the number of changed files, additions and deletions. If -// `useMergebase` is specified then the merge base between `base` and `head` is -// used to compare against `head`. -func (repo *Repository) GetShortStat(base, head string, useMergebase bool) (numFiles, totalAdditions, totalDeletions int, err error) { - cmd := NewCommand(repo.Ctx, "diff-tree", "--shortstat") - if useMergebase { - cmd = cmd.AddArguments("--merge-base") +// GetDiffShortStat counts number of changed files, number of additions and deletions +func (repo *Repository) GetDiffShortStat(base, head string) (numFiles, totalAdditions, totalDeletions int, err error) { + numFiles, totalAdditions, totalDeletions, err = GetDiffShortStat(repo.Ctx, repo.Path, nil, base+"..."+head) + if err != nil && strings.Contains(err.Error(), "no merge base") { + return GetDiffShortStat(repo.Ctx, repo.Path, nil, base, head) } - cmd.AddDynamicArguments(base, head) - - stdout, stderr, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) - if err != nil { - switch stderr { - case "fatal: no merge base found\n": - return 0, 0, 0, ErrNoMergebaseFound - case "fatal: multiple merge bases found\n": - return 0, 0, 0, ErrMultipleMergebasesFound - } - return 0, 0, 0, err - } - - return parseDiffStat(stdout) + return numFiles, totalAdditions, totalDeletions, err } -// GetCommitShortStat returns the number of files, total additions and total deletions the commit has. +// 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}) @@ -220,13 +194,13 @@ func (repo *Repository) GetCommitShortStat(commitID string) (numFiles, totalAddi return parseDiffStat(stdout) } -// GetIndexShortStat returns the number of files, total additions and total -// deletions the commit has. -// -// NOTE: It uses `git-diff-index`, should only be used when working with -// temporary repository. When working on bare repositories use `GetCommitShortStat`. -func GetIndexShortStat(ctx context.Context, repoPath, commitID string) (numFiles, totalAdditions, totalDeletions int, err error) { - cmd := NewCommand(ctx, "diff-index", "--cached", "--shortstat").AddDynamicArguments(commitID) +// 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: + // $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875 + // we get: + // " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n" + cmd := NewCommand(ctx, "diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...) stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) if err != nil { return 0, 0, 0, err diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go index fcdc256112..b1ebdf6177 100644 --- a/modules/git/repo_compare_test.go +++ b/modules/git/repo_compare_test.go @@ -243,87 +243,3 @@ func TestGetCommitShortStat(t *testing.T) { assert.Equal(t, 0, totalDeletions) }) } - -func TestGetShortStat(t *testing.T) { - // https://github.com/git/git/blob/60f3f52f17cceefa5299709b189ce6fe2d181e7b/t/t4068-diff-symmetric-merge-base.sh#L10-L23 - repo, err := OpenRepository(t.Context(), filepath.Join(testReposDir, "symmetric_repo")) - require.NoError(t, err) - defer repo.Close() - - t.Run("Normal", func(t *testing.T) { - t.Run("Via merge base", func(t *testing.T) { - numFiles, totalAdditions, totalDeletions, err := repo.GetShortStat("br2", "main", true) - require.NoError(t, err) - assert.Equal(t, 1, numFiles) - assert.Equal(t, 1, totalAdditions) - assert.Zero(t, totalDeletions) - - numFiles, totalAdditions, totalDeletions, err = repo.GetShortStat("main", "br2", true) - require.NoError(t, err) - assert.Equal(t, 1, numFiles) - assert.Equal(t, 1, totalAdditions) - assert.Zero(t, totalDeletions) - }) - - t.Run("Direct compare", func(t *testing.T) { - numFiles, totalAdditions, totalDeletions, err := repo.GetShortStat("main", "br2", false) - require.NoError(t, err) - assert.Equal(t, 2, numFiles) - assert.Equal(t, 1, totalAdditions) - assert.Equal(t, 1, totalDeletions) - - numFiles, totalAdditions, totalDeletions, err = repo.GetShortStat("main", "br3", false) - require.NoError(t, err) - assert.Equal(t, 1, numFiles) - assert.Equal(t, 1, totalAdditions) - assert.Zero(t, totalDeletions) - - numFiles, totalAdditions, totalDeletions, err = repo.GetShortStat("br3", "main", false) - require.NoError(t, err) - assert.Equal(t, 1, numFiles) - assert.Zero(t, totalAdditions) - assert.Equal(t, 1, totalDeletions) - }) - }) - - t.Run("No merge base", func(t *testing.T) { - numFiles, totalAdditions, totalDeletions, err := repo.GetShortStat("main", "br3", true) - require.ErrorIs(t, err, ErrNoMergebaseFound) - assert.Zero(t, numFiles) - assert.Zero(t, totalAdditions) - assert.Zero(t, totalDeletions) - }) - - t.Run("Multiple merge base", func(t *testing.T) { - numFiles, totalAdditions, totalDeletions, err := repo.GetShortStat("main", "br1", true) - require.ErrorIs(t, err, ErrMultipleMergebasesFound) - assert.Zero(t, numFiles) - assert.Zero(t, totalAdditions) - assert.Zero(t, totalDeletions) - }) -} - -func TestGetMergeBaseSimple(t *testing.T) { - repo, err := OpenRepository(t.Context(), filepath.Join(testReposDir, "symmetric_repo")) - require.NoError(t, err) - - defer repo.Close() - - t.Run("Normal", func(t *testing.T) { - mergebase, err := repo.GetMergeBaseSimple("main", "br2") - require.NoError(t, err) - assert.Equal(t, "9d36f18c8ca14ad28c4751afd14f3e3146a785dc", mergebase) - }) - - t.Run("No mergebase", func(t *testing.T) { - mergebase, err := repo.GetMergeBaseSimple("main", "br3") - require.ErrorContains(t, err, "exit status 1") - assert.Empty(t, mergebase) - }) - - t.Run("Multiple mergebase", func(t *testing.T) { - mergebase, err := repo.GetMergeBaseSimple("main", "br1") - require.NoError(t, err) - assert.Equal(t, "9d36f18c8ca14ad28c4751afd14f3e3146a785dc", mergebase) - }) -} 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/symmetric_repo/HEAD b/modules/git/tests/repos/symmetric_repo/HEAD deleted file mode 100644 index 992854f9fb..0000000000 --- a/modules/git/tests/repos/symmetric_repo/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/br3 diff --git a/modules/git/tests/repos/symmetric_repo/config b/modules/git/tests/repos/symmetric_repo/config deleted file mode 100644 index 07d359d07c..0000000000 --- a/modules/git/tests/repos/symmetric_repo/config +++ /dev/null @@ -1,4 +0,0 @@ -[core] - repositoryformatversion = 0 - filemode = true - bare = true diff --git a/modules/git/tests/repos/symmetric_repo/objects/01/fc2bb01605691ba1f40694c1da70501583a085 b/modules/git/tests/repos/symmetric_repo/objects/01/fc2bb01605691ba1f40694c1da70501583a085 deleted file mode 100644 index 914d958d71..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/01/fc2bb01605691ba1f40694c1da70501583a085 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/02/84078471ec566623807297baf4450382542dcb b/modules/git/tests/repos/symmetric_repo/objects/02/84078471ec566623807297baf4450382542dcb deleted file mode 100644 index 061fc8feec..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/02/84078471ec566623807297baf4450382542dcb and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/19/33da329284aca10dab8dc2fdd54213acd39be5 b/modules/git/tests/repos/symmetric_repo/objects/19/33da329284aca10dab8dc2fdd54213acd39be5 deleted file mode 100644 index 7abecb8b4e..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/19/33da329284aca10dab8dc2fdd54213acd39be5 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/3f/10b1d474ee0a63d008be715494586ccbbbc9da b/modules/git/tests/repos/symmetric_repo/objects/3f/10b1d474ee0a63d008be715494586ccbbbc9da deleted file mode 100644 index b54a2a79d7..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/3f/10b1d474ee0a63d008be715494586ccbbbc9da and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/3f/151445f4d22fec48009aa692142c96bce6dcf6 b/modules/git/tests/repos/symmetric_repo/objects/3f/151445f4d22fec48009aa692142c96bce6dcf6 deleted file mode 100644 index 82c5d29824..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/3f/151445f4d22fec48009aa692142c96bce6dcf6 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/modules/git/tests/repos/symmetric_repo/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 deleted file mode 100644 index adf64119a3..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/54/93447336137e6fc28e3be3067f7049273c6909 b/modules/git/tests/repos/symmetric_repo/objects/54/93447336137e6fc28e3be3067f7049273c6909 deleted file mode 100644 index 30092e4f7b..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/54/93447336137e6fc28e3be3067f7049273c6909 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/61/780798228d17af2d34fce4cfbdf35556832472 b/modules/git/tests/repos/symmetric_repo/objects/61/780798228d17af2d34fce4cfbdf35556832472 deleted file mode 100644 index 586bf17a49..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/61/780798228d17af2d34fce4cfbdf35556832472 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/6a/69f92020f5df77af6e8813ff1232493383b708 b/modules/git/tests/repos/symmetric_repo/objects/6a/69f92020f5df77af6e8813ff1232493383b708 deleted file mode 100644 index 77148f56f4..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/6a/69f92020f5df77af6e8813ff1232493383b708 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/6b/e660545b31f61a82a87d2b1915f0b88bb9f16f b/modules/git/tests/repos/symmetric_repo/objects/6b/e660545b31f61a82a87d2b1915f0b88bb9f16f deleted file mode 100644 index c8bc9c3d9e..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/6b/e660545b31f61a82a87d2b1915f0b88bb9f16f and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/71/3fc38bac217f3909a1ca43479dec4f3f6e039e b/modules/git/tests/repos/symmetric_repo/objects/71/3fc38bac217f3909a1ca43479dec4f3f6e039e deleted file mode 100644 index fde71ec26c..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/71/3fc38bac217f3909a1ca43479dec4f3f6e039e and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/83/966f2fad7dfb1c567ca63bb68e9a7d38b4afc6 b/modules/git/tests/repos/symmetric_repo/objects/83/966f2fad7dfb1c567ca63bb68e9a7d38b4afc6 deleted file mode 100644 index 50d57fceec..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/83/966f2fad7dfb1c567ca63bb68e9a7d38b4afc6 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/9d/36f18c8ca14ad28c4751afd14f3e3146a785dc b/modules/git/tests/repos/symmetric_repo/objects/9d/36f18c8ca14ad28c4751afd14f3e3146a785dc deleted file mode 100644 index d1ecb2995f..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/9d/36f18c8ca14ad28c4751afd14f3e3146a785dc and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/bf/91e6a4737da117217f8b32276998b10ee55f65 b/modules/git/tests/repos/symmetric_repo/objects/bf/91e6a4737da117217f8b32276998b10ee55f65 deleted file mode 100644 index ad3f6a1f9b..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/bf/91e6a4737da117217f8b32276998b10ee55f65 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/d6/dfac318c3dcd5f05554472666d7bb6dfd9e8db b/modules/git/tests/repos/symmetric_repo/objects/d6/dfac318c3dcd5f05554472666d7bb6dfd9e8db deleted file mode 100644 index 75ea7eaed6..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/d6/dfac318c3dcd5f05554472666d7bb6dfd9e8db and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/e4/2ba3e77f66f623836b47df796932f7e5604aec b/modules/git/tests/repos/symmetric_repo/objects/e4/2ba3e77f66f623836b47df796932f7e5604aec deleted file mode 100644 index dc832f465c..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/e4/2ba3e77f66f623836b47df796932f7e5604aec and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 b/modules/git/tests/repos/symmetric_repo/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 deleted file mode 100644 index a36463115d..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/objects/f4/e316302084c597f1678ff1db625b28f68194ae b/modules/git/tests/repos/symmetric_repo/objects/f4/e316302084c597f1678ff1db625b28f68194ae deleted file mode 100644 index 2133e56d5c..0000000000 Binary files a/modules/git/tests/repos/symmetric_repo/objects/f4/e316302084c597f1678ff1db625b28f68194ae and /dev/null differ diff --git a/modules/git/tests/repos/symmetric_repo/packed-refs b/modules/git/tests/repos/symmetric_repo/packed-refs deleted file mode 100644 index 0e61c448af..0000000000 --- a/modules/git/tests/repos/symmetric_repo/packed-refs +++ /dev/null @@ -1,8 +0,0 @@ -# pack-refs with: peeled fully-peeled sorted -01fc2bb01605691ba1f40694c1da70501583a085 refs/heads/br1 -5493447336137e6fc28e3be3067f7049273c6909 refs/heads/br2 -3f10b1d474ee0a63d008be715494586ccbbbc9da refs/heads/br3 -83966f2fad7dfb1c567ca63bb68e9a7d38b4afc6 refs/heads/main -bf91e6a4737da117217f8b32276998b10ee55f65 refs/tags/commit-C -^9d36f18c8ca14ad28c4751afd14f3e3146a785dc -0284078471ec566623807297baf4450382542dcb refs/tags/commit-D diff --git a/modules/git/tests/repos/symmetric_repo/refs/heads/.gitkeep b/modules/git/tests/repos/symmetric_repo/refs/heads/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/modules/git/tests/repos/symmetric_repo/refs/tags/.gitkeep b/modules/git/tests/repos/symmetric_repo/refs/tags/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 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/heads/.gitkeep b/modules/git/tests/repos/templates_repo/refs/heads/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 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/git/url/url_test.go b/modules/git/url/url_test.go index 68ad8bd7c5..54655633bf 100644 --- a/modules/git/url/url_test.go +++ b/modules/git/url/url_test.go @@ -29,12 +29,12 @@ func TestParseGitURLs(t *testing.T) { }, }, { - kase: "git@[fe80::14fc:cec5:c174:d88%2510]:go-gitea/gitea.git", + kase: "git@[fe80:14fc:cec5:c174:d88%2510]:go-gitea/gitea.git", expected: &GitURL{ URL: &url.URL{ Scheme: "ssh", User: url.User("git"), - Host: "[fe80::14fc:cec5:c174:d88%10]", + Host: "[fe80:14fc:cec5:c174:d88%10]", Path: "go-gitea/gitea.git", }, extraMark: 1, @@ -132,11 +132,11 @@ func TestParseGitURLs(t *testing.T) { }, }, { - kase: "https://[fe80::14fc:cec5:c174:d88%2510]:20/go-gitea/gitea.git", + kase: "https://[fe80:14fc:cec5:c174:d88%2510]:20/go-gitea/gitea.git", expected: &GitURL{ URL: &url.URL{ Scheme: "https", - Host: "[fe80::14fc:cec5:c174:d88%10]:20", + Host: "[fe80:14fc:cec5:c174:d88%10]:20", Path: "/go-gitea/gitea.git", }, extraMark: 0, 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..499b9117c4 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 diff --git a/modules/indexer/code/search_test.go b/modules/indexer/code/search_test.go index 2413ddec0b..e542b38c24 100644 --- a/modules/indexer/code/search_test.go +++ b/modules/indexer/code/search_test.go @@ -86,9 +86,9 @@ func TestHighlightSearchResultCode(t *testing.T) { Range: [][3]int{{1, 14, 23}}, Code: "func main() {\n\tfmt.Println(\"mark this\")\n}", Result: []template.HTML{ - "func main() {", - "\tfmt.Println("mark this")", - "}", + "func main() {", + "\tfmt.Println("mark this")", + "}", }, }, { @@ -97,9 +97,9 @@ func TestHighlightSearchResultCode(t *testing.T) { Range: [][3]int{{1, 14, 28}}, Code: "func main() {\n\tfmt.Println(\"mark this 😊\")\n}", Result: []template.HTML{ - "func main() {", - "\tfmt.Println("mark this 😊")", - "}", + "func main() {", + "\tfmt.Println("mark this 😊")", + "}", }, }, } diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index cb98f722c5..80af7bac45 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -171,7 +171,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if issueID, err := token.ParseIssueReference(); err == nil { idQuery := inner_bleve.NumericEqualityQuery(issueID, "index") - idQuery.SetBoost(20.0) + idQuery.SetBoost(5.0) innerQ.AddQuery(idQuery) } @@ -198,15 +198,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..0c4e5fae02 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -166,7 +166,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( } var eitherQ elastic.Query = innerQ if issueID, err := token.ParseIssueReference(); err == nil { - indexQ := elastic.NewTermQuery("index", issueID).Boost(20) + indexQ := elastic.NewTermQuery("index", issueID).Boost(15.0) eitherQ = elastic.NewDisMaxQuery().Query(indexQ).Query(innerQ).TieBreaker(0.5) } switch token.Kind { @@ -189,10 +189,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/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 46014994a0..e7723ea69d 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -772,25 +772,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/main_test.go b/modules/lfs/main_test.go deleted file mode 100644 index 47a0c8138a..0000000000 --- a/modules/lfs/main_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 lfs - -import ( - "context" - "fmt" - "os" - "testing" - - "forgejo.org/modules/git" - "forgejo.org/modules/setting" - "forgejo.org/modules/util" -) - -const testReposDir = "tests/repos/" - -func TestMain(m *testing.M) { - gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home") - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "Test failed: unable to create temp dir: %v", err) - os.Exit(1) - } - - setting.Git.HomePath = gitHomePath - - if err = git.InitFull(context.Background()); err != nil { - util.RemoveAll(gitHomePath) - _, _ = fmt.Fprintf(os.Stderr, "Test failed: failed to call git.InitFull: %v", err) - os.Exit(1) - } - - exitCode := m.Run() - util.RemoveAll(gitHomePath) - os.Exit(exitCode) -} 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/lfs/pointer_scanner_test.go b/modules/lfs/pointer_scanner_test.go deleted file mode 100644 index a82c22e66c..0000000000 --- a/modules/lfs/pointer_scanner_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package lfs - -import ( - "cmp" - "path/filepath" - "slices" - "testing" - - "forgejo.org/modules/git" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestSearchPointerBlobs(t *testing.T) { - repo, err := git.OpenRepository(t.Context(), filepath.Join(testReposDir, "simple-lfs")) - require.NoError(t, err) - - pointerChan := make(chan PointerBlob) - errChan := make(chan error, 1) - - go SearchPointerBlobs(t.Context(), repo, pointerChan, errChan) - - // Collect all found pointer blobs. - var pointerBlobs []PointerBlob - for pointerBlob := range pointerChan { - pointerBlobs = append(pointerBlobs, pointerBlob) - } - - // Check that no errors were reported. - errChanClosed := false - select { - case err, ok := <-errChan: - if ok { - require.NoError(t, err) - } else { - errChanClosed = true - } - default: - } - assert.True(t, errChanClosed) - - // Sort them, they might arrive in any order - slices.SortFunc(pointerBlobs, func(a, b PointerBlob) int { - return cmp.Compare(a.Oid, b.Oid) - }) - - // Assert the values of the found pointer blobs. - if assert.Len(t, pointerBlobs, 3) { - assert.Equal(t, "31b9a6a709729b8ae48bde8176caf2990c0d7121", pointerBlobs[0].Hash) - assert.Equal(t, "2f91b6326743db344ca96b1be86e3ed34abf04262255b4d04db8a961a2a72545", pointerBlobs[0].Oid) - assert.EqualValues(t, 43789, pointerBlobs[0].Size) - - assert.Equal(t, "b53619fbbc3d2bfa85a26787238264cdbf551f19", pointerBlobs[1].Hash) - assert.Equal(t, "36dae031efb96625cda973c11508617b750665933a36bd52dfcfef586c4fd85c", pointerBlobs[1].Oid) - assert.EqualValues(t, 101800, pointerBlobs[1].Size) - - assert.Equal(t, "513d4c000f63ee9fd6a805e9a518206b860ce38a", pointerBlobs[2].Hash) - assert.Equal(t, "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393", pointerBlobs[2].Oid) - assert.EqualValues(t, 1234, pointerBlobs[2].Size) - } -} diff --git a/modules/lfs/tests/repos/simple-lfs/HEAD b/modules/lfs/tests/repos/simple-lfs/HEAD deleted file mode 100644 index b870d82622..0000000000 --- a/modules/lfs/tests/repos/simple-lfs/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/main diff --git a/modules/lfs/tests/repos/simple-lfs/config b/modules/lfs/tests/repos/simple-lfs/config deleted file mode 100644 index 07d359d07c..0000000000 --- a/modules/lfs/tests/repos/simple-lfs/config +++ /dev/null @@ -1,4 +0,0 @@ -[core] - repositoryformatversion = 0 - filemode = true - bare = true diff --git a/modules/lfs/tests/repos/simple-lfs/info/exclude b/modules/lfs/tests/repos/simple-lfs/info/exclude deleted file mode 100644 index a5196d1be8..0000000000 --- a/modules/lfs/tests/repos/simple-lfs/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/lfs/tests/repos/simple-lfs/logs/HEAD b/modules/lfs/tests/repos/simple-lfs/logs/HEAD deleted file mode 100644 index 18126e38d6..0000000000 --- a/modules/lfs/tests/repos/simple-lfs/logs/HEAD +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 ece720b78753aca2c27fd69c42f5578f5bcfbe0e Gusted 1759469736 +0200 diff --git a/modules/lfs/tests/repos/simple-lfs/objects/31/b9a6a709729b8ae48bde8176caf2990c0d7121 b/modules/lfs/tests/repos/simple-lfs/objects/31/b9a6a709729b8ae48bde8176caf2990c0d7121 deleted file mode 100644 index c95ecbafdc..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/31/b9a6a709729b8ae48bde8176caf2990c0d7121 and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/objects/33/9e3c3ddbf4cf6655ed335eb820f4f2912d8063 b/modules/lfs/tests/repos/simple-lfs/objects/33/9e3c3ddbf4cf6655ed335eb820f4f2912d8063 deleted file mode 100644 index dd7eada553..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/33/9e3c3ddbf4cf6655ed335eb820f4f2912d8063 and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/objects/51/3d4c000f63ee9fd6a805e9a518206b860ce38a b/modules/lfs/tests/repos/simple-lfs/objects/51/3d4c000f63ee9fd6a805e9a518206b860ce38a deleted file mode 100644 index 1bd67e2a35..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/51/3d4c000f63ee9fd6a805e9a518206b860ce38a and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/objects/72/78f80aa39386476ffbdd79083808887f99fbc8 b/modules/lfs/tests/repos/simple-lfs/objects/72/78f80aa39386476ffbdd79083808887f99fbc8 deleted file mode 100644 index 60d37fc752..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/72/78f80aa39386476ffbdd79083808887f99fbc8 and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/objects/8d/ebd861c03718cc43154f5317308b88ab5447d2 b/modules/lfs/tests/repos/simple-lfs/objects/8d/ebd861c03718cc43154f5317308b88ab5447d2 deleted file mode 100644 index 22ce593d43..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/8d/ebd861c03718cc43154f5317308b88ab5447d2 and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/objects/9b/af9f95266ceb6043894591f73d97c7ef4cb546 b/modules/lfs/tests/repos/simple-lfs/objects/9b/af9f95266ceb6043894591f73d97c7ef4cb546 deleted file mode 100644 index e9dd5226ce..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/9b/af9f95266ceb6043894591f73d97c7ef4cb546 and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/objects/b5/3619fbbc3d2bfa85a26787238264cdbf551f19 b/modules/lfs/tests/repos/simple-lfs/objects/b5/3619fbbc3d2bfa85a26787238264cdbf551f19 deleted file mode 100644 index 33bd13e08f..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/b5/3619fbbc3d2bfa85a26787238264cdbf551f19 and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/objects/c7/5c2ef6293177b3bcd7f23f5f49e2727fe7dbb6 b/modules/lfs/tests/repos/simple-lfs/objects/c7/5c2ef6293177b3bcd7f23f5f49e2727fe7dbb6 deleted file mode 100644 index e0c154b400..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/c7/5c2ef6293177b3bcd7f23f5f49e2727fe7dbb6 and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/objects/eb/e522c4e7b48a1d939281654589366a63120444 b/modules/lfs/tests/repos/simple-lfs/objects/eb/e522c4e7b48a1d939281654589366a63120444 deleted file mode 100644 index fa39fd57c1..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/eb/e522c4e7b48a1d939281654589366a63120444 and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/objects/ec/e720b78753aca2c27fd69c42f5578f5bcfbe0e b/modules/lfs/tests/repos/simple-lfs/objects/ec/e720b78753aca2c27fd69c42f5578f5bcfbe0e deleted file mode 100644 index e23129ab0d..0000000000 Binary files a/modules/lfs/tests/repos/simple-lfs/objects/ec/e720b78753aca2c27fd69c42f5578f5bcfbe0e and /dev/null differ diff --git a/modules/lfs/tests/repos/simple-lfs/packed-refs b/modules/lfs/tests/repos/simple-lfs/packed-refs deleted file mode 100644 index 08e7d087a5..0000000000 --- a/modules/lfs/tests/repos/simple-lfs/packed-refs +++ /dev/null @@ -1,2 +0,0 @@ -# pack-refs with: peeled fully-peeled sorted -ece720b78753aca2c27fd69c42f5578f5bcfbe0e refs/heads/main diff --git a/modules/lfs/tests/repos/simple-lfs/refs/heads/.gitkeep b/modules/lfs/tests/repos/simple-lfs/refs/heads/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/modules/lfs/tests/repos/simple-lfs/refs/tags/.gitkeep b/modules/lfs/tests/repos/simple-lfs/refs/tags/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 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 2fe0caa5b6..7961c5c930 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -55,13 +55,10 @@ var ( shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`) // anyHashPattern splits url containing SHA into parts - anyHashPattern = regexp.MustCompile(`https?://[^\s/]+/(\S+/(?:commit|tree|blob))/([0-9a-f]{7,64})(/[-+~_%.a-zA-Z0-9/]+)?(\?[-+~_%\.a-zA-Z0-9=&]+)?(#[-+~_%.a-zA-Z0-9]+)?`) + anyHashPattern = regexp.MustCompile(`https?://(?:(?:\S+/){3,4}(?:commit|tree|blob)/)([0-9a-f]{7,64})(/[-+~_%.a-zA-Z0-9/]+)?(\?[-+~_%\.a-zA-Z0-9=&]+)?(#[-+~_%.a-zA-Z0-9]+)?`) // comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash" - comparePattern = regexp.MustCompile(`https?://[^\s/]+/(?:\S+/)?([^\s/]+/[^\s/]+)/compare/([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(\?[-+~_%\.a-zA-Z0-9=&/]+)?(#[-+~_%.a-zA-Z0-9]+)?`) - - // pullReviewCommitPattern matches "https://domain.tld////pulls//commits/" - pullReviewCommitPattern = regexp.MustCompile(`https?://[^\s/]+/(?:\S+/)?([^\s/]+/[^\s/]+)/pulls/(\d+)/commits/([0-9a-f]{7,64})(#[-+~_%.a-zA-Z0-9]+)?`) + comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`) validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`) @@ -150,7 +147,6 @@ func (p *postProcessError) Error() string { type processor func(ctx *RenderContext, node *html.Node) var defaultProcessors = []processor{ - pullReviewCommitPatternProcessor, fullIssuePatternProcessor, comparePatternProcessor, filePreviewPatternProcessor, @@ -181,7 +177,6 @@ func PostProcess( } var commitMessageProcessors = []processor{ - pullReviewCommitPatternProcessor, fullIssuePatternProcessor, comparePatternProcessor, fullHashPatternProcessor, @@ -214,7 +209,6 @@ func RenderCommitMessage( } var commitMessageSubjectProcessors = []processor{ - pullReviewCommitPatternProcessor, fullIssuePatternProcessor, comparePatternProcessor, fullHashPatternProcessor, @@ -563,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{ @@ -802,64 +792,6 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { } } -// pullReviewCommitPatternProcessor creates links to pull review commits. -func pullReviewCommitPatternProcessor(ctx *RenderContext, node *html.Node) { - next := node.NextSibling - for node != nil && node != next { - m := pullReviewCommitPattern.FindStringSubmatchIndex(node.Data) - if m == nil { - return - } - - urlFull := node.Data[m[0]:m[1]] - repoSlug := node.Data[m[2]:m[3]] - id := node.Data[m[4]:m[5]] - sha := base.ShortSha(node.Data[m[6]:m[7]]) - - // Create an `` node with a text of - // `!123 (commit abcdef1234)` - aNode := &html.Node{ - Type: html.ElementNode, - Data: atom.A.String(), - Attr: []html.Attribute{{Key: "href", Val: urlFull}, {Key: "class", Val: "commit"}}, - } - - text := "!" + id + " (commit " - - baseURLEnd := strings.Index(urlFull, repoSlug) + len(repoSlug) - if len(ctx.Links.Base) > 0 && !strings.HasPrefix(ctx.Links.Base, urlFull[:baseURLEnd]) { - text = repoSlug + "@" + text - } - - aNode.AppendChild(&html.Node{ - Type: html.TextNode, - Data: text, - }) - - textNode := &html.Node{ - Type: html.TextNode, - Data: sha, - } - - codeNode := &html.Node{ - Type: html.ElementNode, - Data: atom.Code.String(), - Attr: []html.Attribute{{Key: "class", Val: "nohighlight"}}, - } - - codeNode.AppendChild(textNode) - aNode.AppendChild(codeNode) - - aNode.AppendChild(&html.Node{ - Type: html.TextNode, - Data: ")", - }) - - replaceContent(node, m[0], m[1], aNode) - node = node.NextSibling.NextSibling - } -} - func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return @@ -1016,7 +948,7 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) { } } -// fullHashPatternProcessor renders URLs that contain a SHA +// fullHashPatternProcessor renders SHA containing URLs func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return @@ -1030,103 +962,37 @@ func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) { } urlFull := node.Data[m[0]:m[1]] + text := base.ShortSha(node.Data[m[2]:m[3]]) - // In most cases, the URL will look like this: - // `https://domain.tld////`. - // The amount of components in `` is variable, but that alone is doable with regexp. - // - // However, Forgejo also allows being hosted on a sub path, i.e. - // `https://domain.tld/////`. - // And this sub path can also have any amount of components. But fishing out a section - // between two variable length matches is not something regular grammars are capable of. - // - // Instead, the regexp extracts the entire path section before the SHA - // (i.e. `///`), and we find the components we need by counting. - // `` is unknown, but the possible values for `` are defined by us - // (see `router/web/web.go`). So we count from the back. - subPath := node.Data[m[2]:m[3]] - - components := strings.Split(subPath, "/") - componentCount := len(components) - - // In most cases, the `` component is right at the start of the path. - ownerIndex := 0 - - // But if there are more than three components, this could be `` or an app route - // with two components. Or both. - if componentCount > 3 { - // As mentioned, we count from the back. We decrement for the `` component, and the one - // component from the app route that's guaranteed to be there. - // We also adjust this to be an array index, so we subtract one more. - ownerIndex = componentCount - 3 - - // We then check for known app routes that use two components. - // Currently, this checks for: - // - `src/commit` - // - `commits/commit` - // - // This does have one scenario where we cannot figure things out reliably: - // If there is a sub path, and the repository is named like one of the known app routes - // (e.g. `src`), we cannot distinguish between the repo and the app route. - // We assume that naming a repository like that is uncommon, and prioritize the case where its - // part of the app route. - if components[componentCount-1] == "commit" && - (components[componentCount-2] == "src" || components[componentCount-2] == "commits") { - ownerIndex-- - } - } - - repoSlug := components[ownerIndex] + "/" + components[ownerIndex+1] - - text := base.ShortSha(node.Data[m[4]:m[5]]) - - // We need to figure out the base of the provided URL, which is up to and including the - // `/` slug. - // With that we can determine if it matches the current repo, or if the slug should be shown. - baseURLEnd := strings.Index(urlFull, repoSlug) + len(repoSlug) - if len(ctx.Links.Base) > 0 && !strings.HasPrefix(ctx.Links.Base, urlFull[:baseURLEnd]) { - text = repoSlug + "@" + text - } - - // 3rd capture group matches an optional file path after the SHA - filePath := "" - if m[7] > 0 { - filePath = node.Data[m[6]:m[7]] + // 3rd capture group matches a optional path + subpath := "" + if m[5] > 0 { + subpath = node.Data[m[4]:m[5]] } // 5th capture group matches a optional url hash hash := "" - if m[11] > 0 { - hash = node.Data[m[10]:m[11]][1:] - - // Truncate long diff IDs - if len(hash) > 15 && strings.HasPrefix(hash, "diff-") { - hash = hash[:15] - } + if m[9] > 0 { + hash = node.Data[m[8]:m[9]][1:] } start := m[0] end := m[1] - // If the URL ends in '.', it's very likely that it is not part of the - // actual URL but used to finish a sentence. + // If url ends in '.', it's very likely that it is not part of the + // actual url but used to finish a sentence. if strings.HasSuffix(urlFull, ".") { end-- urlFull = urlFull[:len(urlFull)-1] if hash != "" { hash = hash[:len(hash)-1] - } else if filePath != "" { - filePath = filePath[:len(filePath)-1] + } else if subpath != "" { + subpath = subpath[:len(subpath)-1] } } - if filePath != "" { - decoded, err := url.QueryUnescape(filePath) - if err != nil { - text += decoded - } else { - text += filePath - } + if subpath != "" { + text += subpath } if hash != "" { @@ -1149,71 +1015,41 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) { return } - // Ensure that every group (m[0]...m[9]) has a match - for i := 0; i < 10; i++ { + // Ensure that every group (m[0]...m[7]) has a match + for i := 0; i < 8; i++ { if m[i] == -1 { return } } urlFull := node.Data[m[0]:m[1]] - repoSlug := node.Data[m[2]:m[3]] - text1 := base.ShortSha(node.Data[m[4]:m[5]]) - textDots := base.ShortSha(node.Data[m[6]:m[7]]) - text2 := base.ShortSha(node.Data[m[8]:m[9]]) - - query := "" - if m[11] > 0 { - query = node.Data[m[10]:m[11]][1:] - } + text1 := base.ShortSha(node.Data[m[2]:m[3]]) + textDots := base.ShortSha(node.Data[m[4]:m[5]]) + text2 := base.ShortSha(node.Data[m[6]:m[7]]) hash := "" - if m[13] > 0 { - hash = node.Data[m[12]:m[13]][1:] + if m[9] > 0 { + hash = node.Data[m[8]:m[9]][1:] } start := m[0] end := m[1] - // If the URL ends in '.', it's very likely that it is not part of the - // actual URL but used to finish a sentence. + // If url ends in '.', it's very likely that it is not part of the + // actual url but used to finish a sentence. if strings.HasSuffix(urlFull, ".") { end-- urlFull = urlFull[:len(urlFull)-1] if hash != "" { hash = hash[:len(hash)-1] - } else if query != "" { - query = query[:len(query)-1] } else if text2 != "" { text2 = text2[:len(text2)-1] } } text := text1 + textDots + text2 - - baseURLEnd := strings.Index(urlFull, repoSlug) + len(repoSlug) - if len(ctx.Links.Base) > 0 && !strings.HasPrefix(ctx.Links.Base, urlFull[:baseURLEnd]) { - text = repoSlug + "@" + text - } - - extra := "" - if query != "" { - query, err := url.ParseQuery(query) - if err == nil && query.Has("files") { - extra = query.Get("files") - } - } - if hash != "" { - if extra != "" { - extra += "#" - } - - extra += hash - } - - if extra != "" { - text += " (" + extra + ")" + text += " (" + hash + ")" } replaceContent(node, start, end, createCodeLink(urlFull, text, "compare")) node = node.NextSibling.NextSibling @@ -1238,15 +1074,12 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { next := node.NextSibling for node != nil && node != next { - if node.Parent == nil || node.Parent.Type != html.ElementNode { - node = node.NextSibling - continue - } previews := NewFilePreviews(ctx, node, locale) if previews == nil { node = node.NextSibling continue } + offset := 0 for _, preview := range previews { previewNode := preview.CreateHTML(locale) @@ -1254,39 +1087,23 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { // Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div before := node.Data[:(preview.start - offset)] after := node.Data[(preview.end - offset):] - afterTextNode := &html.Node{ - Type: html.TextNode, - Data: after, - } - matched := true - switch node.Parent.Data { - case "div", "li", "td", "th", "details": - nextSibling := node.NextSibling - node.Parent.InsertBefore(previewNode, nextSibling) - node.Parent.InsertBefore(afterTextNode, nextSibling) - case "p", "span", "em", "strong": - nextParentSibling := node.Parent.NextSibling - node.Parent.Parent.InsertBefore(previewNode, nextParentSibling) - afterNode := &html.Node{ - Type: html.ElementNode, - Data: node.Parent.Data, - Attr: node.Parent.Attr, - } - afterNode.AppendChild(afterTextNode) - node.Parent.Parent.InsertBefore(afterNode, nextParentSibling) - for sibling := node.NextSibling; sibling != nil; sibling = node.NextSibling { - sibling.Parent.RemoveChild(sibling) - afterNode.AppendChild(sibling) - } - default: - matched = false - } - if matched { - offset = preview.end - node.Data = before - node = afterTextNode + afterPrefix := "

" + offset = preview.end - len(afterPrefix) + node.Data = before + nextSibling := node.NextSibling + node.Parent.InsertBefore(&html.Node{ + Type: html.RawNode, + Data: "

", + }, nextSibling) + node.Parent.InsertBefore(previewNode, nextSibling) + afterNode := &html.Node{ + Type: html.RawNode, + Data: afterPrefix + after, } + node.Parent.InsertBefore(afterNode, nextSibling) + node = afterNode } + node = node.NextSibling } } @@ -1325,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_internal_test.go b/modules/markup/html_internal_test.go index 1f717a78ed..08b1fed505 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -303,12 +303,12 @@ func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *Rend func TestRender_AutoLink(t *testing.T) { setting.AppURL = TestAppURL - test := func(input, expected, base string) { + test := func(input, expected string) { var buffer strings.Builder err := PostProcess(&RenderContext{ Ctx: git.DefaultContext, Links: Links{ - Base: base, + Base: TestRepoURL, }, Metas: localMetas, }, strings.NewReader(input), &buffer) @@ -319,7 +319,7 @@ func TestRender_AutoLink(t *testing.T) { err = PostProcess(&RenderContext{ Ctx: git.DefaultContext, Links: Links{ - Base: base, + Base: TestRepoURL, }, Metas: localMetas, IsWiki: true, @@ -328,87 +328,19 @@ func TestRender_AutoLink(t *testing.T) { assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String())) } - t.Run("Issue", func(t *testing.T) { - // render valid issue URLs - test(util.URLJoin(TestRepoURL, "issues", "3333"), - numericIssueLink(util.URLJoin(TestRepoURL, "issues"), "ref-issue", 3333, "#"), - TestRepoURL) - }) + // render valid issue URLs + test(util.URLJoin(TestRepoURL, "issues", "3333"), + numericIssueLink(util.URLJoin(TestRepoURL, "issues"), "ref-issue", 3333, "#")) - t.Run("Commit", func(t *testing.T) { - // render valid commit URLs - tmp := util.URLJoin(TestRepoURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae") - test(tmp, "
d8a994ef24", TestRepoURL) - test(tmp, ""+TestOrgRepo+"@d8a994ef24", "https://localhost/forgejo/forgejo") - test( - tmp+"#diff-2", - "d8a994ef24 (diff-2)", - TestRepoURL, - ) - test( - tmp+"#diff-953bb4f01b7c77fa18f0cd54211255051e647dbc", - "d8a994ef24 (diff-953bb4f01b)", - TestRepoURL, - ) + // render valid commit URLs + tmp := util.URLJoin(TestRepoURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae") + test(tmp, "d8a994ef24") + tmp += "#diff-2" + test(tmp, "d8a994ef24 (diff-2)") - // render other commit URLs - tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2" - test(tmp, "d8a994ef24 (diff-2)", "https://external-link.gitea.io/go-gitea/gitea") - test(tmp, "go-gitea/gitea@d8a994ef24 (diff-2)", TestRepoURL) - - tmp = "http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" - test(tmp, "190d949293", "http://localhost:3000/gogits/gogs") - test(tmp, "gogits/gogs@190d949293", "https://external-link.gitea.io/go-gitea/gitea") - - tmp = "http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" - test(tmp, "190d949293", "http://localhost:3000/sub/gogits/gogs") - test(tmp, "gogits/gogs@190d949293", "http://localhost:3000/gogits/gogs") - test(tmp, "gogits/gogs@190d949293", "https://external-link.gitea.io/go-gitea/gitea") - - tmp = "http://localhost:3000/sub1/sub2/sub3/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" - test(tmp, "190d949293", "http://localhost:3000/sub1/sub2/sub3/gogits/gogs") - test(tmp, "gogits/gogs@190d949293", "http://localhost:3000/sub1/gogits/gogs") - test(tmp, "gogits/gogs@190d949293", "https://external-link.gitea.io/go-gitea/gitea") - - // if the repository happens to be named like one of the known app routes (e.g. `src`), - // we can parse the URL correctly, if there is no sub path - tmp = "http://localhost:3000/gogits/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" - test(tmp, "gogits/src@190d949293", TestRepoURL) - tmp = "http://localhost:3000/gogits/src/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" - test(tmp, "gogits/src@190d949293", TestRepoURL) - // but if there is a sub path, we cannot reliably distinguish the repo name from the app route - tmp = "http://localhost:3000/sub/gogits/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" - test(tmp, "sub/gogits@190d949293", TestRepoURL) - }) - - t.Run("Compare", func(t *testing.T) { - tmp := util.URLJoin(TestRepoURL, "compare", "d8a994ef243349f321568f9e36d5c3f444b99cae..190d9492934af498c3f669d6a2431dc5459e5b20") - test(tmp, "d8a994ef24..190d949293", TestRepoURL) - test(tmp, ""+TestOrgRepo+"@d8a994ef24..190d949293", "https://localhost/forgejo/forgejo") - - tmp = "http://localhost:3000/sub/gogits/gogs/compare/190d9492934af498c3f669d6a2431dc5459e5b20..d8a994ef243349f321568f9e36d5c3f444b99cae" - test(tmp, "190d949293..d8a994ef24", "http://localhost:3000/sub/gogits/gogs") - test(tmp, "gogits/gogs@190d949293..d8a994ef24", "http://localhost:3000/gogits/gogs") - test(tmp, "gogits/gogs@190d949293..d8a994ef24", "https://external-link.gitea.io/go-gitea/gitea") - - tmp = "http://localhost:3000/sub1/sub2/sub3/gogits/gogs/compare/190d9492934af498c3f669d6a2431dc5459e5b20..d8a994ef243349f321568f9e36d5c3f444b99cae" - test(tmp, "190d949293..d8a994ef24", "http://localhost:3000/sub1/sub2/sub3/gogits/gogs") - test(tmp, "gogits/gogs@190d949293..d8a994ef24", "http://localhost:3000/sub1/gogits/gogs") - test(tmp, "gogits/gogs@190d949293..d8a994ef24", "https://external-link.gitea.io/go-gitea/gitea") - - tmp = "https://codeberg.org/forgejo/forgejo/compare/8bbac4c679bea930c74849c355a60ed3c52f8eb5...e2278e5a38187a1dc84dc41d583ec8b44e7257c1?files=options/locale/locale_fi-FI.ini" - test(tmp, "8bbac4c679...e2278e5a38 (options/locale/locale_fi-FI.ini)", "https://codeberg.org/forgejo/forgejo") - test(tmp, "forgejo/forgejo@8bbac4c679...e2278e5a38 (options/locale/locale_fi-FI.ini)", TestRepoURL) - test(tmp+".", "forgejo/forgejo@8bbac4c679...e2278e5a38 (options/locale/locale_fi-FI.ini).", TestRepoURL) - - tmp = "https://codeberg.org/forgejo/forgejo/compare/8bbac4c679bea930c74849c355a60ed3c52f8eb5...e2278e5a38187a1dc84dc41d583ec8b44e7257c1?files=options/locale/locale_fi-FI.ini#L2" - test(tmp, "8bbac4c679...e2278e5a38 (options/locale/locale_fi-FI.ini#L2)", "https://codeberg.org/forgejo/forgejo") - }) - - t.Run("Invalid URLs", func(t *testing.T) { - tmp := "https://local host/gogits/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" - test(tmp, "https://local host/gogits/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20", TestRepoURL) - }) + // render other commit URLs + tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2" + test(tmp, "d8a994ef24 (diff-2)") } func TestRender_IssueIndexPatternRef(t *testing.T) { @@ -497,79 +429,45 @@ func TestRegExp_hashCurrentPattern(t *testing.T) { func TestRegExp_anySHA1Pattern(t *testing.T) { testCases := map[string][]string{ "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": { - "jquery/jquery/blob", "a644101ed04d0beacea864ce805e0c4f86ba1cd1", "/test/unit/event.js", "", "#L2703", }, "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": { - "jquery/jquery/blob", "a644101ed04d0beacea864ce805e0c4f86ba1cd1", "/test/unit/event.js", "", "", }, "https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": { - "jquery/jquery/commit", "0705be475092aede1eddae01319ec931fb9c65fc", "", "", "", }, "https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": { - "jquery/jquery/tree", "0705be475092aede1eddae01319ec931fb9c65fc", "/src", "", "", }, "https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": { - "gogs/gogs/commit", "d8a994ef243349f321568f9e36d5c3f444b99cae", "", "", "#diff-2", }, "https://codeberg.org/forgejo/forgejo/src/commit/949ab9a5c4cac742f84ae5a9fa186f8d6eb2cdc0/RELEASE-NOTES.md?display=source&w=1#L7-L9": { - "forgejo/forgejo/src/commit", "949ab9a5c4cac742f84ae5a9fa186f8d6eb2cdc0", "/RELEASE-NOTES.md", "?display=source&w=1", "#L7-L9", }, - "http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3": { - "gogits/gogs/src/commit", - "190d9492934af498c3f669d6a2431dc5459e5b20", - "/path/to/file.go", - "", - "#L2-L3", - }, - "http://localhost:3000/sub/gogits/gogs/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3": { - "sub/gogits/gogs/commit", - "190d9492934af498c3f669d6a2431dc5459e5b20", - "/path/to/file.go", - "", - "#L2-L3", - }, - "http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3": { - "sub/gogits/gogs/src/commit", - "190d9492934af498c3f669d6a2431dc5459e5b20", - "/path/to/file.go", - "", - "#L2-L3", - }, - "http://localhost:3000/sub1/sub2/sub3/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3": { - "sub1/sub2/sub3/gogits/gogs/src/commit", - "190d9492934af498c3f669d6a2431dc5459e5b20", - "/path/to/file.go", - "", - "#L2-L3", - }, } for k, v := range testCases { - assert.Equal(t, v, anyHashPattern.FindStringSubmatch(k)[1:]) + assert.Equal(t, anyHashPattern.FindStringSubmatch(k)[1:], v) } for _, v := range []string{"https://codeberg.org/forgejo/forgejo/attachments/774421a1-b0ae-4501-8fba-983874b76811"} { diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index c7c53e2678..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) { @@ -241,45 +238,6 @@ func TestRender_links(t *testing.T) { markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes) } -func TestRender_PullReviewCommitLink(t *testing.T) { - setting.AppURL = markup.TestAppURL - - sha := "190d9492934af498c3f669d6a2431dc5459e5b20" - prCommitLink := util.URLJoin(markup.TestRepoURL, "pulls", "1", "commits", sha) - - test := func(input, expected, base string) { - buffer, err := markup.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - RelativePath: ".md", - Links: markup.Links{ - AbsolutePrefix: true, - Base: base, - }, - Metas: localMetas, - }, input) - require.NoError(t, err) - assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) - } - - test(prCommitLink, `

!1 (commit `+sha[0:10]+`)

`, markup.TestRepoURL) - - prCommitLink = util.URLJoin(markup.TestAppURL, "sub1", "sub2", markup.TestOrgRepo, "pulls", "1", "commits", sha) - test( - prCommitLink, - `

!1 (commit `+sha[0:10]+`)

`, - util.URLJoin(markup.TestAppURL, "sub1", "sub2", markup.TestOrgRepo), - ) - test( - prCommitLink, - `

`+markup.TestOrgRepo+`@!1 (commit `+sha[0:10]+`)

`, - markup.TestRepoURL, - ) - - prCommitLink = "https://codeberg.org/forgejo/forgejo/pulls/7979/commits/4d968c08e0a8d24bd2f3fb2a3a48b37e6d84a327#diff-7649acfa98a9ee3faf0d28b488bbff428317fc72" - test(prCommitLink, `

!7979 (commit 4d968c08e0)

`, "https://codeberg.org/forgejo/forgejo") - test(prCommitLink, `

forgejo/forgejo@!7979 (commit 4d968c08e0)

`, markup.TestRepoURL) -} - func TestRender_email(t *testing.T) { setting.AppURL = markup.TestAppURL @@ -400,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:

`) @@ -725,9 +683,6 @@ func TestIssue18471(t *testing.T) { err := markup.PostProcess(&markup.RenderContext{ Ctx: git.DefaultContext, Metas: localMetas, - Links: markup.Links{ - Base: "http://domain/org/repo", - }, }, strings.NewReader(data), &res) require.NoError(t, err) @@ -765,9 +720,6 @@ func TestRender_FilePreview(t *testing.T) { Ctx: git.DefaultContext, RelativePath: ".md", Metas: metas, - Links: markup.Links{ - Base: markup.TestRepoURL, - }, }, input) require.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) @@ -791,11 +743,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -825,11 +777,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -880,7 +832,7 @@ func TestRender_FilePreview(t *testing.T) { testRender( urlWithSub, - `

gogits/gogs@190d949293/path/to/file.go (L2-L3)

`, + `

190d949293/path/to/file.go (L2-L3)

`, localMetas, ) @@ -904,11 +856,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -935,11 +887,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -968,11 +920,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -993,11 +945,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1024,11 +976,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1049,11 +1001,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1074,11 +1026,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1157,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) { @@ -1210,11 +1129,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1226,91 +1145,32 @@ func TestRender_FilePreview(t *testing.T) { }) commitFileURL = util.URLJoin(markup.TestRepoURL, "src", "commit", "eeb243c3395e1921c5d90e73bd739827251fc99d", "path", "to", "file%20%23.txt") - commitFileURLFirstLine := commitFileURL + "#L1" - filePreviewBox := `
` + - `
` + - `
` + - `path/to/file #.txt` + - `
` + - `` + - `Line 1 in eeb243c` + - `` + - `
` + - `
` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `
A` + "\n" + `
` + - `
` + - `
` - linkRendered := `eeb243c339/path/to/file #.txt (L1)` t.Run("file with strange characters in name", func(t *testing.T) { testRender( - commitFileURLFirstLine, - `

`+filePreviewBox+`

`, - localMetas, - ) - }) - - t.Run("file preview with stuff before and after", func(t *testing.T) { - testRender( - ":frog: before"+commitFileURLFirstLine+" :frog: after", - `

🐸 before

`+ - filePreviewBox+ - `

🐸 after

`, - localMetas, - ) - }) - - t.Run("file preview in
,
  • ,
    (not in ) environments", func(t *testing.T) { - testRender( - "
    "+commitFileURLFirstLine+"
    \n"+ - "
    • "+commitFileURLFirstLine+"
    \n"+ - "
    "+commitFileURLFirstLine+""+commitFileURLFirstLine+"
    ", - `
    `+filePreviewBox+`
    `+"\n"+ - `
    • `+filePreviewBox+`
    `+"\n"+ - `
    `+linkRendered+``+filePreviewBox+`
    `, - localMetas, - ) - }) - - t.Run("file preview in , and environments", func(t *testing.T) { - testRender( - "
    "+commitFileURLFirstLine+" "+commitFileURLFirstLine+" "+commitFileURLFirstLine+"
    ", - `
    `+filePreviewBox+` `+ - ``+filePreviewBox+` `+ - ``+filePreviewBox+`
    `, - localMetas, - ) - }) - - t.Run("no file preview in heading, striked out, code environments", func(t *testing.T) { - testRender( - "

    "+commitFileURLFirstLine+"

    \n"+commitFileURLFirstLine+"\n"+commitFileURLFirstLine+"", - `

    `+linkRendered+`

    `+"\n"+ - ``+linkRendered+``+"\n"+ - ``+commitFileURLFirstLine+``, - localMetas, - ) - }) - - t.Run("file previews followed by new line", func(t *testing.T) { - testRender( - commitFileURLFirstLine+"\nand\n"+commitFileURLFirstLine, - "

    "+filePreviewBox+"


    \nand
    \n

    "+filePreviewBox+"

    ", - localMetas, - ) - }) - - t.Run("file previews followed by new line in div", func(t *testing.T) { - testRender( - "
    "+commitFileURLFirstLine+"\nand\n"+commitFileURLFirstLine+"
    ", - "
    "+filePreviewBox+"\nand\n"+filePreviewBox+"
    ", + commitFileURL+"#L1", + `

    `+ + `
    `+ + `
    `+ + `
    `+ + `path/to/file #.txt`+ + `
    `+ + ``+ + `Line 1 in eeb243c`+ + ``+ + `
    `+ + `
    `+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
    A`+"\n"+`
    `+ + `
    `+ + `
    `+ + `

    `, localMetas, ) }) 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..f7955115e0 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -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 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/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 05dd512815..a622d75085 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -17,7 +17,6 @@ import ( "forgejo.org/modules/git" "forgejo.org/modules/setting" "forgejo.org/modules/util" - "forgejo.org/modules/util/donotpanic" "github.com/yuin/goldmark/ast" ) @@ -249,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, @@ -268,15 +268,6 @@ sandbox="allow-scripts" return err } -func postProcessOrCopy(ctx *RenderContext, renderer Renderer, reader io.Reader, writer io.Writer) (err error) { - if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() { - err = PostProcess(ctx, reader, writer) - } else { - _, err = io.Copy(writer, reader) - } - return err -} - func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error { var wg sync.WaitGroup var err error @@ -303,7 +294,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr wg.Add(1) go func() { - err = donotpanic.SafeFuncWithError(func() error { return SanitizeReader(pr2, renderer.Name(), output) }) + err = SanitizeReader(pr2, renderer.Name(), output) _ = pr2.Close() wg.Done() }() @@ -313,7 +304,11 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr wg.Add(1) go func() { - err = donotpanic.SafeFuncWithError(func() error { return postProcessOrCopy(ctx, renderer, pr, pw2) }) + if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() { + err = PostProcess(ctx, pr, pw2) + } else { + _, err = io.Copy(pw2, pr) + } _ = pr.Close() _ = pw2.Close() wg.Done() @@ -322,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/renderer_test.go b/modules/markup/renderer_test.go index 13890d4361..0791081f94 100644 --- a/modules/markup/renderer_test.go +++ b/modules/markup/renderer_test.go @@ -1,49 +1,4 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT -package markup - -import ( - "bytes" - "errors" - "strings" - "testing" - - "forgejo.org/modules/test" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -type failReader struct{} - -func (*failReader) Read(p []byte) (n int, err error) { - return 0, errors.New("FAIL") -} - -func TestRender_postProcessOrCopy(t *testing.T) { - renderContext := &RenderContext{Ctx: t.Context()} - - t.Run("CopyOK", func(t *testing.T) { - input := "SOMETHING" - output := &bytes.Buffer{} - require.NoError(t, postProcessOrCopy(renderContext, nil, strings.NewReader(input), output)) - assert.Equal(t, input, output.String()) - }) - - renderer := GetRendererByType("markdown") - - t.Run("PostProcessOK", func(t *testing.T) { - input := "SOMETHING" - output := &bytes.Buffer{} - defer test.MockVariableValue(&defaultProcessors, []processor{})() - require.NoError(t, postProcessOrCopy(renderContext, renderer, strings.NewReader(input), output)) - assert.Equal(t, input, output.String()) - }) - - t.Run("PostProcessError", func(t *testing.T) { - input := &failReader{} - defer test.MockVariableValue(&defaultProcessors, []processor{})() - assert.ErrorContains(t, postProcessOrCopy(renderContext, renderer, input, &bytes.Buffer{}), "FAIL") - }) -} +package markup_test 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/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/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/actions.go b/modules/setting/actions.go index 303fd6363b..52a3ad5309 100644 --- a/modules/setting/actions.go +++ b/modules/setting/actions.go @@ -12,25 +12,23 @@ import ( // Actions settings var ( Actions = struct { - Enabled bool - LogStorage *Storage // how the created logs should be stored - LogRetentionDays int64 `ini:"LOG_RETENTION_DAYS"` - LogCompression logCompression `ini:"LOG_COMPRESSION"` - ArtifactStorage *Storage // how the created artifacts should be stored - ArtifactRetentionDays int64 `ini:"ARTIFACT_RETENTION_DAYS"` - DefaultActionsURL defaultActionsURL `ini:"DEFAULT_ACTIONS_URL"` - ZombieTaskTimeout time.Duration `ini:"ZOMBIE_TASK_TIMEOUT"` - EndlessTaskTimeout time.Duration `ini:"ENDLESS_TASK_TIMEOUT"` - AbandonedJobTimeout time.Duration `ini:"ABANDONED_JOB_TIMEOUT"` - SkipWorkflowStrings []string `ìni:"SKIP_WORKFLOW_STRINGS"` - LimitDispatchInputs int64 `ini:"LIMIT_DISPATCH_INPUTS"` - ConcurrencyGroupQueueEnabled bool `ini:"CONCURRENCY_GROUP_QUEUE_ENABLED"` + Enabled bool + LogStorage *Storage // how the created logs should be stored + LogRetentionDays int64 `ini:"LOG_RETENTION_DAYS"` + LogCompression logCompression `ini:"LOG_COMPRESSION"` + ArtifactStorage *Storage // how the created artifacts should be stored + ArtifactRetentionDays int64 `ini:"ARTIFACT_RETENTION_DAYS"` + DefaultActionsURL defaultActionsURL `ini:"DEFAULT_ACTIONS_URL"` + ZombieTaskTimeout time.Duration `ini:"ZOMBIE_TASK_TIMEOUT"` + EndlessTaskTimeout time.Duration `ini:"ENDLESS_TASK_TIMEOUT"` + AbandonedJobTimeout time.Duration `ini:"ABANDONED_JOB_TIMEOUT"` + SkipWorkflowStrings []string `ìni:"SKIP_WORKFLOW_STRINGS"` + LimitDispatchInputs int64 `ini:"LIMIT_DISPATCH_INPUTS"` }{ - Enabled: true, - DefaultActionsURL: defaultActionsURLForgejo, - SkipWorkflowStrings: []string{"[skip ci]", "[ci skip]", "[no ci]", "[skip actions]", "[actions skip]"}, - LimitDispatchInputs: 10, - ConcurrencyGroupQueueEnabled: true, + Enabled: true, + DefaultActionsURL: defaultActionsURLForgejo, + SkipWorkflowStrings: []string{"[skip ci]", "[ci skip]", "[no ci]", "[skip actions]", "[actions skip]"}, + LimitDispatchInputs: 10, } ) 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/indexer_test.go b/modules/setting/indexer_test.go index 498f8752a2..8f0437be8a 100644 --- a/modules/setting/indexer_test.go +++ b/modules/setting/indexer_test.go @@ -65,7 +65,7 @@ func checkGlobMatch(t *testing.T, globstr string, list []indexerMatchList) { } } if !found { - assert.Equal(t, -1, m.position, "Test string `%s` doesn't match `%s` anywhere; expected @%d", m.value, globstr, m.position) + assert.Equal(t, m.position, -1, "Test string `%s` doesn't match `%s` anywhere; expected @%d", m.value, globstr, m.position) } } } 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..1f38857af6 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 @@ -114,8 +113,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") @@ -174,39 +171,3 @@ func loadSecurityFrom(rootCfg ConfigProvider) { 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/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 61378e0596..2e6a3df4c6 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -7,33 +7,35 @@ import ( "time" "forgejo.org/modules/container" + "forgejo.org/modules/log" ) // UI settings var UI = struct { - ExplorePagingNum int - SitemapPagingNum int - IssuePagingNum int - RepoSearchPagingNum int - MembersPagingNum int - FeedMaxCommitNum int - FeedPagingNum int - PackagesPagingNum int - GraphMaxCommitNum int - CodeCommentLines int - ReactionMaxUserNum int - MaxDisplayFileSize int64 - ShowUserEmail bool - DefaultShowFullName bool - DefaultTheme string - Themes []string - Reactions []string - ReactionsLookup container.Set[string] `ini:"-"` - CustomEmojis []string - CustomEmojisLookup container.Set[string] `ini:"-"` - SearchRepoDescription bool - OnlyShowRelevantRepos bool - ExploreDefaultSort string `ini:"EXPLORE_PAGING_DEFAULT_SORT"` + ExplorePagingNum int + SitemapPagingNum int + IssuePagingNum int + RepoSearchPagingNum int + MembersPagingNum int + FeedMaxCommitNum int + FeedPagingNum int + PackagesPagingNum int + GraphMaxCommitNum int + CodeCommentLines int + ReactionMaxUserNum int + MaxDisplayFileSize int64 + ShowUserEmail bool + DefaultShowFullName bool + DefaultTheme string + Themes []string + Reactions []string + ReactionsLookup container.Set[string] `ini:"-"` + CustomEmojis []string + CustomEmojisMap map[string]string `ini:"-"` + SearchRepoDescription bool + OnlyShowRelevantRepos bool + ExploreDefaultSort string `ini:"EXPLORE_PAGING_DEFAULT_SORT"` + PreferredTimestampTense string AmbiguousUnicodeDetection bool SkipEscapeContexts []string @@ -69,23 +71,25 @@ var UI = struct { Keywords string } `ini:"ui.meta"` }{ - ExplorePagingNum: 20, - SitemapPagingNum: 20, - IssuePagingNum: 20, - RepoSearchPagingNum: 20, - MembersPagingNum: 20, - FeedMaxCommitNum: 5, - FeedPagingNum: 20, - PackagesPagingNum: 20, - GraphMaxCommitNum: 100, - CodeCommentLines: 4, - ReactionMaxUserNum: 10, - MaxDisplayFileSize: 8388608, - DefaultTheme: `forgejo-auto`, - 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`}, - ExploreDefaultSort: "recentupdate", + ExplorePagingNum: 20, + SitemapPagingNum: 20, + IssuePagingNum: 20, + RepoSearchPagingNum: 20, + MembersPagingNum: 20, + FeedMaxCommitNum: 5, + FeedPagingNum: 20, + PackagesPagingNum: 20, + GraphMaxCommitNum: 100, + CodeCommentLines: 4, + ReactionMaxUserNum: 10, + MaxDisplayFileSize: 8388608, + DefaultTheme: `forgejo-auto`, + 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", AmbiguousUnicodeDetection: true, SkipEscapeContexts: []string{}, @@ -147,6 +151,10 @@ func loadUIFrom(rootCfg ConfigProvider) { UI.DefaultShowFullName = sec.Key("DEFAULT_SHOW_FULL_NAME").MustBool(false) UI.SearchRepoDescription = sec.Key("SEARCH_REPO_DESCRIPTION").MustBool(true) + if UI.PreferredTimestampTense != "mixed" && UI.PreferredTimestampTense != "absolute" { + log.Fatal("ui.PREFERRED_TIMESTAMP_TENSE must be either 'mixed' or 'absolute'") + } + // OnlyShowRelevantRepos=false is important for many private/enterprise instances, // because many private repositories do not have "description/topic", users just want to search by their names. UI.OnlyShowRelevantRepos = sec.Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false) @@ -155,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/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/admin_user.go b/modules/structs/admin_user.go index a7bd5643e9..8a4d066d72 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -50,5 +50,4 @@ type EditUserOption struct { AllowCreateOrganization *bool `json:"allow_create_organization"` Restricted *bool `json:"restricted"` Visibility string `json:"visibility" binding:"In(,public,limited,private)"` - HideEmail *bool `json:"hide_email"` } diff --git a/modules/structs/commit_status.go b/modules/structs/commit_status.go index 2a5dfb7546..dc880ef5eb 100644 --- a/modules/structs/commit_status.go +++ b/modules/structs/commit_status.go @@ -4,7 +4,7 @@ package structs // CommitStatusState holds the state of a CommitStatus -// It can be "pending", "success", "error", "failure" and "warning" +// It can be "pending", "success", "error" and "failure" type CommitStatusState string const ( 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/structs/repo_actions.go b/modules/structs/repo_actions.go index eada2db09f..b13f344738 100644 --- a/modules/structs/repo_actions.go +++ b/modules/structs/repo_actions.go @@ -32,27 +32,3 @@ type ActionTaskResponse struct { Entries []*ActionTask `json:"workflow_runs"` TotalCount int64 `json:"total_count"` } - -// ActionRunnerLabel represents a Runner Label -type ActionRunnerLabel struct { - ID int64 `json:"id"` - Name string `json:"name"` - Type string `json:"type"` -} - -// ActionRunner represents a Runner -type ActionRunner struct { - ID int64 `json:"id"` - Name string `json:"name"` - Status string `json:"status"` - Busy bool `json:"busy"` - // currently unused as forgejo does not support ephemeral runners, but they are defined in gh api spec - Ephemeral bool `json:"ephemeral"` - Labels []*ActionRunnerLabel `json:"labels"` -} - -// ActionRunnersResponse returns Runners -type ActionRunnersResponse struct { - Entries []*ActionRunner `json:"runners"` - TotalCount int64 `json:"total_count"` -} 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_date.go b/modules/templates/util_date.go index 839d2701ef..bb83bf692a 100644 --- a/modules/templates/util_date.go +++ b/modules/templates/util_date.go @@ -144,5 +144,8 @@ func timeSinceTo(then any, now time.Time) template.HTML { // TimeSince renders relative time HTML given a time func TimeSince(then any) template.HTML { + if setting.UI.PreferredTimestampTense == "absolute" { + return dateTimeFormat("full", then) + } return timeSinceTo(then, time.Now()) } 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..62e063213c 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" @@ -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/donotpanic/donotpanic.go b/modules/util/donotpanic/donotpanic.go deleted file mode 100644 index e283ec9040..0000000000 --- a/modules/util/donotpanic/donotpanic.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package donotpanic - -import ( - "fmt" - - "forgejo.org/modules/log" -) - -type FuncWithError func() error - -func SafeFuncWithError(fun FuncWithError) (err error) { - defer func() { - if r := recover(); r != nil { - log.Error("PANIC recovered: %v\nStacktrace: %s", r, log.Stack(2)) - rErr, ok := r.(error) - if ok { - err = fmt.Errorf("PANIC recover with error: %w", rErr) - } else { - err = fmt.Errorf("PANIC recover: %v", r) - } - } - }() - - return fun() -} diff --git a/modules/util/donotpanic/donotpanic_test.go b/modules/util/donotpanic/donotpanic_test.go deleted file mode 100644 index 6ddeafe06a..0000000000 --- a/modules/util/donotpanic/donotpanic_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package donotpanic - -import ( - "errors" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestDoNotPanic_SafeFuncWithError(t *testing.T) { - t.Run("OK", func(t *testing.T) { - assert.NoError(t, SafeFuncWithError(func() error { return nil })) - }) - - t.Run("PanickString", func(t *testing.T) { - errorMessage := "ERROR MESSAGE" - assert.ErrorContains(t, SafeFuncWithError(func() error { panic(errorMessage) }), fmt.Sprintf("recover: %s", errorMessage)) - }) - - t.Run("PanickError", func(t *testing.T) { - errorMessage := "ERROR MESSAGE" - assert.ErrorContains(t, SafeFuncWithError(func() error { panic(errors.New(errorMessage)) }), fmt.Sprintf("recover with error: %s", errorMessage)) - }) -} 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 256692c080..a0adbef8f8 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 = مساعدة @@ -64,6 +66,7 @@ webauthn_unsupported_browser = متصفحك لا يدعم ويب آوثن حال copy = انسخ enabled = مُفَعَّل rerun = أعِد التشغيل +new_org = منظمة جديدة milestones = أهداف webauthn_error_insecure = ويب آوثن يدعم فقط الاتصالات الآمنة. للاختبار على HTTP، يمكنك استخدام "localhost" أو "127.0.0.1" show_timestamps = إظهار الطوابع الزمنية @@ -86,9 +89,12 @@ new_project_column = عمود جديد add = أضف active_stopwatch = متتبِّع وقت النشاط organization = منظمة +new_migrate = ترحيل جديد save = احفظ sign_in_with_provider = سجل الدخول بـ %s ok = وافق +manage_org = إدارة المنظمات +new_repo = مستودع جديد webauthn_error_unable_to_process = الخادم لا يمكنه معالجة طلبك. register = سجل mirror = مرآة @@ -138,9 +144,10 @@ new_migrate.title = انتقال جديد new_org.title = منظمة جديدة new_repo.link = مستودع جديد new_migrate.link = انتقال جديد -copy_path = نسخ المسار -test = اختبار + new_org.link = منظمة جديدة +test = اختبار +copy_path = نسخ المسار error413 = لقد استنفدت حصتك. [install] @@ -160,7 +167,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 الصحيح. @@ -220,9 +227,9 @@ enable_captcha.description = مطالبة المستخدمين باجتياز ا openid_signup.description = فعّل التسجيل الذاتي للمستخدمين عبر OpenID. 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 @@ -244,24 +251,26 @@ default_keep_email_private.description = قم بتمكين إخفاء عنوان env_config_keys = إعدادات بيئية 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 = السماح للمستخدمين الجدد بإنشاء منتديات المجموعة بشكل افتراضي. عند تعطيل هذا الخيار، سيتعين على المسؤول منح إذن لإنشاء منتديات المجموعة للمستخدمين الجدد. password_algorithm = خوارزمية تجزئة كلمة المرور invalid_password_algorithm = خوارزمية بصمة كلمة المرور غير صالحة -password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقيواها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. -app_slogan_helper = أدخل شعار المثيل الخاص بك هنا. اتركه فارغاً لتعطيله. +password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقوتها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. + app_slogan = شعار المثيل +app_slogan_helper = أدخل شعار المثيل الخاص بك هنا. اتركه فارغاً لتعطيله. +smtp_from_invalid = عنوان "،بريد الإرسال كـ" غير صالح allow_only_external_registration = السماح بالتسجيل عبر الخدمات الخارجية فقط config_location_hint = سيتم حفظ خيارات التهيئة هذه في: -smtp_from_invalid = عنوان "،بريد الإرسال كـ" غير صالح [editor] buttons.list.ordered.tooltip = أضف قائمة مرقمة -buttons.bold.tooltip = أضف نصًا عريضًا (Ctrl+B / ⌘B) +buttons.bold.tooltip = أضف نصًا عريضًا buttons.quote.tooltip = اقتبس نص buttons.code.tooltip = أضف نصًا برمجيا buttons.list.unordered.tooltip = أضف قائمة نقطية @@ -271,20 +280,21 @@ buttons.ref.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.unindent.tooltip = ‪عناصر غير متساوية من نفس المستوى + buttons.indent.tooltip = تداخل العناصر بنفس المستوى +buttons.unindent.tooltip = ‪عناصر غير متساوية من نفس المستوى +buttons.new_table.tooltip = إضافة جدول table_modal.header = إضافة جدول table_modal.placeholder.header = الترويسة table_modal.placeholder.content = المحتوى table_modal.label.rows = الصفوف table_modal.label.columns = الأعمدة +link_modal.header = إضافة رابط link_modal.url = Url link_modal.description = الوصف -buttons.new_table.tooltip = إضافة جدول -link_modal.header = إضافة رابط link_modal.paste_reminder = تلميح: باستخدام عنوان URL في حافظتك، يمكنك اللصق مباشرةً في المحرر لإنشاء رابط. [aria] @@ -359,11 +369,13 @@ manage_emails = أدر عناوين البريد الإلكتروني then_enter_passcode = وأدخل رمز الدخول الظاهر في التطبيق: update_password = حدّث كلمة المرور continue = استمر +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,15 +383,18 @@ location_placeholder = شارك مكانك التقريبي مع الآخرين appearance = المظهر repos = المستودعات change_username = تم تحديث اسم مستخدمك. +social = الحسابات الاجتماعية twofa = الاستيثاق الثنائي saved_successfully = حُدِّثت إعداداتك بنجاح. update_theme = حدِّث السمة +access_token_deletion_confirm_action = احذف website = الموقع الإلكتروني delete_token = حذف hidden_comment_types.ref_tooltip = التعليقات التي تقول أن هذه المسألة قد أشير إليها في مسألة أخرى أو إيداع أو غير ذلك… update_language_success = تم تحديث اللغة. privacy = الخصوصية comment_type_group_label = التصنيفات +account_link = الحسابات المرتبطة comment_type_group_assignee = المكلفون update_language = تغيير اللغة organization = المنظمات @@ -425,6 +440,7 @@ webauthn_delete_key_desc = إذا أزلت مفتاح الأمان، فلن تت permissions_list = التصاريح: webauthn_key_loss_warning = إذا فقدت مفاتيح الأمان الخاصة بك، فسوف تفقد الوصول إلى حسابك. hooks.desc = إضافة خطاطيف ويب سيتم تشغيلها لـ جميع المستودعات التي تمتلكها. +keep_activity_private_popup = يجعل النشاط مرأياً لك وللمديرين فقط keep_email_private_popup = سيؤدي هذا إلى إخفاء عنوان بريدك الإلكتروني من ملفك الشخصي، وكذلك عند تقديم طلب سحب أو تحرير ملف باستخدام واجهة الويب. لن يتم تعديل الالتزامات المدفوعة. استخدم %s في الإيداعات لربطها بحسابك. ssh_key_name_used = هناك مفتاح SSH بنفس الاسم موجود بالفعل على حسابك. authorized_oauth2_applications = تطبيقات OAuth2 المأذون لها @@ -438,6 +454,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,6 +462,7 @@ 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 لتأكيد عنوان البريد الإلكتروني. @@ -464,6 +482,7 @@ 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 = تم التحقق من المفتاح مع رمز ويمكن استخدامه للتحقق من الإيداعات المتطابقة مع أي عناوين البريد المفعلة لهذا المستخدم. @@ -481,6 +500,7 @@ gpg_invalid_token_signature = مفتاح GPG المزود، والتوقيع و 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". @@ -506,102 +526,106 @@ update_oauth2_application_success = لقد حدّثت بنجاح تطبيق OAut oauth2_redirect_uris = روابط إعادة التوجيه. نرجو وضع كل رابط في سطر وحده. remove_account_link = أزل الحساب المربوط remove_account_link_success = أُزيل الحساب المرتبط. + quota = كوتا -update_hints = حدِّث التلميحات -keep_activity_private.description = سيكون نشاطك العام مرئيًا لك ولمشرفي المثيل فقط. -manage_ssh_principals = إدارة مدراء شهادات SSH الرئيسية + +storage_overview = نظرة عامة على التخزين 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 يومًا. لا يزال بإمكانك استعادة اسم المستخدم القديم خلال فترة التهدئة. +change_username_redirect_prompt.with_cooldown.few = سيصبح اسم المستخدم القديم متاحًا للجميع بعد فترة تباطؤ تبلغ يومًا. لا يزال بإمكانك استعادة اسم المستخدم القديم خلال فترة التهدئة. +language.title = اللغة الافتراضية +language.description = سيتم حفظ هذه اللغة في حسابك واستخدامها كلغة افتراضية بعد تسجيل الدخول. +language.localization_project = "ساعدنا في ترجمة Forgejo إلى لغتك! المزيد من المعلومات. +hints = تلميحات additional_repo_units_hint = اقتراح تفعيل وحدات المستودعات الإضافية additional_repo_units_hint_description = اعرض تلميح "تفعيل المزيد" للمستودعات التي لم يتم تفعيل جميع الوحدات المتاحة بها. +update_hints = حدِّث التلميحات +update_hints_success = تم تحديث التلميحات. +keep_activity_private.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 = تم حذف الرمز الفريد. لم يعد بإمكان التطبيقات التي تستخدمه الوصول إلى حسابك. +keep_pronouns_private.description = سيؤدي ذلك إلى إخفاء ضمائرك عن الزوار الذين لم يقوموا بتسجيل الدخول. +manage_ssh_principals = إدارة مدراء شهادات SSH الرئيسية +principal_desc = هذه الهُويات الرئيسية لشهادات SSH مرتبطة بحسابك وتتيح وصولاً كاملاً إلى مستودعاتك. +ssh_helper = هل تحتاج مساعدة؟ اطلع على الدليل لـ إنشاء مفاتيح SSH الخاصة بك أو لحل المشكلات الشائعة التي قد تواجهها عند استخدام SSH. +gpg_helper = تحتاج لمساعدة؟ اطّلع على الدليل حول GPG. +add_new_principal = إضافة هوية رئيسية +ssh_principal_been_used = تمت إضافة هذه الهوية الرئيسية إلى الخادم مسبقاً. 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 = المفاتيح الفرعية +add_principal_success = تمت إضافة الهوية الرئيسية لشهادة "SSH "%s. +ssh_principal_deletion = إزالة الهوية الرئيسية لشهادة SSH ssh_principal_deletion_desc = إزالة هوية رئيسية لشهادة SSH ستسحب صلاحية وصولها إلى حسابك. تريد المتابعة؟ +ssh_principal_deletion_success = تم إزالة الهوية. +no_activity = لا يوجد نشاط حديث principal_state_desc = استخدمت هذه الهوية في آخر 7 أيام ssh_signonly = SSH معطّل حاليًا، لذا تُستخدم هذه المفاتيح فقط للتحقق من توقيع الإيداع. ssh_externally_managed = يتم إدارة مفتاح SSH هذا خارجيًا لهذا المستخدم +manage_access_token = رموز الوصول الفريدة +generate_new_token = توليد رمز جديد +tokens_desc = تمنح هذه الرموز الفريدة إمكانية الوصول إلى حسابك باستخدام واجهة برمجة تطبيقات Forgejo. +token_name = اسم الرمز الفريد +generate_token = توليد رمز فريد +generate_token_success = تم إنشاء الرمز الفريد الجديد الخاص بك. انسخه الآن لأنه لن يظهر مرة أخرى. +generate_token_name_duplicate = اسم التطبيق %s مُستخدم مسبقًا. يُرجى استخدام اسم جديد. +access_token_deletion = حذف رمز الوصول الفريد +access_token_deletion_desc = حذف الرمز الفريد سيسحب صلاحية الوصول إلى حسابك من التطبيقات التي تستخدمه. لا يمكن التراجع عن هذا الإجراء. تريد المتابعة؟ +delete_token_success = تم حذف الرمز الفريد. لم يعد بإمكان التطبيقات التي تستخدمه الوصول إلى حسابك. +regenerate_token = إعادة التوليد +access_token_regeneration = إعادة توليد رمز وصول فريد access_token_regeneration_desc = سيؤدي إعادة إنشاء رمز فريد إلى إبطال الوصول إلى حسابك للتطبيقات التي تستخدمه. لا يمكن التراجع عن ذلك. المتابعة؟ regenerate_token_success = تم إعادة إنشاء الرمز الغريد. لم يعد بإمكان التطبيقات التي تستخدمه الوصول إلى حسابك ويجب تحديثها بالرمز الجديد. repo_and_org_access = الوصول إلى المستودع والمنظمة -add_principal_success = تمت إضافة الهوية الرئيسية لشهادة "SSH "%s. -ssh_principal_deletion_success = تم إزالة الهوية. +permission_no_access = لا وصول +permission_write = قراءة وكتابة +access_token_desc = تقتصر صلاحيات الرمز المحددة على مسارات واجهة البرمجة (API) المقابلة فقط. اطلع على الوثائق لمزيد من المعلومات. +at_least_one_permission = يجب عليك تحديد صلاحية واحدة على الأقل لإنشاء رمز فريد oauth2_applications_desc = تمكّن تطبيقات OAuth2 تطبيقات الطرف الثالث من مصادقة المستخدمين بأمان في مثيل Forgejo هذا. +remove_oauth2_application_desc = ستؤدي إزالة تطبيق OAuth2 إلى إبطال الوصول إلى جميع رموز الوصول الفريدة الموقعة. المتابعة؟ +oauth2_confidential_client = العميل السري. حدد للتطبيقات التي تحافظ على السرية، مثل تطبيقات الويب. لا تحدد للتطبيقات الأصلية بما في ذلك تطبيقات سطح المكتب وتطبيقات الأجهزة المحمولة. +oauth2_client_id = معرف العميل 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_remove_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 = سحب صلاحية الوصول revoke_oauth2_grant_description = سيؤدي إبطال الوصول لهذا التطبيق التابع لجهة خارجية إلى منع هذا التطبيق من الوصول إلى بياناتك. أنت متأكد؟ revoke_oauth2_grant_success = تم سحب صلاحية الوصول بنجاح. -webauthn_alternative_tip = قد ترغب في تكوين أسلوب مصادقة إضافي. +twofa_recovery_tip = إذا فقدت جهازك، ستتمكن من استخدام مفتاح الاسترداد للاستخدام مرة واحدة لاستعادة الوصول إلى حسابك. +twofa_is_enrolled = حسابك مسجّل حاليًا في المصادقة الثنائية. +twofa_not_enrolled = حسابك غير مسجّل حالياً في المصادقة الثنائية. +twofa_scratch_token_regenerate = إعادة إنشاء مفتاح الاسترداد للاستخدام مرة واحدة +twofa_scratch_token_regenerated = مفتاح الاسترداد للاستخدام مرة واحدة هو %s الآن. قم بتخزينه في مكان آمن، فلن يتم عرضه مجدداً. +twofa_enroll = التسجيل في المصادقة الثنائية +regenerate_scratch_token_desc = إذا فقدت مفتاح الاسترداد الخاص بك في غير محله أو استخدمته بالفعل لتسجيل الدخول، يمكنك إعادة تعيينه هنا. +twofa_enrolled = تم تسجيل حسابك بنجاح. قم بتخزين مفتاح الاسترداد للاستخدام لمرة واحدة (%s) في مكان آمن، فلن يتم عرضه مجدداً. +twofa_failed_get_secret = إخفاق في الحصول على سر. +webauthn_desc = مفاتيح الأمان هي أجهزة فعلية تحوي على مفاتيح تشفير. يمكن استخدامها للتحقق بخطوتين. يجب أن تدعم مفاتيح الأمان معيار WebAuthn Authenticator. webauthn_register_key = إضافة مفتاح تشفير webauthn_nickname = الاسم المستعار +webauthn_alternative_tip = قد ترغب في تكوين أسلوب مصادقة إضافي. manage_account_links = الحسابات المرتبطة -revoke_oauth2_grant = سحب صلاحية الوصول -twofa_enroll = التسجيل في المصادقة الثنائية -twofa_is_enrolled = حسابك مسجّل حاليًا في المصادقة الثنائية. -twofa_scratch_token_regenerated = مفتاح الاسترداد للاستخدام مرة واحدة هو %s الآن. قم بتخزينه في مكان آمن، فلن يتم عرضه مجدداً. -twofa_enrolled = تم تسجيل حسابك بنجاح. قم بتخزين مفتاح الاسترداد للاستخدام لمرة واحدة (%s) في مكان آمن، فلن يتم عرضه مجدداً. -webauthn_desc = مفاتيح الأمان هي أجهزة فعلية تحوي على مفاتيح تشفير. يمكن استخدامها للتحقق بخطوتين. يجب أن تدعم مفاتيح الأمان معيار WebAuthn Authenticator. +manage_account_links_desc = هذه الحسابات الخارجية مرتبطة بحسابك في Forgejo. remove_account_link_desc = ستؤدي إزالة حساب مرتبط إلى إلغاء وصوله إلى حساب Forgejo الخاص بك. المتابعة؟ +delete_with_all_comments = حسابك أصغر من %s. لتجنب التعليقات الوهمية، سيتم حذف جميع تعليقات المشكلة/المسؤولية الشخصية معها. +delete_account_desc = هل أنت متأكد من رغبتك في حذف حساب المستخدم هذا نهائيًا؟ 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.public_tooltip = مرئي للجميع visibility.limited = محدود +visibility.limited_tooltip = مرئية فقط للمستخدمين الذين قاموا بتسجيل الدخول visibility.private = خاص -quota.applies_to_org = تنطبق قواعد الحصص التالية على هذه المنظمة +visibility.private_tooltip = مرئي فقط لأعضاء المؤسسات التي انضممت إليها +user_block_yourself = لا يمكنك حظر نفسك. +quota.applies_to_user = تنطبق قواعد الحصص التالية على حسابك +quota.rule.exceeded = تم تجاوزه +quota.rule.exceeded.helper = لقد تجاوز الحجم الإجمالي للكائنات لهذه القاعدة الحصة النسبية. quota.rule.no_limit = غير محدود quota.sizes.all = الكل quota.sizes.repos.all = المستودعات @@ -615,9 +639,6 @@ quota.sizes.assets.attachments.issues = إصدار المرفقات quota.sizes.assets.attachments.releases = تحرير المرفقات quota.sizes.assets.artifacts = التحف الفنية quota.sizes.assets.packages.all = الحزم -quota.rule.exceeded.helper = لقد تجاوز الحجم الإجمالي للكائنات لهذه القاعدة الحصة النسبية. -quota.rule.exceeded = تم تجاوزه -quota.applies_to_user = تنطبق قواعد الحصص التالية على حسابك quota.sizes.wiki = الموسوعة [org] @@ -709,8 +730,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,6 +743,7 @@ 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 = أهداف @@ -730,7 +754,7 @@ 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_no_valid_owners = لا يمكن اشتقاق هذا المستودع لعدم وجود مالك صالح. @@ -757,12 +781,12 @@ 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 = لا يوجد ملف مطابق @@ -783,6 +807,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 = أنشئ وسمًا @@ -871,6 +896,7 @@ issues.choose.ignore_invalid_templates = أُهمِلت القوالب التا branch.renamed = غُيّر اسم الفرع %s إلى %s. delete_preexisting = حذف الملفات الموجودة مسبقاً branch.included_desc = هذا الفرع جزء من الفرع المبدئي +trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع issues.reopened_at = `أعاد فتح هذه المسألة %s` issues.action_milestone = هدف issues.new.assignees = المكلَّفون @@ -879,6 +905,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 +917,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 = ألغِ @@ -948,6 +976,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 +993,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 @@ -982,12 +1012,14 @@ issues.label.filter_sort.reverse_by_size = الأكبر حجما release.message = صِف هذا الإصدار 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 = اختر باقة تصنيفات للمسائل. @@ -1147,6 +1179,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 = أُضيف المشترك. @@ -1174,6 +1207,8 @@ activity.period.quarterly = ثلاثة أشهر activity.period.semiyearly = ستة أشهر activity.period.yearly = عام واحد activity.overview = نظرة عامة +activity.active_prs_count_1 = %d طلب دمج حالي +activity.active_prs_count_n = %d طلب دمج حالي activity.merged_prs_count_1 = طلب دمج مقبول activity.merged_prs_count_n = طلب دمج مقبول activity.opened_prs_count_1 = طلب دمج مقترح @@ -1186,6 +1221,8 @@ activity.title.prs_merged_by = %s دمجها %s activity.title.prs_opened_by = %s اقترحها %s activity.merged_prs_label = مقبول activity.opened_prs_label = مقترح +activity.active_issues_count_1 = %d مسألة حالية +activity.active_issues_count_n = %d مسألة حالية activity.closed_issues_count_1 = مسألة تامة activity.closed_issues_count_n = مسألة تامة activity.title.issues_1 = %d مسألة @@ -1291,6 +1328,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 = تحرير @@ -1339,6 +1378,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 = شغّل المؤقت @@ -1370,6 +1410,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 = فعّل الإصدارات في المستودع @@ -1379,10 +1423,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 = أصدر هذا @@ -1399,10 +1445,14 @@ issues.ref_issue_from = `أشار إلى هذه المسألة % issues.ref_pull_from = `أشار إلى هذا الطلب %[3]s %[1]s` issues.ref_closing_from = `أشار إلى طلب دمج %[3]s سيغلق هذه المسألة %[1]s` issues.ref_reopening_from = `أشار إلى طلب دمج %[3]s سيعيد فتح هذه المسألة %[1]s` +issues.ref_closed_from = `أغلق هذه المسألة %[4]s %[2]s` +issues.ref_reopened_from = `أعاد فتح هذه المسألة %[4]s %[2]s` issues.reference_issue.body = المحتوى +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 = تحديث الفرع بإعادة التأسيس @@ -1415,223 +1465,234 @@ 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_from_template_description = يمكنك تحديد قالب مستودع موجود على هذا المثيل وتطبيق إعداداته. 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 = تنقية +size_format = %[1]s: %[2]s, %[3]s: %[4]s +visibility = الرؤية +visibility_description = فقط المالك أو أعضاء المؤسسة إذا كان لديهم حقوق، سيتمكنون من رؤيته. 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 = لا يمكن استخدام المصادقة المستندة إلى المفتاح العام وكلمة المرور معاً. +fork_from = اشتق من +fork_visibility_helper = لا يمكن تغيير ظهور المستودع المشتّق. +open_with_editor = افتح بـ %s +generate_repo = توليد لمستودع +generate_from = التوليد من +repo_gitignore_helper = حدد قوالب .gitignore repo_gitignore_helper_desc = اختر الملفات التي لا تريد تتبعها من قائمة القوالب الخاصة باللغات الشائعة. يتم تضمين القطع الأثرية النموذجية التي تم إنشاؤها بواسطة أدوات البناء الخاصة بكل لغة في .gitignore بشكل افتراضي. -stars = النجوم -default_branch_helper = الفرع الافتراضي هو الفرع الأساسي لطلبات السحب ،وعمليات إلإيداع. -mirror_use_ssh.helper = سيقوم Forgejo بعكس المستودع عبر Git عبر SSH وإنشاء زوج مفاتيح لك عند تحديد هذا الخيار. يجب عليك التأكد من أن المفتاح العام الذي تم إنشاؤه مخول للدفع إلى المستودع الوجهة. لا يمكنك استخدام التخويل المستند إلى كلمة المرور عند تحديد هذا الخيار. -desc.private = خاص +license_helper_desc = تحدد الرخصة ما يمكن للآخرين فعله أو عدم فعله بشيفرة برمجيتك. لست متأكدًا من الرخصة المناسبة لمشروعك؟ طالع اختيار الرخصة. readme = README +readme_helper = حدد قالب ملف README +readme_helper_desc = هذا هو المكان الذي يمكنك فيه كتابة وصف كامل لمشروعك. +auto_init = تهيئة المستودع +auto_init_description = ابدأ سجل Git بملف README، مع إمكانية إضافة ملفات الرخصة و.gitignore اختيارياً. +default_branch_label = افتراضي +default_branch_helper = الفرع الافتراضي هو الفرع الأساسي لطلبات السحب ،وعمليات إلإيداع. +mirror_prune = تنقية +mirror_prune_desc = إزالة مراجع التتبع عن بُعد القديمة 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_interval_invalid = الفاصل الزمني للمرآة غير صالح. +mirror_public_key = مفتاح SSH عام +mirror_use_ssh.text = استخدم مصادقة SSH +mirror_use_ssh.helper = سيقوم Forgejo بعكس المستودع عبر Git عبر SSH وإنشاء زوج مفاتيح لك عند تحديد هذا الخيار. يجب عليك التأكد من أن المفتاح العام الذي تم إنشاؤه مخول للدفع إلى المستودع الوجهة. لا يمكنك استخدام التخويل المستند إلى كلمة المرور عند تحديد هذا الخيار. +mirror_use_ssh.not_available = المصادقة عبر SSH غير متاحة. +mirror_denied_combination = لا يمكن استخدام المصادقة المستندة إلى المفتاح العام وكلمة المرور معاً. mirror_sync_on_commit = المزامنة عند دفع الإيداعات -mirror_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 = استنساخ عبر URL +mirror_address_desc = ضع أي بيانات اعتماد مطلوبة في قسم المصادقة. mirror_address_protocol_invalid = عنوان URL المقدم غير صالح. يمكن استخدام مواقع http(s):// أو git:// فقط للنسخ المتطابق. +mirror_lfs = تخزين الملفات الكبيرة (LFS) +mirror_lfs_desc = تنشيط النسخ المتطابق لبيانات LFS. mirror_lfs_endpoint = نقطة نهاية LFS mirror_lfs_endpoint_desc = ستحاول المزامنة استخدام عنوان url المستنسخ إلى تحديد خادم LFS. يمكنك أيضًا تحديد نقطة نهاية مخصصة إذا كانت بيانات LFS المستودع مخزنة في مكان آخر. +mirror_last_synced = آخر مزامنة +mirror_password_placeholder = (لم يتم تعديله) mirror_password_blank_placeholder = (بلا تعيين) +mirror_password_help = تغيير اسم المستخدم لمسح كلمة المرور المخزنة. +watchers = المراقبون +stargazers = المميِّزون بنجمة +stars_remove_warning = سيؤدي ذلك إلى إزالة جميع النجوم من هذا المستودع. +stars = النجوم +reactions_more = و %d أكثر +language_other = أُخرى +adopt_search = أدخل اسم المستخدم للبحث عن مستودعات غير معتمدة... (اتركه فارغاً للعثور على الكل) +adopt_preexisting_label = اعتماد الملفات +adopt_preexisting = اعتماد الملفات الموجودة مسبقاً +adopt_preexisting_content = إنشاء مستودع من %s +adopt_preexisting_success = الملفات المعتمدة والمستودع الذي تم إنشاؤه من %s 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 = داخلي +blame.ignore_revs.failed = فشل تجاهل المراجعات في .git-blame-ignore-revs. +author_search_tooltip = عرض كحد أقصى 30 مستخدمًا 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 أكثر +transfer.accept_desc = النقل إلى ”%s“ +transfer.reject = رفض النقل +transfer.reject_desc = إلغاء النقل إلى ”%s“ +transfer.no_permission_to_accept = لا تملك الصلاحية لقبول هذا النقل. +transfer.no_permission_to_reject = لا تملك الصلاحية لرفض هذا النقل. +desc.private = خاص +desc.public = عام +desc.template = قالب +desc.internal = داخلي +desc.archived = مؤرشف +template.items = عناصر القالب +template.git_content = محتوى Git (الفرع الافتراضي) +template.git_hooks_tooltip = يتعذر عليك حاليًا تعديل أو إزالة خطافات Git بمجرد إضافتها. حدد هذا فقط إذا كنت تثق بمستودع القالب. +template.topics = المواضيع +template.issue_labels = وسوم الإبلاغات +template.one_item = يجب على الأقل تحديد عنصر قالب واحد template.invalid = يجب تحديد مستودع القوالب -adopt_search = أدخل اسم المستخدم للبحث عن مستودعات غير معتمدة... (اتركه فارغاً للعثور على الكل) -adopt_preexisting = اعتماد الملفات الموجودة مسبقاً -language_other = أُخرى -adopt_preexisting_label = اعتماد الملفات +archive.title = تمت أرشفة هذا المستودع. يمكنك عرض الملفات واستنساخه، لكن لا يمكنك إجراء أي تغييرات على حالته، مثل دفع وإنشاء بلاغات أو طلبات سحب أو تعليقات جديدة. +archive.title_date = تمت أرشفة هذا المستودع على %s. يمكنك عرض الملفات واستنساخه، لكن لا يمكنك إجراء أي تغييرات على حالته، مثل دفع وإنشاء بلاغات أو طلبات سحب أو تعليقات جديدة. +archive.nocomment = التعليق غير ممكن لأن المستودع تمت أرشفته. +archive.pull.noreview = هذا المستودع مؤرشف. لا يمكنك مراجعة طلبات السحب. +sync_fork.branch_behind_one = هذا الفرع هو %[1]d إيداع خلف %[2]s +sync_fork.branch_behind_few = هذا الفرع هو %[1]d إيداعات خلف %[2]s +sync_fork.button = مزامنة form.reach_limit_of_creation_1 = لقد وصل المالك بالفعل إلى الحد الأقصى للمستودع %d. +form.reach_limit_of_creation_n = لقد وصل المالك بالفعل إلى الحد الأقصى للمستودعات %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 = المسار المحلي غير صالح. فهو غير موجود أو ليس مجلداً. +form.name_pattern_not_allowed = النمط ”%s“ غير مسموح به في اسم المستودع. +form.string_too_long = السلسلة المحددة أطول من d حرفاً. +need_auth = المصادقة +migrate_options = خيارات الترحيل +migrate_options_mirror_helper = سيكون هذا المستودع مرآة +migrate_options_lfs = ترحيل ملفات LFS +migrate_options_lfs_endpoint.label = نقطة نهاية LFS +migrate_options_lfs_endpoint.description = سيحاول الترحيل استخدام مسار Git البعيد (remote) لـ تحديد خادم LFS. يمكنك أيضًا تحديد نقطة نهاية مخصصة إذا كانت بيانات LFS للمستودع مخزنة في مكان آخر. +migrate_options_lfs_endpoint.description.local = يتم دعم مسار الخادم المحلي أيضاً. +migrate_options_lfs_endpoint.placeholder = إذا تُركت فارغة، سيتم اشتقاق نقطة النهاية من عنوان URL المستنسخ +migrate_items = عناصر الترحيل +migrate_repo = ترحيل المستودع +migrate.repo_desc_helper = اتركه فارغاً لاستيراد الوصف الموجود +migrate.clone_address = ترحيل / استنساخ من عنوان URL +migrate.clone_address_desc = رابط HTTP(S) أو Git لاستنساخ مستودع موجود +migrate.clone_local_path = أو مسار خادم محلي migrate.permission_denied = لا يُسمح لك باستيراد المستودعات المحلية. +migrate.permission_denied_blocked = لا يمكنك الاستيراد من مضيفين غير مسموح بهم، يُرجى الطلب من المسؤول التحقق من إعدادات ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. +migrate.invalid_local_path = المسار المحلي غير صالح. فهو غير موجود أو ليس مجلداً. +migrate.invalid_lfs_endpoint = نقطة نهاية LFS غير صالحة. migrate.failed = أخفق الترحيل: %v migrate.migrate_items_options = رمز الوصول الفريد مطلوب لترحيل العناصر الإضافية migrated_from = تم الترحيل من %[2]s -mirror_from = مرآة لـ -forked_from = مشتقّ من -create_new_repo_command = إنشاء مستودع جديد على موجّه الأوامر -fork = اشتقاق +migrated_from_fake =تم الترحيل من %[1]s +migrate.migrate = الترحيل من %s +migrate.migrating = الترحيل من %s … +migrate.migrating_failed = فشل الترحيل من %s. +migrate.migrating_failed.error = أخفق الترحيل: %s migrate.migrating_failed_no_addr = أخفق الترحيل. -unstar = إزالة النجمة +migrate.github.description = ترحيل البيانات من github.com أو خادم GitHub Enterprise. +migrate.git.description = ترحيل مستودع فقط من أي خدمة Git. +migrate.gitlab.description = ترحيل البيانات من gitlab.com أو مثيلات GitLab الأخرى. +migrate.gitea.description = ترحيل البيانات من gitea.com أو مثيلات Gitea الأخرى. +migrate.gogs.description = ترحيل البيانات من notabug.org أو مثيلات Gogs الأخرى. +migrate.onedev.description = ترحيل البيانات من code.onedev.io أو مثيلات OneDev الأخرى. +migrate.codebase.description = ترحيل البيانات من codebasehq.com. +migrate.gitbucket.description = ترحيل البيانات من مثيلات GitBucket. +migrate.migrating_git = ترحيل بيانات Git +migrate.migrating_topics = ترحيل المواضيع +migrate.migrating_labels = ترحيل الوسوم +migrate.migrating_releases = ترحيل الإصدارات migrate.migrating_issues = ترحيل البلاغات migrate.migrating_pulls = ترحيل طلبات السحب +migrate.cancel_migrating_title = إلغاء الترحيل +migrate.cancel_migrating_confirm = تريد إلغاء عملية الترحيل هذه؟ +mirror_from = مرآة لـ +forked_from = مشتقّ من +generated_from = تم توليده من +fork_from_self = لا يمكنك اشتقاق مستودع تملكه. +fork_guest_user = سجّل الدخول لاشتقاق هذا المستودع. +watch_guest_user = سجّل الدخول لمشاهدة هذا المستودع. +star_guest_user = قم بتسجيل الدخول لإعطاء نجمة لهذا المستودع. +subscribe.issue.guest.tooltip = سجّل الدخول للاشتراك في هذا البلاغ. +subscribe.pull.guest.tooltip = سجّل الدخول للاشتراك في طلب السحب هذا. +watch = شاهد +unwatch = إلغاء المشاهدة +star = نجمة +unstar = إزالة النجمة +fork = اشتقاق +more_operations = المزيد من العمليات +no_desc = لا يوجد وصف +quick_guide = دليل سريع +clone_this_repo = استنسخ هذا المستودع +cite_this_repo = الاستشهاد بهذا المستودع +create_new_repo_command = إنشاء مستودع جديد على موجّه الأوامر +push_exist_repo = دفع مستودع موجود من موجّه الأوامر +empty_message = لا يحتوي هذا المستودع على أي محتوى. +broken_message = لا يمكن قراءة بيانات Git التي يستند إليها هذا المستودع. اتصل بمسؤول هذا المثيل أو احذف هذا المستودع. code = الكود -migrate.invalid_lfs_endpoint = نقطة نهاية LFS غير صالحة. -migrate.migrating_failed = فشل الترحيل من %s. -branch = فرع -template.git_hooks_tooltip = يتعذر عليك حاليًا تعديل أو إزالة خطافات Git بمجرد إضافتها. حدد هذا فقط إذا كنت تثق بمستودع القالب. code.desc = الوصول إلى الشيفرة المصدرية والملفات والالتزامات والفروع. +branch = فرع 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 فرع +project = المشارييع 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_commit_few = %s إيداعات +n_branch_one = %s فرع n_branch_few = %s فروع n_tag_one = %s علامة n_tag_few = ‪%s علامات +n_release_one = %s إصدار n_release_few = %s إصدارات file.title = %s عند %s -vendored = مضمن file_follow = متابعة الروابط الرمزية +file_view_rendered = عرض المُخرج النهائي +view_git_blame = عرض مسؤول تعديل git +stored_lfs = مخزن مع Git LFS +symbolic_link = رابط رمزي +vendored = مضمن +commit_graph = الرسم البياني للإيداع +commit.contained_in = هذا الإيداع موجود في: +commit.contained_in_default_branch = هذا الإيداع جزء من الفرع الافتراضي +normal_view = عرض عادي +line = سطر +lines = أسطر +from_comment = (تعليق) +no_eol.text = لا EOL +no_eol.tooltip = هذا الملف لا يحتوي على نهاية لخط الشخصية. +editor.edit_file = عدّل الملف +editor.cannot_edit_lfs_files = لا يمكن تحرير ملفات LFS في واجهة الويب. +editor.edit_this_file = عدل الملف +editor.this_file_locked = الملف مقفل +editor.file_delete_success = تم حذف الملف "%s". +editor.name_your_file = اسم ملفك… +editor.add_tmpl.filename = اسم الملف +editor.signoff_desc = أضف مقطورة موقّعة من قِبل المُجرّد في نهاية رسالة سجل الدخول. +editor.propose_file_change = اقتراح تغيير الملف +editor.new_branch_name = تسمية الفرع الجديد لهذا الإيداع [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: @@ -1642,6 +1703,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. @@ -1660,32 +1722,35 @@ 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 = تم تغيير كلمة مرورك +password_change.text_1 = تم تغيير كلمة مرور حسابك للتو. +primary_mail_change.subject = تم تغيير البريد الأساسي الخاص بك +primary_mail_change.text_1 = تم تغيير البريد الإلكتروني الأساسي لحسابك إلى %[1]s. هذا يعني أن عنوان البريد الإلكتروني هذا لن يتلقى إشعارات البريد لحسابك بعد الآن. totp_disabled.subject = تم تعطيل TOTP totp_disabled.text_1 = تم تعطيل كلمة المرور لمرة واحدة المستندة إلى الوقت (TOTP) على حسابك للتو. -totp_enrolled.text_1.has_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يمكنك استخدام TOTP كطريقة للمصادقة الثنائية ، أو استخدام أي من مفاتيح الأمان الخاصة بك. -totp_enrolled.subject = لقد قمت بتشيط TOTP كطريقة 2FA +totp_disabled.no_2fa = لم تعد هناك طرق أُخرى للمصادقة الثنائية (2FA) قيد التهيئة عد الآن ، أي أنه لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (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_1 = إذا كان هذا أنت، فيمكنك تجاهل هذا البريد بأمان. account_security_caution.text_2 = إذا لم تكن أنت، فهذا يعني أن حسابك مخترق. يرجى الاتصال بمسؤولي هذا الموقع. +totp_enrolled.subject = لقد قمت بتشيط TOTP كطريقة 2FA totp_enrolled.text_1.no_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يجب عليك استخدام TOTP كطريقة للمصادقة الثنائية. +totp_enrolled.text_1.has_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يمكنك استخدام TOTP كطريقة للمصادقة الثنائية ، أو استخدام أي من مفاتيح الأمان الخاصة بك. [error] not_found = تعذر العثور على الهدف. report_message = إن كنت متيقِّنًا أن هذه علة في فورجيو، رجاءً ابحث في كودبيرج أو افتح مسأله جديدة إذا لزم الأمر. network_error = خطأ في الشبكة +invalid_csrf = طلب سيئ: رمز CSRF غير صالح occurred = حدث خطأ +missing_csrf = طلب سيئ: لا يوجد رمز CSRF server_internal = خطأ داخلي في الخادم [startpage] @@ -1726,19 +1791,21 @@ 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.one = متابعة following.title.few = متابعة +followers_one = %d متابِع +following_one = %d يُتابع public_activity.visibility_hint.self_public = نشاطك مرئي للجميع، باستثناء التفاعلات في المساحات الخاصة. اضبط الإعدادات. public_activity.visibility_hint.admin_public = هذا النشاط مرئي للجميع، ولكن بصفتك مسؤولاً يمكنك أيضًا رؤية التفاعلات في المساحات الخاصة. +public_activity.visibility_hint.self_private = نشاطك مرئي لك ولسُعاة المثيل فقط. تعديل الإعدادات. +public_activity.visibility_hint.admin_private = هذا النشاط مرئي لك لأنك مسؤول، ولكن المستخدم يريد أن يظل خاصاً. public_activity.visibility_hint.self_private_profile = نشاطك مرئي لك ولسُعاة المثيل فقط لأن ملفك الشخصي خاص. تعديل الإعدادات. [auth] @@ -1750,21 +1817,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 = أرسل بريد الاستعادة resend_mail = اضغط هنا لإعادة إرسالة رسالة تفعيل حسابك has_unconfirmed_mail = أهلا يا %s، لديك عنوان بريد إلكتروني غير مؤكَّد (%s). إن لم تستلم رسالة تأكيد أو تريد إرسال واحدة جديدة، فنرجو الضغط على الزر الذي بالأسفل. +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 = فشل طلب الإذن لأن خادم التوثيق غير متاح مؤقتا. حاول مرة أخرى لاحقاً. @@ -1795,26 +1867,29 @@ 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 لمزيد من المعلومات +use_onetime_code = استخدم رمزًا لمرة واحدة +back_to_sign_in = العودة إلى تسجيل الدخول +sign_in_openid = المتابعة باستخدام OpenID [packages] rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة. @@ -1858,6 +1933,7 @@ less = أقل number_of_contributions_in_the_last_12_months = %s مساهم في آخر 12 شهر contributions_zero = بلا مساهمات more = أكثر + contributions_format = {contributions} مساهمة في {day} {month} {year} contributions_one = المساهمة contributions_few = المساهمات @@ -1932,6 +2008,7 @@ packages.repository = المستودع orgs.teams = الفِرق packages.size = الحجم users.max_repo_creation = العدد الأقصى للمستودعات +repos.forks = الاشتقاقات orgs.new_orga = منظمة جديدة users.allow_import_local = يستطيع استيراد مستودعات محلية repos.name = الاسم @@ -2024,6 +2101,7 @@ email_invalid = عنوان البريد غير صالح. CommitSummary = خلاصة الإيداع team_not_exist = الفريق غير موجود. TreeName = مسار الملف +SSPIDefaultLanguage = اللغة المبدئية password_complexity = كلمة المرور ليست بالتعقيد المطلوب: password_not_match = لا تتطابق كلمتا المرور. still_has_org = "حسابك عضو في منظمة أو أكثر؛ غادرهم أولا." @@ -2032,22 +2110,26 @@ repository_files_already_exist.delete = الملفات موجودة بالفعل repository_files_already_exist.adopt = الملفات موجودة بالفعل لهذا المستودع ويمكن اعتمادها فقط. repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. اتصل بمدير النظام. TeamName = اسم الفريق +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".` @@ -2056,17 +2138,18 @@ 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. +Biography = النبذة +Website = موقع الويب Location = الموقع +To = اسم الفرع +AccessToken = رمز الوصول invalid_group_team_map_error = ` التعيين غير صالح: %s ` +username_claiming_cooldown = لا يمكن المطالبة باسم المستخدم، لأن فترة تباطؤه لم تنتهِ بعد. يمكن المطالبة به عند %[1]s. +repository_force_private = وضع الخاص الإجباري مفعّل: لا يمكن تحويل المستودعات الخاصة إلى عامة. visit_rate_limit = تناولت الزيارة عن بُعد الحد من معدلها. email_domain_is_not_allowed = نطاق البريد الإلكتروني للمستخدم %s يتعارض مع قائمة النطاقات المسموحة ، أو الممنوعة. يرجى التأكد من إدخال عنوان البريد الإلكتروني بشكل صحيح. unset_password = المستخدم المسجل لم يقم بتعيين كلمة مرور. @@ -2077,32 +2160,54 @@ 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_one = %d نجمة +stars_few = %d نجوم +forks_one = %d نسخة +forks_few = %d نُسَخ + [actions] variables.none = لا توجد متغيرات بعد. variables.deletion = أزل المتغير @@ -2135,6 +2240,7 @@ runners.labels = التصنيفات runners.status.unspecified = مجهول runs.commit = إيداع status.success = "نجح" +runs.no_workflows.documentation =لمعرفة المزيد عن إجراءات فورجيو، برجاء رؤية التوثيق. runs.empty_commit_message = (رسالة إيداع فارغة) status.cancelled = "ملغي" runs.status_no_select = كل الحالات @@ -2278,6 +2384,8 @@ search = البحث… type_tooltip = نوع البحث fuzzy = أجعد fuzzy_tooltip = قم بتضمين النتائج التي تتطابق أيضًا مع مصطلح البحث بشكل وثيق +match = تتناسب +match_tooltip = قم بتضمين النتائج التي تطابق مصطلح البحث المحدد فقط repo_kind = بحث في المستودعات… user_kind = بحث عن المستخدمين… team_kind = بحث عن الفرق… @@ -2288,12 +2396,12 @@ no_results = لا توجد نتائج مطابقة. issue_kind = البحث ضمن الأعطال… pull_kind = البحث ضمن طلبات السحب… keyword_search_unavailable = البحث من خلال الكلمات المفتاحية ليس متوفر حالياً. رجاءاً تواصل مع مشرف الموقع. -package_kind = البحث ضمن الحزم… -regexp_tooltip = تعامل مع عبارة البحث على أنها تعبير نمطي -commit_kind = البحث ضمن الإيداعات… union = مطابقة عامة -runner_kind = البحث ضمن المشغِّلات… +union_tooltip = عرض النتائج التي تطابق أي من الكلمات المفتاحية المفصولة بمسافات exact = مطابق exact_tooltip = عرض النتائج التي تطابق مصطلح البحث بالضبط فقط regexp = RegExp -union_tooltip = عرض النتائج التي تطابق أي من الكلمات المفتاحية المفصولة بمسافات +regexp_tooltip = تعامل مع عبارة البحث على أنها تعبير نمطي +package_kind = البحث ضمن الحزم… +commit_kind = البحث ضمن الإيداعات… +runner_kind = البحث ضمن المشغِّلات… diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index b8c11a6807..35e33f4430 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,6 +141,7 @@ webauthn_sign_in = Натиснете бутона на вашия ключ за webauthn_error = Неуспешно прочитане на вашия ключ за сигурност. webauthn_unsupported_browser = Вашият браузър в момента не поддържа WebAuthn. webauthn_error_duplicated = Ключът за сигурност не е разрешен за тази заявка. Моля, уверете се, че ключът не е вече регистриран. + tracked_time_summary = Обобщение на проследеното време въз основа на филтрите в списъка със задачи [settings] @@ -151,9 +157,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 +171,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 +195,7 @@ biography_placeholder = Разкажете на другите малко за orgs = Организации continue = Продължаване blocked_users = Блокирани потребители +emails = Адреси на ел. поща update_profile = Обновяване на профила profile = Профил change_password = Промяна на паролата @@ -218,6 +230,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 +280,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 приложение @@ -357,7 +373,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 +382,20 @@ hidden_comment_types = Скрити типове коментари comment_type_group_lock = Състояние на заключване can_not_add_email_activations_pending = Има чакаща активация, опитайте отново след няколко минути, ако искате да добавите нова ел. поща. storage_overview = Преглед на съхранението + webauthn = Двуфакторно удостоверяване (Ключове за сигурност) +quota.sizes.all = Всички +quota.sizes.repos.all = Хранилища quota.sizes.repos.public = Публични хранилища quota.sizes.repos.private = Частни хранилища quota.sizes.git.all = Git съдържание quota.sizes.git.lfs = Git LFS quota.sizes.assets.attachments.all = Прикачени файлове +quota.sizes.assets.attachments.issues = Прикачени файлове към задачи quota.sizes.assets.attachments.releases = Прикачени файлове към издания quota.sizes.assets.artifacts = Артефакти quota.sizes.assets.packages.all = Пакети quota.sizes.wiki = Уики -quota.sizes.all = Всички -quota.sizes.repos.all = Хранилища -quota.sizes.assets.attachments.issues = Прикачени файлове към задачи [packages] container.labels.value = Стойност @@ -437,112 +454,113 @@ 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 беше успешно преизграден. + +desc = Управление на пакетите на хранилището. +alpine.registry = Настройте този регистър, като добавите URL адреса във вашия файл /etc/apk/repositories: alpine.registry.key = Изтеглете публичния RSA ключ на регистъра в папката /etc/apk/keys/, за да проверите подписа на индекса: alpine.registry.info = Изберете $branch и $repository от списъка по-долу. +alpine.install = За да инсталирате пакета, изпълнете следната команда: +arch.version.properties = Свойства на версията +arch.version.makedepends = Зависимости за изграждането arch.version.checkdepends = Зависимости за проверката +chef.registry = Настройте този регистър във вашия файл ~/.chef/config.rb: +chef.install = За да инсталирате пакета, изпълнете следната команда: +composer.registry = Настройте този регистър във вашия файл ~/.composer/config.json: +composer.install = За да инсталирате пакета с Composer, изпълнете следната команда: composer.dependencies = Зависимости -swift.install = Добавете пакета във вашия файл Package.swift: -settings.link.error = Неуспешно обновяване на връзката на хранилището. -swift.install2 = и изпълнете следната команда: -rpm.repository.multiple_groups = Този пакет е наличен в няколко групи. +conan.details.repository = Хранилище +conan.registry = Настройте този регистър от командния ред: +conan.install = За да инсталирате пакета с Conan, изпълнете следната команда: 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 = Настройте този регистър от командния ред: +container.pull = Издърпайте образа от командния ред: +container.multi_arch = ОС / Архитектура +container.layers = Слоеве на образа +cran.registry = Настройте този регистър във вашия файл Rprofile.site: +cran.install = За да инсталирате пакета, изпълнете следната команда: 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 = Целева платформа +debian.install = За да инсталирате пакета, изпълнете следната команда: +debian.repository = Информация за хранилището +debian.repository.distributions = Дистрибуции +debian.repository.components = Компоненти +debian.repository.architectures = Архитектури +helm.registry = Настройте този регистър от командния ред: +helm.install = За да инсталирате пакета, изпълнете следната команда: +maven.registry = Настройте този регистър във файла на вашия проект pom.xml: +maven.install = За да използвате пакета, включете следното в блока dependencies във файла pom.xml: maven.install2 = Изпълнете през командния ред: maven.download = За да изтеглите зависимостта, изпълнете през командния ред: -container.layers = Слоеве на образа -conan.install = За да инсталирате пакета с Conan, изпълнете следната команда: -composer.registry = Настройте този регистър във вашия файл ~/.composer/config.json: +nuget.registry = Настройте този регистър от командния ред: +nuget.install = За да инсталирате пакета с NuGet, изпълнете следната команда: +nuget.dependency.framework = Целева платформа +npm.registry = Настройте този регистър във файла на вашия проект .npmrc: +npm.install = За да инсталирате пакета с npm, изпълнете следната команда: +npm.install2 = или го добавете във файла package.json: +npm.dependencies.optional = Опционални зависимости +npm.details.tag = Маркер +pub.install = За да инсталирате пакета с Dart, изпълнете следната команда: +pypi.requires = Изисква Python +pypi.install = За да инсталирате пакета с pip, изпълнете следната команда: +rpm.registry = Настройте този регистър от командния ред: +rpm.distros.redhat = на дистрибуции, базирани на RedHat +rpm.distros.suse = на дистрибуции, базирани на SUSE +rpm.install = За да инсталирате пакета, изпълнете следната команда: +rpm.repository = Информация за хранилището +rpm.repository.architectures = Архитектури +rpm.repository.multiple_groups = Този пакет е наличен в няколко групи. +alt.registry = Настройте този регистър от командния ред: +alt.registry.install = За да инсталирате пакета, изпълнете следната команда: +alt.install = Инсталиране на пакет +alt.setup = Добавете хранилище към списъка със свързани хранилища (изберете необходимата архитектура вместо „_arch_“): +alt.repository = Информация за хранилището +alt.repository.architectures = Архитектури +alt.repository.multiple_groups = Този пакет е наличен в няколко групи. +swift.registry = Настройте този регистър от командния ред: +swift.install = Добавете пакета във вашия файл Package.swift: +swift.install2 = и изпълнете следната команда: +vagrant.install = За да добавите Vagrant box, изпълнете следната команда: +settings.link = Свързване на този пакет с хранилище +settings.link.description = Ако свържете пакет с хранилище, пакетът се изброява в списъка с пакети на хранилището. +settings.link.select = Изберете хранилище +settings.link.button = Обновяване на връзката на хранилището +settings.link.success = Връзката на хранилището беше успешно обновена. +settings.link.error = Неуспешно обновяване на връзката на хранилището. +settings.delete.description = Изтриването на пакет е трайно и не може да бъде отменено. +settings.delete.notice = На път сте да изтриете %s (%s). Тази операция е необратима, сигурни ли сте? +owner.settings.cargo.title = Индекс на регистъра на Cargo +owner.settings.cargo.initialize = Инициализиране на индекс +owner.settings.cargo.initialize.description = Необходимо е специално Git хранилище за индекс, за да се използва регистърът на Cargo. Използването на тази опция ще (пре)създаде хранилището и ще го конфигурира автоматично. +owner.settings.cargo.initialize.error = Неуспешно инициализиране на индекса на Cargo: %v +owner.settings.cargo.initialize.success = Индексът на Cargo беше успешно създаден. +owner.settings.cargo.rebuild = Преизграждане на индекс +owner.settings.cargo.rebuild.description = Преизграждането може да бъде полезно, ако индексът не е синхронизиран със съхранените Cargo пакети. +owner.settings.cargo.rebuild.error = Неуспешно преизграждане на индекса на Cargo: %v +owner.settings.cargo.rebuild.success = Индексът на Cargo беше успешно преизграден. +owner.settings.cargo.rebuild.no_index = Не може да се преизгради, няма инициализиран индекс. +owner.settings.cleanuprules.title = Правила за почистване +owner.settings.cleanuprules.add = Добавяне на правило за почистване +owner.settings.cleanuprules.edit = Редактиране на правилото за почистване +owner.settings.cleanuprules.none = Все още няма правила за почистване. +owner.settings.cleanuprules.preview = Преглед на правило за почистване +owner.settings.cleanuprules.preview.overview = %d пакета са насрочени за премахване. +owner.settings.cleanuprules.preview.none = Правилото за почистване не съвпада с нито един пакет. +owner.settings.cleanuprules.enabled = Включено +owner.settings.cleanuprules.pattern_full_match = Прилагане на шаблона към пълното име на пакета +owner.settings.cleanuprules.keep.title = Версиите, които съответстват на тези правила, се запазват, дори ако съответстват на правило за премахване по-долу. +owner.settings.cleanuprules.keep.count = Запазване на най-новите +owner.settings.cleanuprules.keep.count.1 = 1 версия на пакет +owner.settings.cleanuprules.keep.count.n = %d версии на пакет +owner.settings.cleanuprules.keep.pattern = Запазване на версии, съответстващи на +owner.settings.cleanuprules.keep.pattern.container = Версията latest винаги се запазва за Container пакети. +owner.settings.cleanuprules.remove.title = Версиите, които съответстват на тези правила, се премахват, освен ако правило по-горе не казва да се запазят. +owner.settings.cleanuprules.remove.days = Премахване на версии, по-стари от +owner.settings.cleanuprules.remove.pattern = Премахване на версии, съответстващи на +owner.settings.cleanuprules.success.update = Правилото за почистване е обновено. +owner.settings.cleanuprules.success.delete = Правилото за почистване е изтрито. +owner.settings.chef.title = Регистър на Chef +owner.settings.chef.keypair = Генериране на двойка ключове +owner.settings.chef.keypair.description = Заявките, изпратени до регистъра на Chef, трябва да бъдат криптографски подписани като средство за удостоверяване. При генериране на двойка ключове, само публичният ключ се съхранява във Forgejo. Частният ключ ви се предоставя, за да се използва с knife. Генерирането на нова двойка ключове ще презапише предишната. [tool] hours = %d часа @@ -613,6 +631,7 @@ release.source_code = Програмен код settings = Настройки forks = Разклонения editor.or = или +search = Търсене issues.new_label_desc_placeholder = Описание watch_guest_user = Влезте, за да наблюдавате това хранилище. migrate_items_milestones = Етапи @@ -655,6 +674,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 +691,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 етикета @@ -761,6 +782,8 @@ activity.title.user_n = %d потребители activity.title.prs_n = %d заявки за сливане activity.merged_prs_count_1 = Слята заявка за сливане activity.merged_prs_count_n = Слети заявки за сливане +activity.active_issues_count_1 = %d активна задача +activity.active_issues_count_n = %d активни задачи activity.closed_issues_count_1 = Затворена задача activity.closed_issues_count_n = Затворени задачи activity.title.issues_1 = %d задача @@ -768,15 +791,18 @@ 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 = Изтриване на страницата activity.title.prs_1 = %d заявка за сливане +activity.active_prs_count_n = %d активни заявки за сливане activity.period.filter_label = Период: activity.period.daily = 1 ден activity.period.halfweekly = 3 дни activity.period.weekly = 1 седмица activity.period.yearly = 1 година +activity.active_prs_count_1 = %d активна заявка за сливане wiki.page_title = Заглавие на страницата wiki.page_content = Съдържание на страницата wiki.filter_page = Филтриране на страница @@ -828,6 +854,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 = Данните на уикито на хранилището са изтрити. @@ -873,12 +901,14 @@ pulls.reopened_at = `отвори наново тази заявка за сли issues.reopened_at = `отвори наново тази задача %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 = Отваряне наново с коментар @@ -963,12 +993,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 = Публично @@ -1049,6 +1081,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 = Публикуване на издание @@ -1101,7 +1134,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 = `(изтрит)` @@ -1130,11 +1165,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 = Показване на всички промени @@ -1149,11 +1186,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 = Прегледано @@ -1189,6 +1227,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 = Домейн @@ -1214,6 +1253,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 = Добавяне на екип @@ -1227,6 +1267,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 @@ -1234,10 +1276,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 = Всички проверки са успешни @@ -1252,7 +1296,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 = е изтласкал @@ -1293,6 +1339,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 = Създаване на клон @@ -1362,10 +1409,13 @@ 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“ archive.title_date = Това хранилище е архивирано на %s. Можете да преглеждате файлове и да го клонирате, но не можете да правите промени в състоянието му, като изтласкване и създаване на нови задачи, заявки за сливане или коментари. +release.download_count_one = %s изтегляне +release.download_count_few = %s изтегляния branch.restore_success = Клонът „%s“ е възстановен. tag.create_tag_from = Създаване на нов маркер от „%s“ branch.create_new_branch = Създаване на клон от клон: @@ -1543,16 +1593,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 куките. @@ -1573,6 +1627,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 @@ -1581,6 +1636,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 = Тези клонове са равни. Тази заявка за сливане ще бъде празна. @@ -1602,14 +1660,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 = Опции за мигрирането @@ -1639,176 +1698,178 @@ 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 = Всички заявки за сливане +admin.update_flags = Обновяване на флаговете +admin.failed_to_replace_flags = Неуспешна замяна на флаговете на хранилището +admin.flags_replaced = Флаговете на хранилището са заменени 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) +mirror_interval_invalid = Интервалът на огледалото не е валиден. +mirror_use_ssh.text = Използване на SSH удостоверяване +mirror_use_ssh.helper = Forgejo ще създаде огледало на хранилището чрез Git през SSH и ще генерира двойка ключове за вас, когато изберете тази опция. Трябва да се уверите, че генерираният публичен ключ е упълномощен да изтласква към целевото хранилище. Не можете да използвате удостоверяване, базирано на парола, когато избирате това. +mirror_use_ssh.not_available = SSH удостоверяването не е налично. +mirror_denied_combination = Не може да се използва удостоверяване с публичен ключ и парола едновременно. +mirror_sync_on_commit = Синхронизиране при изтласкване на подавания +mirror_address_desc = Поставете всички необходими данни за удостоверяване в секцията „Упълномощаване“. +mirror_address_url_invalid = Предоставеният URL е невалиден. Трябва да екранирате правилно всички компоненти на URL адреса. +mirror_address_protocol_invalid = Предоставеният URL е невалиден. Само http(s):// или git:// адреси могат да се използват за огледални хранилища. +mirror_lfs = Съхранение на големи файлове (LFS) +mirror_password_help = Променете потребителското име, за да изтриете запазена парола. +unit_disabled = Администраторът на сайта е изключил тази секция на хранилището. summary_card_alt = Карта с обобщение на хранилище %s +template.items = Елементи на шаблона +template.git_content = Git съдържание (стандартен клон) +template.git_hooks = Git куки +template.git_hooks_tooltip = В момента не можете да променяте или премахвате Git куки, след като са добавени. Изберете това само ако се доверявате на шаблонното хранилище. +template.one_item = Трябва да изберете поне един елемент от шаблона +template.invalid = Трябва да изберете шаблонно хранилище +migrate.cancel_migrating_title = Отказ от миграцията +migrate.cancel_migrating_confirm = Искате ли да откажете тази миграция? +invisible_runes_description = `Този файл съдържа невидими Уникод знаци, които са неразличими за хората, но могат да бъдат обработени по различен начин от компютър. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` +ambiguous_runes_header = `Този файл съдържа двусмислени Уникод знаци` +ambiguous_runes_description = `Този файл съдържа Уникод знаци, които могат да бъдат объркани с други знаци. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` file_copy_permalink = Копиране на постоянна връзка view_git_blame = Преглед на git blame -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“. +audio_not_supported_in_browser = Вашият браузър не поддържа HTML5 тага „audio“. +stored_lfs = Съхранено с Git LFS +commit_graph.select = Изберете клонове +editor.cannot_edit_lfs_files = LFS файлове не могат да се редактират в уеб интерфейса. editor.filename_help = Добавете директория, като въведете името ѝ, последвано от наклонена черта („/“). Премахнете директория, като натиснете backspace в началото на полето за въвеждане. +editor.commit_signed_changes = Подаване на подписани промени +editor.require_signed_commit = Клонът изисква подписано подаване +editor.commit_email = Ел. поща на подаването +commits.desc = Разглеждане на историята на промените в програмния код. +commits.search.tooltip = Можете да добавите префикс към ключовите думи с „author:“, „committer:“, „after:“ или „before:“, напр. „revert author:Alice before:2019-01-13“. +commits.signed_by = Подписано от +commits.signed_by_untrusted_user = Подписано от недоверен потребител +commits.signed_by_untrusted_user_unmatched = Подписано от недоверен потребител, който не съвпада с подаващия +commits.ssh_key_fingerprint = Отпечатък на SSH ключ commits.view_single_diff = Преглед на промените в този файл, въведени в това подаване +commit.revert = Връщане +commit.revert-header = Връщане: %s +commit.revert-content = Изберете клон, върху който да се върне: +issues.desc = Организирайте доклади за грешки, задачи и етапи. issues.choose.ignore_invalid_templates = Невалидните шаблони са игнорирани -issues.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.choose.invalid_config = Конфигурацията на задачите съдържа грешки: +issues.filter_type.all_pull_requests = Всички заявки за сливане +issues.role.member_helper = Този потребител е участник в организацията, притежаваща това хранилище. +issues.lock.unknown_reason = Не може да се заключи задача с неизвестна причина. +issues.lock_duplicate = Задача не може да бъде заключена два пъти. +issues.unlock_error = Не може да се отключи задача, която не е заключена. +issues.lock.notice_1 = - Други потребители не могат да добавят нови коментари към тази задача. +issues.lock.notice_2 = - Вие и други сътрудници с достъп до това хранилище все още можете да оставяте коментари, които другите да виждат. issues.lock.notice_3 = - Винаги можете да отключите тази задача отново в бъдеще. +issues.unlock.notice_1 = - Всеки ще може отново да коментира тази задача. +issues.unlock.notice_2 = - Винаги можете да заключите тази задача отново в бъдеще. issues.lock.title = Заключване на обсъждането по тази задача. +issues.unlock.title = Отключване на обсъждането по тази задача. +issues.comment_on_locked = Не можете да коментирате заключена задача. +issues.delete.text = Наистина ли искате да изтриете тази задача? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) +issues.cancel_tracking_history = `отмени проследяването на времето %s` +issues.add_time_sum_to_small = Не е въведено време. +issues.due_date_form = гггг-мм-дд +issues.due_date_invalid = Крайният срок е невалиден или извън обхвата. Моля, използвайте формата „гггг-мм-дд“. +issues.dependency.no_permission_1 = Нямате разрешение да прочетете %d зависимост +issues.dependency.no_permission_n = Нямате разрешение да прочетете %d зависимости +issues.dependency.no_permission.can_remove = Нямате разрешение да прочетете тази зависимост, но можете да я премахнете issues.dependency.issue_batch_close_blocked = Не могат да бъдат затворени групово избраните задачи, защото задача #%d все още има отворени зависимости +issues.dependency.blocked_by_short = Зависи от +issues.dependency.setting = Включване на зависимости за задачи и заявки за сливане +issues.dependency.add_error_same_issue = Не можете да направите задача зависима от самата нея. +issues.dependency.add_error_dep_issue_not_exist = Зависимата задача не съществува. issues.dependency.add_error_cannot_create_circular = Не можете да създадете зависимост с две задачи, които се блокират взаимно. +issues.dependency.add_error_dep_not_same_repo = И двете задачи трябва да са в едно и също хранилище. +issues.review.self.rejection = Не можете да поискате промени в собствената си заявка за сливане. +issues.review.dismissed = отхвърли рецензията на %s %s +issues.review.content.empty = Трябва да оставите коментар, посочващ исканите промени. issues.review.add_review_requests = поиска рецензии от %[1]s %[2]s +issues.review.remove_review_request = премахна заявката за рецензия за %[1]s %[2]s +issues.review.remove_review_requests = премахна заявките за рецензия за %[1]s %[2]s +issues.review.remove_review_request_self = отказа да рецензира %s +issues.review.pending.tooltip = Този коментар в момента не е видим за други потребители. За да изпратите изчакващите си коментари, изберете „%s“ -> „%s/%s/%s“ в горната част на страницата. +issues.review.outdated = Остарял +issues.review.outdated_description = Съдържанието е променено, след като е направен този коментар +issues.review.show_outdated = Показване на остарели +issues.review.hide_outdated = Скриване на остарели +issues.content_history.options = Опции +issues.blocked_by_user = Не можете да създавате задачи в това хранилище, защото сте блокирани от притежателя на хранилището. comment.blocked_by_user = Коментирането не е възможно, защото сте блокирани от притежателя на хранилището или от автора. +issues.reopen.blocked_by_user = Не можете да отворите наново тази задача, защото сте блокирани от притежателя на хранилището или от автора на тази задача. +compare.compare_base = основа +compare.compare_head = сравняване +pulls.desc = Включване на заявки за сливане и рецензии на код. pulls.view = Преглед на заявката за сливане -pulls.no_merge_desc = Тази заявка за сливане не може да бъде слята, защото всички опции за сливане в хранилището са изключени. -pulls.no_merge_wip = Тази заявка за сливане не може да бъде слята, защото е отбелязана като в процес на работа. -pulls.switch_comparison_type = Превключване на типа сравнение +pulls.allow_edits_from_maintainers_desc = Потребители с право на запис в основния клон могат също да изтласкват към този клон +pulls.allow_edits_from_maintainers_err = Обновяването е неуспешно pulls.has_changed_since_last_review = Променено след последната ви рецензия +pulls.switch_comparison_type = Превключване на типа сравнение pulls.filter_branch = Филтриране на клон +pulls.review_only_possible_for_full_diff = Рецензирането е възможно само при преглед на пълните разлики +pulls.wrong_commit_id = ID на подаването трябва да бъде ID на подаване в целевия клон +pulls.blocked_by_user = Не можете да създадете заявка за сливане в това хранилище, защото сте блокирани от притежателя на хранилището. +pulls.no_merge_desc = Тази заявка за сливане не може да бъде слята, защото всички опции за сливане в хранилището са изключени. +pulls.no_merge_helper = Включете опциите за сливане в настройките на хранилището или слейте заявката за сливане ръчно. +pulls.no_merge_wip = Тази заявка за сливане не може да бъде слята, защото е отбелязана като в процес на работа. pulls.squash_merge_pull_request = Създаване на сплескано подаване -pulls.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.merge_conflict = Сливането е неуспешно: Възникна конфликт по време на сливането. Подсказка: Опитайте различна стратегия +pulls.merge_conflict_summary = Съобщение за грешка +pulls.rebase_conflict_summary = Съобщение за грешка +pulls.has_merged = Неуспешно: Заявката за сливане е слята, не можете да слеете отново или да промените целевия клон. pulls.push_rejected = Изтласкването е неуспешно: Изтласкването е отхвърлено. Прегледайте Git куките за това хранилище. -pulls.auto_merge_canceled_schedule = Автоматичното сливане е отменено за тази заявка за сливане. -pulls.allow_edits_from_maintainers_err = Обновяването е неуспешно +pulls.push_rejected_no_message = Изтласкването е неуспешно: Изтласкването е отхвърлено, но няма отдалечено съобщение. Прегледайте Git куките за това хранилище +pulls.update_not_allowed = Нямате разрешение да обновявате клона +pulls.outdated_with_base_branch = Този клон е остарял спрямо основния клон +pulls.cmd_instruction_merge_warning = Предупреждение: Настройката „Автоматично откриване на ръчно сливане“ не е включена за това хранилище, ще трябва да отбележите тази заявка за сливане като ръчно слята след това. +pulls.editable_explanation = Тази заявка за сливане позволява редакции от поддържащите. Можете да допринесете директно към нея. +pulls.auto_merge_button_when_succeed = (Когато проверките са успешни) +pulls.auto_merge_when_succeed = Автоматично сливане, когато всички проверки са успешни +pulls.auto_merge_newly_scheduled = Заявката за сливане е насрочена за сливане, когато всички проверки са успешни. +pulls.auto_merge_has_pending_schedule = %[1]s насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[2]s. pulls.auto_merge_cancel_schedule = Отмяна на автоматичното сливане pulls.auto_merge_not_scheduled = Тази заявка за сливане не е насрочена за автоматично сливане. -pulls.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.auto_merge_canceled_schedule = Автоматичното сливане е отменено за тази заявка за сливане. +pulls.auto_merge_newly_scheduled_comment = `насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[1]s` +pulls.auto_merge_canceled_schedule_comment = `отмени автоматичното сливане на тази заявка за сливане, когато всички проверки са успешни %[1]s` 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.bin = ДВОИЧЕН +diff.file_suppressed_line_too_long = Разликите във файла са потиснати, защото един или повече редове са твърде дълги +diff.too_many_files = Някои файлове не бяха показани, защото твърде много файлове имат промени в тези разлики +diff.show_more = Показване на още diff.generated = генериран diff.comment.add_line_comment = Добавяне на коментар към ред diff.comment.add_review_comment = Добавяне на коментар -diff.review.self_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.review.self_approve = Авторите на заявки за сливане не могат да одобряват собствените си заявки diff.image.side_by_side = Едно до друго -release.summary_card_alt = Карта с обобщение на издание със заглавие „%s“ в хранилище %s +diff.image.swipe = Плъзгане +diff.image.overlay = Наслагване +diff.has_escaped = Този ред има скрити Уникод знаци +release.tag_name_protected = Името на маркера е защитено. +release.add_tag_msg = Използване на заглавието и съдържанието на изданието като съобщение на маркера. +release.hide_archive_links = Скриване на автоматично генерираните архиви +release.hide_archive_links_helper = Скрийте автоматично генерираните архиви с програмен код за това издание. Например, ако качвате свои собствени. release.asset_external_url = Външен URL адрес +release.summary_card_alt = Карта с обобщение на издание със заглавие „%s“ в хранилище %s +branch.protected_deletion_failed = Клонът „%s“ е защитен. Не може да бъде изтрит. +branch.default_deletion_failed = Клонът „%s“ е стандартният клон. Не може да бъде изтрит. +branch.included_desc = Този клон е част от стандартния клон +branch.included = Включен +branch.warning_rename_default_branch = Преименувате стандартния клон. +topic.count_prompt = Не можете да изберете повече от 25 теми +find_file.no_matching = Не е намерен съвпадащ файл error.csv.too_large = Не може да се визуализира този файл, защото е твърде голям. +error.csv.unexpected = Не може да се визуализира този файл, защото съдържа неочакван знак на ред %d и колона %d. +error.csv.invalid_field_count = Не може да се визуализира този файл, защото има грешен брой полета на ред %d. +error.broken_git_hook = Git куките на това хранилище изглеждат повредени. Моля, последвайте документацията, за да ги поправите, след което изтласкайте подавания, за да обновите статуса. [modal] confirm = Потвърждаване @@ -1819,7 +1880,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 = Добавяне на неномериран списък @@ -1828,7 +1889,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 = Препратка към задача или заявка за сливане @@ -1881,7 +1942,7 @@ org_full_name_holder = Пълно име на организацията teams = Екипи lower_members = участници lower_repositories = хранилища -settings.repoadminchangeteam = Админ. на хранилище да може да добавя и премахва достъп за екипи +settings.repoadminchangeteam = Админ. на хранилището да може да добавя и премахва достъп за екипи settings.email = Ел. поща за връзка settings.delete_account = Изтриване на тази организация settings.delete_org_title = Изтриване на организацията @@ -1900,8 +1961,10 @@ teams.none_access = Без достъп teams.members.none = Няма участници в този екип. repo_updated = Обновено %s teams.delete_team_success = Екипът е изтрит. +teams.search_repo_placeholder = Потърсете хранилище… teams.delete_team_title = Изтриване на екипа teams.add_team_member = Добавяне на участник в екипа +teams.read_access_helper = Членовете могат да преглеждат и клонират хранилищата на екипа. teams.invite.description = Моля, щракнете върху бутона по-долу, за да се присъедините към екипа. teams.invite.title = Поканени сте да се присъедините към екип %s в организация %s. team_permission_desc = Разрешение @@ -1916,39 +1979,38 @@ teams.no_desc = Този екип няма описание settings.delete_org_desc = Тази организация ще бъде изтрита перманентно. Продължаване? open_dashboard = Отваряне на таблото settings.change_orgname_prompt = Бележка: Промяната на името на организацията ще промени и URL адреса на вашата организация и ще освободи старото име. -teams.add_duplicate_users = Потребителят вече е участник в екипа. + +team_access_desc = Достъп до хранилище +team_unit_desc = Разрешаване на достъп до секции на хранилището 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 = Разрешаване на достъп до секции на хранилището +form.create_org_not_allowed = Нямате разрешение да създавате организация. 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 ден. Все още можете да си върнете старото име по време на периода на изчакване. +settings.change_orgname_redirect_prompt.with_cooldown.few = Старото име на организацията ще бъде достъпно за всички след период на изчакване от %[1]d дни. Все още можете да си върнете старото име по време на периода на изчакване. +settings.update_avatar_success = Профилната снимка на организацията е обновена. +settings.hooks_desc = Добавете уеб-куки, които ще се задействат за всички хранилища в тази организация. +members.membership_visibility = Видимост на участничеството: +members.public = Видим +members.private = Скрит +members.invite_desc = Добавяне на нов участник към %s: +members.invite_now = Поканване сега +teams.admin_access = Администраторски достъп +teams.invite_team_member = Поканване в %s +teams.invite_team_member.list = Чакащи покани +teams.delete_team_desc = Изтриването на екип отнема достъпа до хранилището от неговите участници. Продължаване? +teams.remove_all_repos_desc = Това ще премахне всички хранилища от екипа. +teams.add_all_repos_title = Добавяне на всички хранилища +teams.add_all_repos_desc = Това ще добави всички хранилища на организацията към екипа. teams.add_nonexistent_repo = Хранилището, което се опитвате да добавите, не съществува, моля, първо го създайте. +teams.add_duplicate_users = Потребителят вече е участник в екипа. +teams.repos.none = Няма хранилища, до които този екип да има достъп. +teams.specific_repositories = Конкретни хранилища +teams.specific_repositories_helper = Участниците ще имат достъп само до хранилища, изрично добавени към екипа. Избирането на това няма автоматично да премахне хранилища, вече добавени с Всички хранилища. +teams.all_repositories_helper = Екипът има достъп до всички хранилища. Избирането на това ще добави всички съществуващи хранилища към екипа. teams.invite.by = Поканен от %s -teams.owners_permission_desc = Притежателите имат пълен достъп до всички хранилища и имат администраторски достъп до организацията. -teams.can_create_org_repo_helper = Участниците могат да създават нови хранилища в организацията. Създателят ще получи администраторски достъп до новото хранилище. [install] admin_password = Парола @@ -2017,6 +2079,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 току-що се регистрира @@ -2064,7 +2127,7 @@ projects = Проекти code = Код overview = Обзор watched = Наблюдавани хранилища -unfollow = Отследване +unfollow = Прекратяване на следването block = Блокиране settings = Потребителски настройки starred = Отбелязани хранилища @@ -2076,6 +2139,7 @@ block_user = Блокиране на потребителя change_avatar = Променете профилната си снимка… email_visibility.limited = Вашият адрес за ел. поща е видим за всички удостоверени потребители disabled_public_activity = Този потребител е изключил публичната видимост на дейността. +email_visibility.private = Вашият адрес на ел. поща е видим само за вас и администраторите show_on_map = Показване на това място на картата followers_one = %d последовател following_one = %d следван @@ -2100,6 +2164,7 @@ block_user.detail_1 = Ще спрете да се следвате един др [home] filter = Други филтри show_archived = Архивирани +search_repos = Намиране на хранилище… my_orgs = Организации uname_holder = Потребителско име или ел. поща my_repos = Хранилища @@ -2109,9 +2174,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 = Филтриране по хранилища на екипа @@ -2124,6 +2193,8 @@ dashboard = Табло repositories = Хранилища users.name = Потребителско име organizations = Организации +repos.forks = Разклонения +repos.stars = Звезди config.mailer_name = Име repos.name = Име orgs.name = Име @@ -2137,6 +2208,7 @@ notices.type_1 = Хранилище config.domain = Домейн на сървъра users.max_repo_creation = Максимален брой хранилища defaulthooks = Уеб-куки по подразбиране +auths.sspi_default_language = Потребителски език по подразбиране hooks = Уеб-куки systemhooks = Системни уеб-куки orgs.new_orga = Нова организация @@ -2205,12 +2277,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.` @@ -2219,6 +2293,7 @@ team_not_exist = Екипът не съществува. TeamName = Име на екипа email_error = ` не е валиден адрес за ел. поща.` email_invalid = Адресът за ел. поща е невалиден. +SSHTitle = Име на SSH ключ repo_name_been_taken = Името на хранилището вече е използвано. team_name_been_taken = Името на екипа вече е заето. org_name_been_taken = Името на организацията вече е заето. @@ -2268,17 +2343,18 @@ 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“ +git_ref_name_error = ` трябва да е правилно форматирано име на Git препратка.` regex_pattern_error = ` шаблонът на регулярния израз е невалиден: %s.` repository_files_already_exist = Вече съществуват файлове за това хранилище. Свържете се със системния администратор. repository_files_already_exist.delete = Вече съществуват файлове за това хранилище. Трябва да ги изтриете. -invalid_gpg_key = Не може да се потвърди вашият GPG ключ: %s -git_ref_name_error = ` трябва да е правилно форматирано име на Git препратка.` +enterred_invalid_owner_name = Името на новия притежател не е валидно. last_org_owner = Не можете да премахнете последния потребител от екипа на „притежателите“. Трябва да има поне един притежател за организация. +invalid_ssh_key = Не може да се потвърди вашият SSH ключ: %s +invalid_gpg_key = Не може да се потвърди вашият GPG ключ: %s +unable_verify_ssh_key = Не може да се потвърди SSH ключът, проверете го отново за грешки. +required_prefix = Въведеният текст трябва да започва с „%s“ [action] close_issue = `затвори задача %[3]s#%[2]s` @@ -2298,7 +2374,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 @@ -2307,23 +2383,28 @@ 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 от огледало +review_dismissed = `отхвърли рецензия от %[4]s за %[3]s#%[2]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 = Запомни ме @@ -2332,6 +2413,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 = Вече имате акаунт? Влезте! @@ -2399,12 +2482,21 @@ 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 разклонение +forks_few = %d разклонения relevant_repositories = Показани са само подходящи хранилища, показване на нефилтрирани резултати. relevant_repositories_tooltip = Хранилищата, които са разклонения или нямат тема, икона или описание, са скрити. @@ -2430,14 +2522,15 @@ variables.management = Управление на променливи variables.not_found = Променливата не е открита. variables.id_not_exist = Променлива с идентификатор %d не съществува. runners.owner_type = Тип -status.cancelled = Отменено + +unit.desc = Управление на интегрирани CI/CD pipelines с Forgejo Actions. +status.unknown = Неизвестно +status.waiting = Изчаква се status.running = Изпълнява се status.success = Успешно -status.waiting = Изчаква се -status.unknown = Неизвестно status.failure = Неуспешно +status.cancelled = Отменено status.skipped = Пропуснато -unit.desc = Управление на интегрирани CI/CD pipelines с Forgejo Actions. [heatmap] less = По-малко @@ -2520,32 +2613,31 @@ eib = ЕиБ [translation_meta] test = окей +[repo.permissions] +code.read = Четене: Достъп и клониране на кода на хранилището. +code.write = Писане: Изтласкване към хранилището, създаване на клонове и маркери. +issues.read = Четене: Четене и създаване на задачи и коментари. +issues.write = Писане: Затваряне на задачи и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. +pulls.read = Четене: Четене и създаване на заявки за сливане. +pulls.write = Писане: Затваряне на заявки за сливане и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. +releases.read = Четене: Преглед и изтегляне на издания. +wiki.read = Четене: Четене на интегрираното уики и неговата история. +wiki.write = Писане: Създаване, обновяване и изтриване на страници в интегрираното уики. +projects.read = Четене: Достъп до проектните табла на хранилището. +projects.write = Писане: Създаване и редактиране на проекти и колони. [gpg] default_key = Подписано с ключ по подразбиране +error.extract_sign = Неуспешно извличане на подпис +error.generate_hash = Неуспешно генериране на хеш на подаването +error.no_committer_account = Няма акаунт, свързан с адреса за ел. поща на подаващия error.no_gpg_keys_found = Не е намерен известен ключ за този подпис в базата данни error.not_signed_commit = Не е подписано подаване -error.generate_hash = Неуспешно генериране на хеш на подаването -error.extract_sign = Неуспешно извличане на подпис -error.probable_bad_signature = ВНИМАНИЕ! Въпреки че има ключ с това ID в базата данни, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. error.failed_retrieval_gpg_keys = Неуспешно извличане на ключ, свързан с акаунта на подаващия +error.probable_bad_signature = ВНИМАНИЕ! Въпреки че има ключ с това ID в базата данни, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. 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 +error.no_unit_allowed_repo = Нямате разрешение за достъп до никоя секция на това хранилище. +error.unit_not_allowed = Нямате разрешение за достъп до тази секция на хранилището. diff --git a/options/locale/locale_ca.ini b/options/locale/locale_ca.ini index e1ebee1b00..ea2af3b645 100644 --- a/options/locale/locale_ca.ini +++ b/options/locale/locale_ca.ini @@ -3,7 +3,7 @@ home = Inici dashboard = Panell de control explore = Explorar help = Ajuda -logo = Logotip +logo = Logo sign_in = Iniciar sessió sign_in_with_provider = Entra amb %s sign_in_or = o @@ -39,20 +39,24 @@ 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. webauthn_error_unknown = Hi ha hagut un error desconegut. Si us plau torneu-ho a intentar. webauthn_error_insecure = WebAuthn només suporta connexions segures. Per provar sobre HTTP, podeu utilitzar l'origen "localhost" o "127.0.0.1" -webauthn_error_unable_to_process = El servidor no ha pogut processar la vostra sol·licitud. -webauthn_error_duplicated = La clau de seguretat no és permesa per aquesta sol·licitud. Si us plau, assegureu-vos que la clau encara no ha estat registrada. +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,13 +148,15 @@ new_migrate.link = Nova migració new_org.link = Nova organització [search] +milestone_kind = Cerca fites... fuzzy = Difusa -search = Cerca… +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… code_search_unavailable = La cerca de codi no està disponible actualment. Si us plau concteu amb l'administrador del lloc. +code_search_by_git_grep = Els resultats actuals de la cerca de codi són proporcionats per "git grep". Podríen haver-hi millors resultats si l'administrador del lloc habilita l'indexador de codi. package_kind = Cerca paquets… project_kind = Cerca projectes… branch_kind = Cerca branques… @@ -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] @@ -280,7 +288,7 @@ install_btn_confirm = Instaŀlar Forgejo allow_dots_in_usernames = Permet als usuaris utilitzar punts en els seus noms d'usuari. No afecta als comptes existents. no_reply_address = Domini del correu ocult no_reply_address_helper = Nom del domini per a usuaris amb l'adreça de correu oculta. Per exemple, el nom d'usuari "pep" tindrà la sessió inciada com a "pep@noreply.example.org" si el domini per a adreces ocultes es configurat a "noreply.example.org". -password_algorithm_helper = Configura la funció resum per a contrasenyes. Els algorismes difereixen en requeriments i seguretat. L'algorisme "argon2" és bastant segur, però utilitza molta memòria i podria ser inapropiat per a sistemes petits. +password_algorithm_helper = Configura la funció resum per a contrasenyes. Els algorismes difereixen en requeriments i seguretat. L'algorisme "argon2" es bastant segur, però utilitza molta memòria i podría ser inapropiat per a sistemes petits. invalid_password_algorithm = Funció resum invalida per a contrasenyes enable_update_checker = Habilita la comprovació d'actualitzacions env_config_keys = configuració de l'entorn @@ -324,8 +332,12 @@ relevant_repositories = Només és mostren repositoris rellevants, repos = Repositoris organizations = Organitzacions code = Codi +stars_few = %d estrelles +forks_one = %d fork +forks_few = %d forks go_to = Ves a users = Usuaris +stars_one = %d estrella [auth] disable_register_prompt = El registre està deshabilitat. Si us plau contacti l'administrador del lloc. @@ -366,7 +378,7 @@ openid_connect_title = Entreu a un compte existent openid_register_title = Crear un compte nou authorize_application = Autoritzar aplicació authorize_redirect_notice = Sereu redirigits a %s si autoritzeu aquesta aplicació. -authorize_application_description = Si li concediu l'accés podrà accedir i escriure a tota la informació del vostre compte, inclosos repositoris privats i organitzacions. +authorize_application_description = Si li concediu l'accés podrà accedir i escriure a tota la informació del vostre compte, inclòs repositoris privats i organitzacions. authorize_title = Autoritzeu "%s" a accedir al vostre compte? active_your_account = Activeu el compte sign_up_successful = S'ha creat el compte correctament. Benvingut! @@ -397,7 +409,7 @@ twofa_passcode_incorrect = El codi d'accés és incorrecte. Si heu perdut el dis oauth_signin_tab = Vincular a un compte existent oauth.signin.error = Hi ha hagut un error processant la sol·licitud d'autorització. Si persisteix, poseu-vos en contacte amb l'administrador del lloc. disable_forgot_password_mail_admin = La recuperació de comptes només està disponible quan s'ha configurat el correu electrònic. Si us plau, configureu el correu electrònic per a habilitar la recuperació de comptes. -non_local_account = Els usuaris no locals no poden actualitzar la seva contrasenya mitjançant l'interfície web de Forgejo. +non_local_account = Els usuaris no locals no poden actualitzar la seva contrasenya mitjançant l'interfície web de Forgejo openid_register_desc = No s'ha reconegut la URI OpenID. Vinculeu-la amb un compte nou aquí. openid_connect_desc = No s'ha reconegut la URI OpenID. Vinculeu-la amb un compte nou aquí. sign_in_openid = Accediu amb OpenID @@ -407,8 +419,8 @@ buttons.indent.tooltip = Aniua els elements un nivell buttons.unindent.tooltip = Desaniuna els elements un nivell buttons.ref.tooltip = Referenciar un problema o una "pull request" buttons.heading.tooltip = Afegir capçalera -buttons.bold.tooltip = Afegir text ressaltat (Ctrl+B / ⌘B) -buttons.italic.tooltip = Afegir text en cursiva (Ctrl+I / ⌘I) +buttons.bold.tooltip = Afegir text ressaltat +buttons.italic.tooltip = Afegir text en cursiva buttons.switch_to_legacy.tooltip = En el seu lloc, utilitzar l'editor de codi antic buttons.quote.tooltip = Citar text buttons.enable_monospace_font = Habilitar la font monoespai @@ -432,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 @@ -441,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 @@ -466,616 +481,6 @@ register_notify = Benvinguts a %s admin.new_user.text = Si us plau, cliqueu aui per administrar aquest usuari des del panell d'administració. 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.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 -repo.transfer.to_you = tu -register_notify.text_3 = Si algú altre us ha fet aquest compte, necessitareu configurar la vostra contrasenya abans. -password_change.subject = S'ha canviat la vostra contrasenya -primary_mail_change.subject = S'ha canviat la vostra adreça de correu electrònic principal -totp_disabled.subject = S'ha deshabilitat TOTP -removed_security_key.subject = S'ha eliminat una clau de seguretat -removed_security_key.text_1 = S'ha eliminat la clau de seguretat "%[1]s" del vostre compte. -account_security_caution.text_1 = Podeu ignorar aquest correu si heu sigut vos. -account_security_caution.text_2 = Si no heu sigut vos, el vostre compte està compromès. Si us plau, contacteu els administradors d'aquest lloc. -totp_enrolled.subject = Heu activat TOTP com a mètode d'autenticació de doble factor -totp_enrolled.text_1.no_webauthn = Heu activat TOTP pel vostre compte. Això vol dir que haureu d'usar TOTP com a mètode d'autenticació de doble factor a tots els inicis de sessió futurs al vostre compte. -issue_assigned.pull = @%[1]s us ha assignat la sol·licitud d'extracció %[2]s al repositori %[3]s. -issue.action.reopen = @%[1]s ha reobert #%[2]s. -issue.action.approve = @%[1]s ha aprovat aquesta sol·licitud d'extracció. -issue.action.reject = @%[1]s ha sol·licitat canvis en aquesta sol·licitud d'extracció. -register_notify.text_2 = Podeu iniciar sessió al vostre compte fent servir el vostre nom d'usuari: %s -register_notify.text_1 = aquest és el vostre correu electrònic de confirmació pel registre de %s! -password_change.text_1 = S'acaba de canviar la contrasenya pel vostre compte. -issue.action.review = @%[1]s ha deixat un comentari a la vostra sol·licitud d'extracció. -issue.action.review_dismissed = @%[1]s ha rebutjat l'última revisió de %[2]s per aquesta sol·licitud d'extracció. -primary_mail_change.text_1 = S'ha canviat la vostra adreça de correu electrònic principal a %[1]s. Això vol dir que aquesta adreça de correu electrònic no rebrà més notificacions de correu pel vostre compte. -totp_disabled.text_1 = S'han desactivat les contrasenyes d'un sol ús basades en el temps (TOTP) pel vostre compte. -totp_disabled.no_2fa = Ja no hi ha altres mètodes d'autenticació de doble factor configurats, per la qual cosa ja no és necessari iniciar sessió al vostre compte mitjançat autenticació de doble factor. -removed_security_key.no_2fa = Ja no hi ha altres mètodes d'autenticació de doble factor configurats, per la qual cosa ja no és necessari iniciar sessió al vostre compte mitjançat autenticació de doble factor. -reset_password = Recupereu el vostre compte -reset_password.text = Si heu sigut vós, cliqueu el següent enllaç per recuperar el vostre compte abans de %s: -totp_enrolled.text_1.has_webauthn = Heu habilitat el TOTP per al vostre compte. Això vol dir que, per a totes les futures connexions al vostre compte, podreu utilitzar el TOTP com a mètode d'autenticació en dos passos (2FA) o bé utilitzar qualsevol de les vostres claus de seguretat. -issue.action.force_push = %[1]s ha realitzat un «force push» de %[2]s des de %[3]s fins a %[4]s. -issue.action.ready_for_review = @%[1]s ha marcat aquesta «pull request» com a preparada per a la revisió. -issue.in_tree_path = A %s: -issue.action.push_1 = @%[1]s ha pujat $[3]d commit a %[2]s -issue.action.push_n = @%[1]s ha pujat $[3]d commits a %[2]s -release.note = Nota: -release.new.text = @%[1]s ha publicat %[2]s a %[3]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. -Email = Adreça de correu electrònic -require_error = ` no pot estar buit.` -min_size_error = ` ha de contenir %s caràcters com a mínim.` -max_size_error = ` ha de contenir %s caràcters com a màxim.` -email_domain_is_not_allowed = El domini de l'adreça de correu electrònic %s de l'usuari entra en conflicte amb EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Assegureu-vos d'haver introduït l'adreça de correu electrònic correctament. -Website = Lloc web -Location = Ubicació -AdminEmail = Correu electrònic de l'administrador -AccessToken = Testimoni d'accés -alpha_dash_error = ` hauria de contenir només caràcters alfanumèrics, guions ("-") i barres baixes ("_").` -alpha_dash_dot_error = ` hauria de contenir només caràcters alfanumèrics, guions ("-"), barres baixes ("_") i punts (".").` -regex_pattern_error = ` el patró d'expressió regular no és vàlid: %s.` -required_prefix = L'entrada ha de començar amb "%s" -CommitSummary = Resum del commit -CommitMessage = Missatge del commit -CommitChoice = Selecció de commit -TreeName = Camí del fitxer - -[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 "