diff --git a/.deadcode-out b/.deadcode-out index b70be3e800..97093ce93b 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -19,10 +19,10 @@ forgejo.org/models/auth forgejo.org/models/db TruncateBeans TruncateBeansCascade + InTransaction DumpTables GetTableNames extendBeansForCascade - IsErrNameActivityPubInvalid forgejo.org/models/dbfs file.renameTo @@ -58,6 +58,7 @@ forgejo.org/models/user IsErrUserSettingIsNotExist GetUserAllSettings DeleteUserSetting + GetFederatedUser forgejo.org/modules/activitypub NewContext @@ -87,6 +88,7 @@ forgejo.org/modules/eventsource Event.String forgejo.org/modules/forgefed + NewForgeFollow NewForgeUndoLike ForgeUndoLike.UnmarshalJSON ForgeUndoLike.Validate @@ -132,9 +134,6 @@ forgejo.org/modules/json StdJSON.Indent forgejo.org/modules/log - eventWriterBuffer.Close - eventWriterBuffer.Write - eventWriterBuffer.GetString NewEventWriterBuffer forgejo.org/modules/markup @@ -225,6 +224,9 @@ forgejo.org/routers/web/org forgejo.org/services/context GetPrivateContext +forgejo.org/services/federation + FollowRemoteActor + forgejo.org/services/notify UnregisterNotifier diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 46b85ad93d..fb1a99295f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { - "name": "forgejo-dev", - "image": "mcr.microsoft.com/devcontainers/go:1.26-trixie", + "name": "Gitea DevContainer", + "image": "mcr.microsoft.com/devcontainers/go:1.25-trixie", "features": { // installs nodejs into container "ghcr.io/devcontainers/features/node:1": { diff --git a/.editorconfig b/.editorconfig index 58daceb4f1..5476eb02fb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -30,6 +30,7 @@ insert_final_newline = false [options/locale/locale_*.ini] insert_final_newline = false -# Weblate is configured to use one tab for indention +# Weblate JSON output defaults to four spaces [options/locale_next/locale_*.json] -indent_style = tab +indent_style = space +indent_size = 4 diff --git a/.forgejo/issue_template/1-problem.yaml b/.forgejo/issue_template/1-problem.yaml index 516360a5c4..39008de1d2 100644 --- a/.forgejo/issue_template/1-problem.yaml +++ b/.forgejo/issue_template/1-problem.yaml @@ -13,18 +13,7 @@ body: - Please speak English, as this is the language all maintainers can speak and write. - Be civil, and follow the [Forgejo Code of Conduct](https://codeberg.org/forgejo/code-of-conduct). - Take a moment to [check if a similar problem has already been discussed in the past.](https://codeberg.org/forgejo/forgejo/issues?q=&type=all&labels=78137). Feel free to add your own experience there, if applicable. -- type: markdown - attributes: - value: | - ### New workflow - - We are currently experimenting with a new workflow to manage issues and better understand your problems and needs. This is step 1 of the workflow: Please try to focus on explaining the problem you are facing, which could be a bug in the code, a moment of confusion, or a need that you have. - - We do not expect anything from Forgejo users after creating a problem report, but we appreciate if you stay responsive to further questions. If you want, you can also participate in a discussion for solutions. - - Forgejo contributors will review your report, try to understand how important it is to you and other Forgejo users, and suggest potential solutions. In a next step, solutions can be documented and implemented. - - If you want to learn more about the background of our workflow, feel invited to read and participate in [the discussion that led to the current approach](https://codeberg.org/forgejo/discussions/issues/415). We are looking forward to your feedback. + - We are currently experimenting with a new workflow to understand your problems and requests. Please try to focus on explaining the problem you are facing, which could be a bug in the code, a moment of confusion, or a missing feature. We'll think about solutions to the problem at a later step. If you want to learn more about the background of our workflow, feel invited to read and participate in [the discussion that led to the current approach](https://codeberg.org/forgejo/discussions/issues/415). We are looking forward to your feedback. - type: dropdown id: can-reproduce attributes: @@ -41,18 +30,15 @@ body: - type: textarea id: environment attributes: - label: About your usage of Forgejo + label: Your usage of Forgejo description: | - Please provide a brief description of your usage of Forgejo. There is no clear guideline on how much you need to share here. You can be brief, but we value the insights you provide us to better understand your use case. Thank you very much! + Please provide a description of your usage of Forgejo. To better understand your problem, it will be relevant to know in which environment you use Forgejo and if you have performed specific configuration.
Further instructions - * When reporting problems with certain functionality, you should include related information. Examples: - * When reporting an issue with setting up an identity provider, it is useful to know if you use Forgejo in a 10-users non-profit / start up, or if you are talking about a school / university with several thousands of users. - * When describing confusion, it will be relevant to know your skill level and background ("New, but used a similar product", "In my role as a project manager ..."), so we know for which target audience we need to design the functionality. - * When reporting workflow issues or needs, it will be useful to know how large your project is, how many team members you have, which skill level we are talking about etc. For example, we might choose a different design depending on whether a feature is only for professional developers or for hobby coders. - * If you want, we always appreciate generic information about your Forgejo usage to help us understand your usage and make better decisions. For example: - * Your personal relation to Forgejo and user role ("I'm new to Forgejo, but used a comparable product called …", "In my role as a designer, …"). - * If you already explained your usage of Forgejo elsewhere (e.g. in another issue or in a user research repository), feel free to just drop a link. + * If you report a bug with a certain functionality, it will be relevant to learn about related configuration ("This is how I configured my identity provider", "This is how my Forgejo Actions runner is set up"). + * Please elaborate on your personal relation to Forgejo and user role ("I'm new to Forgejo, but used a comparable product called …", "In my role as a designer, …"). + * If you feel that Forgejo needs a change in functionality, please describe in which environment you want to use it, so that we can understand for which audience the complexity needs to appeal to ("We're a group of friends with no technical / professional background and use a personal Forgejo instance to prepare our first libre game"). + * If you already explained your usage of Forgejo elsewhere (e.g. in another issue or in a user research repository), you can put a link here.
validations: @@ -62,7 +48,7 @@ body: attributes: label: Problem description description: | - Please describe your problem as a first-hand experience. Try to focus on the problem. You do not have to find a solution, you can leave this to us. + Please describe your problem as a first-hand experience. Try to focus on the problem. Finding a solution will happen in a next step.
Further instructions * Start by explaining what you want to achieve ("I wanted to find an issue to work on"). @@ -89,7 +75,7 @@ body: id: forgejo-ver attributes: label: Forgejo Version - description: Forgejo version (or commit reference) your instance is running or that you used to reproduce the bug on Forgejo Next. + description: Forgejo version (or commit reference) your instance is running - type: textarea id: versions attributes: @@ -98,10 +84,3 @@ body: Please include details to help us understand your problem: browser engine and version (for UI issues), operating system and version running Forgejo, database engine and version, deployment methods and relevant third-party packages (e.g. renderers, identity providers) validations: required: true -- type: markdown - attributes: - value: | - ### Solutions - - *Accepted solutions to address this problem will go here* - visible: [content] diff --git a/.forgejo/issue_template/2-enhancement.yaml b/.forgejo/issue_template/2-enhancement.yaml index 68594152b6..3014d8901a 100644 --- a/.forgejo/issue_template/2-enhancement.yaml +++ b/.forgejo/issue_template/2-enhancement.yaml @@ -1,5 +1,5 @@ name: "Step 2: Enhancement" -description: "[Advanced users only] Suggest a solution to one or multiple problems that have already been reported (see step 1)." +description: Suggest a solution to one or multiple problems that have already been reported (see step 1). title: "enh: " labels: ["enhancement/feature"] body: @@ -8,28 +8,19 @@ body: value: | - Please speak English, as this is the language all maintainers can speak and write. - Be civil, and follow the [Forgejo Code of Conduct](https://codeberg.org/forgejo/code-of-conduct). -- type: markdown - attributes: - value: | - ### New workflow - - We are currently experimenting with a new workflow to manage issues and better understand your problems and needs. This is step 2 of the workflow, which is intended for Forgejo contributors: If you just want to raise a problem or need you have, please refer to step 1. - - This step allows to document a solution to one or multiple problems. It allows developers to focus on actionable implementation tasks without the clutter of previous discussions or triaging work. - - If you want to learn more about the background of our workflow, feel invited to read and participate in [the discussion that led to the current approach](https://codeberg.org/forgejo/discussions/issues/415). We are looking forward to your feedback. + - We are currently experimenting with a new workflow to understand your problems and requests. This is step 2 of a two-step-process. The goal is to discuss potential solutions to already-reported problems. If you want to learn more about the background of our workflow, feel invited to read and participate in [the discussion that led to the current approach](https://codeberg.org/forgejo/discussions/issues/415). We are looking forward to your feedback. - type: textarea id: problems attributes: label: Existing problems this enhancement addresses - description: Only list the issue numbers of the **existing** problems that your proposal addresses. **Do not add new descriptions.** If you haven't previously described a problem, please [complete step one of the workflow](https://codeberg.org/forgejo/forgejo/issues/new?template=.forgejo%2fissue_template%2fproblem.yaml) and describe the problem you'd like to solve first. + description: List the issue numbers of the reported problems that this enhancement addresses. If you haven't previously described a problem, please [complete step one of the workflow](https://codeberg.org/forgejo/forgejo/issues/new?template=.forgejo%2fissue_template%2fproblem.yaml) and describe the problem you'd like to solve first. validations: required: true - type: textarea id: description attributes: label: Enhancement description - description: Describe the changes you suggest for Forgejo and explain how they address the problems. + description: As concisely as possible, describe the changes you suggest for Forgejo and explain how they address the problems. validations: required: true - type: textarea diff --git a/.forgejo/issue_template/config.yml b/.forgejo/issue_template/config.yml index b4bd06af17..f2ea8d945a 100644 --- a/.forgejo/issue_template/config.yml +++ b/.forgejo/issue_template/config.yml @@ -1,4 +1,3 @@ -blank_issues_enabled: false contact_links: - name: 🔓 Security Reports url: mailto:security@forgejo.org diff --git a/.forgejo/pull_request_template.md b/.forgejo/pull_request_template.md index cfdb8a5336..d30af48446 100644 --- a/.forgejo/pull_request_template.md +++ b/.forgejo/pull_request_template.md @@ -10,22 +10,13 @@ labels: ## Checklist -The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. All work and communication must conform to Forgejo's [AI Agreement](https://codeberg.org/forgejo/governance/src/branch/main/AIAgreement.md). There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). +The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). -### Tests for Go changes - -(can be removed for JavaScript changes) +### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. -- I ran... - - [ ] `make pr-go` before pushing - -### Tests for JavaScript changes - -(can be removed for Go changes) - - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). @@ -37,9 +28,6 @@ The [contributor guide](https://forgejo.org/docs/next/contributor/) contains inf ### Release notes -- [ ] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change. -- [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change. - -*The decision if the pull request will be shown in the release notes is up to the mergers / release team.* - -The content of the `release-notes/.md` file will serve as the basis for the release notes. If the file does not exist, the title of the pull request will be used instead. +- [ ] I do not want this change to show in the release notes. +- [ ] I want the title to show in the release notes with a link to this pull request. +- [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. diff --git a/.forgejo/workflows-composite/build-backend/action.yaml b/.forgejo/workflows-composite/build-backend/action.yaml index 8c6fdb024b..68a99ffaf9 100644 --- a/.forgejo/workflows-composite/build-backend/action.yaml +++ b/.forgejo/workflows-composite/build-backend/action.yaml @@ -3,7 +3,7 @@ runs: steps: - run: | su forgejo -c 'make deps-backend' - - uses: https://data.forgejo.org/actions/cache@v5 + - uses: https://data.forgejo.org/actions/cache@v4 id: cache-backend with: path: ${{github.workspace}}/gitea diff --git a/.forgejo/workflows-composite/setup-cache-go/action.yaml b/.forgejo/workflows-composite/setup-cache-go/action.yaml index 392723567d..c44eaf24bd 100644 --- a/.forgejo/workflows-composite/setup-cache-go/action.yaml +++ b/.forgejo/workflows-composite/setup-cache-go/action.yaml @@ -50,7 +50,7 @@ runs: - name: "Restore Go dependencies from cache or mark for later caching" id: cache-deps - uses: https://data.forgejo.org/actions/cache@v5 + uses: https://data.forgejo.org/actions/cache@v4 with: key: setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-${{ hashFiles('go.sum', 'go.mod', 'Makefile') }} restore-keys: | diff --git a/.forgejo/workflows/backport.yml b/.forgejo/workflows/backport.yml index 4854eb225e..56956c4641 100644 --- a/.forgejo/workflows/backport.yml +++ b/.forgejo/workflows/backport.yml @@ -47,7 +47,7 @@ jobs: cat <<'EOF' ${{ toJSON(github) }} EOF - - uses: https://data.forgejo.org/actions/git-backporting@v4.9.1 + - uses: https://data.forgejo.org/actions/git-backporting@v4.8.7 with: target-branch-pattern: "^backport/(?(v.*))$" strategy: ort diff --git a/.forgejo/workflows/build-release-integration.yml b/.forgejo/workflows/build-release-integration.yml index 3d024f5f9e..d22126bad4 100644 --- a/.forgejo/workflows/build-release-integration.yml +++ b/.forgejo/workflows/build-release-integration.yml @@ -1,9 +1,6 @@ name: Integration tests for the release process enable-email-notifications: true -env: - FORGEJO_VERSION: 11.0.14 # renovate: datasource=docker depName=data.forgejo.org/forgejo/forgejo - on: push: paths: @@ -29,14 +26,14 @@ jobs: if: vars.ROLE == 'forgejo-coding' runs-on: lxc-bookworm steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - id: forgejo - uses: https://data.forgejo.org/actions/setup-forgejo@v3.1.11 + uses: https://data.forgejo.org/actions/setup-forgejo@v3.1.7 with: user: root password: admin1234 - image-version: ${{ env.FORGEJO_VERSION }} + image-version: 1.21 lxc-ip-prefix: 10.0.9 - name: publish the forgejo release diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index ca259f7e7b..f39954af8f 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@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 with: fetch-depth: 0 @@ -93,7 +93,7 @@ jobs: - name: cache node_modules id: node - uses: https://data.forgejo.org/actions/cache@v5 + uses: https://data.forgejo.org/actions/cache@v4 with: path: | node_modules @@ -227,14 +227,11 @@ jobs: curl -sS -X DELETE $url/api/v1/repos/forgejo-experimental/forgejo/releases/tags/$tag > /dev/null curl -sS -X DELETE $url/api/v1/repos/forgejo-experimental/forgejo/tags/$tag > /dev/null fi - # actions/checkout@v6 sets http.https://codeberg.org/.extraheader with the automatic token. Get rid of it so - # it does not prevent using the token that has write permissions. As of @v6, it is stored in - # $RUNNER_TEMP/git-credentials-(uuid).config and included in the repo via - # includeif.gitdir:...=$RUNNER_TEMP/git-credentials-(uuid).config. Since we don't need these credentials - # anymore we can just remove the generated config file. - rm -f $RUNNER_TEMP/git-credentials-* + # actions/checkout@v3 sets http.https://codeberg.org/.extraheader with the automatic token. + # Get rid of it so it does not prevent using the token that has write permissions + git config --local --unset http.https://codeberg.org/.extraheader if test -f .git/shallow ; then - echo "unexpected .git/shallow file is present" + echo "unexptected .git/shallow file is present" echo "it suggests a checkout --depth X was used which may prevent pushing the commit" echo "it happens when actions/checkout is called without depth: 0" fi diff --git a/.forgejo/workflows/cascade-setup-end-to-end.yml b/.forgejo/workflows/cascade-setup-end-to-end.yml index 36b464d9f3..747e41075f 100644 --- a/.forgejo/workflows/cascade-setup-end-to-end.yml +++ b/.forgejo/workflows/cascade-setup-end-to-end.yml @@ -37,7 +37,7 @@ jobs: container: image: data.forgejo.org/oci/node:24-bookworm steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 with: fetch-depth: '0' show-progress: 'false' diff --git a/.forgejo/workflows/coverage.yml b/.forgejo/workflows/coverage.yml index 3c2a6c245e..f0a27889a1 100644 --- a/.forgejo/workflows/coverage.yml +++ b/.forgejo/workflows/coverage.yml @@ -61,7 +61,7 @@ jobs: image: registry.redict.io/redict:7.3.6-scratch options: --tmpfs /data:noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 with: repository: ${{ inputs.repository }} ref: ${{ inputs.ref }} @@ -83,7 +83,7 @@ jobs: TEST_MINIO_ENDPOINT: minio:9000 TEST_LDAP: 1 TEST_REDIS_SERVER: cacher:6379 - - uses: https://data.forgejo.org/forgejo/upload-artifact@v5 + - uses: https://code.forgejo.org/forgejo/upload-artifact@v4 with: name: coverage path: ${{ forge.workspace }}/coverage/merged diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 4695076581..8eee8e2145 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@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - name: copy & sign uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.6.0 diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index f075406335..76799b06dc 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -6,7 +6,7 @@ on: env: RNA_WORKDIR: /srv/rna - RNA_VERSION: v1.7.0 # renovate: datasource=forgejo-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.4.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: @@ -15,9 +15,9 @@ jobs: container: image: 'data.forgejo.org/oci/ci:1' steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - - uses: https://data.forgejo.org/actions/cache@v5 + - uses: https://data.forgejo.org/actions/cache@v4 with: key: rna-${{ env.RNA_VERSION }} path: ${{ env.RNA_WORKDIR }} diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index e85f11b7f8..4640eb37df 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -8,16 +8,16 @@ on: - labeled env: - RNA_VERSION: v1.7.0 # renovate: datasource=forgejo-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.4.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: if: ( vars.ROLE == 'forgejo-coding' ) && contains(github.event.pull_request.labels.*.name, 'worth a release-note') runs-on: docker container: - image: 'data.forgejo.org/oci/ci:1' + image: 'data.forgejo.org/oci/node:24-bookworm' steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - name: event run: | @@ -28,12 +28,17 @@ jobs: ${{ toJSON(github.event) }} EOF - - name: install release-notes-assistant + - uses: https://data.forgejo.org/actions/setup-go@v6 + with: + go-version-file: "go.mod" + cache: false + + - 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: release-notes-assistant preview run: | - rna --config .release-notes-assistant.yaml --storage pr --storage-location ${{ github.event.pull_request.number }} --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} preview ${{ github.event.pull_request.number }} + go run code.forgejo.org/forgejo/release-notes-assistant@$RNA_VERSION --config .release-notes-assistant.yaml --storage pr --storage-location ${{ github.event.pull_request.number }} --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} preview ${{ github.event.pull_request.number }} diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml new file mode 100644 index 0000000000..cc4801d811 --- /dev/null +++ b/.forgejo/workflows/renovate.yml @@ -0,0 +1,79 @@ +# +# Runs every 2 hours, but Renovate is limited to create new PR before 4am. +# See renovate.json for more settings. +# Automerge is enabled for Renovate PR's but need to be approved before. +# +name: renovate + +on: + push: + branches: + - renovate/** # self-test updates + paths: + - .forgejo/workflows/renovate.yml + schedule: + - cron: '0 0/2 * * *' + workflow_dispatch: + +env: + RENOVATE_DRY_RUN: ${{ (github.event_name != 'schedule' && github.ref_name != github.event.repository.default_branch) && 'full' || '' }} + RENOVATE_REPOSITORIES: ${{ github.repository }} + # fix because 10.0.0-58-7e1df53+gitea-1.22.0 < 10.0.0 for semver + # and codeberg api returns such versions from `git describe --tags` + # RENOVATE_X_PLATFORM_VERSION: 10.0.0+gitea-1.22.0 currently not needed + +jobs: + renovate: + if: vars.ROLE == 'forgejo-coding' && secrets.RENOVATE_TOKEN != '' + + runs-on: docker + container: + image: data.forgejo.org/renovate/renovate:42.39.2 + + steps: + - name: Load renovate repo cache + uses: https://data.forgejo.org/actions/cache/restore@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1 + with: + path: | + .tmp/cache/renovate/repository + .tmp/cache/renovate/renovate-cache-sqlite + .tmp/osv + key: repo-cache-${{ github.run_id }} + restore-keys: | + repo-cache- + + - name: Run renovate + run: renovate + env: + GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_COM_TOKEN }} + LOG_LEVEL: debug + RENOVATE_BASE_DIR: ${{ github.workspace }}/.tmp + RENOVATE_ENDPOINT: ${{ github.server_url }} + RENOVATE_PLATFORM: forgejo + RENOVATE_REPOSITORY_CACHE: 'enabled' + RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }} + RENOVATE_GIT_AUTHOR: 'Renovate Bot ' + RENOVATE_CONFIG_FILE_NAMES: '[".forgejo/renovate.json"]' + + RENOVATE_X_SQLITE_PACKAGE_CACHE: true + + GIT_AUTHOR_NAME: 'Renovate Bot' + GIT_AUTHOR_EMAIL: 'forgejo-renovate-action@forgejo.org' + GIT_COMMITTER_NAME: 'Renovate Bot' + GIT_COMMITTER_EMAIL: 'forgejo-renovate-action@forgejo.org' + + 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 + with: + path: | + .tmp/cache/renovate/repository + .tmp/cache/renovate/renovate-cache-sqlite + .tmp/osv + key: repo-cache-${{ github.run_id }} diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml index 11d20e2ab0..6c690c484f 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -34,7 +34,7 @@ jobs: image: 'data.forgejo.org/oci/node:24-trixie' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - 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 @@ -53,7 +53,7 @@ jobs: image: 'data.forgejo.org/oci/node:24-trixie' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - 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 @@ -85,7 +85,7 @@ jobs: MARIADB_DATABASE: testgitea options: --tmpfs /var/lib/mysql:noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - uses: ./.forgejo/workflows-composite/setup-env - name: install dependencies run: apt-get update -qq && apt-get -q install -qq -y git-lfs diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index 06ef5eb131..ac9d9f0801 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -22,12 +22,10 @@ jobs: cat <<'EOF' ${{ toJSON(github) }} EOF - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - uses: ./.forgejo/workflows-composite/setup-env - # DO NOT add checks here, but rather in the makefile - - run: su forgejo -c './tools/cimake.sh pr-go' - # this will re-run the backend target also contained in pr-go, but - # a re-build is insignificant + - 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 - uses: ./.forgejo/workflows-composite/build-backend frontend-checks: if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' @@ -36,17 +34,14 @@ jobs: image: 'data.forgejo.org/oci/node:24-trixie' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v6 - + - uses: https://data.forgejo.org/actions/checkout@v5 - uses: https://data.forgejo.org/actions/setup-node@v6 with: node-version-file: .node-version - - run: make deps-frontend - run: make lint-frontend - run: make checks-frontend - - name: make test-frontend-coverage - run: | + - 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 @@ -58,8 +53,8 @@ jobs: run: | apt-get update -qq apt-get -q install -qq -y zstd - - name: 'Cache frontend build for playwright testing' - uses: https://data.forgejo.org/actions/cache/save@v5 + - name: "Cache frontend build for playwright testing" + uses: https://data.forgejo.org/actions/cache/save@v4 with: path: ${{github.workspace}}/public/assets key: frontend-build-${{ github.sha }} @@ -76,7 +71,7 @@ jobs: options: --tmpfs /bitnami/elasticsearch/data env: discovery.type: single-node - ES_JAVA_OPTS: '-Xms512m -Xmx512m' + ES_JAVA_OPTS: "-Xms512m -Xmx512m" minio: image: data.forgejo.org/oci/bitnami/minio:2024.8.17 options: >- @@ -86,7 +81,7 @@ jobs: MINIO_ROOT_USER: 123456 MINIO_ROOT_PASSWORD: 12345678 steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - uses: ./.forgejo/workflows-composite/setup-env - name: test release-notes-assistant.sh run: | @@ -109,17 +104,17 @@ jobs: image: 'data.forgejo.org/oci/playwright:latest' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 with: fetch-depth: 20 - uses: ./.forgejo/workflows-composite/setup-env - - name: 'Restore frontend build' - uses: https://data.forgejo.org/actions/cache/restore@v5 + - name: "Restore frontend build" + uses: https://data.forgejo.org/actions/cache/restore@v4 id: cache-frontend with: path: ${{github.workspace}}/public/assets key: frontend-build-${{ github.sha }} - - name: 'Build frontend (if not cached)' + - name: "Build frontend (if not cached)" if: steps.cache-frontend.outputs.cache-hit != 'true' run: | su forgejo -c 'make deps-frontend frontend' @@ -144,7 +139,7 @@ jobs: RUN_ALL: ${{steps.run-all.all}} - name: Upload test artifacts on failure if: failure() - uses: https://data.forgejo.org/forgejo/upload-artifact@v5 + uses: https://data.forgejo.org/forgejo/upload-artifact@v4 with: name: test-artifacts.zip path: tests/e2e/test-artifacts/ @@ -177,9 +172,9 @@ jobs: image: ${{ matrix.cacher.image }} options: ${{ matrix.cacher.options }} env: - ALLOW_EMPTY_PASSWORD: 'yes' # redis & valkey will immediately shutdown with no defined password unless overridden + ALLOW_EMPTY_PASSWORD: "yes" # redis & valkey will immediately shutdown with no defined password unless overridden steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - uses: ./.forgejo/workflows-composite/setup-env - uses: ./.forgejo/workflows-composite/build-backend - run: | @@ -208,7 +203,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@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - uses: ./.forgejo/workflows-composite/setup-env - name: install dependencies run: apt-get update -qq && apt-get -q install -qq -y git-lfs @@ -246,7 +241,7 @@ jobs: POSTGRESQL_EXTRA_FLAGS: -c full_page_writes=off options: --tmpfs /bitnami/postgresql steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - uses: ./.forgejo/workflows-composite/setup-env - name: install dependencies run: apt-get update -qq && apt-get -q install -qq -y git-lfs @@ -268,7 +263,7 @@ jobs: image: 'data.forgejo.org/oci/node:24-trixie' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - uses: ./.forgejo/workflows-composite/setup-env - name: install dependencies run: apt-get update -qq && apt-get -q install -qq -y git-lfs @@ -296,20 +291,7 @@ jobs: image: 'data.forgejo.org/oci/node:24-trixie' options: --tmpfs /tmp:exec,noatime steps: - - uses: https://data.forgejo.org/actions/checkout@v6 + - uses: https://data.forgejo.org/actions/checkout@v5 - uses: ./.forgejo/workflows-composite/setup-env - run: su forgejo -c 'make deps-backend deps-tools' - run: su forgejo -c 'make security-check' - semgrep: - if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing' - name: semgrep/ci - runs-on: docker - container: - image: 'data.forgejo.org/oci/semgrep:latest' - steps: - - run: apk add nodejs # required for actions/checkout - - uses: https://data.forgejo.org/actions/checkout@v6 - - name: self-check semgrep rules - run: semgrep --test .semgrep/tests/ --config .semgrep/config/ - - name: semgrep ci - run: semgrep ci --config .semgrep/config/ --metrics=off diff --git a/.gitignore b/.gitignore index c524583ce8..ffc493a51d 100644 --- a/.gitignore +++ b/.gitignore @@ -107,7 +107,6 @@ cpu.out /.air /.go-licenses /.cur-deadcode-out -/.deadcode.diff # Files and folders that were previously generated /public/assets/img/webpack diff --git a/.golangci.yml b/.golangci.yml index 5fa6c13860..aed39e3c0e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -15,7 +15,6 @@ linters: - govet - importas - ineffassign - - modernize - nakedret - nolintlint - revive @@ -26,7 +25,6 @@ linters: - unused - usetesting - wastedassign - - nilnil settings: depguard: rules: @@ -46,25 +44,6 @@ linters: 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 - migration-isolation: - list-mode: lax - files: - - "**/models/forgejo_migrations/**" - deny: - - pkg: "forgejo.org/models" - desc: > - Migrations must not import application models. Application models will be the most recent schema for - Forgejo, while migrations will be operating against the database schema that existed when they were - authored. - - pkg: "forgejo.org/services" - desc: > - Migrations must not import application services. Application services will reference application - models which will use the most recent schema for Forgejo, while migrations will be operating against the - database schema that existed when they were authored. - allow: - - "forgejo.org/models/db" - - "forgejo.org/models/gitea_migrations/base" - - "forgejo.org/models/gitea_migrations/test" gocritic: disabled-checks: - ifElseChain @@ -134,8 +113,6 @@ linters: disable: - error-is-as - go-require - nilnil: - only-two: false exclusions: generated: lax presets: @@ -186,215 +163,6 @@ linters: - linters: - staticcheck text: "(ST1005|ST1003|QF1001):" - - # TODO: eventually remove this section entirely - - path: cmd/admin_auth_ldap_test.go - linters: - - nilnil - - path: cmd/admin_auth_oauth_test.go - linters: - - nilnil - - path: cmd/admin_auth_pam_test.go - linters: - - nilnil - - path: cmd/cmd.go - linters: - - nilnil - - path: cmd/forgejo/actions.go - linters: - - nilnil - - path: models/actions/run.go - linters: - - nilnil - - path: models/actions/task.go - linters: - - nilnil - - path: models/activities/action_list.go - linters: - - nilnil - - path: models/asymkey/gpg_key_object_verification.go - linters: - - nilnil - - path: models/auth/oauth2.go - linters: - - nilnil - - path: models/db/collation.go - linters: - - nilnil - - path: models/dbfs/dbfile.go - linters: - - nilnil - - path: models/forgejo_migrations_legacy/v32.go - linters: - - nilnil - - path: models/forgejo_migrations_legacy/v32_test.go - linters: - - nilnil - - path: models/db/context.go - linters: - - nilnil - - path: models/git/branch_list.go - linters: - - nilnil - - path: models/git/lfs_lock.go - linters: - - nilnil - - path: models/git/protected_branch.go - linters: - - nilnil - - path: models/git/protected_tag.go - linters: - - nilnil - - path: models/issues/issue.go - linters: - - nilnil - - path: models/issues/issue_xref.go - linters: - - nilnil - - path: models/issues/review.go - linters: - - nilnil - - path: models/organization/org_user.go - linters: - - nilnil - - path: models/quota/rule.go - linters: - - nilnil - - path: models/repo/archiver.go - linters: - - nilnil - - path: models/repo/fork.go - linters: - - nilnil - - path: models/repo/topic.go - linters: - - nilnil - - path: models/user/email_address.go - linters: - - nilnil - - path: models/user/list.go - linters: - - nilnil - - path: models/user/user.go - linters: - - nilnil - - path: models/repo/repo.go - linters: - - nilnil - - path: modules/git/commit.go - linters: - - nilnil - - path: modules/git/foreachref/parser.go - linters: - - nilnil - - path: modules/git/last_commit_cache.go - linters: - - nilnil - - path: modules/git/log_name_status.go - linters: - - nilnil - - path: modules/graceful/net_unix.go - linters: - - nilnil - - path: modules/indexer/internal/bleve/util.go - linters: - - nilnil - - path: modules/indexer/issues/util.go - linters: - - nilnil - - path: modules/optional/serialization.go - linters: - - nilnil - - path: modules/setting/storage.go - linters: - - nilnil - - path: routers/api/packages/chef/auth.go - linters: - - nilnil - - path: routers/api/packages/container/auth.go - linters: - - nilnil - - path: routers/api/packages/nuget/auth.go - linters: - - nilnil - - path: routers/api/packages/swift/swift.go - linters: - - nilnil - - path: routers/web/auth/oauth.go - linters: - - nilnil - - path: routers/web/repo/compare.go - linters: - - nilnil - - path: routers/web/repo/release.go - linters: - - nilnil - - path: routers/web/repo/setting/runners.go - linters: - - nilnil - - path: routers/web/repo/setting/secrets.go - linters: - - nilnil - - path: routers/web/repo/setting/variables.go - linters: - - nilnil - - path: services/actions/context.go - linters: - - nilnil - - path: services/actions/task.go - linters: - - nilnil - - path: services/actions/trust.go - linters: - - nilnil - - path: services/contexttest/context_tests.go - linters: - - nilnil - - path: services/gitdiff/csv.go - linters: - - nilnil - - path: services/issue/assignee.go - linters: - - nilnil - - path: routers/api/packages/conan/auth.go - linters: - - nilnil - - path: services/issue/commit.go - linters: - - nilnil - - path: services/issue/issue.go - linters: - - nilnil - - path: services/migrations/onedev.go - linters: - - nilnil - - path: services/packages/cargo/index.go - linters: - - nilnil - - path: services/pull/check.go - linters: - - nilnil - - path: services/pull/comment.go - linters: - - nilnil - - path: services/pull/merge.go - linters: - - nilnil - - path: services/pull/review.go - linters: - - nilnil - - path: services/remote/promote.go - linters: - - nilnil - - path: services/repository/archiver/archiver.go - linters: - - nilnil - - path: services/repository/generate_repo_commit.go - linters: - - nilnil - - path: services/repository/repository.go - linters: - - nilnil paths: - node_modules - public diff --git a/.mockery.yml b/.mockery.yml deleted file mode 100644 index 2e9427cd77..0000000000 --- a/.mockery.yml +++ /dev/null @@ -1,17 +0,0 @@ -formatter: gofmt -template: testify -packages: - forgejo.org/modules/nosql: - config: - filename: mocks.go # make mocks public so that external packages can use - forgejo.org/services/auth/method: - forgejo.org/services/authz: - config: - filename: authorization_reducer_mock.go # make mocks public so that external packages can use - code.forgejo.org/go-chi/cache: - interfaces: - Cache: - config: - pkgname: cache - dir: modules/cache - filename: mocks.go # make mocks public, not `_test.go`, so that external packages can mock caching diff --git a/.node-version b/.node-version index eefb690f4a..1e4f3920b5 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -24.15.0 \ No newline at end of file +24.12.0 \ No newline at end of file diff --git a/.release-notes-assistant.yaml b/.release-notes-assistant.yaml index f942778fe7..f8264c0897 100644 --- a/.release-notes-assistant.yaml +++ b/.release-notes-assistant.yaml @@ -7,8 +7,7 @@ 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: -# replace with v15 when v11 becomes EOL - - 'v11.0/forgejo' + - '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"' render-header: | diff --git a/.semgrep/config/auth.yaml b/.semgrep/config/auth.yaml deleted file mode 100644 index 09dacf28c1..0000000000 --- a/.semgrep/config/auth.yaml +++ /dev/null @@ -1,111 +0,0 @@ -rules: - - id: forgejo-api-use-resource-SearchRepoOptions - patterns: - - pattern: | - repo_model.SearchRepoOptions{...} - - pattern-not: | - repo_model.SearchRepoOptions{ - ..., - AuthorizationReducer: ctx.Reducer, - ... - } - languages: - - go - message: > - SearchRepoOptions does not take into account fine-grained access token limitations. Include the - AuthorizationReducer field. - severity: ERROR - paths: - include: - - "/routers/api/**/*.go" - - - id: forgejo-api-use-resource-SearchRepoOptions - patterns: - - pattern: | - organization.SearchTeamRepoOptions{...} - - pattern-not: | - organization.SearchTeamRepoOptions{ - ..., - AuthorizationReducer: ctx.Reducer, - ... - } - languages: - - go - message: > - SearchTeamRepoOptions does not take into account fine-grained access token limitations. Include the - AuthorizationReducer field. - severity: ERROR - paths: - include: - - "/routers/api/**/*.go" - - - id: forgejo-api-use-resource-GetUserRepoPermission - patterns: - - pattern: | - $X.GetUserRepoPermission($CTX, $REPO, $DOER) - - metavariable-type: - metavariable: $CTX - types: - - "*context.APIContext" - languages: - - go - message: > - GetUserRepoPermission does not take into account fine-grained access token limitations. Use - GetUserRepoPermissionWithReducer. - fix: | - $X.GetUserRepoPermissionWithReducer($CTX, $REPO, $DOER, $CTX.Reducer) - severity: ERROR - - - id: forgejo-api-suspicious-GetUserRepoPermission - patterns: - - pattern: $X.GetUserRepoPermission($CTX, $REPO, $DOER) - - pattern-not: # don't match if identical to forgejo-api-use-resource-GetUserRepoPermission - patterns: - - pattern: | - $X.GetUserRepoPermission($CTX, $REPO, $DOER) - - metavariable-type: - metavariable: $CTX - types: - - "*context.APIContext" - languages: - - go - message: > - API code is accessing GetUserRepoPermission which does not take into account fine-grained access token - limitations. Should this use GetUserRepoPermissionWithReducer? - severity: ERROR - paths: - include: - - "/routers/api/**/*.go" - - - id: forgejo-api-direct-IsAdmin-check - patterns: - - pattern: | - ctx.Doer.IsAdmin - languages: - - go - message: | - ctx.Doer.IsAdmin does not take into account limited API access tokens. Use ctx.IsUserSiteAdmin() instead. - fix: | - ctx.IsUserSiteAdmin() - severity: ERROR - paths: - include: - - "/routers/api/**/*.go" - - - id: forgejo-api-direct-repo-Admin-check - patterns: - - pattern: | - ctx.Repo.IsAdmin() - - pattern: | - ctx.Repo.IsOwner() - languages: - - go - message: | - ctx.Repo.IsAdmin/IsOwner() does not take into account limited API access tokens. Use ctx.IsUserRepoAdmin() instead. - fix: | - ctx.IsUserRepoAdmin() - severity: ERROR - paths: - include: - - "/routers/api/**/*.go" - diff --git a/.semgrep/config/go.yaml b/.semgrep/config/go.yaml deleted file mode 100644 index f73e5e43d7..0000000000 --- a/.semgrep/config/go.yaml +++ /dev/null @@ -1,18 +0,0 @@ -rules: - - id: forgejo-switch-empty-case - pattern-either: - - pattern: |- - switch $_ { - case $_: - } - - patterns: - - pattern: |- - switch { - case $_: - } - languages: - - go - severity: ERROR - message: > - switch has a case block with no content. This is treated as "break" by Go, but developers may confuse it for - "fallthrough". To fix this error, disambiguate by using "break" or "fallthrough". diff --git a/.semgrep/config/logic.yaml b/.semgrep/config/logic.yaml deleted file mode 100644 index 85c43f5531..0000000000 --- a/.semgrep/config/logic.yaml +++ /dev/null @@ -1,11 +0,0 @@ -rules: - - id: forgejo-logic-suspicious-OwnerID-check - pattern: |- - $X.OwnerID > 0 - languages: - - go - severity: ERROR - message: > - Many resources like comments or runners cannot only be owned by regular users, which have positive IDs, but also - by predefined system users like Ghost or Forgejo Actions that have negative IDs. In those cases, ownership checks - should only exclude 0: `OwnerID != 0`. diff --git a/.semgrep/config/xorm.yaml b/.semgrep/config/xorm.yaml deleted file mode 100644 index 057e1d3aef..0000000000 --- a/.semgrep/config/xorm.yaml +++ /dev/null @@ -1,24 +0,0 @@ -rules: - - id: xorm-sync-missing-ignore-drop-indices - patterns: - - pattern-either: - - pattern: | - $X.Sync(...) - - pattern: | - $X.SyncWithOptions($OPTS, ...) - - pattern-not: | - $X.SyncWithOptions(xorm.SyncOptions{..., IgnoreDropIndices: true, ...}, ...) - - metavariable-type: - metavariable: $X - types: - - "*xorm.Engine" - - "*xorm.Session" - paths: - exclude: - - /models/gitea_migrations/**/*.go - - /models/forgejo_migrations_legacy/**/*.go - languages: - - go - message: | - xorm Sync operation may drop indices if used on an incomplete bean definition for an existing table. Use SyncWithOptions with IgnoreDropIndices: true instead. - severity: ERROR diff --git a/.semgrep/tests/auth.fixed.go b/.semgrep/tests/auth.fixed.go deleted file mode 100644 index 99703b2005..0000000000 --- a/.semgrep/tests/auth.fixed.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2016 The Gogs Authors. All rights reserved. -// Copyright 2020 The Gitea Authors. -// SPDX-License-Identifier: MIT - -package repo - -import ( - "net/http" - - "forgejo.org/models/db" - access_model "forgejo.org/models/perm/access" - repo_model "forgejo.org/models/repo" - api "forgejo.org/modules/structs" - "forgejo.org/routers/api/v1/utils" - "forgejo.org/services/context" - "forgejo.org/services/convert" -) - -// ListForks list a repository's forks -func ListForks(ctx *context.APIContext) { - forks, total, err := repo_model.GetForks(ctx, ctx.Repo.Repository, ctx.Doer, utils.GetListOptions(ctx)) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetForks", err) - return - } - apiForks := make([]*api.Repository, len(forks)) - for i, fork := range forks { - // ruleid:forgejo-api-use-resource-GetUserRepoPermission - permission, err := access_model.GetUserRepoPermissionWithReducer(ctx, fork, ctx.Doer, ctx.Reducer) - // ok:forgejo-api-use-resource-GetUserRepoPermission - permission, err := access_model.GetUserRepoPermissionWithReducer(ctx, fork, ctx.Doer, ctx.Reducer) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) - return - } - apiForks[i] = convert.ToRepo(ctx, fork, permission) - } -} - -// getStarredRepos returns the repos that the user with the specified userID has -// starred -func getStarredRepos(ctx std_context.Context, user *user_model.User, private bool, listOptions db.ListOptions) ([]*api.Repository, error) { - starredRepos, err := repo_model.GetStarredRepos(ctx, user.ID, private, listOptions) - if err != nil { - return nil, err - } - - repos := make([]*api.Repository, len(starredRepos)) - for i, starred := range starredRepos { - // ruleid:forgejo-api-suspicious-GetUserRepoPermission - permission, err := access_model.GetUserRepoPermission(ctx, starred, user) - if err != nil { - return nil, err - } - repos[i] = convert.ToRepo(ctx, starred, permission) - } - return repos, nil -} diff --git a/.semgrep/tests/auth.go b/.semgrep/tests/auth.go deleted file mode 100644 index 686dc5f9f7..0000000000 --- a/.semgrep/tests/auth.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2016 The Gogs Authors. All rights reserved. -// Copyright 2020 The Gitea Authors. -// SPDX-License-Identifier: MIT - -package repo - -import ( - "net/http" - - "forgejo.org/models/db" - access_model "forgejo.org/models/perm/access" - repo_model "forgejo.org/models/repo" - api "forgejo.org/modules/structs" - "forgejo.org/routers/api/v1/utils" - "forgejo.org/services/context" - "forgejo.org/services/convert" -) - -// ListForks list a repository's forks -func ListForks(ctx *context.APIContext) { - forks, total, err := repo_model.GetForks(ctx, ctx.Repo.Repository, ctx.Doer, utils.GetListOptions(ctx)) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetForks", err) - return - } - apiForks := make([]*api.Repository, len(forks)) - for i, fork := range forks { - // ruleid:forgejo-api-use-resource-GetUserRepoPermission - permission, err := access_model.GetUserRepoPermission(ctx, fork, ctx.Doer) - // ok:forgejo-api-use-resource-GetUserRepoPermission - permission, err := access_model.GetUserRepoPermissionWithReducer(ctx, fork, ctx.Doer, ctx.Reducer) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) - return - } - apiForks[i] = convert.ToRepo(ctx, fork, permission) - } -} - -// getStarredRepos returns the repos that the user with the specified userID has -// starred -func getStarredRepos(ctx std_context.Context, user *user_model.User, private bool, listOptions db.ListOptions) ([]*api.Repository, error) { - starredRepos, err := repo_model.GetStarredRepos(ctx, user.ID, private, listOptions) - if err != nil { - return nil, err - } - - repos := make([]*api.Repository, len(starredRepos)) - for i, starred := range starredRepos { - // ruleid:forgejo-api-suspicious-GetUserRepoPermission - permission, err := access_model.GetUserRepoPermission(ctx, starred, user) - if err != nil { - return nil, err - } - repos[i] = convert.ToRepo(ctx, starred, permission) - } - return repos, nil -} diff --git a/.semgrep/tests/go.go b/.semgrep/tests/go.go deleted file mode 100644 index 14c9a31912..0000000000 --- a/.semgrep/tests/go.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package cmd - -import ( - "context" - "os" - "os/signal" - "strings" - "syscall" - - _ "net/http/pprof" // Used for debugging if enabled and a web server is running - - "forgejo.org/modules/setting" -) - -func setPortEmptyCaseBad(port string) error { - setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1) - setting.HTTPPort = port - - // ruleid:forgejo-switch-empty-case - switch setting.Protocol { - case setting.HTTPUnix: - case setting.FCGI: - case setting.FCGIUnix: - default: - defaultLocalURL := string(setting.Protocol) + "://" - } - - // ok:forgejo-switch-empty-case - switch setting.Protocol { - case setting.HTTPUnix: - break - case setting.FCGI: - break - case setting.FCGIUnix: - break - default: - defaultLocalURL := string(setting.Protocol) + "://" - } - - return nil -} diff --git a/.semgrep/tests/logic.go b/.semgrep/tests/logic.go deleted file mode 100644 index 32a2777ff6..0000000000 --- a/.semgrep/tests/logic.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package actions - -import "xorm.io/builder" - -type FindRunJobOptions struct { - RepoID int64 - OwnerID int64 -} - -func (opts FindRunJobOptions) Bad() builder.Cond { - cond := builder.NewCond() - if opts.RepoID > 0 { - cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) - } - // ruleid:forgejo-logic-suspicious-OwnerID-check - if opts.OwnerID > 0 { - cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) - } - return cond -} - -func (opts FindRunJobOptions) Good() builder.Cond { - cond := builder.NewCond() - if opts.RepoID > 0 { - cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) - } - // ok:forgejo-logic-suspicious-OwnerID-check - if opts.OwnerID != 0 { - cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) - } - return cond -} diff --git a/.semgrep/tests/xorm.go b/.semgrep/tests/xorm.go deleted file mode 100644 index 00b86acee1..0000000000 --- a/.semgrep/tests/xorm.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "io/fs" - - "forgejo.org/modules/timeutil" - - "xorm.io/xorm" -) - -type ActionUser struct { - ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"INDEX UNIQUE(action_user_index) REFERENCES(user, id)"` - RepoID int64 `xorm:"INDEX UNIQUE(action_user_index) REFERENCES(repository, id)"` - - TrustedWithPullRequests bool - - LastAccess timeutil.TimeStamp `xorm:"INDEX"` -} - -func testSyncBad1(x *xorm.Engine) error { - // ruleid:xorm-sync-missing-ignore-drop-indices - return x.Sync(new(ActionUser)) -} - -func testSyncBad2(x *xorm.Engine) error { - // ruleid:xorm-sync-missing-ignore-drop-indices - _, err = x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: false}, bean) - return err -} - -func testSyncGood1(x *xorm.Engine) error { - // ok:xorm-sync-missing-ignore-drop-indices - _, err = x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, bean) - return err -} - -func testSyncGood2(x *fs.File) error { - // ok:xorm-sync-missing-ignore-drop-indices - _, err = x.Sync() - return err -} diff --git a/CODEOWNERS b/CODEOWNERS index c2459d4ab9..4934331197 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -51,12 +51,3 @@ modules/structs/.* @Cyborus routers/api/v1/.* @Cyborus routers/api/forgejo/.* @Cyborus tests/integration/api_.* @Cyborus - -# Federation code, requires care to be taken with regards to interoperability -# and backwards compatibility due to the way signatures work. -services/federation/.* @famfo @0xllx0 -modules/forgefed/.* @famfo @0xllx0 -models/forgefed/.* @famfo @0xllx0 -routers/api/v1/activitypub/.* @famfo @0xllx0 -tests/integration/api_activitypub_.* @famfo @0xllx0 -tests/integration/activitypub_.* @famfo @0xllx0 diff --git a/Dockerfile b/Dockerfile index 2639d91e44..3deba255be 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.26-alpine3.23 AS build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.25-alpine3.23 AS build-env ARG GOPROXY ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct} diff --git a/Dockerfile.rootless b/Dockerfile.rootless index d6e037c522..c4079fd918 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -1,6 +1,6 @@ FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.26-alpine3.23 AS build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.25-alpine3.23 AS build-env ARG GOPROXY ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct} @@ -86,8 +86,8 @@ RUN addgroup \ -G git \ git -RUN mkdir -p /var/lib/gitea -RUN chown git:git /var/lib/gitea +RUN mkdir -p /var/lib/gitea /etc/gitea +RUN chown git:git /var/lib/gitea /etc/gitea COPY --from=build-env /tmp/local / RUN cd /usr/local/bin ; ln -s gitea forgejo @@ -103,9 +103,13 @@ ENV GITEA_CUSTOM=/var/lib/gitea/custom ENV GITEA_TEMP=/tmp/gitea ENV TMPDIR=/tmp/gitea +# Legacy config file for backwards compatibility +# TODO: remove on next major version release +ENV GITEA_APP_INI_LEGACY=/etc/gitea/app.ini + ENV GITEA_APP_INI=${GITEA_CUSTOM}/conf/app.ini ENV HOME="/var/lib/gitea/git" -VOLUME ["/var/lib/gitea"] +VOLUME ["/var/lib/gitea", "/etc/gitea"] WORKDIR /var/lib/gitea ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/docker-entrypoint.sh"] diff --git a/Makefile b/Makefile index 80779cfecc..db363d216b 100644 --- a/Makefile +++ b/Makefile @@ -37,18 +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.6.1 # renovate: datasource=go +EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.6.0 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2 # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.6.2 # 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.2 # renovate: datasource=go +SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.33.1 # renovate: datasource=go XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest -GO_LICENSES_PACKAGE ?= github.com/google/go-licenses/v2@v2.0.1 # renovate: datasource=go +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.45.0 # renovate: datasource=go -ERRORTYPE_PACKAGE ?= fillmore-labs.com/errortype@v0.0.11 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@43.170.20 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate -MOCKERY_PACKAGE ?= github.com/vektra/mockery/v3@v3.7.0 # renovate: datasource=go +DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.39.0 # renovate: datasource=go +GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.6.0 # renovate: datasource=go +RENOVATE_NPM_PACKAGE ?= renovate@42.39.2 # 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: ... @@ -245,7 +244,7 @@ help: @echo " - generate-license update license files" @echo " - generate-gitignore update gitignore files" @echo " - generate-manpage generate manpage" - @echo " - generate-mockery generate mockery files" + @echo " - generate-gomock generate gomock files" @echo " - generate-forgejo-api generate the forgejo API from spec" @echo " - forgejo-api-validate check if the forgejo API matches the specs" @echo " - generate-swagger generate the swagger spec from code comments" @@ -323,7 +322,7 @@ git-check: node-check: $(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p')) $(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' '))) - $(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | sed 's:-.*::' | tr '.' ' ');)) + $(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');)) $(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1)) @if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \ echo "Forgejo requires Node.js $(MIN_NODE_VERSION_STR) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \ @@ -466,7 +465,7 @@ lint-swagger: node_modules .PHONY: lint-renovate lint-renovate: node_modules - npx --yes --package $(RENOVATE_NPM_PACKAGE) -- renovate-config-validator --no-global .forgejo/renovate.json > .lint-renovate 2>&1 || true + npx --yes --package $(RENOVATE_NPM_PACKAGE) -- renovate-config-validator > .lint-renovate 2>&1 || true @if grep --quiet --extended-regexp -e '^( ERROR:)' .lint-renovate ; then cat .lint-renovate ; rm .lint-renovate ; exit 1 ; fi @rm .lint-renovate @@ -486,23 +485,10 @@ RUN_DEADCODE = $(GO) run $(DEADCODE_PACKAGE) -generated=false -f='{{println .Pat .PHONY: lint-go lint-go: - $(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GOLANGCI_LINT_ARGS) \ - || (code=$$?; echo "Please run 'make lint-go-fix' and commit the result"; exit $${code}) + $(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GOLANGCI_LINT_ARGS) $(RUN_DEADCODE) > .cur-deadcode-out - @$(DIFF) .deadcode-out .cur-deadcode-out >.deadcode.diff || true - @if grep -qE '^[+][^+]' .deadcode.diff ; then \ - cat .deadcode.diff ; \ - echo "Looks like you added dead code, please evaluate and remove or use it."; \ - echo "If you are sure the dead code should stay around, please run 'make lint-go-fix',"; \ - echo "commit the result and explain the reason in the commit message / PR description."; \ - exit 1; \ - fi - @if grep -qE '^[-][^-]' .deadcode.diff ; then \ - cat .deadcode.diff ; \ - echo "Looks like you removed dead code. Thank you!"; \ - echo "Run 'make lint-go-fix' and commit the result to accept."; \ - fi - $(GO) run $(ERRORTYPE_PACKAGE) ./... + @$(DIFF) .deadcode-out .cur-deadcode-out \ + || (code=$$?; echo "Please run 'make lint-go-fix' and commit the result"; exit $${code}) .PHONY: lint-go-fix lint-go-fix: @@ -534,11 +520,6 @@ security-check: tsc: node_modules npx tsc --noEmit -# target for PRs to be pushed. Mandatory to succeed in CI -.PHONY: pr-go -pr-go: deps-backend deps-tools lint-backend tidy-check swagger-check lint-swagger fmt-check swagger-validate - TAGS=bindata $(MAKE) backend - ### # Development and testing targets ### @@ -562,12 +543,12 @@ test: test-frontend test-backend .PHONY: test-backend test-backend: | compute-go-test-packages @echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." - @TZ=UTC GITEA_ROOT="$(CURDIR)" $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES) + @TZ=UTC $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES) .PHONY: test-remote-cacher test-remote-cacher: @echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." - GITEA_ROOT="$(CURDIR)" $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_REMOTE_CACHER_PACKAGES) + @$(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_REMOTE_CACHER_PACKAGES) .PHONY: test-frontend test-frontend: node_modules @@ -592,7 +573,7 @@ test-check: .PHONY: test\#% test\#%: | compute-go-test-packages @echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." - @TZ=UTC GITEA_ROOT="$(CURDIR)" $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES) + @TZ=UTC $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES) coverage-merge: rm -fr coverage/merged ; mkdir -p coverage/merged @@ -968,13 +949,16 @@ deps-tools: $(GO) install $(XGO_PACKAGE) $(GO) install $(GO_LICENSES_PACKAGE) $(GO) install $(GOVULNCHECK_PACKAGE) - $(GO) install $(ERRORTYPE_PACKAGE) - $(GO) install $(MOCKERY_PACKAGE) + $(GO) install $(GOMOCK_PACKAGE) node_modules: package-lock.json npm install --no-save @touch node_modules +.venv: poetry.lock + poetry install + @touch .venv + .PHONY: fomantic fomantic: rm -rf $(FOMANTIC_WORK_DIR)/build @@ -1024,9 +1008,9 @@ generate-license: generate-gitignore: $(GO) run build/generate-gitignores.go -.PHONY: generate-mockery -generate-mockery: - $(GO) run $(MOCKERY_PACKAGE) +.PHONY: generate-gomock +generate-gomock: + $(GO) run $(GOMOCK_PACKAGE) -package mock -destination ./modules/queue/mock/redisuniversalclient.go forgejo.org/modules/nosql RedisClient .PHONY: generate-images generate-images: | node_modules diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 7f1498042c..5843e169e4 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -389,11 +389,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/blevesearch/zapx/v17", - "path": "github.com/blevesearch/zapx/v17/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", @@ -464,6 +459,11 @@ "path": "github.com/davecgh/go-spew/spew/LICENSE", "licenseText": "ISC License\n\nCopyright (c) 2012-2016 Dave Collins \u003cdave@davec.name\u003e\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\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n" }, + { + "name": "github.com/dgryski/go-rendezvous", + "path": "github.com/dgryski/go-rendezvous/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2017-2020 Damian Gryski \u003cdamian@gryski.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\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": "github.com/djherbis/buffer", "path": "github.com/djherbis/buffer/LICENSE.txt", @@ -549,11 +549,6 @@ "path": "github.com/fxamacker/cbor/v2/LICENSE", "licenseText": "MIT License\n\nCopyright (c) 2019-present Faye Amacker\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." }, - { - "name": "github.com/gdgvda/cron", - "path": "github.com/gdgvda/cron/LICENSE", - "licenseText": "Copyright (C) 2012 Rob Figueiredo\nAll Rights Reserved.\n\nMIT LICENSE\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" - }, { "name": "github.com/gliderlabs/ssh", "path": "github.com/gliderlabs/ssh/LICENSE", @@ -609,6 +604,21 @@ "path": "github.com/go-fed/httpsig/LICENSE", "licenseText": "BSD 3-Clause License\n\nCopyright (c) 2018, go-fed\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/go-git/gcfg", + "path": "github.com/go-git/gcfg/LICENSE", + "licenseText": "Copyright (c) 2012 Péter Surányi. Portions Copyright (c) 2009 The Go\nAuthors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + }, + { + "name": "github.com/go-git/go-billy/v5", + "path": "github.com/go-git/go-billy/v5/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 2017 Sourced Technologies S.L.\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/go-git/go-git/v5", + "path": "github.com/go-git/go-git/v5/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 Sourced Technologies, S.L.\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/go-ini/ini", "path": "github.com/go-ini/ini/LICENSE", @@ -624,20 +634,10 @@ "path": "github.com/go-sql-driver/mysql/LICENSE", "licenseText": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in \n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party's\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party's negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n" }, - { - "name": "github.com/go-viper/mapstructure/v2", - "path": "github.com/go-viper/mapstructure/v2/LICENSE", - "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2013 Mitchell Hashimoto\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": "github.com/go-webauthn/webauthn", "path": "github.com/go-webauthn/webauthn/LICENSE", - "licenseText": "Copyright (c) 2025 github.com/go-webauthn/webauthn authors.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in the\n documentation and/or other materials provided with the distribution.\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\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT 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." - }, - { - "name": "github.com/go-webauthn/x/encoding/asn1", - "path": "github.com/go-webauthn/x/encoding/asn1/LICENSE", - "licenseText": "Copyright (c) 2021-2023 github.com/go-webauthn authors.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the\nfollowing conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following\n disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following\n disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products\n derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\nINCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\nTHIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + "licenseText": "Copyright (c) 2017 Duo Security, Inc. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in the\n documentation and/or other materials provided with the distribution.\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\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT 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\n\nCopyright (c) 2021-2022 github.com/go-webauthn/webauthn authors.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the\nfollowing conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following\n disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following\n disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products\n derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\nINCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\nTHIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." }, { "name": "github.com/go-webauthn/x/revoke", @@ -654,6 +654,11 @@ "path": "github.com/gobwas/glob/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Sergey Kamardin\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." }, + { + "name": "github.com/goccy/go-json", + "path": "github.com/goccy/go-json/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2020 Masaaki Goshima\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/gogs/chardet", "path": "github.com/gogs/chardet/LICENSE", @@ -664,6 +669,11 @@ "path": "github.com/gogs/go-gogs-client/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Go Git Service\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/golang-jwt/jwt/v4", + "path": "github.com/golang-jwt/jwt/v4/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-jwt/jwt/v5", "path": "github.com/golang-jwt/jwt/v5/LICENSE", @@ -794,6 +804,11 @@ "path": "github.com/jackc/puddle/v2/LICENSE", "licenseText": "Copyright (c) 2018 Jack Christensen\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" }, + { + "name": "github.com/jbenet/go-context/io", + "path": "github.com/jbenet/go-context/io/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Juan Batiz-Benet\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": "github.com/jhillyerd/enmime/v2", "path": "github.com/jhillyerd/enmime/v2/LICENSE", @@ -844,16 +859,16 @@ "path": "github.com/klauspost/cpuid/v2/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Klaus Post\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/klauspost/crc32", - "path": "github.com/klauspost/crc32/LICENSE", - "licenseText": "Copyright (c) 2012 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" - }, { "name": "github.com/klauspost/pgzip", "path": "github.com/klauspost/pgzip/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Klaus Post\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/lib/pq", + "path": "github.com/lib/pq/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2011-2013, 'pq' Contributors. Portions Copyright (c) 2011 Blake Mizerany\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/libdns/libdns", "path": "github.com/libdns/libdns/LICENSE", @@ -944,6 +959,11 @@ "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", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2013 Mitchell Hashimoto\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": "github.com/modern-go/concurrent", "path": "github.com/modern-go/concurrent/LICENSE", @@ -954,11 +974,6 @@ "path": "github.com/modern-go/reflect2/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/mschoch/smat", - "path": "github.com/mschoch/smat/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/munnerz/goautoneg", "path": "github.com/munnerz/goautoneg/LICENSE", @@ -1114,11 +1129,6 @@ "path": "github.com/ssor/bom/LICENSE", "licenseText": "MIT License\n\nCopyright (c) 2017 Asher\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/stretchr/objx", - "path": "github.com/stretchr/objx/LICENSE", - "licenseText": "The MIT License\n\nCopyright (c) 2014 Stretchr, Inc.\nCopyright (c) 2017-2018 objx 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 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/stretchr/testify", "path": "github.com/stretchr/testify/LICENSE", @@ -1154,6 +1164,11 @@ "path": "github.com/x448/float16/LICENSE", "licenseText": "MIT License\n\nCopyright (c) 2019 Montgomery Edwards⁴⁴⁸ and Faye Amacker\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/yohcop/openid-go", + "path": "github.com/yohcop/openid-go/LICENSE", + "licenseText": "Copyright 2015 Yohann Coppel\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": "github.com/yuin/goldmark-highlighting/v2", "path": "github.com/yuin/goldmark-highlighting/v2/LICENSE", @@ -1169,11 +1184,6 @@ "path": "github.com/zeebo/blake3/LICENSE", "licenseText": "This work is released into the public domain with CC0 1.0.\n\n-------------------------------------------------------------------------------\n\nCreative Commons Legal Code\n\nCC0 1.0 Universal\n\n CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN\n ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS\n PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM\n THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED\n HEREUNDER.\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator\nand subsequent owner(s) (each and all, an \"owner\") of an original work of\nauthorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for\nthe purpose of contributing to a commons of creative, cultural and\nscientific works (\"Commons\") that the public can reliably and without fear\nof later claims of infringement build upon, modify, incorporate in other\nworks, reuse and redistribute as freely as possible in any form whatsoever\nand for any purposes, including without limitation commercial purposes.\nThese owners may contribute to the Commons to promote the ideal of a free\nculture and the further production of creative, cultural and scientific\nworks, or to gain reputation or greater distribution for their Work in\npart through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any\nexpectation of additional consideration or compensation, the person\nassociating CC0 with a Work (the \"Affirmer\"), to the extent that he or she\nis an owner of Copyright and Related Rights in the Work, voluntarily\nelects to apply CC0 to the Work and publicly distribute the Work under its\nterms, with knowledge of his or her Copyright and Related Rights in the\nWork and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be\nprotected by copyright and related or neighboring rights (\"Copyright and\nRelated Rights\"). Copyright and Related Rights include, but are not\nlimited to, the following:\n\n i. the right to reproduce, adapt, distribute, perform, display,\n communicate, and translate a Work;\n ii. moral rights retained by the original author(s) and/or performer(s);\niii. publicity and privacy rights pertaining to a person's image or\n likeness depicted in a Work;\n iv. rights protecting against unfair competition in regards to a Work,\n subject to the limitations in paragraph 4(a), below;\n v. rights protecting the extraction, dissemination, use and reuse of data\n in a Work;\n vi. database rights (such as those arising under Directive 96/9/EC of the\n European Parliament and of the Council of 11 March 1996 on the legal\n protection of databases, and under any national implementation\n thereof, including any amended or successor version of such\n directive); and\nvii. other similar, equivalent or corresponding rights throughout the\n world based on applicable law or treaty, and any national\n implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention\nof, applicable law, Affirmer hereby overtly, fully, permanently,\nirrevocably and unconditionally waives, abandons, and surrenders all of\nAffirmer's Copyright and Related Rights and associated claims and causes\nof action, whether now known or unknown (including existing as well as\nfuture claims and causes of action), in the Work (i) in all territories\nworldwide, (ii) for the maximum duration provided by applicable law or\ntreaty (including future time extensions), (iii) in any current or future\nmedium and for any number of copies, and (iv) for any purpose whatsoever,\nincluding without limitation commercial, advertising or promotional\npurposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each\nmember of the public at large and to the detriment of Affirmer's heirs and\nsuccessors, fully intending that such Waiver shall not be subject to\nrevocation, rescission, cancellation, termination, or any other legal or\nequitable action to disrupt the quiet enjoyment of the Work by the public\nas contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason\nbe judged legally invalid or ineffective under applicable law, then the\nWaiver shall be preserved to the maximum extent permitted taking into\naccount Affirmer's express Statement of Purpose. In addition, to the\nextent the Waiver is so judged Affirmer hereby grants to each affected\nperson a royalty-free, non transferable, non sublicensable, non exclusive,\nirrevocable and unconditional license to exercise Affirmer's Copyright and\nRelated Rights in the Work (i) in all territories worldwide, (ii) for the\nmaximum duration provided by applicable law or treaty (including future\ntime extensions), (iii) in any current or future medium and for any number\nof copies, and (iv) for any purpose whatsoever, including without\nlimitation commercial, advertising or promotional purposes (the\n\"License\"). The License shall be deemed effective as of the date CC0 was\napplied by Affirmer to the Work. Should any part of the License for any\nreason be judged legally invalid or ineffective under applicable law, such\npartial invalidity or ineffectiveness shall not invalidate the remainder\nof the License, and in such case Affirmer hereby affirms that he or she\nwill not (i) exercise any of his or her remaining Copyright and Related\nRights in the Work or (ii) assert any associated claims and causes of\naction with respect to the Work, in either case contrary to Affirmer's\nexpress Statement of Purpose.\n\n4. Limitations and Disclaimers.\n\n a. No trademark or patent rights held by Affirmer are waived, abandoned,\n surrendered, licensed or otherwise affected by this document.\n b. Affirmer offers the Work as-is and makes no representations or\n warranties of any kind concerning the Work, express, implied,\n statutory or otherwise, including without limitation warranties of\n title, merchantability, fitness for a particular purpose, non\n infringement, or the absence of latent or other defects, accuracy, or\n the present or absence of errors, whether or not discoverable, all to\n the greatest extent permissible under applicable law.\n c. Affirmer disclaims responsibility for clearing rights of other persons\n that may apply to the Work or any use thereof, including without\n limitation any person's Copyright and Related Rights in the Work.\n Further, Affirmer disclaims responsibility for obtaining any necessary\n consents, permissions or other rights required for any use of the\n Work.\n d. Affirmer understands and acknowledges that Creative Commons is not a\n party to this document and has no duty or obligation with respect to\n this CC0 or use of the Work.\n" }, - { - "name": "github.com/zeebo/xxh3", - "path": "github.com/zeebo/xxh3/LICENSE", - "licenseText": "BSD 2-Clause License\n\nCopyright (c) 2012-2014, Yann Collet\nCopyright (c) 2019, Jeff Wendling\nAll rights reserved.\n\nxxHash Library\n\nRedistribution and use in source and binary forms, with or without modification,\nare 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, this\n list of conditions and the following disclaimer in the documentation and/or\n other materials provided with the distribution.\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 OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, 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 ON\nANY 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": "gitlab.com/gitlab-org/api/client-go", "path": "gitlab.com/gitlab-org/api/client-go/LICENSE", @@ -1197,7 +1207,7 @@ { "name": "go.uber.org/zap", "path": "go.uber.org/zap/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" + "licenseText": "Copyright (c) 2016-2017 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.uber.org/zap/exp/zapslog", @@ -1279,6 +1289,11 @@ "path": "gopkg.in/ini.v1/LICENSE", "licenseText": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n Copyright 2014 Unknwon\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/warnings.v0", + "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", diff --git a/build/lint-locale-usage/allowed-masked-usage.txt b/build/lint-locale-usage/allowed-masked-usage.txt index 205d3c3cca..cfab25a5fd 100644 --- a/build/lint-locale-usage/allowed-masked-usage.txt +++ b/build/lint-locale-usage/allowed-masked-usage.txt @@ -6,12 +6,39 @@ translation_meta.test # 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 index 5b68543e9d..b1757fa0fc 100644 --- a/build/lint-locale-usage/bin/handle-go.go +++ b/build/lint-locale-usage/bin/handle-go.go @@ -15,7 +15,6 @@ import ( "strings" llu "forgejo.org/build/lint-locale-usage" - lluAsymKey "forgejo.org/models/asymkey/lint-locale-usage" lluUnit "forgejo.org/models/unit/lint-locale-usage" lluMigrate "forgejo.org/services/migrations/lint-locale-usage" ) @@ -37,172 +36,142 @@ func HandleGoFile(handler llu.Handler, fname string, src any) error { } ast.Inspect(node, func(n ast.Node) bool { - return HandleGoNode(handler, fset, fname, n) - }) + // search for function calls of the form `anything.Tr(any-string-lit, ...)` - return nil -} - -func HandleGoNode(handler llu.Handler, fset *token.FileSet, fname string, 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)], "") + switch n2 := n.(type) { + case *ast.CallExpr: + if len(n2.Args) == 0 { + return true } - } - - 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) - } else if strings.Contains(fname, "models/asymkey/") { - lluAsymKey.HandleCompositeErrorReason(handler, fset, n2) - } - - case *ast.FuncDecl: - if matchInsPrefix := handler.HandleGoCommentGroup(fset, n2.Doc, "llu:returnsTrKeyWeak"); 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)) + funSel, ok := n2.Fun.(*ast.SelectorExpr) + if !ok { 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 + 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)], "") } - return true - }) - } - - if matchInsPrefix := handler.HandleGoCommentGroup(fset, n2.Doc, "llu:returnsTrKey"); 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 - if ret, ok := n.(*ast.ReturnStmt); ok { - for _, res := range ret.Results { - handler.HandleGoTrArgument(fset, res, *matchInsPrefix) - } - return false - } else if _, ok := n.(*ast.FuncDecl); ok { - ast.Inspect(n, func(n2 ast.Node) bool { - return HandleGoNode(handler, fset, fname, n2) - }) - // don't search inside nested functions for return stmts - return false + 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 } - 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) + 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 }) } - 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 + 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 _, field := range structNode.Fields.List { - if field.Names == nil { + 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 } - 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") + for _, field := range structNode.Fields.List { + if field.Names == nil { continue } - tag := reflect.StructTag(rawTag) - if tag.Get("form") == "-" { + if len(field.Names) != 1 { + handler.OnWarning(fset, field.Type.Pos(), "unsupported multiple field names") continue } - tmp := tag.Get("locale") - if len(tmp) != 0 { - msgidPos = field.Tag.ValuePos - msgid = tmp + 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) } - handler.OnMsgid(fset, msgidPos, msgid, true) } } } - } - return 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 index f04b14f3b2..d9cdf9f321 100644 --- a/build/lint-locale-usage/bin/lint-locale-usage.go +++ b/build/lint-locale-usage/bin/lint-locale-usage.go @@ -13,7 +13,6 @@ import ( "io/fs" "os" "path/filepath" - "regexp" "sort" "strings" @@ -45,57 +44,12 @@ type StringTrie interface { type StringTrieMap map[string]StringTrie -func printfPatternToRegex(key string) (string, bool) { - parts := strings.Split(key, "%") - if len(parts) < 2 { - return key, false - } - var pattern strings.Builder - pattern.WriteString("^") - pattern.WriteString(parts[0]) - skip := false - for _, part := range parts[1:] { - if skip { - skip = false - continue - } - if len(part) == 0 { - // "%%" - pattern.WriteString("%") - continue - } - switch part[0] { - case 'd': - pattern.WriteString("[0-9]+") - default: - pattern.WriteString("[A-Za-z0-9]*") - } - pattern.WriteString(part[1:]) - } - pattern.WriteString("$") - return pattern.String(), true -} - func (m StringTrieMap) Matches(key []string) bool { if len(key) == 0 || m == nil { return true } value, ok := m[key[0]] if !ok { - for altKey, value := range m { - // TODO: cache mapping $printfFormatString -> $regexpCompileOutput - pattern, found := printfPatternToRegex(altKey) - if !found { - continue - } - matched, err := regexp.MatchString(pattern, key[0]) - if err != nil { - panic(fmt.Sprintf("unable to compile regexp '%s': %s", pattern, err.Error())) - } - if matched && (value == nil || value.Matches(key[1:])) { - return true - } - } return false } if value == nil { @@ -147,7 +101,7 @@ func ParseAllowedMaskedUsages(fname string, usedMsgids container.Set[string], al if line == "" || strings.HasPrefix(line, "#") { continue } - if linePrefix, found := strings.CutSuffix(line, "."); found || strings.Contains(line, "%") { + if linePrefix, found := strings.CutSuffix(line, "."); found { allowedMaskedPrefixes.Insert(strings.Split(linePrefix, ".")) } else { if !chkMsgid(line) { @@ -191,14 +145,9 @@ func Usage() { fmt.Fprintf(outp, "\nSpecial Go doc comments:\n") for _, i := range []string{ - "//llu:returnsTrKeyWeak", - "\tcan be used in front of functions to indicate", - "\tthat the function returns message IDs (allows nesting inside complicated function calls)", - "\tWARNING: this currently doesn't support nested functions properly", - "", "//llu:returnsTrKey", "\tcan be used in front of functions to indicate", - "\tthat the function returns message IDs (doesn't allow nesting inside complicated function calls)", + "\tthat the function returns message IDs", "\tWARNING: this currently doesn't support nested functions properly", "", "//llu:returnsTrKeySuffix prefix.", @@ -311,10 +260,6 @@ func main() { } handler := llu.Handler{ - OnMsgidPattern: func(fset *token.FileSet, pos token.Pos, msgidPattern string) { - msgidPatternSplit := strings.Split(msgidPattern, ".") - allowedMaskedPrefixes.Insert(msgidPatternSplit) - }, OnMsgidPrefix: func(fset *token.FileSet, pos token.Pos, msgidPrefix string, truncated bool) { msgidPrefixSplit := strings.Split(msgidPrefix, ".") if !truncated { @@ -325,10 +270,6 @@ func main() { } }, OnMsgid: func(fset *token.FileSet, pos token.Pos, msgid string, weak bool) { - if strings.Contains(msgid, "%") { - fmt.Printf("%s:\tunexpected msgid pattern: %s\n", fset.Position(pos).String(), msgid) - return - } if !msgids.Contains(msgid) { if weak && allowWeakMissingMsgids { return @@ -361,7 +302,7 @@ func main() { if name == "docker" || name == ".git" || name == "node_modules" { return fs.SkipDir } - } else if name == "bindata.go" || fpath == "modules/translation/i18n/i18n_test.go" || fpath == "modules/translation/i18n/i18n_ini_test.go" { + } 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)) diff --git a/build/lint-locale-usage/handle-go.go b/build/lint-locale-usage/handle-go.go index a8e478a6ef..44229e52f7 100644 --- a/build/lint-locale-usage/handle-go.go +++ b/build/lint-locale-usage/handle-go.go @@ -34,14 +34,12 @@ func (handler Handler) HandleGoTrBasicLit(fset *token.FileSet, argLit *ast.Basic } func (handler Handler) HandleGoTrArgument(fset *token.FileSet, n ast.Expr, prefix string) { - switch n := n.(type) { - case *ast.BasicLit: - handler.HandleGoTrBasicLit(fset, n, prefix) - - case *ast.BinaryExpr: - if n.Op != token.ADD { + 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 := n.X.(*ast.BasicLit); ok && argLit.Kind == token.STRING { + } 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 { @@ -55,39 +53,6 @@ func (handler Handler) HandleGoTrArgument(fset *token.FileSet, n ast.Expr, prefi } handler.OnMsgidPrefix(fset, argLit.ValuePos, prep, trunc) } - - case *ast.CallExpr: - if selExpr, ok := n.Fun.(*ast.SelectorExpr); ok { - if xIdent, xok := selExpr.X.(*ast.Ident); !xok || xIdent.Name != "fmt" { - return - } - if selExpr.Sel.Name != "Sprintf" { - handler.OnWarning(fset, selExpr.Sel.NamePos, fmt.Sprintf("unexpected formatting function encountered: %s", selExpr.Sel.Name)) - return - } - if len(n.Args) == 0 { - handler.OnWarning(fset, selExpr.Sel.NamePos, fmt.Sprintf("unexpected formatting function invocation (no arguments) of '%s'", selExpr.Sel.Name)) - return - } - - if argLit, ok := n.Args[0].(*ast.BasicLit); ok && argLit.Kind == token.STRING { - // extract string content - arg, err := strconv.Unquote(argLit.Value) - if err != nil { - return - } - if strings.Contains(arg, " ") { - handler.OnWarning(fset, argLit.ValuePos, fmt.Sprintf( - "formatting function invocation of '%s' with weird msgid format string: %s", - selExpr.Sel.Name, - arg, - )) - return - } - // found interesting strings - handler.OnMsgidPattern(fset, argLit.ValuePos, prefix+arg) - } - } } } diff --git a/build/lint-locale-usage/handle-tmpl.go b/build/lint-locale-usage/handle-tmpl.go index e37c1eb486..8d03291205 100644 --- a/build/lint-locale-usage/handle-tmpl.go +++ b/build/lint-locale-usage/handle-tmpl.go @@ -60,13 +60,9 @@ func (handler Handler) handleTemplateNode(fset *token.FileSet, node tmplParser.N case tmplParser.NodeField: nodeField := nodeCommand.Args[0].(*tmplParser.FieldNode) - if len(nodeField.Ident) != 2 || nodeField.Ident[0] != "locale" { + if len(nodeField.Ident) != 2 || !(nodeField.Ident[0] == "locale" || nodeField.Ident[0] == "Locale") { return } - resolvedPos := fset.PositionFor(token.Pos(nodeCommand.Pos), false) - if !strings.Contains(resolvedPos.Filename, "templates/mail/") { - handler.OnWarning(fset, token.Pos(nodeCommand.Pos), "encountered unexpected .locale usage") - } funcname = nodeField.Ident[1] case tmplParser.NodeVariable: @@ -150,12 +146,16 @@ func (handler Handler) handleTemplateMsgid(fset *token.FileSet, node tmplParser. handler.OnMsgid(fset, stringPos, msgidPrefix, false) } else { if nodeIdent.Ident == "printf" { - // found interesting strings - if !(strings.HasSuffix(msgidPrefix, ".%s") && strings.Count(msgidPrefix, "%") == 1) { - handler.OnMsgidPattern(fset, stringPos, msgidPrefix) + 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 = strings.TrimSuffix(msgidPrefix, "%s") + msgidPrefix = parts[0] } msgidPrefixFin, truncated := PrepareMsgidPrefix(msgidPrefix) diff --git a/build/lint-locale-usage/handler.go b/build/lint-locale-usage/handler.go index ef517cd040..6673ac3a4d 100644 --- a/build/lint-locale-usage/handler.go +++ b/build/lint-locale-usage/handler.go @@ -47,7 +47,6 @@ func InitLocaleTrFunctions() map[string][]uint { 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) - OnMsgidPattern func(fset *token.FileSet, pos token.Pos, msgidPattern string) 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 diff --git a/cmd/admin.go b/cmd/admin.go index fe212cc388..60b25eb971 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -47,6 +47,7 @@ func subcmdRegenerate() *cli.Command { Name: "regenerate", Usage: "Regenerate specific files", Commands: []*cli.Command{ + microcmdRegenHooks, microcmdRegenKeys, }, } diff --git a/cmd/admin_regenerate.go b/cmd/admin_regenerate.go index 4620a7106d..4d14df317d 100644 --- a/cmd/admin_regenerate.go +++ b/cmd/admin_regenerate.go @@ -7,15 +7,36 @@ import ( "context" asymkey_model "forgejo.org/models/asymkey" + "forgejo.org/modules/graceful" + repo_service "forgejo.org/services/repository" "github.com/urfave/cli/v3" ) -var microcmdRegenKeys = &cli.Command{ - Name: "keys", - Usage: "Regenerate authorized_keys file", - Before: noDanglingArgs, - Action: runRegenerateKeys, +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 { + ctx, cancel := installSignals(ctx) + defer cancel() + + if err := initDB(ctx); err != nil { + return err + } + return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext()) } func runRegenerateKeys(ctx context.Context, c *cli.Command) error { diff --git a/cmd/admin_user.go b/cmd/admin_user.go index ea62bd3a45..f4f6fb49af 100644 --- a/cmd/admin_user.go +++ b/cmd/admin_user.go @@ -17,7 +17,6 @@ func subcmdUser() *cli.Command { microcmdUserChangePassword(), microcmdUserDelete(), microcmdUserGenerateAccessToken(), - microcmdUserCreateAuthorizedIntegration(), microcmdUserMustChangePassword(), microcmdUserResetMFA(), }, diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index 2881091589..e3800bdb59 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -205,15 +205,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error { // create the access token if accessTokenScope != "" { - t := &auth_model.AccessToken{ - Name: accessTokenName, - UID: u.ID, - Scope: accessTokenScope, - - // maintain legacy behaviour until new CLI options are added -- token has access to all resources, is not - // fine-grained - ResourceAllRepos: true, - } + t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope} if err := auth_model.NewAccessToken(ctx, t); err != nil { return err } diff --git a/cmd/admin_user_generate_access_token.go b/cmd/admin_user_generate_access_token.go index 8b1d14f946..0a3a7fa89d 100644 --- a/cmd/admin_user_generate_access_token.go +++ b/cmd/admin_user_generate_access_token.go @@ -86,10 +86,6 @@ func runGenerateAccessToken(ctx context.Context, c *cli.Command) error { } t.Scope = accessTokenScope - // maintain legacy behaviour until new CLI options are added -- token has access to all resources, is not - // fine-grained - t.ResourceAllRepos = true - // create the token if err := auth_model.NewAccessToken(ctx, t); err != nil { return err diff --git a/cmd/admin_user_generate_authorized_integration.go b/cmd/admin_user_generate_authorized_integration.go deleted file mode 100644 index 262f35161d..0000000000 --- a/cmd/admin_user_generate_authorized_integration.go +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package cmd - -import ( - "bytes" - "context" - "errors" - "fmt" - "os" - "strings" - - auth_model "forgejo.org/models/auth" - "forgejo.org/models/db" - "forgejo.org/models/repo" - user_model "forgejo.org/models/user" - "forgejo.org/modules/json" - "forgejo.org/services/authz" - - "github.com/urfave/cli/v3" -) - -func microcmdUserCreateAuthorizedIntegration() *cli.Command { - return &cli.Command{ - Name: "create-authorized-integration", - Description: `Creates an authorized integration. Authorized integrations allow Forgejo to -receive JWTs from external sources, validate their claims against -user-defined rules, and grant access to Forgejo's API on behalf of a user. - -The issuer may be set to "urn:forgejo:authorized-integrations:actions" -to support JWTs from the local instance's Forgejo Actions, utilizing the -enable-openid-connect flag in a workflow.`, - - // `--claim-in sub=v1,v2,v3` needs to be parsed as a single parameter so that we can comma-split the value into - // an array. To accomplish this, we disable urfave 's slice flag separator, which would cause this to be - // treated as "sub=v1", "v2=?", and "v3=?", resulting in an error of missing values. - DisableSliceFlagSeparator: true, - - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "username", - Aliases: []string{"u"}, - Usage: "Username", - Required: true, - }, - &cli.StringFlag{ - Name: "name", - Usage: "Name of the authorized integration for later identification", - Required: true, - }, - &cli.StringFlag{ - Name: "description", - Usage: "Optional description for the authorized integration", - }, - - // JWT validation: - &cli.StringFlag{ - Name: "issuer", - Usage: `JWT issuer ('iss' claim), example: https://forgejo.example.org/api/actions`, - Required: true, - }, - &cli.StringMapFlag{ - Name: "claim-eq", - Value: map[string]string{}, - Usage: `Zero-or-more claim equality checks, formatted as claim=value, example: "actor=someuser"`, - }, - &cli.StringMapFlag{ - Name: "claim-in", - Value: map[string]string{}, - Usage: `Zero-or-more claim equality in list checks, formatted as claim=value1,value2,... example: "actor=user1,user2"`, - }, - &cli.StringMapFlag{ - Name: "claim-glob", - Value: map[string]string{}, - Usage: `Zero-or-more claim glob checks, formatted as claim=value, example: "sub=repo:forgejo/*:pull_request"`, - }, - &cli.StringMapFlag{ - Name: "claim-glob-in", - Value: map[string]string{}, - Usage: `Zero-or-more claim glob in list checks, formatted as claim=va*ue1,va*ue2,... example: "sub=repo:*/*:pull_request,repo:*/*:refs:*"`, - }, - // nested claim support omitted for now -- pretty complex for a CLI - - // Permissions available on successful auth: - &cli.StringSliceFlag{ - Name: "scope", - Value: []string{"all"}, - Usage: `One-or-more scopes to apply to access token, examples: "all", "read:issue", "write:repository"`, - }, - &cli.StringSliceFlag{ - Name: "repo", - Value: []string{"all"}, - Usage: `Zero-or-more specific repositories that can be accessed, or "all" to allow access to all repositories, example: "owner1/repo1"`, - }, - }, - Before: noDanglingArgs, - Action: runCreateAuthorizedIntegration, - } -} - -func runCreateAuthorizedIntegration(ctx context.Context, c *cli.Command) error { - if !c.IsSet("username") { - return errors.New("you must provide a username to generate a token for") - } - - ctx, cancel := installSignals(ctx) - defer cancel() - - if err := initDB(ctx); err != nil { - return err - } - - user, err := user_model.GetUserByName(ctx, c.String("username")) - if err != nil { - return err - } - - ai := &auth_model.AuthorizedIntegration{ - UserID: user.ID, - Name: c.String("name"), - Description: c.String("description"), - UI: auth_model.AuthorizedIntegrationUIGeneric, - } - - var rules []auth_model.ClaimRule - ai.Issuer = c.String("issuer") - for claim, value := range c.StringMap("claim-eq") { - rules = append(rules, auth_model.ClaimRule{ - Claim: claim, - Comparison: auth_model.ClaimEqual, - Value: value, - }) - } - for claim, value := range c.StringMap("claim-in") { - values := []string{} - for s := range strings.SplitSeq(value, ",") { - values = append(values, strings.TrimSpace(s)) - } - rules = append(rules, auth_model.ClaimRule{ - Claim: claim, - Comparison: auth_model.ClaimIn, - Values: values, - }) - } - for claim, value := range c.StringMap("claim-glob") { - rules = append(rules, auth_model.ClaimRule{ - Claim: claim, - Comparison: auth_model.ClaimGlob, - Value: value, - }) - } - for claim, value := range c.StringMap("claim-glob-in") { - values := []string{} - for s := range strings.SplitSeq(value, ",") { - values = append(values, strings.TrimSpace(s)) - } - rules = append(rules, auth_model.ClaimRule{ - Claim: claim, - Comparison: auth_model.ClaimGlobIn, - Values: values, - }) - } - ai.ClaimRules = &auth_model.ClaimRules{Rules: rules} - - scopes := strings.Join(c.StringSlice("scope"), ",") - accessTokenScope, err := auth_model.AccessTokenScope(scopes).Normalize() - if err != nil { - return fmt.Errorf("invalid access token scope provided: %w", err) - } - ai.Scope = accessTokenScope - - allRepos := false - repos := []*repo.Repository{} - for _, repoName := range c.StringSlice("repo") { - if repoName == "all" { - allRepos = true - } else { - split := strings.Split(repoName, "/") - if len(split) != 2 { - return fmt.Errorf("invalid repo name: %q", split) - } - owner := split[0] - name := split[1] - repo, err := repo.GetRepositoryByOwnerAndName(ctx, owner, name) - if err != nil { - return err - } - repos = append(repos, repo) - } - } - ai.ResourceAllRepos = allRepos - - rr := make([]*auth_model.AuthorizedIntegResourceRepo, len(repos)) - for i := range repos { - rr[i] = &auth_model.AuthorizedIntegResourceRepo{RepoID: repos[i].ID} - } - if err := authz.ValidateAuthorizedIntegration(ai, rr); err != nil { - return err - } - - err = db.WithTx(ctx, func(ctx context.Context) error { - if err := auth_model.InsertAuthorizedIntegration(ctx, ai); err != nil { - return err - } - if !allRepos { - if err := auth_model.InsertAuthorizedIntegrationResourceRepos(ctx, ai.ID, rr); err != nil { - return err - } - } - return nil - }) - if err != nil { - return err - } - - type ClaimRuleDescription struct { - Description string `json:"description"` - Claim string `json:"claim"` - Comparison auth_model.ClaimComparison `json:"compare"` - Value string `json:"value,omitempty"` - Values []string `json:"values,omitempty"` - } - output := struct { - Message string `json:"message"` - Name string `json:"name"` - Description string `json:"description,omitempty"` - Issuer string `json:"issuer"` - Audience string `json:"audience"` - ClaimRules []ClaimRuleDescription `json:"claim_rules"` - }{ - Message: "Authorized integration was successfully created.", - Name: ai.Name, - Description: ai.Description, - Issuer: ai.Issuer, - Audience: ai.Audience, - } - for _, cr := range ai.ClaimRules.Rules { - var description string - switch cr.Comparison { - case auth_model.ClaimEqual: - description = fmt.Sprintf("%q = %q", cr.Claim, cr.Value) - case auth_model.ClaimIn: - description = fmt.Sprintf("%q in %q", cr.Claim, cr.Values) - case auth_model.ClaimGlob: - description = fmt.Sprintf("%q matches %q", cr.Claim, cr.Value) - case auth_model.ClaimGlobIn: - description = fmt.Sprintf("%q matches in %q", cr.Claim, cr.Values) - } - output.ClaimRules = append(output.ClaimRules, ClaimRuleDescription{ - Description: description, - Claim: cr.Claim, - Comparison: cr.Comparison, - Value: cr.Value, - Values: cr.Values, - }) - } - - raw, err := json.Marshal(output) - if err != nil { - return err - } - var indent bytes.Buffer - if err := json.Indent(&indent, raw, "", " "); err != nil { - return err - } - os.Stdout.Write(indent.Bytes()) - - return nil -} diff --git a/cmd/cert.go b/cmd/cert.go index 516ac4ce84..baadcbda85 100644 --- a/cmd/cert.go +++ b/cmd/cert.go @@ -150,8 +150,8 @@ func runCert(ctx context.Context, c *cli.Command) error { BasicConstraintsValid: true, } - hosts := strings.SplitSeq(c.String("host"), ",") - for h := range hosts { + hosts := strings.Split(c.String("host"), ",") + for _, h := range hosts { if ip := net.ParseIP(h); ip != nil { template.IPAddresses = append(template.IPAddresses, ip) } else { diff --git a/cmd/cmd.go b/cmd/cmd.go index a6e5d8fcfe..379609d294 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,5 +1,4 @@ // Copyright 2018 The Gitea Authors. All rights reserved. -// Copyright 2026 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT // Package cmd provides subcommands to the gitea binary - such as "web" or @@ -90,9 +89,26 @@ If this is the intended configuration file complete the [database] section.`, se return nil } -// installSignals returns a context that's cancelled on the SIGINT and SIGTERM signals or if the passed ctx is cancelled. func installSignals(ctx context.Context) (context.Context, context.CancelFunc) { - return signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) + ctx, cancel := context.WithCancel(ctx) + go func() { + // install notify + signalChannel := make(chan os.Signal, 1) + + signal.Notify( + signalChannel, + syscall.SIGINT, + syscall.SIGTERM, + ) + select { + case <-signalChannel: + case <-ctx.Done(): + } + cancel() + signal.Reset() + }() + + return ctx, cancel } func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) { diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go deleted file mode 100644 index 8e66cbdba1..0000000000 --- a/cmd/cmd_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT -package cmd - -import ( - "context" - "fmt" - "runtime" - "syscall" - "testing" - "time" -) - -func Test_installSignals(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skipf("Windows does not terminate in an awaitable manner") - return - } - - for _, s := range []syscall.Signal{syscall.SIGTERM, syscall.SIGINT} { - t.Run(fmt.Sprintf("Context is terminated on %s", s), func(t *testing.T) { - // Register the signal handler. context.Background() is chosen deliberately, - // because unlike t.Context(), we can be sure that it's not cancelled by a - // different handler. - ctx, cancel := installSignals(context.Background()) - t.Cleanup(cancel) - - // Send the signal in the background. - go syscall.Kill(syscall.Getpid(), s) - - select { - case <-time.Tick(time.Second * 10): - t.Fatalf("Context not cancelled via signal after 10 seconds") - case <-ctx.Done(): - t.Logf("Context was cancelled") - } - }) - } -} diff --git a/cmd/dump.go b/cmd/dump.go index 9fe326dfbb..b94277e529 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -12,7 +12,6 @@ import ( "os" "path" "path/filepath" - "slices" "strings" "sync" "time" @@ -84,9 +83,11 @@ func (o outputType) Join() string { } func (o *outputType) Set(value string) error { - if slices.Contains(o.Enum, value) { - o.selected = value - return nil + for _, enum := range o.Enum { + if enum == value { + o.selected = value + return nil + } } return fmt.Errorf("allowed values are %s", o.Join()) @@ -112,10 +113,7 @@ func getArchiverByType(outType string) (archives.ArchiverAsync, error) { var archiver archives.ArchiverAsync switch outType { case "zip": - archiver = archives.Zip{ - Compression: 8, - SelectiveCompression: false, - } + archiver = archives.Zip{} case "tar": archiver = archives.Tar{} case "tar.sz": @@ -252,8 +250,8 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error { setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr) } else { for _, suffix := range outputTypeEnum.Enum { - if before, ok := strings.CutSuffix(fileName, "."+suffix); ok { - fileName = before + if strings.HasSuffix(fileName, "."+suffix) { + fileName = strings.TrimSuffix(fileName, "."+suffix) break } } @@ -332,12 +330,14 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error { go dumpDatabase(ctx, archiveJobs, &wg, verbose) if len(setting.CustomConf) > 0 { - wg.Go(func() { + 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-custom-dir") && ctx.Bool("skip-custom-dir") { @@ -361,13 +361,15 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error { if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") { log.Info("Skipping attachment data") } else { - wg.Go(func() { + 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") { @@ -375,13 +377,15 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error { } else if !setting.Packages.Enabled { log.Info("Package registry not enabled - skipping") } else { - wg.Go(func() { + 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, @@ -395,11 +399,13 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error { log.Error("Failed to check if %s exists: %v", setting.Log.RootPath, err) } if isExist { - wg.Go(func() { + 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) } - }) + }() } } diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go index 60fffe1226..8e0ef0311f 100644 --- a/cmd/dump_repo.go +++ b/cmd/dump_repo.go @@ -143,8 +143,8 @@ func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error { opts.PullRequests = true opts.ReleaseAssets = true } else { - units := strings.SplitSeq(ctx.String("units"), ",") - for unit := range units { + units := strings.Split(ctx.String("units"), ",") + for _, unit := range units { switch strings.ToLower(strings.TrimSpace(unit)) { case "": continue diff --git a/cmd/forgejo/actions.go b/cmd/forgejo/actions.go index 309b9801cb..f520924c20 100644 --- a/cmd/forgejo/actions.go +++ b/cmd/forgejo/actions.go @@ -102,11 +102,6 @@ func SubcmdActionsRegister(ctx context.Context) *cli.Command { Value: "", Usage: "version of the runner (not required since v1.21)", }, - &cli.BoolFlag{ - Name: "ephemeral", - Value: false, - Usage: "instruct Forgejo to permanently unregister this runner after it has run one job", - }, }, } } @@ -177,7 +172,6 @@ func RunRegister(ctx context.Context, cli *cli.Command) error { scope := cli.String("scope") name := cli.String("name") version := cli.String("version") - ephemeral := cli.Bool("ephemeral") labels, err := getLabels(cli) if err != nil { return err @@ -205,7 +199,7 @@ func RunRegister(ctx context.Context, cli *cli.Command) error { return err } - runner, err := actions_model.RegisterRunner(ctx, owner, repo, secret, labels, name, version, ephemeral) + runner, err := actions_model.RegisterRunner(ctx, owner, repo, secret, labels, name, version) if err != nil { return fmt.Errorf("error while registering runner: %v", err) } diff --git a/cmd/forgejo/f3.go b/cmd/forgejo/f3.go index 2d1b617e46..c4aafeac58 100644 --- a/cmd/forgejo/f3.go +++ b/cmd/forgejo/f3.go @@ -24,6 +24,7 @@ import ( ) func CmdF3(ctx context.Context) *cli.Command { + ctx = f3_logger.ContextSetLogger(ctx, util.NewF3Logger(nil, log.GetLogger(log.DEFAULT))) return &cli.Command{ Name: "f3", Usage: "F3", @@ -37,9 +38,7 @@ func SubcmdF3Mirror(ctx context.Context) *cli.Command { mirrorCmd := f3_cmd.CreateCmdMirror() mirrorCmd.Before = prepareWorkPathAndCustomConf(ctx) f3Action := mirrorCmd.Action - mirrorCmd.Action = func(ctx context.Context, cli *cli.Command) error { - return runMirror(ctx, cli, f3Action) - } + mirrorCmd.Action = func(ctx context.Context, cli *cli.Command) error { return runMirror(ctx, cli, f3Action) } return mirrorCmd } @@ -68,8 +67,6 @@ func runMirror(ctx context.Context, c *cli.Command, action cli.ActionFunc) error if err := models.Init(ctx); err != nil { return err } - - ctx = f3_logger.ContextSetLogger(ctx, util.NewF3Logger(nil, log.GetLogger(log.DEFAULT))) } err := action(ctx, c) diff --git a/cmd/forgejo/forgejo.go b/cmd/forgejo/forgejo.go index 7b59eaab15..171ef1a71d 100644 --- a/cmd/forgejo/forgejo.go +++ b/cmd/forgejo/forgejo.go @@ -126,9 +126,7 @@ func installSignals(ctx context.Context) (context.Context, context.CancelFunc) { ) select { case <-signalChannel: - break case <-ctx.Done(): - break } cancel() signal.Reset() diff --git a/cmd/hook.go b/cmd/hook.go index d3eb4dea89..82dcb30866 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -568,7 +568,7 @@ Forgejo or set your environment appropriately.`, "") hookOptions.RefFullNames = make([]git.RefName, 0, hookBatchSize) for { - // note: pktLineTypeUnknown means pktLineTypeFlush and pktLineTypeData all allowed + // note: pktLineTypeUnknow means pktLineTypeFlush and pktLineTypeData all allowed rs, err = readPktLine(ctx, reader, pktLineTypeUnknown) if err != nil { return err diff --git a/cmd/mailer.go b/cmd/mailer.go index 2576e60b58..d05d6c849b 100644 --- a/cmd/mailer.go +++ b/cmd/mailer.go @@ -24,10 +24,10 @@ func runSendMail(ctx context.Context, c *cli.Command) error { } subject := c.String("title") - confirmSkipped := c.Bool("force") + confirmSkiped := c.Bool("force") body := c.String("content") - if !confirmSkipped { + if !confirmSkiped { if len(body) == 0 { fmt.Print("warning: Content is empty") } diff --git a/cmd/main.go b/cmd/main.go index 005dd763cf..65cde47884 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -80,6 +80,7 @@ func appGlobalFlags() []cli.Flag { func prepareSubcommandWithConfig(command *cli.Command, globalFlags func() []cli.Flag) { command.Flags = append(globalFlags(), command.Flags...) command.Action = prepareWorkPathAndCustomConf(command.Action) + command.HideHelp = true if command.Name != "help" { command.Commands = append(command.Commands, cmdHelp()) } @@ -198,6 +199,7 @@ func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmd } app.Flags = append(app.Flags, cli.VersionFlag) app.Flags = append(app.Flags, globalFlags()...) + app.HideHelp = true // use our own help action to show helps (with more information like default config) app.Before = PrepareConsoleLoggerLevel(log.INFO) for i := range subCmdWithConfig { prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags) diff --git a/cmd/main_test.go b/cmd/main_test.go index ca6a56f4af..737150c62f 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "io" - "os" "path/filepath" "strings" "testing" @@ -63,12 +62,7 @@ func runTestApp(app *cli.Command, args ...string) (runResult, error) { } func TestCliCmd(t *testing.T) { - path, err := os.Executable() - if err != nil { - panic(err) - } - - defaultWorkPath := filepath.Dir(path) + defaultWorkPath := filepath.Dir(setting.AppPath) defaultCustomPath := filepath.Join(defaultWorkPath, "custom") defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini") @@ -114,11 +108,6 @@ func TestCliCmd(t *testing.T) { cmd: "./gitea test-cmd --config /tmp/app-other.ini", exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"), }, - { - env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, - cmd: "./gitea forgejo-cli --help", - exp: "(subcommand help template)", - }, } for _, c := range cases { diff --git a/cmd/serv.go b/cmd/serv.go index 3a92b0e5fb..0e0551d297 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -290,9 +290,10 @@ func runServ(ctx context.Context, c *cli.Command) error { Op: lfsVerb, UserID: results.UserID, } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // Sign and get the complete encoded token as a string using the secret - tokenString, err := setting.LFS.SigningKey.JWT(claims) + tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes) if err != nil { return fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err) } diff --git a/cmd/web.go b/cmd/web.go index 4a2a1de1fe..12a8cac797 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -164,6 +164,8 @@ func serveInstall(_ context.Context, ctx *cli.Command) error { } func serveInstalled(_ context.Context, ctx *cli.Command) error { + setting.InitCfgProvider(setting.CustomConf) + setting.LoadCommonSettings() setting.MustInstalled() showWebStartupMessage("Prepare to run web server") @@ -276,11 +278,8 @@ func setPort(port string) error { switch setting.Protocol { case setting.HTTPUnix: - break case setting.FCGI: - break case setting.FCGIUnix: - break default: defaultLocalURL := string(setting.Protocol) + "://" if setting.HTTPAddr == "0.0.0.0" { diff --git a/contrib/systemd/forgejo.service b/contrib/systemd/forgejo.service index 36dc2a3a5b..ee019e11ea 100644 --- a/contrib/systemd/forgejo.service +++ b/contrib/systemd/forgejo.service @@ -52,7 +52,7 @@ After=network.target # Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that # LimitNOFILE=524288:524288 RestartSec=2s -Type=notify +Type=simple User=git Group=git WorkingDirectory=/var/lib/forgejo/ diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 40dd7d56db..f45b7731c5 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -313,9 +313,6 @@ RUN_USER = ; git ;LFS_START_SERVER = false ;; ;; -;; see JWT_* under [oauth2] -;LFS_JWT_SIGNING_ALGORITHM = HS256 -;LFS_JWT_SIGNING_PRIVATE_KEY_FILE = jwt/lfs_private.pem ;; LFS authentication secret, change this yourself ;LFS_JWT_SECRET = ;; @@ -420,8 +417,8 @@ DB_TYPE = sqlite3 ;; Database connection max idle time, 0 prevents closing due to idle time. ;CONN_MAX_IDLETIME = 0 ;; -;; Database maximum number of open connections. Ensure you only increase the value if your database server is configured to handle the amount of open connections accordingly. -;MAX_OPEN_CONNS = 30 +;; Database maximum number of open connections, default is 100 which is the lowest default from Postgres (MariaDB + MySQL default to 151). Ensure you only increase the value if you configured your database server accordingly. +;MAX_OPEN_CONNS = 100 ;; ;; Whether execute database models migrations automatically ;AUTO_MIGRATION = true @@ -460,7 +457,7 @@ INTERNAL_TOKEN = ;GLOBAL_TWO_FACTOR_REQUIREMENT = none ;; ;; Name of cookie used to store authentication information. -;COOKIE_REMEMBER_NAME = persistent +;COOKIE_REMEMBER_NAME = gitea_incredible ;; ;; Reverse proxy authentication header name of user name, email, and full name ;REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER @@ -547,7 +544,6 @@ ENABLED = true ;; Private key file path used to sign OAuth2 tokens. The path is relative to APP_DATA_PATH. ;; This setting is only needed if JWT_SIGNING_ALGORITHM is set to RS256, RS384, RS512, ES256, ES384 or ES512. ;; 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. -;; XXX jwt/ is a misnomer, it should rather be oauth2/, because we use many JWTs ;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 @@ -1066,7 +1062,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; List of file extensions for which lines should be wrapped in the CodeMirror editor +;; List of file extensions for which lines should be wrapped in the Monaco editor ;; Separate extensions with a comma. To line wrap files without an extension, just put a comma ;LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,.livemd, @@ -1136,6 +1132,9 @@ LEVEL = Info ;; If an squash commit's comment should be populated with the commit messages of the squashed commits ;POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES = false ;; +;; Add co-authored-by and co-committed-by trailers if committer does not match author +;ADD_CO_COMMITTER_TRAILERS = true +;; ;; Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo. ;RETARGET_CHILDREN_ON_MERGE = true @@ -1562,17 +1561,15 @@ LEVEL = Info ;DEFAULT_EMAIL_NOTIFICATIONS = enabled ;; Send an email to all admins when a new user signs up to inform the admins about this act. Options: true, false ;SEND_NOTIFICATION_EMAIL_ON_NEW_USER = false -;; Disabled features for users, could be "deletion", "manage_ssh_keys","manage_gpg_keys", "manage_password" more features can be disabled in future +;; Disabled features for users, could be "deletion", "manage_ssh_keys","manage_gpg_keys" more features can be disabled in future ;; - deletion: a user cannot delete their own account ;; - manage_ssh_keys: a user cannot configure ssh keys ;; - manage_gpg_keys: a user cannot configure gpg keys -;; - manage_password: a user cannot configure their password ;USER_DISABLED_FEATURES = -;; Comma separated list of disabled features ONLY if the user has an external login type (eg. LDAP, Oauth, etc.), could be `deletion`, `manage_ssh_keys`, `manage_gpg_keys`, `manage_password`. This setting is independent from `USER_DISABLED_FEATURES` and supplements its behavior. +;; Comma separated list of disabled features ONLY if the user has an external login type (eg. LDAP, Oauth, etc.), could be `deletion`, `manage_ssh_keys`, `manage_gpg_keys`. This setting is independent from `USER_DISABLED_FEATURES` and supplements its behavior. ;; - deletion: a user cannot delete their own account ;; - manage_ssh_keys: a user cannot configure ssh keys ;; - manage_gpg_keys: a user cannot configure gpg keys -;; - manage_password: a user cannot configure their password ;;EXTERNAL_USER_DISABLE_FEATURES = ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1899,7 +1896,7 @@ LEVEL = Info ;PROVIDER_CONFIG = data/sessions ; Relative paths will be made absolute against _`AppWorkPath`_. ;; ;; Session cookie name -;COOKIE_NAME = session +;COOKIE_NAME = i_like_gitea ;; ;; If you use session in https only: true or false. If not set, it defaults to `true` if the ROOT_URL is an HTTPS URL. ;COOKIE_SECURE = @@ -2459,15 +2456,6 @@ LEVEL = Info ;; Enable/Disable RSS/Atom feed ;ENABLE_FEED = true -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;[pwa] -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Enable standalone mode; this allows PWA enabled browsers to "install" the website as an progressive web app, -;; by setting the https://developer.mozilla.org/en-US/docs/Web/Manifest/display property to "standalone". -;STANDALONE = false - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[markup] @@ -2555,7 +2543,7 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;[f3] +;[F3] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; @@ -2788,21 +2776,12 @@ LEVEL = Info ;ABANDONED_JOB_TIMEOUT = 24h ;; Strings committers can place inside a commit message or PR title to skip executing the corresponding actions workflow ;SKIP_WORKFLOW_STRINGS = [skip ci],[ci skip],[no ci],[skip actions],[actions skip] -;; Limit on inputs for manual / workflow_dispatch triggers, default is 100 -;LIMIT_DISPATCH_INPUTS = 100 +;; 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 -;; Algorithm used to sign ID tokens. Valid values: RS256, RS384, RS512, ES256, ES384, ES512, EdDSA. -;; RS256 will ensure compatibility with all relying parties. -;; If a different algorithm is chosen, verify that relying parties of interest support the signing algorithm. -;ID_TOKEN_SIGNING_ALGORITHM = RS256 -;; Private key file path used to sign ID tokens. The path is relative to APP_DATA_PATH. -;; The file must contain an RSA or ECDSA private key in the PKCS8 format. If no key exists, a key will be created for you. -;ID_TOKEN_SIGNING_PRIVATE_KEY_FILE = actions_id_token/private.pem -;; Lifetime of ID tokens generated by the actions `/idtoken` endpoint in seconds. -;ID_TOKEN_EXPIRATION_TIME = 3600 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2823,30 +2802,3 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; storage type ;STORAGE_TYPE = local - -;; Authorized integrations are a capability for users to define external systems which can generate JWTs that Forgejo -;; will trust in order to perform API access on behalf of that user. While validating a JWT from an external system, -;; Forgejo makes outgoing HTTP requests to the JWT issuer. -; [authorized_integration] -;; Timeout for HTTP requests to remote servers. Default is 10 seconds. -;REQUEST_TIMEOUT = 10s -; -;; Allowed domains for authorized integrations. Default is blank which means all domains will be allowed (except local -;; networks, see ALLOW_LOCALNETWORKS). -;; Multiple domains can be separated by commas. -;; Wildcards are supported: "github.com, *.github.com" -;ALLOWED_DOMAINS = -; -;; Blocklist for authorized integrations, default is blank. -;; Multiple domains can be separated by commas. -;; Wildcards are supported: "github.com, *.github.com" -;BLOCKED_DOMAINS = -; -;; Allow private addresses defined by RFC 1918, RFC 1122, RFC 4632 and RFC 4291. -;; Default is false. -;; If a domain is allowed by ALLOWED_DOMAINS, this option will be ignored. -;ALLOW_LOCALNETWORKS = false -; -;; Remote requests are cached after being received for the cache time-to-live (TTL). Default is 10 minutes. -;; Caching uses the configured adapter in the [cache] config section. -;CACHE_TTL = 10m diff --git a/docker/rootless/usr/local/bin/docker-entrypoint.sh b/docker/rootless/usr/local/bin/docker-entrypoint.sh index ca509214bf..e5fa41cc78 100755 --- a/docker/rootless/usr/local/bin/docker-entrypoint.sh +++ b/docker/rootless/usr/local/bin/docker-entrypoint.sh @@ -13,5 +13,10 @@ fi if [ $# -gt 0 ]; then exec "$@" else + # TODO: remove on next major version release + # Honour legacy config file if existing + if [ -f ${GITEA_APP_INI_LEGACY} ]; then + GITEA_APP_INI=${GITEA_APP_INI_LEGACY} + fi exec /usr/local/bin/gitea -c ${GITEA_APP_INI} web fi diff --git a/docker/rootless/usr/local/bin/docker-setup.sh b/docker/rootless/usr/local/bin/docker-setup.sh index 7bcb78c88c..09bbeabc63 100755 --- a/docker/rootless/usr/local/bin/docker-setup.sh +++ b/docker/rootless/usr/local/bin/docker-setup.sh @@ -11,10 +11,22 @@ mkdir -p ${GITEA_CUSTOM} && chmod 0700 ${GITEA_CUSTOM} mkdir -p ${GITEA_TEMP} && chmod 0700 ${GITEA_TEMP} if [ ! -w ${GITEA_TEMP} ]; then echo "${GITEA_TEMP} is not writable"; exit 1; fi -# Prepare config file +# TODO: remove on next major version release +# Honour legacy config file if existing, but inform the user +if [ -f ${GITEA_APP_INI_LEGACY} ] && [ ${GITEA_APP_INI} != ${GITEA_APP_INI_LEGACY} ]; then + GITEA_APP_INI_DEFAULT=/var/lib/gitea/custom/conf/app.ini + echo -e \ + "\033[33mWARNING\033[0m: detected configuration file in deprecated default path ${GITEA_APP_INI_LEGACY}." \ + "The new default is ${GITEA_APP_INI_DEFAULT}. To remove this warning, choose one of the options:\n" \ + "* Move ${GITEA_APP_INI_LEGACY} to ${GITEA_APP_INI_DEFAULT} (or to \$GITEA_APP_INI if you want to override this variable)\n" \ + "* Explicitly override GITEA_APP_INI=${GITEA_APP_INI_LEGACY} in the container environment" + GITEA_APP_INI=${GITEA_APP_INI_LEGACY} +fi + +#Prepare config file if [ ! -f ${GITEA_APP_INI} ]; then - # Prepare config file folder + #Prepare config file folder GITEA_APP_INI_DIR=$(dirname ${GITEA_APP_INI}) mkdir -p ${GITEA_APP_INI_DIR} && chmod 0700 ${GITEA_APP_INI_DIR} if [ ! -w ${GITEA_APP_INI_DIR} ]; then echo "${GITEA_APP_INI_DIR} is not writable"; exit 1; fi diff --git a/docker/rootless/usr/local/bin/gitea b/docker/rootless/usr/local/bin/gitea index d87ec84e86..9a9a569b12 100644 --- a/docker/rootless/usr/local/bin/gitea +++ b/docker/rootless/usr/local/bin/gitea @@ -9,7 +9,7 @@ # And place the original in /usr/lib/gitea with working files in /data/gitea GITEA="/app/gitea/gitea" WORK_DIR="/var/lib/gitea" -APP_INI="/var/lib/gitea/custom/conf/app.ini" +APP_INI="/etc/gitea/app.ini" APP_INI_SET="" for i in "$@"; do diff --git a/eslint.config.mjs b/eslint.config.mjs index 03ca13176a..df3425d7a0 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1145,7 +1145,6 @@ export default tseslint.config( 'playwright/prefer-comparison-matcher': [2], 'playwright/prefer-equality-matcher': [2], - 'playwright/prefer-locator': [0], 'playwright/prefer-native-locators': [2], 'playwright/prefer-to-contain': [2], 'playwright/prefer-to-have-length': [2], diff --git a/flake.lock b/flake.lock index c6670e17b9..4ff76cf2ca 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1777954456, - "narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", + "lastModified": 1762977756, + "narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", + "rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55", "type": "github" }, "original": { diff --git a/go.mod b/go.mod index 59605e8e77..2201d007a6 100644 --- a/go.mod +++ b/go.mod @@ -1,37 +1,37 @@ module forgejo.org -go 1.26.0 +go 1.25.0 -toolchain go1.26.3 +toolchain go1.25.9 require ( - code.forgejo.org/f3/gof3/v3 v3.11.15 - code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20260301104140-add494e31dab - code.forgejo.org/forgejo/actions-proto v0.7.0 + code.forgejo.org/f3/gof3/v3 v3.11.1 + code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 + code.forgejo.org/forgejo/actions-proto v0.6.0 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/v12 v12.10.1 + code.forgejo.org/forgejo/runner/v12 v12.6.4 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.4 + code.forgejo.org/go-chi/session v1.0.2 code.gitea.io/sdk/gitea v0.21.0 - code.superseriousbusiness.org/exif-terminator v0.11.2 + code.superseriousbusiness.org/exif-terminator v0.11.1 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.2 + connectrpc.com/connect v1.19.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.4.1 - github.com/PuerkitoBio/goquery v1.12.0 - github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0 - github.com/alecthomas/chroma/v2 v2.23.1 + 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/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb - github.com/blevesearch/bleve/v2 v2.6.0 + github.com/blevesearch/bleve/v2 v2.5.6 github.com/buildkite/terminal-to-html/v3 v3.16.8 - github.com/caddyserver/certmagic v0.25.3 + github.com/caddyserver/certmagic v0.24.0 github.com/chi-middleware/proxy v1.1.1 github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 @@ -41,77 +41,78 @@ require ( github.com/editorconfig/editorconfig-core-go/v2 v2.6.4 github.com/emersion/go-imap v1.2.1 github.com/felixge/fgprof v0.9.5 - github.com/fsnotify/fsnotify v1.10.1 - github.com/gdgvda/cron v0.7.0 + github.com/fsnotify/fsnotify v1.9.0 github.com/gliderlabs/ssh v0.3.8 - github.com/go-ap/activitypub v0.0.0-20260208110334-902f6cf8c2cc + github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 github.com/go-ap/jsonld v0.0.0-20251216162253-e38fa664ea77 - github.com/go-chi/chi/v5 v5.2.5 + github.com/go-chi/chi/v5 v5.2.4 github.com/go-chi/cors v1.2.2 github.com/go-co-op/gocron v1.37.0 - github.com/go-enry/go-enry/v2 v2.9.6 + github.com/go-enry/go-enry/v2 v2.9.2 github.com/go-ldap/ldap/v3 v3.4.12 - github.com/go-openapi/spec v0.22.3 - github.com/go-sql-driver/mysql v1.10.0 - github.com/go-webauthn/webauthn v0.16.5 + github.com/go-openapi/spec v0.22.1 + github.com/go-sql-driver/mysql v1.9.3 + github.com/go-webauthn/webauthn v0.14.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.1 + github.com/golang-jwt/jwt/v5 v5.3.0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/google/go-github/v81 v81.0.0 - github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc + github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8 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.8.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 v1.0.0 - github.com/jackc/pgx/v5 v5.9.2 + github.com/inbucket/html2text v0.9.0 + github.com/jackc/pgx/v5 v5.9.0 github.com/jhillyerd/enmime/v2 v2.2.0 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/klauspost/compress v1.18.6 - github.com/klauspost/cpuid/v2 v2.3.0 - github.com/markbates/goth v1.82.0 - github.com/mattn/go-isatty v0.0.21 - github.com/mattn/go-sqlite3 v1.14.44 - github.com/meilisearch/meilisearch-go v0.36.2 + github.com/klauspost/compress v1.18.3 + github.com/klauspost/cpuid/v2 v2.2.11 + github.com/markbates/goth v1.80.0 + github.com/mattn/go-isatty v0.0.20 + github.com/mattn/go-sqlite3 v1.14.34 + github.com/meilisearch/meilisearch-go v0.34.0 github.com/mholt/archives v0.1.5 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/minio/minio-go/v7 v7.1.0 + github.com/minio/minio-go/v7 v7.0.95 github.com/msteinert/pam/v2 v2.1.0 github.com/niklasfasching/go-org v1.9.1 github.com/olivere/elastic/v7 v7.0.32 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 - github.com/pquerna/otp v1.5.0 + github.com/pquerna/otp v1.4.0 github.com/prometheus/client_golang v1.21.1 - github.com/redis/go-redis/v9 v9.19.0 + github.com/redis/go-redis/v9 v9.17.2 + 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/syndtr/goleveldb v1.0.0 github.com/ulikunitz/xz v0.5.15 - github.com/urfave/cli/v3 v3.9.0 - github.com/valyala/fastjson v1.6.10 + github.com/urfave/cli/v3 v3.5.0 + github.com/valyala/fastjson v1.6.7 github.com/yohcop/openid-go v1.0.1 - github.com/yuin/goldmark v1.8.2 + github.com/yuin/goldmark v1.7.13 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc gitlab.com/gitlab-org/api/client-go v0.143.2 + go.uber.org/mock v0.6.0 go.yaml.in/yaml/v3 v3.0.4 - golang.org/x/crypto v0.51.0 - golang.org/x/image v0.40.0 - golang.org/x/net v0.54.0 - golang.org/x/oauth2 v0.36.0 + golang.org/x/crypto v0.49.0 + golang.org/x/image v0.39.0 + golang.org/x/net v0.52.0 + golang.org/x/oauth2 v0.34.0 golang.org/x/sync v0.20.0 - golang.org/x/sys v0.44.0 - golang.org/x/text v0.37.0 + golang.org/x/sys v0.42.0 + golang.org/x/text v0.36.0 google.golang.org/protobuf v1.36.11 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 - mvdan.cc/xurls/v2 v2.6.0 + mvdan.cc/xurls/v2 v2.5.0 xorm.io/builder v0.3.13 xorm.io/xorm v1.3.9 ) @@ -119,46 +120,46 @@ require ( require ( cloud.google.com/go/compute/metadata v0.6.0 // indirect code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 // indirect - filippo.io/edwards25519 v1.2.0 // indirect + filippo.io/edwards25519 v1.1.1 // indirect git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect - github.com/RoaringBitmap/roaring/v2 v2.14.5 // 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/cascadia v1.3.3 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.24.2 // indirect - github.com/blevesearch/bleve_index_api v1.3.11 // indirect - github.com/blevesearch/geo v0.2.5 // indirect - github.com/blevesearch/go-faiss v1.1.0 // indirect + github.com/bits-and-blooms/bitset v1.22.0 // indirect + github.com/blevesearch/bleve_index_api v1.2.11 // indirect + github.com/blevesearch/geo v0.2.4 // indirect + github.com/blevesearch/go-faiss v1.0.26 // indirect github.com/blevesearch/go-porterstemmer v1.0.3 // indirect github.com/blevesearch/gtreap v0.1.1 // indirect - github.com/blevesearch/mmap-go v1.2.0 // indirect - github.com/blevesearch/scorch_segment_api/v2 v2.4.7 // indirect + github.com/blevesearch/mmap-go v1.0.4 // indirect + github.com/blevesearch/scorch_segment_api/v2 v2.3.13 // indirect github.com/blevesearch/segment v0.9.1 // indirect github.com/blevesearch/snowballstem v0.9.0 // indirect github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect - github.com/blevesearch/vellum v1.2.0 // indirect - github.com/blevesearch/zapx/v11 v11.4.3 // indirect - github.com/blevesearch/zapx/v12 v12.4.3 // indirect - github.com/blevesearch/zapx/v13 v13.4.3 // indirect - github.com/blevesearch/zapx/v14 v14.4.3 // indirect - github.com/blevesearch/zapx/v15 v15.4.3 // indirect - github.com/blevesearch/zapx/v16 v16.3.4 // indirect - github.com/blevesearch/zapx/v17 v17.1.2 // indirect + github.com/blevesearch/vellum v1.1.0 // indirect + github.com/blevesearch/zapx/v11 v11.4.2 // indirect + github.com/blevesearch/zapx/v12 v12.4.2 // indirect + github.com/blevesearch/zapx/v13 v13.4.2 // indirect + github.com/blevesearch/zapx/v14 v14.4.2 // indirect + github.com/blevesearch/zapx/v15 v15.4.2 // indirect + github.com/blevesearch/zapx/v16 v16.2.7 // 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.5 // 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.3 // 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 @@ -166,32 +167,35 @@ require ( 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/fatih/color v1.18.0 // indirect - github.com/fxamacker/cbor/v2 v2.9.1 // indirect - github.com/go-ap/errors v0.0.0-20260208110149-e1b309365966 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // 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.8.0 // indirect + github.com/go-git/go-git/v5 v5.18.0 // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-openapi/jsonpointer v0.22.4 // indirect - github.com/go-openapi/jsonreference v0.21.4 // indirect - github.com/go-openapi/swag/conv v0.25.4 // indirect - github.com/go-openapi/swag/jsonname v0.25.4 // indirect - github.com/go-openapi/swag/jsonutils v0.25.4 // indirect - github.com/go-openapi/swag/loading v0.25.4 // indirect - github.com/go-openapi/swag/stringutils v0.25.4 // indirect - github.com/go-openapi/swag/typeutils v0.25.4 // indirect - github.com/go-openapi/swag/yamlutils v0.25.4 // indirect - github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/go-webauthn/x v0.2.3 // indirect + github.com/go-openapi/jsonpointer v0.22.3 // indirect + github.com/go-openapi/jsonreference v0.21.3 // indirect + github.com/go-openapi/swag/conv v0.25.3 // indirect + github.com/go-openapi/swag/jsonname v0.25.3 // indirect + github.com/go-openapi/swag/jsonutils v0.25.3 // indirect + github.com/go-openapi/swag/loading v0.25.3 // indirect + github.com/go-openapi/swag/stringutils v0.25.3 // indirect + github.com/go-openapi/swag/typeutils v0.25.3 // indirect + github.com/go-openapi/swag/yamlutils v0.25.3 // indirect + github.com/go-webauthn/x v0.1.25 // indirect github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // 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/snappy v1.0.0 // indirect - github.com/google/btree v1.1.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/go-tpm v0.9.8 // indirect + github.com/google/go-tpm v0.9.5 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -200,21 +204,23 @@ require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/crc32 v1.3.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect - github.com/libdns/libdns v1.1.1 // indirect + github.com/lib/pq v1.11.2 // indirect + github.com/libdns/libdns v1.0.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/markbates/going v1.0.3 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-runewidth v0.0.17 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect - github.com/mholt/acmez/v3 v3.1.6 // indirect - github.com/miekg/dns v1.1.72 // 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.1.1 // indirect + github.com/minio/crc64nvme v1.0.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/minlz v1.0.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 @@ -225,7 +231,6 @@ require ( github.com/olekukonko/ll v0.0.9 // indirect github.com/olekukonko/tablewriter v1.0.7 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/gomega v1.34.1 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -235,28 +240,27 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/rhysd/actionlint v1.7.10 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/sorairolake/lzip-go v0.3.8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect - github.com/stretchr/objx v0.5.2 // indirect - github.com/tinylib/msgp v1.6.4 // indirect + github.com/tinylib/msgp v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/zeebo/assert v1.3.0 // indirect github.com/zeebo/blake3 v0.2.4 // indirect - github.com/zeebo/xxh3 v1.1.0 // indirect go.etcd.io/bbolt v1.4.3 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.1 // 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.3 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/mod v0.35.0 // indirect - golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.44.0 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.43.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 ) @@ -269,4 +273,4 @@ replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-2024121 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.12 +replace xorm.io/xorm v1.3.9 => code.forgejo.org/xorm/xorm v1.3.9-forgejo.8 diff --git a/go.sum b/go.sum index a790c948e5..9739506ff1 100644 --- a/go.sum +++ b/go.sum @@ -16,12 +16,12 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k 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.15 h1:/EzJWRQUhVVia1EmCWRZHCuW8qdAX1DEXY0M1n0mECc= -code.forgejo.org/f3/gof3/v3 v3.11.15/go.mod h1:k99wl7QiI9LjS3qEd1RXLdcZPE3wZP5gkVhy8/WhzJ4= -code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20260301104140-add494e31dab h1:g/uf/tNOvCdGL9EuYV9EJQglEH8n572P1ZwH4lBBkjI= -code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20260301104140-add494e31dab/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= -code.forgejo.org/forgejo/actions-proto v0.7.0 h1:0UA8AIOIWEiMLQvBbnZ6GQ9reh6BbM15Ep8x+oeTCY8= -code.forgejo.org/forgejo/actions-proto v0.7.0/go.mod h1:+444hHBs9/qDh5X/AedaTv0Egj3vd/EXP93vg9zFV2E= +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/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/actions-proto v0.6.0 h1:dw1Dogk9A4V/yrLVkhe9dSZPsqNAIkI1kCXPSqG3tZA= +code.forgejo.org/forgejo/actions-proto v0.6.0/go.mod h1:+444hHBs9/qDh5X/AedaTv0Egj3vd/EXP93vg9zFV2E= 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= @@ -30,8 +30,8 @@ code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/Uf code.forgejo.org/forgejo/levelqueue v1.0.0/go.mod h1:fmG6zhVuqim2rxSFOoasgXO8V2W/k9U31VVYqLIRLhQ= code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ= code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= -code.forgejo.org/forgejo/runner/v12 v12.10.1 h1:qRKjWItVDc2lEMl3jGlKyFpqgX4xHeOdEyl/irO/Nk8= -code.forgejo.org/forgejo/runner/v12 v12.10.1/go.mod h1:A51GyZJlril5cIpVMvOn3NqE8upOE6ePjwC6s31kRHk= +code.forgejo.org/forgejo/runner/v12 v12.6.4 h1:nhYj2wSj5BVKxcF0bRtMt/A9iGkxHPFJiIui+T/4mrc= +code.forgejo.org/forgejo/runner/v12 v12.6.4/go.mod h1:34ATLtcxtOgjAJiINaJvBoNJiKpL1hGn0kt+gk+zdyk= 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,27 +40,25 @@ code.forgejo.org/go-chi/cache v1.0.1 h1:w6IsDcPbeEnEYZn7M2HJe3/3/Ehtcw/72VjcVK7+ code.forgejo.org/go-chi/cache v1.0.1/go.mod h1:K3aQSyRIN4xiuqV1kanfQ6O4ToDpzDpY3bNOyGjFe3U= code.forgejo.org/go-chi/captcha v1.0.2 h1:vyHDPXkpjDv8bLO9NqtWzZayzstD/WpJ5xwEkAaqZGQ= code.forgejo.org/go-chi/captcha v1.0.2/go.mod h1:lxiPLcJ76UCZHoH31/Wbum4GUi2NgjfFZLrJkKv1lLE= -code.forgejo.org/go-chi/session v1.0.4 h1:WQ1NaVxcCpxYwCliEGypKclZnOCjh3p1fk8XciJc62U= -code.forgejo.org/go-chi/session v1.0.4/go.mod h1:+sSTiomM5C8AUPtxZyTENIbcTz22kcVottKO0lnmDRk= -code.forgejo.org/xorm/xorm v1.3.9-forgejo.12 h1:EodlU2MGu/EeAdUpEOe+X4cU/qlnJol1ucJ0sHUCM1o= -code.forgejo.org/xorm/xorm v1.3.9-forgejo.12/go.mod h1:ozQINrM8b7uYFMBB/w19nUTTLcda3RKTQ8HspocVKdg= +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.8 h1:dsSKm2nus0NhHsqYxeuB3Gldk6TtlusD1CBGV6V1SS0= +code.forgejo.org/xorm/xorm v1.3.9-forgejo.8/go.mod h1:A7sFd3BFmRp20h6drSsCXgQRQdF8Vz8HuCSrzFS3m90= code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4= code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA= -code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE= -code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM= -code.superseriousbusiness.org/exif-terminator v0.11.2 h1:nkTdaghZb6I0oGYFhTLSALhA2ShgkPnYAJryE5IE9+0= -code.superseriousbusiness.org/exif-terminator v0.11.2/go.mod h1:/Z+3DHSrefCzzN5ePkGjVYKFErRimoeUf694Gz8Pn/Y= +code.superseriousbusiness.org/exif-terminator v0.11.1 h1:qnujLH4/Yk/CFtFMmtjozbdV6Ry5G3Q/E/mLlWm/gQI= +code.superseriousbusiness.org/exif-terminator v0.11.1/go.mod h1:/Z+3DHSrefCzzN5ePkGjVYKFErRimoeUf694Gz8Pn/Y= 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.2 h1:McQ83FGdzL+t60peksi0gXC7MQ/iLKgLduAnThbM0mo= -connectrpc.com/connect v1.19.2/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= +connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= +connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= -filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw= +filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= @@ -73,24 +71,24 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+ 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/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM= -github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= -github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo= -github.com/PuerkitoBio/goquery v1.12.0/go.mod h1:802ej+gV2y7bbIhOIoPY5sT183ZW0YFofScC4q/hIpQ= -github.com/RoaringBitmap/roaring/v2 v2.14.5 h1:ckd0o545JqDPeVJDgeFoaM21eBixUnlWfYgjE5VnyWw= -github.com/RoaringBitmap/roaring/v2 v2.14.5/go.mod h1:eq4wdNXxtJIS/oikeCzdX1rBzek7ANzbth041hrU8Q4= +github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= +github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= +github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= +github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg= +github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= github.com/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.8.0 h1:tgjwQrDH5m6jIYB7kac5IQZmfUzQNseac/e3H4VoCNE= -github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0/go.mod h1:1HmmMEVsr+0R1QWahSeMJkjSkq6CYAZu1aIbYSpfJ4o= +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.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY= -github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o= +github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= +github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= -github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= -github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= +github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI= github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= @@ -103,48 +101,47 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.24.2 h1:M7/NzVbsytmtfHbumG+K2bremQPMJuqv1JD3vOaFxp0= -github.com/bits-and-blooms/bitset v1.24.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4= +github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= -github.com/blevesearch/bleve/v2 v2.6.0 h1:Cyd3dd4q5tCbOV8MnKUVRUDYMHOir9xn12NZzXVSEd4= -github.com/blevesearch/bleve/v2 v2.6.0/go.mod h1:gLmI8lWgHgrIYf7UpUX7JISI1CaqC6VScu46mHThuAY= -github.com/blevesearch/bleve_index_api v1.3.11 h1:x29vbV8OjWfLcrDVd7Lr1q+BkLNS0JWNEig0MCVnKH4= -github.com/blevesearch/bleve_index_api v1.3.11/go.mod h1:xvd48t5XMeeioWQ5/jZvgLrV98flT2rdvEJ3l/ki4Ko= -github.com/blevesearch/geo v0.2.5 h1:yJg9FX1oRwLnjXSXF+ECHfXFTF4diF02Ca/qUGVjJhE= -github.com/blevesearch/geo v0.2.5/go.mod h1:Jhq7WE2K6mJTx1xS44M2pUO6Io+wjCSHh1+co3YOgH4= -github.com/blevesearch/go-faiss v1.1.0 h1:xM7Jc0ZUCv5lssG9Ohj3Jv0SdTpxcUABU1dDt9XVsc4= -github.com/blevesearch/go-faiss v1.1.0/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= +github.com/blevesearch/bleve/v2 v2.5.6 h1:YdixQmOUuZHojQRe8Te7BY2cRirbzpbcpybAFs0m2DI= +github.com/blevesearch/bleve/v2 v2.5.6/go.mod h1:t5WoESS5TDteTdnjhhvpA1BpLYErOBX2IQViTMLK7wo= +github.com/blevesearch/bleve_index_api v1.2.11 h1:bXQ54kVuwP8hdrXUSOnvTQfgK0KI1+f9A0ITJT8tX1s= +github.com/blevesearch/bleve_index_api v1.2.11/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= +github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk= +github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8= +github.com/blevesearch/go-faiss v1.0.26 h1:4dRLolFgjPyjkaXwff4NfbZFdE/dfywbzDqporeQvXI= +github.com/blevesearch/go-faiss v1.0.26/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= -github.com/blevesearch/mmap-go v1.2.0 h1:l33nNKPFcBjJUMwem6sAYJPUzhUCABoK9FxZDGiFNBI= -github.com/blevesearch/mmap-go v1.2.0/go.mod h1:Vd6+20GBhEdwJnU1Xohgt88XCD/CTWcqbCNxkZpyBo0= -github.com/blevesearch/scorch_segment_api/v2 v2.4.7 h1:GlMzW08hcsM3DnLUxhyF/1PcDal1qtvvIuytuph5djw= -github.com/blevesearch/scorch_segment_api/v2 v2.4.7/go.mod h1://IJ7tG3QCf0cWW/aVSXqy77tc1AvLu3fcJLYEvOAFs= +github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= +github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= +github.com/blevesearch/scorch_segment_api/v2 v2.3.13 h1:ZPjv/4VwWvHJZKeMSgScCapOy8+DdmsmRyLmSB88UoY= +github.com/blevesearch/scorch_segment_api/v2 v2.3.13/go.mod h1:ENk2LClTehOuMS8XzN3UxBEErYmtwkE7MAArFTXs9Vc= github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= -github.com/blevesearch/vellum v1.2.0 h1:xkDiOEsHc2t3Cp0NsNZZ36pvc130sCzcGKOPMzXe+e0= -github.com/blevesearch/vellum v1.2.0/go.mod h1:uEcfBJz7mAOf0Kvq6qoEKQQkLODBF46SINYNkZNae4k= -github.com/blevesearch/zapx/v11 v11.4.3 h1:PTZOO5loKpHC/x/GzmPZNa9cw7GZIQxd5qRjwij9tHY= -github.com/blevesearch/zapx/v11 v11.4.3/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc= -github.com/blevesearch/zapx/v12 v12.4.3 h1:eElXvAaAX4m04t//CGBQAtHNPA+Q6A1hHZVrN3LSFYo= -github.com/blevesearch/zapx/v12 v12.4.3/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58= -github.com/blevesearch/zapx/v13 v13.4.3 h1:qsdhRhaSpVnqDFlRiH9vG5+KJ+dE7KAW9WyZz/KXAiE= -github.com/blevesearch/zapx/v13 v13.4.3/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk= -github.com/blevesearch/zapx/v14 v14.4.3 h1:GY4Hecx0C6UTmiNC2pKdeA2rOKiLR5/rwpU9WR51dgM= -github.com/blevesearch/zapx/v14 v14.4.3/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8= -github.com/blevesearch/zapx/v15 v15.4.3 h1:iJiMJOHrz216jyO6lS0m9RTCEkprUnzvqAI2lc/0/CU= -github.com/blevesearch/zapx/v15 v15.4.3/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= -github.com/blevesearch/zapx/v16 v16.3.4 h1:hDAqA8qusZTNbPEL7//w5P65UZ2de6yhSeUaTbp0Po0= -github.com/blevesearch/zapx/v16 v16.3.4/go.mod h1:zqkPPqs9GS9FzVWzCO3Wf1X044yWAV17+4zb+FTiEHg= -github.com/blevesearch/zapx/v17 v17.1.2 h1:avbOk2igaASNoiy0BE/jPgcxAnRI2PGeydeP4hg7Ikk= -github.com/blevesearch/zapx/v17 v17.1.2/go.mod h1:WQObxKrqUX7cd0G1GMvDfc/bmZzQvoy7APOPimx7DiI= +github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= +github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= +github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs= +github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc= +github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE= +github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58= +github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks= +github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk= +github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0= +github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8= +github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k= +github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= +github.com/blevesearch/zapx/v16 v16.2.7 h1:xcgFRa7f/tQXOwApVq7JWgPYSlzyUMmkuYa54tMDuR0= +github.com/blevesearch/zapx/v16 v16.2.7/go.mod h1:murSoCJPCk25MqURrcJaBQ1RekuqSCSfMjXH4rHyA14= 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= @@ -164,10 +161,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.25.3 h1:mGf5ba8F7xA4c5jfDZZbK2buY1VEkbnwpMDixaju94A= -github.com/caddyserver/certmagic v0.25.3/go.mod h1:YVs43D5+H/Dckt4bTga1KSO/xYfFBfVZainGDywYPAA= -github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE= -github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= +github.com/caddyserver/certmagic v0.24.0 h1:EfXTWpxHAUKgDfOj6MHImJN8Jm4AMFfMT6ITuKhrDF0= +github.com/caddyserver/certmagic v0.24.0/go.mod h1:xPT7dC1DuHHnS2yuEQCEyks+b89sUkMENh8dJF+InLE= +github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= +github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8= @@ -194,6 +191,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/djherbis/buffer v1.1.0/go.mod h1:VwN8VdFkMY0DCALdY8o00d3IZ6Amz/UNVMWcSaJT44o= github.com/djherbis/buffer v1.2.0 h1:PH5Dd2ss0C7CRRhQCZ2u7MssF+No9ide8Ye71nPHcrQ= github.com/djherbis/buffer v1.2.0/go.mod h1:fjnebbZjCUpPinBRD+TDwXSOeNQ7fPQWLfGQqiAiUyE= @@ -250,29 +249,28 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= -github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= -github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ= -github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/gdgvda/cron v0.7.0 h1:LFPZUTbCb5ZpzYxavbQDDbjd6nwTwkiNUWyulOdlY2I= -github.com/gdgvda/cron v0.7.0/go.mod h1:caBF+mzTZGtQqFE05T1m6u9OmCASY3EK51XAICf3wio= -github.com/go-ap/activitypub v0.0.0-20260208110334-902f6cf8c2cc h1:yLe7YJhK+XNjNV4SqDxAjpWAgft+KU+XwKZS4AKEUV0= -github.com/go-ap/activitypub v0.0.0-20260208110334-902f6cf8c2cc/go.mod h1:jUs8eczo1EAT4ByRpZ4mQmNvjarw9eNf7Nm5udpMRhY= -github.com/go-ap/errors v0.0.0-20260208110149-e1b309365966 h1:tV+3kZgqFMKVUf+JPKBV400ISM8440+6y/SQCS0WZwQ= -github.com/go-ap/errors v0.0.0-20260208110149-e1b309365966/go.mod h1:zkp58Q5yXpCxZbh3d0GDvwqiYclfVuHEHjc9SZKAj6I= +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/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/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA= github.com/go-ap/jsonld v0.0.0-20251216162253-e38fa664ea77 h1:yHAmoR6avNy84PlLmjHt1z9flAp2Qs2ens5QDE/CNWk= github.com/go-ap/jsonld v0.0.0-20251216162253-e38fa664ea77/go.mod h1:4h93IBxgfnE/DEleMLgJ/XCeu/RtQ+MUh3ucANseeXA= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/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.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= -github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= +github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= +github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= -github.com/go-enry/go-enry/v2 v2.9.6 h1:np63eOtMV56zfYDHnFVgpEVOk8fr2kmylcMnAZUDbSs= -github.com/go-enry/go-enry/v2 v2.9.6/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8= +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= @@ -282,51 +280,53 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI 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= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0= +github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= +github.com/go-git/go-git/v5 v5.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM= +github.com/go-git/go-git/v5 v5.18.0/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4= github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo= -github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= -github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= -github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= -github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= -github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc= -github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs= -github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= -github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= -github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= -github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= -github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= -github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= -github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= -github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= -github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= -github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= -github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= -github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= -github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= -github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/jsonpointer v0.22.3 h1:dKMwfV4fmt6Ah90zloTbUKWMD+0he+12XYAsPotrkn8= +github.com/go-openapi/jsonpointer v0.22.3/go.mod h1:0lBbqeRsQ5lIanv3LHZBrmRGHLHcQoOXQnf88fHlGWo= +github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= +github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= +github.com/go-openapi/spec v0.22.1 h1:beZMa5AVQzRspNjvhe5aG1/XyBSMeX1eEOs7dMoXh/k= +github.com/go-openapi/spec v0.22.1/go.mod h1:c7aeIQT175dVowfp7FeCvXXnjN/MrpaONStibD2WtDA= +github.com/go-openapi/swag/conv v0.25.3 h1:PcB18wwfba7MN5BVlBIV+VxvUUeC2kEuCEyJ2/t2X7E= +github.com/go-openapi/swag/conv v0.25.3/go.mod h1:n4Ibfwhn8NJnPXNRhBO5Cqb9ez7alBR40JS4rbASUPU= +github.com/go-openapi/swag/jsonname v0.25.3 h1:U20VKDS74HiPaLV7UZkztpyVOw3JNVsit+w+gTXRj0A= +github.com/go-openapi/swag/jsonname v0.25.3/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.3 h1:kV7wer79KXUM4Ea4tBdAVTU842Rg6tWstX3QbM4fGdw= +github.com/go-openapi/swag/jsonutils v0.25.3/go.mod h1:ILcKqe4HC1VEZmJx51cVuZQ6MF8QvdfXsQfiaCs0z9o= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.3 h1:/i3E9hBujtXfHy91rjtwJ7Fgv5TuDHgnSrYjhFxwxOw= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.3/go.mod h1:8kYfCR2rHyOj25HVvxL5Nm8wkfzggddgjZm6RgjT8Ao= +github.com/go-openapi/swag/loading v0.25.3 h1:Nn65Zlzf4854MY6Ft0JdNrtnHh2bdcS/tXckpSnOb2Y= +github.com/go-openapi/swag/loading v0.25.3/go.mod h1:xajJ5P4Ang+cwM5gKFrHBgkEDWfLcsAKepIuzTmOb/c= +github.com/go-openapi/swag/stringutils v0.25.3 h1:nAmWq1fUTWl/XiaEPwALjp/8BPZJun70iDHRNq/sH6w= +github.com/go-openapi/swag/stringutils v0.25.3/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.3 h1:2w4mEEo7DQt3V4veWMZw0yTPQibiL3ri2fdDV4t2TQc= +github.com/go-openapi/swag/typeutils v0.25.3/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.3 h1:LKTJjCn/W1ZfMec0XDL4Vxh8kyAnv1orH5F2OREDUrg= +github.com/go-openapi/swag/yamlutils v0.25.3/go.mod h1:Y7QN6Wc5DOBXK14/xeo1cQlq0EA0wvLoSv13gDQoCao= github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= -github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw= -github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk= +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-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= -github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/go-webauthn/webauthn v0.16.5 h1:x+vADHlaiIjta23kGhtwyCIlB5mayKx6SBlpwQ5NF9A= -github.com/go-webauthn/webauthn v0.16.5/go.mod h1:mQC6L0lZ5Kiu35G70zeB2WnrW4+vbHjR8Koq4HdVaMg= -github.com/go-webauthn/x v0.2.3 h1:8oArS+Rc1SWFLXhE17KZNx258Z4kUSyaDgsSncCO5RA= -github.com/go-webauthn/x v0.2.3/go.mod h1:tM04GF3V6VYq79AZMl7vbj4q6pz9r7L2criWRzbWhPk= +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/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -340,8 +340,10 @@ 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/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= -github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +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/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= @@ -367,12 +369,12 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= -github.com/golang/snappy v1.0.0/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.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= -github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +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= @@ -386,10 +388,8 @@ github.com/google/go-github/v81 v81.0.0 h1:hTLugQRxSLD1Yei18fk4A5eYjOGLUBKAl/VCq github.com/google/go-github/v81 v81.0.0/go.mod h1:upyjaybucIbBIuxgJS7YLOZGziyvvJ92WX6WEBNE3sM= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= -github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= -github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc= -github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc= +github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU= +github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -398,8 +398,8 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI 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-20260302011040-a15ffb7f9dcc h1:VBbFa1lDYWEeV5FZKUiYKYT0VxCp9twUmmaq9eb8sXw= -github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8 h1:3DsUAV+VNEQa2CUVLxCY3f87278uWfIDhJnbdvDjvmE= +github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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= @@ -439,16 +439,18 @@ 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 v1.0.0 h1:N5kza++4uBBDJ2Z3KUnTRyPNoBcW+YfOgNiNmNB+sgs= -github.com/inbucket/html2text v1.0.0/go.mod h1:5TrhXQKGU+LXurODaSm55Y9eXoPBRnYiOz4x2XfUoJU= +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/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= -github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/pgx/v5 v5.9.0 h1:T/dI+2TvmI2H8s/KH1/lXIbz1CUFk3gn5oTjr0/mBsE= +github.com/jackc/pgx/v5 v5.9.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +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/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -475,14 +477,12 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 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.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= -github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= +github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= 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.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM= -github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= +github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= +github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -497,49 +497,49 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= -github.com/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU= -github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk= -github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U= -github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ= -github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U= -github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= +github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= +github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/libdns/libdns v1.0.0 h1:IvYaz07JNz6jUQ4h/fv2R4sVnRnm77J/aOuC9B+TQTA= +github.com/libdns/libdns v1.0.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= 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.82.0 h1:8j/c34AjBSTNzO7zTsOyP5IYCQCMBTRBHAbBt/PI0bQ= -github.com/markbates/goth v1.82.0/go.mod h1:/DRlcq0pyqkKToyZjsL2KgiA1zbF1HIjE7u2uC79rUk= +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-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= -github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +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.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8= -github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ= -github.com/meilisearch/meilisearch-go v0.36.2 h1:MYaMPCpdLh2aYPt+zK+19mLoA4dfBY3S1L7T0FADCjU= -github.com/meilisearch/meilisearch-go v0.36.2/go.mod h1:hWcR0MuWLSzHfbz9GGzIr3s9rnXLm1jqkmHkJPbUSvM= -github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk= -github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY= +github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk= +github.com/mattn/go-sqlite3 v1.14.34/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/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.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= -github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= +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.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= -github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= +github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg= +github.com/minio/crc64nvme v1.0.2/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.1.0 h1:QEt5IStDpxgGjEdtOgpiZ5QhmSl3ax7qy61vi2SwHO8= -github.com/minio/minio-go/v7 v7.1.0/go.mod h1:Dm7WS1AgLmBa0NcQD6SeJnJf+K/EUW3GR7Ks6olB3OA= +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/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= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -595,8 +595,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= -github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +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= @@ -606,8 +606,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= -github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= +github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= +github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= 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.10 h1:FL3XIEs72G4/++168vlv5FKOWMSWvWIQw1kBCadyOcM= @@ -655,15 +655,16 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ= -github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= +github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= +github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli/v3 v3.9.0 h1:AV9lIiPv3ukYnxunaCUsHnEozptYmDN2F0+yWqLMn/c= -github.com/urfave/cli/v3 v3.9.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= -github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4= -github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= +github.com/urfave/cli/v3 v3.5.0 h1:qCuFMmdayTF3zmjG8TSsoBzrDqszNrklYg2x3g4MSgw= +github.com/urfave/cli/v3 v3.5.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM= +github.com/valyala/fastjson v1.6.7/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/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= @@ -673,8 +674,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.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= -github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= +github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= @@ -683,8 +684,6 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= -github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= 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= @@ -702,8 +701,8 @@ 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/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.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +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= @@ -723,8 +722,8 @@ 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.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= 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= @@ -733,12 +732,12 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= 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.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8= -golang.org/x/image v0.40.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= +golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= +golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= 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= @@ -760,8 +759,8 @@ 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.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= -golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -792,15 +791,15 @@ 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.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= 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.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= 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= @@ -850,8 +849,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.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= 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= @@ -861,8 +860,8 @@ 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.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= -golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -876,12 +875,12 @@ 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.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= 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.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= -golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +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/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= @@ -911,8 +910,8 @@ 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.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= 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= @@ -973,6 +972,8 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -982,6 +983,7 @@ 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= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= @@ -991,16 +993,16 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh 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.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c= -modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ= +modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI= +modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE= 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.50.0 h1:eMowQSWLK0MeiQTdmz3lqoF5dqclujdlIKeJA11+7oM= -modernc.org/sqlite v1.50.0/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew= -mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= -mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= +modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= +modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= +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= diff --git a/models/actions/TestActionTask_GetTasksByRunnerRequestKey/action_task.yml b/models/actions/TestActionTask_GetTasksByRunnerRequestKey/action_task.yml deleted file mode 100644 index 2d6ce501ab..0000000000 --- a/models/actions/TestActionTask_GetTasksByRunnerRequestKey/action_task.yml +++ /dev/null @@ -1,36 +0,0 @@ -- - id: 100 - attempt: 3 - runner_id: 12345678 - status: 5 # StatusWaiting - repo_id: 4 - owner_id: 1 - commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 - is_fork_pull_request: false - token_hash: a1 - token_salt: eeeeeeee - token_last_eight: eeeeeeee - log_filename: artifact-test2/2f/47.log - log_in_storage: true - log_length: 707 - log_size: 90179 - log_expired: false - runner_request_key: 0a7e017d-4201-4b34-8cf4-de0f431893a4 -- - id: 101 - attempt: 3 - runner_id: 12345678 - status: 5 # StatusWaiting - repo_id: 4 - owner_id: 1 - commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 - is_fork_pull_request: false - token_hash: a2 - token_salt: eeeeeeee - token_last_eight: eeeeeeee - log_filename: artifact-test2/2f/47.log - log_in_storage: true - log_length: 707 - log_size: 90179 - log_expired: false - runner_request_key: 0a7e017d-4201-4b34-8cf4-de0f431893a4 diff --git a/models/actions/TestRunner_FindRunnerOptionsToConds/action_runner.yml b/models/actions/TestRunner_FindRunnerOptionsToConds/action_runner.yml deleted file mode 100644 index fc5190107e..0000000000 --- a/models/actions/TestRunner_FindRunnerOptionsToConds/action_runner.yml +++ /dev/null @@ -1,45 +0,0 @@ -- id: 719931 - uuid: "9e0eb762-cbdf-4725-9c90-4298568a2a77" - name: "runner-1" - version: "dev" - owner_id: 3 # Owned by org3 - repo_id: 0 - description: "A superb runner" - agent_labels: ["debian", "gpu"] - deleted: 0 -- id: 719932 - uuid: "b0a0a168-1b08-4365-95dd-e65040ca384d" - name: "runner-2" - version: "11.3.1" - owner_id: 2 # Owned by user2 - repo_id: 0 - description: "An exclusive runner" - agent_labels: ["docker"] - deleted: 0 -- id: 719933 - uuid: "974f9caf-ee64-4022-a56b-67b28ea06a0d" - name: "runner-3" - version: "12.2.0" - owner_id: 0 - repo_id: 0 - description: "A runner for everyone" - agent_labels: ["docker"] - deleted: 0 -- id: 719934 - uuid: "022650a8-e999-4a3d-afb0-21f75233798b" - name: "runner-4" - version: "12.1.0" - owner_id: 0 - repo_id: 32 # owned by org3 - description: "" - agent_labels: ["debian"] - deleted: 0 -- id: 719935 - uuid: "f3031695-87ea-41cf-8d48-21948e83ee17" - name: "runner-5" - version: "12.7.0" - owner_id: 0 - repo_id: 36 # owned by user2 - description: "" - agent_labels: ["arch"] - deleted: 0 diff --git a/models/actions/TestRunner_GetVisibleRunnerByID/action_runner.yml b/models/actions/TestRunner_GetVisibleRunnerByID/action_runner.yml deleted file mode 100644 index 1b8198236e..0000000000 --- a/models/actions/TestRunner_GetVisibleRunnerByID/action_runner.yml +++ /dev/null @@ -1,36 +0,0 @@ -- id: 719931 - uuid: "9e0eb762-cbdf-4725-9c90-4298568a2a77" - name: "runner-1" - version: "dev" - owner_id: 3 # Owned by org3 - repo_id: 0 - description: "A superb runner" - agent_labels: ["debian", "gpu"] - deleted: 0 -- id: 719932 - uuid: "b0a0a168-1b08-4365-95dd-e65040ca384d" - name: "runner-2" - version: "11.3.1" - owner_id: 2 # Owned by user2 - repo_id: 0 - description: "An exclusive runner" - agent_labels: ["docker"] - deleted: 0 -- id: 719933 - uuid: "974f9caf-ee64-4022-a56b-67b28ea06a0d" - name: "runner-3" - version: "12.2.0" - owner_id: 0 - repo_id: 0 - description: "A runner for everyone" - agent_labels: ["docker"] - deleted: 0 -- id: 719934 - uuid: "022650a8-e999-4a3d-afb0-21f75233798b" - name: "runner-4" - version: "12.1.0" - owner_id: 0 - repo_id: 32 - description: "" - agent_labels: ["debian"] - deleted: 0 diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 20b96f829c..492e3f6cea 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -9,7 +9,6 @@ package actions import ( "context" "errors" - "fmt" "time" "forgejo.org/models/db" @@ -89,13 +88,6 @@ func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPa return artifact, nil } -// IsV4 reports whether the artifact was uploaded via the v4 backend. -// The v4 backend stores the whole artifact as a single zip file; -// v1-v3 stores each file as a separate row. -func (a *ActionArtifact) IsV4() bool { - return a.ArtifactName+".zip" == a.ArtifactPath && a.ContentEncoding == "application/zip" -} - func getArtifactByNameAndPath(ctx context.Context, runID int64, name, fpath string) (*ActionArtifact, error) { var art ActionArtifact has, err := db.GetEngine(ctx).Where("run_id = ? AND artifact_name = ? AND artifact_path = ?", runID, name, fpath).Get(&art) @@ -158,32 +150,11 @@ type ActionArtifactMeta struct { Status ArtifactStatus } -// AggregatedArtifact is the aggregated view of a logical artifact -// (one or more rows sharing the same run_id + artifact_name), used by the -// public API to represent a single artifact to clients. -type AggregatedArtifact struct { - ID int64 `xorm:"id"` - RunID int64 `xorm:"run_id"` - RepoID int64 `xorm:"-"` - ArtifactName string `xorm:"artifact_name"` - FileSize int64 `xorm:"file_size"` - Status ArtifactStatus `xorm:"status"` - CreatedUnix timeutil.TimeStamp `xorm:"created_unix"` - UpdatedUnix timeutil.TimeStamp `xorm:"updated_unix"` - ExpiredUnix timeutil.TimeStamp `xorm:"expired_unix"` -} - -// APIDownloadURL returns the download URL for this artifact under the given -// repository API URL prefix (e.g. "https://host/api/v1/repos/owner/name"). -func (a *AggregatedArtifact) APIDownloadURL(repoAPIURL string) string { - return fmt.Sprintf("%s/actions/artifacts/%d/zip", repoAPIURL, a.ID) -} - // ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtifactMeta, error) { arts := make([]*ActionArtifactMeta, 0, 10) return arts, db.GetEngine(ctx).Table("action_artifact"). - Where(builder.Eq{"run_id": runID}.And(builder.In("status", ArtifactStatusUploadConfirmed, ArtifactStatusExpired))). + Where("run_id=? AND (status=? OR status=?)", runID, ArtifactStatusUploadConfirmed, ArtifactStatusExpired). GroupBy("artifact_name"). Select("artifact_name, sum(file_size) as file_size, max(status) as status"). Find(&arts) @@ -221,94 +192,3 @@ func SetArtifactDeleted(ctx context.Context, artifactID int64) error { _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)}) return err } - -// SetArtifactsOfRunDeleted marks all artifacts of the given run as deleted. -func SetArtifactsOfRunDeleted(ctx context.Context, runID int64) error { - _, err := db.GetEngine(ctx). - Where("run_id=?", runID). - Cols("status"). - Update(&ActionArtifact{Status: int64(ArtifactStatusPendingDeletion)}) - return err -} - -// aggregatedArtifactConds returns the common WHERE clause used by aggregated -// artifact queries: restrict to visible statuses and apply the caller's filters. -// The Status field on opts is ignored — visibility is fixed to UploadConfirmed/Expired. -func aggregatedArtifactConds(opts FindArtifactsOptions) builder.Cond { - opts.Status = 0 - return opts.ToConds().And(builder.In("status", ArtifactStatusUploadConfirmed, ArtifactStatusExpired)) -} - -const aggregatedArtifactSelect = "min(id) as id, run_id, artifact_name, sum(file_size) as file_size, max(status) as status, min(created_unix) as created_unix, max(updated_unix) as updated_unix, max(expired_unix) as expired_unix" - -// ListAggregatedArtifacts returns paginated aggregated artifacts. -// Each result represents one logical artifact: a (run_id, artifact_name) group, -// with ID = MIN(id), FileSize = SUM(file_size), Status = MAX(status), and -// timestamps aggregated accordingly. Status filter in opts is ignored; results -// are always restricted to UploadConfirmed and Expired statuses. -func ListAggregatedArtifacts(ctx context.Context, opts FindArtifactsOptions) ([]*AggregatedArtifact, int64, error) { - cond := aggregatedArtifactConds(opts) - - var countKeys []struct { - ID int64 `xorm:"id"` - } - if err := db.GetEngine(ctx).Table("action_artifact"). - Where(cond). - GroupBy("run_id, artifact_name"). - Select("min(id) as id"). - Find(&countKeys); err != nil { - return nil, 0, err - } - total := int64(len(countKeys)) - - sess := db.GetEngine(ctx).Table("action_artifact"). - Where(cond). - GroupBy("run_id, artifact_name"). - Select(aggregatedArtifactSelect). - OrderBy("id DESC") - - capacity := 10 - if opts.PageSize > 0 { - sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - capacity = opts.PageSize - } - - arts := make([]*AggregatedArtifact, 0, capacity) - return arts, total, sess.Find(&arts) -} - -// GetAggregatedArtifactByID returns the aggregated artifact by its canonical ID -// (MIN(id) of the group), scoped to the given repository. Returns util.ErrNotExist -// when the ID does not exist, is not canonical for its group, or does not belong to repoID. -// The repoID scoping is performed in the query so callers don't need a follow-up check. -func GetAggregatedArtifactByID(ctx context.Context, repoID, artifactID int64) (*AggregatedArtifact, error) { - var art ActionArtifact - has, err := db.GetEngine(ctx).Where(builder.Eq{"id": artifactID, "repo_id": repoID}).Get(&art) - if err != nil { - return nil, err - } - if !has { - return nil, util.ErrNotExist - } - - cond := aggregatedArtifactConds(FindArtifactsOptions{ - RunID: art.RunID, - ArtifactName: art.ArtifactName, - }) - - meta := new(AggregatedArtifact) - has, err = db.GetEngine(ctx).Table("action_artifact"). - Where(cond). - GroupBy("run_id, artifact_name"). - Select(aggregatedArtifactSelect). - Get(meta) - if err != nil { - return nil, err - } - if !has || meta.ID != artifactID { - return nil, util.ErrNotExist - } - - meta.RepoID = art.RepoID - return meta, nil -} diff --git a/models/actions/forgejo.go b/models/actions/forgejo.go index a55066fe3a..ce3f8b0c8b 100644 --- a/models/actions/forgejo.go +++ b/models/actions/forgejo.go @@ -14,7 +14,7 @@ import ( gouuid "github.com/google/uuid" ) -func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, labels *[]string, name, version string, ephemeral bool) (*ActionRunner, error) { +func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, labels *[]string, name, version string) (*ActionRunner, error) { uuid, err := gouuid.FromBytes([]byte(token[:16])) if err != nil { return nil, fmt.Errorf("gouuid.FromBytes %v", err) @@ -60,12 +60,11 @@ func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, la // name, _ = util.SplitStringAtByteN(name, 255) - cols := []string{"name", "owner_id", "repo_id", "version", "ephemeral"} + cols := []string{"name", "owner_id", "repo_id", "version"} runner.Name = name runner.OwnerID = ownerID runner.RepoID = repoID runner.Version = version - runner.Ephemeral = ephemeral if labels != nil { runner.AgentLabels = *labels cols = append(cols, "agent_labels") diff --git a/models/actions/forgejo_test.go b/models/actions/forgejo_test.go index 2170b0927a..5702068c1b 100644 --- a/models/actions/forgejo_test.go +++ b/models/actions/forgejo_test.go @@ -22,11 +22,9 @@ func TestActions_RegisterRunner_Token(t *testing.T) { labels := []string{} name := "runner" version := "v1.2.3" - ephemeral := true - runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version, ephemeral) + runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version) require.NoError(t, err) assert.Equal(t, name, runner.Name) - assert.True(t, runner.Ephemeral) assert.Equal(t, 1, subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))), "the token cannot be verified with the same method as routers/api/actions/runner/interceptor.go as of 8228751c55d6a4263f0fec2932ca16181c09c97d") } @@ -46,7 +44,7 @@ func TestActions_RegisterRunner_TokenUpdate(t *testing.T) { "the initial token should match the runner's secret", ) - RegisterRunner(db.DefaultContext, before.OwnerID, before.RepoID, newToken, nil, before.Name, before.Version, false) + RegisterRunner(db.DefaultContext, before.OwnerID, before.RepoID, newToken, nil, before.Name, before.Version) after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) @@ -68,11 +66,10 @@ func TestActions_RegisterRunner_CreateWithLabels(t *testing.T) { token := "0123456789012345678901234567890123456789" name := "runner" version := "v1.2.3" - ephemeral := true labels := []string{"woop", "doop"} labelsCopy := labels // labels may be affected by the tested function so we copy them - runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version, ephemeral) + runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version) require.NoError(t, err) // Check that the returned record has been updated, except for the labels @@ -81,7 +78,6 @@ func TestActions_RegisterRunner_CreateWithLabels(t *testing.T) { assert.Equal(t, name, runner.Name) assert.Equal(t, version, runner.Version) assert.Equal(t, labelsCopy, runner.AgentLabels) - assert.Equal(t, ephemeral, runner.Ephemeral) // Check that whatever is in the DB has been updated, except for the labels after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID}) @@ -90,7 +86,6 @@ func TestActions_RegisterRunner_CreateWithLabels(t *testing.T) { assert.Equal(t, name, after.Name) assert.Equal(t, version, after.Version) assert.Equal(t, labelsCopy, after.AgentLabels) - assert.Equal(t, ephemeral, after.Ephemeral) } func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) { @@ -100,9 +95,8 @@ func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) { token := "0123456789012345678901234567890123456789" name := "runner" version := "v1.2.3" - ephemeral := true - runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, nil, name, version, ephemeral) + runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, nil, name, version) require.NoError(t, err) // Check that the returned record has been updated, except for the labels @@ -111,7 +105,6 @@ func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) { assert.Equal(t, name, runner.Name) assert.Equal(t, version, runner.Version) assert.Equal(t, []string{}, runner.AgentLabels) - assert.Equal(t, ephemeral, runner.Ephemeral) // Check that whatever is in the DB has been updated, except for the labels after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID}) @@ -120,7 +113,6 @@ func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) { assert.Equal(t, name, after.Name) assert.Equal(t, version, after.Version) assert.Equal(t, []string{}, after.AgentLabels) - assert.Equal(t, ephemeral, after.Ephemeral) } func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) { @@ -133,11 +125,10 @@ func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) { newRepoID := int64(1) newName := "rennur" newVersion := "v4.5.6" - ephemeral := true newLabels := []string{"warp", "darp"} labelsCopy := newLabels // labels may be affected by the tested function so we copy them - runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, &newLabels, newName, newVersion, ephemeral) + runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, &newLabels, newName, newVersion) require.NoError(t, err) // Check that the returned record has been updated @@ -146,7 +137,6 @@ func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) { assert.Equal(t, newName, runner.Name) assert.Equal(t, newVersion, runner.Version) assert.Equal(t, labelsCopy, runner.AgentLabels) - assert.Equal(t, ephemeral, runner.Ephemeral) // Check that whatever is in the DB has been updated after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) @@ -155,7 +145,6 @@ func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) { assert.Equal(t, newName, after.Name) assert.Equal(t, newVersion, after.Version) assert.Equal(t, labelsCopy, after.AgentLabels) - assert.Equal(t, ephemeral, after.Ephemeral) } func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) { @@ -168,9 +157,8 @@ func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) { newRepoID := int64(1) newName := "rennur" newVersion := "v4.5.6" - ephemeral := true - runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, nil, newName, newVersion, ephemeral) + runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, nil, newName, newVersion) require.NoError(t, err) // Check that the returned record has been updated, except for the labels @@ -179,7 +167,6 @@ func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) { assert.Equal(t, newName, runner.Name) assert.Equal(t, newVersion, runner.Version) assert.Equal(t, before.AgentLabels, runner.AgentLabels) - assert.Equal(t, ephemeral, runner.Ephemeral) // Check that whatever is in the DB has been updated, except for the labels after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) @@ -188,5 +175,4 @@ func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) { assert.Equal(t, newName, after.Name) assert.Equal(t, newVersion, after.Version) assert.Equal(t, before.AgentLabels, after.AgentLabels) - assert.Equal(t, ephemeral, after.Ephemeral) } diff --git a/models/actions/pre_execution_errors.go b/models/actions/pre_execution_errors.go index 000d59d71d..28cd589ef9 100644 --- a/models/actions/pre_execution_errors.go +++ b/models/actions/pre_execution_errors.go @@ -26,11 +26,6 @@ const ( ErrorCodeIncompleteRunsOnMissingOutput ErrorCodeIncompleteRunsOnMissingMatrixDimension ErrorCodeIncompleteRunsOnUnknownCause - ErrorCodeIncompleteWithMissingJob - ErrorCodeIncompleteWithMissingOutput - ErrorCodeIncompleteWithMissingMatrixDimension - ErrorCodeIncompleteWithUnknownCause - ErrorCodeUnknownJobInNeeds ) func TranslatePreExecutionError(lang translation.Locale, run *ActionRun) string { @@ -62,16 +57,6 @@ func TranslatePreExecutionError(lang translation.Locale, run *ActionRun) string return lang.TrString("actions.workflow.incomplete_runson_missing_matrix_dimension", run.PreExecutionErrorDetails...) case ErrorCodeIncompleteRunsOnUnknownCause: return lang.TrString("actions.workflow.incomplete_runson_unknown_cause", run.PreExecutionErrorDetails...) - case ErrorCodeIncompleteWithMissingJob: - return lang.TrString("actions.workflow.incomplete_with_missing_job", run.PreExecutionErrorDetails...) - case ErrorCodeIncompleteWithMissingOutput: - return lang.TrString("actions.workflow.incomplete_with_missing_output", run.PreExecutionErrorDetails...) - case ErrorCodeIncompleteWithMissingMatrixDimension: - return lang.TrString("actions.workflow.incomplete_with_missing_matrix_dimension", run.PreExecutionErrorDetails...) - case ErrorCodeIncompleteWithUnknownCause: - return lang.TrString("actions.workflow.incomplete_with_unknown_cause", run.PreExecutionErrorDetails...) - case ErrorCodeUnknownJobInNeeds: - return lang.TrString("actions.workflow.unknown_job_in_needs", run.PreExecutionErrorDetails...) } return fmt.Sprintf(" 0 || run.NeedApproval || v.IncompleteMatrix || v.IncompleteRunsOn || v.IncompleteWith { + if len(needs) > 0 || run.NeedApproval || v.IncompleteMatrix || v.IncompleteRunsOn { status = StatusBlocked } else { status = StatusWaiting @@ -427,8 +351,7 @@ func InsertRunJobs(ctx context.Context, run *ActionRun, jobs []*jobparser.Single name, _ = util.SplitStringAtByteN(job.Name, 255) runsOn = job.RunsOn() } - - runJob := &ActionRunJob{ + runJobs = append(runJobs, &ActionRunJob{ RunID: run.ID, RepoID: run.RepoID, OwnerID: run.OwnerID, @@ -439,12 +362,8 @@ func InsertRunJobs(ctx context.Context, run *ActionRun, jobs []*jobparser.Single JobID: id, Needs: needs, RunsOn: runsOn, - } - if err := runJob.PrepareNextAttempt(status); err != nil { - return err - } - - runJobs = append(runJobs, runJob) + Status: status, + }) } if len(runJobs) > 0 { @@ -612,11 +531,4 @@ func ComputeRunStatus(ctx context.Context, runID int64) (run *ActionRun, columns return run, columns, nil } -// DeleteRun removes the given run. It is the caller's responsibility to handle the run's dependencies like artifacts or -// jobs. Nothing happens if the run does not exist. -func DeleteRun(ctx context.Context, runID int64) error { - _, err := db.GetEngine(ctx).Delete(&ActionRun{ID: runID}) - return err -} - type ActionRunIndex db.ResourceIndex diff --git a/models/actions/run_job.go b/models/actions/run_job.go index e64b62805c..ed1cefbaa5 100644 --- a/models/actions/run_job.go +++ b/models/actions/run_job.go @@ -15,7 +15,6 @@ import ( "forgejo.org/modules/util" "code.forgejo.org/forgejo/runner/v12/act/jobparser" - gouuid "github.com/google/uuid" "go.yaml.in/yaml/v3" "xorm.io/builder" ) @@ -31,7 +30,6 @@ type ActionRunJob struct { IsForkPullRequest bool Name string `xorm:"VARCHAR(255)"` Attempt int64 - Handle string `xorm:"unique"` WorkflowPayload []byte JobID string `xorm:"VARCHAR(255)"` // job id in workflow, not job's id Needs []string `xorm:"JSON TEXT"` @@ -110,12 +108,6 @@ func (job *ActionRunJob) LoadAttributes(ctx context.Context) error { return job.Run.LoadAttributes(ctx) } -// IsRequestedByRunner returns true if this attempt of this ActionRunJob was explicitly requested by the runner or if -// the runner expressed no preference. -func (job *ActionRunJob) IsRequestedByRunner(handle *string) bool { - return handle == nil || job.Handle == *handle -} - func (job *ActionRunJob) ItRunsOn(labels []string) bool { if len(labels) == 0 || len(job.RunsOn) == 0 { return false @@ -125,34 +117,6 @@ func (job *ActionRunJob) ItRunsOn(labels []string) bool { return labelSet.IsSubset(job.RunsOn) } -func (job *ActionRunJob) PrepareNextAttempt(initialStatus Status) error { - if job.Status != StatusUnknown && !job.Status.IsDone() { - return fmt.Errorf("cannot prepare next attempt because job %d is active: %s", job.ID, job.Status.String()) - } - - job.Attempt++ - job.Started = 0 - job.Stopped = 0 - job.TaskID = 0 - job.Handle = gouuid.New().String() - job.Status = initialStatus - - return nil -} - -// CanBeRerun answers whether this ActionRunJob can be rerun. Returns true if it is done and the Run it belongs to -// is valid. Returns false in all other cases. -func (job *ActionRunJob) CanBeRerun(ctx context.Context) (bool, error) { - if err := job.LoadRun(ctx); err != nil { - return false, fmt.Errorf("cannot load run %d of job %d: %w", job.RunID, job.ID, err) - } - - if !job.Run.IsValid() { - return false, nil - } - return job.Status.IsDone(), nil -} - func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) { var job ActionRunJob has, err := db.GetEngine(ctx).Where("id=?", id).Get(&job) @@ -270,9 +234,7 @@ var AggregateJobStatus = func(jobs []*ActionRunJob) Status { } } -// Retrieves the parsed workflow for this specific job. This field is often accessed multiple times in succession, so -// the parsed content is cached in-memory on the `ActionRunJob` instance. -func (job *ActionRunJob) DecodeWorkflowPayload() (*jobparser.SingleWorkflow, error) { +func (job *ActionRunJob) decodeWorkflowPayload() (*jobparser.SingleWorkflow, error) { if job.workflowPayloadDecoded != nil { return job.workflowPayloadDecoded, nil } @@ -296,8 +258,8 @@ func (job *ActionRunJob) ClearCachedWorkflowPayload() { // Checks whether the target job is an `(incomplete matrix)` job that will be blocked until the matrix is complete, and // then regenerated and deleted. If it is incomplete, and if the information is available, the specific job and/or // output that causes it to be incomplete will be returned as well. -func (job *ActionRunJob) HasIncompleteMatrix() (bool, *jobparser.IncompleteNeeds, error) { - jobWorkflow, err := job.DecodeWorkflowPayload() +func (job *ActionRunJob) IsIncompleteMatrix() (bool, *jobparser.IncompleteNeeds, error) { + jobWorkflow, err := job.decodeWorkflowPayload() if err != nil { return false, nil, fmt.Errorf("failure decoding workflow payload: %w", err) } @@ -306,69 +268,10 @@ func (job *ActionRunJob) HasIncompleteMatrix() (bool, *jobparser.IncompleteNeeds // Checks whether the target job has a `runs-on` field with an expression that requires an input from another job. The // job will be blocked until the other job is complete, and then regenerated and deleted. -func (job *ActionRunJob) HasIncompleteRunsOn() (bool, *jobparser.IncompleteNeeds, *jobparser.IncompleteMatrix, error) { - jobWorkflow, err := job.DecodeWorkflowPayload() +func (job *ActionRunJob) IsIncompleteRunsOn() (bool, *jobparser.IncompleteNeeds, *jobparser.IncompleteMatrix, error) { + jobWorkflow, err := job.decodeWorkflowPayload() if err != nil { return false, nil, nil, fmt.Errorf("failure decoding workflow payload: %w", err) } return jobWorkflow.IncompleteRunsOn, jobWorkflow.IncompleteRunsOnNeeds, jobWorkflow.IncompleteRunsOnMatrix, nil } - -// Check whether the target job was generated as a result of expanding a reusable workflow. -func (job *ActionRunJob) IsWorkflowCallInnerJob() (bool, error) { - jobWorkflow, err := job.DecodeWorkflowPayload() - if err != nil { - return false, fmt.Errorf("failure decoding workflow payload: %w", err) - } - return jobWorkflow.Metadata.WorkflowCallParent != "", nil -} - -// Check whether this job is a caller of a reusable workflow -- in other words, the real work done in this job is in -// spawned child jobs, not this job. -func (job *ActionRunJob) IsWorkflowCallOuterJob() (bool, error) { - jobWorkflow, err := job.DecodeWorkflowPayload() - if err != nil { - return false, fmt.Errorf("failure decoding workflow payload: %w", err) - } - return jobWorkflow.Metadata.WorkflowCallID != "", nil -} - -// Checks whether the target job has a `with` field with an expression that requires an input from another job. The job -// will be blocked until the other job is complete, and then regenerated and deleted. -func (job *ActionRunJob) HasIncompleteWith() (bool, *jobparser.IncompleteNeeds, *jobparser.IncompleteMatrix, error) { - jobWorkflow, err := job.DecodeWorkflowPayload() - if err != nil { - return false, nil, nil, fmt.Errorf("failure decoding workflow payload: %w", err) - } - return jobWorkflow.IncompleteWith, jobWorkflow.IncompleteWithNeeds, jobWorkflow.IncompleteWithMatrix, nil -} - -// EnableOpenIDConnect checks whether the job allows for ID token generation. -func (job *ActionRunJob) EnableOpenIDConnect() (bool, error) { - jobWorkflow, err := job.DecodeWorkflowPayload() - if err != nil { - return false, fmt.Errorf("failure decoding workflow payload: %w", err) - } - return jobWorkflow.EnableOpenIDConnect, nil -} - -// AllNeedsExist checks whether this ActionRunJob's Needs can theoretically be met by comparing them with the supplied -// list of all job IDs that part of a particular workflow run. Returns the list of unknown job IDs found in Needs -// alongside an indicator whether the check was successful. -func (job *ActionRunJob) AllNeedsExist(allExistingJobIDs container.Set[string]) ([]string, bool) { - unknownJobIDs := []string{} - for _, need := range job.Needs { - if !allExistingJobIDs.Contains(need) { - unknownJobIDs = append(unknownJobIDs, need) - } - } - - return unknownJobIDs, len(unknownJobIDs) == 0 -} - -// DeleteJob removes the given job. Removing all associated tasks is up to the caller. If the given job does not exist, -// nothing happens. -func DeleteJob(ctx context.Context, jobID int64) error { - _, err := db.GetEngine(ctx).Delete(&ActionRunJob{ID: jobID}) - return err -} diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go index 40a182082f..f4cc4bfb45 100644 --- a/models/actions/run_job_list.go +++ b/models/actions/run_job_list.go @@ -22,14 +22,6 @@ func (jobs ActionJobList) GetRunIDs() []int64 { }) } -func (jobs ActionJobList) GetJobIDs() container.Set[string] { - jobIDs := container.SetOf[string]() - for _, job := range jobs { - jobIDs.Add(job.JobID) - } - return jobIDs -} - func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error { runIDs := jobs.GetRunIDs() runs := make(map[int64]*ActionRun, len(runIDs)) @@ -63,6 +55,8 @@ type FindRunJobOptions struct { CommitSHA string Statuses []Status UpdatedBefore timeutil.TimeStamp + Events []string // []webhook_module.HookEventType + RunNumber int64 RunNeedsApproval optional.Option[bool] } @@ -74,7 +68,7 @@ func (opts FindRunJobOptions) ToConds() builder.Cond { if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) } - if opts.OwnerID != 0 { + if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } if opts.CommitSHA != "" { @@ -86,10 +80,16 @@ func (opts FindRunJobOptions) ToConds() builder.Cond { if opts.UpdatedBefore > 0 { cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore}) } - if has, value := opts.RunNeedsApproval.Get(); has { + if len(opts.Events) > 0 { + cond = cond.And(builder.In("event", opts.Events)) + } + if opts.RunNumber > 0 { + cond = cond.And(builder.Eq{"`index`": opts.RunNumber}) + } + if opts.RunNeedsApproval.Has() { cond = cond.And(builder.Exists(builder.Select("id").From("action_run", "outer_run"). Where(builder.Eq{ - "outer_run.need_approval": value, + "outer_run.need_approval": opts.RunNeedsApproval.Value(), "outer_run.id": builder.Expr("run_id"), }))) } diff --git a/models/actions/run_job_list_test.go b/models/actions/run_job_list_test.go deleted file mode 100644 index 223dd3b411..0000000000 --- a/models/actions/run_job_list_test.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package actions - -import ( - "testing" - - "forgejo.org/modules/container" - - "github.com/stretchr/testify/assert" -) - -func TestActionJobList_GetJobIDs(t *testing.T) { - jobs := ActionJobList{ - &ActionRunJob{JobID: "job 1"}, - &ActionRunJob{JobID: "job 2"}, - } - - assert.Equal(t, container.SetOf("job 2", "job 1"), jobs.GetJobIDs()) -} diff --git a/models/actions/run_job_test.go b/models/actions/run_job_test.go index d9275a81ec..c122b1ff55 100644 --- a/models/actions/run_job_test.go +++ b/models/actions/run_job_test.go @@ -8,8 +8,6 @@ import ( "forgejo.org/models/db" "forgejo.org/models/unittest" - "forgejo.org/modules/container" - "forgejo.org/modules/timeutil" "code.forgejo.org/forgejo/runner/v12/act/jobparser" "github.com/stretchr/testify/assert" @@ -74,7 +72,7 @@ func TestActionRunJob_HTMLURL(t *testing.T) { } } -func TestActionRunJob_HasIncompleteMatrix(t *testing.T) { +func TestActionRunJob_IsIncompleteMatrix(t *testing.T) { tests := []struct { name string job ActionRunJob @@ -102,7 +100,7 @@ func TestActionRunJob_HasIncompleteMatrix(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - isIncomplete, needs, err := tt.job.HasIncompleteMatrix() + isIncomplete, needs, err := tt.job.IsIncompleteMatrix() if tt.errContains != "" { assert.ErrorContains(t, err, tt.errContains) } else { @@ -114,7 +112,7 @@ func TestActionRunJob_HasIncompleteMatrix(t *testing.T) { } } -func TestActionRunJob_HasIncompleteRunsOn(t *testing.T) { +func TestActionRunJob_IsIncompleteRunsOn(t *testing.T) { tests := []struct { name string job ActionRunJob @@ -149,129 +147,7 @@ func TestActionRunJob_HasIncompleteRunsOn(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - isIncomplete, needs, matrix, err := tt.job.HasIncompleteRunsOn() - if tt.errContains != "" { - assert.ErrorContains(t, err, tt.errContains) - } else { - require.NoError(t, err) - assert.Equal(t, tt.isIncomplete, isIncomplete) - assert.Equal(t, tt.needs, needs) - assert.Equal(t, tt.matrix, matrix) - } - }) - } -} - -func TestActionRunJob_IsWorkflowCallOuterJob(t *testing.T) { - tests := []struct { - name string - job ActionRunJob - isWorkflowCallOuterJob bool - errContains string - }{ - { - name: "normal workflow", - job: ActionRunJob{WorkflowPayload: []byte("name: workflow")}, - isWorkflowCallOuterJob: false, - }, - { - name: "workflow_call outer job", - job: ActionRunJob{WorkflowPayload: []byte("name: test\njobs:\n job:\n if: false\n__metadata:\n workflow_call_id: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed\n")}, - isWorkflowCallOuterJob: true, - }, - { - name: "unparseable workflow", - job: ActionRunJob{WorkflowPayload: []byte("name: []\nincomplete_runs_on: true")}, - errContains: "failure unmarshaling WorkflowPayload to SingleWorkflow: yaml: unmarshal errors", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - isWorkflowCallOuterJob, err := tt.job.IsWorkflowCallOuterJob() - if tt.errContains != "" { - assert.ErrorContains(t, err, tt.errContains) - } else { - require.NoError(t, err) - assert.Equal(t, tt.isWorkflowCallOuterJob, isWorkflowCallOuterJob) - } - }) - } -} - -func TestActionRunJob_IsWorkflowCallInnerJob(t *testing.T) { - tests := []struct { - name string - job ActionRunJob - isWorkflowCallInnerJob bool - errContains string - }{ - { - name: "normal workflow", - job: ActionRunJob{WorkflowPayload: []byte("on: [workflow_dispatch]\nname: workflow")}, - isWorkflowCallInnerJob: false, - }, - { - name: "inner job", - job: ActionRunJob{WorkflowPayload: []byte("on:\n workflow_call:\nname: workflow\n__metadata:\n workflow_call_parent: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed\n")}, - isWorkflowCallInnerJob: true, - }, - { - name: "unparseable workflow", - job: ActionRunJob{WorkflowPayload: []byte("name: []\nincomplete_runs_on: true")}, - errContains: "failure unmarshaling WorkflowPayload to SingleWorkflow: yaml: unmarshal errors", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - isWorkflowCallInnerJob, err := tt.job.IsWorkflowCallInnerJob() - if tt.errContains != "" { - assert.ErrorContains(t, err, tt.errContains) - } else { - require.NoError(t, err) - assert.Equal(t, tt.isWorkflowCallInnerJob, isWorkflowCallInnerJob) - } - }) - } -} - -func TestActionRunJob_HasIncompleteWith(t *testing.T) { - tests := []struct { - name string - job ActionRunJob - isIncomplete bool - needs *jobparser.IncompleteNeeds - matrix *jobparser.IncompleteMatrix - errContains string - }{ - { - name: "normal workflow", - job: ActionRunJob{WorkflowPayload: []byte("name: workflow")}, - isIncomplete: false, - }, - { - name: "incomplete_with workflow", - job: ActionRunJob{WorkflowPayload: []byte("name: workflow\nincomplete_with: true\nincomplete_with_needs: { job: abc }")}, - needs: &jobparser.IncompleteNeeds{Job: "abc"}, - isIncomplete: true, - }, - { - name: "incomplete_with workflow", - job: ActionRunJob{WorkflowPayload: []byte("name: workflow\nincomplete_with: true\nincomplete_with_matrix: { dimension: abc }")}, - matrix: &jobparser.IncompleteMatrix{Dimension: "abc"}, - isIncomplete: true, - }, - { - name: "unparseable workflow", - job: ActionRunJob{WorkflowPayload: []byte("name: []\nincomplete_with: true")}, - errContains: "failure unmarshaling WorkflowPayload to SingleWorkflow: yaml: unmarshal errors", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - isIncomplete, needs, matrix, err := tt.job.HasIncompleteWith() + isIncomplete, needs, matrix, err := tt.job.IsIncompleteRunsOn() if tt.errContains != "" { assert.ErrorContains(t, err, tt.errContains) } else { @@ -303,202 +179,3 @@ func TestRunHasOtherJobs(t *testing.T) { require.NoError(t, err) assert.False(t, has) } - -func TestActionRunJobPrepareNextAttempt(t *testing.T) { - lastHandle := "original-handle" - job := ActionRunJob{ID: 46, Handle: lastHandle} - - err := job.PrepareNextAttempt(StatusWaiting) - require.NoError(t, err) - - assert.NotEqual(t, lastHandle, job.Handle) - assert.NotEmpty(t, job.Handle) - assert.Equal(t, int64(1), job.Attempt) - assert.Zero(t, job.Started) - assert.Zero(t, job.Stopped) - assert.Zero(t, job.TaskID) - assert.Equal(t, StatusWaiting, job.Status) - - lastHandle = job.Handle - job.Started = timeutil.TimeStampNow() - job.Stopped = timeutil.TimeStampNow() - job.TaskID = int64(59) - job.Status = StatusFailure - - err = job.PrepareNextAttempt(StatusBlocked) - require.NoError(t, err) - - assert.NotEqual(t, lastHandle, job.Handle) - assert.NotEmpty(t, job.Handle) - assert.Equal(t, int64(2), job.Attempt) - assert.Zero(t, job.Started) - assert.Zero(t, job.Stopped) - assert.Zero(t, job.TaskID) - assert.Equal(t, StatusBlocked, job.Status) - - lastHandle = job.Handle - - // The job hasn't finished yet. Preparing a next attempt should not be possible. It should be left untouched. - err = job.PrepareNextAttempt(StatusWaiting) - require.ErrorContains(t, err, "cannot prepare next attempt because job 46 is active: blocked") - - assert.Equal(t, lastHandle, job.Handle) - assert.Equal(t, int64(2), job.Attempt) - assert.Zero(t, job.Started) - assert.Zero(t, job.Stopped) - assert.Zero(t, job.TaskID) - assert.Equal(t, StatusBlocked, job.Status) -} - -func TestIsRequestedByRunner(t *testing.T) { - sameHandle := "4a1ca0be-4470-486d-8504-89b4a5ac00cf" - differentHandle := "88423da3-67af-4f2d-9a92-a0db822697e9" - emptyHandle := "" - - job := &ActionRunJob{ID: 422, Attempt: 5, Handle: sameHandle} - - assert.True(t, job.IsRequestedByRunner(nil)) - assert.True(t, job.IsRequestedByRunner(&sameHandle)) - assert.False(t, job.IsRequestedByRunner(&differentHandle)) - assert.False(t, job.IsRequestedByRunner(&emptyHandle)) - - // Old jobs that were created before the introduction of Handle do not have one. - emptyHandleJob := &ActionRunJob{ID: 422, Attempt: 5, Handle: ""} - - assert.True(t, emptyHandleJob.IsRequestedByRunner(nil)) - assert.True(t, emptyHandleJob.IsRequestedByRunner(&emptyHandle)) - - assert.False(t, emptyHandleJob.IsRequestedByRunner(&differentHandle)) -} - -func TestAllNeedsExist(t *testing.T) { - testCases := []struct { - name string - job ActionRunJob - existingJobIDs container.Set[string] - expectedUnknownIDs []string - ok bool - }{ - { - name: "no needs", - job: ActionRunJob{Needs: nil}, - existingJobIDs: container.Set[string]{}, - expectedUnknownIDs: []string{}, - ok: true, - }, - { - name: "empty needs", - job: ActionRunJob{Needs: []string{}}, - existingJobIDs: container.Set[string]{}, - expectedUnknownIDs: []string{}, - ok: true, - }, - { - name: "satisfied needs", - job: ActionRunJob{Needs: []string{"job1", "job2"}}, - existingJobIDs: container.SetOf("job2", "job1"), - expectedUnknownIDs: []string{}, - ok: true, - }, - { - name: "unsatisfied needs", - job: ActionRunJob{Needs: []string{"unknown", "job2"}}, - existingJobIDs: container.SetOf("job2", "job1"), - expectedUnknownIDs: []string{"unknown"}, - ok: false, - }, - { - name: "comparison is case-sensitive", - job: ActionRunJob{Needs: []string{"Job1", "job2"}}, - existingJobIDs: container.SetOf("job2", "job1"), - expectedUnknownIDs: []string{"Job1"}, - ok: false, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - unknownIDs, ok := testCase.job.AllNeedsExist(testCase.existingJobIDs) - - assert.Equal(t, testCase.ok, ok) - assert.Equal(t, testCase.expectedUnknownIDs, unknownIDs) - }) - } -} - -func TestActionRunJob_CanBeRerun(t *testing.T) { - testCases := []struct { - name string - job ActionRunJob - canBeRerun bool - expectedError string - }{ - { - name: "job with unknown status", - job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusUnknown}, - canBeRerun: false, - }, - { - name: "successful job", - job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusSuccess}, - canBeRerun: true, - }, - { - name: "failed job", - job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusFailure}, - canBeRerun: true, - }, - { - name: "cancelled job", - job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusCancelled}, - canBeRerun: true, - }, - { - name: "skipped job", - job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusSkipped}, - canBeRerun: true, - }, - { - name: "waiting job", - job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusWaiting}, - canBeRerun: false, - }, - { - name: "blocked job", - job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusBlocked}, - canBeRerun: false, - }, - { - name: "ActionRun is nil", - job: ActionRunJob{ID: 12, Run: nil, Status: StatusSuccess}, - expectedError: "cannot load run 0 of job 12", - }, - { - name: "with busy run but completed job", - job: ActionRunJob{Run: &ActionRun{Status: StatusRunning}, Status: StatusSuccess}, - canBeRerun: true, - }, - { - name: "with run that cannot be run", - job: ActionRunJob{ - Run: &ActionRun{Status: StatusRunning, PreExecutionErrorCode: ErrorCodeEventDetectionError}, - Status: StatusSuccess, - }, - canBeRerun: false, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - result, err := testCase.job.CanBeRerun(t.Context()) - - if testCase.expectedError == "" { - require.NoError(t, err) - } else { - require.ErrorContains(t, err, testCase.expectedError) - } - - assert.Equal(t, testCase.canBeRerun, result) - }) - } -} diff --git a/models/actions/run_list.go b/models/actions/run_list.go index 11fa8441a5..92be510569 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -5,7 +5,6 @@ package actions import ( "context" - "slices" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" @@ -14,8 +13,6 @@ import ( "forgejo.org/modules/translation" webhook_module "forgejo.org/modules/webhook" - "golang.org/x/text/collate" - "golang.org/x/text/language" "xorm.io/builder" ) @@ -75,9 +72,6 @@ type FindRunOptions struct { TriggerEvent webhook_module.HookEventType Approved bool // not util.OptionalBool, it works only when it's true Status []Status - Events []string // []webhook_module.HookEventType - RunNumber int64 - CommitSHA string } func (opts FindRunOptions) ToConds() builder.Cond { @@ -85,7 +79,7 @@ func (opts FindRunOptions) ToConds() builder.Cond { if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) } - if opts.OwnerID != 0 { + if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } if opts.WorkflowID != "" { @@ -106,15 +100,6 @@ func (opts FindRunOptions) ToConds() builder.Cond { if opts.TriggerEvent != "" { cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent}) } - if len(opts.Events) > 0 { - cond = cond.And(builder.In("event", opts.Events)) - } - if opts.RunNumber > 0 { - cond = cond.And(builder.Eq{"`index`": opts.RunNumber}) - } - if opts.CommitSHA != "" { - cond = cond.And(builder.Eq{"commit_sha": opts.CommitSHA}) - } return cond } @@ -130,18 +115,14 @@ type StatusInfo struct { // GetStatusInfoList returns a slice of StatusInfo func GetStatusInfoList(ctx context.Context, lang translation.Locale) []StatusInfo { // same as those in aggregateJobStatus - allStatus := []Status{StatusBlocked, StatusCancelled, StatusFailure, StatusRunning, StatusSkipped, StatusSuccess, StatusWaiting} - statusInfoList := make([]StatusInfo, 0, 7) + allStatus := []Status{StatusSuccess, StatusFailure, StatusWaiting, StatusRunning} + statusInfoList := make([]StatusInfo, 0, 4) for _, s := range allStatus { statusInfoList = append(statusInfoList, StatusInfo{ Status: int(s), DisplayedStatus: s.LocaleString(lang), }) } - collator := collate.New(language.Und, collate.IgnoreCase) - slices.SortFunc(statusInfoList, func(a, b StatusInfo) int { - return collator.CompareString(a.DisplayedStatus, b.DisplayedStatus) - }) return statusInfoList } diff --git a/models/actions/run_list_test.go b/models/actions/run_list_test.go deleted file mode 100644 index b3d22b5155..0000000000 --- a/models/actions/run_list_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package actions - -import ( - "testing" - - "forgejo.org/models/unittest" - "forgejo.org/modules/translation" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestActionStatusList(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - translation.InitLocales(t.Context()) - - statusInfoList := GetStatusInfoList(t.Context(), translation.NewLocale("en-US")) - assert.Len(t, statusInfoList, 7) - statuses := []string{"Blocked", "Canceled", "Failure", "Running", "Skipped", "Success", "Waiting"} - statusInts := []int{7, 3, 2, 6, 4, 1, 5} - for i, statusString := range statuses { - assert.Equal(t, statusInfoList[i].Status, statusInts[i]) - assert.Equal(t, statusInfoList[i].DisplayedStatus, statusString) - } - - statusInfoList = GetStatusInfoList(t.Context(), translation.NewLocale("de-DE")) - assert.Len(t, statusInfoList, 7) - statuses = []string{"Abgebrochen", "Blockiert", "Erfolg", "Fehler", "Laufend", "Übersprungen", "Wartend"} - statusInts = []int{3, 7, 1, 2, 6, 4, 5} - for i, statusString := range statuses { - assert.Equal(t, statusInfoList[i].Status, statusInts[i]) - assert.Equal(t, statusInfoList[i].DisplayedStatus, statusString) - } -} diff --git a/models/actions/run_test.go b/models/actions/run_test.go index c87765ed72..064f3fbcea 100644 --- a/models/actions/run_test.go +++ b/models/actions/run_test.go @@ -11,14 +11,15 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" "forgejo.org/modules/cache" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" "code.forgejo.org/forgejo/runner/v12/act/jobparser" "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") @@ -44,135 +45,6 @@ func TestSetDefaultConcurrencyGroup(t *testing.T) { assert.Equal(t, "refs/heads/main_testing_pull_request__auto", run.ConcurrencyGroup) } -func TestGetWorkflowPath(t *testing.T) { - run := ActionRun{ - WorkflowID: "ci.yml", - WorkflowDirectory: ".some/path/to/workflows", - } - assert.Equal(t, ".some/path/to/workflows/ci.yml", run.WorkflowPath()) -} - -func TestGetCommitLink(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - defer test.MockVariableValue(&setting.AppSubURL, "/sub")() - - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - - run := ActionRun{ - Repo: repo, - CommitSHA: "a356d1f1f82945a039cd16d4ce0137bd55284e77", - } - assert.Equal(t, "/sub/user2/repo1/commit/a356d1f1f82945a039cd16d4ce0137bd55284e77", run.CommitLink()) -} - -func TestIsScheduledRun(t *testing.T) { - scheduledRun := ActionRun{ - CommitSHA: "a356d1f1f82945a039cd16d4ce0137bd55284e77", - TriggerEvent: "schedule", - } - pushRun := ActionRun{ - CommitSHA: "8f9b5c6ab342eb11d7422deecef7195b18058b90", - TriggerEvent: "push", - } - - assert.True(t, scheduledRun.IsScheduledRun()) - assert.False(t, pushRun.IsScheduledRun()) -} - -func TestIsManualRun(t *testing.T) { - manualRunRun := ActionRun{ - CommitSHA: "a356d1f1f82945a039cd16d4ce0137bd55284e77", - TriggerEvent: "workflow_dispatch", - } - pushRun := ActionRun{ - CommitSHA: "8f9b5c6ab342eb11d7422deecef7195b18058b90", - TriggerEvent: "push", - } - - assert.True(t, manualRunRun.IsDispatchedRun()) - assert.False(t, pushRun.IsDispatchedRun()) -} - -func TestActionRun_IsValid(t *testing.T) { - testCases := []struct { - name string - run ActionRun - isValid bool - }{ - { - name: "valid run", - run: ActionRun{}, - isValid: true, - }, - { - name: "with pre-execution error", - run: ActionRun{PreExecutionErrorCode: ErrorCodeIncompleteRunsOnMissingOutput}, - isValid: false, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - assert.Equal(t, testCase.isValid, testCase.run.IsValid()) - }) - } -} - -func TestActionRun_CanBeRerun(t *testing.T) { - testCases := []struct { - name string - run ActionRun - canBeRerun bool - }{ - { - name: "run with unknown status", - run: ActionRun{Status: StatusUnknown}, - canBeRerun: false, - }, - { - name: "successful run", - run: ActionRun{Status: StatusSuccess}, - canBeRerun: true, - }, - { - name: "failed run", - run: ActionRun{Status: StatusFailure}, - canBeRerun: true, - }, - { - name: "cancelled run", - run: ActionRun{Status: StatusCancelled}, - canBeRerun: true, - }, - { - name: "skipped run", - run: ActionRun{Status: StatusSkipped}, - canBeRerun: true, - }, - { - name: "waiting run", - run: ActionRun{Status: StatusWaiting}, - canBeRerun: false, - }, - { - name: "blocked run", - run: ActionRun{Status: StatusBlocked}, - canBeRerun: false, - }, - { - name: "with pre-execution error", - run: ActionRun{PreExecutionErrorCode: ErrorCodeIncompleteRunsOnMissingOutput, Status: StatusSuccess}, - canBeRerun: false, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - assert.Equal(t, testCase.canBeRerun, testCase.run.CanBeRerun()) - }) - } -} - func TestRepoNumOpenActions(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) err := cache.Init() @@ -401,109 +273,6 @@ jobs: assert.Equal(t, StatusBlocked, job.Status) } -func TestActionRun_FindOuterWorkflowCall(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - pullRequestPosterID := int64(4) - repoID := int64(10) - pullRequestID := int64(2) - run := &ActionRun{ - RepoID: repoID, - PullRequestID: pullRequestID, - PullRequestPosterID: pullRequestPosterID, - } - - workflowRaw := []byte(` -jobs: - outer-job: - uses: ./.forgejo/workflows/reusable.yml -`) - workflows, err := jobparser.Parse(workflowRaw, false, - jobparser.WithJobOutputs(map[string]map[string]string{}), - jobparser.ExpandLocalReusableWorkflows(func(job *jobparser.Job, path string) ([]byte, error) { - return []byte(` -on: - workflow_call: -jobs: - inner-job-1: - runs-on: debian - steps: [] - inner-job-2: - runs-on: debian - steps: [] -`), nil - })) - require.NoError(t, err) - require.NoError(t, InsertRun(t.Context(), run, workflows)) - - jobs, err := db.Find[ActionRunJob](t.Context(), FindRunJobOptions{RunID: run.ID}) - require.NoError(t, err) - require.Len(t, jobs, 3) - - for _, j := range jobs { - t.Run(j.Name, func(t *testing.T) { - _, err := j.DecodeWorkflowPayload() - require.NoError(t, err) - outer, err := run.FindOuterWorkflowCall(t.Context(), j) - if j.Name == "outer-job" { - require.ErrorContains(t, err, "invalid state for FindOuterWorkflowCall") - } else { - require.NoError(t, err) - require.NotNil(t, outer) - assert.Equal(t, "outer-job", outer.Name) - } - }) - } -} - -func TestActionRun_IncompleteWith(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - pullRequestPosterID := int64(4) - repoID := int64(10) - pullRequestID := int64(2) - runDoesNotNeedApproval := &ActionRun{ - RepoID: repoID, - PullRequestID: pullRequestID, - PullRequestPosterID: pullRequestPosterID, - } - - workflowRaw := []byte(` -jobs: - outer-job: - with: - some_input: ${{ needs.other-job.outputs.some-output }} - uses: ./.forgejo/workflows/reusable.yml -`) - workflows, err := jobparser.Parse(workflowRaw, false, - jobparser.WithJobOutputs(map[string]map[string]string{}), - jobparser.ExpandLocalReusableWorkflows(func(job *jobparser.Job, path string) ([]byte, error) { - return []byte(` -on: - workflow_call: - inputs: - some_input: - type: string -jobs: - inner-job: - runs-on: debian - steps: [] -`), nil - })) - require.NoError(t, err) - require.True(t, workflows[0].IncompleteWith) // must be set for this test scenario to be valid - - require.NoError(t, InsertRun(t.Context(), runDoesNotNeedApproval, workflows)) - - jobs, err := db.Find[ActionRunJob](t.Context(), FindRunJobOptions{RunID: runDoesNotNeedApproval.ID}) - require.NoError(t, err) - require.Len(t, jobs, 1) - job := jobs[0] - - // Expect job with an incomplete with to be StatusBlocked: - assert.Equal(t, StatusBlocked, job.Status) -} - func TestComputeRunStatus(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) @@ -622,73 +391,3 @@ func TestComputeRunStatus(t *testing.T) { assert.Contains(t, columns, "stopped") }) } - -func TestInsertRunJobs(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - pullRequestPosterID := int64(4) - repoID := int64(10) - pullRequestID := int64(2) - actionRun := &ActionRun{ - RepoID: repoID, - PullRequestID: pullRequestID, - PullRequestPosterID: pullRequestPosterID, - CommitSHA: "1421f75bc5474c69fdb1dc176bcb96d381f935dd", - } - - workflowRaw := []byte(` -jobs: - build: - runs-on: fedora - test: - runs-on: debian - steps: [] -`) - jobs, err := jobparser.Parse(workflowRaw, false) - require.NoError(t, err) - - require.NoError(t, InsertRun(t.Context(), actionRun, jobs)) - - insertedJobs, err := db.Find[ActionRunJob](t.Context(), FindRunJobOptions{RunID: actionRun.ID}) - require.NoError(t, err) - require.Len(t, insertedJobs, 2) - - assert.Equal(t, actionRun.ID, insertedJobs[0].RunID) - assert.Equal(t, actionRun.RepoID, insertedJobs[0].RepoID) - assert.Equal(t, actionRun.OwnerID, insertedJobs[0].OwnerID) - assert.Equal(t, actionRun.CommitSHA, insertedJobs[0].CommitSHA) - assert.Equal(t, actionRun.IsForkPullRequest, insertedJobs[0].IsForkPullRequest) - assert.Equal(t, "build", insertedJobs[0].Name) - assert.Equal(t, "build", insertedJobs[0].JobID) - assert.Empty(t, insertedJobs[0].Needs) - assert.Equal(t, []string{"fedora"}, insertedJobs[0].RunsOn) - assert.Equal(t, int64(1), insertedJobs[0].Attempt) - assert.Zero(t, insertedJobs[0].Started) - assert.Zero(t, insertedJobs[0].Stopped) - assert.Zero(t, insertedJobs[0].TaskID) - assert.Equal(t, StatusWaiting, insertedJobs[0].Status) - - assert.Equal(t, actionRun.ID, insertedJobs[1].RunID) - assert.Equal(t, actionRun.RepoID, insertedJobs[1].RepoID) - assert.Equal(t, actionRun.OwnerID, insertedJobs[1].OwnerID) - assert.Equal(t, actionRun.CommitSHA, insertedJobs[1].CommitSHA) - assert.Equal(t, actionRun.IsForkPullRequest, insertedJobs[1].IsForkPullRequest) - assert.Equal(t, "test", insertedJobs[1].Name) - assert.Equal(t, "test", insertedJobs[1].JobID) - assert.Empty(t, insertedJobs[1].Needs) - assert.Equal(t, []string{"debian"}, insertedJobs[1].RunsOn) - assert.Equal(t, int64(1), insertedJobs[1].Attempt) - assert.Zero(t, insertedJobs[1].Started) - assert.Zero(t, insertedJobs[1].Stopped) - assert.Zero(t, insertedJobs[1].TaskID) - assert.Equal(t, StatusWaiting, insertedJobs[1].Status) -} - -func TestActionRunLoadAttributes(t *testing.T) { - run := &ActionRun{ - RepoID: 10, - TriggerUserID: 1000, - } - require.NoError(t, run.LoadAttributes(t.Context())) - assert.Equal(t, "ghost", run.TriggerUser.LowerName) -} diff --git a/models/actions/runner.go b/models/actions/runner.go index f324adec52..99ae7629a8 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -61,8 +61,6 @@ type ActionRunner struct { // Store labels defined in state file (default: .runner file) of `act_runner` AgentLabels []string `xorm:"TEXT"` - // Store if this is a runner that only ever get one single job assigned - Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"` Created timeutil.TimeStamp `xorm:"created"` Updated timeutil.TimeStamp `xorm:"updated"` @@ -127,18 +125,6 @@ func (r *ActionRunner) IsOnline() bool { return false } -func (r *ActionRunner) IsActive() bool { - return r.Status() == runnerv1.RunnerStatus_RUNNER_STATUS_ACTIVE -} - -func (r *ActionRunner) IsIdle() bool { - return r.Status() == runnerv1.RunnerStatus_RUNNER_STATUS_IDLE -} - -func (r *ActionRunner) IsOffline() bool { - return r.Status() == runnerv1.RunnerStatus_RUNNER_STATUS_OFFLINE -} - // Editable checks if the runner is editable by the user func (r *ActionRunner) Editable(ownerID, repoID int64) bool { if ownerID == 0 && repoID == 0 { @@ -152,7 +138,6 @@ func (r *ActionRunner) Editable(ownerID, repoID int64) bool { // LoadAttributes loads the attributes of the runner func (r *ActionRunner) LoadAttributes(ctx context.Context) error { - // nosemgrep: forgejo-logic-suspicious-OwnerID-check (system users are not stored in the database) if r.OwnerID > 0 { var user user_model.User has, err := db.GetEngine(ctx).ID(r.OwnerID).Get(&user) @@ -197,12 +182,12 @@ func init() { type FindRunnerOptions struct { db.ListOptions - RepoID int64 - OwnerID int64 // it will be ignored if RepoID is set - Sort string - Filter string - IsOnline optional.Option[bool] - WithVisible bool // include all runners that are visible to the repository, owner, or instance + RepoID int64 + OwnerID int64 // it will be ignored if RepoID is set + Sort string + Filter string + IsOnline optional.Option[bool] + WithAvailable bool // not only runners belong to, but also runners can be used } func (opts FindRunnerOptions) ToConds() builder.Cond { @@ -210,27 +195,25 @@ func (opts FindRunnerOptions) ToConds() builder.Cond { if opts.RepoID > 0 { c := builder.NewCond().And(builder.Eq{"repo_id": opts.RepoID}) - if opts.WithVisible { + if opts.WithAvailable { c = c.Or(builder.Eq{"owner_id": builder.Select("owner_id").From("repository").Where(builder.Eq{"id": opts.RepoID})}) c = c.Or(builder.Eq{"repo_id": 0, "owner_id": 0}) } cond = cond.And(c) - } else if opts.OwnerID != 0 { // OwnerID is ignored if RepoID is set + } else if opts.OwnerID > 0 { // OwnerID is ignored if RepoID is set c := builder.NewCond().And(builder.Eq{"owner_id": opts.OwnerID}) - if opts.WithVisible { + if opts.WithAvailable { c = c.Or(builder.Eq{"repo_id": 0, "owner_id": 0}) } cond = cond.And(c) - } else if !opts.WithVisible { - cond = cond.And(builder.Eq{"repo_id": 0, "owner_id": 0}) } if opts.Filter != "" { - cond = cond.And(builder.Like{"name", opts.Filter}).Or(builder.Like{"uuid", opts.Filter}) + cond = cond.And(builder.Like{"name", opts.Filter}) } - if has, value := opts.IsOnline.Get(); has { - if value { + if opts.IsOnline.Has() { + if opts.IsOnline.Value() { cond = cond.And(builder.Gt{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()}) } else { cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()}) @@ -281,31 +264,6 @@ func GetRunnerByID(ctx context.Context, id int64) (*ActionRunner, error) { return &runner, nil } -// GetVisibleRunnerByID is like GetRunnerByID, but it only finds the runner if it is visible to the given owner or -// repository. If it is not, util.ErrNotExist will be returned even if the runner exists. -func GetVisibleRunnerByID(ctx context.Context, id, ownerID, repoID int64) (*ActionRunner, error) { - query := db.GetEngine(ctx).Where("id=?", id) - - if repoID > 0 { - cond := builder.NewCond().And(builder.Eq{"repo_id": repoID}) - cond = cond.Or(builder.Eq{"owner_id": builder.Select("owner_id").From("repository").Where(builder.Eq{"id": repoID})}) - cond = cond.Or(builder.Eq{"repo_id": 0, "owner_id": 0}) - query = query.And(cond) - } else if ownerID > 0 { // ownerID is ignored if repoID is set - cond := builder.NewCond().And(builder.Eq{"owner_id": ownerID}).Or(builder.Eq{"repo_id": 0, "owner_id": 0}) - query = query.And(cond) - } - - var runner ActionRunner - has, err := query.Get(&runner) - if err != nil { - return nil, err - } else if !has { - return nil, fmt.Errorf("runner with ID %d: %w", id, util.ErrNotExist) - } - return &runner, nil -} - // UpdateRunner updates runner's information. func UpdateRunner(ctx context.Context, r *ActionRunner, cols ...string) error { e := db.GetEngine(ctx) @@ -396,13 +354,6 @@ func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) { return res.RowsAffected() } -// DeleteEphemeralRunner removes the ephemeral runner with the given ID. If the runner with the given ID is not an -// ephemeral runner, nothing happens. -func DeleteEphemeralRunner(ctx context.Context, id int64) error { - _, err := db.GetEngine(ctx).Where(builder.Eq{"id": id, "ephemeral": true}).Delete(&ActionRunner{}) - return err -} - func DeleteOfflineRunners(ctx context.Context, olderThan timeutil.TimeStamp, globalOnly bool) error { log.Info("Doing: DeleteOfflineRunners") diff --git a/models/actions/runner_list.go b/models/actions/runner_list.go index 4186ee60be..6a64c46596 100644 --- a/models/actions/runner_list.go +++ b/models/actions/runner_list.go @@ -28,7 +28,6 @@ func (runners RunnerList) LoadOwners(ctx context.Context) error { return err } for _, runner := range runners { - // nosemgrep: forgejo-logic-suspicious-OwnerID-check (system users are not stored in the database) if runner.OwnerID > 0 && runner.Owner == nil { runner.Owner = users[runner.OwnerID] } diff --git a/models/actions/runner_test.go b/models/actions/runner_test.go index 31e9706851..1916c35a76 100644 --- a/models/actions/runner_test.go +++ b/models/actions/runner_test.go @@ -10,7 +10,6 @@ import ( auth_model "forgejo.org/models/auth" "forgejo.org/models/db" - "forgejo.org/models/repo" "forgejo.org/models/unittest" "forgejo.org/modules/timeutil" @@ -141,400 +140,3 @@ func TestDeleteOfflineRunnersErrorOnInvalidOlderThanValue(t *testing.T) { defer timeutil.MockUnset() require.Error(t, DeleteOfflineRunners(db.DefaultContext, timeutil.TimeStampNow(), false)) } - -func TestRunnerEditable(t *testing.T) { - testCases := []struct { - name string - runner *ActionRunner - ownerID int64 - repoID int64 - editable bool - }{ - { - name: "admin-can-edit-global-runner", - runner: &ActionRunner{Name: "global-runner", OwnerID: 0, RepoID: 0}, - ownerID: 0, - repoID: 0, - editable: true, - }, - { - name: "admin-can-edit-user-runner", - runner: &ActionRunner{Name: "user-runner", OwnerID: 36, RepoID: 0}, - ownerID: 0, - repoID: 0, - editable: true, - }, - { - name: "admin-can-edit-repository-runner", - runner: &ActionRunner{Name: "user-runner", OwnerID: 0, RepoID: 110}, - ownerID: 0, - repoID: 0, - editable: true, - }, - { - name: "user-can-edit-its-runner", - runner: &ActionRunner{Name: "user-runner", OwnerID: 469, RepoID: 0}, - ownerID: 469, - repoID: 0, - editable: true, - }, - { - name: "user-cannot-edit-global-runner", - runner: &ActionRunner{Name: "global-runner", OwnerID: 0, RepoID: 0}, - ownerID: 469, - repoID: 0, - editable: false, - }, - { - name: "user-cannot-edit-other-users-runner", - runner: &ActionRunner{Name: "user-runner", OwnerID: 892, RepoID: 0}, - ownerID: 469, - repoID: 0, - editable: false, - }, - { - name: "user-cannot-edit-repo-runner", - runner: &ActionRunner{Name: "repo-runner", OwnerID: 0, RepoID: 151}, - ownerID: 469, - repoID: 0, - editable: false, - }, - { - name: "repo-can-edit-its-runner", - runner: &ActionRunner{Name: "repo-runner", OwnerID: 0, RepoID: 693}, - ownerID: 0, - repoID: 693, - editable: true, - }, - { - name: "repo-cannot-edit-other-repo-runner", - runner: &ActionRunner{Name: "repo-runner", OwnerID: 0, RepoID: 519}, - ownerID: 0, - repoID: 693, - editable: false, - }, - { - name: "repo-cannot-edit-global-runner", - runner: &ActionRunner{Name: "global-runner", OwnerID: 0, RepoID: 0}, - ownerID: 0, - repoID: 693, - editable: false, - }, - { - name: "repo-cannot-edit-user-runner", - runner: &ActionRunner{Name: "user-runner", OwnerID: 6, RepoID: 0}, - ownerID: 0, - repoID: 693, - editable: false, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - result := testCase.runner.Editable(testCase.ownerID, testCase.repoID) - assert.Equal(t, testCase.editable, result) - }) - } -} - -func TestRunner_GetVisibleRunnerByID(t *testing.T) { - defer unittest.OverrideFixtures("models/actions/TestRunner_GetVisibleRunnerByID")() - require.NoError(t, unittest.PrepareTestDatabase()) - - repository32 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 32, OwnerID: 3}) - repository1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1, OwnerID: 2}) - - runner1 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719931, OwnerID: 3, RepoID: 0}) // Owned by org3 - runner2 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719932, OwnerID: 2, RepoID: 0}) // Owned by user2 - runner3 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719933, OwnerID: 0, RepoID: 0}) - runner4 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719934, OwnerID: 0, RepoID: repository32.ID}) - - testCases := []struct { - name string - runner *ActionRunner - ownerID int64 - repoID int64 - expectedError string - }{ - { - name: "Organization runner", - runner: runner1, - ownerID: 3, - repoID: 0, - expectedError: "", - }, - { - name: "Organization runner visible to admins", - runner: runner1, - ownerID: 0, - repoID: 0, - expectedError: "", - }, - { - name: "Organization runner invisible to different owner", - runner: runner1, - ownerID: 2, - repoID: 0, - expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner1.ID), - }, - { - name: "Organization runner visible to its repositories", - runner: runner1, - ownerID: 0, - repoID: repository32.ID, - expectedError: "", - }, - { - name: "Organization runner invisible to repositories owned by somebody else", - runner: runner1, - ownerID: 0, - repoID: repository1.ID, - expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner1.ID), - }, - { - name: "User runner", - runner: runner2, - ownerID: 2, - repoID: 0, - expectedError: "", - }, - { - name: "User runner invisible to different user", - runner: runner2, - ownerID: 1, - repoID: 0, - expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner2.ID), - }, - { - name: "User runner visible to repository owned by user", - runner: runner2, - ownerID: 0, - repoID: repository1.ID, - expectedError: "", - }, - { - name: "User runner invisible to repository owned by different user", - runner: runner2, - ownerID: 0, - repoID: repository32.ID, - expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner2.ID), - }, - { - name: "Global runner", - runner: runner3, - ownerID: 0, - repoID: 0, - expectedError: "", - }, - { - name: "Global runner is visible to any user", - runner: runner3, - ownerID: 2, - repoID: 0, - expectedError: "", - }, - { - name: "Global runner is visible to any repository", - runner: runner3, - ownerID: 0, - repoID: repository32.ID, - expectedError: "", - }, - { - name: "Repository runner", - runner: runner4, - ownerID: 0, - repoID: repository32.ID, - expectedError: "", - }, - { - name: "Repository runner is visible to admins", - runner: runner4, - ownerID: 0, - repoID: 0, - expectedError: "", - }, - { - name: "Repository runner is invisible to repository owner", - runner: runner4, - ownerID: repository32.OwnerID, - repoID: 0, - expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner4.ID), - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - _, err := GetVisibleRunnerByID(t.Context(), testCase.runner.ID, testCase.ownerID, testCase.repoID) - if testCase.expectedError == "" { - require.NoError(t, err) - } else { - assert.ErrorContains(t, err, testCase.expectedError) - } - }) - } -} - -func TestRunner_FindRunnerOptionsToConds(t *testing.T) { - defer unittest.OverrideFixtures("models/actions/TestRunner_FindRunnerOptionsToConds")() - require.NoError(t, unittest.PrepareTestDatabase()) - - runner1 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719931, OwnerID: 3, RepoID: 0}) // Owned by org3 - runner2 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719932, OwnerID: 2, RepoID: 0}) // Owned by user2 - runner3 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719933, OwnerID: 0, RepoID: 0}) - runner4 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719934, OwnerID: 0, RepoID: 32}) - runner5 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719935, OwnerID: 0, RepoID: 36}) - - testCases := []struct { - name string - opts FindRunnerOptions - expectedRunners RunnerList - unexpectedRunners RunnerList - }{ - { - name: "Only runners owned by instance", - opts: FindRunnerOptions{OwnerID: 0, RepoID: 0, WithVisible: false}, - expectedRunners: RunnerList{runner3}, - unexpectedRunners: RunnerList{runner1, runner2, runner4, runner5}, - }, - { - name: "All runners on instance", - opts: FindRunnerOptions{OwnerID: 0, RepoID: 0, WithVisible: true}, - expectedRunners: RunnerList{runner1, runner2, runner3, runner4, runner5}, - unexpectedRunners: RunnerList{}, - }, - { - name: "Only runners owned by organization", - opts: FindRunnerOptions{OwnerID: 3, RepoID: 0, WithVisible: false}, - expectedRunners: RunnerList{runner1}, - unexpectedRunners: RunnerList{runner2, runner3, runner4, runner5}, - }, - { - name: "Runners available to organization", - opts: FindRunnerOptions{OwnerID: 3, RepoID: 0, WithVisible: true}, - expectedRunners: RunnerList{runner1, runner3}, - unexpectedRunners: RunnerList{runner2, runner4, runner5}, - }, - { - name: "Only runners owned by user", - opts: FindRunnerOptions{OwnerID: 2, RepoID: 0, WithVisible: false}, - expectedRunners: RunnerList{runner2}, - unexpectedRunners: RunnerList{runner1, runner3, runner4, runner5}, - }, - { - name: "Runners available to user", - opts: FindRunnerOptions{OwnerID: 2, RepoID: 0, WithVisible: true}, - expectedRunners: RunnerList{runner2, runner3}, - unexpectedRunners: RunnerList{runner1, runner4, runner5}, - }, - { - name: "Only runners owned by organization repository", - opts: FindRunnerOptions{OwnerID: 0, RepoID: 32, WithVisible: false}, - expectedRunners: RunnerList{runner4}, - unexpectedRunners: RunnerList{runner1, runner2, runner3, runner5}, - }, - { - name: "Runners available to organization repository", - opts: FindRunnerOptions{OwnerID: 0, RepoID: 32, WithVisible: true}, - expectedRunners: RunnerList{runner1, runner3, runner4}, - unexpectedRunners: RunnerList{runner2, runner5}, - }, - { - name: "Only runners owned by user repository", - opts: FindRunnerOptions{OwnerID: 0, RepoID: 36, WithVisible: false}, - expectedRunners: RunnerList{runner5}, - unexpectedRunners: RunnerList{runner1, runner2, runner3, runner4}, - }, - { - name: "Runners available to user repository", - opts: FindRunnerOptions{OwnerID: 0, RepoID: 36, WithVisible: true}, - expectedRunners: RunnerList{runner2, runner3, runner5}, - unexpectedRunners: RunnerList{runner1, runner4}, - }, - { - name: "Runners with partially matching name", - opts: FindRunnerOptions{Filter: "er-3"}, - expectedRunners: RunnerList{runner3}, - unexpectedRunners: RunnerList{runner1, runner2, runner4, runner5}, - }, - { - name: "Runners with partially matching UUID", - opts: FindRunnerOptions{Filter: "21f75233798b"}, - expectedRunners: RunnerList{runner4}, - unexpectedRunners: RunnerList{runner1, runner2, runner3, runner5}, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - runners, err := db.Find[ActionRunner](t.Context(), testCase.opts) - require.NoError(t, err) - - for _, expectedRunner := range testCase.expectedRunners { - assert.Contains(t, runners, expectedRunner) - } - for _, unexpectedRunner := range testCase.unexpectedRunners { - assert.NotContains(t, runners, unexpectedRunner) - } - }) - } -} - -func TestDeleteEphemeralRunner(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - persistentRunnerOne := &ActionRunner{ - ID: 606526, - UUID: "d53a1222-ae7a-4430-97f8-8fcb6efd04c9", - Name: "persistent-runner-one", - OwnerID: 2, - RepoID: 0, - Ephemeral: false, - TokenHash: "J9YDsQL", - } - persistentRunnerTwo := &ActionRunner{ - ID: 606527, - UUID: "3dc23067-b2fd-4daf-b428-dddad80d7f37", - Name: "persistent-runner-two", - OwnerID: 2, - RepoID: 0, - Ephemeral: false, - TokenHash: "jvIylZtHsS", - } - ephemeralRunnerOne := &ActionRunner{ - ID: 606528, - UUID: "2d9bc0a1-7019-4ed3-ba67-6415415ac2a9", - Name: "ephemeral-runner-one", - OwnerID: 2, - RepoID: 0, - Ephemeral: true, - TokenHash: "t9C8L0kM3W", - } - ephemeralRunnerTwo := &ActionRunner{ - ID: 606529, - UUID: "da7a03f8-ab39-4c54-9ec9-2bd312fe3be1", - Name: "ephemeral-runner-two", - OwnerID: 2, - RepoID: 0, - Ephemeral: true, - TokenHash: "g9oTOFM", - } - - require.NoError(t, CreateRunner(t.Context(), persistentRunnerOne)) - require.NoError(t, CreateRunner(t.Context(), persistentRunnerTwo)) - require.NoError(t, CreateRunner(t.Context(), ephemeralRunnerOne)) - require.NoError(t, CreateRunner(t.Context(), ephemeralRunnerTwo)) - - unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: persistentRunnerOne.ID}) - unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: persistentRunnerTwo.ID}) - unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: ephemeralRunnerOne.ID}) - unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: ephemeralRunnerTwo.ID}) - - require.NoError(t, DeleteEphemeralRunner(t.Context(), persistentRunnerOne.ID)) - require.NoError(t, DeleteEphemeralRunner(t.Context(), ephemeralRunnerOne.ID)) - - unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: persistentRunnerOne.ID}) - unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: persistentRunnerTwo.ID}) - unittest.AssertNotExistsBean(t, &ActionRunner{ID: ephemeralRunnerOne.ID}) - unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: ephemeralRunnerTwo.ID}) -} diff --git a/models/actions/runner_token.go b/models/actions/runner_token.go index f789f30ddf..ece15b55e8 100644 --- a/models/actions/runner_token.go +++ b/models/actions/runner_token.go @@ -10,11 +10,8 @@ import ( "forgejo.org/models/db" repo_model "forgejo.org/models/repo" user_model "forgejo.org/models/user" - "forgejo.org/modules/optional" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" - - "xorm.io/builder" ) // ActionRunnerToken represents runner tokens @@ -32,14 +29,15 @@ import ( type ActionRunnerToken struct { ID int64 Token string `xorm:"UNIQUE"` - OwnerID optional.Option[int64] `xorm:"index REFERENCES(user, id)"` + OwnerID int64 `xorm:"index"` Owner *user_model.User `xorm:"-"` - RepoID optional.Option[int64] `xorm:"index REFERENCES(repository, id)"` + RepoID int64 `xorm:"index"` Repo *repo_model.Repository `xorm:"-"` IsActive bool // true means it can be used Created timeutil.TimeStamp `xorm:"created"` Updated timeutil.TimeStamp `xorm:"updated"` + Deleted timeutil.TimeStamp `xorm:"deleted"` } func init() { @@ -72,11 +70,11 @@ func UpdateRunnerToken(ctx context.Context, r *ActionRunnerToken, cols ...string // NewRunnerToken creates a new active runner token and invalidate all old tokens // ownerID will be ignored and treated as 0 if repoID is non-zero. -func NewRunnerToken(ctx context.Context, ownerID, repoID optional.Option[int64]) (*ActionRunnerToken, error) { - if ownerID.Has() && repoID.Has() { +func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) { + if ownerID != 0 && repoID != 0 { // It's trying to create a runner token that belongs to a repository, but OwnerID has been set accidentally. // Remove OwnerID to avoid confusion; it's not worth returning an error here. - ownerID = optional.None[int64]() + ownerID = 0 } token := util.CryptoRandomString(util.RandomStringHigh) @@ -88,7 +86,7 @@ func NewRunnerToken(ctx context.Context, ownerID, repoID optional.Option[int64]) } return runnerToken, db.WithTx(ctx, func(ctx context.Context) error { - if _, err := db.GetEngine(ctx).Where(runnerTokenCond(ownerID, repoID)).Cols("is_active").Update(&ActionRunnerToken{ + if _, err := db.GetEngine(ctx).Where("owner_id =? AND repo_id = ?", ownerID, repoID).Cols("is_active").Update(&ActionRunnerToken{ IsActive: false, }); err != nil { return err @@ -99,32 +97,16 @@ func NewRunnerToken(ctx context.Context, ownerID, repoID optional.Option[int64]) }) } -func runnerTokenCond(ownerID, repoID optional.Option[int64]) builder.Cond { - var condOwnerID builder.Cond - if has, value := ownerID.Get(); !has { - condOwnerID = builder.IsNull{"owner_id"} - } else { - condOwnerID = builder.Eq{"owner_id": value} - } - var condRepoID builder.Cond - if has, value := repoID.Get(); !has { - condRepoID = builder.IsNull{"repo_id"} - } else { - condRepoID = builder.Eq{"repo_id": value} - } - return builder.And(condOwnerID, condRepoID) -} - // GetLatestRunnerToken returns the latest runner token -func GetLatestRunnerToken(ctx context.Context, ownerID, repoID optional.Option[int64]) (*ActionRunnerToken, error) { - if ownerID.Has() && repoID.Has() { - // It's trying to create a runner token that belongs to a repository, but OwnerID has been set accidentally. +func GetLatestRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) { + if ownerID != 0 && repoID != 0 { + // It's trying to get a runner token that belongs to a repository, but OwnerID has been set accidentally. // Remove OwnerID to avoid confusion; it's not worth returning an error here. - ownerID = optional.None[int64]() + ownerID = 0 } var runnerToken ActionRunnerToken - has, err := db.GetEngine(ctx).Where(runnerTokenCond(ownerID, repoID)). + has, err := db.GetEngine(ctx).Where("owner_id=? AND repo_id=?", ownerID, repoID). OrderBy("id DESC").Get(&runnerToken) if err != nil { return nil, err diff --git a/models/actions/runner_token_test.go b/models/actions/runner_token_test.go index 4b69f5488f..0de9ca5648 100644 --- a/models/actions/runner_token_test.go +++ b/models/actions/runner_token_test.go @@ -8,7 +8,6 @@ import ( "forgejo.org/models/db" "forgejo.org/models/unittest" - "forgejo.org/modules/optional" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,16 +16,16 @@ import ( func TestGetLatestRunnerToken(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3}) - expectedToken, err := GetLatestRunnerToken(db.DefaultContext, optional.Some[int64](1), optional.None[int64]()) + expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) assert.Equal(t, expectedToken, token) } func TestNewRunnerToken(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - token, err := NewRunnerToken(db.DefaultContext, optional.Some[int64](1), optional.None[int64]()) + token, err := NewRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) - expectedToken, err := GetLatestRunnerToken(db.DefaultContext, optional.Some[int64](1), optional.None[int64]()) + expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) assert.Equal(t, expectedToken, token) } @@ -35,8 +34,8 @@ func TestUpdateRunnerToken(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3}) token.IsActive = true - require.NoError(t, UpdateRunnerToken(db.DefaultContext, token, "is_active")) - expectedToken, err := GetLatestRunnerToken(db.DefaultContext, optional.Some[int64](1), optional.None[int64]()) + require.NoError(t, UpdateRunnerToken(db.DefaultContext, token)) + expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0) require.NoError(t, err) assert.Equal(t, expectedToken, token) } diff --git a/models/actions/schedule.go b/models/actions/schedule.go index bd6755621b..05c9f15d38 100644 --- a/models/actions/schedule.go +++ b/models/actions/schedule.go @@ -5,6 +5,7 @@ package actions import ( "context" + "time" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" @@ -20,7 +21,7 @@ import ( type ActionSchedule struct { ID int64 Title string - Specs []*ActionScheduleSpec `xorm:"-"` + Specs []string RepoID int64 `xorm:"index"` Repo *repo_model.Repository `xorm:"-"` OwnerID int64 `xorm:"index"` @@ -72,12 +73,25 @@ func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error { return err } + // Loop through each schedule spec and create a new spec row + now := time.Now() + for _, spec := range row.Specs { - spec.ScheduleID = row.ID - spec.RepoID = row.RepoID + specRow := &ActionScheduleSpec{ + RepoID: row.RepoID, + ScheduleID: row.ID, + Spec: spec, + } + // Parse the spec and check for errors + schedule, err := specRow.Parse() + if err != nil { + continue // skip to the next spec if there's an error + } + + specRow.Next = timeutil.TimeStamp(schedule.Next(now).Unix()) // Insert the new schedule spec row - if err = db.Insert(ctx, spec); err != nil { + if err = db.Insert(ctx, specRow); err != nil { return err } } @@ -116,7 +130,7 @@ func (opts FindScheduleOptions) ToConds() builder.Cond { if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) } - if opts.OwnerID != 0 { + if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } diff --git a/models/actions/schedule_spec.go b/models/actions/schedule_spec.go index 864fb5e2db..83bdceb850 100644 --- a/models/actions/schedule_spec.go +++ b/models/actions/schedule_spec.go @@ -10,10 +10,9 @@ import ( "forgejo.org/models/db" repo_model "forgejo.org/models/repo" - "forgejo.org/modules/optional" "forgejo.org/modules/timeutil" - "github.com/gdgvda/cron" + "github.com/robfig/cron/v3" ) // ActionScheduleSpec represents a schedule spec of a workflow file @@ -28,58 +27,36 @@ type ActionScheduleSpec struct { // started or this entry's schedule is unsatisfiable Next timeutil.TimeStamp `xorm:"index"` // Prev is the last time this job was run, or the zero time if never. - Prev timeutil.TimeStamp - Spec string - TimeZone optional.Option[string] + Prev timeutil.TimeStamp + Spec string Created timeutil.TimeStamp `xorm:"created"` Updated timeutil.TimeStamp `xorm:"updated"` } -func NewActionScheduleSpec(cron string, tz optional.Option[string], referenceTime time.Time) (*ActionScheduleSpec, error) { - spec := &ActionScheduleSpec{ - Spec: cron, - TimeZone: tz, - } - cronSchedule, err := spec.Parse() - if err != nil { - return nil, err - } - - spec.Next = timeutil.TimeStamp(cronSchedule.Next(referenceTime).Unix()) - return spec, nil -} - // Parse parses the spec and returns a cron.Schedule // Unlike the default cron parser, Parse uses UTC timezone as the default if none is specified. func (s *ActionScheduleSpec) Parse() (cron.Schedule, error) { - parser, err := cron.NewDefaultParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor) - if err != nil { - return nil, err - } - + parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor) schedule, err := parser.Parse(s.Spec) if err != nil { return nil, err } - // If `timezone` is not defined in the workflow, but the spec includes a timezone, use it. - if !s.TimeZone.Has() && (strings.HasPrefix(s.Spec, "TZ=") || strings.HasPrefix(s.Spec, "CRON_TZ=")) { + // If the spec has specified a timezone, use it + if strings.HasPrefix(s.Spec, "TZ=") || strings.HasPrefix(s.Spec, "CRON_TZ=") { return schedule, nil } - var location *time.Location - if present, tz := s.TimeZone.Get(); present { - location, err = time.LoadLocation(tz) - if err != nil { - return nil, err - } - } else { - // UTC is the default time zone. - location = time.UTC + specSchedule, ok := schedule.(*cron.SpecSchedule) + // If it's not a spec schedule, like "@every 5m", timezone is not relevant + if !ok { + return schedule, nil } - return schedule.WithLocation(location), nil + // Set the timezone to UTC + specSchedule.Location = time.UTC + return specSchedule, nil } func init() { diff --git a/models/actions/schedule_spec_test.go b/models/actions/schedule_spec_test.go index 6b9db9f39a..0c26fce4b2 100644 --- a/models/actions/schedule_spec_test.go +++ b/models/actions/schedule_spec_test.go @@ -7,50 +7,10 @@ import ( "testing" "time" - "forgejo.org/modules/optional" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestActionScheduleSpec_NewActionScheduleSpec(t *testing.T) { - tests := []struct { - name string - refTime time.Time - cronPattern string - timeZone string - want string - wantErr assert.ErrorAssertionFunc - }{ - { - name: "without timezone", - refTime: time.Date(2026, 4, 6, 11, 56, 0, 0, time.UTC), - cronPattern: "58 14 * * *", - want: "2026-04-06T14:58:00Z", - wantErr: assert.NoError, - }, - { - name: "with separate timezone", - refTime: time.Date(2026, 4, 6, 11, 56, 0, 0, time.UTC), - cronPattern: "58 14 * * *", - timeZone: "Europe/Tallinn", // +03 (EEST) - want: "2026-04-06T11:58:00Z", - wantErr: assert.NoError, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - s, err := NewActionScheduleSpec(test.cronPattern, optional.FromNonDefault(test.timeZone), test.refTime) - test.wantErr(t, err) - - if err == nil { - assert.Equal(t, test.want, s.Next.AsTime().UTC().Format(time.RFC3339)) - } - }) - } -} - func TestActionScheduleSpec_Parse(t *testing.T) { // Mock the local timezone is not UTC local := time.Local @@ -61,105 +21,50 @@ func TestActionScheduleSpec_Parse(t *testing.T) { }() time.Local = tz + now, err := time.Parse(time.RFC3339, "2024-07-31T15:47:55+08:00") + require.NoError(t, err) + tests := []struct { - name string - refTime time.Time - spec string - timeZone string - want string - wantErr assert.ErrorAssertionFunc + name string + spec string + want string + wantErr assert.ErrorAssertionFunc }{ { name: "regular", - refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local), spec: "0 10 * * *", want: "2024-07-31T10:00:00Z", wantErr: assert.NoError, }, { name: "invalid", - refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local), spec: "0 10 * *", want: "", wantErr: assert.Error, }, { - name: "with TZ in cron schedule", - refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local), + name: "with timezone", spec: "TZ=America/New_York 0 10 * * *", want: "2024-07-31T14:00:00Z", wantErr: assert.NoError, }, { - name: "with CRON_TZ in cron schedule", - refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local), - spec: "CRON_TZ=America/New_York 0 10 * * *", - want: "2024-07-31T14:00:00Z", - wantErr: assert.NoError, - }, - { - name: "with separate time zone", - refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local), - spec: "0 10 * * *", - timeZone: "America/New_York", - want: "2024-07-31T14:00:00Z", - wantErr: assert.NoError, - }, - { - name: "separate time zone takes precedence over inlined time zone", - refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local), - spec: "CRON_TZ=Europe/Berlin 0 10 * * *", - timeZone: "America/New_York", - want: "2024-07-31T14:00:00Z", - wantErr: assert.NoError, - }, - { - name: "time zone irrelevant", - refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local), + name: "timezone irrelevant", spec: "@every 5m", want: "2024-07-31T07:52:55Z", wantErr: assert.NoError, }, - { - // The various cron implementations handle the DST jump forwards differently. The most popular approaches - // are (a) scheduling all jobs at 3 o'clock that were supposed to run between 2 and 3 o'clock, or (b) - // skipping the execution on that day because any time between 2 and 3 o'clock never happened. Forgejo uses - // option B because the code it inherited already did that and was exposed to users. - name: "skips execution during DST jump forwards", - refTime: time.Date(2025, 3, 30, 0, 55, 0, 0, time.UTC), // 01:55 local time - spec: "10 2 * * *", // The clock jumps at 2 o'clock to 3 o'clock. - timeZone: "Europe/Berlin", - want: "2025-03-31T00:10:00Z", - wantErr: assert.NoError, - }, - { - name: "executes a first time before DST jump backwards", - refTime: time.Date(2025, 10, 26, 0, 5, 0, 0, time.UTC), // 02:05 local time - spec: "10 2 * * *", // The clock jumps at 3 o'clock to 2 o'clock. - timeZone: "Europe/Berlin", - want: "2025-10-26T00:10:00Z", - wantErr: assert.NoError, - }, - { - name: "executes a second time after DST jump backwards", - refTime: time.Date(2025, 10, 26, 1, 5, 0, 0, time.UTC), // 02:05 local time - spec: "10 2 * * *", // The clock jumps at 3 o'clock to 2 o'clock. - timeZone: "Europe/Berlin", - want: "2025-10-26T01:10:00Z", - wantErr: assert.NoError, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := &ActionScheduleSpec{ - Spec: tt.spec, - TimeZone: optional.FromNonDefault(tt.timeZone), + Spec: tt.spec, } got, err := s.Parse() tt.wantErr(t, err) if err == nil { - assert.Equal(t, tt.want, got.Next(tt.refTime).UTC().Format(time.RFC3339)) + assert.Equal(t, tt.want, got.Next(now).UTC().Format(time.RFC3339)) } }) } diff --git a/models/actions/schedule_test.go b/models/actions/schedule_test.go deleted file mode 100644 index 016185cb42..0000000000 --- a/models/actions/schedule_test.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package actions - -import ( - "testing" - "time" - - "forgejo.org/models/db" - "forgejo.org/models/repo" - "forgejo.org/models/unittest" - "forgejo.org/models/user" - "forgejo.org/modules/optional" - "forgejo.org/modules/timeutil" - "forgejo.org/modules/webhook" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestScheduleCreateScheduleTask(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - user2 := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 2}) - repo62 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 62, Name: "test_workflows", OwnerID: user2.ID}) - - content := ` -on: - push: - schedule: - - cron: "2 13 * * *" - - cron: "03 13 * * *" - timezone: Europe/Paris -jobs: - test: - runs-on: debian - steps: - - run: | - echo "OK" -` - - referenceTime := time.Date(2026, 3, 27, 17, 41, 21, 0, time.UTC) - - specWithoutTZ, err := NewActionScheduleSpec("2 13 * * *", optional.None[string](), referenceTime) - require.NoError(t, err) - - specWithTZ, err := NewActionScheduleSpec("3 13 * * *", optional.Some("Europe/Paris"), referenceTime) - require.NoError(t, err) - - schedule := &ActionSchedule{ - Title: ".forgejo/workflows/test.yaml", - Specs: []*ActionScheduleSpec{specWithoutTZ, specWithTZ}, - RepoID: repo62.ID, - OwnerID: user2.ID, - WorkflowID: "test.yaml", - WorkflowDirectory: ".forgejo/workflows", - TriggerUserID: -2, - Ref: "main", - CommitSHA: "6af834a5bc97c1a337eb3a21d26903c5cdceca0c", - Event: webhook.HookEventPush, - EventPayload: "{\"action\":\"schedule\"}", - Content: []byte(content), - } - - err = CreateScheduleTask(t.Context(), []*ActionSchedule{schedule}) - require.NoError(t, err) - - schedules, err := db.Find[ActionSchedule](t.Context(), FindScheduleOptions{OwnerID: user2.ID, RepoID: repo62.ID}) - require.NoError(t, err) - require.Len(t, schedules, 1) - - assert.NotZero(t, schedules[0].ID) - assert.Equal(t, ".forgejo/workflows/test.yaml", schedules[0].Title) - assert.Equal(t, "test.yaml", schedules[0].WorkflowID) - assert.Equal(t, ".forgejo/workflows", schedules[0].WorkflowDirectory) - assert.Equal(t, int64(-2), schedules[0].TriggerUserID) - assert.Equal(t, "main", schedules[0].Ref) - assert.Equal(t, "6af834a5bc97c1a337eb3a21d26903c5cdceca0c", schedules[0].CommitSHA) - assert.Equal(t, webhook.HookEventPush, schedules[0].Event) - assert.JSONEq(t, "{\"action\":\"schedule\"}", schedules[0].EventPayload) - assert.Equal(t, []byte(content), schedules[0].Content) - - specs, total, err := FindSpecs(t.Context(), FindSpecOptions{RepoID: repo62.ID}) - require.NoError(t, err) - - assert.Equal(t, int64(2), total) - - assert.NotZero(t, specs[0].ID) - assert.Equal(t, schedules[0].ID, specs[0].ScheduleID) - assert.Equal(t, timeutil.TimeStamp(1774699380), specs[0].Next) - assert.Equal(t, "3 13 * * *", specs[0].Spec) - assert.Equal(t, optional.Some("Europe/Paris"), specs[0].TimeZone) - assert.Zero(t, specs[0].Prev) - - assert.NotZero(t, specs[1].ID) - assert.Equal(t, schedules[0].ID, specs[1].ScheduleID) - assert.Equal(t, timeutil.TimeStamp(1774702920), specs[1].Next) - assert.Equal(t, "2 13 * * *", specs[1].Spec) - assert.Equal(t, optional.None[string](), specs[1].TimeZone) - assert.Zero(t, specs[1].Prev) -} diff --git a/models/actions/status.go b/models/actions/status.go index 7f06b6231b..1f6aa5e890 100644 --- a/models/actions/status.go +++ b/models/actions/status.go @@ -4,8 +4,6 @@ package actions import ( - "slices" - "forgejo.org/modules/translation" runnerv1 "code.forgejo.org/forgejo/actions-proto/runner/v1" @@ -109,7 +107,12 @@ func (s Status) IsBlocked() bool { // In returns whether s is one of the given statuses func (s Status) In(statuses ...Status) bool { - return slices.Contains(statuses, s) + for _, v := range statuses { + if s == v { + return true + } + } + return false } func (s Status) AsResult() runnerv1.Result { diff --git a/models/actions/task.go b/models/actions/task.go index d056c786c4..1924c816bc 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -29,7 +29,7 @@ type ActionTask struct { Job *ActionRunJob `xorm:"-"` Steps []*ActionTaskStep `xorm:"-"` Attempt int64 - RunnerID int64 `xorm:"index index(request_key)"` + RunnerID int64 `xorm:"index"` Status Status `xorm:"index"` Started timeutil.TimeStamp `xorm:"index"` Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"` @@ -51,15 +51,6 @@ type ActionTask struct { LogIndexes LogIndexes `xorm:"LONGBLOB"` // line number to offset LogExpired bool `xorm:"index(stopped_log_expired)"` // files that are too old will be deleted - // When the FetchTask() API is invoked to create a task, unpreventable environmental errors may occur; for example, - // network disconnects and timeouts. If that API call has a unique identifier associated with it, it is stored in - // RunnerRequestKey. This allows the API call to be implemented idempotently using this state: if one API call - // assigns a task to a runner and a second API call is received from the same runner with the same request key, the - // existing assigned tasks can be returned. - // - // Indexed for an efficient search on runner_id=? AND runner_request_key=?. - RunnerRequestKey string `xorm:"index(request_key)"` - Created timeutil.TimeStamp `xorm:"created"` Updated timeutil.TimeStamp `xorm:"updated index"` } @@ -156,11 +147,6 @@ func (task *ActionTask) GenerateToken() { task.Token, task.TokenSalt, task.TokenHash, task.TokenLastEight = generateSaltedToken() } -// After using GenerateToken, UpdateToken can be used to update the database record affecting the same columns. -func (task *ActionTask) UpdateToken(ctx context.Context) error { - return UpdateTask(ctx, task, "token_hash", "token_salt", "token_last_eight") -} - // 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) { @@ -188,19 +174,6 @@ func GetTaskByID(ctx context.Context, id int64) (*ActionTask, error) { return &task, nil } -func HasTaskForRunner(ctx context.Context, runnerID int64) (bool, error) { - return db.GetEngine(ctx).Where("runner_id = ?", runnerID).Exist(&ActionTask{}) -} - -func GetTasksOfJob(ctx context.Context, jobID int64) ([]*ActionTask, error) { - var tasks []*ActionTask - err := db.GetEngine(ctx).Where("job_id=?", jobID).Find(&tasks) - if err != nil { - return nil, fmt.Errorf("cannot fetch tasks of job %d: %w", jobID, err) - } - return tasks, 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) @@ -265,15 +238,6 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro return nil, errNotExist } -func GetTasksByRunnerRequestKey(ctx context.Context, runner *ActionRunner, requestKey string) ([]*ActionTask, error) { - var tasks []*ActionTask - err := db.GetEngine(ctx).Where("runner_id = ? AND runner_request_key = ?", runner.ID, requestKey).Find(&tasks) - if err != nil { - return nil, err - } - return tasks, nil -} - func getConcurrencyCondition() builder.Cond { concurrencyCond := builder.NewCond() @@ -356,12 +320,12 @@ func GetAvailableJobsForRunner(e db.Engine, runner *ActionRunner) ([]*ActionRunJ return jobs, nil } -func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey, handle *string) (*ActionTask, bool, error) { - ctx, committer, err := db.TxContext(ctx) +func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) { + ctx, commiter, err := db.TxContext(ctx) if err != nil { return nil, false, err } - defer committer.Close() + defer commiter.Close() e := db.GetEngine(ctx) @@ -373,9 +337,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey, // TODO: a more efficient way to filter labels var job *ActionRunJob log.Trace("runner labels: %v", runner.AgentLabels) - for _, j := range jobs { - if j.IsRequestedByRunner(handle) && j.ItRunsOn(runner.AgentLabels) { - job = j + for _, v := range jobs { + if v.ItRunsOn(runner.AgentLabels) { + job = v break } } @@ -387,6 +351,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey, } now := timeutil.TimeStampNow() + job.Attempt++ job.Started = now job.Status = StatusRunning @@ -401,9 +366,6 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey, CommitSHA: job.CommitSHA, IsForkPullRequest: job.IsForkPullRequest, } - if requestKey != nil { - task.RunnerRequestKey = *requestKey - } task.GenerateToken() var workflowJob *jobparser.Job @@ -452,51 +414,13 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey, task.Job = job - if err := committer.Commit(); err != nil { + if err := commiter.Commit(); err != nil { return nil, false, err } return task, true, nil } -// Placeholder tasks are created when the status/content of an [ActionRunJob] is resolved by Forgejo without dispatch to -// a runner, specifically in the case of a workflow call's outer job. -func CreatePlaceholderTask(ctx context.Context, job *ActionRunJob, outputs map[string]string) (*ActionTask, error) { - actionTask := &ActionTask{ - JobID: job.ID, - Attempt: job.Attempt, - Started: timeutil.TimeStampNow(), - Stopped: timeutil.TimeStampNow(), - Status: job.Status, - RepoID: job.RepoID, - OwnerID: job.OwnerID, - CommitSHA: job.CommitSHA, - IsForkPullRequest: job.IsForkPullRequest, - } - // token isn't used on a placeholder task, but generation is needed due to the unique constraint on field TokenHash - actionTask.GenerateToken() - - err := db.WithTx(ctx, func(ctx context.Context) error { - _, err := db.GetEngine(ctx).Insert(actionTask) - if err != nil { - return fmt.Errorf("failure inserting action_task: %w", err) - } - - for key, value := range outputs { - err := InsertTaskOutputIfNotExist(ctx, actionTask.ID, key, value) - if err != nil { - return fmt.Errorf("failure inserting action_task_output %q: %w", key, err) - } - } - - return nil - }) - if err != nil { - return nil, err - } - return actionTask, nil -} - func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error { sess := db.GetEngine(ctx).ID(task.ID) if len(cols) > 0 { @@ -506,27 +430,6 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error { return err } -// DeleteTask removes the given task including all its steps and outputs. Removing logs and ephemeral runners is the -// caller's responsibility. -func DeleteTask(ctx context.Context, taskID int64) error { - return db.WithTx(ctx, func(ctx context.Context) error { - var err error - _, err = db.GetEngine(ctx).Delete(&ActionTaskStep{TaskID: taskID}) - if err != nil { - return fmt.Errorf("unable to delete steps of task %d: %w", taskID, err) - } - _, err = db.GetEngine(ctx).Delete(&ActionTaskOutput{TaskID: taskID}) - if err != nil { - return fmt.Errorf("unable to delete outputs of task %d: %w", taskID, err) - } - _, err = db.GetEngine(ctx).Delete(&ActionTask{ID: taskID}) - if err != nil { - return fmt.Errorf("unable to delete task %d: %w", taskID, err) - } - return nil - }) -} - func FindOldTasksToExpire(ctx context.Context, olderThan timeutil.TimeStamp, limit int) ([]*ActionTask, error) { e := db.GetEngine(ctx) diff --git a/models/actions/task_list.go b/models/actions/task_list.go index 4c0e060497..712eada378 100644 --- a/models/actions/task_list.go +++ b/models/actions/task_list.go @@ -1,5 +1,4 @@ // Copyright 2022 The Gitea Authors. All rights reserved. -// Copyright 2026 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package actions @@ -65,13 +64,13 @@ func (opts FindTaskOptions) ToConds() builder.Cond { if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) } - if opts.OwnerID != 0 { + if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } if opts.CommitSHA != "" { cond = cond.And(builder.Eq{"commit_sha": opts.CommitSHA}) } - if len(opts.Status) > 0 { + if opts.Status != nil { cond = cond.And(builder.In("status", opts.Status)) } if opts.UpdatedBefore > 0 { @@ -83,11 +82,11 @@ func (opts FindTaskOptions) ToConds() builder.Cond { if opts.RunnerID > 0 { cond = cond.And(builder.Eq{"runner_id": opts.RunnerID}) } - if has, value := opts.LogExpired.Get(); has { - cond = cond.And(builder.Eq{"log_expired": value}) + if opts.LogExpired.Has() { + cond = cond.And(builder.Eq{"log_expired": opts.LogExpired.Value()}) } - if has, value := opts.LogInStorage.Get(); has { - cond = cond.And(builder.Eq{"log_in_storage": value}) + if opts.LogInStorage.Has() { + cond = cond.And(builder.Eq{"log_in_storage": opts.LogInStorage.Value()}) } return cond } diff --git a/models/actions/task_test.go b/models/actions/task_test.go index 7969f52e73..73aff17a85 100644 --- a/models/actions/task_test.go +++ b/models/actions/task_test.go @@ -46,56 +46,3 @@ func TestActionTask_GetTaskByJobAttempt(t *testing.T) { _, err = GetTaskByJobAttempt(t.Context(), 192, 100) assert.ErrorContains(t, err, "task with job_id 192 and attempt 100: resource does not exist") } - -func TestActionTask_CreatePlaceholderTask(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - job := unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: 396}) - assert.EqualValues(t, 0, job.TaskID) - - task, err := CreatePlaceholderTask(t.Context(), job, map[string]string{"output1": "value1", "output2": "value2"}) - require.NoError(t, err) - - assert.NotEqualValues(t, 0, task.ID) - assert.Equal(t, job.ID, task.JobID) - assert.EqualValues(t, 0, task.Attempt) - assert.NotEqualValues(t, 0, task.Started) - assert.NotEqualValues(t, 0, task.Stopped) - assert.Equal(t, job.Status, task.Status) - assert.Equal(t, job.RepoID, task.RepoID) - assert.Equal(t, job.OwnerID, task.OwnerID) - assert.Equal(t, job.CommitSHA, task.CommitSHA) - assert.Equal(t, job.IsForkPullRequest, task.IsForkPullRequest) - - taskOutputs, err := FindTaskOutputByTaskID(t.Context(), task.ID) - require.NoError(t, err) - require.Len(t, taskOutputs, 2) - finalOutputs := map[string]string{} - for _, to := range taskOutputs { - finalOutputs[to.OutputKey] = to.OutputValue - } - assert.Equal(t, map[string]string{"output1": "value1", "output2": "value2"}, finalOutputs) -} - -func TestActionTask_GetTasksByRunnerRequestKey(t *testing.T) { - defer unittest.OverrideFixtures("models/actions/TestActionTask_GetTasksByRunnerRequestKey")() - require.NoError(t, unittest.PrepareTestDatabase()) - - runner := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 12345678}) - - // not matching runner_request_key - tasks, err := GetTasksByRunnerRequestKey(t.Context(), runner, "22288392-2c70-4125-bb01-c7da79fa280c") - require.NoError(t, err) - assert.Empty(t, tasks) - - // matching both runner_id and runner_request_key - tasks, err = GetTasksByRunnerRequestKey(t.Context(), runner, "0a7e017d-4201-4b34-8cf4-de0f431893a4") - require.NoError(t, err) - assert.Len(t, tasks, 2) - - // not matching runner_id - runner = unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 10000001}) - tasks, err = GetTasksByRunnerRequestKey(t.Context(), runner, "0a7e017d-4201-4b34-8cf4-de0f431893a4") - require.NoError(t, err) - assert.Empty(t, tasks) -} diff --git a/models/actions/tasks_version.go b/models/actions/tasks_version.go index 8678f052f0..a5c357888f 100644 --- a/models/actions/tasks_version.go +++ b/models/actions/tasks_version.go @@ -73,11 +73,11 @@ func increaseTasksVersionByScope(ctx context.Context, ownerID, repoID int64) err } func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error { - ctx, committer, err := db.TxContext(ctx) + ctx, commiter, err := db.TxContext(ctx) if err != nil { return err } - defer committer.Close() + defer commiter.Close() // 1. increase global if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil { @@ -101,5 +101,5 @@ func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error { } } - return committer.Commit() + return commiter.Commit() } diff --git a/models/activities/action.go b/models/activities/action.go index 9b67bd67f6..8fd7709e81 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -132,7 +132,12 @@ func (at ActionType) String() string { } func (at ActionType) InActions(actions ...string) bool { - return slices.Contains(actions, at.String()) + for _, action := range actions { + if action == at.String() { + return true + } + } + return false } // Action represents user operation type and other information to @@ -812,36 +817,3 @@ func FixActionCreatedUnixString(ctx context.Context) (int64, error) { } return 0, nil } - -func (a *Action) IsActionPrivate(ctx context.Context) (bool, error) { - if a.IsPrivate { - return true, nil - } - - a.loadRepo(ctx) - if a.Repo == nil { - return true, repo_model.ErrRepoNotExist{} - } - - repo := a.Repo - err := repo.LoadOwner(ctx) - if err != nil { - return true, err - } - - if repo.IsPrivate || repo.Owner.KeepActivityPrivate || repo.Owner.Visibility != structs.VisibleTypePublic { - return true, nil - } - - a.LoadActUser(ctx) - if a.ActUser == nil { - return true, user_model.ErrUserNotExist{} - } - - user := a.ActUser - if user.KeepActivityPrivate || user.Visibility != structs.VisibleTypePublic { - return true, nil - } - - return false, nil -} diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 592fdf71e3..bfc07f58ae 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -371,28 +371,3 @@ func TestGetIssueInfos(t *testing.T) { assert.Equal(t, test.field3, issueInfos[2]) } } - -func TestIsPrivate(t *testing.T) { - defer unittest.OverrideFixtures("models/activities/fixtures/TestIsPrivate")() - require.NoError(t, unittest.PrepareTestDatabase()) - - tt := []struct { - activityID int64 - private bool - }{ - {1, true}, // private repo - {3, false}, // public activities, public repo - {11, true}, // private activities - } - - for _, test := range tt { - ctx := t.Context() - action, err := activities_model.GetActivityByID(ctx, test.activityID) - require.NoError(t, err) - - private, err := action.IsActionPrivate(ctx) - require.NoError(t, err) - - assert.Equal(t, test.private, private, "action ID: %d", test.activityID) - } -} diff --git a/models/activities/error.go b/models/activities/error.go deleted file mode 100644 index 85896e97d9..0000000000 --- a/models/activities/error.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package activities - -import "fmt" - -type ErrActivityPrivate struct { - id int64 -} - -func (err ErrActivityPrivate) Error() string { - return fmt.Sprintf("Activity with id %d is private", err.id) -} diff --git a/models/activities/federated_user_activity.go b/models/activities/federated_user_activity.go index a9f509e8f7..1ff3a855d0 100644 --- a/models/activities/federated_user_activity.go +++ b/models/activities/federated_user_activity.go @@ -82,7 +82,7 @@ func GetFollowingFeeds(ctx context.Context, actorID int64, opts GetFollowingFeed sess = db.SetSessionPagination(sess, &opts) actions := make([]*FederatedUserActivity, 0, opts.PageSize) - count, err := sess.Desc("`federated_user_activity`.created").FindAndCount(&actions) + count, err := sess.FindAndCount(&actions) if err != nil { return nil, 0, fmt.Errorf("FindAndCount: %w", err) } diff --git a/models/activities/fixtures/TestIsPrivate/action.yml b/models/activities/fixtures/TestIsPrivate/action.yml deleted file mode 100644 index 681cea1939..0000000000 --- a/models/activities/fixtures/TestIsPrivate/action.yml +++ /dev/null @@ -1,7 +0,0 @@ -- id: 11 - user_id: 44 - op_type: 1 # create repo - act_user_id: 44 # private user activities - repo_id: 60 # public - is_private: false - created_unix: 1680454039 diff --git a/models/activities/fixtures/TestIsPrivate/user.yml b/models/activities/fixtures/TestIsPrivate/user.yml deleted file mode 100644 index dd7bf1a657..0000000000 --- a/models/activities/fixtures/TestIsPrivate/user.yml +++ /dev/null @@ -1,36 +0,0 @@ -- id: 44 - lower_name: user44 - name: user44 - full_name: user44 - email: user44@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: user44 - type: 0 - salt: ZogKvWdyEx - max_repo_creation: -1 - is_active: true - is_admin: false - is_restricted: false - allow_git_hook: false - allow_import_local: false - allow_create_organization: true - prohibit_login: false - avatar: "" - avatar_email: user44@example.com - use_custom_avatar: true - 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: true - created_unix: 1672578380 diff --git a/models/activities/notification_list.go b/models/activities/notification_list.go index 3f3a48eaa5..02206b1d6b 100644 --- a/models/activities/notification_list.go +++ b/models/activities/notification_list.go @@ -210,9 +210,34 @@ func (nl NotificationList) LoadRepos(ctx context.Context) (repo_model.Repository } repoIDs := nl.getPendingRepoIDs() - repos, err := db.GetByIDs(ctx, "id", repoIDs, &repo_model.Repository{}) - if err != nil { - return nil, nil, err + repos := make(map[int64]*repo_model.Repository, len(repoIDs)) + left := len(repoIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("id", repoIDs[:limit]). + Rows(new(repo_model.Repository)) + if err != nil { + return nil, nil, err + } + + for rows.Next() { + var repo repo_model.Repository + err = rows.Scan(&repo) + if err != nil { + rows.Close() + return nil, nil, err + } + + repos[repo.ID] = &repo + } + _ = rows.Close() + + left -= limit + repoIDs = repoIDs[limit:] } failed := []int{} @@ -259,9 +284,34 @@ func (nl NotificationList) LoadIssues(ctx context.Context) ([]int, error) { } issueIDs := nl.getPendingIssueIDs() - issues, err := db.GetByIDs(ctx, "id", issueIDs, &issues_model.Issue{}) - if err != nil { - return nil, err + issues := make(map[int64]*issues_model.Issue, len(issueIDs)) + left := len(issueIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("id", issueIDs[:limit]). + Rows(new(issues_model.Issue)) + if err != nil { + return nil, err + } + + for rows.Next() { + var issue issues_model.Issue + err = rows.Scan(&issue) + if err != nil { + rows.Close() + return nil, err + } + + issues[issue.ID] = &issue + } + _ = rows.Close() + + left -= limit + issueIDs = issueIDs[limit:] } failures := []int{} @@ -329,9 +379,34 @@ func (nl NotificationList) LoadUsers(ctx context.Context) ([]int, error) { } userIDs := nl.getUserIDs() - users, err := db.GetByIDs(ctx, "id", userIDs, &user_model.User{}) - if err != nil { - return nil, err + users := make(map[int64]*user_model.User, len(userIDs)) + left := len(userIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("id", userIDs[:limit]). + Rows(new(user_model.User)) + if err != nil { + return nil, err + } + + for rows.Next() { + var user user_model.User + err = rows.Scan(&user) + if err != nil { + rows.Close() + return nil, err + } + + users[user.ID] = &user + } + _ = rows.Close() + + left -= limit + userIDs = userIDs[limit:] } failures := []int{} @@ -355,9 +430,34 @@ func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) { } commentIDs := nl.getPendingCommentIDs() - comments, err := db.GetByIDs(ctx, "id", commentIDs, &issues_model.Comment{}) - if err != nil { - return nil, err + comments := make(map[int64]*issues_model.Comment, len(commentIDs)) + left := len(commentIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("id", commentIDs[:limit]). + Rows(new(issues_model.Comment)) + if err != nil { + return nil, err + } + + for rows.Next() { + var comment issues_model.Comment + err = rows.Scan(&comment) + if err != nil { + rows.Close() + return nil, err + } + + comments[comment.ID] = &comment + } + _ = rows.Close() + + left -= limit + commentIDs = commentIDs[limit:] } failures := []int{} diff --git a/models/activities/repo_activity.go b/models/activities/repo_activity.go index 0b9522be1b..3d15c22e19 100644 --- a/models/activities/repo_activity.go +++ b/models/activities/repo_activity.go @@ -138,7 +138,10 @@ func GetActivityStatsTopAuthors(ctx context.Context, repo *repo_model.Repository return v[i].Commits > v[j].Commits }) - cnt := min(count, len(v)) + cnt := count + if cnt > len(v) { + cnt = len(v) + } return v[:cnt], nil } diff --git a/models/asymkey/gpg_key.go b/models/asymkey/gpg_key.go index 620e0ef0ae..cae717011e 100644 --- a/models/asymkey/gpg_key.go +++ b/models/asymkey/gpg_key.go @@ -81,7 +81,7 @@ func (opts FindGPGKeyOptions) ToConds() builder.Cond { cond = cond.And(builder.Eq{"primary_key_id": ""}) } - if opts.OwnerID != 0 { + if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } if opts.KeyID != "" { diff --git a/models/asymkey/gpg_key_test.go b/models/asymkey/gpg_key_test.go index 2da3bbe6e0..d265f438eb 100644 --- a/models/asymkey/gpg_key_test.go +++ b/models/asymkey/gpg_key_test.go @@ -11,6 +11,7 @@ import ( "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "github.com/ProtonMail/go-crypto/openpgp/packet" "github.com/stretchr/testify/assert" @@ -457,7 +458,7 @@ epiDVQ== func TestTryGetKeyIDFromSignature(t *testing.T) { assert.Empty(t, tryGetKeyIDFromSignature(&packet.Signature{})) assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{ - IssuerKeyId: new(uint64(0x38D1A3EADDBEA9C)), + IssuerKeyId: util.ToPointer(uint64(0x38D1A3EADDBEA9C)), })) assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{ IssuerFingerprint: []uint8{0xb, 0x23, 0x24, 0xc7, 0xe6, 0xfe, 0x4f, 0x3a, 0x6, 0x26, 0xc1, 0x21, 0x3, 0x8d, 0x1a, 0x3e, 0xad, 0xdb, 0xea, 0x9c}, diff --git a/models/asymkey/lint-locale-usage/llu.go b/models/asymkey/lint-locale-usage/llu.go deleted file mode 100644 index 2d6a02ff44..0000000000 --- a/models/asymkey/lint-locale-usage/llu.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package lintLocaleUsage - -import ( - "go/ast" - "go/token" - - llu "forgejo.org/build/lint-locale-usage" -) - -// special case: models/asymkey/*.go, -// -// handle &ObjectVerification{...} -func HandleCompositeErrorReason(handler llu.Handler, fset *token.FileSet, n *ast.CompositeLit) { - ident, ok := n.Type.(*ast.Ident) - if !ok || ident.Name != "ObjectVerification" { - return - } - - // fields are normally named - var reason ast.Expr - verified := false - for _, i := range n.Elts { - if kve, ok := i.(*ast.KeyValueExpr); ok { - ident, ok = kve.Key.(*ast.Ident) - if !ok { - continue - } - switch ident.Name { - case "Reason": - reason = kve.Value - case "Verified": - if valueIdent, ok := kve.Value.(*ast.Ident); ok { - switch valueIdent.Name { - case "true": - verified = true - case "false": - verified = false - } - } - } - } else { - handler.OnWarning(fset, i.Pos(), "unable to parse ObjectVerification field assignment") - } - } - if !verified && reason != nil { - handler.HandleGoTrArgument(fset, reason, "") - } -} diff --git a/models/asymkey/ssh_key.go b/models/asymkey/ssh_key.go index ea05195b5d..7f76009e7f 100644 --- a/models/asymkey/ssh_key.go +++ b/models/asymkey/ssh_key.go @@ -190,7 +190,7 @@ type FindPublicKeyOptions struct { func (opts FindPublicKeyOptions) ToConds() builder.Cond { cond := builder.NewCond() - if opts.OwnerID != 0 { + if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } if opts.Fingerprint != "" { diff --git a/models/asymkey/ssh_key_object_verification_test.go b/models/asymkey/ssh_key_object_verification_test.go index 27d8d66a3c..4bfd79d56e 100644 --- a/models/asymkey/ssh_key_object_verification_test.go +++ b/models/asymkey/ssh_key_object_verification_test.go @@ -24,14 +24,14 @@ func TestParseCommitWithSSHSignature(t *testing.T) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) sshKey := unittest.AssertExistsAndLoadBean(t, &PublicKey{ID: 1000, OwnerID: 2}) - t.Run("No committer", func(t *testing.T) { + t.Run("No commiter", func(t *testing.T) { o := commitToGitObject(&git.Commit{}) commitVerification := ParseObjectWithSSHSignature(db.DefaultContext, &o, &user_model.User{}) assert.False(t, commitVerification.Verified) assert.Equal(t, NoKeyFound, commitVerification.Reason) }) - t.Run("Committer without keys", func(t *testing.T) { + t.Run("Commiter without keys", func(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) o := commitToGitObject(&git.Commit{Committer: &git.Signature{Email: user.Email}}) diff --git a/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integ_resource_repo.yml b/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integ_resource_repo.yml deleted file mode 100644 index 5e0ccf1130..0000000000 --- a/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integ_resource_repo.yml +++ /dev/null @@ -1,9 +0,0 @@ -- integ_id: 1 - repo_id: 1 - created_unix: 1772158384 -- integ_id: 1 - repo_id: 2 - created_unix: 1772158384 -- integ_id: 1 - repo_id: 3 - created_unix: 1772158384 diff --git a/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integration.yml b/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integration.yml deleted file mode 100644 index 18e5e68054..0000000000 --- a/models/auth/TestGetRepositoriesAccessibleWithIntegration/authorized_integration.yml +++ /dev/null @@ -1,9 +0,0 @@ -- id: 1 - user_id: 2 - scope: all - resource_all_repos: false - issuer: https://example.org/ - audience: https://forgejo.example.org/-/user/integration/abcdef123 - claim_rules: "{}" - created_unix: 1777153359 - updated_unix: 1777153359 diff --git a/models/auth/TestGetRepositoriesAccessibleWithToken/access_token_resource_repo.yml b/models/auth/TestGetRepositoriesAccessibleWithToken/access_token_resource_repo.yml deleted file mode 100644 index e3266a8c91..0000000000 --- a/models/auth/TestGetRepositoriesAccessibleWithToken/access_token_resource_repo.yml +++ /dev/null @@ -1,9 +0,0 @@ -- token_id: 3 - repo_id: 1 - created_unix: 1772158384 -- token_id: 3 - repo_id: 2 - created_unix: 1772158384 -- token_id: 3 - repo_id: 3 - created_unix: 1772158384 diff --git a/models/auth/TestGetRepositoriesAccessibleWithTokens/access_token_resource_repo.yml b/models/auth/TestGetRepositoriesAccessibleWithTokens/access_token_resource_repo.yml deleted file mode 100644 index 1f1a552361..0000000000 --- a/models/auth/TestGetRepositoriesAccessibleWithTokens/access_token_resource_repo.yml +++ /dev/null @@ -1,17 +0,0 @@ -- token_id: 3 - repo_id: 1 - created_unix: 1772158384 -- token_id: 3 - repo_id: 2 - created_unix: 1772158384 -- token_id: 3 - repo_id: 3 - created_unix: 1772158384 - -- token_id: 2 - repo_id: 1 - created_unix: 1772158384 - # (no repo 2 for token 2) -- token_id: 2 - repo_id: 3 - created_unix: 1772158384 diff --git a/models/auth/access_token.go b/models/auth/access_token.go index 3311769573..ceade7dbad 100644 --- a/models/auth/access_token.go +++ b/models/auth/access_token.go @@ -73,8 +73,6 @@ type AccessToken struct { UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` HasRecentActivity bool `xorm:"-"` HasUsed bool `xorm:"-"` - - ResourceAllRepos bool `xorm:"NOT NULL DEFAULT TRUE"` // flag for whether AccessTokenResourceRepo instances will limit the resources this access token can access (false) or won't limit them (true). } // AfterLoad is invoked from XORM after setting the values of all fields of this object. @@ -224,23 +222,15 @@ func (opts ListAccessTokensOptions) ToOrders() string { // DeleteAccessTokenByID deletes access token by given ID. func DeleteAccessTokenByID(ctx context.Context, id, userID int64) error { - return db.WithTx(ctx, func(ctx context.Context) error { - if err := db.DeleteBeans(ctx, - &AccessTokenResourceRepo{TokenID: id}, - ); err != nil { - return fmt.Errorf("DeleteBeans: %w", err) - } - - cnt, err := db.GetEngine(ctx).ID(id).Delete(&AccessToken{ - UID: userID, - }) - if err != nil { - return err - } else if cnt != 1 { - return ErrAccessTokenNotExist{} - } - return nil + cnt, err := db.GetEngine(ctx).ID(id).Delete(&AccessToken{ + UID: userID, }) + if err != nil { + return err + } else if cnt != 1 { + return ErrAccessTokenNotExist{} + } + return nil } // RegenerateAccessTokenByID regenerates access token by given ID. diff --git a/models/auth/access_token_resource.go b/models/auth/access_token_resource.go deleted file mode 100644 index 85978dd131..0000000000 --- a/models/auth/access_token_resource.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package auth - -import ( - "context" - - "forgejo.org/models/db" - "forgejo.org/modules/timeutil" -) - -// Represents a many-to-many join table which indicates specific repositories (RepoID) that can be accessed by an access -// token (TokenID). An access token's ResourceAllRepos field must be false for records in this table to become active. -type AccessTokenResourceRepo struct { - ID int64 `xorm:"pk autoincr"` - TokenID int64 `xorm:"NOT NULL REFERENCES(access_token, id)"` // needs to be shortened from "AccessTokenID" for the index to fit MySQL table identifier length restrictions - RepoID int64 `xorm:"NOT NULL REFERENCES(repository, id)"` - - CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` -} - -func init() { - db.RegisterModel(new(AccessTokenResourceRepo)) -} - -func (atr *AccessTokenResourceRepo) GetTargetRepoID() int64 { - return atr.RepoID -} - -func GetRepositoriesAccessibleWithToken(ctx context.Context, accessTokenID int64) ([]*AccessTokenResourceRepo, error) { - var resources []*AccessTokenResourceRepo - err := db.GetEngine(ctx). - Where("token_id = ?", accessTokenID). - Find(&resources) - if err != nil { - return nil, err - } - return resources, nil -} - -func GetRepositoriesAccessibleWithTokens(ctx context.Context, accessTokens []*AccessToken) (map[int64][]*AccessTokenResourceRepo, error) { - accessTokenIDs := make([]int64, len(accessTokens)) - for i, at := range accessTokens { - accessTokenIDs[i] = at.ID - } - - var resources []*AccessTokenResourceRepo - err := db.GetEngine(ctx). - In("token_id", accessTokenIDs). - Find(&resources) - if err != nil { - return nil, err - } - retval := make(map[int64][]*AccessTokenResourceRepo) - for _, resource := range resources { - retval[resource.TokenID] = append(retval[resource.TokenID], resource) - } - return retval, nil -} - -func InsertAccessTokenResourceRepos(ctx context.Context, accessTokenID int64, resources []*AccessTokenResourceRepo) error { - return db.WithTx(ctx, func(ctx context.Context) error { - for _, resourceRepo := range resources { - resourceRepo.TokenID = accessTokenID - if err := db.Insert(ctx, resourceRepo); err != nil { - return err - } - } - return nil - }) -} diff --git a/models/auth/access_token_resource_test.go b/models/auth/access_token_resource_test.go deleted file mode 100644 index d8cd4e286a..0000000000 --- a/models/auth/access_token_resource_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package auth_test - -import ( - "testing" - - auth_model "forgejo.org/models/auth" - "forgejo.org/models/unittest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetRepositoriesAccessibleWithToken(t *testing.T) { - defer unittest.OverrideFixtures("models/auth/TestGetRepositoriesAccessibleWithToken")() - require.NoError(t, unittest.PrepareTestDatabase()) - - t.Run("No Resources", func(t *testing.T) { - resources, err := auth_model.GetRepositoriesAccessibleWithToken(t.Context(), 999) - require.NoError(t, err) - assert.Empty(t, resources) - }) - - t.Run("Has Resources", func(t *testing.T) { - resources, err := auth_model.GetRepositoriesAccessibleWithToken(t.Context(), 3) - require.NoError(t, err) - require.Len(t, resources, 3) - - // Verify all expected repo IDs are present - repoIDs := make([]int64, len(resources)) - for i, res := range resources { - repoIDs[i] = res.RepoID - } - assert.Contains(t, repoIDs, int64(1)) - assert.Contains(t, repoIDs, int64(2)) - assert.Contains(t, repoIDs, int64(3)) - }) -} - -func TestGetRepositoriesAccessibleWithTokens(t *testing.T) { - defer unittest.OverrideFixtures("models/auth/TestGetRepositoriesAccessibleWithTokens")() - require.NoError(t, unittest.PrepareTestDatabase()) - - token1 := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 1}) - token2 := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 2}) - token3 := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 3}) - - t.Run("No Tokens", func(t *testing.T) { - resources, err := auth_model.GetRepositoriesAccessibleWithTokens(t.Context(), []*auth_model.AccessToken{}) - require.NoError(t, err) - assert.Empty(t, resources) - }) - - t.Run("Multiple Access Tokens", func(t *testing.T) { - resources, err := auth_model.GetRepositoriesAccessibleWithTokens(t.Context(), []*auth_model.AccessToken{token1, token2, token3}) - require.NoError(t, err) - - repos1, ok := resources[token1.ID] - assert.False(t, ok) - assert.Empty(t, repos1) - - repos2, ok := resources[token2.ID] - assert.True(t, ok) - assert.Len(t, repos2, 2) - - repos3, ok := resources[token3.ID] - assert.True(t, ok) - assert.Len(t, repos3, 3) - }) -} - -func TestInsertAccessTokenResourceRepos(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - token1 := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 1}) - token2 := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 2}) - token3 := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ID: 3}) - - t.Run("blank insert", func(t *testing.T) { - err := auth_model.InsertAccessTokenResourceRepos(t.Context(), token1.ID, nil) - require.NoError(t, err) - }) - - t.Run("multiple insert", func(t *testing.T) { - resRepo1 := &auth_model.AccessTokenResourceRepo{ - TokenID: token2.ID, - RepoID: 1, - } - resRepo3 := &auth_model.AccessTokenResourceRepo{ - TokenID: token2.ID, - RepoID: 3, - } - err := auth_model.InsertAccessTokenResourceRepos(t.Context(), token2.ID, - []*auth_model.AccessTokenResourceRepo{resRepo1, resRepo3}) - require.NoError(t, err) - - unittest.AssertCount(t, &auth_model.AccessTokenResourceRepo{TokenID: token2.ID}, 2) - }) - - t.Run("in tx", func(t *testing.T) { - // Pre-condition: count is 0. - unittest.AssertCount(t, &auth_model.AccessTokenResourceRepo{TokenID: token3.ID}, 0) - - // Verify that InsertAccessTokenResourceRepos performs inserts in a TX by having a second one with an invalid - // RepoID, causing a foreign key violation - resRepo1 := &auth_model.AccessTokenResourceRepo{ - TokenID: token3.ID, - RepoID: 1, - } - resRepo3 := &auth_model.AccessTokenResourceRepo{ - TokenID: token3.ID, - RepoID: 30000, // invalid - } - err := auth_model.InsertAccessTokenResourceRepos(t.Context(), token3.ID, - []*auth_model.AccessTokenResourceRepo{resRepo1, resRepo3}) - require.ErrorContains(t, err, "foreign key") - - // Count remains 0; the first record was not inserted. - unittest.AssertCount(t, &auth_model.AccessTokenResourceRepo{TokenID: token3.ID}, 0) - }) -} diff --git a/models/auth/access_token_scope.go b/models/auth/access_token_scope.go index 6dc143b122..d14838cf02 100644 --- a/models/auth/access_token_scope.go +++ b/models/auth/access_token_scope.go @@ -5,7 +5,6 @@ package auth import ( "fmt" - "slices" "strings" "forgejo.org/models/perm" @@ -205,7 +204,12 @@ func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTok // ContainsCategory checks if a list of categories contains a specific category func ContainsCategory(categories []AccessTokenScopeCategory, category AccessTokenScopeCategory) bool { - return slices.Contains(categories, category) + for _, c := range categories { + if c == category { + return true + } + } + return false } // GetScopeLevelFromAccessMode converts permission access mode to scope level diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go index 69dc1f8d94..d01ddbca1e 100644 --- a/models/auth/auth_token.go +++ b/models/auth/auth_token.go @@ -11,7 +11,6 @@ import ( "time" "forgejo.org/models/db" - "forgejo.org/modules/optional" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" ) @@ -22,9 +21,6 @@ var ( // Used to store long term authorization tokens. LongTermAuthorization AuthorizationPurpose = "long_term_authorization" - // Long-term token for OAuth2/OIDC sign-ins; bounces through the IdP rather than auto-logging in. - LongTermAuthorizationSSO AuthorizationPurpose = "long_term_authorization_sso" - // Used to activate a user account. UserActivation AuthorizationPurpose = "user_activation" @@ -43,8 +39,7 @@ type AuthorizationToken struct { UID int64 `xorm:"INDEX REFERENCES(user, id)"` LookupKey string `xorm:"INDEX UNIQUE"` HashedValidator string - Purpose AuthorizationPurpose `xorm:"NOT NULL DEFAULT 'long_term_authorization'"` - LoginSourceID optional.Option[int64] `xorm:"INDEX REFERENCES(login_source, id)"` + Purpose AuthorizationPurpose `xorm:"NOT NULL DEFAULT 'long_term_authorization'"` Expiry timeutil.TimeStamp } @@ -65,7 +60,7 @@ func (authToken *AuthorizationToken) IsExpired() bool { // GenerateAuthToken generates a new authentication token for the given user. // It returns the lookup key and validator values that should be passed to the // user via a long-term cookie. -func GenerateAuthToken(ctx context.Context, userID int64, loginSourceID optional.Option[int64], expiry timeutil.TimeStamp, purpose AuthorizationPurpose) (lookupKey, validator string, err error) { +func GenerateAuthToken(ctx context.Context, userID int64, expiry timeutil.TimeStamp, purpose AuthorizationPurpose) (lookupKey, validator string, err error) { // Request 64 random bytes. The first 32 bytes will be used for the lookupKey // and the other 32 bytes will be used for the validator. rBytes := util.CryptoRandomBytes(64) @@ -78,7 +73,6 @@ func GenerateAuthToken(ctx context.Context, userID int64, loginSourceID optional LookupKey: lookupKey, HashedValidator: HashValidator(rBytes[32:]), Purpose: purpose, - LoginSourceID: loginSourceID, }) return lookupKey, validator, err } diff --git a/models/auth/authorized_integration.go b/models/auth/authorized_integration.go deleted file mode 100644 index 1f1fc52a2d..0000000000 --- a/models/auth/authorized_integration.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package auth - -import ( - "context" - "errors" - "fmt" - "time" - - "forgejo.org/models/db" - "forgejo.org/modules/optional" - "forgejo.org/modules/timeutil" - "forgejo.org/modules/util" - - gouuid "github.com/google/uuid" - "xorm.io/builder" -) - -// An Authorized Integration allow users to define external systems which can generate JSON Web Tokens (JWTs) that -// Forgejo will trust in order to perform API access on behalf of a user defined by the UserID field. -// -// When a JWT is received by Forgejo, the issuer (iss) and audience (aud) claims are used to lookup an authorized -// integration with an exact match. Together these fields serve as a unique key for the authorized issuer. Duplicates -// cannot be permitted because we would not know which user to authenticate the JWT as. -type AuthorizedIntegration struct { - ID int64 `xorm:"pk autoincr"` - - UserID int64 `xorm:"NOT NULL REFERENCES(user, id)"` - Scope AccessTokenScope `xorm:"NOT NULL"` - ResourceAllRepos bool `xorm:"NOT NULL"` // flag for whether AuthorizedIntegrationResourceRepo instances will limit the resources this access token can access (false) or won't limit them (true). - - Name string // short name for lists of authorized integrations - Description string `xorm:"LONGTEXT"` // long description, optional to document relevant details of the integration - - // Which UI to use for view/edit of this Authorized Integration. Authorized Integrations' functional behaviour is - // defined by other fields, such as the Issuer, Audience, ClaimRules. The UI field only defines how this record can - // be interacted with by the user in order to provide user-friendly access for specific systems -- like Forgejo - // Actions. Within the potential scope of the UI is any user interaction with the Authorized Integration -- web - // create, read, update, API, CLI. - // - // The UI field must never be used to make functional decisions about evaluating JWTs. It must always be possible - // to convert an Authorized Integration to a "generic" UI (for customization that the UI doesn't support). The - // intent of this design is that, the Authorized Integration system is complicated in its claim rules, but they - // always fully define the behaviour in a transparent manner. - UI AuthorizedIntegrationUI `xorm:"NOT NULL default('generic')"` - - // Exact-match `iss` claim of the JWT - Issuer string `xorm:"NOT NULL UNIQUE(s)"` - // Exact-match `aud` claim of the JWT - Audience string `xorm:"NOT NULL UNIQUE(s)"` - ClaimRules *ClaimRules `xorm:"NOT NULL JSON"` - - CreatedUnix timeutil.TimeStamp `xorm:"NOT NULL created"` - UpdatedUnix timeutil.TimeStamp `xorm:"NOT NULL updated"` -} - -func init() { - db.RegisterModel(new(AuthorizedIntegration)) -} - -// An [AuthorizedIntegration] can validate the claims in a JWT against a set of rules defined by this structure. -// -// JWTs can contain any number of claims, which are represented as a JSON object. A small number of common claims are -// described in RFC7519 (sec 4.1) which defines JWTs, but most claims are entirely arbitrarily defined by the JWT -// issuer. -// -// For example, eg. a claim may be {"sub": "repo:coolguy/forgejo-runner-testrepo:pull_request"} indicating that an OIDC -// token was received from an Actions execution in a specific repo on a specific event. -// -// Validating the claims from a JWT issuer is a critical part of creating a secure [AuthorizedIssuer]. For example, -// assume that we receive a JWT from a public hosting platform like Codeberg. We will validate that it is a claim -// created by the correct Issuer, Codeberg -- but anyone can do that through Forgejo Actions. We will validate that it -// has the correct audience -- but that's an *input* to Forgejo Actions, so anyone can create a claim on Codeberg with -// an arbitrary audience. The rest of the claims contain the critical information about who ran a Forgejo Action, on -// which repository, and in response to which events, and those must be validated to ensure that an authorized issuer is -// correctly authorized. -// -// Following that an example, a minimum claim rule that would be required for securely using Forgejo Actions would be -// something like: -// -// { -// "rules": [{ -// "claim": "sub", -// "comparison": "eq", -// "value": "repo:forgejo/website:pull_request" -// }] -// } -// -// This defines a single rule which says that the `sub` claim must be exactly equal to -// "repo:forgejo/website:pull_request". Forgejo Actions would generate this subject when an Action is running on the -// repo forgejo/website in response to the pull_request event. -// -// Some JWT claims are JSON objects. The [ClaimNested] comparison operator can be used to define rules that inspect the -// object within a claim. For example, AWS STS generates a claim "https://sts.amazonaws.com/": {...} with values inside -// an object, like "aws_account". A nested claim can inspect those values: -// -// { -// "rules":[{ -// "claim": "https://sts.amazonaws.com/", -// "compare": "nest", -// "nested": {"rules":[ -// {"claim": "aws_account", "compare": "eq", "value": "1234567890"}, -// {"claim": "lambda_source_function_arn", "compare": "eq", "value": "arn:aws:lambda:ca-central-1:1234567890:function:forgejo-oidc-accepting-test"} -// ]} -// } -// -// ]} -// -// This defines a rule that looks into the "https://sts..." claim and verifies the "aws_account" and -// "lambda_source_function_arn" keys match specific known values. -type ClaimRules struct { - Rules []ClaimRule `json:"rules"` -} - -// Defines a single rule that will check the value of one JWT claim. -type ClaimRule struct { - // The target claim, eg. "sub" - Claim string `json:"claim"` - // Comparison rule to use on this claim - Comparison ClaimComparison `json:"compare"` - - // For Comparison of ClaimEqual or ClaimGlob, the specific value or glob to match against - Value string `json:"value,omitempty"` - - // For Comparison of ClaimIn or ClaimGlobIn, an array of values to match against - Values []string `json:"values,omitempty"` - - // For ClaimNested, the rules to apply to the nested object - Nested *ClaimRules `json:"nested,omitempty"` -} - -type ClaimComparison string - -const ( - ClaimEqual ClaimComparison = "eq" // exactly equal claim - ClaimIn ClaimComparison = "in" // exactly equal any of the options in a list - ClaimGlob ClaimComparison = "glob" // glob match complete claim string - ClaimGlobIn ClaimComparison = "glob-in" // glob match any of the options in a list - ClaimNested ClaimComparison = "nest" // recurse into a claim that is an map[string]any with it's own data fields -) - -type AuthorizedIntegrationUI string - -const ( - // Generic UI which allows the user to view and edit claim rules directly to support integrations that Forgejo - // doesn't have a user-friendly UI to support. - AuthorizedIntegrationUIGeneric AuthorizedIntegrationUI = "generic" - - // UI specific to Actions that are running on this local Forgejo instance accessing itself. - AuthorizedIntegrationUIForgejoActionsLocal AuthorizedIntegrationUI = "forgejo-actions-local" -) - -func GetAuthorizedIntegration(ctx context.Context, issuer, audience string) (*AuthorizedIntegration, error) { - var ai AuthorizedIntegration - found, err := db.GetEngine(ctx).Where("issuer = ? AND audience = ?", issuer, audience).Get(&ai) - if err != nil { - return nil, err - } else if !found { - return nil, util.ErrNotExist - } - return &ai, nil -} - -func GetAuthorizedIntegrationByUI(ctx context.Context, ownerID int64, aiUI AuthorizedIntegrationUI, aiID int64) (*AuthorizedIntegration, error) { - var ai AuthorizedIntegration - found, err := db.GetEngine(ctx).Where("id = ? AND user_id = ? AND ui = ?", aiID, ownerID, aiUI).Get(&ai) - if err != nil { - return nil, err - } else if !found { - return nil, util.ErrNotExist - } - return &ai, nil -} - -func InsertAuthorizedIntegration(ctx context.Context, ai *AuthorizedIntegration) error { - if ai.Audience != "" { - return errors.New("audience cannot be provided, and must be generated by NewAuthorizedIntegration") - } else if err := ai.generateAudience(); err != nil { - return err - } - _, err := db.GetEngine(ctx).Insert(ai) - return err -} - -// Bump the UpdatedUnix field of this authorized integration to now, tracking when it was last used for authentication. -// To reduce database write workload, this is only tracked by one-minute intervals -- the UPDATE statement conditionally -// avoids writes. -func (ai *AuthorizedIntegration) UpdateLastUsed(ctx context.Context) error { - newTime := timeutil.TimeStampNow() - cnt, err := db.GetEngine(ctx). - Table(&AuthorizedIntegration{}). - Where(builder.Eq{"id": ai.ID}). - Where(builder.Lt{"updated_unix": newTime.AddDuration(-1 * time.Minute)}). - NoAutoTime(). - Update(map[string]any{"updated_unix": newTime}) - if cnt == 1 { - ai.UpdatedUnix = newTime - } - return err -} - -// Generates the `aud` claim that the remote JWT generator must use to match this authorized integration. The `aud` -// claim is an arbitrary value in a JWT claim, but Forgejo is faced with a few hard and soft requirements: -// -// - Hard requirement: each authorized integration must have a unique `aud`, as it is used to find the DB record that -// authenticates a request. -// - If authentication is failing, being able to inspect the `aud` claim can be useful to identify the intent. -// - Inspection should have a stable meaning -- eg. if it included the username, and the user was renamed, the `aud` -// value which can't be changed would continue to reference the old username causing confusion when inspecting it. -// - Forgejo & GitHub Actions uses a URL $ACTIONS_ID_TOKEN_REQUEST_URL&audience=... to generate a JWT for the running -// action, so it should only consist of safe characters for URL encoding. -// - It should be relatively short, as it's encoded into the JWT and increases its size. -// -// Meeting these requirements decently well is a combination of the owner's ID, a guid, and a "u:" prefix that makes the -// fact that it's an `aud` claim value a little bit identifiable. -func (ai *AuthorizedIntegration) generateAudience() error { - if ai.UserID == 0 { - return errors.New("UserID must be initialized") - } - ai.Audience = fmt.Sprintf("u:%d:%s", ai.UserID, gouuid.New().String()) - return nil -} - -func (ai *AuthorizedIntegration) HasRecentActivity() bool { - return ai.HasBeenUsed() && ai.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow() -} - -func (ai *AuthorizedIntegration) HasBeenUsed() bool { - return ai.UpdatedUnix > ai.CreatedUnix -} - -type ListAuthorizedIntegrationOptions struct { - db.ListOptions - UserID optional.Option[int64] -} - -func (opts ListAuthorizedIntegrationOptions) ToConds() builder.Cond { - cond := builder.NewCond() - if has, userID := opts.UserID.Get(); has { - cond = cond.And(builder.Eq{"user_id": userID}) - } - return cond -} - -func (opts ListAuthorizedIntegrationOptions) ToOrders() string { - return "created_unix DESC" -} - -func ParseAuthorizedIntegrationUI(ui string) (AuthorizedIntegrationUI, error) { - switch ui { - case string(AuthorizedIntegrationUIGeneric): - return AuthorizedIntegrationUIGeneric, nil - case string(AuthorizedIntegrationUIForgejoActionsLocal): - return AuthorizedIntegrationUIForgejoActionsLocal, nil - } - return AuthorizedIntegrationUI(""), fmt.Errorf("invalid authorized integration UI: %q", ui) -} diff --git a/models/auth/authorized_integration_resource_repo.go b/models/auth/authorized_integration_resource_repo.go deleted file mode 100644 index 77445b60ae..0000000000 --- a/models/auth/authorized_integration_resource_repo.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package auth - -import ( - "context" - - "forgejo.org/models/db" - "forgejo.org/modules/timeutil" -) - -// Represents a many-to-many join table which indicates specific repositories (RepoID) that can be accessed by an -// authorized integration (IntegID). An authorized integrations's ResourceAllRepos field must be false for records in -// this table to become active. -// -// Model name is shortend (from AuthorizedIntegrationResourceRepo) to accomodate recreate-tables + MySQL, where the -// "tmp_recreate_" + foreign key index name would exceed the max identifier length. -type AuthorizedIntegResourceRepo struct { - ID int64 `xorm:"pk autoincr"` - IntegID int64 `xorm:"NOT NULL REFERENCES(authorized_integration, id)"` // field name shortened (AuthorizationIntegrationID) for max identifier length - RepoID int64 `xorm:"NOT NULL REFERENCES(repository, id)"` - - CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` -} - -func init() { - db.RegisterModel(new(AuthorizedIntegResourceRepo)) -} - -func (air *AuthorizedIntegResourceRepo) GetTargetRepoID() int64 { - return air.RepoID -} - -func GetRepositoriesAccessibleWithIntegration(ctx context.Context, aiID int64) ([]*AuthorizedIntegResourceRepo, error) { - var resources []*AuthorizedIntegResourceRepo - err := db.GetEngine(ctx). - Where("integ_id = ?", aiID). - Find(&resources) - if err != nil { - return nil, err - } - return resources, nil -} - -func InsertAuthorizedIntegrationResourceRepos(ctx context.Context, aiID int64, resources []*AuthorizedIntegResourceRepo) error { - return db.WithTx(ctx, func(ctx context.Context) error { - for _, resourceRepo := range resources { - resourceRepo.IntegID = aiID - if err := db.Insert(ctx, resourceRepo); err != nil { - return err - } - } - return nil - }) -} diff --git a/models/auth/authorized_integration_resource_repo_test.go b/models/auth/authorized_integration_resource_repo_test.go deleted file mode 100644 index 15c5b4854f..0000000000 --- a/models/auth/authorized_integration_resource_repo_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package auth_test - -import ( - "testing" - - auth_model "forgejo.org/models/auth" - "forgejo.org/models/unittest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetRepositoriesAccessibleWithIntegration(t *testing.T) { - defer unittest.OverrideFixtures("models/auth/TestGetRepositoriesAccessibleWithIntegration")() - require.NoError(t, unittest.PrepareTestDatabase()) - - t.Run("No Resources", func(t *testing.T) { - resources, err := auth_model.GetRepositoriesAccessibleWithIntegration(t.Context(), 999) - require.NoError(t, err) - assert.Empty(t, resources) - }) - - t.Run("Has Resources", func(t *testing.T) { - resources, err := auth_model.GetRepositoriesAccessibleWithIntegration(t.Context(), 1) - require.NoError(t, err) - require.Len(t, resources, 3) - - // Verify all expected repo IDs are present - repoIDs := make([]int64, len(resources)) - for i, res := range resources { - repoIDs[i] = res.RepoID - } - assert.Contains(t, repoIDs, int64(1)) - assert.Contains(t, repoIDs, int64(2)) - assert.Contains(t, repoIDs, int64(3)) - }) -} - -func TestInsertAuthorizedIntegration(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - ai1 := makeAuthorizedIntegration(t) - ai2 := makeAuthorizedIntegration(t) - ai3 := makeAuthorizedIntegration(t) - - t.Run("blank insert", func(t *testing.T) { - err := auth_model.InsertAuthorizedIntegrationResourceRepos(t.Context(), ai1.ID, nil) - require.NoError(t, err) - }) - - t.Run("multiple insert", func(t *testing.T) { - resRepo1 := &auth_model.AuthorizedIntegResourceRepo{ - IntegID: ai2.ID, - RepoID: 1, - } - resRepo3 := &auth_model.AuthorizedIntegResourceRepo{ - IntegID: ai2.ID, - RepoID: 3, - } - err := auth_model.InsertAuthorizedIntegrationResourceRepos(t.Context(), ai2.ID, - []*auth_model.AuthorizedIntegResourceRepo{resRepo1, resRepo3}) - require.NoError(t, err) - - unittest.AssertCount(t, &auth_model.AuthorizedIntegResourceRepo{IntegID: ai2.ID}, 2) - }) - - t.Run("in tx", func(t *testing.T) { - // Pre-condition: count is 0. - unittest.AssertCount(t, &auth_model.AuthorizedIntegResourceRepo{IntegID: ai3.ID}, 0) - - // Verify that InsertAuthorizedIntegrationResourceRepos performs inserts in a TX by having a second one with an invalid - // RepoID, causing a foreign key violation - resRepo1 := &auth_model.AuthorizedIntegResourceRepo{ - IntegID: ai3.ID, - RepoID: 1, - } - resRepo3 := &auth_model.AuthorizedIntegResourceRepo{ - IntegID: ai3.ID, - RepoID: 30000, // invalid - } - err := auth_model.InsertAuthorizedIntegrationResourceRepos(t.Context(), ai3.ID, - []*auth_model.AuthorizedIntegResourceRepo{resRepo1, resRepo3}) - require.ErrorContains(t, err, "foreign key") - - // Count remains 0; the first record was not inserted. - unittest.AssertCount(t, &auth_model.AuthorizedIntegResourceRepo{IntegID: ai3.ID}, 0) - }) -} diff --git a/models/auth/authorized_integration_test.go b/models/auth/authorized_integration_test.go deleted file mode 100644 index 447640b38c..0000000000 --- a/models/auth/authorized_integration_test.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package auth_test - -import ( - "testing" - "time" - - auth_model "forgejo.org/models/auth" - "forgejo.org/models/db" - "forgejo.org/models/unittest" - "forgejo.org/modules/optional" - "forgejo.org/modules/timeutil" - "forgejo.org/modules/util" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func makeAuthorizedIntegration(t *testing.T) *auth_model.AuthorizedIntegration { - t.Helper() - ai := &auth_model.AuthorizedIntegration{ - UserID: 2, - Scope: auth_model.AccessTokenScopeAll, - ResourceAllRepos: true, - Issuer: "https://example.org/", - ClaimRules: &auth_model.ClaimRules{}, - } - require.NoError(t, auth_model.InsertAuthorizedIntegration(t.Context(), ai)) - return ai -} - -func TestGetAuthorizedIntegration(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - ai := makeAuthorizedIntegration(t) - - get, err := auth_model.GetAuthorizedIntegration(t.Context(), "abc", "123") - require.ErrorIs(t, err, util.ErrNotExist) - assert.Nil(t, get) - - get, err = auth_model.GetAuthorizedIntegration(t.Context(), ai.Issuer, ai.Audience) - require.NoError(t, err) - require.NotNil(t, get) - assert.Equal(t, ai.ID, get.ID) -} - -func TestGetAuthorizedIntegrationByUI(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - ai := makeAuthorizedIntegration(t) - - get, err := auth_model.GetAuthorizedIntegrationByUI(t.Context(), 3, ai.UI, ai.ID) - require.ErrorIs(t, err, util.ErrNotExist) - assert.Nil(t, get) - - get, err = auth_model.GetAuthorizedIntegrationByUI(t.Context(), ai.UserID, auth_model.AuthorizedIntegrationUI("incorrect"), ai.ID) - require.ErrorIs(t, err, util.ErrNotExist) - assert.Nil(t, get) - - get, err = auth_model.GetAuthorizedIntegrationByUI(t.Context(), ai.UserID, ai.UI, -1) - require.ErrorIs(t, err, util.ErrNotExist) - assert.Nil(t, get) - - get, err = auth_model.GetAuthorizedIntegrationByUI(t.Context(), ai.UserID, ai.UI, ai.ID) - require.NoError(t, err) - require.NotNil(t, get) - assert.Equal(t, ai.ID, get.ID) -} - -func TestAuthorizedIntegrationUpdateLastUsed(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - ai := makeAuthorizedIntegration(t) - ai.UpdatedUnix = 0 - cnt, err := db.GetEngine(t.Context()).ID(ai.ID).Cols("updated_unix").NoAutoTime().Update(ai) - require.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - timeutil.MockSet(time.Unix(1777130023, 0)) - defer timeutil.MockUnset() - - assert.EqualValues(t, 0, ai.UpdatedUnix) - require.NoError(t, ai.UpdateLastUsed(t.Context())) - assert.EqualValues(t, 1777130023, ai.UpdatedUnix) // object field updated - assert.EqualValues(t, 1777130023, unittest.AssertExistsAndLoadBean(t, &auth_model.AuthorizedIntegration{ID: ai.ID}).UpdatedUnix) - - // nearly immediate redo should have same timestamp due to the 1 minute deduplication: - timeutil.MockSet(time.Unix(1777130025, 0)) - require.NoError(t, ai.UpdateLastUsed(t.Context())) - assert.EqualValues(t, 1777130023, ai.UpdatedUnix) // object field not updated - assert.EqualValues(t, 1777130023, unittest.AssertExistsAndLoadBean(t, &auth_model.AuthorizedIntegration{ID: ai.ID}).UpdatedUnix) // database field not updated - - // but if it's a little while later.. - timeutil.MockSet(time.Unix(1777131139, 0)) - require.NoError(t, ai.UpdateLastUsed(t.Context())) - assert.EqualValues(t, 1777131139, ai.UpdatedUnix) // object field updated - assert.EqualValues(t, 1777131139, unittest.AssertExistsAndLoadBean(t, &auth_model.AuthorizedIntegration{ID: ai.ID}).UpdatedUnix) // database field updated -} - -func TestNewAuthorizedIntegration(t *testing.T) { - ai := &auth_model.AuthorizedIntegration{ - UserID: 2, - Scope: auth_model.AccessTokenScopeAll, - ResourceAllRepos: true, - Issuer: "https://example.org/", - ClaimRules: &auth_model.ClaimRules{}, - } - require.NoError(t, auth_model.InsertAuthorizedIntegration(t.Context(), ai)) - assert.Contains(t, ai.Audience, "u:2:") - - ai = &auth_model.AuthorizedIntegration{ - UserID: 2, - Scope: auth_model.AccessTokenScopeAll, - ResourceAllRepos: true, - Issuer: "https://example.org/", - Audience: "I made my own audience", - ClaimRules: &auth_model.ClaimRules{}, - } - require.ErrorContains(t, auth_model.InsertAuthorizedIntegration(t.Context(), ai), "audience cannot be provided") - - ai = &auth_model.AuthorizedIntegration{ - // Forgot to set UserID - Scope: auth_model.AccessTokenScopeAll, - ResourceAllRepos: true, - Issuer: "https://example.org/", - ClaimRules: &auth_model.ClaimRules{}, - } - require.ErrorContains(t, auth_model.InsertAuthorizedIntegration(t.Context(), ai), "UserID must be initialized") -} - -func TestAuthorizedIntegrationCalculatedValues(t *testing.T) { - t.Run("HasRecentActivity", func(t *testing.T) { - timeutil.MockSet(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)) - ai := &auth_model.AuthorizedIntegration{ - UpdatedUnix: 1609459200, - CreatedUnix: 1609459200, - } - assert.False(t, ai.HasRecentActivity()) - ai.UpdatedUnix = 1609459201 - assert.True(t, ai.HasRecentActivity()) - ai.CreatedUnix = 1577836800 - ai.UpdatedUnix = 1577836801 - assert.False(t, ai.HasRecentActivity()) - }) - - t.Run("HasBeenUsed", func(t *testing.T) { - ai := &auth_model.AuthorizedIntegration{ - UpdatedUnix: 1, - CreatedUnix: 1, - } - assert.False(t, ai.HasBeenUsed()) - ai.UpdatedUnix = 2 - assert.True(t, ai.HasBeenUsed()) - }) -} - -func TestListAuthorizedIntegrationOptions(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - makeAuthorizedIntegration(t) - makeAuthorizedIntegration(t) - - ais, err := db.Find[auth_model.AuthorizedIntegration](t.Context(), - auth_model.ListAuthorizedIntegrationOptions{UserID: optional.None[int64]()}) - require.NoError(t, err) - assert.Len(t, ais, 2) - - ais, err = db.Find[auth_model.AuthorizedIntegration](t.Context(), - auth_model.ListAuthorizedIntegrationOptions{UserID: optional.Some(int64(2))}) - require.NoError(t, err) - assert.Len(t, ais, 2) - - ais, err = db.Find[auth_model.AuthorizedIntegration](t.Context(), - auth_model.ListAuthorizedIntegrationOptions{UserID: optional.Some(int64(22))}) - require.NoError(t, err) - assert.Empty(t, ais) -} diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 9ef89cb624..fa68197cf0 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -6,7 +6,6 @@ package auth import ( "context" "crypto/sha256" - "crypto/subtle" "encoding/base32" "encoding/base64" "errors" @@ -152,9 +151,9 @@ func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool { // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-12#section-3.1 contains := func(s string) bool { - s = strings.TrimSuffix(util.ToUpperASCII(s), "/") + s = strings.TrimSuffix(strings.ToLower(s), "/") for _, u := range app.RedirectURIs { - if strings.TrimSuffix(util.ToUpperASCII(u), "/") == s { + if strings.TrimSuffix(strings.ToLower(u), "/") == s { return true } } @@ -409,41 +408,26 @@ func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (*url.URL return redirect, err } -// Invalidate deletes the auth code from the database to invalidate this code. -// It returns an error if the code was already invalidated (i.e., no rows were deleted), -// which prevents authorization code replay attacks. +// Invalidate deletes the auth code from the database to invalidate this code func (code *OAuth2AuthorizationCode) Invalidate(ctx context.Context) error { - affected, err := db.GetEngine(ctx).ID(code.ID).NoAutoCondition().Delete(code) - if err != nil { - return err - } - if affected == 0 { - return fmt.Errorf("authorization code already used or does not exist") - } - return nil + _, err := db.GetEngine(ctx).ID(code.ID).NoAutoCondition().Delete(code) + return err } -// ValidateCodeChallenge validates the given verifier against the saved code challenge. This is part of the PKCE -// implementation. If a code challenge was set during authorization, a valid verifier MUST be provided. +// ValidateCodeChallenge validates the given verifier against the saved code challenge. This is part of the PKCE implementation. func (code *OAuth2AuthorizationCode) ValidateCodeChallenge(verifier string) bool { - // If no PKCE was used during authorization, no verifier is needed. - if code.CodeChallengeMethod == "" && code.CodeChallenge == "" { - return true - } - // A challenge was set but no verifier provided: reject outright, no comparison or hashing is required. - if verifier == "" { - return false - } switch code.CodeChallengeMethod { case "S256": // base64url(SHA256(verifier)) see https://tools.ietf.org/html/rfc7636#section-4.6 h := sha256.Sum256([]byte(verifier)) hashedVerifier := base64.RawURLEncoding.EncodeToString(h[:]) - return subtle.ConstantTimeCompare([]byte(hashedVerifier), []byte(code.CodeChallenge)) == 1 + return hashedVerifier == code.CodeChallenge case "plain": - return subtle.ConstantTimeCompare([]byte(verifier), []byte(code.CodeChallenge)) == 1 + return verifier == code.CodeChallenge + case "": + return true default: - // unsupported or empty method with a non-empty challenge -> reject + // unsupported method -> return false return false } } @@ -506,25 +490,22 @@ func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redi } // IncreaseCounter increases the counter and updates the grant -// IncreaseCounter atomically increments the counter only if it still matches -// the value loaded into this grant. Returns an error if the counter was already -// changed by a concurrent request (refresh token replay). func (grant *OAuth2Grant) IncreaseCounter(ctx context.Context) error { - affected, err := db.GetEngine(ctx).Where("id = ? AND counter = ?", grant.ID, grant.Counter). - Incr("counter").Update(new(OAuth2Grant)) + _, err := db.GetEngine(ctx).ID(grant.ID).Incr("counter").Update(new(OAuth2Grant)) if err != nil { return err } - if affected == 0 { - return fmt.Errorf("grant counter changed unexpectedly (possible replay)") + updatedGrant, err := GetOAuth2GrantByID(ctx, grant.ID) + if err != nil { + return err } - grant.Counter++ + grant.Counter = updatedGrant.Counter return nil } // ScopeContains returns true if the grant scope contains the specified scope func (grant *OAuth2Grant) ScopeContains(scope string) bool { - for currentScope := range strings.SplitSeq(grant.Scope, " ") { + for _, currentScope := range strings.Split(grant.Scope, " ") { if scope == currentScope { return true } diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go index aa474393c1..9c6836ed0d 100644 --- a/models/auth/oauth2_test.go +++ b/models/auth/oauth2_test.go @@ -75,13 +75,6 @@ func TestOAuth2Application_ContainsRedirect_Slash(t *testing.T) { assert.False(t, app.ContainsRedirectURI("http://127.0.0.1/other")) } -func TestOAuth2Application_ContainsRedirect_Normalization(t *testing.T) { - app := &auth_model.OAuth2Application{RedirectURIs: []string{"https://website.com"}} - assert.True(t, app.ContainsRedirectURI("https://website.com")) - assert.True(t, app.ContainsRedirectURI("https://webSITE.com")) // ascii uppercase I - assert.False(t, app.ContainsRedirectURI("https://websİte.com")) // U+0130 as I, Latin Capital Letter I with Dot Above -} - func TestOAuth2Application_ValidateClientSecret(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1}) @@ -154,18 +147,9 @@ func TestGetOAuth2GrantByID(t *testing.T) { func TestOAuth2Grant_IncreaseCounter(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 1}) - - // First increment succeeds require.NoError(t, grant.IncreaseCounter(db.DefaultContext)) assert.Equal(t, int64(2), grant.Counter) unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 2}) - - // Simulate a stale grant (counter still 1): must fail (concurrent replay) - grant.Counter = 1 - require.Error(t, grant.IncreaseCounter(db.DefaultContext), "stale counter must be rejected") - - // Counter in DB should be unchanged - unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 2}) } func TestOAuth2Grant_ScopeContains(t *testing.T) { @@ -248,33 +232,12 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) { } assert.False(t, code.ValidateCodeChallenge("foiwgjioriogeiogjerger")) - // test no PKCE at all (no challenge set, no verifier needed) - code = &auth_model.OAuth2AuthorizationCode{ - CodeChallengeMethod: "", - CodeChallenge: "", - } - assert.True(t, code.ValidateCodeChallenge("")) - - // test PKCE required: challenge was set but verifier is empty - code = &auth_model.OAuth2AuthorizationCode{ - CodeChallengeMethod: "S256", - CodeChallenge: "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg", - } - assert.False(t, code.ValidateCodeChallenge(""), "PKCE required: S256 challenge set but empty verifier must be rejected") - - code = &auth_model.OAuth2AuthorizationCode{ - CodeChallengeMethod: "plain", - CodeChallenge: "test123", - } - assert.False(t, code.ValidateCodeChallenge(""), "PKCE required: plain challenge set but empty verifier must be rejected") - - // test challenge stored but method empty (malformed: should reject) + // test no code challenge code = &auth_model.OAuth2AuthorizationCode{ CodeChallengeMethod: "", CodeChallenge: "foierjiogerogerg", } - assert.False(t, code.ValidateCodeChallenge(""), "challenge present with empty method must be rejected") - assert.False(t, code.ValidateCodeChallenge("foierjiogerogerg"), "challenge present with empty method must be rejected even with matching verifier") + assert.True(t, code.ValidateCodeChallenge("")) } func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) { @@ -299,20 +262,6 @@ func TestOAuth2AuthorizationCode_Invalidate(t *testing.T) { unittest.AssertNotExistsBean(t, &auth_model.OAuth2AuthorizationCode{Code: "authcode"}) } -func TestOAuth2AuthorizationCode_Invalidate_DoubleUse(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - code := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2AuthorizationCode{Code: "authcode"}) - - // First invalidation should succeed - require.NoError(t, code.Invalidate(db.DefaultContext)) - unittest.AssertNotExistsBean(t, &auth_model.OAuth2AuthorizationCode{Code: "authcode"}) - - // Second invalidation of the same code must fail (replay prevention) - err := code.Invalidate(db.DefaultContext) - require.Error(t, err) - assert.Contains(t, err.Error(), "authorization code already used") -} - func TestOAuth2AuthorizationCode_TableName(t *testing.T) { assert.Equal(t, "oauth2_authorization_code", new(auth_model.OAuth2AuthorizationCode).TableName()) } diff --git a/models/auth/source.go b/models/auth/source.go index fd016e02da..ecd3abc39d 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -91,7 +91,7 @@ var registeredConfigs = map[Type]func() Config{} // RegisterTypeConfig register a config for a provided type func RegisterTypeConfig(typ Type, exemplar Config) { - if reflect.TypeOf(exemplar).Kind() == reflect.Pointer { + if reflect.TypeOf(exemplar).Kind() == reflect.Ptr { // Pointer: registeredConfigs[typ] = func() Config { return reflect.New(reflect.ValueOf(exemplar).Elem().Type()).Interface().(Config) @@ -250,8 +250,8 @@ type FindSourcesOptions struct { func (opts FindSourcesOptions) ToConds() builder.Cond { conds := builder.NewCond() - if has, value := opts.IsActive.Get(); has { - conds = conds.And(builder.Eq{"is_active": value}) + if opts.IsActive.Has() { + conds = conds.And(builder.Eq{"is_active": opts.IsActive.Value()}) } if opts.LoginType != NoType { conds = conds.And(builder.Eq{"`type`": opts.LoginType}) diff --git a/models/avatars/avatar.go b/models/avatars/avatar.go index 43d52c562b..ad59bd8769 100644 --- a/models/avatars/avatar.go +++ b/models/avatars/avatar.go @@ -165,7 +165,10 @@ func GenerateUserAvatarFastLink(userName string, size int) string { } // GenerateUserAvatarImageLink returns a link for `User.Avatar` image file: "/avatars/${User.Avatar}" -func GenerateUserAvatarImageLink(userAvatar string) string { +func GenerateUserAvatarImageLink(userAvatar string, size int) string { + if size > 0 { + return setting.AppSubURL + "/avatars/" + url.PathEscape(userAvatar) + "?size=" + strconv.Itoa(size) + } return setting.AppSubURL + "/avatars/" + url.PathEscape(userAvatar) } @@ -207,6 +210,9 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final } // for non-final link, we should return fast (use a 302 redirection link) urlStr := setting.AppSubURL + "/avatar/" + url.PathEscape(emailHash) + if size > 0 { + urlStr += "?size=" + strconv.Itoa(size) + } return urlStr } diff --git a/models/db/context.go b/models/db/context.go index b51861d896..89b6c96555 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -6,7 +6,6 @@ package db import ( "context" "database/sql" - "errors" "fmt" "slices" @@ -313,49 +312,6 @@ func GetByIDs[Bean any, Id comparable](ctx context.Context, idField string, idLi return retval, nil } -// Retrieves multiple objects with database queries similar to an xorm `.In(field, valueList)`. Similar to GetByIDs, -// except that a map[Id][]*Bean is returned as the field value is not assumed to be a unique value -- if there are -// multiple rows in the table for each value, all of them are returned. -// -// The length of the IN list is constrained to DefaultMaxInSize for each database query, resulting in multiple database -// queries if the length of the idList exceeds that setting; this constraint prevents exceeding bind parameter -// limitations or query length limitations in the database engine. -func GetByFieldIn[Bean any, Id comparable](ctx context.Context, field string, valueList []Id, bean *Bean) (map[Id][]*Bean, error) { - retval := make(map[Id][]*Bean, len(valueList)) - if len(valueList) == 0 { - return retval, nil - } - - table, err := TableInfo(bean) - if err != nil { - return nil, fmt.Errorf("unable to fetch table info for bean %v: %w", bean, err) - } - - var structFieldName string - for _, c := range table.Columns() { - if c.Name == field { - structFieldName = c.FieldName - break - } - } - if structFieldName == "" { - return nil, fmt.Errorf("unable to identify struct field for field %s", field) - } - - for idChunk := range slices.Chunk(valueList, DefaultMaxInSize) { - beans := make([]*Bean, 0, len(idChunk)) - if err := GetEngine(ctx).In(field, idChunk).Find(&beans); err != nil { - return nil, err - } - for _, bean := range beans { - fieldValue := extractFieldValue(bean, structFieldName).(Id) - retval[fieldValue] = append(retval[fieldValue], bean) - } - } - - return retval, nil -} - func Exist[T any](ctx context.Context, cond builder.Cond) (bool, error) { if !cond.IsValid() { panic("cond is invalid in db.Exist(ctx, cond). This should not be possible.") @@ -504,68 +460,3 @@ func inTransaction(ctx context.Context) (*xorm.Session, bool) { return nil, false } } - -type RetryConfig struct { - ErrorIs []error - AttemptCount int -} - -var ErrNestedRetryTxFailure = errors.New("(nested)") - -type nestedRetryTxState int - -var nestedRetryTx nestedRetryTxState - -// Execute the given function in a transaction. RetryConfig will retry the function on an error, if it matches the -// ErrorIs parameter, up to the total of AttemptCount number of tries. RetryTx cannot be invoked when already within a -// transaction and will return an error immediately. -// -// ErrNestedRetryTxFailure is an error type that will occur when RetryTx is nested within each other, and indicates that -// an inner RetryTx encountered an error that matched its error list. -func RetryTx(ctx context.Context, config RetryConfig, f func(ctx context.Context) error) error { - matchError := func(err error) bool { - for _, possibleError := range config.ErrorIs { - if errors.Is(err, possibleError) { - return true - } - } - return false - } - - // Accept `ErrNestedRetryTxFailure` as error to retry on, means that a nested - // RetryTx indicated to retry the whole transaction. - config.ErrorIs = append(config.ErrorIs, ErrNestedRetryTxFailure) - - withinRetryTx, present := ctx.Value(nestedRetryTx).(bool) - if present && withinRetryTx { - // If a caller already started `RetryTx`, then we assume we don't have to actually perform retries here -- we - // can attempt the requested function once, and if an error is returned that matches the configured error list, - // we'll return that error + ErrNestedRetryTxFailure wrapping. - err := f(ctx) - if err == nil { - return nil - } else if matchError(err) { - return fmt.Errorf("nested RetryTx; internal Tx failed with error that won't be retried: %w %w", err, ErrNestedRetryTxFailure) - } - return err - } else if InTransaction(ctx) { - return errors.New("unsupported operation: attempted to use RetryTx while already within a transaction") - } else if config.AttemptCount == 0 { - return errors.New("unsupported operation: attempted to use RetryTx with 0 attempts") - } - - innerCtx := context.WithValue(ctx, nestedRetryTx, true) - var lastError error - for range config.AttemptCount { - err := WithTx(innerCtx, f) - if err == nil { - return nil - } else if !matchError(err) { - return err - } - - lastError = err - } - - return fmt.Errorf("retry tx failed after %d attempts; last error: %w", config.AttemptCount, lastError) -} diff --git a/models/db/context_test.go b/models/db/context_test.go index 59ef1e7754..525ab54d99 100644 --- a/models/db/context_test.go +++ b/models/db/context_test.go @@ -220,103 +220,3 @@ func TestAfterTx(t *testing.T) { }) } } - -func TestRetryTx(t *testing.T) { - t.Run("success", func(t *testing.T) { - err := db.RetryTx(t.Context(), db.RetryConfig{AttemptCount: 1}, func(ctx context.Context) error { - assert.True(t, db.InTransaction(ctx)) - return nil - }) - require.NoError(t, err) - }) - - t.Run("fail constantly", func(t *testing.T) { - attemptCount := 0 - testError := errors.New("hello") - err := db.RetryTx(t.Context(), db.RetryConfig{ - AttemptCount: 2, - ErrorIs: []error{testError}, - }, func(ctx context.Context) error { - attemptCount++ - return testError - }) - require.ErrorIs(t, err, testError) - require.ErrorContains(t, err, "2 attempts") - assert.Equal(t, 2, attemptCount) - }) - - t.Run("fail w/ non retriable error", func(t *testing.T) { - attemptCount := 0 - testError := errors.New("hello") - err := db.RetryTx(t.Context(), db.RetryConfig{ - AttemptCount: 2, - ErrorIs: []error{}, - }, func(ctx context.Context) error { - attemptCount++ - return testError - }) - require.ErrorIs(t, err, testError) - assert.Equal(t, 1, attemptCount) - }) - - t.Run("succeed on retry", func(t *testing.T) { - attemptCount := 0 - testError := errors.New("hello") - err := db.RetryTx(t.Context(), db.RetryConfig{ - AttemptCount: 2, - ErrorIs: []error{testError}, - }, func(ctx context.Context) error { - attemptCount++ - if attemptCount == 1 { - return testError - } - return nil - }) - require.NoError(t, err) - assert.Equal(t, 2, attemptCount) - }) - - t.Run("nested", func(t *testing.T) { - attemptCount := 0 - testError := errors.New("hello") - err := db.RetryTx(t.Context(), db.RetryConfig{ - AttemptCount: 2, - }, func(ctx context.Context) error { - attemptCount++ - return db.RetryTx(ctx, db.RetryConfig{ - AttemptCount: 2, - ErrorIs: []error{testError}, - }, func(ctx context.Context) error { - if attemptCount == 2 { - return nil - } - return testError - }) - }) - - require.NoError(t, err) - assert.Equal(t, 2, attemptCount) - }) - - t.Run("inner RetryTx decides on error", func(t *testing.T) { - attemptCount := 0 - testError := errors.New("hello") - err := db.RetryTx(t.Context(), db.RetryConfig{ - AttemptCount: 2, - ErrorIs: []error{}, - }, func(ctx context.Context) error { - attemptCount++ - return db.RetryTx(ctx, db.RetryConfig{ - AttemptCount: 2, - }, func(ctx context.Context) error { - if attemptCount == 2 { - return nil - } - return testError - }) - }) - - require.ErrorIs(t, err, testError) - assert.Equal(t, 1, attemptCount) - }) -} diff --git a/models/db/foreign_keys.go b/models/db/foreign_keys.go index 1bf022596d..7af0b92f73 100644 --- a/models/db/foreign_keys.go +++ b/models/db/foreign_keys.go @@ -256,13 +256,6 @@ func extendBeansForCascade(beans []any) ([]any, error) { if deduplicateTables.Contains(schema.Name) { continue } - - for _, column := range schema.Columns() { - if column.IsDeleted { - return nil, fmt.Errorf("unable to use table %q in a cascade operation, as it has a soft-delete column %q", schema.Name, column.FieldName) - } - } - deduplicateTables.Add(schema.Name) for _, referencingTable := range referencedTables[schema.Name] { table := tableMap[referencingTable] diff --git a/models/db/index.go b/models/db/index.go index 923576edb3..c069f0febd 100644 --- a/models/db/index.go +++ b/models/db/index.go @@ -20,8 +20,8 @@ type ResourceIndex struct { } var ( - // ErrResourceOutdated represents an error when request resource outdated - ErrResourceOutdated = errors.New("resource outdated") + // ErrResouceOutdated represents an error when request resource outdated + ErrResouceOutdated = errors.New("resource outdated") // ErrGetResourceIndexFailed represents an error when resource index retries 3 times ErrGetResourceIndexFailed = errors.New("get resource index failed") ) diff --git a/models/db/iterate.go b/models/db/iterate.go index c56871d503..d2315cb12c 100644 --- a/models/db/iterate.go +++ b/models/db/iterate.go @@ -80,7 +80,7 @@ func Iterate[Bean any](ctx context.Context, cond builder.Cond, f func(ctx contex func extractFieldValue(bean any, fieldName string) any { v := reflect.ValueOf(bean) - if v.Kind() == reflect.Pointer { + if v.Kind() == reflect.Ptr { v = v.Elem() } field := v.FieldByName(fieldName) diff --git a/models/db/list.go b/models/db/list.go index 71e9a0b1d2..057221936c 100644 --- a/models/db/list.go +++ b/models/db/list.go @@ -14,7 +14,7 @@ import ( const ( // DefaultMaxInSize represents default variables number on IN () in SQL - DefaultMaxInSize = 500 + DefaultMaxInSize = 50 defaultFindSliceSize = 10 ) diff --git a/models/db/name.go b/models/db/name.go index efd1c2b5f3..29b60b2373 100644 --- a/models/db/name.go +++ b/models/db/name.go @@ -6,7 +6,6 @@ package db import ( "fmt" "regexp" - "slices" "strings" "unicode/utf8" @@ -81,31 +80,6 @@ func (err ErrNameCharsNotAllowed) Unwrap() error { return util.ErrInvalidArgument } -// ErrNameActivityPubInvalid represents an error for usernames which cannot -// belong to ActivityPub accounts. -type ErrNameActivityPubInvalid struct { - Name string -} - -// Similarly to IsErrNameCharsNotAllowed, IsErrNameActivityPubInvalid checks if -// an error is an ErrNameActivityPubInvalid. -func IsErrNameActivityPubInvalid(err error) bool { - _, ok := err.(ErrNameActivityPubInvalid) - return ok -} - -func (err ErrNameActivityPubInvalid) Error() string { - return fmt.Sprintf( - "name is invalid [%s]: not acceptable for users from federated activitypub instances (e.g. @username@domain.example)", - err.Name, - ) -} - -// Unwrap unwraps this as a ErrInvalidArgument err -func (err ErrNameActivityPubInvalid) Unwrap() error { - return util.ErrInvalidArgument -} - // IsUsableName checks if name is reserved or pattern of name is not allowed // based on given reserved names and patterns. // Names are exact match, patterns can be prefix or suffix match with placeholder '*'. @@ -115,8 +89,10 @@ func IsUsableName(names, patterns []string, name string) error { return ErrNameEmpty } - if slices.Contains(names, name) { - return ErrNameReserved{name} + for i := range names { + if name == names[i] { + return ErrNameReserved{name} + } } for _, pat := range patterns { diff --git a/models/db/optional_test.go b/models/db/optional_test.go deleted file mode 100644 index fedf20161f..0000000000 --- a/models/db/optional_test.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package db_test - -import ( - "testing" - - "forgejo.org/models/db" - "forgejo.org/models/unittest" - "forgejo.org/modules/optional" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "xorm.io/xorm/schemas" -) - -func TestOptionFieldInt(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - type OptionInt struct { - ID int64 `xorm:"pk autoincr"` - Number optional.Option[int64] - } - db.GetEngine(t.Context()).Sync(&OptionInt{}) - - t.Run("insert null, read back", func(t *testing.T) { - null := OptionInt{ - Number: optional.None[int64](), - } - cnt, err := db.GetEngine(t.Context()).Insert(&null) - require.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - { - var read OptionInt - has, err := db.GetEngine(t.Context()).ID(null.ID).Get(&read) - require.NoError(t, err) - require.True(t, has) - hasNumber, _ := read.Number.Get() - assert.False(t, hasNumber) - } - }) - - t.Run("insert not null, read back", func(t *testing.T) { - notNull := OptionInt{ - Number: optional.Some[int64](123), - } - - cnt, err := db.GetEngine(t.Context()).Insert(¬Null) - require.NoError(t, err) - assert.EqualValues(t, 1, cnt) - { - var read OptionInt - has, err := db.GetEngine(t.Context()).ID(notNull.ID).Get(&read) - require.NoError(t, err) - require.True(t, has) - hasNumber, number := read.Number.Get() - assert.True(t, hasNumber) - assert.EqualValues(t, 123, number) - } - }) - - t.Run("read multiple records without filters", func(t *testing.T) { - var arr []OptionInt - err := db.GetEngine(t.Context()).Find(&arr) - require.NoError(t, err) - assert.Len(t, arr, 2) - }) - - t.Run("read multiple records with bean filters", func(t *testing.T) { - var arr []OptionInt - cond := &OptionInt{ - Number: optional.Some[int64](123), - } - err := db.GetEngine(t.Context()).Find(&arr, cond) - require.NoError(t, err) - require.Len(t, arr, 1) - v := arr[0] - has, value := v.Number.Get() - assert.True(t, has) - assert.EqualValues(t, 123, value) - }) -} - -func TestOptionFieldString(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - type OptionString struct { - ID int64 `xorm:"pk autoincr"` - String optional.Option[string] - } - assert.NoError(t, db.GetEngine(t.Context()).Sync(new(OptionString))) - - t.Run("insert null, read back", func(t *testing.T) { - null := OptionString{ - String: optional.None[string](), - } - cnt, err := db.GetEngine(t.Context()).Insert(&null) - require.NoError(t, err) - assert.EqualValues(t, 1, cnt) - - { - var read OptionString - has, err := db.GetEngine(t.Context()).ID(null.ID).Get(&read) - require.NoError(t, err) - require.True(t, has) - hasString, _ := read.String.Get() - assert.False(t, hasString) - } - }) - - t.Run("insert not null, read back", func(t *testing.T) { - notNull := OptionString{ - String: optional.Some("hello"), - } - - cnt, err := db.GetEngine(t.Context()).Insert(¬Null) - require.NoError(t, err) - assert.EqualValues(t, 1, cnt) - { - var read OptionString - has, err := db.GetEngine(t.Context()).ID(notNull.ID).Get(&read) - require.NoError(t, err) - require.True(t, has) - hasString, str := read.String.Get() - assert.True(t, hasString) - assert.Equal(t, "hello", str) - } - }) - - t.Run("read multiple records without filters", func(t *testing.T) { - var arr []OptionString - err := db.GetEngine(t.Context()).Find(&arr) - require.NoError(t, err) - assert.Len(t, arr, 2) - }) - - t.Run("read multiple records with bean filters", func(t *testing.T) { - var arr []OptionString - cond := &OptionString{ - String: optional.Some("hello"), - } - err := db.GetEngine(t.Context()).Find(&arr, cond) - require.NoError(t, err) - require.Len(t, arr, 1) - v := arr[0] - has, value := v.String.Get() - assert.True(t, has) - assert.Equal(t, "hello", value) - }) -} - -func TestOptionFieldIntrospection(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - type OptionIntrospectInt struct { - ID int64 `xorm:"pk autoincr"` - OptionNumber optional.Option[int64] - NormalNumber int64 - } - assert.NoError(t, db.GetEngine(t.Context()).Sync(new(OptionIntrospectInt))) - - schema, err := db.GetEngine(t.Context()).NoAutoTime().Engine().TableInfo(&OptionIntrospectInt{}) - require.NoError(t, err) - - var optionColumn, normalColumn *schemas.Column - for _, c := range schema.Columns() { - switch c.Name { - case "option_number": - optionColumn = c - case "normal_number": - normalColumn = c - } - } - require.NotNil(t, optionColumn) - require.NotNil(t, normalColumn) - - assert.Equal(t, normalColumn.TableName, optionColumn.TableName, "field TableName") - assert.Equal(t, normalColumn.SQLType, optionColumn.SQLType, "field SQLType") - assert.Equal(t, normalColumn.IsJSON, optionColumn.IsJSON, "field IsJSON") - assert.Equal(t, normalColumn.Length, optionColumn.Length, "field Length") - assert.Equal(t, normalColumn.Length2, optionColumn.Length2, "field Length2") - assert.Equal(t, normalColumn.Nullable, optionColumn.Nullable, "field Nullable") - assert.Equal(t, normalColumn.Default, optionColumn.Default, "field Default") - assert.Equal(t, normalColumn.Indexes, optionColumn.Indexes, "field Indexes") - assert.Equal(t, normalColumn.IsPrimaryKey, optionColumn.IsPrimaryKey, "field IsPrimaryKey") - assert.Equal(t, normalColumn.IsAutoIncrement, optionColumn.IsAutoIncrement, "field IsAutoIncrement") - assert.Equal(t, normalColumn.MapType, optionColumn.MapType, "field MapType") - assert.Equal(t, normalColumn.IsCreated, optionColumn.IsCreated, "field IsCreated") - assert.Equal(t, normalColumn.IsUpdated, optionColumn.IsUpdated, "field IsUpdated") - assert.Equal(t, normalColumn.IsDeleted, optionColumn.IsDeleted, "field IsDeleted") - assert.Equal(t, normalColumn.IsCascade, optionColumn.IsCascade, "field IsCascade") - assert.Equal(t, normalColumn.IsVersion, optionColumn.IsVersion, "field IsVersion") - assert.Equal(t, normalColumn.DefaultIsEmpty, optionColumn.DefaultIsEmpty, "field DefaultIsEmpty") - assert.Equal(t, normalColumn.EnumOptions, optionColumn.EnumOptions, "field EnumOptions") - assert.Equal(t, normalColumn.SetOptions, optionColumn.SetOptions, "field SetOptions") - assert.Equal(t, normalColumn.DisableTimeZone, optionColumn.DisableTimeZone, "field DisableTimeZone") - assert.Equal(t, normalColumn.TimeZone, optionColumn.TimeZone, "field TimeZone") - assert.Equal(t, normalColumn.Comment, optionColumn.Comment, "field Comment") - assert.Equal(t, normalColumn.Collation, optionColumn.Collation, "field Collation") -} diff --git a/models/dbfs/dbfile.go b/models/dbfs/dbfile.go index 7e7c58cc6c..8cd64177dd 100644 --- a/models/dbfs/dbfile.go +++ b/models/dbfs/dbfile.go @@ -46,7 +46,10 @@ func (f *file) readAt(fileMeta *DbfsMeta, offset int64, p []byte) (n int, err er blobPos := int(offset % f.blockSize) blobOffset := offset - int64(blobPos) blobRemaining := int(f.blockSize) - blobPos - needRead := min(len(p), blobRemaining) + needRead := len(p) + if needRead > blobRemaining { + needRead = blobRemaining + } if blobOffset+int64(blobPos)+int64(needRead) > fileMeta.FileSize { needRead = int(fileMeta.FileSize - blobOffset - int64(blobPos)) } @@ -63,8 +66,14 @@ func (f *file) readAt(fileMeta *DbfsMeta, offset int64, p []byte) (n int, err er blobData = nil } - canCopy := max(len(blobData)-blobPos, 0) - realRead := min(needRead, canCopy) + canCopy := len(blobData) - blobPos + if canCopy <= 0 { + canCopy = 0 + } + realRead := needRead + if realRead > canCopy { + realRead = canCopy + } if realRead > 0 { copy(p[:realRead], fileData.BlobData[blobPos:blobPos+realRead]) } @@ -104,7 +113,10 @@ func (f *file) Write(p []byte) (n int, err error) { blobPos := int(f.offset % f.blockSize) blobOffset := f.offset - int64(blobPos) blobRemaining := int(f.blockSize) - blobPos - needWrite := min(len(p), blobRemaining) + needWrite := len(p) + if needWrite > blobRemaining { + needWrite = blobRemaining + } buf := make([]byte, f.blockSize) readBytes, err := f.readAt(fileMeta, blobOffset, buf) if err != nil && !errors.Is(err, io.EOF) { diff --git a/models/fixtures/PrivateIssueProjects/project_board.yml b/models/fixtures/PrivateIssueProjects/project_board.yml index a1c257dacc..3f1fe1e705 100644 --- a/models/fixtures/PrivateIssueProjects/project_board.yml +++ b/models/fixtures/PrivateIssueProjects/project_board.yml @@ -4,7 +4,6 @@ title: Triage creator_id: 2 default: true - sorting: 0 created_unix: 1738000000 updated_unix: 1738000000 @@ -14,6 +13,5 @@ title: Triage creator_id: 2 default: true - sorting: 0 created_unix: 1738000000 updated_unix: 1738000000 diff --git a/models/fixtures/PrivateIssueProjects/project_issue.yml b/models/fixtures/PrivateIssueProjects/project_issue.yml index edbaa9640b..0245fb47f3 100644 --- a/models/fixtures/PrivateIssueProjects/project_issue.yml +++ b/models/fixtures/PrivateIssueProjects/project_issue.yml @@ -3,25 +3,21 @@ issue_id: 6 project_id: 1001 project_board_id: 1001 - sorting: 1 - id: 1002 issue_id: 7 project_id: 1002 project_board_id: 1002 - sorting: 0 - id: 1003 issue_id: 16 project_id: 1001 project_board_id: 1001 - sorting: 0 - id: 1004 issue_id: 1 project_id: 1002 project_board_id: 1002 - sorting: 1 diff --git a/models/fixtures/TestPrivateRepoProjects/project_board.yml b/models/fixtures/TestPrivateRepoProjects/project_board.yml index 68121387b9..9829cf7e27 100644 --- a/models/fixtures/TestPrivateRepoProjects/project_board.yml +++ b/models/fixtures/TestPrivateRepoProjects/project_board.yml @@ -4,6 +4,5 @@ title: Triage creator_id: 2 default: true - sorting: 0 created_unix: 1738000000 updated_unix: 1738000000 diff --git a/models/fixtures/TestPrivateRepoProjects/project_issue.yml b/models/fixtures/TestPrivateRepoProjects/project_issue.yml index aa3082af27..3e8c1dca9e 100644 --- a/models/fixtures/TestPrivateRepoProjects/project_issue.yml +++ b/models/fixtures/TestPrivateRepoProjects/project_issue.yml @@ -3,11 +3,9 @@ issue_id: 6 project_id: 1001 project_board_id: 1001 - sorting: 1 - id: 1002 issue_id: 15 project_id: 1001 project_board_id: 1001 - sorting: 0 diff --git a/models/fixtures/action.yml b/models/fixtures/action.yml index c776218be0..f1592d4569 100644 --- a/models/fixtures/action.yml +++ b/models/fixtures/action.yml @@ -81,4 +81,4 @@ act_user_id: 40 repo_id: 60 # public is_private: false - created_unix: 1577404800 # end of heatmap + created_unix: 1577404800 # end of heatmap \ No newline at end of file diff --git a/models/fixtures/action_run.yml b/models/fixtures/action_run.yml index e37fb2f955..41559ef788 100644 --- a/models/fixtures/action_run.yml +++ b/models/fixtures/action_run.yml @@ -2,7 +2,7 @@ id: 791 title: "update actions" repo_id: 4 - owner_id: 5 + owner_id: 1 workflow_id: "artifact.yaml" index: 187 trigger_user_id: 1 @@ -210,7 +210,7 @@ id: 792 title: "update actions" repo_id: 4 - owner_id: 5 + owner_id: 1 workflow_id: "artifact.yaml" index: 188 trigger_user_id: 1 @@ -417,7 +417,7 @@ id: 793 title: "job output" repo_id: 4 - owner_id: 5 + owner_id: 1 workflow_id: "test.yaml" index: 189 trigger_user_id: 1 @@ -436,7 +436,7 @@ id: 794 title: "job output" repo_id: 4 - owner_id: 5 + owner_id: 1 workflow_id: "test.yaml" index: 190 trigger_user_id: 1 @@ -455,7 +455,7 @@ id: 891 title: "update actions" repo_id: 1 - owner_id: 5 + owner_id: 1 workflow_id: "artifact.yaml" index: 187 trigger_user_id: 1 @@ -538,7 +538,7 @@ id: 895 title: "job output" repo_id: 4 - owner_id: 5 + owner_id: 1 workflow_id: "test.yaml" index: 191 trigger_user_id: 1 @@ -558,7 +558,7 @@ id: 896 title: "job output" repo_id: 4 - owner_id: 5 + owner_id: 1 workflow_id: "test.yaml" index: 192 trigger_user_id: 1 diff --git a/models/fixtures/action_run_job.yml b/models/fixtures/action_run_job.yml index a3afaa8461..2d8ea9ff6f 100644 --- a/models/fixtures/action_run_job.yml +++ b/models/fixtures/action_run_job.yml @@ -152,7 +152,6 @@ is_fork_pull_request: false name: job_2 attempt: 1 - handle: 18e9cf40-c2f6-409f-b832-b945ea7dc79b job_id: job_2 task_id: 47 status: 5 @@ -168,7 +167,6 @@ is_fork_pull_request: false name: job_2 attempt: 2 - handle: a723d3e3-49a1-4e6b-947f-e987e60bfbd6 job_id: job_2 task_id: 47 status: 5 @@ -184,7 +182,6 @@ is_fork_pull_request: false name: job_2 attempt: 1 - handle: 40317a2f-2f00-4a82-8cc4-57347989a493 job_id: job_2 task_id: 47 status: 5 diff --git a/models/fixtures/action_runner_token.yml b/models/fixtures/action_runner_token.yml index dffada23e2..8318349c9d 100644 --- a/models/fixtures/action_runner_token.yml +++ b/models/fixtures/action_runner_token.yml @@ -1,8 +1,8 @@ - id: 1 # instance scope token: xeiWBL5kuTYxGPynHCqQdoeYmJAeG3IzGXCYTrDX - owner_id: null - repo_id: null + owner_id: 0 + repo_id: 0 is_active: true created: 1695617748 updated: 1695617748 @@ -11,7 +11,7 @@ id: 2 # user scope and can't be used token: vohJB9QcZuSv1gAXESTk2uqpSjHhsKT9j4zYF84x owner_id: 1 - repo_id: null + repo_id: 0 is_active: false created: 1695617749 updated: 1695617749 @@ -20,7 +20,7 @@ id: 3 # user scope and can be used token: gjItAeJ3CA74hNPmPPo0Zco8I1eMaNcP1jVifjOE owner_id: 1 - repo_id: null + repo_id: 0 is_active: true created: 1695617750 updated: 1695617750 @@ -28,7 +28,7 @@ - id: 4 # repo scope token: NOjLubxzFxPGhPXflZknys0gjVvQNhomFbAYuhbH - owner_id: null + owner_id: 0 repo_id: 1 is_active: true created: 1695617751 diff --git a/models/fixtures/project_board.yml b/models/fixtures/project_board.yml index e205c01a21..3293dea6ed 100644 --- a/models/fixtures/project_board.yml +++ b/models/fixtures/project_board.yml @@ -4,7 +4,6 @@ title: To Do creator_id: 2 default: true - sorting: 0 created_unix: 1588117528 updated_unix: 1588117528 @@ -13,7 +12,6 @@ project_id: 1 title: In Progress creator_id: 2 - sorting: 1 created_unix: 1588117528 updated_unix: 1588117528 @@ -22,7 +20,6 @@ project_id: 1 title: Done creator_id: 2 - sorting: 2 created_unix: 1588117528 updated_unix: 1588117528 @@ -31,7 +28,6 @@ project_id: 4 title: Done creator_id: 2 - sorting: 0 created_unix: 1588117528 updated_unix: 1588117528 @@ -41,7 +37,6 @@ title: Backlog creator_id: 2 default: true - sorting: 0 created_unix: 1588117528 updated_unix: 1588117528 @@ -51,7 +46,6 @@ title: Backlog creator_id: 2 default: true - sorting: 1 created_unix: 1588117528 updated_unix: 1588117528 @@ -61,7 +55,6 @@ title: Done creator_id: 2 default: false - sorting: 0 created_unix: 1588117528 updated_unix: 1588117528 @@ -71,7 +64,6 @@ title: Backlog creator_id: 2 default: true - sorting: 0 created_unix: 1588117528 updated_unix: 1588117528 @@ -81,6 +73,5 @@ title: Uncategorized creator_id: 2 default: true - sorting: 1 created_unix: 1588117528 updated_unix: 1588117528 diff --git a/models/fixtures/project_issue.yml b/models/fixtures/project_issue.yml index 61ad79cd3e..b1af05908a 100644 --- a/models/fixtures/project_issue.yml +++ b/models/fixtures/project_issue.yml @@ -3,25 +3,21 @@ issue_id: 1 project_id: 1 project_board_id: 1 - sorting: 0 - id: 2 issue_id: 2 project_id: 1 project_board_id: 0 # no board assigned - sorting: 0 - id: 3 issue_id: 3 project_id: 1 project_board_id: 2 - sorting: 0 - id: 4 issue_id: 5 project_id: 1 project_board_id: 3 - sorting: 0 diff --git a/models/fixtures/public_key.yml b/models/fixtures/public_key.yml index a1c7f4feb6..ae620ee2d1 100644 --- a/models/fixtures/public_key.yml +++ b/models/fixtures/public_key.yml @@ -9,36 +9,3 @@ created_unix: 1559593109 updated_unix: 1565224552 login_source_id: 0 -- - id: 2 - owner_id: 5 - name: user5 - fingerprint: "" - content: "user5" - mode: 2 - type: 3 - created_unix: 1775805112 - updated_unix: 1775805112 - login_source_id: 0 -- - id: 3 - owner_id: 9 - name: user9@localhost - fingerprint: "SHA256:K3RfDvtQ/aYVzh6RfXGFxlffLLTRgksf9UQwTlwSM8M" - content: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDN7KuFUnlztx/UM6PUTyiBAq5SeIqr+qSVFC6JzLQAh user9@localhost" - mode: 2 - type: 1 - created_unix: 1775805112 - updated_unix: 1775805112 - login_source_id: 0 -- - id: 4 - owner_id: 9 - name: user9 - fingerprint: "" - content: "user9" - mode: 2 - type: 3 - created_unix: 1775805112 - updated_unix: 1775805112 - login_source_id: 0 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 17592ef330..4e957f30f3 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -1566,9 +1566,9 @@ - id: 42 - lower_name: "@federated@example.net" - name: "@federated@example.net" - full_name: "@federated@example.net" + 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 @@ -1576,7 +1576,7 @@ passwd_hash_algo: "" must_change_password: false login_source: 0 - login_name: "@federated@example.net" + login_name: federated-example.net type: 6 salt: "" max_repo_creation: -1 diff --git a/models/forgefed/error.go b/models/forgefed/error.go deleted file mode 100644 index f8b5ef852e..0000000000 --- a/models/forgefed/error.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgefed - -import ( - "fmt" -) - -type ErrFederationHostNotFound struct { - SearchKey string - SearchValue string -} - -func (err ErrFederationHostNotFound) Error() string { - return fmt.Sprintf("ErrFederationHostNotFound: search key: %s, search value: %s", err.SearchKey, err.SearchValue) -} - -func IsErrFederationHostNotFound(err error) bool { - _, ok := err.(ErrFederationHostNotFound) - return ok -} diff --git a/models/forgefed/federationhost_repository.go b/models/forgefed/federationhost_repository.go index 0b3329313e..a44b502ba1 100644 --- a/models/forgefed/federationhost_repository.go +++ b/models/forgefed/federationhost_repository.go @@ -16,31 +16,6 @@ func init() { db.RegisterModel(new(FederationHost)) } -func CountFederationHosts(ctx context.Context) (int64, error) { - return db.GetEngine(ctx).Count(FederationHost{}) -} - -func FindFederationHosts(ctx context.Context, opts db.ListOptions) (hosts []*FederationHost, err error) { - sess := db.GetEngine(ctx) - - if opts.PageSize > 0 { - sess = db.SetSessionPagination(sess, &opts) - } - - err = sess.Find(&hosts) - if err != nil { - return nil, err - } - - for _, host := range hosts { - if res, err := validation.IsValid(host); !res { - return nil, err - } - } - - return hosts, nil -} - func GetFederationHost(ctx context.Context, ID int64) (*FederationHost, error) { log.Trace("GetFederationHost: %v", ID) host := new(FederationHost) @@ -57,13 +32,13 @@ func GetFederationHost(ctx context.Context, ID int64) (*FederationHost, error) { return host, nil } -func findFederationHostFromDB(ctx context.Context, searchKey string, searchValue ...any) (*FederationHost, error) { +func findFederationHostFromDB(ctx context.Context, searchKey, searchValue string) (*FederationHost, error) { host := new(FederationHost) - has, err := db.GetEngine(ctx).Where(searchKey, searchValue...).Get(host) + has, err := db.GetEngine(ctx).Where(searchKey, searchValue).Get(host) if err != nil { return nil, err } else if !has { - return nil, ErrFederationHostNotFound{SearchKey: searchKey, SearchValue: fmt.Sprintf("%v", searchValue)} + return nil, nil } if res, err := validation.IsValid(host); !res { return nil, err @@ -72,7 +47,17 @@ func findFederationHostFromDB(ctx context.Context, searchKey string, searchValue } func FindFederationHostByFqdnAndPort(ctx context.Context, fqdn string, port uint16) (*FederationHost, error) { - return findFederationHostFromDB(ctx, "host_fqdn=? AND host_port=?", fqdn, port) + host := new(FederationHost) + has, err := db.GetEngine(ctx).Where("host_fqdn=? AND host_port=?", fqdn, port).Get(host) + if err != nil { + return nil, err + } else if !has { + return nil, nil + } + if res, err := validation.IsValid(host); !res { + return nil, err + } + return host, nil } func FindFederationHostByKeyID(ctx context.Context, keyID string) (*FederationHost, error) { diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index d64009db51..53ab95d216 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -153,7 +153,7 @@ func Migrate(x *xorm.Engine, freshDB bool) error { // Set a new clean the default mapper to GonicMapper as that is the default for . x.SetMapper(names.GonicMapper{}) - if _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(ForgejoMigration)); err != nil { + if err := x.Sync(new(ForgejoMigration)); err != nil { return fmt.Errorf("sync: %w", err) } diff --git a/models/forgejo_migrations/migrate_test.go b/models/forgejo_migrations/migrate_test.go index 87dea780d8..360f6a16d5 100644 --- a/models/forgejo_migrations/migrate_test.go +++ b/models/forgejo_migrations/migrate_test.go @@ -44,7 +44,7 @@ func TestRegisterMigration(t *testing.T) { "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 underscore + "v12a-neat-migration.go", // no undescore "v12a.go", // no descriptive identifier } { t.Run(fmt.Sprintf("bad name - %s", fn), func(t *testing.T) { diff --git a/models/forgejo_migrations/v14a_actions-approval-and-trust.go b/models/forgejo_migrations/v14a_actions-approval-and-trust.go index f8be109dda..657d512548 100644 --- a/models/forgejo_migrations/v14a_actions-approval-and-trust.go +++ b/models/forgejo_migrations/v14a_actions-approval-and-trust.go @@ -6,6 +6,7 @@ package forgejo_migrations import ( "context" + actions_model "forgejo.org/models/actions" "forgejo.org/models/db" "forgejo.org/modules/log" "forgejo.org/modules/timeutil" @@ -40,7 +41,7 @@ func v14ActionsApprovalAndTrustCreateTableActionUser(x *xorm.Engine) error { LastAccess timeutil.TimeStamp `xorm:"INDEX"` } - return x.Sync(new(ActionUser)) // nosemgrep:xorm-sync-missing-ignore-drop-indices + return x.Sync(new(ActionUser)) } func v14ActionsApprovalAndTrustAddActionsRunFields(x *xorm.Engine) error { @@ -58,18 +59,6 @@ type v14ActionsApprovalAndTrustTrusted struct { } func v14ActionsApprovalAndTrustPopulateTableActionUser(x *xorm.Engine) error { - type ActionUser struct { - ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"INDEX UNIQUE(action_user_index) REFERENCES(user, id)"` - RepoID int64 `xorm:"INDEX UNIQUE(action_user_index) REFERENCES(repository, id)"` - TrustedWithPullRequests bool - LastAccess timeutil.TimeStamp `xorm:"INDEX"` - } - insertActionUser := func(ctx context.Context, user *ActionUser) error { - user.LastAccess = timeutil.TimeStampNow() - return db.Insert(ctx, user) - } - // // Users approved once were trusted before and are trusted now. // @@ -98,7 +87,7 @@ func v14ActionsApprovalAndTrustPopulateTableActionUser(x *xorm.Engine) error { if err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { for _, trusted := range trustedList { log.Debug("v14a_actions-approval-and-trust: repository %d trusts user %d", trusted.RepoID, trusted.UserID) - if err := insertActionUser(ctx, &ActionUser{ + if err := actions_model.InsertActionUser(ctx, &actions_model.ActionUser{ RepoID: trusted.RepoID, UserID: trusted.UserID, TrustedWithPullRequests: true, diff --git a/models/forgejo_migrations/v14a_actions-approval-and-trust_test.go b/models/forgejo_migrations/v14a_actions-approval-and-trust_test.go index 8ff1b1c066..c639a0d2e9 100644 --- a/models/forgejo_migrations/v14a_actions-approval-and-trust_test.go +++ b/models/forgejo_migrations/v14a_actions-approval-and-trust_test.go @@ -7,8 +7,11 @@ import ( "testing" "time" + actions_model "forgejo.org/models/actions" "forgejo.org/models/db" migration_tests "forgejo.org/models/gitea_migrations/test" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" "forgejo.org/modules/timeutil" webhook_module "forgejo.org/modules/webhook" @@ -17,9 +20,6 @@ import ( ) func Test_v14ActionsApprovalAndTrustPopulateTableActionUser(t *testing.T) { - type ConcurrencyMode int - type Status int - type ActionUser struct { ID int64 `xorm:"pk autoincr"` UserID int64 `xorm:"INDEX UNIQUE(action_user_index) REFERENCES(user, id)"` @@ -32,18 +32,21 @@ func Test_v14ActionsApprovalAndTrustPopulateTableActionUser(t *testing.T) { type ActionRun struct { ID int64 Title string - RepoID int64 `xorm:"index unique(repo_index) index(concurrency)"` - OwnerID int64 `xorm:"index"` - WorkflowID string `xorm:"index"` // the name of workflow file - Index int64 `xorm:"index unique(repo_index)"` // a unique number for each run of a repository - TriggerUserID int64 `xorm:"index"` + RepoID int64 `xorm:"index unique(repo_index) index(concurrency)"` + Repo *repo_model.Repository `xorm:"-"` + OwnerID int64 `xorm:"index"` + WorkflowID string `xorm:"index"` // the name of workflow file + Index int64 `xorm:"index unique(repo_index)"` // a unique number for each run of a repository + TriggerUserID int64 `xorm:"index"` + TriggerUser *user_model.User `xorm:"-"` ScheduleID int64 Ref string `xorm:"index"` // the commit/tag/… that caused the run + IsRefDeleted bool `xorm:"-"` CommitSHA string Event webhook_module.HookEventType // the webhook event that causes the workflow to run EventPayload string `xorm:"LONGTEXT"` TriggerEvent string // the trigger event defined in the `on` configuration of the triggered workflow - Status Status `xorm:"index"` + Status actions_model.Status `xorm:"index"` Version int `xorm:"version default 0"` // Status could be updated concomitantly, so an optimistic lock is needed // Started and Stopped is used for recording last run time, if rerun happened, they will be reset to 0 Started timeutil.TimeStamp @@ -62,7 +65,7 @@ func Test_v14ActionsApprovalAndTrustPopulateTableActionUser(t *testing.T) { ApprovedBy int64 `xorm:"index"` ConcurrencyGroup string `xorm:"'concurrency_group' index(concurrency)"` - ConcurrencyType ConcurrencyMode + ConcurrencyType actions_model.ConcurrencyMode PreExecutionError string `xorm:"LONGTEXT"` // used to report errors that blocked execution of a workflow } @@ -80,10 +83,10 @@ func Test_v14ActionsApprovalAndTrustPopulateTableActionUser(t *testing.T) { require.NoError(t, v14ActionsApprovalAndTrustPopulateTableActionUser(x)) - var users []*ActionUser + var users []*actions_model.ActionUser require.NoError(t, db.GetEngine(t.Context()).Select("`repo_id`, `user_id`").OrderBy("`id`").Find(&users)) // See models/gitea_migrations/fixtures/Test_v14ActionsApprovalAndTrustPopulateTableActionUser/action_run.yml - assert.Equal(t, []*ActionUser{ + assert.Equal(t, []*actions_model.ActionUser{ { UserID: 3, RepoID: 15, diff --git a/models/forgejo_migrations/v14a_add-forgejo-migrations-table.go b/models/forgejo_migrations/v14a_add-forgejo-migrations-table.go index 8f22983e80..f78eec8789 100644 --- a/models/forgejo_migrations/v14a_add-forgejo-migrations-table.go +++ b/models/forgejo_migrations/v14a_add-forgejo-migrations-table.go @@ -21,5 +21,5 @@ func addForgejoMigration(x *xorm.Engine) error { ID string `xorm:"pk"` CreatedUnix timeutil.TimeStamp `xorm:"created"` } - return x.Sync(new(ForgejoMigration)) // nosemgrep:xorm-sync-missing-ignore-drop-indices + return x.Sync(new(ForgejoMigration)) } diff --git a/models/forgejo_migrations/v14a_ap-change-fedi-handle-structure.go b/models/forgejo_migrations/v14a_ap-change-fedi-handle-structure.go deleted file mode 100644 index a412ceb737..0000000000 --- a/models/forgejo_migrations/v14a_ap-change-fedi-handle-structure.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -// Due to a mistake during code review, this code was merged with the prefix 14a -// but this code was merged for the v15 cycle, the correct prefix would be 15a. -// As it would lead to breakage for instance who already ran with the old prefix -// the incorrect prefix is kept. - -package forgejo_migrations - -import ( - "context" - "database/sql" - "fmt" - "strings" - - "forgejo.org/models/db" - "forgejo.org/modules/log" - "forgejo.org/modules/timeutil" - "forgejo.org/modules/validation" - - "xorm.io/xorm" -) - -func init() { - registerMigration(&Migration{ - Description: "use structure @PreferredUsername@host.tld:port for actors", - Upgrade: changeActivityPubUsernameFormat, - }) -} - -func changeActivityPubUsernameFormat(x *xorm.Engine) error { - type FederationHost struct { - ID int64 `xorm:"pk autoincr"` - HostFqdn string `xorm:"host_fqdn UNIQUE(federation_host) INDEX VARCHAR(255) NOT NULL"` - HostPort uint16 `xorm:" UNIQUE(federation_host) INDEX NOT NULL DEFAULT 443"` - HostSchema string `xorm:"NOT NULL DEFAULT 'https'"` - Created timeutil.TimeStamp `xorm:"created"` - Updated timeutil.TimeStamp `xorm:"updated"` - } - type FederatedUser struct { - ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"NOT NULL INDEX user_id"` - ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` - FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` - KeyID sql.NullString `xorm:"key_id UNIQUE"` - PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"` - InboxPath string - NormalizedOriginalURL string // This field is just to keep original information. Pls. do not use for search or as ID! - } - type User struct { - ID int64 `xorm:"pk autoincr"` - LowerName string `xorm:"UNIQUE NOT NULL"` - Name string `xorm:"UNIQUE NOT NULL"` - CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` - UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` - } - deleteFederatedUser := func(ctx context.Context, userID int64) error { - _, err := db.GetEngine(ctx).Delete(&FederatedUser{UserID: userID}) - return err - } - userLogString := func(u *User) string { - if u == nil { - return "" - } - return fmt.Sprintf("", u.ID, u.Name) - } - - // Normally, the db.WithTx statement ensures that the database transaction (aka. all changes made - // by this migration) will only be committed if the SQL operations inside of the iteration - // (db.Iterate) don't return an error. - // - // This migration was originally authored with those cases in mind, but it was later agreed that - // migrations concerning Forgejo's federation-related components should not return any errors at - // this point in time, as federation is not considered to be stable at the moment. For more - // information, check the relevant discussion here: - // https://codeberg.org/forgejo-contrib/federation/issues/67 - // - // Nevertheless, this structure involves some useful boilerplate that can be used for future - // migrations at a later point and has been kept as-is. - return db.WithTx(db.DefaultContext, func(ctx context.Context) error { - // The transaction is committed only if modifying all federated users is possible. - return db.Iterate(ctx, nil, func(ctx context.Context, federatedUser *FederatedUser) error { - // localUser represents the "local" representation of an ActivityPub (federated) user - localUser := &User{} - has, err := db.GetEngine(ctx).ID(federatedUser.UserID).Get(localUser) - if err != nil { - log.Warn("Migration[v14a_ap-change-fedi-handle-structure]: Database error occurred while getting local user (ID: %d), ignoring...: %e", federatedUser.UserID, err) - return nil - } - - if !has { - log.Warn("Migration[v14a_ap-change-fedi-handle-structure]: User missing for federated user: %v", federatedUser) - err := deleteFederatedUser(ctx, federatedUser.UserID) - if err != nil { - log.Warn("Migration[v14a_ap-change-fedi-handle-structure]: Database error occurred while deleting federated user (%s), ignoring...: %e", federatedUser, err) - return nil - } - } - - if validation.IsValidActivityPubUsername(localUser.Name) { - log.Warn("Migration[v14a_ap-change-fedi-handle-structure]: FederatedUser was already migrated: %v", federatedUser) - } else { - // Copied from models/forgefed/federationhost_repository.go (forgefed.GetFederationHost), - // minus some validation code for FederationHost which we do not otherwise manipulate here. - federationHost := new(FederationHost) - has, err := db.GetEngine(ctx).ID(federatedUser.FederationHostID).Get(federationHost) - if err != nil { - log.Warn("Migration[v14a_ap-change-fedi-handle-structure]: Database error occurred while looking up federation host info (for %v), ignoring...: %e", federatedUser, err) - return nil - } else if !has { - log.Warn("Migration[v14a_ap-change-fedi-handle-structure]: Federation host for federated user %s is missing", federatedUser) - return nil - } - - // Take part of the username before the first dash, reconstruct the rest - // of it using whatever we have in FederationHost. Before this migration, - // usernames of ActivityPub accounts have an expected format of - // "username-subdomain-domain-tld-port". We don't know how many subdomains - // there are, but that doesn't matter. We can always get the username unless - // if the username of an ActivityPub account was manually changed by an admin, - // in which case they should either delete the account or change it back. - s := strings.Split(localUser.Name, "-") - if len(s) == 0 { - log.Warn( - "Migration[v14a_ap-change-fedi-handle-structure]: Username %s belonging to federatedUser %v does not contain any dashes, can't construct new username", - localUser.Name, - federatedUser, - ) - return nil - } - - // Were a running Forgejo instance to create a new federated account, would the port - // have been marked as "supplemented" (thus leading to its omission)? - var newUsername string - if (federationHost.HostPort == 443 && federationHost.HostSchema == "https") || (federationHost.HostPort == 80 && federationHost.HostSchema == "http") { - newUsername = fmt.Sprintf("@%s@%s", s[0], federationHost.HostFqdn) - } else { - newUsername = fmt.Sprintf("@%s@%s:%d", s[0], federationHost.HostFqdn, federationHost.HostPort) - } - - // Implicitly assumes that there won't be a lower name unique constraint violation. - // Potentially a bit paranoid, but why not? - userThatShouldntExist := &User{} - lowernameTaken, err := db.GetEngine(ctx).Where("lower_name = ?", strings.ToLower(newUsername)).Table("user").Get(userThatShouldntExist) - if err != nil { - log.Warn("Migration[v14a_ap-change-fedi-handle-structure]: Database error occurred, skipping migration of %s: %e", userLogString(localUser), err) - return nil - } - - if lowernameTaken { - log.Warn( - "Migration[v14a_ap-change-fedi-handle-structure]: New username %s for %s already taken by %s, deleting the former...", - newUsername, - userLogString(localUser), - userLogString(userThatShouldntExist), - ) - err := deleteFederatedUser(ctx, localUser.ID) - if err != nil { - log.Warn("Migration[v14a_ap-change-fedi-handle-structure]: Database error occurred while deleting federated user (%s), ignoring...: %e", userLogString(localUser), err) - } - return nil - } - - // Safe to assume that the following operations should just work now. - log.Info("Migration[v14a_ap-change-fedi-handle-structure]: Updating username of %s to %s", userLogString(localUser), newUsername) - if _, err := db.GetEngine(ctx).ID(localUser.ID).Cols("lower_name", "name").Update(&User{ - LowerName: strings.ToLower(newUsername), - Name: newUsername, - }); err != nil { - log.Warn("Migration[v14a_ap-change-fedi-handle-structure]: Database error occurred when updating federated user's username (%s), ignoring...: %e", userLogString(localUser), err) - return nil - } - } - - return nil - }) - }) -} diff --git a/models/forgejo_migrations/v14a_migrate_task_secrets.go b/models/forgejo_migrations/v14a_migrate_task_secrets.go index 3484a024b2..a177dff92a 100644 --- a/models/forgejo_migrations/v14a_migrate_task_secrets.go +++ b/models/forgejo_migrations/v14a_migrate_task_secrets.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "fmt" + admin_model "forgejo.org/models/admin" "forgejo.org/models/db" "forgejo.org/modules/json" "forgejo.org/modules/keying" @@ -16,7 +17,6 @@ import ( "forgejo.org/modules/secret" "forgejo.org/modules/setting" "forgejo.org/modules/structs" - "forgejo.org/modules/timeutil" "xorm.io/builder" "xorm.io/xorm" @@ -30,19 +30,6 @@ func init() { } func migrateTaskSecrets(x *xorm.Engine) error { - type Task struct { - ID int64 - DoerID int64 `xorm:"index"` - OwnerID int64 `xorm:"index"` - RepoID int64 `xorm:"index"` - PayloadContent string `xorm:"TEXT"` - Created timeutil.TimeStamp `xorm:"created"` - } - taskUpdateCols := func(ctx context.Context, task *Task, cols ...string) error { - _, err := db.GetEngine(ctx).ID(task.ID).Cols(cols...).Update(task) - return err - } - return db.WithTx(db.DefaultContext, func(ctx context.Context) error { sess := db.GetEngine(ctx) @@ -52,7 +39,7 @@ func migrateTaskSecrets(x *xorm.Engine) error { messages := make([]string, 0, 100) ids := make([]int64, 0, 100) - err := db.Iterate(ctx, builder.Eq{"type": structs.TaskTypeMigrateRepo}, func(ctx context.Context, bean *Task) error { + err := db.Iterate(ctx, builder.Eq{"type": structs.TaskTypeMigrateRepo}, func(ctx context.Context, bean *admin_model.Task) error { var opts migration.MigrateOptions err := json.Unmarshal([]byte(bean.PayloadContent), &opts) if err != nil { @@ -109,7 +96,7 @@ func migrateTaskSecrets(x *xorm.Engine) error { } bean.PayloadContent = string(bs) - return taskUpdateCols(ctx, bean, "payload_content") + return bean.UpdateCols(ctx, "payload_content") }) if err == nil { @@ -119,7 +106,7 @@ func migrateTaskSecrets(x *xorm.Engine) error { log.Error("v14a_migrate_task_secrets: %s", message) } - _, err = sess.In("id", ids).NoAutoCondition().NoAutoTime().Delete(&Task{}) + _, err = sess.In("id", ids).NoAutoCondition().NoAutoTime().Delete(&admin_model.Task{}) } } return err diff --git a/models/forgejo_migrations/v14a_migrate_webhook_authorization.go b/models/forgejo_migrations/v14a_migrate_webhook_authorization.go deleted file mode 100644 index 5921329b3e..0000000000 --- a/models/forgejo_migrations/v14a_migrate_webhook_authorization.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "context" - "fmt" - - "forgejo.org/models/db" - "forgejo.org/modules/keying" - "forgejo.org/modules/log" - "forgejo.org/modules/secret" - "forgejo.org/modules/setting" - "forgejo.org/modules/timeutil" - - "xorm.io/xorm" - "xorm.io/xorm/schemas" -) - -func init() { - registerMigration(&Migration{ - Description: "migrate `header_authorization_encrypted` of `webhook` table to store keying material", - Upgrade: migrateWebhookSecrets, - }) -} - -func migrateWebhookSecrets(x *xorm.Engine) error { - type Webhook struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX"` // An ID of 0 indicates either a default or system webhook - OwnerID int64 `xorm:"INDEX"` - HeaderAuthorizationEncrypted []byte `xorm:"BLOB"` - - CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` - UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` - } - - 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 `webhook` MODIFY `header_authorization_encrypted` BLOB"); err != nil { - return err - } - case schemas.SQLITE: - if _, err := sess.Exec("ALTER TABLE `webhook` RENAME COLUMN `header_authorization_encrypted` TO `header_authorization_encrypted_backup`"); err != nil { - return err - } - if _, err := sess.Exec("ALTER TABLE `webhook` ADD COLUMN `header_authorization_encrypted` BLOB"); err != nil { - return err - } - if _, err := sess.Exec("UPDATE `webhook` SET `header_authorization_encrypted` = `header_authorization_encrypted_backup`"); err != nil { - return err - } - if _, err := sess.Exec("ALTER TABLE `webhook` DROP COLUMN `header_authorization_encrypted_backup`"); err != nil { - return err - } - case schemas.POSTGRES: - if _, err := sess.Exec("ALTER TABLE `webhook` ALTER COLUMN `header_authorization_encrypted` SET DATA TYPE bytea USING header_authorization_encrypted::text::bytea"); err != nil { - return err - } - } - - key := keying.Webhook - - oldEncryptionKey := setting.SecretKey - messages := make([]string, 0, 100) - ids := make([]int64, 0, 100) - - err := db.Iterate(ctx, nil, func(ctx context.Context, bean *Webhook) error { - if len(bean.HeaderAuthorizationEncrypted) == 0 { - return nil - } - - secretBytes, err := secret.DecryptSecret(oldEncryptionKey, string(bean.HeaderAuthorizationEncrypted)) - if err != nil { - messages = append(messages, fmt.Sprintf("webhook.id=%d, webhook.repo_id=%d, webhook.owner_id=%d: secret.DecryptSecret(): %v", bean.ID, bean.RepoID, bean.OwnerID, err)) - ids = append(ids, bean.ID) - return nil - } - - bean.HeaderAuthorizationEncrypted = key.Encrypt([]byte(secretBytes), keying.ColumnAndID("header_authorization_encrypted", bean.ID)) - _, err = sess.Cols("header_authorization_encrypted").ID(bean.ID).Update(bean) - return err - }) - - if err == nil { - if len(ids) > 0 { - log.Error("migration[v14a_migrate_webhook_authorization]: The following webhook were found to be corrupted and removed from the database.") - for _, message := range messages { - log.Error("migration[v14a_migrate_webhook_authorization]: %s", message) - } - - _, err = sess.In("id", ids).NoAutoCondition().NoAutoTime().Delete(&Webhook{}) - } - } - return err - }) -} diff --git a/models/forgejo_migrations/v14a_migrate_webhook_authorization_test.go b/models/forgejo_migrations/v14a_migrate_webhook_authorization_test.go deleted file mode 100644 index 9da06f4baf..0000000000 --- a/models/forgejo_migrations/v14a_migrate_webhook_authorization_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "testing" - - migration_tests "forgejo.org/models/gitea_migrations/test" - "forgejo.org/modules/keying" - "forgejo.org/modules/timeutil" - webhook_module "forgejo.org/modules/webhook" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_MigrateWebhookSecrets(t *testing.T) { - type HookContentType int - type Webhook struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX"` - OwnerID int64 `xorm:"INDEX"` - IsSystemWebhook bool - URL string `xorm:"url TEXT"` - HTTPMethod string `xorm:"http_method"` - ContentType HookContentType - Secret string `xorm:"TEXT"` - Events string `xorm:"TEXT"` - IsActive bool `xorm:"INDEX"` - Type webhook_module.HookType `xorm:"VARCHAR(16) 'type'"` - Meta string `xorm:"TEXT"` - LastStatus webhook_module.HookStatus - - HeaderAuthorizationEncrypted string `xorm:"TEXT"` - - CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` - UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` - } - - type NewWebhook struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX"` - OwnerID int64 `xorm:"INDEX"` - IsSystemWebhook bool - URL string `xorm:"url TEXT"` - HTTPMethod string `xorm:"http_method"` - ContentType HookContentType - Secret string `xorm:"TEXT"` - Events string `xorm:"TEXT"` - IsActive bool `xorm:"INDEX"` - Type webhook_module.HookType `xorm:"VARCHAR(16) 'type'"` - Meta string `xorm:"TEXT"` - LastStatus webhook_module.HookStatus - - HeaderAuthorizationEncrypted []byte `xorm:"BLOB"` - - CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` - UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` - } - - // Prepare and load the testing database - x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Webhook)) - defer deferable() - if x == nil || t.Failed() { - return - } - - cnt, err := x.Table("webhook").Count() - require.NoError(t, err) - assert.EqualValues(t, 3, cnt) - - require.NoError(t, migrateWebhookSecrets(x)) - - cnt, err = x.Table("webhook").Count() - require.NoError(t, err) - assert.EqualValues(t, 2, cnt) - - key := keying.Webhook - - t.Run("webhook 1", func(t *testing.T) { - var webhook NewWebhook - _, err = x.Table("webhook").ID(1).Get(&webhook) - require.NoError(t, err) - - secret, err := key.Decrypt(webhook.HeaderAuthorizationEncrypted, keying.ColumnAndID("header_authorization_encrypted", webhook.ID)) - require.NoError(t, err) - assert.EqualValues(t, "Bearer s3cr3t", secret) - }) - - t.Run("webhook 3", func(t *testing.T) { - var webhook NewWebhook - _, err = x.Table("webhook").ID(3).Get(&webhook) - require.NoError(t, err) - assert.Empty(t, webhook.HeaderAuthorizationEncrypted) - }) -} diff --git a/models/forgejo_migrations/v14a_rework-notification.go b/models/forgejo_migrations/v14a_rework-notification.go index 04303559e8..77ae79d86f 100644 --- a/models/forgejo_migrations/v14a_rework-notification.go +++ b/models/forgejo_migrations/v14a_rework-notification.go @@ -4,6 +4,7 @@ package forgejo_migrations import ( + activities_model "forgejo.org/models/activities" "forgejo.org/modules/setting" "xorm.io/xorm" @@ -17,10 +18,9 @@ func init() { } func reworkNotification(x *xorm.Engine) error { - type NotificationStatus uint8 type Notification struct { - UserID int64 `xorm:"NOT NULL INDEX(s)"` - Status NotificationStatus `xorm:"SMALLINT NOT NULL INDEX(s)"` + UserID int64 `xorm:"NOT NULL INDEX(s)"` + Status activities_model.NotificationStatus `xorm:"SMALLINT NOT NULL INDEX(s)"` } if err := dropIndexIfExists(x, "notification", "IDX_notification_user_id"); err != nil { diff --git a/models/forgejo_migrations/v14a_set_remote_user_prohibit_login.go b/models/forgejo_migrations/v14a_set_remote_user_prohibit_login.go index 9f453e05f3..3575dad832 100644 --- a/models/forgejo_migrations/v14a_set_remote_user_prohibit_login.go +++ b/models/forgejo_migrations/v14a_set_remote_user_prohibit_login.go @@ -5,11 +5,10 @@ package forgejo_migrations import ( "context" - "fmt" "forgejo.org/models/db" + user_model "forgejo.org/models/user" "forgejo.org/modules/log" - "forgejo.org/modules/timeutil" "xorm.io/builder" "xorm.io/xorm" @@ -23,45 +22,13 @@ func init() { } func setProhibitLoginActivityPubUser(x *xorm.Engine) error { - type UserType int - const ( - UserTypeIndividual UserType = iota // Historic reason to make it starts at 0. - UserTypeOrganization // 1 - UserTypeUserReserved // 2 - UserTypeOrganizationReserved // 3 - UserTypeBot // 4 - UserTypeRemoteUser // 5 - UserTypeActivityPubUser // 6 - ) - type User struct { - ID int64 `xorm:"pk autoincr"` - Name string `xorm:"UNIQUE NOT NULL"` - Passwd string `xorm:"NOT NULL"` - PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"` - Type UserType - Salt string `xorm:"VARCHAR(32)"` - CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` - UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` - ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"` - } - type FederatedUser struct { - UserID int64 `xorm:"NOT NULL INDEX user_id"` - } - - userLogString := func(u *User) string { - if u == nil { - return "" - } - return fmt.Sprintf("", u.ID, u.Name) - } - return db.WithTx(db.DefaultContext, func(ctx context.Context) error { - return db.Iterate(ctx, builder.Eq{"type": 5}, func(ctx context.Context, user *User) error { - log.Info("Checking if user %s is created from ActivityPub", userLogString(user)) + return db.Iterate(ctx, builder.Eq{"type": 5}, func(ctx context.Context, user *user_model.User) error { + log.Info("Checking if user %s is created from ActivityPub", user.LogString()) // Users created from f3 also have the RemoteUser user type. All // FederatedUser should reference exactly one User. - has, err := db.GetEngine(ctx).Table("federated_user").Get(&FederatedUser{UserID: user.ID}) + has, err := db.GetEngine(ctx).Table("federated_user").Get(&user_model.FederatedUser{UserID: user.ID}) if err != nil { return err } @@ -70,9 +37,9 @@ func setProhibitLoginActivityPubUser(x *xorm.Engine) error { return nil } - log.Info("Updating user %s", userLogString(user)) - _, err = db.GetEngine(ctx).Table("user").ID(user.ID).Cols("type", "prohibit_login", "passwd", "salt", "passwd_hash_algo").Update(&User{ - Type: UserTypeActivityPubUser, + log.Info("Updating user %s", user.LogString()) + _, err = db.GetEngine(ctx).Table("user").ID(user.ID).Cols("type", "prohibit_login", "passwd", "salt", "passwd_hash_algo").Update(&user_model.User{ + Type: user_model.UserTypeActivityPubUser, ProhibitLogin: true, Passwd: "", Salt: "", diff --git a/models/forgejo_migrations/v14b_action-reindexing.go b/models/forgejo_migrations/v14b_action-reindexing.go index 83dc452191..6b5608a5d5 100644 --- a/models/forgejo_migrations/v14b_action-reindexing.go +++ b/models/forgejo_migrations/v14b_action-reindexing.go @@ -69,5 +69,5 @@ func reworkActionIndexes(x *xorm.Engine) error { return err } - return x.Sync(new(v14bAction)) // nosemgrep:xorm-sync-missing-ignore-drop-indices + return x.Sync(new(v14bAction)) } diff --git a/models/forgejo_migrations/v15a_remove-softdelete-action_runner_token.go b/models/forgejo_migrations/v15a_remove-softdelete-action_runner_token.go deleted file mode 100644 index c0f2e964a9..0000000000 --- a/models/forgejo_migrations/v15a_remove-softdelete-action_runner_token.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "xorm.io/builder" - "xorm.io/xorm" -) - -func init() { - registerMigration(&Migration{ - Description: "remove soft-delete capability from action_runner_token", - Upgrade: removeSoftDeleteActionRunnerToken, - }) -} - -func removeSoftDeleteActionRunnerToken(x *xorm.Engine) error { - // ActionRunnerToken was implemented with a column: "Deleted timeutil.TimeStamp `xorm:"deleted"``", which invokes - // xorm's soft-delete capability -- that is, if a record is deleted from the table, then it is just marked with a - // delete timestamp which causes it to be automatically excluded from future queries. This functionality is not - // used on `ActionRunnerToken` and it stands in the way of foreign key implementation -- if you can't actually - // delete the record in the table, then you can't remove foreign key references and therefore can't delete contents - // of the target tables, repository and user. - // - // This migration removes that column and deletes any records that were soft-deleted. - - // Before dropping the 'deleted' column, hard-delete any soft-deleted records. - if _, err := x.Table("action_runner_token").Where(builder.NotNull{"deleted"}).Delete(); err != nil { - return err - } - - _, err := x.Exec("ALTER TABLE action_runner_token DROP COLUMN `deleted`") - return err -} diff --git a/models/forgejo_migrations/v15a_remove-softdelete-action_runner_token_test.go b/models/forgejo_migrations/v15a_remove-softdelete-action_runner_token_test.go deleted file mode 100644 index 7a2c80e547..0000000000 --- a/models/forgejo_migrations/v15a_remove-softdelete-action_runner_token_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2026 The Forgejo Authors. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "testing" - - "forgejo.org/models/db" - migration_tests "forgejo.org/models/gitea_migrations/test" - "forgejo.org/modules/timeutil" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_removeSoftDeleteActionRunnerToken(t *testing.T) { - type ActionRunnerToken struct { - ID int64 - Token string `xorm:"UNIQUE"` - OwnerID int64 `xorm:"index"` - RepoID int64 `xorm:"index"` - IsActive bool - Created timeutil.TimeStamp `xorm:"created"` - Updated timeutil.TimeStamp `xorm:"updated"` - Deleted timeutil.TimeStamp `xorm:"deleted"` - } - x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ActionRunnerToken)) - defer deferable() - if x == nil || t.Failed() { - return - } - - require.NoError(t, removeSoftDeleteActionRunnerToken(x)) - - var remainingRecords []*ActionRunnerToken - require.NoError(t, - db.GetEngine(t.Context()). - Table("action_runner_token"). - Select("`id`, `owner_id`, `repo_id`"). - OrderBy("`id`"). - Unscoped(). // `Deleted` column doesn't exist anymore, so don't include in query - Find(&remainingRecords)) - assert.Equal(t, - []*ActionRunnerToken{ - {ID: 4}, - {ID: 5, OwnerID: 1}, - {ID: 6, RepoID: 1}, - }, - remainingRecords) -} diff --git a/models/forgejo_migrations/v15b_add-access_token-owned-repos.go b/models/forgejo_migrations/v15b_add-access_token-owned-repos.go deleted file mode 100644 index dd96c55c05..0000000000 --- a/models/forgejo_migrations/v15b_add-access_token-owned-repos.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2026 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 resource_all_owned_repositories to table access_token", - Upgrade: addAllOwnedRepositoriesToAccessToken, - }) -} - -func addAllOwnedRepositoriesToAccessToken(x *xorm.Engine) error { - type AccessToken struct { - ResourceAllRepos bool `xorm:"NOT NULL DEFAULT TRUE"` - } - _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(AccessToken)) - return err -} diff --git a/models/forgejo_migrations/v15b_add-access_token_resource.go b/models/forgejo_migrations/v15b_add-access_token_resource.go deleted file mode 100644 index 2e4afbcd43..0000000000 --- a/models/forgejo_migrations/v15b_add-access_token_resource.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2026 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 access_token_resource table", - Upgrade: addAccessTokenResource, - }) -} - -func addAccessTokenResource(x *xorm.Engine) error { - type AccessTokenResourceRepo struct { - ID int64 `xorm:"pk autoincr"` - TokenID int64 `xorm:"NOT NULL REFERENCES(access_token, id)"` // needs to be shortened from "AccessTokenID" for the index to fit MySQL table identifier length restrictions - RepoID int64 `xorm:"NOT NULL REFERENCES(repository, id)"` - - CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` - } - _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(AccessTokenResourceRepo)) - return err -} diff --git a/models/forgejo_migrations/v15b_add-ephemeral_runner.go b/models/forgejo_migrations/v15b_add-ephemeral_runner.go deleted file mode 100644 index 746763aa66..0000000000 --- a/models/forgejo_migrations/v15b_add-ephemeral_runner.go +++ /dev/null @@ -1,24 +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 ephemeral to action_runner", - Upgrade: addRunnerEphemeralField, - }) -} - -func addRunnerEphemeralField(x *xorm.Engine) error { - type ActionRunner struct { - Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"` - } - - _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(ActionRunner)) - return err -} diff --git a/models/forgejo_migrations/v15b_add-foreign-keys-action_runner_token.go b/models/forgejo_migrations/v15b_add-foreign-keys-action_runner_token.go deleted file mode 100644 index 584addf77b..0000000000 --- a/models/forgejo_migrations/v15b_add-foreign-keys-action_runner_token.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "xorm.io/builder" - "xorm.io/xorm" -) - -func init() { - registerMigration(&Migration{ - Description: "add foreign keys to action_runner_token", - Upgrade: addForeignKeysActionRunnerToken, - }) -} - -func addForeignKeysActionRunnerToken(x *xorm.Engine) error { - type ActionRunnerToken struct { - OwnerID int64 `xorm:"index REFERENCES(user, id)"` - RepoID int64 `xorm:"index REFERENCES(repository, id)"` - } - - // With the introduction of a foreign key, owner_id & repo_id cannot be set to "0". Runners can be registered as - // global (owner_id = NULL, repo_id = NULL), user/org (repo_id = NULL), or repo (owner_id = NULL) and NULL values - // now replace the '0' values. - _, err := x.Table(&ActionRunnerToken{}).Where("owner_id = 0").Update(map[string]any{"owner_id": nil}) - if err != nil { - return err - } - _, err = x.Table(&ActionRunnerToken{}).Where("repo_id = 0").Update(map[string]any{"repo_id": nil}) - if err != nil { - return err - } - - return syncForeignKeyWithDelete(x, - new(ActionRunnerToken), - builder.Or( - builder.And( - builder.Expr("owner_id IS NOT NULL"), - builder.Expr("NOT EXISTS (SELECT id FROM `user` WHERE `user`.id = action_runner_token.owner_id)"), - ), - builder.And( - builder.Expr("repo_id IS NOT NULL"), - builder.Expr("NOT EXISTS (SELECT id FROM repository WHERE repository.id = action_runner_token.repo_id)"), - ), - ), - ) -} diff --git a/models/forgejo_migrations/v15b_add-foreign-keys-action_runner_token_test.go b/models/forgejo_migrations/v15b_add-foreign-keys-action_runner_token_test.go deleted file mode 100644 index 04589d8eaf..0000000000 --- a/models/forgejo_migrations/v15b_add-foreign-keys-action_runner_token_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2026 The Forgejo Authors. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "testing" - - "forgejo.org/models/db" - migration_tests "forgejo.org/models/gitea_migrations/test" - "forgejo.org/modules/timeutil" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_addForeignKeysActionRunnerToken(t *testing.T) { - type ActionRunnerToken struct { - ID int64 - Token string `xorm:"UNIQUE"` - OwnerID int64 `xorm:"index"` - RepoID int64 `xorm:"index"` - IsActive bool - Created timeutil.TimeStamp `xorm:"created"` - Updated timeutil.TimeStamp `xorm:"updated"` - } - type User struct { - ID int64 `xorm:"pk autoincr"` - } - type Repository struct { - ID int64 `xorm:"pk autoincr"` - } - x, deferable := migration_tests.PrepareTestEnv(t, 0, new(User), new(Repository), new(ActionRunnerToken)) - defer deferable() - if x == nil || t.Failed() { - return - } - - require.NoError(t, addForeignKeysActionRunnerToken(x)) - - var remainingRecords []*ActionRunnerToken - require.NoError(t, - db.GetEngine(t.Context()). - Table("action_runner_token"). - Select("`id`, `owner_id`, `repo_id`"). - OrderBy("`id`"). - Find(&remainingRecords)) - assert.Equal(t, - []*ActionRunnerToken{ - {ID: 1}, - {ID: 2, OwnerID: 1}, - {ID: 3, RepoID: 1}, - }, - remainingRecords) -} diff --git a/models/forgejo_migrations/v15b_add-runner_request_key.go b/models/forgejo_migrations/v15b_add-runner_request_key.go deleted file mode 100644 index fb8150469b..0000000000 --- a/models/forgejo_migrations/v15b_add-runner_request_key.go +++ /dev/null @@ -1,24 +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 runner_request_key to action_task", - Upgrade: addActionTaskRunnerRequestKey, - }) -} - -func addActionTaskRunnerRequestKey(x *xorm.Engine) error { - type ActionTask struct { - RunnerID int64 `xorm:"index index(request_key)"` - RunnerRequestKey string `xorm:"index(request_key)"` - } - _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(ActionTask)) - return err -} diff --git a/models/forgejo_migrations/v15c_add_job_handle.go b/models/forgejo_migrations/v15c_add_job_handle.go deleted file mode 100644 index 59dca1b991..0000000000 --- a/models/forgejo_migrations/v15c_add_job_handle.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2026 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 handle to action_run_job", - Upgrade: addActionRunJobHandle, - }) -} - -func addActionRunJobHandle(x *xorm.Engine) error { - type ActionRunJob struct { - Handle string `xorm:"unique"` - } - _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(ActionRunJob)) - return err -} diff --git a/models/forgejo_migrations/v15c_add_mirror_remoteaddressauth.go b/models/forgejo_migrations/v15c_add_mirror_remoteaddressauth.go deleted file mode 100644 index b2f30235ad..0000000000 --- a/models/forgejo_migrations/v15c_add_mirror_remoteaddressauth.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2026 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: "replace remote_address with encrypted_remote_address in table mirror", - Upgrade: addMirrorRemoteAddressAuth, - }) -} - -func addMirrorRemoteAddressAuth(x *xorm.Engine) error { - type Mirror struct { - EncryptedRemoteAddress []byte `xorm:"BLOB NULL"` - } - if _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(Mirror)); err != nil { - return err - } - // No data migration is necessary or desired. `remote_address` contains sanitized URLs which don't have - // credentials, so they can't be migrated to `encrypted_remote_address`. Instead, as this data is accessed, - // `DecryptOrRecoverRemoteAddress` will recover the fully credentialed contents of the remote address from the git - // repo's `origin` remote address. - _, err := x.Exec("ALTER TABLE `mirror` DROP COLUMN `remote_address`") - return err -} diff --git a/models/forgejo_migrations/v15c_add_schedule_spec_time_zones.go b/models/forgejo_migrations/v15c_add_schedule_spec_time_zones.go deleted file mode 100644 index d72d585725..0000000000 --- a/models/forgejo_migrations/v15c_add_schedule_spec_time_zones.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "forgejo.org/modules/optional" - - "xorm.io/xorm" -) - -func init() { - registerMigration(&Migration{ - Description: "add time zone support to action_schedule_spec", - Upgrade: addActionScheduleSpecTimeZone, - }) -} - -func addActionScheduleSpecTimeZone(x *xorm.Engine) error { - type ActionScheduleSpec struct { - TimeZone optional.Option[string] - } - - _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(ActionScheduleSpec)) - if err != nil { - return err - } - - _, err = x.Exec("ALTER TABLE action_schedule DROP COLUMN `specs`") - return err -} diff --git a/models/forgejo_migrations/v15c_fix-project-sorting-unique-constraints.go b/models/forgejo_migrations/v15c_fix-project-sorting-unique-constraints.go deleted file mode 100644 index 9fc28d523c..0000000000 --- a/models/forgejo_migrations/v15c_fix-project-sorting-unique-constraints.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "fmt" - - "forgejo.org/models/gitea_migrations/base" - "forgejo.org/modules/setting" - - "xorm.io/xorm" -) - -func init() { - registerMigration(&Migration{ - Description: "Fix duplicate project sorting values and add unique constraints", - Upgrade: fixProjectSortingUniqueConstraints, - }) -} - -func fixProjectSortingUniqueConstraints(x *xorm.Engine) error { - // Step 1: Fix existing duplicates in project_issue (cards) - // Reassign sequential sorting values within each column - if err := fixProjectIssueDuplicates(x); err != nil { - return err - } - - // Step 2: Fix existing duplicates in project_board (columns) - // Reassign sequential sorting values within each project - if err := fixProjectBoardDuplicates(x); err != nil { - return err - } - - // Step 3: Remove duplicate (project_id, issue_id) rows keeping the lowest id - if err := fixProjectIssueDuplicateAssignments(x); err != nil { - return err - } - - // Step 4: Add unique constraints (idempotent — skip if already exists) - if err := createUniqueIndexIfNotExists(x, "project_issue", "UQE_project_issue_column_sorting", "project_board_id, sorting"); err != nil { - return err - } - if err := createUniqueIndexIfNotExists(x, "project_issue", "UQE_project_issue_project_issue", "project_id, issue_id"); err != nil { - return err - } - if err := createUniqueIndexIfNotExists(x, "project_board", "UQE_project_board_project_sorting", "project_id, sorting"); err != nil { - return err - } - - // Step 5: Enforce NOT NULL on project_issue columns. - // The struct tags declare NOT NULL but the DB may not enforce it. - // On SQLite, RecreateTables rebuilds the table with NOT NULL and unique constraints. - return enforceProjectIssueNotNull(x) -} - -func createUniqueIndexIfNotExists(x *xorm.Engine, tableName, indexName, columns string) error { - exists, err := indexExists(x, tableName, indexName) - if err != nil { - return err - } - if exists { - return nil - } - _, err = x.Exec(fmt.Sprintf("CREATE UNIQUE INDEX %s ON %s (%s)", indexName, tableName, columns)) - return err -} - -func fixProjectIssueDuplicates(x *xorm.Engine) error { - switch { - case setting.Database.Type.IsSQLite3(): - // SQLite: Use UPDATE with subquery - _, err := x.Exec(` - UPDATE project_issue SET sorting = ( - SELECT new_sort FROM ( - SELECT id, ROW_NUMBER() OVER (PARTITION BY project_board_id ORDER BY sorting, id) - 1 as new_sort - FROM project_issue - ) ranked WHERE ranked.id = project_issue.id - ) - `) - return err - - case setting.Database.Type.IsPostgreSQL(): - // PostgreSQL: Use UPDATE FROM with subquery - _, err := x.Exec(` - UPDATE project_issue pi SET sorting = ranked.new_sort - FROM ( - SELECT id, ROW_NUMBER() OVER (PARTITION BY project_board_id ORDER BY sorting, id) - 1 as new_sort - FROM project_issue - ) ranked - WHERE pi.id = ranked.id - `) - return err - - case setting.Database.Type.IsMySQL(): - // MySQL: Use UPDATE with JOIN - _, err := x.Exec(` - UPDATE project_issue pi - INNER JOIN ( - SELECT id, ROW_NUMBER() OVER (PARTITION BY project_board_id ORDER BY sorting, id) - 1 as new_sort - FROM project_issue - ) ranked ON pi.id = ranked.id - SET pi.sorting = ranked.new_sort - `) - return err - - default: - return fmt.Errorf("unsupported db dialect type %v", x.Dialect().URI().DBType) - } -} - -func fixProjectBoardDuplicates(x *xorm.Engine) error { - switch { - case setting.Database.Type.IsSQLite3(): - // SQLite: Use UPDATE with subquery - _, err := x.Exec(` - UPDATE project_board SET sorting = ( - SELECT new_sort FROM ( - SELECT id, ROW_NUMBER() OVER (PARTITION BY project_id ORDER BY sorting, id) - 1 as new_sort - FROM project_board - ) ranked WHERE ranked.id = project_board.id - ) - `) - return err - - case setting.Database.Type.IsPostgreSQL(): - // PostgreSQL: Use UPDATE FROM with subquery - _, err := x.Exec(` - UPDATE project_board pb SET sorting = ranked.new_sort - FROM ( - SELECT id, ROW_NUMBER() OVER (PARTITION BY project_id ORDER BY sorting, id) - 1 as new_sort - FROM project_board - ) ranked - WHERE pb.id = ranked.id - `) - return err - - case setting.Database.Type.IsMySQL(): - // MySQL: Use UPDATE with JOIN - _, err := x.Exec(` - UPDATE project_board pb - INNER JOIN ( - SELECT id, ROW_NUMBER() OVER (PARTITION BY project_id ORDER BY sorting, id) - 1 as new_sort - FROM project_board - ) ranked ON pb.id = ranked.id - SET pb.sorting = ranked.new_sort - `) - return err - - default: - return fmt.Errorf("unsupported db dialect type %v", x.Dialect().URI().DBType) - } -} - -func enforceProjectIssueNotNull(x *xorm.Engine) error { - switch { - case setting.Database.Type.IsSQLite3(): - type ProjectIssue struct { - ID int64 `xorm:"pk autoincr"` - IssueID int64 `xorm:"INDEX NOT NULL unique(project_issue)"` - ProjectID int64 `xorm:"INDEX NOT NULL unique(project_issue)"` - ProjectColumnID int64 `xorm:"'project_board_id' INDEX NOT NULL unique(column_sorting)"` - Sorting int64 `xorm:"NOT NULL DEFAULT 0 unique(column_sorting)"` - } - return base.RecreateTables(new(ProjectIssue))(x) - - case setting.Database.Type.IsPostgreSQL(): - for _, col := range []string{"issue_id", "project_id", "project_board_id"} { - if _, err := x.Exec(fmt.Sprintf("ALTER TABLE project_issue ALTER COLUMN %s SET NOT NULL", col)); err != nil { - return err - } - } - return nil - - case setting.Database.Type.IsMySQL(): - for _, col := range []string{"issue_id", "project_id", "project_board_id"} { - if _, err := x.Exec(fmt.Sprintf("ALTER TABLE project_issue MODIFY COLUMN %s BIGINT NOT NULL", col)); err != nil { - return err - } - } - return nil - - default: - return fmt.Errorf("unsupported db dialect type %v", x.Dialect().URI().DBType) - } -} - -// fixProjectIssueDuplicateAssignments removes duplicate (project_id, issue_id) rows, -// keeping only the row with the lowest id for each pair. -func fixProjectIssueDuplicateAssignments(x *xorm.Engine) error { - switch { - case setting.Database.Type.IsSQLite3(): - _, err := x.Exec(` - DELETE FROM project_issue WHERE id NOT IN ( - SELECT MIN(id) FROM project_issue GROUP BY project_id, issue_id - ) - `) - return err - - case setting.Database.Type.IsPostgreSQL(): - _, err := x.Exec(` - DELETE FROM project_issue pi USING project_issue pi2 - WHERE pi.project_id = pi2.project_id AND pi.issue_id = pi2.issue_id AND pi.id > pi2.id - `) - return err - - case setting.Database.Type.IsMySQL(): - _, err := x.Exec(` - DELETE pi FROM project_issue pi - INNER JOIN project_issue pi2 - ON pi.project_id = pi2.project_id AND pi.issue_id = pi2.issue_id AND pi.id > pi2.id - `) - return err - - default: - return fmt.Errorf("unsupported db dialect type %v", x.Dialect().URI().DBType) - } -} diff --git a/models/forgejo_migrations/v16a_add_authorized_integration.go b/models/forgejo_migrations/v16a_add_authorized_integration.go deleted file mode 100644 index 4cfd5219f4..0000000000 --- a/models/forgejo_migrations/v16a_add_authorized_integration.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2026 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 authorized_integration tables", - Upgrade: addAuthorizedIntegrationTables, - }) -} - -func addAuthorizedIntegrationTables(x *xorm.Engine) error { - type ClaimRules struct{} - type AuthorizedIntegration struct { - ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"NOT NULL REFERENCES(user, id)"` - Scope string `xorm:"NOT NULL"` - ResourceAllRepos bool `xorm:"NOT NULL"` - Issuer string `xorm:"NOT NULL UNIQUE(s)"` - Audience string `xorm:"NOT NULL UNIQUE(s)"` - ClaimRules *ClaimRules `xorm:"NOT NULL JSON"` - CreatedUnix timeutil.TimeStamp `xorm:"NOT NULL created"` - UpdatedUnix timeutil.TimeStamp `xorm:"NOT NULL updated"` - } - type AuthorizedIntegResourceRepo struct { - ID int64 `xorm:"pk autoincr"` - IntegID int64 `xorm:"NOT NULL REFERENCES(authorized_integration, id)"` - RepoID int64 `xorm:"NOT NULL REFERENCES(repository, id)"` - CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` - } - - _, err := x.SyncWithOptions( - xorm.SyncOptions{IgnoreDropIndices: true}, - new(AuthorizedIntegration), - new(AuthorizedIntegResourceRepo), - ) - return err -} diff --git a/models/forgejo_migrations/v16a_add_oidcsubjectformat_actions_unit_config.go b/models/forgejo_migrations/v16a_add_oidcsubjectformat_actions_unit_config.go deleted file mode 100644 index efe737ad5a..0000000000 --- a/models/forgejo_migrations/v16a_add_oidcsubjectformat_actions_unit_config.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "context" - "fmt" - - "forgejo.org/models/db" - "forgejo.org/modules/json" - "forgejo.org/modules/optional" - - "xorm.io/builder" - "xorm.io/xorm" -) - -func init() { - registerMigration(&Migration{ - Description: "add OIDCSubjectFormat=legacy-forgejo-v15 to all existing repositories with actions enabled", - Upgrade: setOIDCSubjectFormatLegacy15, - }) -} - -func setOIDCSubjectFormatLegacy15(x *xorm.Engine) error { - type Type int - const TypeActions Type = 10 // 10 Actions - type ActionsConfig struct { - DisabledWorkflows []string `json:",omitempty"` - OIDCSubjectFormat string - } - type RepoUnit struct { //revive:disable-line:exported - ID int64 `xorm:"pk"` - Type Type `xorm:"INDEX(s)"` - Config optional.Option[string] `xorm:"TEXT"` - } - - return db.Iterate( - db.DefaultContext, - builder.Eq{"type": TypeActions}, - func(ctx context.Context, unit *RepoUnit) error { - has, config := unit.Config.Get() - if !has { - config = "{}" - } - var actionsConfig ActionsConfig - if err := json.Unmarshal([]byte(config), &actionsConfig); err != nil { - return fmt.Errorf("failed to parse Actions config %q: %w", config, err) - } - - actionsConfig.OIDCSubjectFormat = "legacy-forgejo-v15" - - configBytes, err := json.Marshal(actionsConfig) - if err != nil { - return fmt.Errorf("failed to convert Actions config to JSON: %w", err) - } - r, err := db.GetEngine(ctx). - ID(unit.ID). - Cols("config"). - Update(&RepoUnit{Config: optional.Some(string(configBytes))}) - if err != nil { - return err - } else if r != 1 { - return fmt.Errorf("UPDATE expected to affect 1 row, but was %d", r) - } - return nil - }) -} diff --git a/models/forgejo_migrations/v16a_add_oidcsubjectformat_actions_unit_config_test.go b/models/forgejo_migrations/v16a_add_oidcsubjectformat_actions_unit_config_test.go deleted file mode 100644 index 865c0e394b..0000000000 --- a/models/forgejo_migrations/v16a_add_oidcsubjectformat_actions_unit_config_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2026 The Forgejo Authors. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "testing" - - "forgejo.org/models/db" - migration_tests "forgejo.org/models/gitea_migrations/test" - "forgejo.org/modules/timeutil" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "xorm.io/xorm/convert" -) - -func Test_setOIDCSubjectFormatLegacy15(t *testing.T) { - type Type int - type UnitAccessMode int - type RepoUnit struct { //revive:disable-line:exported - ID int64 - RepoID int64 `xorm:"INDEX(s)"` - Type Type `xorm:"INDEX(s)"` - Config convert.Conversion `xorm:"TEXT"` - CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"` - DefaultPermissions UnitAccessMode `xorm:"NOT NULL DEFAULT 0"` - } - x, deferable := migration_tests.PrepareTestEnv(t, 0, new(RepoUnit)) - defer deferable() - if x == nil || t.Failed() { - return - } - - require.NoError(t, setOIDCSubjectFormatLegacy15(x)) - - var records []map[string]string - require.NoError(t, - db.GetEngine(t.Context()). - Table("repo_unit"). - Select("`id`, `repo_id`, `config`"). - OrderBy("`id`"). - Find(&records)) - assert.Equal(t, []map[string]string{ - { - "config": "{\"OIDCSubjectFormat\":\"legacy-forgejo-v15\"}", - "id": "1", - "repo_id": "4", - }, - { - "config": "{\"DisabledWorkflows\":[\"renovate.yml\"],\"OIDCSubjectFormat\":\"legacy-forgejo-v15\"}", - "id": "2", - "repo_id": "5", - }, - { - "config": "", - "id": "3", - "repo_id": "6", - }, - }, records) -} diff --git a/models/forgejo_migrations/v16b_add-login-source-id-to-forgejo-auth-token.go b/models/forgejo_migrations/v16b_add-login-source-id-to-forgejo-auth-token.go deleted file mode 100644 index 3839e4a25b..0000000000 --- a/models/forgejo_migrations/v16b_add-login-source-id-to-forgejo-auth-token.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "forgejo.org/modules/optional" - - "xorm.io/xorm" -) - -func init() { - registerMigration(&Migration{ - Description: "add login_source_id column to forgejo_auth_token", - Upgrade: addLoginSourceIDToForgejoAuthToken, - }) -} - -func addLoginSourceIDToForgejoAuthToken(x *xorm.Engine) error { - type ForgejoAuthToken struct { - LoginSourceID optional.Option[int64] `xorm:"INDEX REFERENCES(login_source, id)"` - } - _, err := x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, new(ForgejoAuthToken)) - return err -} diff --git a/models/forgejo_migrations/v16b_authorized_integration_name_description.go b/models/forgejo_migrations/v16b_authorized_integration_name_description.go deleted file mode 100644 index 3f5450310f..0000000000 --- a/models/forgejo_migrations/v16b_authorized_integration_name_description.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package forgejo_migrations - -import ( - "context" - "fmt" - - "forgejo.org/models/db" - "forgejo.org/modules/timeutil" - - "xorm.io/xorm" -) - -func init() { - registerMigration(&Migration{ - Description: "add name & description to authorized_integration", - Upgrade: addAuthorizedIntegrationNameDescription, - }) -} - -func addAuthorizedIntegrationNameDescription(x *xorm.Engine) error { - type AuthorizedIntegration struct { - // New fields: - Name string - Description string `xorm:"LONGTEXT"` - - // Existing fields, used for UPDATE in migration: - ID int64 `xorm:"pk autoincr"` - Issuer string `xorm:"NOT NULL UNIQUE(s)"` - Audience string `xorm:"NOT NULL UNIQUE(s)"` - CreatedUnix timeutil.TimeStamp `xorm:"NOT NULL created"` - // don't include `UpdatedUnix`, so the updated timestamp isn't bumped when Name is set in migration - } - - _, err := x.SyncWithOptions( - xorm.SyncOptions{IgnoreDropIndices: true}, - new(AuthorizedIntegration), - ) - if err != nil { - return err - } - - // As v16a has creating this table, v16b will likely have no records for any users. But for developers working on - // v16, populate "Name" with a quick computed value: - return db.Iterate(db.DefaultContext, nil, func(ctx context.Context, ai *AuthorizedIntegration) error { - ai.Name = fmt.Sprintf("%s created %s", ai.Issuer, ai.CreatedUnix.FormatDate()) - r, err := db.GetEngine(ctx). - ID(ai.ID). - Cols("name"). - Update(ai) - if err != nil { - return err - } else if r != 1 { - return fmt.Errorf("UPDATE expected to affect 1 row, but was %d", r) - } - return nil - }) -} diff --git a/models/forgejo_migrations/v16c_authorized_integration_ui.go b/models/forgejo_migrations/v16c_authorized_integration_ui.go deleted file mode 100644 index 4c5ee8dd5e..0000000000 --- a/models/forgejo_migrations/v16c_authorized_integration_ui.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2026 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 ui to authorized_integration", - Upgrade: addAuthorizedIntegrationUI, - }) -} - -func addAuthorizedIntegrationUI(x *xorm.Engine) error { - type AuthorizedIntegration struct { - UI string `xorm:"NOT NULL default('generic')"` - } - - _, err := x.SyncWithOptions( - xorm.SyncOptions{IgnoreDropIndices: true}, - new(AuthorizedIntegration), - ) - return err -} diff --git a/models/forgejo_migrations_legacy/v33.go b/models/forgejo_migrations_legacy/v33.go index b29c5feeb3..ce220d8179 100644 --- a/models/forgejo_migrations_legacy/v33.go +++ b/models/forgejo_migrations_legacy/v33.go @@ -52,7 +52,7 @@ func addFederatedUserActivityTables(x *xorm.Engine) { FollowingUserID int64 `xorm:"NOT NULL unique(fuf_rel)"` } - // Add InboxPath to FederatedUser & add index to UserID + // Add InboxPath to FederatedUser & add index fo UserID type FederatedUser struct { ID int64 `xorm:"pk autoincr"` UserID int64 `xorm:"NOT NULL INDEX user_id"` diff --git a/models/forgejo_migrations_legacy/v39.go b/models/forgejo_migrations_legacy/v39.go index 616404f7eb..3f0d94b6aa 100644 --- a/models/forgejo_migrations_legacy/v39.go +++ b/models/forgejo_migrations_legacy/v39.go @@ -58,7 +58,7 @@ func MigrateActionSecretsToKeying(x *xorm.Engine) error { return nil } - bean.SetData(secretBytes) + bean.SetSecret(secretBytes) _, err = sess.Cols("data").ID(bean.ID).Update(bean) return err }) diff --git a/models/git/branch_list.go b/models/git/branch_list.go index 07bcff8db9..4b678f15c0 100644 --- a/models/git/branch_list.go +++ b/models/git/branch_list.go @@ -77,8 +77,8 @@ func (opts FindBranchOptions) ToConds() builder.Cond { if len(opts.ExcludeBranchNames) > 0 { cond = cond.And(builder.NotIn("name", opts.ExcludeBranchNames)) } - if has, value := opts.IsDeletedBranch.Get(); has { - cond = cond.And(builder.Eq{"is_deleted": value}) + if opts.IsDeletedBranch.Has() { + cond = cond.And(builder.Eq{"is_deleted": opts.IsDeletedBranch.Value()}) } if opts.Keyword != "" { cond = cond.And(builder.Like{"name", opts.Keyword}) diff --git a/models/git/branch_test.go b/models/git/branch_test.go index 5eca70324d..4340e8f729 100644 --- a/models/git/branch_test.go +++ b/models/git/branch_test.go @@ -115,7 +115,7 @@ func TestFindRenamedBranch(t *testing.T) { assert.True(t, exist) assert.Equal(t, "master", branch.To) - _, exist, err = git_model.FindRenamedBranch(db.DefaultContext, 1, "unknown") + _, exist, err = git_model.FindRenamedBranch(db.DefaultContext, 1, "unknow") require.NoError(t, err) assert.False(t, exist) } diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go index 3eaada2fdd..c1eb750230 100644 --- a/models/git/protected_branch.go +++ b/models/git/protected_branch.go @@ -213,7 +213,7 @@ func (protectBranch *ProtectedBranch) GetUnprotectedFilePatterns() []glob.Glob { func getFilePatterns(filePatterns string) []glob.Glob { extarr := make([]glob.Glob, 0, 10) - for expr := range strings.SplitSeq(strings.ToLower(filePatterns), ";") { + for _, expr := range strings.Split(strings.ToLower(filePatterns), ";") { expr = strings.TrimSpace(expr) if expr != "" { if g, err := glob.Compile(expr, '.', '/'); err != nil { diff --git a/models/git/protected_branch_list.go b/models/git/protected_branch_list.go index 54930b7eac..c7a3154884 100644 --- a/models/git/protected_branch_list.go +++ b/models/git/protected_branch_list.go @@ -50,7 +50,7 @@ func FindRepoProtectedBranchRules(ctx context.Context, repoID int64) (ProtectedB func FindAllMatchedBranches(ctx context.Context, repoID int64, ruleName string) ([]string, error) { results := make([]string, 0, 10) for page := 1; ; page++ { - branchNames, err := FindBranchNames(ctx, FindBranchOptions{ + brancheNames, err := FindBranchNames(ctx, FindBranchOptions{ ListOptions: db.ListOptions{ PageSize: 100, Page: page, @@ -63,12 +63,12 @@ func FindAllMatchedBranches(ctx context.Context, repoID int64, ruleName string) } rule := glob.MustCompile(ruleName) - for _, branch := range branchNames { + for _, branch := range brancheNames { if rule.Match(branch) { results = append(results, branch) } } - if len(branchNames) < 100 { + if len(brancheNames) < 100 { break } } diff --git a/models/gitea_migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml b/models/gitea_migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml index d7eab38185..2e1b1c7eee 100644 --- a/models/gitea_migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml +++ b/models/gitea_migrations/fixtures/Test_CheckProjectColumnsConsistency/project_board.yml @@ -4,7 +4,6 @@ title: Done creator_id: 2 default: false - sorting: 1 created_unix: 1588117528 updated_unix: 1588117528 @@ -14,7 +13,6 @@ title: Backlog creator_id: 2 default: true - sorting: 0 created_unix: 1588117528 updated_unix: 1588117528 @@ -24,6 +22,5 @@ title: Uncategorized creator_id: 2 default: true - sorting: 1 created_unix: 1588117528 updated_unix: 1588117528 diff --git a/models/gitea_migrations/fixtures/Test_MigrateWebhookSecrets/webhook.yml b/models/gitea_migrations/fixtures/Test_MigrateWebhookSecrets/webhook.yml deleted file mode 100644 index 338bf4468e..0000000000 --- a/models/gitea_migrations/fixtures/Test_MigrateWebhookSecrets/webhook.yml +++ /dev/null @@ -1,17 +0,0 @@ -- - id: 1 - owner_id: 3 - repo_id: 3 - header_authorization_encrypted: '54586e944822336738b940c2560b7ef38bea3a91dcfe43c9c32e55b2a57050f75c63456b' - -- - id: 2 - owner_id: 1 - repo_id: 2 - header_authorization_encrypted: 'badbadbad' - -- - id: 3 - owner_id: 2 - repo_id: 1 - header_authorization_encrypted: '' diff --git a/models/gitea_migrations/fixtures/Test_addForeignKeysActionRunnerToken/action_runner_token.yml b/models/gitea_migrations/fixtures/Test_addForeignKeysActionRunnerToken/action_runner_token.yml deleted file mode 100644 index 9905c440cc..0000000000 --- a/models/gitea_migrations/fixtures/Test_addForeignKeysActionRunnerToken/action_runner_token.yml +++ /dev/null @@ -1,24 +0,0 @@ -- - id: 1 - owner_id: null - repo_id: null - -- - id: 2 - owner_id: 1 - repo_id: null - -- - id: 3 - owner_id: null - repo_id: 1 - -# Expected to be deleted due to invalid owner_id foreign key -- - id: 4 - owner_id: 100 - -# Expected to be deleted due to invalid repo_id foreign key -- - id: 5 - repo_id: 100 diff --git a/models/gitea_migrations/fixtures/Test_addForeignKeysActionRunnerToken/repository.yml b/models/gitea_migrations/fixtures/Test_addForeignKeysActionRunnerToken/repository.yml deleted file mode 100644 index a88c2ef89f..0000000000 --- a/models/gitea_migrations/fixtures/Test_addForeignKeysActionRunnerToken/repository.yml +++ /dev/null @@ -1,2 +0,0 @@ -- - id: 1 diff --git a/models/gitea_migrations/fixtures/Test_addForeignKeysActionRunnerToken/user.yml b/models/gitea_migrations/fixtures/Test_addForeignKeysActionRunnerToken/user.yml deleted file mode 100644 index a88c2ef89f..0000000000 --- a/models/gitea_migrations/fixtures/Test_addForeignKeysActionRunnerToken/user.yml +++ /dev/null @@ -1,2 +0,0 @@ -- - id: 1 diff --git a/models/gitea_migrations/fixtures/Test_removeSoftDeleteActionRunnerToken/action_runner_token.yml b/models/gitea_migrations/fixtures/Test_removeSoftDeleteActionRunnerToken/action_runner_token.yml deleted file mode 100644 index 3e79a7cb8a..0000000000 --- a/models/gitea_migrations/fixtures/Test_removeSoftDeleteActionRunnerToken/action_runner_token.yml +++ /dev/null @@ -1,33 +0,0 @@ -- - id: 1 - owner_id: null - repo_id: null - deleted: 1767994875 - -- - id: 2 - owner_id: 1 - repo_id: null - deleted: 1767994875 - -- - id: 3 - owner_id: null - repo_id: 1 - deleted: 1767994875 - -- - id: 4 - owner_id: null - repo_id: null - -- - id: 5 - owner_id: 1 - repo_id: null - -- - id: 6 - owner_id: null - repo_id: 1 - diff --git a/models/gitea_migrations/fixtures/Test_setOIDCSubjectFormatLegacy15/repo_unit.yml b/models/gitea_migrations/fixtures/Test_setOIDCSubjectFormatLegacy15/repo_unit.yml deleted file mode 100644 index bcd732bc1e..0000000000 --- a/models/gitea_migrations/fixtures/Test_setOIDCSubjectFormatLegacy15/repo_unit.yml +++ /dev/null @@ -1,14 +0,0 @@ -- id: 1 - repo_id: 4 - type: 10 # actions - # config - null - created_unix: 1773518296 -- id: 2 - repo_id: 5 - type: 10 # actions - config: '{"DisabledWorkflows":["renovate.yml"]}' - created_unix: 1773518296 -- id: 3 - repo_id: 6 - type: 1 # code - created_unix: 1773518296 diff --git a/models/gitea_migrations/migrations.go b/models/gitea_migrations/migrations.go index 9b30f9940d..5d6bf0aab2 100644 --- a/models/gitea_migrations/migrations.go +++ b/models/gitea_migrations/migrations.go @@ -126,7 +126,7 @@ func prepareMigrationTasks() []*migration { newMigration(102, "update migration repositories' service type", v1_11.DropColumnHeadUserNameOnPullRequest), newMigration(103, "Add WhitelistDeployKeys to protected branch", v1_11.AddWhitelistDeployKeysToBranches), - newMigration(104, "remove unnecessary columns from label", v1_11.RemoveLabelUnneededCols), + newMigration(104, "remove unnecessary columns from label", v1_11.RemoveLabelUneededCols), newMigration(105, "add includes_all_repositories to teams", v1_11.AddTeamIncludesAllRepositories), newMigration(106, "add column `mode` to table watch", v1_11.AddModeColumnToWatch), newMigration(107, "Add template options to repository", v1_11.AddTemplateToRepo), @@ -323,7 +323,7 @@ func prepareMigrationTasks() []*migration { newMigration(268, "Update Action Ref", v1_21.UpdateActionsRefIndex), newMigration(269, "Drop deleted branch table", v1_21.DropDeletedBranchTable), newMigration(270, "Fix PackageProperty typo", v1_21.FixPackagePropertyTypo), - newMigration(271, "Allow archiving labels", v1_21.AddArchivedUnixColumnInLabelTable), + newMigration(271, "Allow archiving labels", v1_21.AddArchivedUnixColumInLabelTable), newMigration(272, "Add Version to ActionRun table", v1_21.AddVersionToActionRunTable), newMigration(273, "Add Action Schedule Table", v1_21.AddActionScheduleTable), newMigration(274, "Add Actions artifacts expiration date", v1_21.AddExpiredUnixColumnInActionArtifactTable), diff --git a/models/gitea_migrations/test/tests.go b/models/gitea_migrations/test/tests.go index 086127a2e8..fc54b65626 100644 --- a/models/gitea_migrations/test/tests.go +++ b/models/gitea_migrations/test/tests.go @@ -265,7 +265,7 @@ func deleteDB() error { func removeAllWithRetry(dir string) error { var err error - for range 20 { + for i := 0; i < 20; i++ { err = os.RemoveAll(dir) if err == nil { break diff --git a/models/gitea_migrations/v1_11/v104.go b/models/gitea_migrations/v1_11/v104.go index 606690009d..47cf320359 100644 --- a/models/gitea_migrations/v1_11/v104.go +++ b/models/gitea_migrations/v1_11/v104.go @@ -9,7 +9,7 @@ import ( "xorm.io/xorm" ) -func RemoveLabelUnneededCols(x *xorm.Engine) error { +func RemoveLabelUneededCols(x *xorm.Engine) error { // Make sure the columns exist before dropping them type Label struct { QueryString string diff --git a/models/gitea_migrations/v1_11/v111.go b/models/gitea_migrations/v1_11/v111.go index 59ca416af0..6f531e4858 100644 --- a/models/gitea_migrations/v1_11/v111.go +++ b/models/gitea_migrations/v1_11/v111.go @@ -5,7 +5,6 @@ package v1_11 import ( "fmt" - "slices" "xorm.io/xorm" ) @@ -346,8 +345,10 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { } return AccessModeWrite <= perm.UnitsMode[UnitTypeCode], nil } - if slices.Contains(protectedBranch.ApprovalsWhitelistUserIDs, reviewer.ID) { - return true, nil + for _, id := range protectedBranch.ApprovalsWhitelistUserIDs { + if id == reviewer.ID { + return true, nil + } } // isUserInTeams @@ -409,7 +410,7 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { official, err := isOfficialReviewer(sess, review.IssueID, reviewer) if err != nil { - // Branch might not be protected or other error, ignore it. + // Branch might not be proteced or other error, ignore it. continue } review.Official = official diff --git a/models/gitea_migrations/v1_11/v115.go b/models/gitea_migrations/v1_11/v115.go index 84364e310b..65094df93d 100644 --- a/models/gitea_migrations/v1_11/v115.go +++ b/models/gitea_migrations/v1_11/v115.go @@ -146,7 +146,7 @@ func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error) return "", fmt.Errorf("io.ReadAll: %w", err) } - newAvatar := fmt.Sprintf("%x", md5.Sum(fmt.Appendf(nil, "%d-%x", userID, md5.Sum(data)))) + newAvatar := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", userID, md5.Sum(data))))) if newAvatar == oldAvatar { return newAvatar, nil } diff --git a/models/gitea_migrations/v1_13/v151.go b/models/gitea_migrations/v1_13/v151.go index 09abfd4b9e..a464e6e7a7 100644 --- a/models/gitea_migrations/v1_13/v151.go +++ b/models/gitea_migrations/v1_13/v151.go @@ -25,7 +25,6 @@ func SetDefaultPasswordToArgon2(x *xorm.Engine) error { return err case setting.Database.Type.IsSQLite3(): // drop through - break default: log.Fatal("Unrecognized DB") } diff --git a/models/gitea_migrations/v1_15/v182.go b/models/gitea_migrations/v1_15/v182.go index 069f9890cd..f53ff11df9 100644 --- a/models/gitea_migrations/v1_15/v182.go +++ b/models/gitea_migrations/v1_15/v182.go @@ -24,7 +24,7 @@ func AddIssueResourceIndexTable(x *xorm.Engine) error { return err } - // Remove data we're going to rebuild + // Remove data we're goint to rebuild if _, err := sess.Table("issue_index").Where("1=1").Delete(&ResourceIndex{}); err != nil { return err } diff --git a/models/gitea_migrations/v1_20/v259.go b/models/gitea_migrations/v1_20/v259.go index 1ae8b2e30f..9b2b68263e 100644 --- a/models/gitea_migrations/v1_20/v259.go +++ b/models/gitea_migrations/v1_20/v259.go @@ -329,7 +329,7 @@ func ConvertScopedAccessTokens(x *xorm.Engine) error { for _, token := range tokens { var scopes []string allNewScopesMap := make(map[AccessTokenScope]bool) - for oldScope := range strings.SplitSeq(token.Scope, ",") { + for _, oldScope := range strings.Split(token.Scope, ",") { if newScopes, exists := accessTokenScopeMap[OldAccessTokenScope(oldScope)]; exists { for _, newScope := range newScopes { allNewScopesMap[newScope] = true diff --git a/models/gitea_migrations/v1_21/v271.go b/models/gitea_migrations/v1_21/v271.go index 38b171c323..e3ce2d4b74 100644 --- a/models/gitea_migrations/v1_21/v271.go +++ b/models/gitea_migrations/v1_21/v271.go @@ -9,7 +9,7 @@ import ( "xorm.io/xorm" ) -func AddArchivedUnixColumnInLabelTable(x *xorm.Engine) error { +func AddArchivedUnixColumInLabelTable(x *xorm.Engine) error { type Label struct { ArchivedUnix timeutil.TimeStamp `xorm:"DEFAULT NULL"` } diff --git a/models/issues/TestGetParticipantIDsByIssue/comment.yml b/models/issues/TestGetParticipantIDsByIssue/comment.yml deleted file mode 100644 index 7ab1cf88a6..0000000000 --- a/models/issues/TestGetParticipantIDsByIssue/comment.yml +++ /dev/null @@ -1,10 +0,0 @@ -- id: 1001 - type: 21 # code comment - poster_id: 10 - issue_id: 1 - review_id: 1001 - content: "Some code comment that is pending to be published" - line: -4 - tree_path: "README.md" - created_unix: 946684812 - invalidated: false diff --git a/models/issues/TestGetParticipantIDsByIssue/review.yml b/models/issues/TestGetParticipantIDsByIssue/review.yml deleted file mode 100644 index 2b6fe5706e..0000000000 --- a/models/issues/TestGetParticipantIDsByIssue/review.yml +++ /dev/null @@ -1,7 +0,0 @@ -- id: 1001 - type: 0 # Pending review - reviewer_id: 10 - issue_id: 1 - content: "Pending review for issue 1" - updated_unix: 946684810 - created_unix: 946684810 diff --git a/models/issues/comment.go b/models/issues/comment.go index e0ea1e111d..769feba54a 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -6,14 +6,10 @@ package issues import ( - "bytes" "context" - "errors" "fmt" "html/template" - "slices" "strconv" - "strings" "unicode/utf8" "forgejo.org/models/db" @@ -22,9 +18,7 @@ import ( project_model "forgejo.org/models/project" repo_model "forgejo.org/models/repo" user_model "forgejo.org/models/user" - "forgejo.org/modules/cache" "forgejo.org/modules/container" - "forgejo.org/modules/git" "forgejo.org/modules/gitrepo" "forgejo.org/modules/json" "forgejo.org/modules/log" @@ -204,7 +198,12 @@ func (t CommentType) HasMailReplySupport() bool { } func (t CommentType) CountedAsConversation() bool { - return slices.Contains(ConversationCountedCommentType(), t) + for _, ct := range ConversationCountedCommentType() { + if t == ct { + return true + } + } + return false } // ConversationCountedCommentType returns the comment types that are counted as a conversation @@ -326,8 +325,6 @@ type Comment struct { CommitsNum int64 `xorm:"-"` IsForcePush bool `xorm:"-"` - reverseLineBlame *git.ReverseLineBlame `xorm:"-"` - // If you add new fields that might be used to store abusive content (mainly string fields), // please also add them in the CommentData struct and the corresponding constructor. } @@ -622,7 +619,7 @@ func (c *Comment) UpdateAttachments(ctx context.Context, uuids []string) error { if err != nil { return fmt.Errorf("FindRepoAttachmentsByUUID[uuids=%q,repoID=%d]: %w", uuids, c.Issue.RepoID, err) } - for i := range attachments { + for i := 0; i < len(attachments); i++ { attachments[i].IssueID = c.IssueID attachments[i].CommentID = c.ID if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil { @@ -670,6 +667,21 @@ func (c *Comment) LoadAssigneeUserAndTeam(ctx context.Context) error { return nil } +// LoadResolveDoer if comment.Type is CommentTypeCode and ResolveDoerID not zero, then load resolveDoer +func (c *Comment) LoadResolveDoer(ctx context.Context) (err error) { + if c.ResolveDoerID == 0 || c.Type != CommentTypeCode { + return nil + } + c.ResolveDoer, err = user_model.GetUserByID(ctx, c.ResolveDoerID) + if err != nil { + if user_model.IsErrUserNotExist(err) { + c.ResolveDoer = user_model.NewGhostUser() + err = nil + } + } + return err +} + // IsResolved check if an code comment is resolved func (c *Comment) IsResolved() bool { return c.ResolveDoerID != 0 && c.Type == CommentTypeCode @@ -751,89 +763,6 @@ func (c *Comment) UnsignedLine() uint64 { return uint64(c.Line) } -func (c *Comment) ResolveCurrentLine(ctx context.Context, repo *repo_model.Repository, currentHead string) (*git.ReverseLineBlame, error) { - if c.reverseLineBlame != nil { - return c.reverseLineBlame, nil - } - - // When a PR is viewed, the requirement to perform `git blame --reverse...` on every comment is a bit of a - // performance risk. To minimize this risk, cache the results relative to the requested head, so it only needs to be - // recalculated when head changes (or on cache eviction). - // - // Some performance testing was done which showed that a hot cache is much faster than the blame reverse - // operation -- 500-1000x runtime difference: - // - // - cache miss (Forgejo repo) took 7,690,574 ns - // - cache miss (~1000 commit repo) took 1,671,223 ns - // - cache hit (in-memory adapter) took 3,710 ns - // - cache hit (redis adapter) took 77,311 ns - resolveJSON, err := cache.GetString(fmt.Sprintf("comment.Resolve;ID=%d;HEAD=%s", c.ID, currentHead), func() (string, error) { - gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) - if err != nil { - return "", fmt.Errorf("failed to open repo: %w", err) - } - defer closer.Close() - - var reverseBlame *git.ReverseLineBlame - if c.Line > 0 { - var err error - reverseBlame, err = gitRepo.ReverseLineBlame(c.CommitSHA, c.TreePath, c.UnsignedLine(), currentHead) - if err != nil { - return "", fmt.Errorf("failed to perform `git blame --reverse` to resolve current line for comment (id=%d): %w", c.ID, err) - } - } else { - // For comments on removed lines, perform a `git diff` between the last commit that the line of code was - // known to exist (which is recorded as CommitSHA) and the requested head. Then inspect the diff to verify - // that the removed line of code is present in the diff. - buffer := bytes.Buffer{} - err := git.GetRepoRawDiffForFile(gitRepo, c.CommitSHA, currentHead, git.RawDiffNormal, c.TreePath, &buffer) - if err != nil { - return "", fmt.Errorf("failed to get diff: %w", err) - } - - diff := buffer.String() - adjustedLine, err := git.FindAdjustedLineNumber(c.Patch, int64(c.UnsignedLine()), strings.NewReader(diff)) - if err != nil && errors.Is(err, git.ErrLineNotFound) { - // Line not found in the diff. Don't treat this as an error, because that would break the caching -- - // instead, return a blame where CommitID != headCommitID, which will be an indicator to callers (for - // both resolution methods) that the line of code is outdated in the diff. - reverseBlame = &git.ReverseLineBlame{ - CommitID: "", // not currentHead - LineNumber: c.UnsignedLine(), - FilePath: c.TreePath, - } - } else if err != nil { - return "", fmt.Errorf("failed in finding adjusted line number: %w", err) - } else { - reverseBlame = &git.ReverseLineBlame{ - CommitID: currentHead, - LineNumber: uint64(adjustedLine.Left), - FilePath: c.TreePath, - } - } - } - - data, err := json.Marshal(reverseBlame) - if err != nil { - return "", err - } - - return string(data), nil - }) - if err != nil { - return nil, err - } - - var reverseBlame *git.ReverseLineBlame - err = json.Unmarshal([]byte(resolveJSON), &reverseBlame) - if err != nil { - return nil, err - } - - c.reverseLineBlame = reverseBlame - return c.reverseLineBlame, nil -} - // CodeCommentLink returns the url to a comment in code func (c *Comment) CodeCommentLink(ctx context.Context) string { err := c.LoadIssue(ctx) @@ -1160,11 +1089,11 @@ func (opts FindCommentsOptions) ToConds() builder.Cond { if len(opts.TreePath) > 0 { cond = cond.And(builder.Eq{"comment.tree_path": opts.TreePath}) } - if has, value := opts.Invalidated.Get(); has { - cond = cond.And(builder.Eq{"comment.invalidated": value}) + if opts.Invalidated.Has() { + cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.Value()}) } - if has, value := opts.IsPull.Get(); has { - cond = cond.And(builder.Eq{"issue.is_pull": value}) + if opts.IsPull.Has() { + cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull.Value()}) } return cond } diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go index cc46541743..3c87a1b41a 100644 --- a/models/issues/comment_code.go +++ b/models/issues/comment_code.go @@ -7,10 +7,7 @@ import ( "context" "forgejo.org/models/db" - repo_model "forgejo.org/models/repo" user_model "forgejo.org/models/user" - "forgejo.org/modules/git" - "forgejo.org/modules/log" "forgejo.org/modules/markup" "forgejo.org/modules/markup/markdown" @@ -26,62 +23,33 @@ type CodeConversationsAtLine map[int64][]CodeConversation // CodeConversationsAtLineAndTreePath contains the conversations for a given TreePath and line type CodeConversationsAtLineAndTreePath map[string]CodeConversationsAtLine -func newCodeConversationsAtLineAndTreePath(ctx context.Context, comments []*Comment, repo *repo_model.Repository, headCommitID string) (CodeConversationsAtLineAndTreePath, error) { +func newCodeConversationsAtLineAndTreePath(comments []*Comment) CodeConversationsAtLineAndTreePath { tree := make(CodeConversationsAtLineAndTreePath) for _, comment := range comments { - blame, err := comment.ResolveCurrentLine(ctx, repo, headCommitID) - if err != nil { - // ResolveCurrentLine can fail in at least one known situation -- where a comment is left on a line in a - // file that is being deleted. The blame would be for the commit that deleted the file, and a reverse git - // blame won't work because the file is missing in the target sha. - log.Warn("ResolveCurrentLine failed: %s", err.Error()) - // handle gracefully -- insertComment will use the original values which may be usable - blame = nil - } else if blame.CommitID != headCommitID { - // Commit was made on a line that can't be reverse-blamed to the currently viewing head. This can happen - // because: - // - line of code was removed between the commit it was tagged on, and the head commit - // - force push on the repo caused there to be no git relationship between blame.CommitID->headCommitID - // We won't insert this comment into the comment tree because we don't know where to place it; it may appear - // when the user views a different commit in the PR, and it will always appear on the "Conversations" tab. - continue - } - tree.insertComment(comment, blame) + tree.insertComment(comment) } - return tree, nil + return tree } -func (tree CodeConversationsAtLineAndTreePath) insertComment(comment *Comment, blame *git.ReverseLineBlame) { - treePath := comment.TreePath - line := comment.Line - if blame != nil { - treePath = blame.FilePath - line = int64(blame.LineNumber) - if comment.Line < 0 { - line *= -1 - } - } - +func (tree CodeConversationsAtLineAndTreePath) insertComment(comment *Comment) { // attempt to append comment to existing conversations (i.e. list of comments belonging to the same review) - for i, conversation := range tree[treePath][line] { + for i, conversation := range tree[comment.TreePath][comment.Line] { if conversation[0].ReviewID == comment.ReviewID { - tree[treePath][line][i] = append(conversation, comment) + tree[comment.TreePath][comment.Line][i] = append(conversation, comment) return } } // no previous conversation was found at this line, create it - if tree[treePath] == nil { - tree[treePath] = make(map[int64][]CodeConversation) + if tree[comment.TreePath] == nil { + tree[comment.TreePath] = make(map[int64][]CodeConversation) } - tree[treePath][line] = append(tree[treePath][line], CodeConversation{comment}) + tree[comment.TreePath][comment.Line] = append(tree[comment.TreePath][comment.Line], CodeConversation{comment}) } -// FetchCodeConversations will return a 2d-map: ["Path"]["Line"] = List of CodeConversation (one per review) for this -// line. headCommitID will be used to reverse-blame the comment into the correct path & line for the current context -// that is being viewed. -func FetchCodeConversations(ctx context.Context, issue *Issue, doer *user_model.User, showOutdatedComments bool, headCommitID string) (CodeConversationsAtLineAndTreePath, error) { +// FetchCodeConversations will return a 2d-map: ["Path"]["Line"] = List of CodeConversation (one per review) for this line +func FetchCodeConversations(ctx context.Context, issue *Issue, doer *user_model.User, showOutdatedComments bool) (CodeConversationsAtLineAndTreePath, error) { opts := FindCommentsOptions{ Type: CommentTypeCode, IssueID: issue.ID, @@ -91,7 +59,7 @@ func FetchCodeConversations(ctx context.Context, issue *Issue, doer *user_model. return nil, err } - return newCodeConversationsAtLineAndTreePath(ctx, comments, issue.Repo, headCommitID) + return newCodeConversationsAtLineAndTreePath(comments), nil } // CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS @@ -165,7 +133,7 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu return nil, err } - readyComments := make(CommentList, 0, len(comments)) + n := 0 for _, comment := range comments { if re, ok := reviews[comment.ReviewID]; ok && re != nil { // If the review is pending only the author can see the comments (except if the review is set) @@ -175,18 +143,17 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu } comment.Review = re } - readyComments = append(readyComments, comment) - } + comments[n] = comment + n++ - if err := readyComments.LoadResolveDoers(ctx); err != nil { - return nil, err - } + if err := comment.LoadResolveDoer(ctx); err != nil { + return nil, err + } - if err := readyComments.LoadReactions(ctx, issue.Repo); err != nil { - return nil, err - } + if err := comment.LoadReactions(ctx, issue.Repo); err != nil { + return nil, err + } - for _, comment := range readyComments { var err error if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ Ctx: ctx, @@ -198,8 +165,7 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu return nil, err } } - - return readyComments, nil + return comments[:n], nil } // FetchCodeConversation fetches the code conversation of a given comment (same review, treePath and line number) diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index b218f11dfa..9b502d1c91 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -5,7 +5,6 @@ package issues import ( "context" - "errors" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" @@ -52,9 +51,32 @@ func (comments CommentList) loadLabels(ctx context.Context) error { } labelIDs := comments.getLabelIDs() - commentLabels, err := db.GetByIDs(ctx, "id", labelIDs, &Label{}) - if err != nil { - return err + commentLabels := make(map[int64]*Label, len(labelIDs)) + left := len(labelIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("id", labelIDs[:limit]). + Rows(new(Label)) + if err != nil { + return err + } + + for rows.Next() { + var label Label + err = rows.Scan(&label) + if err != nil { + _ = rows.Close() + return err + } + commentLabels[label.ID] = &label + } + _ = rows.Close() + left -= limit + labelIDs = labelIDs[limit:] } for _, comment := range comments { @@ -79,9 +101,21 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { return nil } - milestones, err := db.GetByIDs(ctx, "id", milestoneIDs, &Milestone{}) - if err != nil { - return err + milestones := make(map[int64]*Milestone, len(milestoneIDs)) + left := len(milestoneIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + err := db.GetEngine(ctx). + In("id", milestoneIDs[:limit]). + Find(&milestones) + if err != nil { + return err + } + left -= limit + milestoneIDs = milestoneIDs[limit:] } for _, comment := range comments { @@ -106,9 +140,21 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { return nil } - milestones, err := db.GetByIDs(ctx, "id", milestoneIDs, &Milestone{}) - if err != nil { - return err + milestones := make(map[int64]*Milestone, len(milestoneIDs)) + left := len(milestoneIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + err := db.GetEngine(ctx). + In("id", milestoneIDs[:limit]). + Find(&milestones) + if err != nil { + return err + } + left -= limit + milestoneIDs = milestoneIDs[limit:] } for _, comment := range comments { @@ -129,9 +175,34 @@ func (comments CommentList) loadAssignees(ctx context.Context) error { } assigneeIDs := comments.getAssigneeIDs() - assignees, err := db.GetByIDs(ctx, "id", assigneeIDs, &user_model.User{}) - if err != nil { - return err + assignees := make(map[int64]*user_model.User, len(assigneeIDs)) + left := len(assigneeIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("id", assigneeIDs[:limit]). + Rows(new(user_model.User)) + if err != nil { + return err + } + + for rows.Next() { + var user user_model.User + err = rows.Scan(&user) + if err != nil { + rows.Close() + return err + } + + assignees[user.ID] = &user + } + _ = rows.Close() + + left -= limit + assigneeIDs = assigneeIDs[limit:] } for _, comment := range comments { @@ -172,9 +243,34 @@ func (comments CommentList) LoadIssues(ctx context.Context) error { } issueIDs := comments.getIssueIDs() - issues, err := db.GetByIDs(ctx, "id", issueIDs, &Issue{}) - if err != nil { - return err + issues := make(map[int64]*Issue, len(issueIDs)) + left := len(issueIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("id", issueIDs[:limit]). + Rows(new(Issue)) + if err != nil { + return err + } + + for rows.Next() { + var issue Issue + err = rows.Scan(&issue) + if err != nil { + rows.Close() + return err + } + + issues[issue.ID] = &issue + } + _ = rows.Close() + + left -= limit + issueIDs = issueIDs[limit:] } for _, comment := range comments { @@ -199,10 +295,36 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error { return nil } + e := db.GetEngine(ctx) issueIDs := comments.getDependentIssueIDs() - issues, err := db.GetByIDs(ctx, "id", issueIDs, &Issue{}) - if err != nil { - return err + issues := make(map[int64]*Issue, len(issueIDs)) + left := len(issueIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := e. + In("id", issueIDs[:limit]). + Rows(new(Issue)) + if err != nil { + return err + } + + for rows.Next() { + var issue Issue + err = rows.Scan(&issue) + if err != nil { + _ = rows.Close() + return err + } + + issues[issue.ID] = &issue + } + _ = rows.Close() + + left -= limit + issueIDs = issueIDs[limit:] } for _, comment := range comments { @@ -253,10 +375,34 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) { return nil } + attachments := make(map[int64][]*repo_model.Attachment, len(comments)) commentsIDs := comments.getAttachmentCommentIDs() - attachments, err := db.GetByFieldIn(ctx, "comment_id", commentsIDs, &repo_model.Attachment{}) - if err != nil { - return err + left := len(commentsIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("comment_id", commentsIDs[:limit]). + Rows(new(repo_model.Attachment)) + if err != nil { + return err + } + + for rows.Next() { + var attachment repo_model.Attachment + err = rows.Scan(&attachment) + if err != nil { + _ = rows.Close() + return err + } + attachments[attachment.CommentID] = append(attachments[attachment.CommentID], &attachment) + } + + _ = rows.Close() + left -= limit + commentsIDs = commentsIDs[limit:] } for _, comment := range comments { @@ -265,91 +411,13 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) { return nil } -func (comments CommentList) LoadResolveDoers(ctx context.Context) (err error) { - relevant := func(c *Comment) bool { - return c.ResolveDoerID != 0 && c.Type == CommentTypeCode - } - userIDs := make(container.Set[int64]) - for _, comment := range comments { - if relevant(comment) { - userIDs.Add(comment.ResolveDoerID) - } - } - - if len(userIDs) == 0 { - return nil - } - - userMap := make(map[int64]*user_model.User) - users, err := user_model.GetUsersByIDs(ctx, userIDs.Slice()) - if err != nil { - return err - } - for _, user := range users { - userMap[user.ID] = user - } - - for _, comment := range comments { - if !relevant(comment) { - continue - } - resolveDoer, ok := userMap[comment.ResolveDoerID] - if !ok { - comment.ResolveDoer = user_model.NewGhostUser() - } else { - comment.ResolveDoer = resolveDoer - } - } - - return nil -} - -func (comments CommentList) LoadReactions(ctx context.Context, repo *repo_model.Repository) (err error) { - loadIssueID := int64(0) - loadCommentIDs := make([]int64, 0, len(comments)) - - for _, comment := range comments { - if loadIssueID == 0 { - loadIssueID = comment.IssueID - } else if loadIssueID != comment.IssueID { - return errors.New("unable to load reactions from comments on different issues than each other") - } - if comment.Reactions == nil { - loadCommentIDs = append(loadCommentIDs, comment.ID) - } - } - - if loadIssueID == 0 { - return nil - } - - reactions, err := getReactionsForComments(ctx, loadIssueID, loadCommentIDs) - if err != nil { - return err - } - - allReactions := make(ReactionList, 0, len(reactions)) - for _, comment := range comments { - if comment.Reactions == nil { - comment.Reactions = reactions[comment.ID] - allReactions = append(allReactions, comment.Reactions...) - } - } - - if _, err := allReactions.LoadUsers(ctx, repo); err != nil { - return err - } - - return nil -} - func (comments CommentList) getReviewIDs() []int64 { return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { return comment.ReviewID, comment.ReviewID > 0 }) } -func (comments CommentList) LoadReviews(ctx context.Context) error { +func (comments CommentList) loadReviews(ctx context.Context) error { if len(comments) == 0 { return nil } @@ -408,7 +476,7 @@ func (comments CommentList) LoadAttributes(ctx context.Context) (err error) { return err } - if err = comments.LoadReviews(ctx); err != nil { + if err = comments.loadReviews(ctx); err != nil { return err } diff --git a/models/issues/comment_list_test.go b/models/issues/comment_list_test.go index 12a9144722..062a710b84 100644 --- a/models/issues/comment_list_test.go +++ b/models/issues/comment_list_test.go @@ -84,111 +84,3 @@ func TestCommentListLoadUser(t *testing.T) { }) } } - -func TestCommentListLoadResolveDoers(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - issue := unittest.AssertExistsAndLoadBean(t, &Issue{}) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) - doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - empty := CommentList{} - require.NoError(t, empty.LoadResolveDoers(t.Context())) - - comment1, err := CreateComment(db.DefaultContext, &CreateCommentOptions{ - Type: CommentTypeCode, - Doer: doer, - Repo: repo, - Issue: issue, - Content: "Hello", - }) - require.NoError(t, err) - require.NoError(t, MarkConversation(t.Context(), comment1, doer, true)) - comment1 = unittest.AssertExistsAndLoadBean(t, &Comment{ID: comment1.ID}) // reload after change - comment1List := CommentList{comment1} - require.NoError(t, comment1List.LoadResolveDoers(t.Context())) - require.NotNil(t, comment1.ResolveDoer) - assert.Equal(t, doer.ID, comment1.ResolveDoer.ID) - - comment2, err := CreateComment(db.DefaultContext, &CreateCommentOptions{ - Type: CommentTypeCode, - Doer: doer, - Repo: repo, - Issue: issue, - Content: "Hello again", - }) - require.NoError(t, err) - require.NoError(t, MarkConversation(t.Context(), comment2, user_model.NewGhostUser(), true)) - - // Reload for fresh objects - comment1 = unittest.AssertExistsAndLoadBean(t, &Comment{ID: comment1.ID}) - comment2 = unittest.AssertExistsAndLoadBean(t, &Comment{ID: comment2.ID}) - - comment2List := CommentList{comment1, comment2} - require.NoError(t, comment2List.LoadResolveDoers(t.Context())) - require.NotNil(t, comment1.ResolveDoer) - assert.Equal(t, doer.ID, comment1.ResolveDoer.ID) - require.NotNil(t, comment2.ResolveDoer) - assert.EqualValues(t, -1, comment2.ResolveDoer.ID) -} - -func TestCommentListLoadReactions(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - issue := unittest.AssertExistsAndLoadBean(t, &Issue{}) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) - doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - - empty := CommentList{} - require.NoError(t, empty.LoadReactions(t.Context(), repo)) - - comment1, err := CreateComment(db.DefaultContext, &CreateCommentOptions{ - Type: CommentTypeCode, - Doer: doer, - Repo: repo, - Issue: issue, - Content: "Hello", - }) - require.NoError(t, err) - _, err = CreateReaction(t.Context(), &ReactionOptions{ - Type: "eyes", - DoerID: doer.ID, - IssueID: issue.ID, - CommentID: comment1.ID, - }) - require.NoError(t, err) - - comment1 = unittest.AssertExistsAndLoadBean(t, &Comment{ID: comment1.ID}) // reload after change - comment1List := CommentList{comment1} - require.NoError(t, comment1List.LoadReactions(t.Context(), repo)) - require.Len(t, comment1.Reactions, 1) - assert.Equal(t, "eyes", comment1.Reactions[0].Type) - assert.NotNil(t, comment1.Reactions[0].User) - - comment2, err := CreateComment(db.DefaultContext, &CreateCommentOptions{ - Type: CommentTypeCode, - Doer: doer, - Repo: repo, - Issue: issue, - Content: "Hello again", - }) - require.NoError(t, err) - _, err = CreateReaction(t.Context(), &ReactionOptions{ - Type: "rocket", - DoerID: doer.ID, - IssueID: issue.ID, - CommentID: comment2.ID, - }) - require.NoError(t, err) - - // Reload for fresh objects - comment1 = unittest.AssertExistsAndLoadBean(t, &Comment{ID: comment1.ID}) - comment2 = unittest.AssertExistsAndLoadBean(t, &Comment{ID: comment2.ID}) - - comment2List := CommentList{comment1, comment2} - require.NoError(t, comment2List.LoadReactions(t.Context(), repo)) - require.Len(t, comment1.Reactions, 1) - require.Len(t, comment2.Reactions, 1) - assert.Equal(t, "rocket", comment2.Reactions[0].Type) - assert.NotNil(t, comment2.Reactions[0].User) -} diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go index ea59d4f215..c7adf6f62e 100644 --- a/models/issues/comment_test.go +++ b/models/issues/comment_test.go @@ -52,32 +52,15 @@ func TestFetchCodeConversations(t *testing.T) { issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - _, err := issues_model.CreateReaction(t.Context(), &issues_model.ReactionOptions{ - Type: "eyes", - DoerID: 2, - IssueID: issue.ID, - CommentID: 4, - }) + res, err := issues_model.FetchCodeConversations(db.DefaultContext, issue, user, false) require.NoError(t, err) - require.NoError(t, issues_model.MarkConversation(t.Context(), - unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4}), - user, true)) - - res, err := issues_model.FetchCodeConversations(db.DefaultContext, issue, user, false, "") - require.NoError(t, err) - require.Contains(t, res, "README.md") - require.Contains(t, res["README.md"], int64(4)) - require.Len(t, res["README.md"][4], 1) - require.Len(t, res["README.md"][4][0], 1) - comment := res["README.md"][4][0][0] - assert.Equal(t, int64(4), comment.ID) - assert.NotNil(t, comment.ResolveDoer) - require.Len(t, comment.Reactions, 1) - r := comment.Reactions[0] - assert.NotNil(t, r.User) + assert.Contains(t, res, "README.md") + assert.Contains(t, res["README.md"], int64(4)) + assert.Len(t, res["README.md"][4], 1) + assert.Equal(t, int64(4), res["README.md"][4][0][0].ID) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - res, err = issues_model.FetchCodeConversations(db.DefaultContext, issue, user2, false, "") + res, err = issues_model.FetchCodeConversations(db.DefaultContext, issue, user2, false) require.NoError(t, err) assert.Len(t, res, 1) } diff --git a/models/issues/content_history.go b/models/issues/content_history.go index 0849686607..476c6e0f90 100644 --- a/models/issues/content_history.go +++ b/models/issues/content_history.go @@ -222,7 +222,7 @@ func GetIssueContentHistoryAndPrev(dbCtx context.Context, issueID, id int64) (hi return nil, nil, err } else if !has { log.Error("issue content history does not exist. id=%v. err=%v", id, err) - return nil, nil, ErrIssueContentHistoryNotExist{id} + return nil, nil, &ErrIssueContentHistoryNotExist{id} } prevHistory = &ContentHistory{} diff --git a/models/issues/issue.go b/models/issues/issue.go index a90686eb50..fc8794ad50 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -591,12 +591,10 @@ func GetParticipantsIDsByIssueID(ctx context.Context, issueID int64) ([]int64, e userIDs := make([]int64, 0, 5) return userIDs, db.GetEngine(ctx). Table("comment"). - Cols("`comment`.poster_id"). - Where("`comment`.issue_id = ?", issueID). - And("`comment`.type in (?,?,?)", CommentTypeComment, CommentTypeCode, CommentTypeReview). - And("`review`.type is null or `review`.type != ?", ReviewTypePending). - Join("LEFT", "`review`", "`review`.id = `comment`.review_id"). - Distinct("`comment`.poster_id"). + Cols("poster_id"). + Where("issue_id = ?", issueID). + And("type in (?,?,?)", CommentTypeComment, CommentTypeCode, CommentTypeReview). + Distinct("poster_id"). Find(&userIDs) } @@ -625,11 +623,9 @@ func (issue *Issue) GetParticipantIDsByIssue(ctx context.Context) ([]int64, erro if err := db.GetEngine(ctx).Table("comment").Cols("poster_id"). Where("`comment`.issue_id = ?", issue.ID). And("`comment`.type in (?,?,?)", CommentTypeComment, CommentTypeCode, CommentTypeReview). - And("`review`.type != ?", ReviewTypePending). And("`user`.is_active = ?", true). And("`user`.prohibit_login = ?", false). Join("INNER", "`user`", "`user`.id = `comment`.poster_id"). - Join("INNER", "`review`", "`review`.reviewer_id = `user`.id"). Distinct("poster_id"). Find(&userIDs); err != nil { return nil, fmt.Errorf("get poster IDs: %w", err) diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index e4fd9eef2b..5a02baa428 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -6,7 +6,6 @@ package issues import ( "context" "fmt" - "slices" "forgejo.org/models/db" project_model "forgejo.org/models/project" @@ -41,9 +40,21 @@ func (issues IssueList) LoadRepositories(ctx context.Context) (repo_model.Reposi } repoIDs := issues.getRepoIDs() - repoMaps, err := db.GetByIDs(ctx, "id", repoIDs, &repo_model.Repository{}) - if err != nil { - return nil, fmt.Errorf("find repository: %w", err) + repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs)) + left := len(repoIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + err := db.GetEngine(ctx). + In("id", repoIDs[:limit]). + Find(&repoMaps) + if err != nil { + return nil, fmt.Errorf("find repository: %w", err) + } + left -= limit + repoIDs = repoIDs[limit:] } for _, issue := range issues { @@ -85,9 +96,21 @@ func (issues IssueList) LoadPosters(ctx context.Context) error { } func getPostersByIDs(ctx context.Context, posterIDs []int64) (map[int64]*user_model.User, error) { - posterMaps, err := db.GetByIDs(ctx, "id", posterIDs, &user_model.User{}) - if err != nil { - return nil, err + posterMaps := make(map[int64]*user_model.User, len(posterIDs)) + left := len(posterIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + err := db.GetEngine(ctx). + In("id", posterIDs[:limit]). + Find(&posterMaps) + if err != nil { + return nil, err + } + left -= limit + posterIDs = posterIDs[limit:] } return posterMaps, nil } @@ -112,15 +135,21 @@ func (issues IssueList) LoadLabels(ctx context.Context) error { issueLabels := make(map[int64][]*Label, len(issues)*3) issueIDs := issues.getIssueIDs() - for issueIDChunk := range slices.Chunk(issueIDs, db.DefaultMaxInSize) { + left := len(issueIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } rows, err := db.GetEngine(ctx).Table("label"). Join("LEFT", "issue_label", "issue_label.label_id = label.id"). - In("issue_label.issue_id", issueIDChunk). + In("issue_label.issue_id", issueIDs[:limit]). Asc("label.name"). Rows(new(LabelIssue)) if err != nil { return err } + for rows.Next() { var labelIssue LabelIssue err = rows.Scan(&labelIssue) @@ -137,6 +166,8 @@ func (issues IssueList) LoadLabels(ctx context.Context) error { if err1 := rows.Close(); err1 != nil { return fmt.Errorf("IssueList.LoadLabels: Close: %w", err1) } + left -= limit + issueIDs = issueIDs[limit:] } for _, issue := range issues { @@ -158,9 +189,21 @@ func (issues IssueList) LoadMilestones(ctx context.Context) error { return nil } - milestoneMaps, err := db.GetByIDs(ctx, "id", milestoneIDs, &Milestone{}) - if err != nil { - return err + milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) + left := len(milestoneIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + err := db.GetEngine(ctx). + In("id", milestoneIDs[:limit]). + Find(&milestoneMaps) + if err != nil { + return err + } + left -= limit + milestoneIDs = milestoneIDs[limit:] } for _, issue := range issues { @@ -173,19 +216,25 @@ func (issues IssueList) LoadMilestones(ctx context.Context) error { func (issues IssueList) LoadProjects(ctx context.Context) error { issueIDs := issues.getIssueIDs() projectMaps := make(map[int64]*project_model.Project, len(issues)) + left := len(issueIDs) type projectWithIssueID struct { *project_model.Project `xorm:"extends"` IssueID int64 } - for issueIDChunk := range slices.Chunk(issueIDs, db.DefaultMaxInSize) { - projects := make([]*projectWithIssueID, 0, len(issueIDChunk)) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + + projects := make([]*projectWithIssueID, 0, limit) err := db.GetEngine(ctx). Table("project"). Select("project.*, project_issue.issue_id"). Join("INNER", "project_issue", "project.id = project_issue.project_id"). - In("project_issue.issue_id", issueIDChunk). + In("project_issue.issue_id", issueIDs[:limit]). Find(&projects) if err != nil { return err @@ -193,6 +242,8 @@ func (issues IssueList) LoadProjects(ctx context.Context) error { for _, project := range projects { projectMaps[project.IssueID] = project.Project } + left -= limit + issueIDs = issueIDs[limit:] } for _, issue := range issues { @@ -213,10 +264,15 @@ func (issues IssueList) LoadAssignees(ctx context.Context) error { assignees := make(map[int64][]*user_model.User, len(issues)) issueIDs := issues.getIssueIDs() - for issueIDChunk := range slices.Chunk(issueIDs, db.DefaultMaxInSize) { + left := len(issueIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } rows, err := db.GetEngine(ctx).Table("issue_assignees"). Join("INNER", "`user`", "`user`.id = `issue_assignees`.assignee_id"). - In("`issue_assignees`.issue_id", issueIDChunk).OrderBy(user_model.GetOrderByName()). + In("`issue_assignees`.issue_id", issueIDs[:limit]).OrderBy(user_model.GetOrderByName()). Rows(new(AssigneeIssue)) if err != nil { return err @@ -237,6 +293,8 @@ func (issues IssueList) LoadAssignees(ctx context.Context) error { if err1 := rows.Close(); err1 != nil { return fmt.Errorf("IssueList.loadAssignees: Close: %w", err1) } + left -= limit + issueIDs = issueIDs[limit:] } for _, issue := range issues { @@ -266,9 +324,36 @@ func (issues IssueList) LoadPullRequests(ctx context.Context) error { return nil } - pullRequestMaps, err := db.GetByIDs(ctx, "issue_id", issuesIDs, &PullRequest{}) - if err != nil { - return err + pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs)) + left := len(issuesIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("issue_id", issuesIDs[:limit]). + Rows(new(PullRequest)) + if err != nil { + return err + } + + for rows.Next() { + var pr PullRequest + err = rows.Scan(&pr) + if err != nil { + if err1 := rows.Close(); err1 != nil { + return fmt.Errorf("IssueList.loadPullRequests: Close: %w", err1) + } + return err + } + pullRequestMaps[pr.IssueID] = &pr + } + if err1 := rows.Close(); err1 != nil { + return fmt.Errorf("IssueList.loadPullRequests: Close: %w", err1) + } + left -= limit + issuesIDs = issuesIDs[limit:] } for _, issue := range issues { @@ -286,10 +371,37 @@ func (issues IssueList) LoadAttachments(ctx context.Context) (err error) { return nil } + attachments := make(map[int64][]*repo_model.Attachment, len(issues)) issuesIDs := issues.getIssueIDs() - attachments, err := db.GetByFieldIn(ctx, "issue_id", issuesIDs, &repo_model.Attachment{}) - if err != nil { - return err + left := len(issuesIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("issue_id", issuesIDs[:limit]). + Rows(new(repo_model.Attachment)) + if err != nil { + return err + } + + for rows.Next() { + var attachment repo_model.Attachment + err = rows.Scan(&attachment) + if err != nil { + if err1 := rows.Close(); err1 != nil { + return fmt.Errorf("IssueList.loadAttachments: Close: %w", err1) + } + return err + } + attachments[attachment.IssueID] = append(attachments[attachment.IssueID], &attachment) + } + if err1 := rows.Close(); err1 != nil { + return fmt.Errorf("IssueList.loadAttachments: Close: %w", err1) + } + left -= limit + issuesIDs = issuesIDs[limit:] } for _, issue := range issues { @@ -306,10 +418,15 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er comments := make(map[int64][]*Comment, len(issues)) issuesIDs := issues.getIssueIDs() - for issueIDChunk := range slices.Chunk(issuesIDs, db.DefaultMaxInSize) { + left := len(issuesIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } rows, err := db.GetEngine(ctx).Table("comment"). Join("INNER", "issue", "issue.id = comment.issue_id"). - In("issue.id", issueIDChunk). + In("issue.id", issuesIDs[:limit]). Where(cond). Rows(new(Comment)) if err != nil { @@ -330,6 +447,8 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er if err1 := rows.Close(); err1 != nil { return fmt.Errorf("IssueList.loadComments: Close: %w", err1) } + left -= limit + issuesIDs = issuesIDs[limit:] } for _, issue := range issues { @@ -365,12 +484,18 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) { } } - for idChunk := range slices.Chunk(ids, db.DefaultMaxInSize) { + left := len(ids) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + // select issue_id, sum(time) from tracked_time where issue_id in () group by issue_id rows, err := db.GetEngine(ctx).Table("tracked_time"). Where("deleted = ?", false). Select("issue_id, sum(time) as time"). - In("issue_id", idChunk). + In("issue_id", ids[:limit]). GroupBy("issue_id"). Rows(new(totalTimesByIssue)) if err != nil { @@ -391,6 +516,8 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) { if err1 := rows.Close(); err1 != nil { return fmt.Errorf("IssueList.loadTotalTrackedTimes: Close: %w", err1) } + left -= limit + ids = ids[limit:] } for _, issue := range issues { diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index a8644758ce..fbfcd3529a 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -226,8 +226,8 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) { applyRepoConditions(sess, opts) - if has, value := opts.IsClosed.Get(); has { - sess.And("issue.is_closed=?", value) + if opts.IsClosed.Has() { + sess.And("issue.is_closed=?", opts.IsClosed.Value()) } if opts.AssigneeID > 0 { @@ -269,18 +269,18 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) { applyProjectColumnCondition(sess, opts) - if has, value := opts.IsPull.Get(); has { - sess.And("issue.is_pull=?", value) + if opts.IsPull.Has() { + sess.And("issue.is_pull=?", opts.IsPull.Value()) } - if has, value := opts.IsArchived.Get(); has { - sess.And(builder.Eq{"repository.is_archived": value}) + if opts.IsArchived.Has() { + sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.Value()}) } applyLabelsCondition(sess, opts) if opts.User != nil { - cond := issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.ValueOrZeroValue()) + cond := issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value()) // If AllPublic was set, then also consider all issues in public // repositories in addition to the private repositories the user has access // to. diff --git a/models/issues/issue_stats.go b/models/issues/issue_stats.go index 03660803a4..eee8760b9f 100644 --- a/models/issues/issue_stats.go +++ b/models/issues/issue_stats.go @@ -94,7 +94,10 @@ func GetIssueStats(ctx context.Context, opts *IssuesOptions) (*IssueStats, error // ids in a temporary table and join from them. accum := &IssueStats{} for i := 0; i < len(opts.IssueIDs); { - chunk := min(i+MaxQueryParameters, len(opts.IssueIDs)) + chunk := i + MaxQueryParameters + if chunk > len(opts.IssueIDs) { + chunk = len(opts.IssueIDs) + } stats, err := getIssueStatsChunk(ctx, opts, opts.IssueIDs[i:chunk]) if err != nil { return nil, err @@ -177,8 +180,8 @@ func applyIssuesOptions(sess *xorm.Session, opts *IssuesOptions, issueIDs []int6 applyReviewedCondition(sess, opts.ReviewedID) } - if has, value := opts.IsPull.Get(); has { - sess.And("issue.is_pull=?", value) + if opts.IsPull.Has() { + sess.And("issue.is_pull=?", opts.IsPull.Value()) } return sess diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go index 0c5da6a2aa..2059c5013a 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -5,7 +5,6 @@ package issues_test import ( "fmt" - "slices" "sort" "sync" "testing" @@ -86,7 +85,6 @@ func TestGetIssuesByIDs(t *testing.T) { } func TestGetParticipantIDsByIssue(t *testing.T) { - defer unittest.OverrideFixtures("models/issues/TestGetParticipantIDsByIssue")() require.NoError(t, unittest.PrepareTestDatabase()) checkParticipants := func(issueID int64, userIDs []int) { @@ -109,7 +107,6 @@ func TestGetParticipantIDsByIssue(t *testing.T) { // User 2 only labeled issue1 (see fixtures/comment.yml) // Users 3 and 5 made actual comments (see fixtures/comment.yml) // User 3 is inactive, thus not active participant - // User 10 has a pending review, thus not an active participant, yet (see TestGetParticipantIDsByIssue/comment.yml) checkParticipants(1, []int{1, 5}) } @@ -312,7 +309,7 @@ func TestIssue_ResolveMentions(t *testing.T) { for i, user := range resolved { ids[i] = user.ID } - slices.Sort(ids) + sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) assert.Equal(t, expected, ids) } @@ -339,7 +336,7 @@ func TestResourceIndex(t *testing.T) { require.NoError(t, err) var wg sync.WaitGroup - for i := range 100 { + for i := 0; i < 100; i++ { wg.Add(1) t.Run(fmt.Sprintf("issue %d", i+1), func(t *testing.T) { t.Parallel() @@ -370,7 +367,7 @@ func TestCorrectIssueStats(t *testing.T) { issueAmount := issues_model.MaxQueryParameters + 10 var wg sync.WaitGroup - for i := range issueAmount { + for i := 0; i < issueAmount; i++ { wg.Add(1) go func(i int) { testInsertIssue(t, fmt.Sprintf("Issue %d", i+1), "Bugs are nasty", 0) diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 35f69e3a0b..22e6fcb8d4 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -244,7 +244,7 @@ func UpdateIssueAttachments(ctx context.Context, issue *Issue, uuids []string) ( if err != nil { return fmt.Errorf("FindRepoAttachmentsByUUID[uuids=%q,repoID=%d]: %w", uuids, issue.RepoID, err) } - for i := range attachments { + for i := 0; i < len(attachments); i++ { attachments[i].IssueID = issue.ID if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil { return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err) diff --git a/models/issues/label.go b/models/issues/label.go index 29ac82cfa3..4a5c3b4bfe 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -150,7 +150,6 @@ func (l *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, curr for i, curSel := range currentSelectedLabels { if curSel == l.ID { labelSelected = true - l.IsExcluded = false } else if -curSel == l.ID { labelSelected = true l.IsExcluded = true @@ -162,10 +161,9 @@ func (l *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64, curr } } - if !labelSelected || l.IsExcluded { + if !labelSelected { labelQuerySlice = append(labelQuerySlice, l.ID) } - l.IsSelected = labelSelected // Sort and deduplicate the ids to avoid the crawlers asking for the diff --git a/models/issues/label_test.go b/models/issues/label_test.go index f52c20e4a2..d3e6146fc9 100644 --- a/models/issues/label_test.go +++ b/models/issues/label_test.go @@ -32,27 +32,18 @@ func TestLabel_LoadSelectedLabelsAfterClick(t *testing.T) { // First test : with negative and scope label.LoadSelectedLabelsAfterClick([]int64{1, -8}, []string{"", "scope"}) - // Flips to positive to include after click - assert.Equal(t, "1,8", label.QueryString) - assert.True(t, label.IsSelected) - assert.True(t, label.IsExcluded) - - // Second test : with positive and scope - label.LoadSelectedLabelsAfterClick([]int64{1, 8}, []string{"", "scope"}) assert.Equal(t, "1", label.QueryString) assert.True(t, label.IsSelected) - // Third test : with duplicates + // Second test : with duplicates label.LoadSelectedLabelsAfterClick([]int64{1, 7, 1, 7, 7}, []string{"", "scope", "", "scope", "scope"}) assert.Equal(t, "1,8", label.QueryString) assert.False(t, label.IsSelected) - assert.False(t, label.IsExcluded) - // Fourth test : empty set + // Third test : empty set label.LoadSelectedLabelsAfterClick([]int64{}, []string{}) assert.False(t, label.IsSelected) assert.Equal(t, "8", label.QueryString) - assert.False(t, label.IsExcluded) } func TestLabel_ExclusiveScope(t *testing.T) { diff --git a/models/issues/milestone.go b/models/issues/milestone.go index d5b3c2c293..d718decb18 100644 --- a/models/issues/milestone.go +++ b/models/issues/milestone.go @@ -378,8 +378,8 @@ func doRecalcMilestone(ctx context.Context, cond builder.Cond, updateTimestamp o }), ). Where(cond) - if has, value := updateTimestamp.Get(); has { - sess.SetExpr("updated_unix", value).NoAutoTime() + if updateTimestamp.Has() { + sess.SetExpr("updated_unix", updateTimestamp.Value()).NoAutoTime() } _, err := sess.Update(&Milestone{}) if err != nil { diff --git a/models/issues/milestone_list.go b/models/issues/milestone_list.go index 6ef3385f7c..e2079fb324 100644 --- a/models/issues/milestone_list.go +++ b/models/issues/milestone_list.go @@ -40,8 +40,8 @@ func (opts FindMilestoneOptions) ToConds() builder.Cond { if opts.RepoID != 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) } - if has, value := opts.IsClosed.Get(); has { - cond = cond.And(builder.Eq{"is_closed": value}) + if opts.IsClosed.Has() { + cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.Value()}) } if opts.RepoCond != nil && opts.RepoCond.IsValid() { cond = cond.And(builder.In("repo_id", builder.Select("id").From("repository").Where(opts.RepoCond))) diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index 10e39c6a49..38325e181f 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -27,8 +27,6 @@ type PullRequestsOptions struct { Labels []int64 MilestoneID int64 PosterID int64 - BaseBranch string - HeadBranch string } func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullRequestsOptions) *xorm.Session { @@ -53,14 +51,6 @@ func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullR sess.And("issue.poster_id=?", opts.PosterID) } - if opts.BaseBranch != "" { - sess.And("pull_request.base_branch=?", opts.BaseBranch) - } - - if opts.HeadBranch != "" { - sess.And("pull_request.head_branch=?", opts.HeadBranch) - } - return sess } diff --git a/models/issues/reaction.go b/models/issues/reaction.go index 21975c6b00..522040c022 100644 --- a/models/issues/reaction.go +++ b/models/issues/reaction.go @@ -7,7 +7,6 @@ import ( "bytes" "context" "fmt" - "slices" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" @@ -177,34 +176,6 @@ func FindReactions(ctx context.Context, opts FindReactionsOptions) (ReactionList return reactions, count, err } -func getReactionsForComments(ctx context.Context, issueID int64, commentIDs []int64) (map[int64]ReactionList, error) { - reactions := make(map[int64]ReactionList, len(commentIDs)) - - for commentIDChunk := range slices.Chunk(commentIDs, db.DefaultMaxInSize) { - rows, err := db.GetEngine(ctx). - Where(builder.Eq{"issue_id": issueID}). - In("reaction.`type`", setting.UI.Reactions). - In("comment_id", commentIDChunk). - Rows(&Reaction{}) - if err != nil { - return nil, err - } - - for rows.Next() { - var reaction Reaction - err = rows.Scan(&reaction) - if err != nil { - _ = rows.Close() - return nil, err - } - reactions[reaction.CommentID] = append(reactions[reaction.CommentID], &reaction) - } - - _ = rows.Close() - } - return reactions, nil -} - func createReaction(ctx context.Context, opts *ReactionOptions) (*Reaction, error) { reaction := &Reaction{ Type: opts.Type, diff --git a/models/issues/review.go b/models/issues/review.go index 6ff36615db..5370117a81 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -457,11 +457,6 @@ func SubmitReview(ctx context.Context, doer *user_model.User, issue *Issue, revi if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil { return nil, nil, err } - // delete previous review requests from the same user - reviewCond := builder.Eq{"reviewer_id": doer.ID, "issue_id": issue.ID} - if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil { - return nil, nil, err - } } review.Official = official @@ -516,14 +511,10 @@ func SubmitReview(ctx context.Context, doer *user_model.User, issue *Issue, revi // GetReviewByIssueIDAndUserID get the latest review of reviewer for a pull request func GetReviewByIssueIDAndUserID(ctx context.Context, issueID, userID int64) (*Review, error) { - return GetReviewByIssueIDUserIDAndTypes(ctx, issueID, userID, []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest}) -} - -func GetReviewByIssueIDUserIDAndTypes(ctx context.Context, issueID, userID int64, types []ReviewType) (*Review, error) { review := new(Review) has, err := db.GetEngine(ctx).Where( - builder.In("type", types). + builder.In("type", ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest). And(builder.Eq{"issue_id": issueID, "reviewer_id": userID, "original_author_id": 0})). Desc("id"). Get(review) @@ -716,12 +707,12 @@ func RemoveReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user } defer committer.Close() - review, err := GetReviewByIssueIDUserIDAndTypes(ctx, issue.ID, reviewer.ID, []ReviewType{ReviewTypeRequest}) + review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID) if err != nil && !IsErrReviewNotExist(err) { return nil, err } - if review == nil { + if review == nil || review.Type != ReviewTypeRequest { return nil, nil } diff --git a/models/issues/review_list.go b/models/issues/review_list.go index 878ceac9ce..9eca4a9b4b 100644 --- a/models/issues/review_list.go +++ b/models/issues/review_list.go @@ -20,7 +20,7 @@ type ReviewList []*Review // LoadReviewers loads reviewers func (reviews ReviewList) LoadReviewers(ctx context.Context) error { reviewerIDs := make([]int64, len(reviews)) - for i := range reviews { + for i := 0; i < len(reviews); i++ { reviewerIDs[i] = reviews[i].ReviewerID } reviewers, err := user_model.GetPossibleUserByIDs(ctx, reviewerIDs) @@ -113,8 +113,8 @@ func (opts *FindReviewOptions) toCond() builder.Cond { if opts.OfficialOnly { cond = cond.And(builder.Eq{"official": true}) } - if has, value := opts.Dismissed.Get(); has { - cond = cond.And(builder.Eq{"dismissed": value}) + if opts.Dismissed.Has() { + cond = cond.And(builder.Eq{"dismissed": opts.Dismissed.Value()}) } return cond } diff --git a/models/issues/review_test.go b/models/issues/review_test.go index e035be617b..bdeaae5ea3 100644 --- a/models/issues/review_test.go +++ b/models/issues/review_test.go @@ -321,82 +321,6 @@ func TestAddReviewRequest(t *testing.T) { assert.True(t, issues_model.IsErrReviewRequestOnClosedPR(err)) } -func TestSubmitPendingReviewDeletesReviewRequest(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) - require.NoError(t, pull.LoadIssue(db.DefaultContext)) - issue := pull.Issue - require.NoError(t, issue.LoadRepo(db.DefaultContext)) - reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - reviewRequest, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{ - Issue: issue, - Reviewer: reviewer, - Type: issues_model.ReviewTypeRequest, - }) - require.NoError(t, err) - - // creating a pending review should NOT remove review requests - reviewPending, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{ - Issue: issue, - Reviewer: reviewer, - Type: issues_model.ReviewTypePending, - }) - require.NoError(t, err) - unittest.AssertExistsIf(t, true, &issues_model.Review{ID: reviewRequest.ID}) - // submitting a pending review to finish it SHOULD remove review requests - _, _, err = issues_model.SubmitReview( - db.DefaultContext, - reviewer, - issue, - issues_model.ReviewTypeReject, - "test content", - reviewPending.CommitID, - false, - []string{}, - ) - require.NoError(t, err) - unittest.AssertNotExistsBean(t, &issues_model.Review{ID: reviewRequest.ID}) -} - -// this test is for handling a state correctly that should never exist, but is representable and was -// achievable thanks to #12243 -func TestReviewRequestDeletesReviewRequestsBeforeRejectedReviews(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - sess := db.GetEngine(db.DefaultContext) - - pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) - require.NoError(t, pull.LoadIssue(db.DefaultContext)) - issue := pull.Issue - require.NoError(t, issue.LoadRepo(db.DefaultContext)) - reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) - - // this one will end up being a ReviewTypeRequest. We are initially creating it as - // ReviewTypeReject to avoid it being deleted on making the actual rejected review - reviewRequest, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{ - Issue: issue, - Reviewer: reviewer, - Type: issues_model.ReviewTypeReject, - }) - require.NoError(t, err) - // this review is an actual rejected review that somehow managed to be saved without deleting - // reviewRequest. This is a state that is representable and is/was achievable thanks to #12243 - _, err = issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{ - Issue: issue, - Reviewer: reviewer, - Type: issues_model.ReviewTypeReject, - }) - require.NoError(t, err) - reviewRequest.Type = issues_model.ReviewTypeRequest - _, err = sess.ID(reviewRequest.ID).Cols("type").Update(reviewRequest) - require.NoError(t, err) - - _, err = issues_model.RemoveReviewRequest(db.DefaultContext, issue, reviewer, doer) - require.NoError(t, err) - unittest.AssertNotExistsBean(t, &issues_model.Review{ID: reviewRequest.ID}) -} - func TestAddTeamReviewRequest(t *testing.T) { defer unittest.OverrideFixtures("models/fixtures/TestAddTeamReviewRequest")() require.NoError(t, unittest.PrepareTestDatabase()) diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go index 54173681bd..e083f6e1e8 100644 --- a/models/issues/tracked_time.go +++ b/models/issues/tracked_time.go @@ -350,7 +350,10 @@ func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed // we get the statistics in smaller chunks and get accumulates var accum int64 for i := 0; i < len(opts.IssueIDs); { - chunk := min(i+MaxQueryParameters, len(opts.IssueIDs)) + chunk := i + MaxQueryParameters + if chunk > len(opts.IssueIDs) { + chunk = len(opts.IssueIDs) + } time, err := getIssueTotalTrackedTimeChunk(ctx, opts, isClosed, opts.IssueIDs[i:chunk]) if err != nil { return 0, err @@ -376,8 +379,8 @@ func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isC } session := sumSession(opts, issueIDs) - if has, value := isClosed.Get(); has { - session = session.And("issue.is_closed = ?", value) + if isClosed.Has() { + session = session.And("issue.is_closed = ?", isClosed.Value()) } return session.SumInt(new(trackedTime), "tracked_time.time") } diff --git a/models/org_team.go b/models/org_team.go index 187ea1560d..ecda43f0a9 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -23,44 +23,34 @@ import ( "xorm.io/builder" ) -func AddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) error { - _, err := InsertTeamRepository(ctx, t, repo) - return err -} - -func InsertTeamRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (teamRepo *organization.TeamRepo, err error) { - teamRepo = &organization.TeamRepo{ - OrgID: t.OrgID, - TeamID: t.ID, - RepoID: repo.ID, - } - if _, err = db.GetEngine(ctx).Insert(teamRepo); err != nil { - return nil, err +func AddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) { + if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil { + return err } if err = organization.IncrTeamRepoNum(ctx, t.ID); err != nil { - return nil, fmt.Errorf("update team: %w", err) + return fmt.Errorf("update team: %w", err) } t.NumRepos++ if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil { - return nil, fmt.Errorf("recalculateAccesses: %w", err) + return fmt.Errorf("recalculateAccesses: %w", err) } // Make all team members watch this repo if enabled in global settings if setting.Service.AutoWatchNewRepos { if err = t.LoadMembers(ctx); err != nil { - return nil, fmt.Errorf("getMembers: %w", err) + return fmt.Errorf("getMembers: %w", err) } for _, u := range t.Members { if err = repo_model.WatchRepo(ctx, u.ID, repo.ID, true); err != nil { - return nil, fmt.Errorf("watchRepo: %w", err) + return fmt.Errorf("watchRepo: %w", err) } } } - return teamRepo, nil + return nil } // addAllRepositories adds all repositories to the team. @@ -126,7 +116,7 @@ func removeAllRepositories(ctx context.Context, t *organization.Team) (err error return err } - // Remove watches from all users and now inaccessible repos + // Remove watches from all users and now unaccessible repos for _, user := range t.Members { has, err := access_model.HasAccess(ctx, user.ID, repo) if err != nil { @@ -364,27 +354,16 @@ func DeleteTeam(ctx context.Context, t *organization.Team) error { return committer.Commit() } -func AddTeamMember(ctx context.Context, team *organization.Team, userID int64) error { - _, err := InsertTeamMember(ctx, team, userID) - return err -} - // AddTeamMember adds new membership of given team to given organization, // the user will have membership to given organization automatically when needed. -func InsertTeamMember(ctx context.Context, team *organization.Team, userID int64) (*organization.TeamUser, error) { +func AddTeamMember(ctx context.Context, team *organization.Team, userID int64) error { isAlreadyMember, err := organization.IsTeamMember(ctx, team.OrgID, team.ID, userID) if err != nil || isAlreadyMember { - return nil, err + return err } if err := organization.AddOrgUser(ctx, team.OrgID, userID); err != nil { - return nil, err - } - - teamUser := &organization.TeamUser{ - UID: userID, - OrgID: team.OrgID, - TeamID: team.ID, + return err } err = db.WithTx(ctx, func(ctx context.Context) error { @@ -396,7 +375,11 @@ func InsertTeamMember(ctx context.Context, team *organization.Team, userID int64 sess := db.GetEngine(ctx) - if err := db.Insert(ctx, teamUser); err != nil { + if err := db.Insert(ctx, &organization.TeamUser{ + UID: userID, + OrgID: team.OrgID, + TeamID: team.ID, + }); err != nil { return err } else if _, err := sess.Incr("num_members").ID(team.ID).Update(new(organization.Team)); err != nil { return err @@ -437,7 +420,7 @@ func InsertTeamMember(ctx context.Context, team *organization.Team, userID int64 return nil }) if err != nil { - return nil, err + return err } // this behaviour may spend much time so run it in a goroutine @@ -457,7 +440,7 @@ func InsertTeamMember(ctx context.Context, team *organization.Team, userID int64 }(team.Repos) } - return teamUser, nil + return nil } func removeTeamMember(ctx context.Context, team *organization.Team, userID int64) error { @@ -497,12 +480,12 @@ func removeTeamMember(ctx context.Context, team *organization.Team, userID int64 return err } - // Remove watches from now inaccessible + // Remove watches from now unaccessible if err := ReconsiderWatches(ctx, repo, userID); err != nil { return err } - // Remove issue assignments from now inaccessible + // Remove issue assignments from now unaccessible if err := ReconsiderRepoIssuesAssignee(ctx, repo, userID); err != nil { return err } diff --git a/models/org_team_test.go b/models/org_team_test.go index 5f7544e9ea..730bc65f7d 100644 --- a/models/org_team_test.go +++ b/models/org_team_test.go @@ -19,6 +19,20 @@ import ( "github.com/stretchr/testify/require" ) +func TestTeam_AddMember(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + test := func(teamID, userID int64) { + team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) + require.NoError(t, AddTeamMember(db.DefaultContext, team, userID)) + unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{UID: userID, TeamID: teamID}) + unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &user_model.User{ID: team.OrgID}) + } + test(1, 2) + test(1, 4) + test(3, 2) +} + func TestTeam_RemoveMember(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) @@ -118,96 +132,6 @@ func TestAddTeamMember(t *testing.T) { test(3, 2) } -func TestTeam_AddAndReturnTeamMember(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - for _, testCase := range []struct { - name string - alreadyMember bool - teamID int64 - userID int64 - }{ - { - name: "Already member of a team with repositories", - alreadyMember: true, - teamID: 1, - userID: 2, - }, - { - name: "New member of a team with repositories", - teamID: 1, - userID: 4, - }, - { - name: "New member of a team with no repositories", - teamID: 3, - userID: 2, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: testCase.teamID}) - teamUser, err := InsertTeamMember(db.DefaultContext, team, testCase.userID) - require.NoError(t, err) - if testCase.alreadyMember { - assert.Nil(t, teamUser) - } else { - require.NotNil(t, teamUser) - assert.Equal(t, testCase.teamID, teamUser.TeamID) - assert.Equal(t, testCase.userID, teamUser.UID) - } - unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{UID: testCase.userID, TeamID: testCase.teamID}) - unittest.CheckConsistencyFor(t, &organization.Team{ID: testCase.teamID}, &user_model.User{ID: team.OrgID}) - }) - } -} - -func TestTeam_AddTeamRepository(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - for _, testCase := range []struct { - name string - teamID int64 - repoID int64 - }{ - { - name: "AddAndReturnTeamRepository", - teamID: 8, - repoID: 23, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: testCase.teamID}) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: testCase.repoID}) - teamRepo, err := InsertTeamRepository(t.Context(), team, repo) - require.NoError(t, err) - require.NotNil(t, teamRepo) - assert.Equal(t, testCase.teamID, teamRepo.TeamID) - assert.Equal(t, testCase.repoID, teamRepo.RepoID) - unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{RepoID: testCase.repoID, TeamID: testCase.teamID}) - }) - } - - for _, testCase := range []struct { - name string - teamID int64 - repoID int64 - }{ - { - name: "AddTeamRepository", - teamID: 9, - repoID: 23, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: testCase.teamID}) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: testCase.repoID}) - err := AddRepository(t.Context(), team, repo) - require.NoError(t, err) - unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{RepoID: testCase.repoID, TeamID: testCase.teamID}) - }) - } -} - func TestRemoveTeamMember(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) diff --git a/models/organization/org.go b/models/organization/org.go index 02794ba2cb..6da8886c2f 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -17,7 +17,6 @@ import ( "forgejo.org/models/unit" user_model "forgejo.org/models/user" "forgejo.org/modules/log" - "forgejo.org/modules/optional" "forgejo.org/modules/setting" "forgejo.org/modules/structs" "forgejo.org/modules/util" @@ -192,7 +191,7 @@ func (org *Organization) IsGhost() bool { return org.AsUser().IsGhost() } -// FindOrgMembersOpts represents find org members conditions +// FindOrgMembersOpts represensts find org members conditions type FindOrgMembersOpts struct { db.ListOptions Doer *user_model.User @@ -397,14 +396,6 @@ func DeleteOrganization(ctx context.Context, org *Organization) error { return fmt.Errorf("%s is a user not an organization", org.Name) } - // Decrease following count of users that follow the organisation. - followerIDs, err := db.FindIDs(ctx, "follow", "follow.user_id", builder.Eq{"follow.follow_id": org.ID}) - if err != nil { - return fmt.Errorf("get all followers: %w", err) - } else if err = db.DecrByIDs(ctx, followerIDs, "num_following", new(user_model.User)); err != nil { - return fmt.Errorf("decrease user num_following: %w", err) - } - if err := db.DeleteBeans(ctx, &Team{OrgID: org.ID}, &OrgUser{OrgID: org.ID}, @@ -413,9 +404,7 @@ func DeleteOrganization(ctx context.Context, org *Organization) error { &TeamInvite{OrgID: org.ID}, &secret_model.Secret{OwnerID: org.ID}, &actions_model.ActionRunner{OwnerID: org.ID}, - &actions_model.ActionRunnerToken{OwnerID: optional.Some(org.ID)}, - &user_model.BlockedUser{UserID: org.ID}, - &user_model.Follow{FollowID: org.ID}, + &actions_model.ActionRunnerToken{OwnerID: org.ID}, ); err != nil { return fmt.Errorf("DeleteBeans: %w", err) } @@ -521,10 +510,10 @@ func ChangeOrgUserStatus(ctx context.Context, orgID, uid int64, public bool) err // AddOrgUser adds new user to given organization. func AddOrgUser(ctx context.Context, orgID, uid int64) error { - eligible, err := IsAnEligibleTeamMemberByID(ctx, uid) + isUser, err := user_model.IsUserByID(ctx, uid) if err != nil { return err - } else if !eligible { + } else if !isUser { return user_model.ErrUserWrongType{UID: uid} } diff --git a/models/organization/org_user.go b/models/organization/org_user.go index 4c84fccbf3..81671c5cf5 100644 --- a/models/organization/org_user.go +++ b/models/organization/org_user.go @@ -112,15 +112,6 @@ func IsUserOrgOwner(ctx context.Context, users user_model.UserList, orgID int64) return results } -// Returns true if the given user ID is allowed to be a team member -func IsAnEligibleTeamMemberByID(ctx context.Context, uid int64) (bool, error) { - return db.GetEngine(ctx). - Where("id=?", uid). - In("type", user_model.UserTypeIndividual, user_model.UserTypeBot, user_model.UserTypeRemoteUser). - Table("user"). - Exist() -} - func loadOrganizationOwners(ctx context.Context, users user_model.UserList, orgID int64) (map[int64]*TeamUser, error) { if len(users) == 0 { return nil, nil diff --git a/models/organization/org_user_test.go b/models/organization/org_user_test.go index 670a641214..4011e5df88 100644 --- a/models/organization/org_user_test.go +++ b/models/organization/org_user_test.go @@ -162,41 +162,3 @@ func TestAddOrgUser(t *testing.T) { unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{}) } - -func TestIsAnEligibleTeamMemberByID(t *testing.T) { - defer unittest.OverrideFixtures("models/user/fixtures/")() - require.NoError(t, unittest.PrepareTestDatabase()) - - for _, testCase := range []struct { - name string - id int64 - eligible bool - }{ - { - name: "Regular user", - id: 1, - eligible: true, - }, - { - name: "Bot user", - id: 1042, - eligible: true, - }, - { - name: "Organization", - id: 3, - eligible: false, - }, - { - name: "F3 Remote user", - id: 1041, - eligible: true, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - eligible, err := organization.IsAnEligibleTeamMemberByID(t.Context(), testCase.id) - require.NoError(t, err) - assert.Equal(t, testCase.eligible, eligible) - }) - } -} diff --git a/models/organization/team.go b/models/organization/team.go index abc60fcb72..209471e013 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -150,33 +150,21 @@ func (t *Team) IsMember(ctx context.Context, userID int64) bool { return isMember } -// LoadRepositories returns the repositories of the team in t.Repos. +// LoadRepositories returns paginated repositories in team of organization. func (t *Team) LoadRepositories(ctx context.Context) (err error) { - return t.LoadPaginatedRepositories(ctx, db.ListOptionsAll) -} - -// LoadPaginatedRepositories loads paginated repositories of the team in t.Repos. -func (t *Team) LoadPaginatedRepositories(ctx context.Context, listOptions db.ListOptions) (err error) { if t.Repos != nil { return nil } t.Repos, err = GetTeamRepositories(ctx, &SearchTeamRepoOptions{ - ListOptions: listOptions, - TeamID: t.ID, + TeamID: t.ID, }) return err } -// LoadMembers loads the members of the team in t.Members. +// LoadMembers returns paginated members in team of organization. func (t *Team) LoadMembers(ctx context.Context) (err error) { - return t.LoadPaginatedMembers(ctx, db.ListOptionsAll) -} - -// LoadPaginatedMembers loads paginated members of the team in t.Members. -func (t *Team) LoadPaginatedMembers(ctx context.Context, listOptions db.ListOptions) (err error) { t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{ - ListOptions: listOptions, - TeamID: t.ID, + TeamID: t.ID, }) return err } diff --git a/models/organization/team_repo.go b/models/organization/team_repo.go index 895d5a56d0..334b139808 100644 --- a/models/organization/team_repo.go +++ b/models/organization/team_repo.go @@ -34,8 +34,6 @@ func HasTeamRepo(ctx context.Context, orgID, teamID, repoID int64) bool { type SearchTeamRepoOptions struct { db.ListOptions TeamID int64 - // Filters repositories based upon optional authorization restrictions. - AuthorizationReducer repo_model.RepositoryAuthorizationReducer } // GetRepositories returns paginated repositories in team of organization. @@ -48,9 +46,6 @@ func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (repo Where(builder.Eq{"team_id": opts.TeamID}), ) } - if opts.AuthorizationReducer != nil { - sess = sess.Where(opts.AuthorizationReducer.RepoReadAccessFilter()) - } if opts.PageSize > 0 { sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) } @@ -59,6 +54,16 @@ func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (repo Find(&repos) } +// AddTeamRepo adds a repo for an organization's team +func AddTeamRepo(ctx context.Context, orgID, teamID, repoID int64) error { + _, err := db.GetEngine(ctx).Insert(&TeamRepo{ + OrgID: orgID, + TeamID: teamID, + RepoID: repoID, + }) + return err +} + // RemoveTeamRepo remove repository from team func RemoveTeamRepo(ctx context.Context, teamID, repoID int64) error { _, err := db.DeleteByBean(ctx, &TeamRepo{ diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go index 947144ec54..19e0e8f5d5 100644 --- a/models/packages/descriptor.go +++ b/models/packages/descriptor.go @@ -174,10 +174,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc metadata = &debian.Metadata{} case TypeGeneric: // generic packages have no metadata - break case TypeGo: // go packages have no metadata - break case TypeHelm: metadata = &helm.Metadata{} case TypeNuGet: diff --git a/models/packages/nuget/search.go b/models/packages/nuget/search.go index d609e7e894..af83c27c66 100644 --- a/models/packages/nuget/search.go +++ b/models/packages/nuget/search.go @@ -55,7 +55,7 @@ func CountPackages(ctx context.Context, opts *packages_model.PackageSearchOption func toConds(opts *packages_model.PackageSearchOptions) builder.Cond { var cond builder.Cond = builder.Eq{ - "package.is_internal": opts.IsInternal.ValueOrZeroValue(), + "package.is_internal": opts.IsInternal.Value(), "package.owner_id": opts.OwnerID, "package.type": packages_model.TypeNuGet, } diff --git a/models/packages/package_version.go b/models/packages/package_version.go index 545ad63eb4..87a97143f3 100644 --- a/models/packages/package_version.go +++ b/models/packages/package_version.go @@ -5,13 +5,11 @@ package packages import ( "context" - "errors" "strconv" "strings" "forgejo.org/models/db" "forgejo.org/modules/optional" - "forgejo.org/modules/setting" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" @@ -157,25 +155,6 @@ func HasVersionFileReferences(ctx context.Context, versionID int64) (bool, error }) } -func (pv *PackageVersion) LockForUpdate(ctx context.Context) error { - if !db.InTransaction(ctx) { - return errors.New("invalid state for PackageVersion.LockForUpdate: database is not in a transaction") - } else if setting.Database.Type.IsSQLite3() { - // SQLite both doesn't support "SELECT ... FOR UPDATE", and it's irrelevant for SQLite as the entire database is - // locked for write when a write transaction is open. - return nil - } - - pvfu := PackageVersion{} - has, err := db.GetEngine(ctx).ID(pv.ID).ForUpdate().Get(&pvfu) - if err != nil { - return err - } else if !has { - return ErrPackageNotExist - } - return nil -} - // SearchValue describes a value to search // If ExactMatch is true, the field must match the value otherwise a LIKE search is performed. type SearchValue struct { @@ -213,9 +192,9 @@ type PackageSearchOptions struct { func (opts *PackageSearchOptions) ToConds() builder.Cond { cond := builder.NewCond() - if has, value := opts.IsInternal.Get(); has { + if opts.IsInternal.Has() { cond = builder.Eq{ - "package_version.is_internal": value, + "package_version.is_internal": opts.IsInternal.Value(), } } @@ -275,10 +254,10 @@ func (opts *PackageSearchOptions) ToConds() builder.Cond { cond = cond.And(builder.Exists(builder.Select("package_file.id").From("package_file").Where(fileCond))) } - if has, value := opts.HasFiles.Get(); has { + if opts.HasFiles.Has() { filesCond := builder.Exists(builder.Select("package_file.id").From("package_file").Where(builder.Expr("package_file.version_id = package_version.id"))) - if !value { + if !opts.HasFiles.Value() { filesCond = builder.Not{filesCond} } diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index fd1b93c867..f7daf38e5c 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -6,7 +6,6 @@ package access import ( "context" "fmt" - "strings" actions_model "forgejo.org/models/actions" "forgejo.org/models/db" @@ -16,7 +15,6 @@ import ( "forgejo.org/models/unit" user_model "forgejo.org/models/user" "forgejo.org/modules/log" - "forgejo.org/services/authz" ) // Permission contains all the permissions related variables to a repository for a user @@ -116,8 +114,7 @@ func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool { } func (p *Permission) LogString() string { - var format strings.Builder - format.WriteString("") - return fmt.Sprintf(format.String(), args...) + format += " ]>" + return fmt.Sprintf(format, args...) } func GetActionRepoPermission(ctx context.Context, repo *repo_model.Repository, task *actions_model.ActionTask) (Permission, error) { @@ -167,28 +164,7 @@ func GetActionRepoPermission(ctx context.Context, repo *repo_model.Repository, t return GetUserRepoPermission(ctx, repo, user_model.NewActionsUser()) } -// GetUserRepoPermission returns the user permissions to the repository, where the user's permissions may be -// artificially restricted by a an authorization reducer. -func GetUserRepoPermissionWithReducer(ctx context.Context, repo *repo_model.Repository, user *user_model.User, reducer authz.AuthorizationReducer) (Permission, error) { - perm, err := GetUserRepoPermission(ctx, repo, user) - if err != nil { - return perm, err - } - perm.AccessMode, err = reducer.ReduceRepoAccess(ctx, repo, perm.AccessMode) - if err != nil { - return perm, fmt.Errorf("failure in ReduceRepoAccess: %w", err) - } - for unit, currentAccessMode := range perm.UnitsMode { - reduced, err := reducer.ReduceRepoAccess(ctx, repo, currentAccessMode) - if err != nil { - return perm, fmt.Errorf("failure in ReduceRepoAccess: %w", err) - } - perm.UnitsMode[unit] = reduced - } - return perm, nil -} - -// GetUserRepoPermission returns the user permissions to the repository. +// 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 if log.IsTrace() { diff --git a/models/perm/access/repo_permission_test.go b/models/perm/access/repo_permission_test.go index 303605047f..55bc975421 100644 --- a/models/perm/access/repo_permission_test.go +++ b/models/perm/access/repo_permission_test.go @@ -8,13 +8,9 @@ import ( perm_model "forgejo.org/models/perm" "forgejo.org/models/perm/access" repo_model "forgejo.org/models/repo" - "forgejo.org/models/unit" "forgejo.org/models/unittest" - user_model "forgejo.org/models/user" - "forgejo.org/services/authz" "github.com/stretchr/testify/assert" - mock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -80,73 +76,3 @@ func TestActionTaskNoAccessPrivateRepo(t *testing.T) { require.NoError(t, err) assertAccess(t, perm_model.AccessModeNone, &perm) } - -func TestGetUserRepoPermissionWithReducer(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - t.Run("no unit-level overrides", func(t *testing.T) { - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - - // Baseline check that without a reducer, we get AccessModeOwner... - permWithoutReducer, err := access.GetUserRepoPermission(t.Context(), repo, user) - require.NoError(t, err) - require.NotNil(t, permWithoutReducer) - assert.True(t, permWithoutReducer.IsOwner()) - assert.True(t, permWithoutReducer.IsAdmin()) - assert.True(t, permWithoutReducer.HasAccess()) - assert.True(t, permWithoutReducer.CanWrite(unit.TypeIssues)) - - reducer := authz.NewMockAuthorizationReducer(t) - reducer.On( - "ReduceRepoAccess", - mock.Anything, // context - mock.MatchedBy(func(repo *repo_model.Repository) bool { // repo - return repo.ID == 1 - }), - perm_model.AccessModeOwner, // incoming access mode - ).Return(perm_model.AccessModeNone, nil) - - permWithReducer, err := access.GetUserRepoPermissionWithReducer(t.Context(), repo, user, reducer) - require.NoError(t, err) - require.NotNil(t, permWithReducer) - assert.False(t, permWithReducer.IsOwner()) - assert.False(t, permWithReducer.IsAdmin()) - assert.False(t, permWithReducer.HasAccess()) - assert.False(t, permWithReducer.CanWrite(unit.TypeIssues)) - }) - - t.Run("team unit-level overrides", func(t *testing.T) { - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32}) - - // Baseline check that without a reducer, we get mixed access for different units... - permWithoutReducer, err := access.GetUserRepoPermission(t.Context(), repo, user) - require.NoError(t, err) - require.NotNil(t, permWithoutReducer) - require.NotEmpty(t, permWithoutReducer.UnitsMode) // unit-specific access modes loaded - assert.True(t, permWithoutReducer.CanRead(unit.TypeCode)) - assert.False(t, permWithoutReducer.CanWrite(unit.TypeCode)) - assert.True(t, permWithoutReducer.CanRead(unit.TypeIssues)) - assert.True(t, permWithoutReducer.CanWrite(unit.TypeIssues)) - - reducer := authz.NewMockAuthorizationReducer(t) - reducer.On( - "ReduceRepoAccess", - mock.Anything, // context - mock.MatchedBy(func(repo *repo_model.Repository) bool { // repo - return repo.ID == 32 - }), - mock.Anything, // incoming access mode - will vary for each unit - ).Return(perm_model.AccessModeRead, nil) - - permWithReducer, err := access.GetUserRepoPermissionWithReducer(t.Context(), repo, user, reducer) - require.NoError(t, err) - require.NotNil(t, permWithReducer) - require.NotEmpty(t, permWithReducer.UnitsMode) // unit-specific access modes loaded - assert.True(t, permWithReducer.CanRead(unit.TypeCode)) - assert.False(t, permWithReducer.CanWrite(unit.TypeCode)) - assert.True(t, permWithReducer.CanRead(unit.TypeIssues)) - assert.False(t, permWithReducer.CanWrite(unit.TypeIssues)) - }) -} diff --git a/models/project/column.go b/models/project/column.go index 858a531bfc..20de39357b 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -10,7 +10,6 @@ import ( "regexp" "forgejo.org/models/db" - "forgejo.org/modules/container" "forgejo.org/modules/setting" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" @@ -43,10 +42,10 @@ type Column struct { ID int64 `xorm:"pk autoincr"` Title string Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific column will be assigned to this column - Sorting int8 `xorm:"NOT NULL DEFAULT 0 unique(project_sorting)"` + Sorting int8 `xorm:"NOT NULL DEFAULT 0"` Color string `xorm:"VARCHAR(7)"` - ProjectID int64 `xorm:"INDEX NOT NULL unique(project_sorting)"` + ProjectID int64 `xorm:"INDEX NOT NULL"` CreatorID int64 `xorm:"NOT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` @@ -104,9 +103,8 @@ func createDefaultColumnsForProject(ctx context.Context, project *Project) error Title: "Backlog", ProjectID: project.ID, Default: true, - Sorting: 0, } - if err := db.Insert(ctx, &column); err != nil { + if err := db.Insert(ctx, column); err != nil { return err } @@ -115,13 +113,12 @@ func createDefaultColumnsForProject(ctx context.Context, project *Project) error } columns := make([]Column, 0, len(items)) - for i, v := range items { + for _, v := range items { columns = append(columns, Column{ CreatedUnix: timeutil.TimeStampNow(), CreatorID: project.CreatorID, Title: v, ProjectID: project.ID, - Sorting: int8(i + 1), }) } @@ -218,7 +215,9 @@ func GetColumn(ctx context.Context, columnID int64) (*Column, error) { func UpdateColumn(ctx context.Context, column *Column) error { var fieldToUpdate []string - fieldToUpdate = append(fieldToUpdate, "sorting") + if column.Sorting != 0 { + fieldToUpdate = append(fieldToUpdate, "sorting") + } if column.Title != "" { fieldToUpdate = append(fieldToUpdate, "title") @@ -258,22 +257,12 @@ func (p *Project) GetDefaultColumn(ctx context.Context) (*Column, error) { return &column, nil } - // create a default column if none is found, using the next available sorting value - res := struct { - MaxSorting int64 - ColumnCount int64 - }{} - if _, err := db.GetEngine(ctx).Select("max(sorting) as max_sorting, count(*) as column_count"). - Table("project_board").Where("project_id=?", p.ID).Get(&res); err != nil { - return nil, err - } - + // create a default column if none is found column = Column{ ProjectID: p.ID, Default: true, Title: "Uncategorized", CreatorID: p.CreatorID, - Sorting: int8(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0)), } if _, err := db.GetEngine(ctx).Insert(&column); err != nil { return nil, err @@ -316,59 +305,27 @@ func GetColumnsByIDs(ctx context.Context, projectID int64, columnsIDs []int64) ( return columns, nil } -// MoveColumnsOnProject sorts columns in a project using a two-phase approach -// to avoid unique constraint collisions during swap operations. -// All columns in the project must be included in the sortedColumnIDs map. +// MoveColumnsOnProject sorts columns in a project func MoveColumnsOnProject(ctx context.Context, project *Project, sortedColumnIDs map[int64]int64) error { return db.WithTx(ctx, func(ctx context.Context) error { sess := db.GetEngine(ctx) - - // Validate no duplicate column IDs in map values - columnIDSet := make(container.Set[int64], len(sortedColumnIDs)) - for _, columnID := range sortedColumnIDs { - if !columnIDSet.Add(columnID) { - return errors.New("duplicate column ID in reorder request") - } - } - - // Validate all columns exist and belong to this project - allColumns, err := project.GetColumns(ctx) - if err != nil { - return err - } - if len(allColumns) != len(sortedColumnIDs) { - return errors.New("all columns in the project must be included in the reorder request") - } - columnIDs := util.ValuesOfMap(sortedColumnIDs) movedColumns, err := GetColumnsByIDs(ctx, project.ID, columnIDs) if err != nil { return err } if len(movedColumns) != len(sortedColumnIDs) { - return errors.New("some columns do not exist in this project") + return errors.New("some columns do not exist") } - // Build reverse map: columnID → target sorting - targetSortingByColumn := make(map[int64]int64, len(sortedColumnIDs)) - for sorting, columnID := range sortedColumnIDs { - targetSortingByColumn[columnID] = sorting - } - - // Phase 1: negate using target sorting values (guaranteed unique since - // they are map keys) to avoid unique constraint collisions during swap for _, column := range movedColumns { - targetSorting := targetSortingByColumn[column.ID] - if _, err := sess.Exec("UPDATE `project_board` SET sorting=? WHERE id=?", - -(targetSorting + 1), column.ID); err != nil { - return err + if column.ProjectID != project.ID { + return fmt.Errorf("column[%d]'s projectID is not equal to project's ID [%d]", column.ProjectID, project.ID) } } - // Phase 2: set final values for sorting, columnID := range sortedColumnIDs { - if _, err := sess.Exec("UPDATE `project_board` SET sorting=? WHERE id=?", - sorting, columnID); err != nil { + if _, err := sess.Exec("UPDATE `project_board` SET sorting=? WHERE id=?", sorting, columnID); err != nil { return err } } diff --git a/models/project/column_test.go b/models/project/column_test.go index 2f4cc79367..4fc452df33 100644 --- a/models/project/column_test.go +++ b/models/project/column_test.go @@ -84,9 +84,9 @@ func Test_MoveColumnsOnProject(t *testing.T) { columns, err := project1.GetColumns(db.DefaultContext) require.NoError(t, err) assert.Len(t, columns, 3) - assert.EqualValues(t, 0, columns[0].Sorting) - assert.EqualValues(t, 1, columns[1].Sorting) - assert.EqualValues(t, 2, columns[2].Sorting) + assert.EqualValues(t, 0, columns[0].Sorting) // even if there is no default sorting, the code should also work + assert.EqualValues(t, 0, columns[1].Sorting) + assert.EqualValues(t, 0, columns[2].Sorting) err = MoveColumnsOnProject(db.DefaultContext, project1, map[int64]int64{ 0: columns[1].ID, @@ -103,59 +103,6 @@ func Test_MoveColumnsOnProject(t *testing.T) { assert.Equal(t, columns[0].ID, columnsAfter[2].ID) } -func TestMoveColumnsOnProjectSwap(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - project1 := unittest.AssertExistsAndLoadBean(t, &Project{ID: 1}) - columns, err := project1.GetColumns(db.DefaultContext) - require.NoError(t, err) - require.Len(t, columns, 3) - - // First give them distinct positions - err = MoveColumnsOnProject(db.DefaultContext, project1, map[int64]int64{ - 0: columns[0].ID, - 1: columns[1].ID, - 2: columns[2].ID, - }) - require.NoError(t, err) - - // Now swap columns 0 and 1 (would collide under single-phase update) - err = MoveColumnsOnProject(db.DefaultContext, project1, map[int64]int64{ - 0: columns[1].ID, - 1: columns[0].ID, - 2: columns[2].ID, - }) - require.NoError(t, err) - - columnsAfter, err := project1.GetColumns(db.DefaultContext) - require.NoError(t, err) - assert.Len(t, columnsAfter, 3) - assert.Equal(t, columns[1].ID, columnsAfter[0].ID) - assert.Equal(t, columns[0].ID, columnsAfter[1].ID) - assert.Equal(t, columns[2].ID, columnsAfter[2].ID) -} - -func TestUpdateColumnSortingZero(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - column := unittest.AssertExistsAndLoadBean(t, &Column{ID: 1}) - column.Sorting = 5 - require.NoError(t, UpdateColumn(db.DefaultContext, column)) - - // Verify it was set to 5 - updated, err := GetColumn(db.DefaultContext, column.ID) - require.NoError(t, err) - assert.Equal(t, int8(5), updated.Sorting) - - // Now set it back to 0 - column.Sorting = 0 - require.NoError(t, UpdateColumn(db.DefaultContext, column)) - - updated, err = GetColumn(db.DefaultContext, column.ID) - require.NoError(t, err) - assert.Equal(t, int8(0), updated.Sorting) -} - func Test_NewColumn(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) @@ -164,7 +111,7 @@ func Test_NewColumn(t *testing.T) { require.NoError(t, err) assert.Len(t, columns, 3) - for i := range maxProjectColumns - 3 { + for i := 0; i < maxProjectColumns-3; i++ { err := NewColumn(db.DefaultContext, &Column{ Title: fmt.Sprintf("column-%d", i+4), ProjectID: project1.ID, diff --git a/models/project/issue.go b/models/project/issue.go index 41caac991a..d404033446 100644 --- a/models/project/issue.go +++ b/models/project/issue.go @@ -6,7 +6,6 @@ package project import ( "context" "errors" - "slices" "forgejo.org/models/db" "forgejo.org/modules/log" @@ -16,14 +15,14 @@ import ( // ProjectIssue saves relation from issue to a project type ProjectIssue struct { //revive:disable-line:exported ID int64 `xorm:"pk autoincr"` - IssueID int64 `xorm:"INDEX NOT NULL unique(project_issue)"` - ProjectID int64 `xorm:"INDEX NOT NULL unique(project_issue)"` + IssueID int64 `xorm:"INDEX"` + ProjectID int64 `xorm:"INDEX"` // ProjectColumnID should not be zero since 1.22. If it's zero, the issue will not be displayed on UI and it might result in errors. - ProjectColumnID int64 `xorm:"'project_board_id' INDEX NOT NULL unique(column_sorting)"` + ProjectColumnID int64 `xorm:"'project_board_id' INDEX"` // the sorting order on the column - Sorting int64 `xorm:"NOT NULL DEFAULT 0 unique(column_sorting)"` + Sorting int64 `xorm:"NOT NULL DEFAULT 0"` } func init() { @@ -63,86 +62,26 @@ func (p *Project) NumOpenIssues(ctx context.Context) int { return int(c) } -// MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column. -// The sortedIssueIDs map keys are sorting positions and values are issue IDs. -// Cards not in the map that already exist in the target column are shifted to -// positions after the highest requested sorting value. +// MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column func MoveIssuesOnProjectColumn(ctx context.Context, column *Column, sortedIssueIDs map[int64]int64) error { - if len(sortedIssueIDs) == 0 { - return nil - } return db.WithTx(ctx, func(ctx context.Context) error { sess := db.GetEngine(ctx) issueIDs := util.ValuesOfMap(sortedIssueIDs) - // Build reverse map: issueID → sorting and validate no duplicate issue IDs - sortingByIssue := make(map[int64]int64, len(sortedIssueIDs)) - for sorting, issueID := range sortedIssueIDs { - sortingByIssue[issueID] = sorting - } - if len(sortingByIssue) != len(sortedIssueIDs) { - return errors.New("duplicate issue IDs in reorder request") - } - - // Validate all issues exist and belong to this project - count, err := sess.Table(new(ProjectIssue)). - Where("project_id=?", column.ProjectID). - In("issue_id", issueIDs).Count() + count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", column.ProjectID).In("issue_id", issueIDs).Count() if err != nil { return err } if int(count) != len(sortedIssueIDs) { - return errors.New("all issues must belong to the specified project") + return errors.New("all issues have to be added to a project first") } - // Sort issue IDs to ensure consistent lock ordering across concurrent transactions. - // This prevents deadlocks when multiple transactions update overlapping rows. - slices.Sort(issueIDs) - - // Phase 1: Negate sorting for ALL cards currently in the target column - // to free up all positive sorting positions. This prevents collisions - // when moved cards are assigned their final positions. - if _, err := sess.Exec("UPDATE `project_issue` SET sorting = -(sorting + 1) WHERE project_board_id=? AND sorting >= 0", - column.ID); err != nil { - return err - } - - // Phase 2: Move the specified cards to the target column with their - // final sorting values. Since all existing cards in the column now have - // negative sorting, there are no collisions. - for _, issueID := range issueIDs { - _, err := sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE project_id=? AND issue_id=?", - column.ID, sortingByIssue[issueID], column.ProjectID, issueID) + for sorting, issueID := range sortedIssueIDs { + _, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID) if err != nil { return err } } - - // Phase 3: Re-pack any remaining cards in the column that still have - // negative sorting (these are pre-existing cards NOT in the move set). - // Assign them positions after the highest requested sorting value. - var maxSorting int64 - for sorting := range sortedIssueIDs { - if sorting > maxSorting { - maxSorting = sorting - } - } - - var remainingCards []ProjectIssue - if err := sess.Where("project_board_id=? AND sorting < 0", column.ID). - OrderBy("sorting DESC"). // original order was -(original+1), so DESC gives ascending original order - Find(&remainingCards); err != nil { - return err - } - nextSorting := maxSorting + 1 - for _, card := range remainingCards { - if _, err := sess.Exec("UPDATE `project_issue` SET sorting=? WHERE id=?", - nextSorting, card.ID); err != nil { - return err - } - nextSorting++ - } - return nil }) } diff --git a/models/project/issue_test.go b/models/project/issue_test.go deleted file mode 100644 index 95a10182d3..0000000000 --- a/models/project/issue_test.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package project - -import ( - "testing" - - "forgejo.org/models/db" - "forgejo.org/models/unittest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMoveIssuesOnProjectColumn(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - // Get column 1 which belongs to project 1 and has issue 1 - column := unittest.AssertExistsAndLoadBean(t, &Column{ID: 1}) - require.Equal(t, int64(1), column.ProjectID) - - t.Run("Success", func(t *testing.T) { - // Issue 1 is in column 1 (from fixtures) - sortedIssueIDs := map[int64]int64{ - 0: 1, // sorting position 0 -> issue_id 1 - } - err := MoveIssuesOnProjectColumn(db.DefaultContext, column, sortedIssueIDs) - require.NoError(t, err) - - // Verify the sorting was updated using direct DB query - var card ProjectIssue - has, err := db.GetEngine(db.DefaultContext).Where("project_id=? AND issue_id=?", column.ProjectID, 1).Get(&card) - require.NoError(t, err) - require.True(t, has) - assert.Equal(t, int64(0), card.Sorting) - }) - - t.Run("MoveIssueFromDifferentColumn", func(t *testing.T) { - // Issue 3 is in column 2, not column 1 — but same project, so cross-column move should succeed - sortedIssueIDs := map[int64]int64{ - 0: 3, - } - err := MoveIssuesOnProjectColumn(db.DefaultContext, column, sortedIssueIDs) - require.NoError(t, err) - - // Verify the card was moved to column 1 and sorting updated - var card ProjectIssue - has, err := db.GetEngine(db.DefaultContext).Where("project_id=? AND issue_id=?", column.ProjectID, 3).Get(&card) - require.NoError(t, err) - require.True(t, has) - assert.Equal(t, column.ID, card.ProjectColumnID) - assert.Equal(t, int64(0), card.Sorting) - }) - - t.Run("ErrorIssueNotInProject", func(t *testing.T) { - // Issue 999 doesn't exist - sortedIssueIDs := map[int64]int64{ - 0: 999, - } - err := MoveIssuesOnProjectColumn(db.DefaultContext, column, sortedIssueIDs) - require.Error(t, err) - assert.Contains(t, err.Error(), "all issues must belong to the specified project") - }) -} - -func TestMoveIssuesOnProjectColumnSwap(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - column := unittest.AssertExistsAndLoadBean(t, &Column{ID: 1}) - - // Setup: insert two cards at distinct positions using direct DB inserts - card1 := &ProjectIssue{ - IssueID: 14, - ProjectID: 1, - ProjectColumnID: column.ID, - Sorting: 10, - } - card2 := &ProjectIssue{ - IssueID: 15, - ProjectID: 1, - ProjectColumnID: column.ID, - Sorting: 11, - } - _, err := db.GetEngine(db.DefaultContext).Insert(card1) - require.NoError(t, err) - _, err = db.GetEngine(db.DefaultContext).Insert(card2) - require.NoError(t, err) - - // Swap them: card at 10→11, card at 11→10 - sortedIssueIDs := map[int64]int64{ - 11: 14, // issue 14 goes to position 11 - 10: 15, // issue 15 goes to position 10 - } - err = MoveIssuesOnProjectColumn(db.DefaultContext, column, sortedIssueIDs) - require.NoError(t, err) - - var resultCard14 ProjectIssue - has, err := db.GetEngine(db.DefaultContext).Where("project_id=? AND issue_id=?", 1, 14).Get(&resultCard14) - require.NoError(t, err) - require.True(t, has) - assert.Equal(t, int64(11), resultCard14.Sorting) - - var resultCard15 ProjectIssue - has, err = db.GetEngine(db.DefaultContext).Where("project_id=? AND issue_id=?", 1, 15).Get(&resultCard15) - require.NoError(t, err) - require.True(t, has) - assert.Equal(t, int64(10), resultCard15.Sorting) -} - -func TestMoveIssuesOnProjectColumnEmptyMap(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - column := unittest.AssertExistsAndLoadBean(t, &Column{ID: 1}) - err := MoveIssuesOnProjectColumn(db.DefaultContext, column, map[int64]int64{}) - require.NoError(t, err) // empty map should be a no-op -} - -func TestMoveIssuesOnProjectColumnDuplicateIssueIDs(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - column := unittest.AssertExistsAndLoadBean(t, &Column{ID: 1}) - err := MoveIssuesOnProjectColumn(db.DefaultContext, column, map[int64]int64{ - 0: 1, - 1: 1, // duplicate issue ID - }) - require.Error(t, err) - assert.Contains(t, err.Error(), "duplicate issue IDs") -} - -func TestMoveIssuesToAnotherColumnErrorPaths(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - t.Run("DifferentProject", func(t *testing.T) { - col1 := unittest.AssertExistsAndLoadBean(t, &Column{ID: 1, ProjectID: 1}) - col5 := unittest.AssertExistsAndLoadBean(t, &Column{ID: 5, ProjectID: 2}) - - err := col1.moveIssuesToAnotherColumn(db.DefaultContext, col5) - require.Error(t, err) - assert.Contains(t, err.Error(), "columns have to be in the same project") - }) - - t.Run("SameColumnIsNoOp", func(t *testing.T) { - col1 := unittest.AssertExistsAndLoadBean(t, &Column{ID: 1, ProjectID: 1}) - - err := col1.moveIssuesToAnotherColumn(db.DefaultContext, col1) - require.NoError(t, err) - }) -} diff --git a/models/project/project.go b/models/project/project.go index be1b9d59c6..7d507b358d 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -136,7 +136,6 @@ func ProjectLinkForRepo(repo *repo_model.Repository, projectID int64) string { / // Link returns the project's relative URL. func (p *Project) Link(ctx context.Context) string { - // nosemgrep: forgejo-logic-suspicious-OwnerID-check (system users are not stored in the database) if p.OwnerID > 0 { err := p.LoadOwner(ctx) if err != nil { @@ -184,7 +183,7 @@ func init() { // GetCardConfig retrieves the types of configurations project column cards could have // -//llu:returnsTrKeyWeak +//llu:returnsTrKey func GetCardConfig() []CardConfig { return []CardConfig{ {CardTypeTextOnly, "repo.projects.card_type.text_only"}, @@ -218,14 +217,14 @@ func (opts SearchOptions) ToConds() builder.Cond { if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) } - if has, value := opts.IsClosed.Get(); has { - cond = cond.And(builder.Eq{"is_closed": value}) + if opts.IsClosed.Has() { + cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.Value()}) } if opts.Type > 0 { cond = cond.And(builder.Eq{"type": opts.Type}) } - if opts.OwnerID != 0 { + if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } diff --git a/models/project/template.go b/models/project/template.go index 3d55cd27f2..278cf5b781 100644 --- a/models/project/template.go +++ b/models/project/template.go @@ -27,7 +27,7 @@ const ( // GetTemplateConfigs retrieves the template configs of configurations project columns could have // -//llu:returnsTrKeyWeak +//llu:returnsTrKey func GetTemplateConfigs() []TemplateConfig { return []TemplateConfig{ {TemplateTypeNone, "repo.projects.type.none"}, diff --git a/models/pull/review_state.go b/models/pull/review_state.go index 3fc3ab65c2..2702d5d5a1 100644 --- a/models/pull/review_state.go +++ b/models/pull/review_state.go @@ -6,7 +6,6 @@ package pull import ( "context" "fmt" - "maps" "forgejo.org/models/db" "forgejo.org/modules/log" @@ -101,7 +100,9 @@ func mergeFiles(oldFiles, newFiles map[string]ViewedState) map[string]ViewedStat return oldFiles } - maps.Copy(oldFiles, newFiles) + for file, viewed := range newFiles { + oldFiles[file] = viewed + } return oldFiles } diff --git a/models/repo.go b/models/repo.go index 6a4da96b95..1b9cc8fa60 100644 --- a/models/repo.go +++ b/models/repo.go @@ -14,8 +14,10 @@ import ( asymkey_model "forgejo.org/models/asymkey" "forgejo.org/models/db" issues_model "forgejo.org/models/issues" + access_model "forgejo.org/models/perm/access" repo_model "forgejo.org/models/repo" "forgejo.org/models/unit" + user_model "forgejo.org/models/user" "forgejo.org/modules/log" "forgejo.org/services/stats" @@ -275,7 +277,7 @@ func DoctorUserStarNum(ctx context.Context) (err error) { } // DeleteDeployKey delete deploy keys -func DeleteDeployKey(ctx context.Context, id, repoID int64) error { +func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error { key, err := asymkey_model.GetDeployKeyByID(ctx, id) if err != nil { if asymkey_model.IsErrDeployKeyNotExist(err) { @@ -284,10 +286,21 @@ func DeleteDeployKey(ctx context.Context, id, repoID int64) error { return fmt.Errorf("GetDeployKeyByID: %w", err) } - if key.RepoID != repoID { - return asymkey_model.ErrKeyAccessDenied{ - KeyID: key.ID, - Note: "deploy", + // Check if user has access to delete this key. + if !doer.IsAdmin { + repo, err := repo_model.GetRepositoryByID(ctx, key.RepoID) + if err != nil { + return fmt.Errorf("GetRepositoryByID: %w", err) + } + has, err := access_model.IsUserRepoAdmin(ctx, repo, doer) + if err != nil { + return fmt.Errorf("GetUserRepoPermission: %w", err) + } else if !has { + return asymkey_model.ErrKeyAccessDenied{ + UserID: doer.ID, + KeyID: key.ID, + Note: "deploy", + } } } diff --git a/models/repo/authz.go b/models/repo/authz.go deleted file mode 100644 index 9196370583..0000000000 --- a/models/repo/authz.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package repo - -import ( - "context" - - "forgejo.org/models/perm" - - "xorm.io/builder" -) - -// Defines an API for reducing available permissions to specific repositories. -type RepositoryAuthorizationReducer interface { - // Given a repository and an accessMode, ReduceRepoAccess will return a new, possibly reduced, AccessMode that - // reflects the actual access that is currently permitted. For example, when using a fine-grained access token that - // only grants write access to one target repository, `ReduceRepoAccess(target, AccessModeWrite)` would return - // `AccessModeWrite`, and `ReduceRepoAccess(other-repo, AccessModeWrite)` would return a lesser access mode, - // restricting access to other repositories. - ReduceRepoAccess(ctx context.Context, repo *Repository, accessMode perm.AccessMode) (perm.AccessMode, error) - - // If querying the repository table, apply the provided condition to query only repositories that the restriction - // will allow AccessModeRead (or higher). For example, when using a fine-grained access token that only grants - // write access to one target repository, `RepoReadAccessFilter()` will return a query condition that provides - // visibility for all the public repos (which have read access) and all the target private repos (which have write - // access, which is greater-than read access). - RepoReadAccessFilter() builder.Cond -} diff --git a/models/repo/mirror.go b/models/repo/mirror.go index c64f9dc734..1fe9afd8e9 100644 --- a/models/repo/mirror.go +++ b/models/repo/mirror.go @@ -6,14 +6,10 @@ package repo import ( "context" - "errors" - "net/url" "time" "forgejo.org/models/db" - "forgejo.org/modules/keying" "forgejo.org/modules/log" - "forgejo.org/modules/optional" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" ) @@ -35,9 +31,7 @@ type Mirror struct { LFS bool `xorm:"lfs_enabled NOT NULL DEFAULT false"` LFSEndpoint string `xorm:"lfs_endpoint TEXT"` - // Encrypted remote address w/ credentials; can be NULL if a mirror has not performed a sync since this field was - // introduced, in which case the remote address exists only in the repo's configured git remote on disk. - EncryptedRemoteAddress []byte `xorm:"BLOB NULL"` + RemoteAddress string `xorm:"VARCHAR(2048)"` } func init() { @@ -79,71 +73,6 @@ func (m *Mirror) ScheduleNextUpdate() { } } -// InsertMirror inserts a mirror to database. RemoteAddress must be provided so that it can be encrypted and stored -// during the insert process. -func (m *Mirror) InsertWithAddress(ctx context.Context, addr string) error { - return db.WithTx(ctx, func(ctx context.Context) error { - if _, err := db.GetEngine(ctx).Insert(m); err != nil { - return err - } - return m.UpdateRemoteAddress(ctx, addr) - }) -} - -// Stores a credential-free version of the address in `RemoteAddress`, encrypts the original into `RemoteAddressAuth`, -// and stores both in the database. The ID of the mirror must be known, so this must be done after the mirror is -// inserted. -func (m *Mirror) UpdateRemoteAddress(ctx context.Context, addr string) error { - if m.ID == 0 { - return errors.New("must persist mirror to database before using UpdateRemoteAddress") - } - - m.EncryptedRemoteAddress = keying.PullMirror.Encrypt( - []byte(addr), - keying.ColumnAndID("remote_address_auth", m.ID), - ) - _, err := db.GetEngine(ctx).ID(m.ID).Cols("encrypted_remote_address").Update(m) - return err -} - -// Retrieves the encrypted remote address and decrypts it. Note that this field is expected to be absent for mirrors -// created before the introduction of EncryptedRemoteAddress, in which case credentials are not known to Forgejo -// directly (but may be on-disk in the repository's config file) and None will be returned. -func (m *Mirror) DecryptRemoteAddress() (optional.Option[string], error) { - if m.EncryptedRemoteAddress == nil { - return optional.None[string](), nil - } - - contents, err := keying.PullMirror.Decrypt(m.EncryptedRemoteAddress, keying.ColumnAndID("remote_address_auth", m.ID)) - if err != nil { - return optional.None[string](), err - } - return optional.Some(string(contents)), nil -} - -// Retrieves the remote address but sanitizes it of sensitive credentials. May be absent for mirrors created before the -// introduction of EncryptedRemoteAddress. -func (m *Mirror) SanitizedRemoteAddress() (optional.Option[string], error) { - maybeAddr, err := m.DecryptRemoteAddress() - if err != nil { - return optional.None[string](), err - } else if has, addr := maybeAddr.Get(); has { - parsedURL, err := url.Parse(addr) - if err != nil { - return optional.None[string](), err - } - - // Remove the password if present. Retain the username for consistency with `AddAuthCredentialHelperForRemote` - // which retains the username for the `git clone` command line, which ends up as the remote URL in the mirror's - // git config. - if parsedURL.User != nil { - parsedURL.User = url.User(parsedURL.User.Username()) - } - return optional.Some(parsedURL.String()), nil - } - return optional.None[string](), nil -} - // GetMirrorByRepoID returns mirror information of a repository. func GetMirrorByRepoID(ctx context.Context, repoID int64) (*Mirror, error) { m := &Mirror{RepoID: repoID} @@ -186,3 +115,9 @@ func MirrorsIterate(ctx context.Context, limit int, f func(idx int, bean any) er } return sess.Iterate(new(Mirror), f) } + +// InsertMirror inserts a mirror to database +func InsertMirror(ctx context.Context, mirror *Mirror) error { + _, err := db.GetEngine(ctx).Insert(mirror) + return err +} diff --git a/models/repo/release.go b/models/repo/release.go index 8aa447bda8..a421930d61 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -308,14 +308,14 @@ func (opts FindReleasesOptions) ToConds() builder.Cond { if len(opts.TagNames) > 0 { cond = cond.And(builder.In("tag_name", opts.TagNames)) } - if has, value := opts.IsPreRelease.Get(); has { - cond = cond.And(builder.Eq{"is_prerelease": value}) + if opts.IsPreRelease.Has() { + cond = cond.And(builder.Eq{"is_prerelease": opts.IsPreRelease.Value()}) } - if has, value := opts.IsDraft.Get(); has { - cond = cond.And(builder.Eq{"is_draft": value}) + if opts.IsDraft.Has() { + cond = cond.And(builder.Eq{"is_draft": opts.IsDraft.Value()}) } - if has, value := opts.HasSha1.Get(); has { - if value { + if opts.HasSha1.Has() { + if opts.HasSha1.Value() { cond = cond.And(builder.Neq{"sha1": ""}) } else { cond = cond.And(builder.Eq{"sha1": ""}) @@ -608,7 +608,6 @@ func InsertReleases(ctx context.Context, rels ...*Release) error { if len(rel.Attachments) > 0 { for i := range rel.Attachments { rel.Attachments[i].ReleaseID = rel.ID - rel.Attachments[i].RepoID = rel.RepoID } if _, err := sess.NoAutoTime().Insert(rel.Attachments); err != nil { diff --git a/models/repo/release_test.go b/models/repo/release_test.go index 940de757c7..69f9333589 100644 --- a/models/repo/release_test.go +++ b/models/repo/release_test.go @@ -20,14 +20,11 @@ func TestMigrate_InsertReleases(t *testing.T) { UUID: "a0eebc91-9c0c-4ef7-bb6e-6bb9bd380a12", } r := &Release{ - RepoID: 1001, Attachments: []*Attachment{a}, } err := InsertReleases(db.DefaultContext, r) require.NoError(t, err) - - assert.EqualValues(t, 1001, unittest.AssertExistsAndLoadBean(t, &Attachment{UUID: "a0eebc91-9c0c-4ef7-bb6e-6bb9bd380a12"}).RepoID) } func TestReleaseLoadRepo(t *testing.T) { diff --git a/models/repo/repo.go b/models/repo/repo.go index 1bd779b5a0..a8432c0f90 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -9,19 +9,16 @@ import ( "errors" "fmt" "html/template" - "maps" "net" "net/url" "path/filepath" "strconv" "strings" - auth_model "forgejo.org/models/auth" "forgejo.org/models/db" "forgejo.org/models/unit" user_model "forgejo.org/models/user" "forgejo.org/modules/cache" - "forgejo.org/modules/container" "forgejo.org/modules/git" "forgejo.org/modules/log" "forgejo.org/modules/markup" @@ -544,7 +541,9 @@ func (repo *Repository) ComposeMetas(ctx context.Context) map[string]string { func (repo *Repository) ComposeDocumentMetas(ctx context.Context) map[string]string { if len(repo.DocumentRenderingMetas) == 0 { metas := map[string]string{} - maps.Copy(metas, repo.ComposeMetas(ctx)) + for k, v := range repo.ComposeMetas(ctx) { + metas[k] = v + } metas["mode"] = "document" repo.DocumentRenderingMetas = metas } @@ -785,8 +784,8 @@ func GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*Repo // getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url func getRepositoryURLPathSegments(repoURL string) []string { - if after, ok := strings.CutPrefix(repoURL, setting.AppURL); ok { - return strings.Split(after, "/") + if strings.HasPrefix(repoURL, setting.AppURL) { + return strings.Split(strings.TrimPrefix(repoURL, setting.AppURL), "/") } sshURLVariants := [4]string{ @@ -797,8 +796,8 @@ func getRepositoryURLPathSegments(repoURL string) []string { } for _, sshURL := range sshURLVariants { - if after, ok := strings.CutPrefix(repoURL, sshURL); ok { - return strings.Split(after, "/") + if strings.HasPrefix(repoURL, sshURL) { + return strings.Split(strings.TrimPrefix(repoURL, sshURL), "/") } } @@ -893,12 +892,11 @@ type CountRepositoryOptions struct { func CountRepositories(ctx context.Context, opts CountRepositoryOptions) (int64, error) { sess := db.GetEngine(ctx).Where("id > 0") - // nosemgrep: forgejo-logic-suspicious-OwnerID-check (repositories cannot be owned by system users) if opts.OwnerID > 0 { sess.And("owner_id = ?", opts.OwnerID) } - if has, value := opts.Private.Get(); has { - sess.And("is_private=?", value) + if opts.Private.Has() { + sess.And("is_private=?", opts.Private.Value()) } count, err := sess.Count(new(Repository)) @@ -1003,55 +1001,3 @@ func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed }) return nil } - -// Bulk load of all the repo_model.Repository objects for the repository resources that can be accessed by the given -// access tokens. Any access tokens which are not repository-specific tokens will not be present in the map. An -// optional filter function can be used to remove repositories (based upon a user visibility check, for example) before -// the map is constructed -- return `true` for repos to include. -func BulkGetRepositoriesForAccessTokens(ctx context.Context, tokens []*auth_model.AccessToken, filter func(*Repository) (bool, error)) (map[int64][]*Repository, error) { - // Load all the AccessTokenResourceRepo for the tokens that we're returning: - allRepoIDs := container.Set[int64]{} - repoResourcesByTokenID, err := auth_model.GetRepositoriesAccessibleWithTokens(ctx, tokens) - if err != nil { - return nil, fmt.Errorf("failed to fetch repositories for tokens: %w", err) - } - - // Load all the Repository models that are referenced by the AccessTokenResourceRepo's: - for _, repoResources := range repoResourcesByTokenID { - for _, repoResource := range repoResources { - allRepoIDs.Add(repoResource.RepoID) - } - } - reposByID, err := GetRepositoriesMapByIDs(ctx, allRepoIDs.Slice()) - if err != nil { - return nil, fmt.Errorf("failed to fetch repositories: %w", err) - } - - if filter != nil { - // Rebuild reposByID, filtering it with the provided filter function. It's more efficient to do this here, - // rather than returning the data and allowing the caller to filter it, because this guarantees one invocation - // per repository. `reposByTokenID` could have the same repository referenced by multiple access tokens. - tmp := reposByID - reposByID = make(map[int64]*Repository, len(tmp)) - for id, repo := range tmp { - if ok, err := filter(repo); err != nil { - return nil, fmt.Errorf("error filtering repo %d: %w", repo.ID, err) - } else if ok { - reposByID[id] = repo - } - } - } - - // Prepare a lookup map to access the repositories by token ID: - reposByTokenID := make(map[int64][]*Repository) - for tokenID, repoResources := range repoResourcesByTokenID { - for _, repoResource := range repoResources { - repo, ok := reposByID[repoResource.RepoID] - if ok { - reposByTokenID[tokenID] = append(reposByTokenID[tokenID], repo) - } - } - } - - return reposByTokenID, nil -} diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index a0eb0ce7c2..36fc0c8a5a 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -186,11 +186,6 @@ type SearchRepoOptions struct { // - Don't show forks, when opts.Fork is OptionalBoolNone. // - Do not display repositories that don't have a description, an icon and topics. OnlyShowRelevant bool - // Filters repositories based upon optional authorization restrictions. - AuthorizationReducer RepositoryAuthorizationReducer - // Retrieve multiple repositories by their owner name & repository name, similar to [GetRepositoryByOwnerAndName] - // but in bulk. - OwnerAndName [][2]string } // UserOwnedRepoCond returns user ownered repositories @@ -316,6 +311,23 @@ func userOrgPublicRepoCond(userID int64) builder.Cond { ) } +// userOrgPublicRepoCondPrivate returns the condition that one user could access all public repositories in private organizations +func userOrgPublicRepoCondPrivate(userID int64) builder.Cond { + return builder.And( + builder.Eq{"`repository`.is_private": false}, + builder.In("`repository`.owner_id", + builder.Select("`org_user`.org_id"). + From("org_user"). + Join("INNER", "`user`", "`user`.id = `org_user`.org_id"). + Where(builder.Eq{ + "`org_user`.uid": userID, + "`user`.`type`": user_model.UserTypeOrganization, + "`user`.visibility": structs.VisibleTypePrivate, + }), + ), + ) +} + // UserOrgPublicUnitRepoCond returns the condition that one user could access all public repositories in the special organization func UserOrgPublicUnitRepoCond(userID, orgID int64) builder.Cond { return userOrgPublicRepoCond(userID). @@ -342,12 +354,12 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { ))) } - if has, value := opts.IsPrivate.Get(); has { - cond = cond.And(builder.Eq{"is_private": value}) + if opts.IsPrivate.Has() { + cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.Value()}) } - if has, value := opts.Template.Get(); has { - cond = cond.And(builder.Eq{"is_template": value}) + if opts.Template.Has() { + cond = cond.And(builder.Eq{"is_template": opts.Template.Value()}) } // Restrict to starred repositories @@ -363,19 +375,31 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { // Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate if opts.OwnerID != 0 { accessCond := builder.NewCond() - if !opts.Collaborate.ValueOrZeroValue() { + if !opts.Collaborate.Value() { accessCond = builder.Eq{"owner_id": opts.OwnerID} } if opts.Collaborate.ValueOrDefault(true) { - collaborateCond := builder.NewCond() - // A Collaboration is: + + collaborateCond := builder.NewCond() // 1. Repository we don't own collaborateCond = collaborateCond.And(builder.Neq{"owner_id": opts.OwnerID}) - // 2. But we can have access to unit on the repo > AccessModeNone - collaborateCond = collaborateCond.And(UserAccessRepoCond("`repository`.id", opts.OwnerID)) - + // 2. But we can see because of: + { + userAccessCond := builder.NewCond() + // A. We have unit independent access + userAccessCond = userAccessCond.Or(UserAccessRepoCond("`repository`.id", opts.OwnerID)) + // B. We are in a team for + if opts.UnitType == unit.TypeInvalid { + userAccessCond = userAccessCond.Or(UserOrgTeamRepoCond("`repository`.id", opts.OwnerID)) + } else { + userAccessCond = userAccessCond.Or(userOrgTeamUnitRepoCond("`repository`.id", opts.OwnerID, opts.UnitType)) + } + // C. Public repositories in organizations that we are member of + userAccessCond = userAccessCond.Or(userOrgPublicRepoCondPrivate(opts.OwnerID)) + collaborateCond = collaborateCond.And(userAccessCond) + } if !opts.Private { collaborateCond = collaborateCond.And(builder.Expr("owner_id NOT IN (SELECT org_id FROM org_user WHERE org_user.uid = ? AND org_user.is_public = ?)", opts.OwnerID, false)) } @@ -401,7 +425,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { if opts.Keyword != "" { // separate keyword subQueryCond := builder.NewCond() - for v := range strings.SplitSeq(opts.Keyword, ",") { + for _, v := range strings.Split(opts.Keyword, ",") { if opts.TopicOnly { subQueryCond = subQueryCond.Or(builder.Eq{"topic.name": strings.ToLower(v)}) } else { @@ -416,7 +440,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { keywordCond := builder.In("id", subQuery) if !opts.TopicOnly { likes := builder.NewCond() - for v := range strings.SplitSeq(opts.Keyword, ",") { + for _, v := range strings.Split(opts.Keyword, ",") { likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)}) // If the string looks like "org/repo", match against that pattern too @@ -443,28 +467,28 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true}))) } - if has, value := opts.Fork.Get(); has || opts.OnlyShowRelevant { - if opts.OnlyShowRelevant && !has { + if opts.Fork.Has() || opts.OnlyShowRelevant { + if opts.OnlyShowRelevant && !opts.Fork.Has() { cond = cond.And(builder.Eq{"is_fork": false}) } else { - cond = cond.And(builder.Eq{"is_fork": value}) + cond = cond.And(builder.Eq{"is_fork": opts.Fork.Value()}) } } - if has, value := opts.Mirror.Get(); has { - cond = cond.And(builder.Eq{"is_mirror": value}) + if opts.Mirror.Has() { + cond = cond.And(builder.Eq{"is_mirror": opts.Mirror.Value()}) } if opts.Actor != nil && opts.Actor.IsRestricted { cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid)) } - if has, value := opts.Archived.Get(); has { - cond = cond.And(builder.Eq{"is_archived": value}) + if opts.Archived.Has() { + cond = cond.And(builder.Eq{"is_archived": opts.Archived.Value()}) } - if has, value := opts.HasMilestones.Get(); has { - if value { + if opts.HasMilestones.Has() { + if opts.HasMilestones.Value() { cond = cond.And(builder.Gt{"num_milestones": 0}) } else { cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"})) @@ -494,30 +518,6 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { cond = cond.And(subQueryCond) } - if opts.AuthorizationReducer != nil { - cond = cond.And(opts.AuthorizationReducer.RepoReadAccessFilter()) - } - - if opts.OwnerAndName != nil { - if len(opts.OwnerAndName) > 0 { - // repository is indexed on `(owner_id, lower_name)`, but not on the `owner_name` field. Plus the `owner_name` - // field isn't ToLower'd. So this becomes a subquery: - subQuery := builder.Select("inner_repo.id").From("repository", "inner_repo"). - Join("INNER", "`user`", "`user`.id = inner_repo.owner_id") - for _, ownerAndName := range opts.OwnerAndName { - subQuery.Or(builder.Eq{ - "`user`.lower_name": strings.ToLower(ownerAndName[0]), - "inner_repo.lower_name": strings.ToLower(ownerAndName[1]), - }) - } - cond = cond.And(builder.In("id", subQuery)) - } else { - // If opts.OwnerAndName is a non-nil, empty array, then we want to return zero repositories. The loop to - // build the `Eq` conditions wouldn't occur, so we would have no filtering if this wasn't special-case'd. - cond = cond.And(builder.Eq{"1": "2"}) - } - } - return cond } diff --git a/models/repo/repo_list_test.go b/models/repo/repo_list_test.go index fc32226975..085f660f15 100644 --- a/models/repo/repo_list_test.go +++ b/models/repo/repo_list_test.go @@ -179,26 +179,6 @@ func getTestCases() []struct { opts: &repo_model.SearchRepoOptions{Keyword: "user20/", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, OwnerID: 0}, count: 4, }, - { - name: "OwnerAndName Single", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerAndName: [][2]string{{"user15", "big_test_public_1"}}}, - count: 1, - }, - { - name: "OwnerAndName Multiple", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerAndName: [][2]string{{"user15", "big_test_public_1"}, {"user15", "big_test_public_2"}}}, - count: 2, - }, - { - name: "OwnerAndName Miss", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerAndName: [][2]string{{"user15", "big_test_public_1"}, {"user15", "blah blah"}}}, - count: 1, - }, - { - name: "OwnerAndName Empty", - opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerAndName: [][2]string{}}, - count: 0, - }, } return testCases @@ -351,21 +331,21 @@ func TestSearchRepository(t *testing.T) { assert.False(t, repo.IsPrivate) } - if testCase.opts.Fork.ValueOrZeroValue() && testCase.opts.Mirror.ValueOrZeroValue() { + if testCase.opts.Fork.Value() && testCase.opts.Mirror.Value() { assert.True(t, repo.IsFork && repo.IsMirror) } else { - if has, value := testCase.opts.Fork.Get(); has { - assert.Equal(t, value, repo.IsFork) + if testCase.opts.Fork.Has() { + assert.Equal(t, testCase.opts.Fork.Value(), repo.IsFork) } - if has, value := testCase.opts.Mirror.Get(); has { - assert.Equal(t, value, repo.IsMirror) + if testCase.opts.Mirror.Has() { + assert.Equal(t, testCase.opts.Mirror.Value(), repo.IsMirror) } } if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic { - if has, value := testCase.opts.Collaborate.Get(); has { - if value { + if testCase.opts.Collaborate.Has() { + if testCase.opts.Collaborate.Value() { assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID) } else { assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID) diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index 2cde375775..aa6f2fa0ae 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -220,42 +220,8 @@ func (cfg *PullRequestsConfig) GetDefaultUpdateStyle() UpdateStyle { return UpdateStyleMerge } -// Represents the current format for `sub` claim generation when using `enable-openid-connect: true` on an Action. -// -// We try to follow GitHub's format for the `sub` claim because it should allow third-party integrations, which may have -// existing knowledge and code that works with GitHub's OIDC tokens, to reuse that knowledge and code in the future to -// implement Forgejo support. As GitHub's format changes, we may implement those format changes to maintain that -// familarity. Forgejo isn't required to do so, though -- if future changes from GitHub don't make sense for Forgejo, -// or vice-versa, this format matching effort may be discarded. -type OIDCSubjectFormat string - -var ( - // Default is the current, most preferred method of generating an `sub` JWT claim for an Actions JWT token. It is - // an empty string which allows [ActionsConfig] to always default to the current preferred default value for new - // repositories. At present, it is a `sub` claim that contains information about the repository owner and - // repository where the action occurred, their immutable identifiers (ID numbers), and the event that triggered the - // Action. - // - // Immutable identifiers have been added since OIDCSubjectFormatLegacyForgejo15. The intent of adding them is to - // protect resource servers which may be requiring a specific subject claim from having that claim be impersonated - // when a user or repository are renamed or deleted. For example, if a JWT from my-org/my-repo is trusted, but then - // my-org is deleted and a new user takes ownership of the name my-org, they should not be granted the same trust. - // - // Example: repo:my-org-123456/my-repo-456789:ref:refs/heads/main - OIDCSubjectFormatDefault OIDCSubjectFormat // defaults to "" - - // The `sub` JWT claim generation that was shipped in Forgejo 15. Contains information about the repository owner - // and repository where the action occurred and the event that triggered the Action. - // - // Example: repo:my-org/my-repo:ref:refs/heads/main - OIDCSubjectFormatLegacyForgejo15 OIDCSubjectFormat = "legacy-forgejo-v15" -) - type ActionsConfig struct { DisabledWorkflows []string - - // Format of the OIDC 'sub' claim that will be used when `enable-openid-connect` is true in an Action. - OIDCSubjectFormat OIDCSubjectFormat `json:",omitempty"` } func (cfg *ActionsConfig) EnableWorkflow(file string) { @@ -271,8 +237,10 @@ func (cfg *ActionsConfig) IsWorkflowDisabled(file string) bool { } func (cfg *ActionsConfig) DisableWorkflow(file string) { - if slices.Contains(cfg.DisabledWorkflows, file) { - return + for _, workflow := range cfg.DisabledWorkflows { + if file == workflow { + return + } } cfg.DisabledWorkflows = append(cfg.DisabledWorkflows, file) diff --git a/models/repo/upload.go b/models/repo/upload.go index 67b5409650..a213cb1986 100644 --- a/models/repo/upload.go +++ b/models/repo/upload.go @@ -117,7 +117,7 @@ func DeleteUploads(ctx context.Context, uploads ...*Upload) (err error) { defer committer.Close() ids := make([]int64, len(uploads)) - for i := range uploads { + for i := 0; i < len(uploads); i++ { ids[i] = uploads[i].ID } if err = db.DeleteByIDs[Upload](ctx, ids...); err != nil { diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index e46e5e1d5a..ca02c1e3f0 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -17,14 +17,13 @@ import ( ) // GetStarredRepos returns the repos starred by a particular user -func GetStarredRepos(ctx context.Context, userID int64, private bool, listOptions db.ListOptions, reducer RepositoryAuthorizationReducer) ([]*Repository, error) { +func GetStarredRepos(ctx context.Context, userID int64, private bool, listOptions db.ListOptions) ([]*Repository, error) { sess := db.GetEngine(ctx). Where("star.uid=?", userID). Join("LEFT", "star", "`repository`.id=`star`.repo_id") if !private { sess = sess.And("is_private=?", false) } - sess = sess.And(reducer.RepoReadAccessFilter()) if listOptions.Page != 0 { sess = db.SetSessionPagination(sess, &listOptions) @@ -38,7 +37,7 @@ func GetStarredRepos(ctx context.Context, userID int64, private bool, listOption } // GetWatchedRepos returns the repos watched by a particular user -func GetWatchedRepos(ctx context.Context, userID int64, private bool, listOptions db.ListOptions, reducer RepositoryAuthorizationReducer) ([]*Repository, int64, error) { +func GetWatchedRepos(ctx context.Context, userID int64, private bool, listOptions db.ListOptions) ([]*Repository, int64, error) { sess := db.GetEngine(ctx). Where("watch.user_id=?", userID). And("`watch`.mode<>?", WatchModeDont). @@ -46,7 +45,6 @@ func GetWatchedRepos(ctx context.Context, userID int64, private bool, listOption if !private { sess = sess.And("is_private=?", false) } - sess = sess.And(reducer.RepoReadAccessFilter()) if listOptions.Page != 0 { sess = db.SetSessionPagination(sess, &listOptions) diff --git a/models/repo/watch.go b/models/repo/watch.go index 3a20880a5c..e5d4b2f90e 100644 --- a/models/repo/watch.go +++ b/models/repo/watch.go @@ -17,20 +17,12 @@ type WatchMode int8 const ( // WatchModeNone don't watch - // This means there is no Watch record in the db. - // We never store this mode in the db and instead remove the record from the db. - // Furthermore, this means there is a WatchMode for all combinations of user and repo. WatchModeNone WatchMode = iota // 0 // WatchModeNormal watch repository (from other sources) - // This means the user explicitly chose to watch the repo. WatchModeNormal // 1 // WatchModeDont explicit don't auto-watch - // This means the user explicitly removed themselves as a watcher. - // Then the AutoWatchOnChanges feature doesn't make the user a watcher when they push to the repo. WatchModeDont // 2 // WatchModeAuto watch repository (from AutoWatchOnChanges) - // This is used when the user pushed to the repo and setting.Service.AutoWatchOnChanges is true. - // That way we can differentiate people explicitly watching the repo and people only watching it because of the AutoWatchOnChanges feature. WatchModeAuto // 3 ) @@ -82,7 +74,6 @@ func watchRepoMode(ctx context.Context, watch Watch, mode WatchMode) (err error) } hadrec := watch.Mode != WatchModeNone - // WatchModeNone means there is no record in the db. needsrec := mode != WatchModeNone repodiff := 0 @@ -178,8 +169,8 @@ func GetRepoWatchers(ctx context.Context, repoID int64, opts db.ListOptions) ([] } // WatchIfAuto subscribes to repo if AutoWatchOnChanges is set -func WatchIfAuto(ctx context.Context, userID, repoID int64) error { - if !setting.Service.AutoWatchOnChanges { +func WatchIfAuto(ctx context.Context, userID, repoID int64, isWrite bool) error { + if !isWrite || !setting.Service.AutoWatchOnChanges { return nil } watch, err := GetWatch(ctx, userID, repoID) diff --git a/models/repo/watch_test.go b/models/repo/watch_test.go index ccc56ad168..698f6a5f49 100644 --- a/models/repo/watch_test.go +++ b/models/repo/watch_test.go @@ -74,13 +74,13 @@ func TestWatchIfAuto(t *testing.T) { prevCount := repo.NumWatches // Must not add watch - require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 8, 1)) + require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 8, 1, true)) watchers, err = repo_model.GetRepoWatchers(db.DefaultContext, repo.ID, db.ListOptions{Page: 1}) require.NoError(t, err) assert.Len(t, watchers, prevCount) // Should not add watch - require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 10, 1)) + require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 10, 1, true)) watchers, err = repo_model.GetRepoWatchers(db.DefaultContext, repo.ID, db.ListOptions{Page: 1}) require.NoError(t, err) assert.Len(t, watchers, prevCount) @@ -88,19 +88,19 @@ func TestWatchIfAuto(t *testing.T) { setting.Service.AutoWatchOnChanges = true // Must not add watch - require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 8, 1)) + require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 8, 1, true)) watchers, err = repo_model.GetRepoWatchers(db.DefaultContext, repo.ID, db.ListOptions{Page: 1}) require.NoError(t, err) assert.Len(t, watchers, prevCount) // Should not add watch - // We simply don't WatchIfAuto + require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1, false)) watchers, err = repo_model.GetRepoWatchers(db.DefaultContext, repo.ID, db.ListOptions{Page: 1}) require.NoError(t, err) assert.Len(t, watchers, prevCount) // Should add watch - require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1)) + require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1, true)) watchers, err = repo_model.GetRepoWatchers(db.DefaultContext, repo.ID, db.ListOptions{Page: 1}) require.NoError(t, err) assert.Len(t, watchers, prevCount+1) @@ -112,7 +112,7 @@ func TestWatchIfAuto(t *testing.T) { assert.Len(t, watchers, prevCount) // Must not add watch - require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1)) + require.NoError(t, repo_model.WatchIfAuto(db.DefaultContext, 12, 1, true)) watchers, err = repo_model.GetRepoWatchers(db.DefaultContext, repo.ID, db.ListOptions{Page: 1}) require.NoError(t, err) assert.Len(t, watchers, prevCount) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 8031a20964..f515f1bcf0 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -175,11 +175,11 @@ func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_m } // GetPendingTransfers returns the pending transfers of recipient which were sent by by doer. -func GetPendingTransferIDs(ctx context.Context, recipientID, doerID int64) ([]int64, error) { +func GetPendingTransferIDs(ctx context.Context, reciepientID, doerID int64) ([]int64, error) { pendingTransferIDs := make([]int64, 0, 8) return pendingTransferIDs, db.GetEngine(ctx).Table("repo_transfer"). Where("doer_id = ?", doerID). - And("recipient_id = ?", recipientID). + And("recipient_id = ?", reciepientID). Cols("id"). Find(&pendingTransferIDs) } diff --git a/models/repo_transfer_test.go b/models/repo_transfer_test.go index 58fc63a263..7f01ac2b97 100644 --- a/models/repo_transfer_test.go +++ b/models/repo_transfer_test.go @@ -17,10 +17,10 @@ import ( func TestGetPendingTransferIDs(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) - recipient := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - pendingTransfer := unittest.AssertExistsAndLoadBean(t, &RepoTransfer{RecipientID: recipient.ID, DoerID: doer.ID}) + reciepient := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + pendingTransfer := unittest.AssertExistsAndLoadBean(t, &RepoTransfer{RecipientID: reciepient.ID, DoerID: doer.ID}) - pendingTransferIDs, err := GetPendingTransferIDs(db.DefaultContext, recipient.ID, doer.ID) + pendingTransferIDs, err := GetPendingTransferIDs(db.DefaultContext, reciepient.ID, doer.ID) require.NoError(t, err) if assert.Len(t, pendingTransferIDs, 1) { assert.Equal(t, pendingTransfer.ID, pendingTransferIDs[0]) diff --git a/models/secret/TestSecretGetSecretByID/secret.yml b/models/secret/TestSecretGetSecretByID/secret.yml deleted file mode 100644 index 08e3e2c561..0000000000 --- a/models/secret/TestSecretGetSecretByID/secret.yml +++ /dev/null @@ -1,23 +0,0 @@ -- id: 637340 - name: TEST_SECRET - owner_id: 3 - repo_id: 0 - # very secret - data: 0x0e17d357077de0c1f72e9d87e1899c8026a207fcbbc0f1a2a79d0cb305da39fa3e9fe201b085e963fa1f9eeb59b4ff9ee6d748 - created_unix: 1773692671 - -- id: 637341 - name: ANOTHER_SECRET - owner_id: 0 - repo_id: 62 # user2/test_workflows - # also very secret - data: 0xb697cd4a66b02bc36d0afddcb6f158d7602f6cf0b0d31a0c96e178469e8b66babccb30b95e01c84824add04c5bfe91b9b773a21a78088a3e - created_unix: 1773692672 - -- id: 637342 - name: TEST_SECRET - owner_id: 1 - repo_id: 0 - # super secret - data: 0xd97d8cb662c07ca94953a388bc93209f713281a8b0d25499359cbd03a1fbe565a2a0ba183b8290fc110ee6b1c6437c569451e0c3 - created_unix: 1773692673 diff --git a/models/secret/secret.go b/models/secret/secret.go index 758f346f22..0a5db17690 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -6,10 +6,11 @@ package secret import ( "context" "fmt" - "regexp" "strings" + actions_model "forgejo.org/models/actions" "forgejo.org/models/db" + actions_module "forgejo.org/modules/actions" "forgejo.org/modules/keying" "forgejo.org/modules/log" "forgejo.org/modules/timeutil" @@ -18,13 +19,6 @@ import ( "xorm.io/builder" ) -var ( - namePattern = regexp.MustCompile("(?i)^[A-Z_][A-Z0-9_]*$") - forbiddenPrefixPattern = regexp.MustCompile("(?i)^(FORGEJO_|GITEA_|GITHUB_|[0-9])") - - ErrInvalidName = util.NewInvalidArgumentErrorf("invalid secret name") -) - // Secret represents a secret // // It can be: @@ -71,9 +65,6 @@ func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, dat if ownerID == 0 && repoID == 0 { return nil, fmt.Errorf("%w: ownerID and repoID cannot be both zero, global secrets are not supported", util.ErrInvalidArgument) } - if err := ValidateName(name); err != nil { - return nil, err - } secret := &Secret{ OwnerID: ownerID, @@ -86,7 +77,7 @@ func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, dat return err } - secret.SetData(data) + secret.SetSecret(data) _, err := db.GetEngine(ctx).ID(secret.ID).Cols("data").Update(secret) return err }) @@ -125,90 +116,44 @@ func (opts FindSecretsOptions) ToConds() builder.Cond { return cond } -func (s *Secret) SetData(data string) { - normalizedData := util.ReserveLineBreakForTextarea(data) - s.Data = keying.ActionSecret.Encrypt([]byte(normalizedData), keying.ColumnAndID("data", s.ID)) +func (s *Secret) SetSecret(data string) { + s.Data = keying.ActionSecret.Encrypt([]byte(data), keying.ColumnAndID("data", s.ID)) } -func (s *Secret) GetDecryptedData() (string, error) { - key := keying.ActionSecret - v, err := key.Decrypt(s.Data, keying.ColumnAndID("data", s.ID)) - if err != nil { - return "", fmt.Errorf("unable to decrypt secret[id=%d,name=%q]: %w", s.ID, s.Name, err) - } - - return string(v), nil -} - -func GetSecretByID(ctx context.Context, ownerID, repoID, id int64) (*Secret, error) { - query := db.GetEngine(ctx).Where("id=?", id) - - if repoID > 0 { - query = query.And(builder.Eq{"repo_id": repoID}) - } else if ownerID > 0 { - query = query.And(builder.Eq{"owner_id": ownerID}) - } else { - return nil, fmt.Errorf("ownerID and repoID cannot be simultaneously 0") - } - - var secret Secret - has, err := query.Get(&secret) - - if err != nil { - return nil, err - } else if !has { - return nil, fmt.Errorf("secret with ID %d: %w", id, util.ErrNotExist) - } - return &secret, nil -} - -func UpdateSecret(ctx context.Context, secret *Secret, columns ...string) error { - e := db.GetEngine(ctx) - - if err := ValidateName(secret.Name); err != nil { - return err - } - secret.Name = strings.ToUpper(secret.Name) - - var err error - if len(columns) == 0 { - _, err = e.ID(secret.ID).AllCols().Update(secret) - } else { - _, err = e.ID(secret.ID).Cols(columns...).Update(secret) - } - - return err -} - -func FetchActionSecrets(ctx context.Context, ownerID, repoID int64) (map[string]string, error) { +func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) { secrets := map[string]string{} - ownerSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{OwnerID: ownerID}) + secrets["GITHUB_TOKEN"] = task.Token + secrets["GITEA_TOKEN"] = task.Token + secrets["FORGEJO_TOKEN"] = task.Token + + if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget { + // ignore secrets for fork pull request, except GITHUB_TOKEN, GITEA_TOKEN and FORGEJO_TOKEN which are automatically generated. + // for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch + // see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target + return secrets, nil + } + + ownerSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID}) if err != nil { - log.Error("find secrets of owner %v: %v", ownerID, err) + log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err) return nil, err } - repoSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{RepoID: repoID}) + repoSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{RepoID: task.Job.Run.RepoID}) if err != nil { - log.Error("find secrets of repo %v: %v", repoID, err) + log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) return nil, err } + key := keying.ActionSecret for _, secret := range append(ownerSecrets, repoSecrets...) { - decryptedData, err := secret.GetDecryptedData() + v, err := key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) if err != nil { - log.Error("%v", err) + log.Error("unable to decrypt secret[id=%d,name=%q]: %v", secret.ID, secret.Name, err) return nil, err } - secrets[secret.Name] = decryptedData + secrets[secret.Name] = string(v) } return secrets, nil } - -func ValidateName(name string) error { - if !namePattern.MatchString(name) || forbiddenPrefixPattern.MatchString(name) { - return ErrInvalidName - } - return nil -} diff --git a/models/secret/secret_test.go b/models/secret/secret_test.go index e323986084..76b673f2fe 100644 --- a/models/secret/secret_test.go +++ b/models/secret/secret_test.go @@ -4,9 +4,10 @@ package secret import ( - "strings" "testing" + "forgejo.org/models/actions" + "forgejo.org/models/repo" "forgejo.org/models/unittest" "forgejo.org/modules/keying" "forgejo.org/modules/util" @@ -84,220 +85,19 @@ func TestInsertEncryptedSecret(t *testing.T) { }) }) - t.Run("Rejects invalid name", func(t *testing.T) { - _, err := InsertEncryptedSecret(t.Context(), 2, 0, "invalid name", "some secret") - require.ErrorContains(t, err, "invalid secret name") - }) - - t.Run("FetchActionSecrets", func(t *testing.T) { - secrets, err := FetchActionSecrets(t.Context(), 2, 1) + 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"]) }) } - -func TestSecretDataIsNormalized(t *testing.T) { - secret := Secret{ID: 494, OwnerID: 829, RepoID: 0, Name: "A_SECRET"} - - secret.SetData(" \r\ndatà\t ") - - decryptedData, err := secret.GetDecryptedData() - require.NoError(t, err) - assert.Equal(t, " \ndatà\t ", decryptedData) -} - -func TestSecretGetDecryptedData(t *testing.T) { - t.Run("Recovers original data", func(t *testing.T) { - secret := Secret{ID: 494, OwnerID: 829, RepoID: 0, Name: "A_SECRET"} - secret.SetData("data") - - decryptedData, err := secret.GetDecryptedData() - require.NoError(t, err) - assert.Equal(t, "data", decryptedData) - }) - - t.Run("Returns error if data cannot be decrypted", func(t *testing.T) { - secret := Secret{ID: 494, OwnerID: 829, RepoID: 0, Name: "A_SECRET"} - secret.SetData("data") - - // Changing the ID without updating the secret makes the secret irrecoverable. - secret.ID++ - - decryptedData, err := secret.GetDecryptedData() - assert.Empty(t, decryptedData) - assert.ErrorContains(t, err, "unable to decrypt secret[id=495,name=\"A_SECRET\"]") - }) -} - -func TestSecretGetSecretByID(t *testing.T) { - defer unittest.OverrideFixtures("models/secret/TestSecretGetSecretByID")() - require.NoError(t, unittest.PrepareTestDatabase()) - - testCases := []struct { - name string - ownerID int64 - repoID int64 - id int64 - expectedName string - expectedData string - expectedError string - }{ - { - name: "Organization secret", - ownerID: 3, - repoID: 0, - id: 637340, - expectedName: "TEST_SECRET", - expectedData: "very secret", - }, - { - name: "Owner mismatch", - ownerID: 4, - repoID: 0, - id: 637340, - expectedError: "secret with ID 637340: resource does not exist", - }, - { - name: "Repository mismatch", - ownerID: 0, - repoID: 1, - id: 637340, - expectedError: "secret with ID 637340: resource does not exist", - }, - { - name: "Repository secret", - ownerID: 0, - repoID: 62, - id: 637341, - expectedName: "ANOTHER_SECRET", - expectedData: "also very secret", - }, - { - name: "Unsupported instance secret", - ownerID: 0, - repoID: 0, - id: 637341, - expectedError: "ownerID and repoID cannot be simultaneously 0", - }, - { - name: "User secret", - ownerID: 1, - repoID: 0, - id: 637342, - expectedName: "TEST_SECRET", - expectedData: "super secret", - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - secret, err := GetSecretByID(t.Context(), testCase.ownerID, testCase.repoID, testCase.id) - - if testCase.expectedError != "" { - assert.ErrorContains(t, err, testCase.expectedError) - } else { - require.NoError(t, err) - assert.Equal(t, testCase.id, secret.ID) - assert.Equal(t, testCase.ownerID, secret.OwnerID) - assert.Equal(t, testCase.repoID, secret.RepoID) - assert.Equal(t, testCase.expectedName, secret.Name) - - data, err := secret.GetDecryptedData() - require.NoError(t, err) - assert.Equal(t, testCase.expectedData, data) - } - }) - } -} - -func TestSecretUpdateSecret(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - secret, err := InsertEncryptedSecret(t.Context(), 2, 0, "a_secret", "very secret") - require.NoError(t, err) - - secret.Name = "new_name" - secret.SetData("also very secret") - - err = UpdateSecret(t.Context(), secret) - require.NoError(t, err) - - updatedSecret := unittest.AssertExistsAndLoadBean(t, &Secret{ID: secret.ID}) - decryptedData, err := updatedSecret.GetDecryptedData() - require.NoError(t, err) - - assert.Equal(t, "NEW_NAME", updatedSecret.Name) - assert.Equal(t, "also very secret", decryptedData) -} - -func TestSecretUpdateSecret_RejectsInvalidName(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - secret, err := InsertEncryptedSecret(t.Context(), 2, 0, "a_secret", "very secret") - require.NoError(t, err) - - secret.Name = "GITHUB_IS_REJECTED" // Because it starts with `GITHUB_`. - secret.SetData("also very secret") - - err = UpdateSecret(t.Context(), secret) - require.ErrorContains(t, err, "invalid secret name") - - updatedSecret := unittest.AssertExistsAndLoadBean(t, &Secret{ID: secret.ID}) - decryptedData, err := updatedSecret.GetDecryptedData() - require.NoError(t, err) - - assert.Equal(t, "A_SECRET", updatedSecret.Name) - assert.Equal(t, "very secret", decryptedData) -} - -func TestSecretValidateName(t *testing.T) { - testCases := []struct { - name string - valid bool - }{ - {"FORGEJO_", false}, - {"PRE_FORGEJO_", true}, - {"PRE_FORGEJO_SUF", true}, - {"FORGEJO_123", false}, - {"FORGEJO_ABC", false}, - {"GITEA_", false}, - {"PRE_GITEA_", true}, - {"PRE_GITEA_SUF", true}, - {"GITEA_123", false}, - {"GITEA_ABC", false}, - {"GITHUB_", false}, - {"PRE_GITHUB_", true}, - {"PRE_GITHUB_SUF", true}, - {"GITHUB_123", false}, - {"GITHUB_ABC", false}, - {"123_TEST", false}, - {"CI", true}, - {"_CI", true}, - {"CI_", true}, - {"CI123", true}, - {"CIABC", true}, - {"FORGEJO", true}, - {"FORGEJO123", true}, - {"FORGEJOABC", true}, - {"GITEA", true}, - {"GITEA123", true}, - {"GITEAABC", true}, - {"GITHUB", true}, - {"GITHUB123", true}, - {"GITHUBABC", true}, - {"_123_TEST", true}, - } - for _, tC := range testCases { - t.Run(tC.name, func(t *testing.T) { - t.Helper() - if tC.valid { - assert.NoError(t, ValidateName(tC.name)) - assert.NoError(t, ValidateName(strings.ToLower(tC.name))) - } else { - require.ErrorIs(t, ValidateName(tC.name), ErrInvalidName) - require.ErrorIs(t, ValidateName(strings.ToLower(tC.name)), ErrInvalidName) - } - }) - } -} diff --git a/models/system/notice.go b/models/system/notice.go index 7801686889..b1fdd2e4f2 100644 --- a/models/system/notice.go +++ b/models/system/notice.go @@ -38,8 +38,6 @@ func init() { } // TrStr returns a translation format string. -// -//llu:returnsTrKey func (n *Notice) TrStr() string { return fmt.Sprintf("admin.notices.type_%d", n.Type) } diff --git a/models/unit/tests/units.go b/models/unit/tests/units.go deleted file mode 100644 index 7507b80e2a..0000000000 --- a/models/unit/tests/units.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package tests - -import ( - unit_model "forgejo.org/models/unit" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" -) - -func SaveUnits() func() { - disabledGlobal := unit_model.DisabledRepoUnitsGet() - restoreDisabledGlobal := func() { - unit_model.DisabledRepoUnitsSet(disabledGlobal) - } - restoreDisabledRepo := test.MockProtect(&setting.Repository.DisabledRepoUnits) - - restoreDefaultGlobal := test.MockProtect(&unit_model.DefaultRepoUnits) - restoreDefaultRepo := test.MockProtect(&setting.Repository.DefaultRepoUnits) - - restoreForkGlobal := test.MockProtect(&unit_model.DefaultForkRepoUnits) - restoreForkRepo := test.MockProtect(&setting.Repository.DefaultForkRepoUnits) - - return func() { - restoreDisabledGlobal() - restoreDisabledRepo() - - restoreDefaultGlobal() - restoreDefaultRepo() - - restoreForkGlobal() - restoreForkRepo() - } -} diff --git a/models/unit/tests/units_test.go b/models/unit/tests/units_test.go deleted file mode 100644 index acf543285f..0000000000 --- a/models/unit/tests/units_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package tests - -import ( - "testing" - - unit_model "forgejo.org/models/unit" - "forgejo.org/modules/setting" - - "github.com/stretchr/testify/assert" -) - -func TestSaveUnits(t *testing.T) { - restoreUnits := SaveUnits() - - unit_model.DisabledRepoUnitsSet([]unit_model.Type{unit_model.TypeInvalid}) - setting.Repository.DisabledRepoUnits = []string{"invalid"} - - unit_model.DefaultRepoUnits = []unit_model.Type{unit_model.TypeInvalid} - setting.Repository.DefaultRepoUnits = []string{"invalid"} - - unit_model.DefaultForkRepoUnits = []unit_model.Type{unit_model.TypeInvalid} - setting.Repository.DefaultForkRepoUnits = []string{"invalid"} - - restoreUnits() - - assert.NotEqual(t, []unit_model.Type{unit_model.TypeInvalid}, unit_model.DisabledRepoUnitsGet()) - assert.NotEqual(t, []string{"invalid"}, setting.Repository.DisabledRepoUnits) - - assert.NotEqual(t, []unit_model.Type{unit_model.TypeInvalid}, unit_model.DefaultRepoUnits) - assert.NotEqual(t, []string{"invalid"}, setting.Repository.DefaultRepoUnits) - - assert.NotEqual(t, []unit_model.Type{unit_model.TypeInvalid}, unit_model.DefaultForkRepoUnits) - assert.NotEqual(t, []string{"invalid"}, setting.Repository.DefaultForkRepoUnits) -} diff --git a/models/unit/unit.go b/models/unit/unit.go index 2a31c804aa..6b4f2765ee 100644 --- a/models/unit/unit.go +++ b/models/unit/unit.go @@ -7,7 +7,6 @@ package unit import ( "errors" "fmt" - "slices" "strings" "sync/atomic" @@ -142,7 +141,7 @@ func DisabledRepoUnitsSet(v []Type) { // Get valid set of default repository units from settings func validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type { - units := slices.Clone(defaultUnits) + units := defaultUnits // Use setting if not empty if len(settingDefaultUnits) > 0 { @@ -248,12 +247,22 @@ func LoadUnitConfig() error { // UnitGlobalDisabled checks if unit type is global disabled func (u Type) UnitGlobalDisabled() bool { - return slices.Contains(DisabledRepoUnitsGet(), u) + for _, ud := range DisabledRepoUnitsGet() { + if u == ud { + return true + } + } + return false } // CanBeDefault checks if the unit type can be a default repo unit func (u *Type) CanBeDefault() bool { - return !slices.Contains(NotAllowedDefaultRepoUnits, *u) + for _, nadU := range NotAllowedDefaultRepoUnits { + if *u == nadU { + return false + } + } + return true } // Unit is a section of one repository @@ -422,7 +431,7 @@ func AllUnitKeyNames() []string { return res } -// MinUnitAccessMode returns the minimal permission of the permission map +// MinUnitAccessMode returns the minial permission of the permission map func MinUnitAccessMode(unitsMap map[Type]perm.AccessMode) perm.AccessMode { res := perm.AccessModeNone for t, mode := range unitsMap { @@ -431,7 +440,7 @@ func MinUnitAccessMode(unitsMap map[Type]perm.AccessMode) perm.AccessMode { continue } - // get the minimal permission greater than AccessModeNone except all are AccessModeNone + // get the minial permission great than AccessModeNone except all are AccessModeNone if mode > perm.AccessModeNone && (res == perm.AccessModeNone || mode < res) { res = mode } diff --git a/models/unit/unit_test.go b/models/unit/unit_test.go index 1da15c4d85..efcad4a405 100644 --- a/models/unit/unit_test.go +++ b/models/unit/unit_test.go @@ -1,13 +1,11 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package unit_test +package unit import ( "testing" - unit_model "forgejo.org/models/unit" - "forgejo.org/models/unit/tests" "forgejo.org/modules/setting" "github.com/stretchr/testify/assert" @@ -16,47 +14,83 @@ import ( func TestLoadUnitConfig(t *testing.T) { t.Run("regular", func(t *testing.T) { - defer tests.SaveUnits()() + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) { + DisabledRepoUnitsSet(disabledRepoUnits) + DefaultRepoUnits = defaultRepoUnits + DefaultForkRepoUnits = defaultForkRepoUnits + }(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits) + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) { + setting.Repository.DisabledRepoUnits = disabledRepoUnits + setting.Repository.DefaultRepoUnits = defaultRepoUnits + setting.Repository.DefaultForkRepoUnits = defaultForkRepoUnits + }(setting.Repository.DisabledRepoUnits, setting.Repository.DefaultRepoUnits, setting.Repository.DefaultForkRepoUnits) setting.Repository.DisabledRepoUnits = []string{"repo.issues"} setting.Repository.DefaultRepoUnits = []string{"repo.code", "repo.releases", "repo.issues", "repo.pulls"} setting.Repository.DefaultForkRepoUnits = []string{"repo.releases"} - require.NoError(t, unit_model.LoadUnitConfig()) - assert.Equal(t, []unit_model.Type{unit_model.TypeIssues}, unit_model.DisabledRepoUnitsGet()) - assert.Equal(t, []unit_model.Type{unit_model.TypeCode, unit_model.TypeReleases, unit_model.TypePullRequests}, unit_model.DefaultRepoUnits) - assert.Equal(t, []unit_model.Type{unit_model.TypeReleases}, unit_model.DefaultForkRepoUnits) + require.NoError(t, LoadUnitConfig()) + assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet()) + assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits) + assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits) }) t.Run("invalid", func(t *testing.T) { - defer tests.SaveUnits()() + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) { + DisabledRepoUnitsSet(disabledRepoUnits) + DefaultRepoUnits = defaultRepoUnits + DefaultForkRepoUnits = defaultForkRepoUnits + }(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits) + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) { + setting.Repository.DisabledRepoUnits = disabledRepoUnits + setting.Repository.DefaultRepoUnits = defaultRepoUnits + setting.Repository.DefaultForkRepoUnits = defaultForkRepoUnits + }(setting.Repository.DisabledRepoUnits, setting.Repository.DefaultRepoUnits, setting.Repository.DefaultForkRepoUnits) setting.Repository.DisabledRepoUnits = []string{"repo.issues", "invalid.1"} setting.Repository.DefaultRepoUnits = []string{"repo.code", "invalid.2", "repo.releases", "repo.issues", "repo.pulls"} setting.Repository.DefaultForkRepoUnits = []string{"invalid.3", "repo.releases"} - require.NoError(t, unit_model.LoadUnitConfig()) - assert.Equal(t, []unit_model.Type{unit_model.TypeIssues}, unit_model.DisabledRepoUnitsGet()) - assert.Equal(t, []unit_model.Type{unit_model.TypeCode, unit_model.TypeReleases, unit_model.TypePullRequests}, unit_model.DefaultRepoUnits) - assert.Equal(t, []unit_model.Type{unit_model.TypeReleases}, unit_model.DefaultForkRepoUnits) + require.NoError(t, LoadUnitConfig()) + assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet()) + assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits) + assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits) }) t.Run("duplicate", func(t *testing.T) { - defer tests.SaveUnits()() + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) { + DisabledRepoUnitsSet(disabledRepoUnits) + DefaultRepoUnits = defaultRepoUnits + DefaultForkRepoUnits = defaultForkRepoUnits + }(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits) + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) { + setting.Repository.DisabledRepoUnits = disabledRepoUnits + setting.Repository.DefaultRepoUnits = defaultRepoUnits + setting.Repository.DefaultForkRepoUnits = defaultForkRepoUnits + }(setting.Repository.DisabledRepoUnits, setting.Repository.DefaultRepoUnits, setting.Repository.DefaultForkRepoUnits) setting.Repository.DisabledRepoUnits = []string{"repo.issues", "repo.issues"} setting.Repository.DefaultRepoUnits = []string{"repo.code", "repo.releases", "repo.issues", "repo.pulls", "repo.code"} setting.Repository.DefaultForkRepoUnits = []string{"repo.releases", "repo.releases"} - require.NoError(t, unit_model.LoadUnitConfig()) - assert.Equal(t, []unit_model.Type{unit_model.TypeIssues}, unit_model.DisabledRepoUnitsGet()) - assert.Equal(t, []unit_model.Type{unit_model.TypeCode, unit_model.TypeReleases, unit_model.TypePullRequests}, unit_model.DefaultRepoUnits) - assert.Equal(t, []unit_model.Type{unit_model.TypeReleases}, unit_model.DefaultForkRepoUnits) + require.NoError(t, LoadUnitConfig()) + assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet()) + assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits) + assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits) }) t.Run("empty_default", func(t *testing.T) { - defer tests.SaveUnits()() + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) { + DisabledRepoUnitsSet(disabledRepoUnits) + DefaultRepoUnits = defaultRepoUnits + DefaultForkRepoUnits = defaultForkRepoUnits + }(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits) + defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) { + setting.Repository.DisabledRepoUnits = disabledRepoUnits + setting.Repository.DefaultRepoUnits = defaultRepoUnits + setting.Repository.DefaultForkRepoUnits = defaultForkRepoUnits + }(setting.Repository.DisabledRepoUnits, setting.Repository.DefaultRepoUnits, setting.Repository.DefaultForkRepoUnits) setting.Repository.DisabledRepoUnits = []string{"repo.issues", "repo.issues"} setting.Repository.DefaultRepoUnits = []string{} setting.Repository.DefaultForkRepoUnits = []string{"repo.releases", "repo.releases"} - require.NoError(t, unit_model.LoadUnitConfig()) - assert.Equal(t, []unit_model.Type{unit_model.TypeIssues}, unit_model.DisabledRepoUnitsGet()) - assert.ElementsMatch(t, []unit_model.Type{unit_model.TypeCode, unit_model.TypePullRequests, unit_model.TypeReleases, unit_model.TypeWiki, unit_model.TypePackages, unit_model.TypeProjects, unit_model.TypeActions}, unit_model.DefaultRepoUnits) - assert.Equal(t, []unit_model.Type{unit_model.TypeReleases}, unit_model.DefaultForkRepoUnits) + require.NoError(t, LoadUnitConfig()) + assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet()) + assert.ElementsMatch(t, []Type{TypeCode, TypePullRequests, TypeReleases, TypeWiki, TypePackages, TypeProjects, TypeActions}, DefaultRepoUnits) + assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits) }) } diff --git a/models/unittest/fixture_loader.go b/models/unittest/fixture_loader.go index 3cf2efdced..5aea06550c 100644 --- a/models/unittest/fixture_loader.go +++ b/models/unittest/fixture_loader.go @@ -151,8 +151,8 @@ func (l *loader) buildFixtureFile(fixturePath string) (*fixtureFile, error) { switch v := value.(type) { case string: // Try to decode hex. - if after, ok := strings.CutPrefix(v, "0x"); ok { - value, err = hex.DecodeString(after) + if strings.HasPrefix(v, "0x") { + value, err = hex.DecodeString(strings.TrimPrefix(v, "0x")) if err != nil { return nil, err } diff --git a/models/unittest/mock_http.go b/models/unittest/mock_http.go index 5e420533d8..b8413104b3 100644 --- a/models/unittest/mock_http.go +++ b/models/unittest/mock_http.go @@ -102,13 +102,13 @@ func NewMockWebServer(t *testing.T, liveServerBaseURL, testDataDir string, liveM // parse back the fixture file into a series of HTTP headers followed by response body lines := strings.Split(stringFixture, "\n") for idx, line := range lines { - before, after, ok := strings.Cut(line, ": ") - if ok { + colonIndex := strings.Index(line, ": ") + if colonIndex != -1 { // Because we modified the body with ReplaceAll() above, we need to // remove Content-Length. w.Write() should add it back. - header := before + header := line[0:colonIndex] if !strings.EqualFold(header, "Content-Length") { - w.Header().Set(before, after) + w.Header().Set(line[0:colonIndex], line[colonIndex+2:]) } } else { // we reached the end of the headers (empty line), so what follows is the body diff --git a/models/unittest/reflection.go b/models/unittest/reflection.go index 939891283d..141fc66b99 100644 --- a/models/unittest/reflection.go +++ b/models/unittest/reflection.go @@ -9,7 +9,7 @@ import ( ) func fieldByName(v reflect.Value, field string) reflect.Value { - if v.Kind() == reflect.Pointer { + if v.Kind() == reflect.Ptr { v = v.Elem() } f := v.FieldByName(field) diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index c926a85df6..8e212bc0a3 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -47,29 +47,10 @@ func fatalTestError(fmtStr string, args ...any) { // InitSettings initializes config provider and load common settings for tests func InitSettings() { - InitCustomSettings("unittest.ini") -} - -func InitCustomSettings(confFileName string) { - root := base.SetupGiteaRoot() - if root == "" { - fatalTestError("Environment variable $GITEA_ROOT not set") - } - setting.AppPath = filepath.Join(root, "gitea") if setting.CustomConf == "" { - templateFile := confFileName + ".tmpl" - content, err := os.ReadFile(filepath.Join(root, "tests", templateFile)) - if err != nil { - log.Fatalf("couldn't read config template: %s", templateFile) - } - err = os.WriteFile(filepath.Join(root, "tests", confFileName), content, 0o644) - if err != nil { - log.Fatalf("couldn't write config: %s", confFileName) - } - setting.CustomConf = filepath.Join(root, "tests", confFileName) + setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini") + _ = os.Remove(setting.CustomConf) } - os.Setenv("GITEA_CONF", setting.CustomConf) - setting.InitCfgProvider(setting.CustomConf) setting.LoadCommonSettings() @@ -91,10 +72,9 @@ func InitCustomSettings(confFileName string) { // TestOptions represents test options type TestOptions struct { - FixtureFiles []string - SetUp func() error // SetUp will be executed before all tests in this package - TearDown func() error // TearDown will be executed after all tests in this package - IniFileOverride string + FixtureFiles []string + SetUp func() error // SetUp will be executed before all tests in this package + TearDown func() error // TearDown will be executed after all tests in this package } // MainTest a reusable TestMain(..) function for unit tests that need to use a @@ -117,11 +97,7 @@ func MainTest(m *testing.M, testOpts ...*TestOptions) { giteaRoot = searchDir setting.CustomPath = filepath.Join(giteaRoot, "custom") - if len(testOpts) == 0 || testOpts[0].IniFileOverride == "" { - InitSettings() - } else { - InitCustomSettings(testOpts[0].IniFileOverride) - } + InitSettings() fixturesDir = filepath.Join(giteaRoot, "models", "fixtures") var opts FixturesOptions diff --git a/models/unittest/unit_tests.go b/models/unittest/unit_tests.go index 411777cb17..2fa49e443a 100644 --- a/models/unittest/unit_tests.go +++ b/models/unittest/unit_tests.go @@ -67,7 +67,7 @@ func BeanExists(t testing.TB, bean any, conditions ...any) bool { } // AssertExistsAndLoadBean assert that a bean exists and load it from the test database -func AssertExistsAndLoadBean[T any](t require.TestingT, bean T, conditions ...any) T { +func AssertExistsAndLoadBean[T any](t testing.TB, bean T, conditions ...any) T { exists, err := LoadBeanIfExists(bean, conditions...) require.NoError(t, err) assert.True(t, exists, diff --git a/models/user/avatar.go b/models/user/avatar.go index 726d67f5e0..d534bd7bea 100644 --- a/models/user/avatar.go +++ b/models/user/avatar.go @@ -60,8 +60,7 @@ func GenerateRandomAvatar(ctx context.Context, u *User) error { return nil } -// AvatarLinkWithSize returns a link to the user's avatar. Size is only used for -// GenerateEmailAvatarFastLink, for external email-based avatar services +// AvatarLinkWithSize returns a link to the user's avatar with size. size <= 0 means default size func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string { if u.IsGhost() || u.ID <= 0 { return avatars.DefaultAvatarLink() @@ -89,7 +88,7 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string { if u.Avatar == "" { return avatars.DefaultAvatarLink() } - return avatars.GenerateUserAvatarImageLink(u.Avatar) + return avatars.GenerateUserAvatarImageLink(u.Avatar, size) } return avatars.GenerateEmailAvatarFastLink(ctx, u.AvatarEmail, size) } @@ -108,7 +107,7 @@ func (u *User) IsUploadAvatarChanged(data []byte) bool { if !u.UseCustomAvatar || len(u.Avatar) == 0 { return true } - avatarID := fmt.Sprintf("%x", md5.Sum(fmt.Appendf(nil, "%d-%x", u.ID, md5.Sum(data)))) + avatarID := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data))))) return u.Avatar != avatarID } diff --git a/models/user/email_address.go b/models/user/email_address.go index 54e3c1dc19..a3c33ffc00 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -323,12 +323,12 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail )) } - if has, value := opts.IsPrimary.Get(); has { - cond = cond.And(builder.Eq{"email_address.is_primary": value}) + if opts.IsPrimary.Has() { + cond = cond.And(builder.Eq{"email_address.is_primary": opts.IsPrimary.Value()}) } - if has, value := opts.IsActivated.Get(); has { - cond = cond.And(builder.Eq{"email_address.is_activated": value}) + if opts.IsActivated.Has() { + cond = cond.And(builder.Eq{"email_address.is_activated": opts.IsActivated.Value()}) } count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.id = email_address.uid"). diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go index 35b33933c2..85f5b16c65 100644 --- a/models/user/email_address_test.go +++ b/models/user/email_address_test.go @@ -5,7 +5,6 @@ package user_test import ( "fmt" - "slices" "testing" "forgejo.org/models/db" @@ -78,7 +77,12 @@ func TestListEmails(t *testing.T) { assert.Greater(t, count, int64(5)) contains := func(match func(s *user_model.SearchEmailResult) bool) bool { - return slices.ContainsFunc(emails, match) + for _, v := range emails { + if match(v) { + return true + } + } + return false } assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 18 })) diff --git a/models/user/error.go b/models/user/error.go index 7f23758768..264b3fdcd2 100644 --- a/models/user/error.go +++ b/models/user/error.go @@ -105,16 +105,3 @@ func IsErrUserIsNotLocal(err error) bool { _, ok := err.(ErrUserIsNotLocal) return ok } - -type ErrFederatedUserNotExists struct { - Identifier string -} - -func (err ErrFederatedUserNotExists) Error() string { - return fmt.Sprintf("No cached federated user found for identifier %s", err.Identifier) -} - -func IsErrFederatedUserNotExists(err error) bool { - _, ok := err.(ErrFederatedUserNotExists) - return ok -} diff --git a/models/user/federated_user.go b/models/user/federated_user.go index 8199a9cd22..d2a9c34c9e 100644 --- a/models/user/federated_user.go +++ b/models/user/federated_user.go @@ -5,7 +5,6 @@ package user import ( "database/sql" - "fmt" "forgejo.org/modules/validation" ) @@ -43,18 +42,3 @@ func (federatedUser FederatedUser) Validate() []string { result = append(result, validation.ValidateNotEmpty(federatedUser.InboxPath, "InboxPath")...) return result } - -func (federatedUser *FederatedUser) LogString() string { - if federatedUser == nil { - return "" - } - - return fmt.Sprintf( - "", - federatedUser.ID, - federatedUser.UserID, - federatedUser.ExternalID, - federatedUser.NormalizedOriginalURL, - federatedUser.InboxPath, - ) -} diff --git a/models/user/moderation.go b/models/user/moderation.go index fe8eef1806..7bc857489a 100644 --- a/models/user/moderation.go +++ b/models/user/moderation.go @@ -87,10 +87,10 @@ func newUserData(user *User) UserData { // (e.g. FieldName -> field_name) corresponding to UserData struct fields. var userDataColumnNames = sync.OnceValue(func() []string { mapper := new(names.GonicMapper) - udType := reflect.TypeFor[UserData]() + udType := reflect.TypeOf(UserData{}) columnNames := make([]string, 0, udType.NumField()) - for field := range udType.Fields() { - columnNames = append(columnNames, mapper.Obj2Table(field.Name)) + for i := 0; i < udType.NumField(); i++ { + columnNames = append(columnNames, mapper.Obj2Table(udType.Field(i).Name)) } return columnNames }) diff --git a/models/user/search.go b/models/user/search.go index 25c23a3740..08cf6a14a3 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -101,41 +101,40 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess cond = cond.And(builder.Eq{"id": opts.UID}) } - if has, value := opts.SourceID.Get(); has { - cond = cond.And(builder.Eq{"login_source": value}) + if opts.SourceID.Has() { + cond = cond.And(builder.Eq{"login_source": opts.SourceID.Value()}) } if opts.LoginName != "" { cond = cond.And(builder.Eq{"login_name": opts.LoginName}) } - if has, value := opts.IsActive.Get(); has { - cond = cond.And(builder.Eq{"is_active": value}) + if opts.IsActive.Has() { + cond = cond.And(builder.Eq{"is_active": opts.IsActive.Value()}) } - if has, value := opts.IsAdmin.Get(); has { - cond = cond.And(builder.Eq{"is_admin": value}) + if opts.IsAdmin.Has() { + cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()}) } - if has, value := opts.IsRestricted.Get(); has { - cond = cond.And(builder.Eq{"is_restricted": value}) + if opts.IsRestricted.Has() { + cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.Value()}) } - if has, value := opts.IsProhibitLogin.Get(); has { - cond = cond.And(builder.Eq{"prohibit_login": value}) + if opts.IsProhibitLogin.Has() { + cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.Value()}) } - if has, value := opts.AccountType.Get(); has { - cond = cond.And(builder.Eq{"type": value}) + if opts.AccountType.Has() { + cond = cond.And(builder.Eq{"type": opts.AccountType.Value()}) } e := db.GetEngine(ctx) - hasTwoFactor, isTwoFactorEnabled := opts.IsTwoFactorEnabled.Get() - if !hasTwoFactor { + if !opts.IsTwoFactorEnabled.Has() { return e.Where(cond) } // Check if the user has two factor enabled, which is TOTP or Webauthn. - if isTwoFactorEnabled { + if opts.IsTwoFactorEnabled.Value() { cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL OR webauthn_credential.user_id IS NOT NULL")) } else { cond = cond.And(builder.Expr("two_factor.uid IS NULL AND webauthn_credential.user_id IS NULL")) diff --git a/models/user/user.go b/models/user/user.go index 80843cfed9..f871284890 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -362,7 +362,7 @@ func (u *User) OrganisationLink() string { // GenerateEmailAuthorizationCode generates an activation code based for the user for the specified purpose. // The standard expiry is ActiveCodeLives minutes. func (u *User) GenerateEmailAuthorizationCode(ctx context.Context, purpose auth.AuthorizationPurpose) (string, error) { - lookup, validator, err := auth.GenerateAuthToken(ctx, u.ID, optional.None[int64](), timeutil.TimeStampNow().Add(int64(setting.Service.ActiveCodeLives)*60), purpose) + lookup, validator, err := auth.GenerateAuthToken(ctx, u.ID, timeutil.TimeStampNow().Add(int64(setting.Service.ActiveCodeLives)*60), purpose) if err != nil { return "", err } @@ -464,6 +464,15 @@ func (u *User) IsUser() bool { return u.Type == UserTypeIndividual || u.Type == UserTypeBot } +// Returns true if the given user ID belongs to an actual user, not an organization +func IsUserByID(ctx context.Context, uid int64) (bool, error) { + return db.GetEngine(ctx). + Where("id=?", uid). + In("type", UserTypeIndividual, UserTypeBot). + Table("user"). + Exist() +} + // IsBot returns whether or not the user is of type bot func (u *User) IsBot() bool { return u.Type == UserTypeBot @@ -655,6 +664,7 @@ var ( "user", // user login/activate/settings, etc "admin", + "devtest", "explore", "issues", "pulls", @@ -689,14 +699,6 @@ func IsUsableUsername(name string) error { return db.IsUsableName(reservedUsernames, reservedUserPatterns, name) } -// IsActivityPubUsername returns an error if a fediverse handle (referred to as a username) cannot exist -func IsActivityPubUsername(name string) error { - if !validation.IsValidActivityPubUsername(name) { - return db.ErrNameActivityPubInvalid{Name: name} - } - return db.IsUsableName(reservedUsernames, reservedUserPatterns, name) -} - // CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation type CreateUserOverwriteOptions struct { KeepEmailPrivate optional.Option[bool] @@ -707,7 +709,6 @@ type CreateUserOverwriteOptions struct { Theme *string IsRestricted optional.Option[bool] IsActive optional.Option[bool] - IsActivityPub optional.Option[bool] } // CreateUser creates record of a new user. @@ -722,26 +723,12 @@ func AdminCreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUs // createUser creates record of a new user. func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefault ...*CreateUserOverwriteOptions) (err error) { - overwriteDefaultPresent := len(overwriteDefault) != 0 && overwriteDefault[0] != nil - - // If a username is invalid as-is, check whether the username is meant - // for an ActivityPub account. Username constraints that belong to "foreign" - // ActivityPub servers, whose implementations we cannot control, are expected - // to be much less restrictive than those of Forgejo itself. - if overwriteDefaultPresent && overwriteDefault[0].IsActivityPub.Has() { - if err = IsActivityPubUsername(u.Name); err != nil { - return err - } - } else if err := IsUsableUsername(u.Name); err != nil { + if err = IsUsableUsername(u.Name); err != nil { return err } // Check if the new username can be claimed. // Skip this check if done by an admin. - // - // Note: This skip should not currently cover usernames that could belong to - // fediverse accounts. This "defensive programming" is in place to prevent future - // breakage until the ActivityPub component matures more. if !createdByAdmin { if ok, expireTime, err := CanClaimUsername(ctx, u.Name, -1); err != nil { return err @@ -768,16 +755,16 @@ func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefa } // overwrite defaults if set - if overwriteDefaultPresent { + if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { overwrite := overwriteDefault[0] - if has, value := overwrite.KeepEmailPrivate.Get(); has { - u.KeepEmailPrivate = value + if overwrite.KeepEmailPrivate.Has() { + u.KeepEmailPrivate = overwrite.KeepEmailPrivate.Value() } if overwrite.Visibility != nil { u.Visibility = *overwrite.Visibility } - if has, value := overwrite.AllowCreateOrganization.Get(); has { - u.AllowCreateOrganization = value + if overwrite.AllowCreateOrganization.Has() { + u.AllowCreateOrganization = overwrite.AllowCreateOrganization.Value() } if overwrite.EmailNotificationsPreference != nil { u.EmailNotificationsPreference = *overwrite.EmailNotificationsPreference @@ -788,11 +775,11 @@ func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefa if overwrite.Theme != nil { u.Theme = *overwrite.Theme } - if has, value := overwrite.IsRestricted.Get(); has { - u.IsRestricted = value + if overwrite.IsRestricted.Has() { + u.IsRestricted = overwrite.IsRestricted.Value() } - if has, value := overwrite.IsActive.Get(); has { - u.IsActive = value + if overwrite.IsActive.Has() { + u.IsActive = overwrite.IsActive.Value() } } @@ -901,15 +888,15 @@ func CountUsers(ctx context.Context, opts *CountUserFilter) int64 { func countUsers(ctx context.Context, opts *CountUserFilter) int64 { sess := db.GetEngine(ctx) cond := builder.NewCond() - cond = cond.And(builder.In("type", UserTypeIndividual, UserTypeRemoteUser)) + cond = cond.And(builder.Eq{"type": UserTypeIndividual}) if opts != nil { if opts.LastLoginSince != nil { cond = cond.And(builder.Gte{"last_login_unix": *opts.LastLoginSince}) } - if has, value := opts.IsAdmin.Get(); has { - cond = cond.And(builder.Eq{"is_admin": value}) + if opts.IsAdmin.Has() { + cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()}) } } @@ -923,46 +910,46 @@ func countUsers(ctx context.Context, opts *CountUserFilter) int64 { // VerifyUserActiveCode verifies that the code is valid for the given purpose for this user. // If delete is specified, the token will be deleted. -func VerifyUserAuthorizationToken(ctx context.Context, code string, purpose auth.AuthorizationPurpose) (user *User, authToken *auth.AuthorizationToken, deleteToken func() error, err error) { +func VerifyUserAuthorizationToken(ctx context.Context, code string, purpose auth.AuthorizationPurpose) (user *User, deleteToken func() error, err error) { lookupKey, validator, found := strings.Cut(code, ":") if !found { - return nil, nil, nil, nil + return nil, nil, nil } - authToken, err = auth.FindAuthToken(ctx, lookupKey, purpose) + authToken, err := auth.FindAuthToken(ctx, lookupKey, purpose) if err != nil { if errors.Is(err, util.ErrNotExist) { - return nil, nil, nil, nil + return nil, nil, nil } - return nil, nil, nil, err + return nil, nil, err } if authToken.IsExpired() { - return nil, nil, nil, auth.DeleteAuthToken(ctx, authToken) + return nil, nil, auth.DeleteAuthToken(ctx, authToken) } rawValidator, err := hex.DecodeString(validator) if err != nil { - return nil, nil, nil, err + return nil, nil, err } if subtle.ConstantTimeCompare([]byte(authToken.HashedValidator), []byte(auth.HashValidator(rawValidator))) == 0 { - return nil, nil, nil, errors.New("validator doesn't match") + return nil, nil, errors.New("validator doesn't match") } u, err := GetUserByID(ctx, authToken.UID) if err != nil { if IsErrUserNotExist(err) { - return nil, nil, nil, nil + return nil, nil, nil } - return nil, nil, nil, err + return nil, nil, err } deleteToken = func() error { return auth.DeleteAuthToken(ctx, authToken) } - return u, authToken, deleteToken, nil + return u, deleteToken, nil } // ValidateUser check if user is valid to insert / update into database @@ -1243,8 +1230,8 @@ func GetUserByEmail(ctx context.Context, email string) (*User, error) { } // Finally, if email address is the protected email address: - if before, ok := strings.CutSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)); ok { - username := before + if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) { + username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) user := &User{} has, err := db.GetEngine(ctx).Where("lower_name=?", username).Get(user) if err != nil { diff --git a/models/user/user_repository.go b/models/user/user_repository.go index 506d1cfb15..f1d06abe17 100644 --- a/models/user/user_repository.go +++ b/models/user/user_repository.go @@ -23,9 +23,8 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat return err } overwrite := CreateUserOverwriteOptions{ - IsActive: optional.Some(false), - IsRestricted: optional.Some(false), - IsActivityPub: optional.Some(true), + IsActive: optional.Some(false), + IsRestricted: optional.Some(false), } // Begin transaction @@ -65,7 +64,7 @@ func FindFederatedUser(ctx context.Context, externalID string, federationHostID if err != nil { return nil, nil, err } else if !has { - return nil, nil, ErrFederatedUserNotExists{Identifier: externalID} + return nil, nil, nil } has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user) if err != nil { @@ -83,55 +82,14 @@ func FindFederatedUser(ctx context.Context, externalID string, federationHostID return user, federatedUser, nil } -func CountFederatedUsers(ctx context.Context) (int64, error) { - return db.GetEngine(ctx).Count(FederatedUser{}) -} - -func FindFederatedUsers(ctx context.Context, opts db.ListOptions) (users []*FederatedUser, err error) { - sess := db.GetEngine(ctx) - - if opts.PageSize > 0 { - sess = db.SetSessionPagination(sess, &opts) - } - - err = sess.Find(&users) +func GetFederatedUser(ctx context.Context, externalID string, federationHostID int64) (*User, *FederatedUser, error) { + user, federatedUser, err := FindFederatedUser(ctx, externalID, federationHostID) if err != nil { - return nil, err + return nil, nil, err + } else if federatedUser == nil { + return nil, nil, fmt.Errorf("FederatedUser not found (given externalId: %v, federationHostId: %v)", externalID, federationHostID) } - - for _, user := range users { - if res, err := validation.IsValid(user); !res { - return nil, err - } - } - - return users, err -} - -func CountFederatedUsersByHostID(ctx context.Context, federationHostID int64) (int64, error) { - return db.GetEngine(ctx).Where("federation_host_id = ?", federationHostID).Count(FederatedUser{}) -} - -func FindFederatedUsersByHostID(ctx context.Context, federationHostID int64, opts db.ListOptions) ([]*FederatedUser, error) { - var users []*FederatedUser - sess := db.GetEngine(ctx).Where("federation_host_id = ?", federationHostID) - - if opts.PageSize > 0 { - sess = db.SetSessionPagination(sess, &opts) - } - - err := sess.Find(&users) - if err != nil { - return nil, err - } - - for _, user := range users { - if res, err := validation.IsValid(user); !res { - return nil, err - } - } - - return users, nil + return user, federatedUser, nil } func GetFederatedUserByUserID(ctx context.Context, userID int64) (*User, *FederatedUser, error) { @@ -167,7 +125,7 @@ func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *Federa if err != nil { return nil, nil, err } else if !has { - return nil, nil, ErrFederatedUserNotExists{Identifier: keyID} + return nil, nil, nil } has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user) if err != nil { diff --git a/models/user/user_test.go b/models/user/user_test.go index fd5285d69c..330a3fd563 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -273,9 +273,9 @@ func TestHashPasswordDeterministic(t *testing.T) { b := make([]byte, 16) u := &user_model.User{} algos := hash.RecommendedHashAlgorithms - for j := range algos { + for j := 0; j < len(algos); j++ { u.PasswdHashAlgo = algos[j] - for range 50 { + for i := 0; i < 50; i++ { // generate a random password rand.Read(b) pass := string(b) @@ -440,63 +440,6 @@ func TestCreateUserClaimingUsername(t *testing.T) { }) } -// Attempts to create a username with a fediverse-format handle, which should -// fail (without the override IsActivityPub, which is set by CreateFederatedUser) -func TestCreateUserPlainWithFediverseHandle(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(&user_model.Redirect{RedirectUserID: 1, LowerName: "redirecting", CreatedUnix: timeutil.TimeStampNow()}) - require.NoError(t, err) - - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - - user.Name = "@example@example.tld" - user.LowerName = strings.ToLower(user.Name) - user.ID = 0 - user.Email = "unique@example.com" - - t.Run("Normal creation (without ActivityPub override)", func(t *testing.T) { - err = user_model.CreateUser(db.DefaultContext, user) - require.Error(t, err) - assert.True(t, db.IsErrNameCharsNotAllowed(err)) - }) - - t.Run("Creation as admin (without ActivityPub override)", func(t *testing.T) { - err = user_model.AdminCreateUser(db.DefaultContext, user) - require.Error(t, err) - assert.True(t, db.IsErrNameCharsNotAllowed(err)) - }) - - // Logic borrowed from CreateFederatedUser (which invokes CreateUser), but - // we "lend" this here to verify CreateUser's paths. - overwrite := user_model.CreateUserOverwriteOptions{ - IsActive: optional.Some(false), - IsRestricted: optional.Some(false), - IsActivityPub: optional.Some(true), - } - - t.Run("Normal creation (with ActivityPub override, invalid format)", func(t *testing.T) { - user.Name = "invalid-format-for-an-activitypub-account" - user.LowerName = strings.ToLower(user.Name) - - err = user_model.CreateUser(db.DefaultContext, user, &overwrite) - require.Error(t, err) - assert.True(t, db.IsErrNameActivityPubInvalid(err)) - }) - - t.Run("Normal creation (with ActivityPub override)", func(t *testing.T) { - user.Name = "@valid@example.tld" - user.LowerName = strings.ToLower(user.Name) - - err = user_model.CreateUser(db.DefaultContext, user, &overwrite) - require.NoError(t, err) - }) - - // Note: We don't expect that admins are able to access any front-facing - // function that sets the overwrite (i.e. CreateFederatedUser), hence it - // has been omitted for now. -} - func TestGetUserIDsByNames(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) @@ -982,25 +925,23 @@ func TestVerifyUserAuthorizationToken(t *testing.T) { assert.True(t, ok) t.Run("Wrong purpose", func(t *testing.T) { - u, _, _, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.PasswordReset) + u, _, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.PasswordReset) require.NoError(t, err) assert.Nil(t, u) }) t.Run("No delete", func(t *testing.T) { - u, authToken, _, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation) + u, _, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation) require.NoError(t, err) assert.Equal(t, user.ID, u.ID) - require.NotNil(t, authToken) - assert.False(t, authToken.LoginSourceID.Has()) - stored, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation) + authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation) require.NoError(t, err) - assert.NotNil(t, stored) + assert.NotNil(t, authToken) }) t.Run("Delete", func(t *testing.T) { - u, _, deleteToken, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation) + u, deleteToken, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation) require.NoError(t, err) assert.Equal(t, user.ID, u.ID) require.NoError(t, deleteToken()) @@ -1115,3 +1056,20 @@ func TestGetUserByEmailSimple(t *testing.T) { assert.Nil(t, u) }) } + +func TestIsUserConsistency(t *testing.T) { + defer unittest.OverrideFixtures("models/user/fixtures/")() + require.NoError(t, unittest.PrepareTestDatabase()) + + test := func(userID int64) { + user, err := user_model.GetUserByID(t.Context(), userID) + require.NoError(t, err) + isUser, err := user_model.IsUserByID(t.Context(), userID) + require.NoError(t, err) + assert.Equal(t, user.IsUser(), isUser) + } + + test(1) + test(1041) + test(1042) +} diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index 196a5313bc..1f81caf424 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -11,9 +11,10 @@ import ( "forgejo.org/models/db" "forgejo.org/modules/json" - "forgejo.org/modules/keying" "forgejo.org/modules/log" "forgejo.org/modules/optional" + "forgejo.org/modules/secret" + "forgejo.org/modules/setting" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" webhook_module "forgejo.org/modules/webhook" @@ -136,7 +137,7 @@ type Webhook struct { LastStatus webhook_module.HookStatus // Last delivery status // HeaderAuthorizationEncrypted should be accessed using HeaderAuthorization() and SetHeaderAuthorization() - HeaderAuthorizationEncrypted []byte `xorm:"BLOB"` + HeaderAuthorizationEncrypted string `xorm:"TEXT"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` @@ -375,15 +376,10 @@ func (w *Webhook) EventsArray() []string { // HeaderAuthorization returns the decrypted Authorization header. // Not on the reference (*w), to be accessible on WebhooksNew. func (w Webhook) HeaderAuthorization() (string, error) { - if len(w.HeaderAuthorizationEncrypted) == 0 { + if w.HeaderAuthorizationEncrypted == "" { return "", nil } - - headerAuthorization, err := keying.Webhook.Decrypt(w.HeaderAuthorizationEncrypted, keying.ColumnAndID("header_authorization_encrypted", w.ID)) - if err != nil { - return "", err - } - return string(headerAuthorization), nil + return secret.DecryptSecret(setting.SecretKey, w.HeaderAuthorizationEncrypted) } // HeaderAuthorizationTrimPrefix returns the decrypted Authorization with a specified prefix trimmed. @@ -396,31 +392,23 @@ func (w Webhook) HeaderAuthorizationTrimPrefix(prefix string) (string, error) { } // SetHeaderAuthorization encrypts and sets the Authorization header. -func (w *Webhook) SetHeaderAuthorization(cleartext string) { +func (w *Webhook) SetHeaderAuthorization(cleartext string) error { if cleartext == "" { - w.HeaderAuthorizationEncrypted = nil - return + w.HeaderAuthorizationEncrypted = "" + return nil } - - w.HeaderAuthorizationEncrypted = keying.Webhook.Encrypt([]byte(cleartext), keying.ColumnAndID("header_authorization_encrypted", w.ID)) + ciphertext, err := secret.EncryptSecret(setting.SecretKey, cleartext) + if err != nil { + return err + } + w.HeaderAuthorizationEncrypted = ciphertext + return nil } // CreateWebhook creates a new web hook. -func CreateWebhook(ctx context.Context, w *Webhook, authorizationHeader string) error { +func CreateWebhook(ctx context.Context, w *Webhook) error { w.Type = strings.TrimSpace(w.Type) - - if len(authorizationHeader) == 0 { - return db.Insert(ctx, w) - } - return db.WithTx(ctx, func(ctx context.Context) error { - if err := db.Insert(ctx, w); err != nil { - return err - } - - w.SetHeaderAuthorization(authorizationHeader) - _, err := db.GetEngine(ctx).Cols("header_authorization_encrypted").ID(w.ID).Update(w) - return err - }) + return db.Insert(ctx, w) } // CreateWebhooks creates multiple web hooks @@ -429,7 +417,7 @@ func CreateWebhooks(ctx context.Context, ws []*Webhook) error { if len(ws) == 0 { return nil } - for i := range ws { + for i := 0; i < len(ws); i++ { ws[i].Type = strings.TrimSpace(ws[i].Type) } return db.Insert(ctx, ws) @@ -487,8 +475,8 @@ func (opts ListWebhookOptions) ToConds() builder.Cond { if opts.OwnerID != 0 { cond = cond.And(builder.Eq{"webhook.owner_id": opts.OwnerID}) } - if has, value := opts.IsActive.Get(); has { - cond = cond.And(builder.Eq{"webhook.is_active": value}) + if opts.IsActive.Has() { + cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.Value()}) } return cond } diff --git a/models/webhook/webhook_system.go b/models/webhook/webhook_system.go index 9cdd323235..2e53f639dc 100644 --- a/models/webhook/webhook_system.go +++ b/models/webhook/webhook_system.go @@ -77,7 +77,7 @@ func CopyDefaultWebhooksToRepo(ctx context.Context, repoID int64) error { for _, w := range ws { w.ID = 0 w.RepoID = repoID - if err := CreateWebhook(ctx, w, ""); err != nil { + if err := CreateWebhook(ctx, w); err != nil { return fmt.Errorf("CreateWebhook: %v", err) } } diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index 1dabc0ff38..60cd2b333b 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -98,7 +98,7 @@ func TestCreateWebhook(t *testing.T) { Events: `{"push_only":false,"send_everything":false,"choose_events":true,"events":{"create":false,"push":true,"pull_request":true}}`, } unittest.AssertNotExistsBean(t, hook) - require.NoError(t, CreateWebhook(db.DefaultContext, hook, "")) + require.NoError(t, CreateWebhook(db.DefaultContext, hook)) hookFromDb := unittest.AssertExistsAndLoadBean(t, hook) assert.Equal(t, []string{ string(webhook_module.HookEventPush), @@ -114,7 +114,7 @@ func TestCreateWebhook(t *testing.T) { Events: `{"push_only":false,"send_everything":false,"choose_events":true,"events":{"action_run_recover":false,"action_run_success":true}}`, } unittest.AssertNotExistsBean(t, hook) - require.NoError(t, CreateWebhook(db.DefaultContext, hook, "")) + require.NoError(t, CreateWebhook(db.DefaultContext, hook)) hookFromDb := unittest.AssertExistsAndLoadBean(t, hook) assert.Equal(t, []string{string(webhook_module.HookEventActionRunSuccess)}, hookFromDb.EventsArray()) }) @@ -127,7 +127,7 @@ func TestCreateWebhook(t *testing.T) { Events: `{"push_only":false,"send_everything":false,"choose_events":true,"events":{"create":true,"delete":true,"fork":true,"issues":true,"issue_assign":true,"issue_label":true,"issue_milestone":true,"issue_comment":true,"push":true,"pull_request":true,"pull_request_assign":true,"pull_request_label":true,"pull_request_milestone":true,"pull_request_comment":true,"pull_request_review":true,"pull_request_sync":true,"pull_request_review_request":true,"wiki":true,"repository":true,"release":true,"package":true,"action_run_failure":true,"action_run_recover":true,"action_run_success":true}}`, } unittest.AssertNotExistsBean(t, hook) - require.NoError(t, CreateWebhook(db.DefaultContext, hook, "")) + require.NoError(t, CreateWebhook(db.DefaultContext, hook)) hookFromDb := unittest.AssertExistsAndLoadBean(t, hook) assert.Equal(t, []string{ string(webhook_module.HookEventCreate), diff --git a/modules/actions/task_state.go b/modules/actions/task_state.go index 889a0b0364..77bfc747ee 100644 --- a/modules/actions/task_state.go +++ b/modules/actions/task_state.go @@ -111,8 +111,6 @@ func fullStepsOfEmptySteps(task *actions_model.ActionTask) []*actions_model.Acti preStep.Status = task.Status if preStep.Status.IsSuccess() { postStep.Status = actions_model.StatusSuccess - } else if preStep.Status.IsSkipped() { - postStep.Status = actions_model.StatusSkipped } else { postStep.Status = actions_model.StatusCancelled } diff --git a/modules/actions/task_state_test.go b/modules/actions/task_state_test.go index 084de1c709..e18de4573f 100644 --- a/modules/actions/task_state_test.go +++ b/modules/actions/task_state_test.go @@ -156,21 +156,6 @@ func TestFullSteps(t *testing.T) { {Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 10100}, }, }, - { - // situation occurs with a reusable workflow's outer job which has no steps - name: "skipped task w/ zero steps", - task: &actions_model.ActionTask{ - Steps: []*actions_model.ActionTaskStep{}, - Status: actions_model.StatusSkipped, - Started: 0, - Stopped: 0, - LogLength: 0, - }, - want: []*actions_model.ActionTaskStep{ - {Name: preStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, - {Name: postStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0}, - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index ed54ccc98b..6a7630548a 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -5,9 +5,7 @@ package actions import ( "bytes" - "fmt" "io" - "slices" "strings" actions_model "forgejo.org/models/actions" @@ -32,12 +30,6 @@ type DetectedWorkflow struct { NeedApproval actions_model.ApprovalType } -// GetWorkflowPath returns the full path to the workflow from the repository root, for example, -// .forgejo/workflows/test.yaml. -func (wf *DetectedWorkflow) GetWorkflowPath() string { - return fmt.Sprintf("%s/%s", wf.EntryDirectory, wf.EntryName) -} - func init() { model.OnDecodeNodeError = func(node yaml.Node, out any, err error) { // Log the error instead of panic or fatal. @@ -610,8 +602,11 @@ func matchPullRequestReviewEvent(prPayload *api.PullRequestPayload, evt *jobpars matched := false for _, val := range vals { - if slices.ContainsFunc(actions, glob.MustCompile(val, '/').Match) { - matched = true + for _, action := range actions { + if glob.MustCompile(val, '/').Match(action) { + matched = true + break + } } if matched { break @@ -656,8 +651,11 @@ func matchPullRequestReviewCommentEvent(prPayload *api.PullRequestPayload, evt * matched := false for _, val := range vals { - if slices.ContainsFunc(actions, glob.MustCompile(val, '/').Match) { - matched = true + for _, action := range actions { + if glob.MustCompile(val, '/').Match(action) { + matched = true + break + } } if matched { break diff --git a/modules/actions/workflows_test.go b/modules/actions/workflows_test.go index 911d72a09a..b431989def 100644 --- a/modules/actions/workflows_test.go +++ b/modules/actions/workflows_test.go @@ -19,14 +19,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestDetectedWorkflowGetWorkflowPath(t *testing.T) { - buildWorkflow := DetectedWorkflow{EntryDirectory: ".github/workflows", EntryName: "build.yaml"} - testWorkflow := DetectedWorkflow{EntryDirectory: ".forgejo/workflows", EntryName: "test.yaml"} - - assert.Equal(t, ".github/workflows/build.yaml", buildWorkflow.GetWorkflowPath()) - assert.Equal(t, ".forgejo/workflows/test.yaml", testWorkflow.GetWorkflowPath()) -} - func TestActionsWorkflowsDetectMatched(t *testing.T) { testCases := []struct { desc string diff --git a/modules/activitypub/main_test.go b/modules/activitypub/main_test.go index 3f1a1b3562..a3f173f408 100644 --- a/modules/activitypub/main_test.go +++ b/modules/activitypub/main_test.go @@ -7,8 +7,6 @@ import ( "testing" "forgejo.org/models/unittest" - - _ "forgejo.org/modules/testimport" ) func TestMain(m *testing.M) { diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index a9b99556e2..2041f28bb1 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "io" "io/fs" "os" "path/filepath" @@ -96,12 +97,14 @@ func (l *LayeredFS) ReadFile(elems ...string) ([]byte, error) { func (l *LayeredFS) ReadLayeredFile(elems ...string) ([]byte, string, error) { name := util.PathJoinRel(elems...) for _, layer := range l.layers { - bs, err := fs.ReadFile(layer, name) - if errors.Is(err, fs.ErrNotExist) { + f, err := layer.Open(name) + if os.IsNotExist(err) { continue } else if err != nil { return nil, layer.name, err } + bs, err := io.ReadAll(f) + _ = f.Close() return bs, layer.name, err } return nil, "", fs.ErrNotExist diff --git a/modules/auth/password/hash/setting.go b/modules/auth/password/hash/setting.go index 24d0f726c7..05cd36fe3c 100644 --- a/modules/auth/password/hash/setting.go +++ b/modules/auth/password/hash/setting.go @@ -14,7 +14,7 @@ const DefaultHashAlgorithmName = "pbkdf2_hi" var DefaultHashAlgorithm *PasswordHashAlgorithm -// aliasAlgorithmNames provides a mapping between the value of PASSWORD_HASH_ALGO +// aliasAlgorithNames provides a mapping between the value of PASSWORD_HASH_ALGO // configured in the app.ini and the parameters used within the hashers internally. // // If it is necessary to change the default parameters for any hasher in future you diff --git a/modules/auth/password/password.go b/modules/auth/password/password.go index 744a431ea8..fdbc4ff291 100644 --- a/modules/auth/password/password.go +++ b/modules/auth/password/password.go @@ -101,7 +101,7 @@ func Generate(n int) (string, error) { buffer := make([]byte, n) max := big.NewInt(int64(len(validChars))) for { - for j := range n { + for j := 0; j < n; j++ { rnd, err := rand.Int(rand.Reader, max) if err != nil { return "", err diff --git a/modules/auth/password/password_test.go b/modules/auth/password/password_test.go index 8f5d64514c..1fe3fb5ce1 100644 --- a/modules/auth/password/password_test.go +++ b/modules/auth/password/password_test.go @@ -51,7 +51,7 @@ func TestComplexity_Generate(t *testing.T) { test := func(t *testing.T, modes []string) { testComplextity(modes) - for range maxCount { + for i := 0; i < maxCount; i++ { pwd, err := Generate(pwdLen) require.NoError(t, err) assert.Len(t, pwd, pwdLen) diff --git a/modules/auth/password/pwn/pwn.go b/modules/auth/password/pwn/pwn.go index f3277ff616..10693ec663 100644 --- a/modules/auth/password/pwn/pwn.go +++ b/modules/auth/password/pwn/pwn.go @@ -101,7 +101,7 @@ func (c *Client) CheckPassword(pw string, padding bool) (int, error) { } defer resp.Body.Close() - for pair := range strings.SplitSeq(string(body), "\n") { + for _, pair := range strings.Split(string(body), "\n") { parts := strings.Split(pair, ":") if len(parts) != 2 { continue diff --git a/modules/avatar/identicon/block.go b/modules/avatar/identicon/block.go index fc8ce90212..cb1803a231 100644 --- a/modules/avatar/identicon/block.go +++ b/modules/avatar/identicon/block.go @@ -24,8 +24,8 @@ func drawBlock(img *image.Paletted, x, y, size, angle int, points []int) { rotate(points, m, m, angle) } - for i := range size { - for j := range size { + for i := 0; i < size; i++ { + for j := 0; j < size; j++ { if pointInPolygon(i, j, points) { img.SetColorIndex(x+i, y+j, 1) } diff --git a/modules/avatar/identicon/identicon.go b/modules/avatar/identicon/identicon.go index 19f87da85a..13e8ec88e6 100644 --- a/modules/avatar/identicon/identicon.go +++ b/modules/avatar/identicon/identicon.go @@ -134,7 +134,7 @@ func drawBlocks(p *image.Paletted, size int, c, b1, b2 blockFunc, b1Angle, b2Ang // then we make it left-right mirror, so we didn't draw 3/6/9 before for x := 0; x < size/2; x++ { - for y := range size { + for y := 0; y < size; y++ { p.SetColorIndex(size-x, y, p.ColorIndexAt(x, y)) } } diff --git a/modules/base/tool.go b/modules/base/tool.go index 1cc053ba58..e3a3ff4a23 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -103,7 +103,7 @@ func Int64sToStrings(ints []int64) []string { func EntryIcon(entry *git.TreeEntry) string { switch { case entry.IsLink(): - te, err := entry.FollowLink() + te, _, err := entry.FollowLink() if err != nil { log.Debug(err.Error()) return "file-symlink-file" @@ -118,17 +118,9 @@ func EntryIcon(entry *git.TreeEntry) string { return "file-submodule" } - if IsCitationFile(entry) { - return "cross-reference" - } - return "file" } -func IsCitationFile(entry *git.TreeEntry) bool { - return entry.Name() == "CITATION.cff" || entry.Name() == "CITATION.bib" -} - // SetupGiteaRoot Sets GITEA_ROOT if it is not already set and returns the value func SetupGiteaRoot() string { giteaRoot := os.Getenv("GITEA_ROOT") diff --git a/modules/cache/mocks.go b/modules/cache/mocks.go deleted file mode 100644 index 8bfec19d6b..0000000000 --- a/modules/cache/mocks.go +++ /dev/null @@ -1,497 +0,0 @@ -// Code generated by mockery; DO NOT EDIT. -// github.com/vektra/mockery -// template: testify - -package cache - -import ( - "code.forgejo.org/go-chi/cache" - mock "github.com/stretchr/testify/mock" -) - -// NewMockCache creates a new instance of MockCache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockCache(t interface { - mock.TestingT - Cleanup(func()) -}, -) *MockCache { - mock := &MockCache{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// MockCache is an autogenerated mock type for the Cache type -type MockCache struct { - mock.Mock -} - -type MockCache_Expecter struct { - mock *mock.Mock -} - -func (_m *MockCache) EXPECT() *MockCache_Expecter { - return &MockCache_Expecter{mock: &_m.Mock} -} - -// Decr provides a mock function for the type MockCache -func (_mock *MockCache) Decr(key string) error { - ret := _mock.Called(key) - - if len(ret) == 0 { - panic("no return value specified for Decr") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(string) error); ok { - r0 = returnFunc(key) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockCache_Decr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Decr' -type MockCache_Decr_Call struct { - *mock.Call -} - -// Decr is a helper method to define mock.On call -// - key string -func (_e *MockCache_Expecter) Decr(key any) *MockCache_Decr_Call { - return &MockCache_Decr_Call{Call: _e.mock.On("Decr", key)} -} - -func (_c *MockCache_Decr_Call) Run(run func(key string)) *MockCache_Decr_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 string - if args[0] != nil { - arg0 = args[0].(string) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *MockCache_Decr_Call) Return(err error) *MockCache_Decr_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockCache_Decr_Call) RunAndReturn(run func(key string) error) *MockCache_Decr_Call { - _c.Call.Return(run) - return _c -} - -// Delete provides a mock function for the type MockCache -func (_mock *MockCache) Delete(key string) error { - ret := _mock.Called(key) - - if len(ret) == 0 { - panic("no return value specified for Delete") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(string) error); ok { - r0 = returnFunc(key) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockCache_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' -type MockCache_Delete_Call struct { - *mock.Call -} - -// Delete is a helper method to define mock.On call -// - key string -func (_e *MockCache_Expecter) Delete(key any) *MockCache_Delete_Call { - return &MockCache_Delete_Call{Call: _e.mock.On("Delete", key)} -} - -func (_c *MockCache_Delete_Call) Run(run func(key string)) *MockCache_Delete_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 string - if args[0] != nil { - arg0 = args[0].(string) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *MockCache_Delete_Call) Return(err error) *MockCache_Delete_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockCache_Delete_Call) RunAndReturn(run func(key string) error) *MockCache_Delete_Call { - _c.Call.Return(run) - return _c -} - -// Flush provides a mock function for the type MockCache -func (_mock *MockCache) Flush() error { - ret := _mock.Called() - - if len(ret) == 0 { - panic("no return value specified for Flush") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func() error); ok { - r0 = returnFunc() - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockCache_Flush_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Flush' -type MockCache_Flush_Call struct { - *mock.Call -} - -// Flush is a helper method to define mock.On call -func (_e *MockCache_Expecter) Flush() *MockCache_Flush_Call { - return &MockCache_Flush_Call{Call: _e.mock.On("Flush")} -} - -func (_c *MockCache_Flush_Call) Run(run func()) *MockCache_Flush_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockCache_Flush_Call) Return(err error) *MockCache_Flush_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockCache_Flush_Call) RunAndReturn(run func() error) *MockCache_Flush_Call { - _c.Call.Return(run) - return _c -} - -// Get provides a mock function for the type MockCache -func (_mock *MockCache) Get(key string) any { - ret := _mock.Called(key) - - if len(ret) == 0 { - panic("no return value specified for Get") - } - - var r0 any - if returnFunc, ok := ret.Get(0).(func(string) any); ok { - r0 = returnFunc(key) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(any) - } - } - return r0 -} - -// MockCache_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' -type MockCache_Get_Call struct { - *mock.Call -} - -// Get is a helper method to define mock.On call -// - key string -func (_e *MockCache_Expecter) Get(key any) *MockCache_Get_Call { - return &MockCache_Get_Call{Call: _e.mock.On("Get", key)} -} - -func (_c *MockCache_Get_Call) Run(run func(key string)) *MockCache_Get_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 string - if args[0] != nil { - arg0 = args[0].(string) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *MockCache_Get_Call) Return(v any) *MockCache_Get_Call { - _c.Call.Return(v) - return _c -} - -func (_c *MockCache_Get_Call) RunAndReturn(run func(key string) any) *MockCache_Get_Call { - _c.Call.Return(run) - return _c -} - -// Incr provides a mock function for the type MockCache -func (_mock *MockCache) Incr(key string) error { - ret := _mock.Called(key) - - if len(ret) == 0 { - panic("no return value specified for Incr") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(string) error); ok { - r0 = returnFunc(key) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockCache_Incr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Incr' -type MockCache_Incr_Call struct { - *mock.Call -} - -// Incr is a helper method to define mock.On call -// - key string -func (_e *MockCache_Expecter) Incr(key any) *MockCache_Incr_Call { - return &MockCache_Incr_Call{Call: _e.mock.On("Incr", key)} -} - -func (_c *MockCache_Incr_Call) Run(run func(key string)) *MockCache_Incr_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 string - if args[0] != nil { - arg0 = args[0].(string) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *MockCache_Incr_Call) Return(err error) *MockCache_Incr_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockCache_Incr_Call) RunAndReturn(run func(key string) error) *MockCache_Incr_Call { - _c.Call.Return(run) - return _c -} - -// IsExist provides a mock function for the type MockCache -func (_mock *MockCache) IsExist(key string) bool { - ret := _mock.Called(key) - - if len(ret) == 0 { - panic("no return value specified for IsExist") - } - - var r0 bool - if returnFunc, ok := ret.Get(0).(func(string) bool); ok { - r0 = returnFunc(key) - } else { - r0 = ret.Get(0).(bool) - } - return r0 -} - -// MockCache_IsExist_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsExist' -type MockCache_IsExist_Call struct { - *mock.Call -} - -// IsExist is a helper method to define mock.On call -// - key string -func (_e *MockCache_Expecter) IsExist(key any) *MockCache_IsExist_Call { - return &MockCache_IsExist_Call{Call: _e.mock.On("IsExist", key)} -} - -func (_c *MockCache_IsExist_Call) Run(run func(key string)) *MockCache_IsExist_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 string - if args[0] != nil { - arg0 = args[0].(string) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *MockCache_IsExist_Call) Return(b bool) *MockCache_IsExist_Call { - _c.Call.Return(b) - return _c -} - -func (_c *MockCache_IsExist_Call) RunAndReturn(run func(key string) bool) *MockCache_IsExist_Call { - _c.Call.Return(run) - return _c -} - -// Ping provides a mock function for the type MockCache -func (_mock *MockCache) Ping() error { - ret := _mock.Called() - - if len(ret) == 0 { - panic("no return value specified for Ping") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func() error); ok { - r0 = returnFunc() - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockCache_Ping_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Ping' -type MockCache_Ping_Call struct { - *mock.Call -} - -// Ping is a helper method to define mock.On call -func (_e *MockCache_Expecter) Ping() *MockCache_Ping_Call { - return &MockCache_Ping_Call{Call: _e.mock.On("Ping")} -} - -func (_c *MockCache_Ping_Call) Run(run func()) *MockCache_Ping_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockCache_Ping_Call) Return(err error) *MockCache_Ping_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockCache_Ping_Call) RunAndReturn(run func() error) *MockCache_Ping_Call { - _c.Call.Return(run) - return _c -} - -// Put provides a mock function for the type MockCache -func (_mock *MockCache) Put(key string, val any, timeout int64) error { - ret := _mock.Called(key, val, timeout) - - if len(ret) == 0 { - panic("no return value specified for Put") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(string, any, int64) error); ok { - r0 = returnFunc(key, val, timeout) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockCache_Put_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Put' -type MockCache_Put_Call struct { - *mock.Call -} - -// Put is a helper method to define mock.On call -// - key string -// - val any -// - timeout int64 -func (_e *MockCache_Expecter) Put(key, val, timeout any) *MockCache_Put_Call { - return &MockCache_Put_Call{Call: _e.mock.On("Put", key, val, timeout)} -} - -func (_c *MockCache_Put_Call) Run(run func(key string, val any, timeout int64)) *MockCache_Put_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 string - if args[0] != nil { - arg0 = args[0].(string) - } - var arg1 any - if args[1] != nil { - arg1 = args[1].(any) - } - var arg2 int64 - if args[2] != nil { - arg2 = args[2].(int64) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *MockCache_Put_Call) Return(err error) *MockCache_Put_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockCache_Put_Call) RunAndReturn(run func(key string, val any, timeout int64) error) *MockCache_Put_Call { - _c.Call.Return(run) - return _c -} - -// StartAndGC provides a mock function for the type MockCache -func (_mock *MockCache) StartAndGC(opt cache.Options) error { - ret := _mock.Called(opt) - - if len(ret) == 0 { - panic("no return value specified for StartAndGC") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func(cache.Options) error); ok { - r0 = returnFunc(opt) - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockCache_StartAndGC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartAndGC' -type MockCache_StartAndGC_Call struct { - *mock.Call -} - -// StartAndGC is a helper method to define mock.On call -// - opt cache.Options -func (_e *MockCache_Expecter) StartAndGC(opt any) *MockCache_StartAndGC_Call { - return &MockCache_StartAndGC_Call{Call: _e.mock.On("StartAndGC", opt)} -} - -func (_c *MockCache_StartAndGC_Call) Run(run func(opt cache.Options)) *MockCache_StartAndGC_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 cache.Options - if args[0] != nil { - arg0 = args[0].(cache.Options) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *MockCache_StartAndGC_Call) Return(err error) *MockCache_StartAndGC_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockCache_StartAndGC_Call) RunAndReturn(run func(opt cache.Options) error) *MockCache_StartAndGC_Call { - _c.Call.Return(run) - return _c -} diff --git a/modules/cache/mutex_map_test.go b/modules/cache/mutex_map_test.go index 7dd21c2f56..324b3228d8 100644 --- a/modules/cache/mutex_map_test.go +++ b/modules/cache/mutex_map_test.go @@ -57,7 +57,7 @@ func TestMutexMap_DifferentKeys(t *testing.T) { done := make(chan bool, 1) go func() { - // If these somehow referred to the same underlying `sync.Mutex`, because `sync.Mutex` is not re-entrant this would + // If these somehow refered to the same underlying `sync.Mutex`, because `sync.Mutex` is not re-entrant this would // never complete. unlock1 := mm.Lock("test-key-1") unlock2 := mm.Lock("test-key-2") diff --git a/modules/charset/charset.go b/modules/charset/charset.go index d4121fb27f..cb03deb966 100644 --- a/modules/charset/charset.go +++ b/modules/charset/charset.go @@ -164,7 +164,7 @@ func DetectEncoding(content []byte) (string, error) { } times := 1024 / len(content) detectContent = make([]byte, 0, times*len(content)) - for range times { + for i := 0; i < times; i++ { detectContent = append(detectContent, content...) } } else { diff --git a/modules/charset/charset_test.go b/modules/charset/charset_test.go index c29987beb6..358220494b 100644 --- a/modules/charset/charset_test.go +++ b/modules/charset/charset_test.go @@ -243,7 +243,7 @@ func stringMustEndWith(t *testing.T, expected, value string) { func TestToUTF8WithFallbackReader(t *testing.T) { resetDefaultCharsetsOrder() - for testLen := range 2048 { + for testLen := 0; testLen < 2048; testLen++ { pattern := " test { () }\n" input := "" for len(input) < testLen { diff --git a/modules/charset/escape.go b/modules/charset/escape.go index 5b185b1608..57b13c1f18 100644 --- a/modules/charset/escape.go +++ b/modules/charset/escape.go @@ -30,7 +30,7 @@ const ( WikiContext escapeContext = "wiki" // Rendered content (except markup), source code and blames. FileviewContext escapeContext = "file-view" - // Commits or pull request's diff. + // Commits or pull requet's diff. DiffContext escapeContext = "diff" ) diff --git a/modules/eventsource/manager.go b/modules/eventsource/manager.go index e0d7ab78c4..730cacd940 100644 --- a/modules/eventsource/manager.go +++ b/modules/eventsource/manager.go @@ -39,9 +39,7 @@ func (m *Manager) Register(uid int64) <-chan *Event { } select { case m.connection <- struct{}{}: - break default: - break } m.mutex.Unlock() return messenger.Register() diff --git a/modules/eventsource/manager_run.go b/modules/eventsource/manager_run.go index 48500feafc..0eaee5dc3c 100644 --- a/modules/eventsource/manager_run.go +++ b/modules/eventsource/manager_run.go @@ -47,9 +47,7 @@ loop: // empty the connection channel select { case <-m.connection: - break default: - break } } m.mutex.Unlock() @@ -65,9 +63,7 @@ loop: // We won't change the "then" time because there could be concurrency issues select { case <-timer.C: - break default: - break } continue } diff --git a/modules/eventsource/messenger.go b/modules/eventsource/messenger.go index 380cb13f20..378e717126 100644 --- a/modules/eventsource/messenger.go +++ b/modules/eventsource/messenger.go @@ -62,9 +62,7 @@ func (m *Messenger) SendMessage(message *Event) { channel := m.channels[i] select { case channel <- message: - break default: - break } } } diff --git a/modules/forgefed/activity_follow.go b/modules/forgefed/activity_follow.go index 928321a00a..5cb45ca885 100644 --- a/modules/forgefed/activity_follow.go +++ b/modules/forgefed/activity_follow.go @@ -48,8 +48,8 @@ func (follow *ForgeFollow) UnmarshalJSON(data []byte) error { func (follow ForgeFollow) Validate() []string { var result []string - result = append(result, validation.ValidateNotEmpty(follow.Type, "type")...) - result = append(result, validation.ValidateOneOf(follow.Type, []any{ap.FollowType}, "type")...) + result = append(result, validation.ValidateNotEmpty(string(follow.Type), "type")...) + result = append(result, validation.ValidateOneOf(string(follow.Type), []any{"Follow"}, "type")...) result = append(result, validation.ValidateIDExists(follow.Actor, "actor")...) result = append(result, validation.ValidateIDExists(follow.Object, "object")...) diff --git a/modules/forgefed/activity_follow_test.go b/modules/forgefed/activity_follow_test.go index e0142a39f2..18fbef33aa 100644 --- a/modules/forgefed/activity_follow_test.go +++ b/modules/forgefed/activity_follow_test.go @@ -15,7 +15,7 @@ import ( func Test_NewForgeFollowValidation(t *testing.T) { sut := forgefed.ForgeFollow{} - sut.Type = ap.FollowType + sut.Type = "Follow" sut.Actor = ap.IRI("example.org/alice") sut.Object = ap.IRI("example.org/bob") diff --git a/modules/forgefed/activity_like.go b/modules/forgefed/activity_like.go index 7849aec568..e52d0a9af6 100644 --- a/modules/forgefed/activity_like.go +++ b/modules/forgefed/activity_like.go @@ -44,8 +44,8 @@ func (like ForgeLike) IsNewer(compareTo time.Time) bool { func (like ForgeLike) Validate() []string { var result []string - result = append(result, validation.ValidateNotEmpty(like.Type, "type")...) - result = append(result, validation.ValidateOneOf(like.Type, []any{ap.LikeType}, "type")...) + result = append(result, validation.ValidateNotEmpty(string(like.Type), "type")...) + result = append(result, validation.ValidateOneOf(string(like.Type), []any{"Like"}, "type")...) if like.Actor == nil { result = append(result, "Actor should not be nil.") diff --git a/modules/forgefed/activity_like_test.go b/modules/forgefed/activity_like_test.go index dc2d8efdc7..c0b565f4db 100644 --- a/modules/forgefed/activity_like_test.go +++ b/modules/forgefed/activity_like_test.go @@ -51,7 +51,7 @@ func Test_LikeMarshalJSON(t *testing.T) { item: forgefed.ForgeLike{ Activity: ap.Activity{ Actor: ap.IRI("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1"), - Type: ap.LikeType, + Type: "Like", Object: ap.IRI("https://codeberg.org/api/v1/activitypub/repository-id/1"), }, }, @@ -80,7 +80,7 @@ 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: &forgefed.ForgeLike{ Activity: ap.Activity{ - Type: ap.LikeType, + Type: "Like", Actor: ap.IRI("https://repo.prod.meissa.de/api/activitypub/user-id/1"), Object: ap.IRI("https://codeberg.org/api/activitypub/repository-id/1"), }, @@ -124,7 +124,7 @@ func Test_ForgeLikeValidation(t *testing.T) { validate := sut.Validate() assert.Len(t, validate, 2) assert.Equal(t, - "Field type contains the value , which is not in allowed subset [Like]", + "Field type contains the value , which is not in allowed subset [Like]", validate[1]) sut.UnmarshalJSON([]byte(`{"type":"bad-type", diff --git a/modules/forgefed/activity_undo_like.go b/modules/forgefed/activity_undo_like.go index eaf32ab09f..8b7df582ad 100644 --- a/modules/forgefed/activity_undo_like.go +++ b/modules/forgefed/activity_undo_like.go @@ -42,8 +42,8 @@ func (undo *ForgeUndoLike) UnmarshalJSON(data []byte) error { func (undo ForgeUndoLike) Validate() []string { var result []string - result = append(result, validation.ValidateNotEmpty(undo.Type, "type")...) - result = append(result, validation.ValidateOneOf(undo.Type, []any{ap.UndoType}, "type")...) + result = append(result, validation.ValidateNotEmpty(string(undo.Type), "type")...) + result = append(result, validation.ValidateOneOf(string(undo.Type), []any{"Undo"}, "type")...) if undo.Actor == nil { result = append(result, "Actor should not be nil.") @@ -61,8 +61,8 @@ func (undo ForgeUndoLike) Validate() []string { } else if activity, ok := undo.Object.(*ap.Activity); !ok { result = append(result, "object is not of type Activity") } else { - result = append(result, validation.ValidateNotEmpty(activity.Type, "type")...) - result = append(result, validation.ValidateOneOf(activity.Type, []any{ap.LikeType}, "type")...) + result = append(result, validation.ValidateNotEmpty(string(activity.Type), "type")...) + result = append(result, validation.ValidateOneOf(string(activity.Type), []any{"Like"}, "type")...) if activity.Actor == nil { result = append(result, "Object.Actor should not be nil.") diff --git a/modules/forgefed/activity_undo_like_test.go b/modules/forgefed/activity_undo_like_test.go index cbb309afc5..18db688c48 100644 --- a/modules/forgefed/activity_undo_like_test.go +++ b/modules/forgefed/activity_undo_like_test.go @@ -64,7 +64,7 @@ func Test_UndoLikeMarshalJSON(t *testing.T) { Activity: ap.Activity{ StartTime: startTime, Actor: ap.IRI("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1"), - Type: ap.UndoType, + Type: "Undo", Object: like, }, }, @@ -117,7 +117,7 @@ func Test_UndoLikeUnmarshalJSON(t *testing.T) { Activity: ap.Activity{ StartTime: startTime, Actor: ap.IRI("https://repo.prod.meissa.de/api/v1/activitypub/user-id/1"), - Type: ap.UndoType, + Type: "Undo", Object: like, }, }, diff --git a/modules/forgefed/activity_user_activity.go b/modules/forgefed/activity_user_activity.go index 69e7bc3ead..82353245c9 100644 --- a/modules/forgefed/activity_user_activity.go +++ b/modules/forgefed/activity_user_activity.go @@ -60,8 +60,8 @@ func NewForgeUserActivity(doer *user_model.User, actionID int64, content string) func (userActivity ForgeUserActivity) Validate() []string { var result []string - result = append(result, validation.ValidateNotEmpty(userActivity.Type, "type")...) - result = append(result, validation.ValidateOneOf(userActivity.Type, []any{ap.CreateType}, "type")...) + result = append(result, validation.ValidateNotEmpty(string(userActivity.Type), "type")...) + result = append(result, validation.ValidateOneOf(string(userActivity.Type), []any{"Create"}, "type")...) result = append(result, validation.ValidateIDExists(userActivity.Actor, "actor")...) if len(userActivity.To) == 0 { diff --git a/modules/forgefed/activity_user_activity_test.go b/modules/forgefed/activity_user_activity_test.go index 106f0ac73c..49137c7ab4 100644 --- a/modules/forgefed/activity_user_activity_test.go +++ b/modules/forgefed/activity_user_activity_test.go @@ -15,14 +15,17 @@ import ( func Test_ForgeUserActivityValidation(t *testing.T) { note := forgefed.ForgeUserActivityNote{} - note.Type = ap.NoteType + note.Type = "Note" note.Content = ap.NaturalLanguageValues{ - ap.NilLangRef: ap.Content("Any Content!"), + { + Ref: ap.NilLangRef, + Value: ap.Content("Any Content!"), + }, } note.URL = ap.IRI("example.org/user-id/57") sut := forgefed.ForgeUserActivity{} - sut.Type = ap.CreateType + sut.Type = "Create" sut.Actor = ap.IRI("example.org/user-id/23") sut.CC = ap.ItemCollection{ ap.IRI("example.org/registration/public#2nd"), diff --git a/modules/forgefed/actor.go b/modules/forgefed/actor.go index 1f6e1f1fdf..5383d5adaf 100644 --- a/modules/forgefed/actor.go +++ b/modules/forgefed/actor.go @@ -6,7 +6,6 @@ package forgefed import ( "fmt" "net/url" - "slices" "strconv" "strings" @@ -108,7 +107,12 @@ func newActorID(uri string) (ActorID, error) { } func containsEmptyString(ar []string) bool { - return slices.Contains(ar, "") + for _, elem := range ar { + if elem == "" { + return true + } + } + return false } func removeEmptyStrings(ls []string) []string { diff --git a/modules/forgefed/actor_person.go b/modules/forgefed/actor_person.go index aee7d3c37f..7c43b0d7ce 100644 --- a/modules/forgefed/actor_person.go +++ b/modules/forgefed/actor_person.go @@ -20,8 +20,6 @@ type PersonID struct { const ( personIDapiPathV1 = "api/v1/activitypub/user-id" personIDapiPathV1Latest = "api/activitypub/user-id" - actorIDapiPathV1 = "api/v1/activitypub" - actorIDapiPathLatest = "api/activitypub" ) // Factory function for PersonID. Created struct is asserted to be valid @@ -73,13 +71,12 @@ func (id PersonID) AsLoginName() string { return result } -// HostSuffix returns the host part of a handle, i.e. @host.tld (if port is supplemented) or @host.tld:1234 func (id PersonID) HostSuffix() string { var result string if !id.IsPortSupplemented { - result = fmt.Sprintf("@%s:%d", strings.ToLower(id.Host), id.HostPort) + result = fmt.Sprintf("-%s-%d", strings.ToLower(id.Host), id.HostPort) } else { - result = fmt.Sprintf("@%s", strings.ToLower(id.Host)) + result = fmt.Sprintf("-%s", strings.ToLower(id.Host)) } return result } @@ -90,11 +87,8 @@ func (id PersonID) Validate() []string { result = append(result, validation.ValidateOneOf(id.Source, []any{"forgejo", "gitea", "mastodon", "gotosocial"}, "Source")...) if id.Source == "forgejo" { result = append(result, validation.ValidateNotEmpty(id.Path, "path")...) - lowerPath := strings.ToLower(id.Path) - if lowerPath != personIDapiPathV1 && lowerPath != personIDapiPathV1Latest { - if lowerPath != actorIDapiPathV1 && lowerPath != actorIDapiPathLatest || id.ID != "actor" { - result = append(result, fmt.Sprintf("path: %q has to be a person specific api path", id.Path)) - } + if strings.ToLower(id.Path) != personIDapiPathV1 && strings.ToLower(id.Path) != personIDapiPathV1Latest { + result = append(result, fmt.Sprintf("path: %q has to be a person specific api path", id.Path)) } } @@ -120,8 +114,8 @@ func (s *ForgePerson) UnmarshalJSON(data []byte) error { func (s ForgePerson) Validate() []string { var result []string - result = append(result, validation.ValidateNotEmpty(s.Type, "Type")...) - result = append(result, validation.ValidateOneOf(s.Type, []any{ap.PersonType}, "Type")...) + result = append(result, validation.ValidateNotEmpty(string(s.Type), "Type")...) + result = append(result, validation.ValidateOneOf(string(s.Type), []any{string(ap.PersonType)}, "Type")...) result = append(result, validation.ValidateNotEmpty(s.PreferredUsername.String(), "PreferredUsername")...) return result diff --git a/modules/forgefed/actor_person_test.go b/modules/forgefed/actor_person_test.go index 72ff976688..a5f3ee47b1 100644 --- a/modules/forgefed/actor_person_test.go +++ b/modules/forgefed/actor_person_test.go @@ -159,48 +159,6 @@ func TestPersonIdValidation(t *testing.T) { result, err = validation.IsValid(sut) assert.False(t, result) require.EqualError(t, err, "Validation Error: forgefed.PersonID: Field Source contains the value forgejox, which is not in allowed subset [forgejo gitea mastodon gotosocial]") - - sut = forgefed.PersonID{} - sut.ID = "actor" - sut.Source = "forgejo" - sut.HostSchema = "https" - sut.Path = "api/v1/activitypub" - sut.Host = "example.com" - sut.HostPort = 443 - sut.IsPortSupplemented = true - sut.UnvalidatedInput = "https://example.com/api/v1/activitypub/actor" - - result, err = validation.IsValid(sut) - assert.True(t, result) - require.NoError(t, err) - - sut = forgefed.PersonID{} - sut.ID = "actor" - sut.Source = "forgejo" - sut.HostSchema = "https" - sut.Path = "api/activitypub" - sut.Host = "example.com" - sut.HostPort = 443 - sut.IsPortSupplemented = true - sut.UnvalidatedInput = "https://example.com/api/activitypub/actor" - - result, err = validation.IsValid(sut) - assert.True(t, result) - require.NoError(t, err) - - sut = forgefed.PersonID{} - sut.ID = "1" - sut.Source = "forgejo" - sut.HostSchema = "https" - sut.Path = "api/v1/activitypub" - sut.Host = "example.com" - sut.HostPort = 443 - sut.IsPortSupplemented = true - sut.UnvalidatedInput = "https://example.com/api/v1/activitypub/1" - - result, err = validation.IsValid(sut) - assert.False(t, result) - require.EqualError(t, err, "Validation Error: forgefed.PersonID: path: \"api/v1/activitypub\" has to be a person specific api path") } func TestWebfingerId(t *testing.T) { @@ -236,9 +194,9 @@ func TestShouldThrowErrorOnInvalidInput(t *testing.T) { func Test_PersonMarshalJSON(t *testing.T) { sut := forgefed.ForgePerson{} - sut.Type = ap.PersonType + sut.Type = "Person" sut.PreferredUsername = ap.NaturalLanguageValuesNew() - sut.PreferredUsername.Set(ap.English, ap.Content("MaxMuster")) + sut.PreferredUsername.Set("en", ap.Content("MaxMuster")) result, _ := sut.MarshalJSON() assert.JSONEq(t, `{"type":"Person","preferredUsername":"MaxMuster"}`, string(result), "Expected string is not equal") } @@ -246,9 +204,9 @@ func Test_PersonMarshalJSON(t *testing.T) { func Test_PersonUnmarshalJSON(t *testing.T) { expected := &forgefed.ForgePerson{ Actor: ap.Actor{ - Type: ap.PersonType, + Type: "Person", PreferredUsername: ap.NaturalLanguageValues{ - ap.English: []byte("MaxMuster"), + ap.LangRefValue{Ref: "en", Value: []byte("MaxMuster")}, }, }, } @@ -288,19 +246,8 @@ func TestForgePersonValidation(t *testing.T) { func TestAsloginName(t *testing.T) { sut, _ := forgefed.NewPersonID("https://codeberg.org/api/v1/activitypub/user-id/12345", "forgejo") - assert.Equal(t, "12345@codeberg.org", sut.AsLoginName()) + assert.Equal(t, "12345-codeberg.org", sut.AsLoginName()) sut, _ = forgefed.NewPersonID("https://codeberg.org:443/api/v1/activitypub/user-id/12345", "forgejo") - assert.Equal(t, "12345@codeberg.org:443", sut.AsLoginName()) -} - -func TestHostSuffix(t *testing.T) { - sut, _ := forgefed.NewPersonID("https://codeberg.org/api/v1/activitypub/user-id/12345", "forgejo") - sut.Host = "forgejo.example.tld" - sut.HostPort = 80 - - // sut.IsPortSupplemented is true by default at time of writing. - assert.Equal(t, "@forgejo.example.tld", sut.HostSuffix()) - sut.IsPortSupplemented = false - assert.Equal(t, "@forgejo.example.tld:80", sut.HostSuffix()) + assert.Equal(t, "12345-codeberg.org-443", sut.AsLoginName()) } diff --git a/modules/forgefed/inbox.go b/modules/forgefed/inbox.go deleted file mode 100644 index c02aedd38c..0000000000 --- a/modules/forgefed/inbox.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package forgefed - -import ( - ap "github.com/go-ap/activitypub" -) - -// ForgeFollow activity data type -// swagger:model -type ForgeInbox struct { - // swagger:ignore - ap.InboxStream -} diff --git a/modules/forgefed/object_user_activity_note.go b/modules/forgefed/object_user_activity_note.go index b8e09ecf94..758c25aef8 100644 --- a/modules/forgefed/object_user_activity_note.go +++ b/modules/forgefed/object_user_activity_note.go @@ -35,7 +35,10 @@ func newNote(doer *user_model.User, content, id string, published time.Time) (Fo note.Type = ap.NoteType note.AttributedTo = ap.IRI(doer.APActorID()) note.Content = ap.NaturalLanguageValues{ - ap.NilLangRef: ap.Content(content), + { + Ref: ap.NilLangRef, + Value: ap.Content(content), + }, } note.ID = ap.IRI(id) note.Published = published @@ -56,8 +59,8 @@ func newNote(doer *user_model.User, content, id string, published time.Time) (Fo func (note ForgeUserActivityNote) Validate() []string { var result []string - result = append(result, validation.ValidateNotEmpty(note.Type, "type")...) - result = append(result, validation.ValidateOneOf(note.Type, []any{ap.NoteType}, "type")...) + result = append(result, validation.ValidateNotEmpty(string(note.Type), "type")...) + result = append(result, validation.ValidateOneOf(string(note.Type), []any{"Note"}, "type")...) result = append(result, validation.ValidateNotEmpty(note.Content.String(), "content")...) result = append(result, validation.ValidateIDExists(note.URL, "url")...) diff --git a/modules/forgefed/object_user_activity_note_test.go b/modules/forgefed/object_user_activity_note_test.go index e559ce84f7..4f790033bc 100644 --- a/modules/forgefed/object_user_activity_note_test.go +++ b/modules/forgefed/object_user_activity_note_test.go @@ -15,9 +15,12 @@ import ( func Test_UserActivityNoteValidation(t *testing.T) { sut := forgefed.ForgeUserActivityNote{} - sut.Type = ap.NoteType + sut.Type = "Note" sut.Content = ap.NaturalLanguageValues{ - ap.NilLangRef: ap.Content("Any Content!"), + { + Ref: ap.NilLangRef, + Value: ap.Content("Any Content!"), + }, } sut.URL = ap.IRI("example.org/user-id/57") diff --git a/modules/forgefed/repository.go b/modules/forgefed/repository.go index 1e85d1e64c..63680ccd35 100644 --- a/modules/forgefed/repository.go +++ b/modules/forgefed/repository.go @@ -88,7 +88,7 @@ func ToRepository(it ap.Item) (*Repository, error) { return (*Repository)(unsafe.Pointer(&i)), nil default: // NOTE(marius): this is an ugly way of dealing with the interface conversion error: types from different scopes - typ := reflect.TypeFor[*Repository]() + typ := reflect.TypeOf(new(Repository)) if i, ok := reflect.ValueOf(it).Convert(typ).Interface().(*Repository); ok { return i, nil } diff --git a/modules/generate/generate.go b/modules/generate/generate.go index 0dd5d071de..2df808fe9e 100644 --- a/modules/generate/generate.go +++ b/modules/generate/generate.go @@ -37,7 +37,7 @@ func DecodeJwtSecret(src string) ([]byte, error) { encoding := base64.RawURLEncoding decoded := make([]byte, encoding.DecodedLen(len(src))+3) if n, err := encoding.Decode(decoded, []byte(src)); err != nil { - return nil, fmt.Errorf("JwtSecret decode failed: %v", err) + return nil, err } else if n != defaultJwtSecretLen { return nil, fmt.Errorf("invalid base64 decoded length: %d, expects: %d", n, defaultJwtSecretLen) } diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go index a8cd626d81..1297c7247f 100644 --- a/modules/git/batch_reader.go +++ b/modules/git/batch_reader.go @@ -273,7 +273,7 @@ func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBu idx := bytes.IndexByte(readBytes, ' ') if idx < 0 { log.Debug("missing space in readBytes ParseTreeLine: %s", readBytes) - return mode, fname, sha, n, ErrNotExist{} + return mode, fname, sha, n, &ErrNotExist{} } n += idx + 1 diff --git a/modules/git/blame.go b/modules/git/blame.go index 881ef04302..868edab2b8 100644 --- a/modules/git/blame.go +++ b/modules/git/blame.go @@ -207,5 +207,5 @@ func tryCreateBlameIgnoreRevsFile(commit *Commit) *string { return nil } - return new(f.Name()) + return util.ToPointer(f.Name()) } diff --git a/modules/git/command.go b/modules/git/command.go index 4ab081418b..bf1d624dbf 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -10,7 +10,6 @@ import ( "errors" "fmt" "io" - "net/url" "os" "os/exec" "runtime/trace" @@ -447,54 +446,6 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS return stdoutBuf.Bytes(), stderr, nil } -// If `remoteURL` is a URL with a password in it, add parameters to the git command that will read that password from a -// credential store file, and return the URL that should be used in the command instead of the original, and a cleanup -// function to call to remove the credential file. If `remoteURL` doesn't have a password, then it is returned as-is. -// This function must be invoked on the the git command before the git sub-command -- eg. before the `clone` or `fetch` -// parameter is added to the command's args. -func (c *Command) AddAuthCredentialHelperForRemote(remoteURL string) (commandURL string, cleanup func(), err error) { - parsedFromURL, _ := url.Parse(remoteURL) - - // If the clone URL has credentials, build a credential file for usage by git-credential-store - // to prevent credential leak in the process list. - // https://git-scm.com/docs/git-credential-store#_storage_format - // credential.helper adjustment must be set before the git subcommand - if strings.Contains(remoteURL, "://") && strings.Contains(remoteURL, "@") && parsedFromURL != nil { - credentialsFile, err := os.CreateTemp("", "forgejo-clone-credentials-") - if err != nil { - return "", nil, err - } - credentialsPath := credentialsFile.Name() - - cleanup := func() { - _ = credentialsFile.Close() - if err := util.Remove(credentialsPath); err != nil { - log.Warn("Unable to remove temporary file %q: %v", credentialsPath, err) - } - } - _, err = credentialsFile.Write([]byte(parsedFromURL.String())) - if err != nil { - cleanup() - return "", nil, err - } - err = credentialsFile.Close() - if err != nil { - cleanup() - return "", nil, err - } - - c.AddArguments("-c").AddDynamicArguments("credential.helper=store --file=" + credentialsPath) - - // remove the password from the URL argument - parsedFromURL.User = url.User(parsedFromURL.User.Username()) - commandURL = parsedFromURL.String() - - return commandURL, cleanup, nil - } - - return remoteURL, func() {}, nil -} - // AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests func AllowLFSFiltersArgs() TrustedCmdArgs { // Now here we should explicitly allow lfs filters to run diff --git a/modules/git/commit.go b/modules/git/commit.go index 36ba8ef8ca..4fb13ecd4f 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -269,8 +269,8 @@ func NewSearchCommitsOptions(searchString string, forAllRefs bool) SearchCommits var keywords, authors, committers []string var after, before string - fields := strings.FieldsSeq(searchString) - for k := range fields { + fields := strings.Fields(searchString) + for _, k := range fields { switch { case strings.HasPrefix(k, "author:"): authors = append(authors, strings.TrimPrefix(k, "author:")) diff --git a/modules/git/commit_info.go b/modules/git/commit_info.go index 62f58f8767..6511a1689a 100644 --- a/modules/git/commit_info.go +++ b/modules/git/commit_info.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "io" - "maps" "path" "sort" @@ -46,7 +45,9 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath return nil, nil, err } - maps.Copy(revs, commits) + for pth, found := range commits { + revs[pth] = found + } } } else { sort.Strings(entryPaths) diff --git a/modules/git/diff.go b/modules/git/diff.go index c954a933ba..0ba9c60912 100644 --- a/modules/git/diff.go +++ b/modules/git/diff.go @@ -7,7 +7,6 @@ import ( "bufio" "bytes" "context" - "errors" "fmt" "io" "os" @@ -101,7 +100,7 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff } // ParseDiffHunkString parse the diffhunk content and return -func ParseDiffHunkString(diffhunk string) (leftLine, leftHunk, rightLine, rightHunk int) { +func ParseDiffHunkString(diffhunk string) (leftLine, leftHunk, rightLine, righHunk int) { ss := strings.Split(diffhunk, "@@") ranges := strings.Split(ss[1][1:], " ") leftRange := strings.Split(ranges[0], ",") @@ -113,14 +112,14 @@ func ParseDiffHunkString(diffhunk string) (leftLine, leftHunk, rightLine, rightH rightRange := strings.Split(ranges[1], ",") rightLine, _ = strconv.Atoi(rightRange[0]) if len(rightRange) > 1 { - rightHunk, _ = strconv.Atoi(rightRange[1]) + righHunk, _ = strconv.Atoi(rightRange[1]) } } else { log.Debug("Parse line number failed: %v", diffhunk) rightLine = leftLine - rightHunk = leftHunk + righHunk = leftHunk } - return leftLine, leftHunk, rightLine, rightHunk + return leftLine, leftHunk, rightLine, righHunk } // Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9] @@ -222,7 +221,6 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi } case '\\': // FIXME: handle `\ No newline at end of file` - break default: currentLine++ otherLine++ @@ -278,76 +276,6 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi return strings.Join(newHunk, "\n"), nil } -var ErrLineNotFound = errors.New("line not found in diff") - -type LinePlacement struct { - Left int64 - Right int64 -} - -// Find the line of code where an old line of code from an old patch is, if present, in a new patch. Given a cutDiff -// (from CutDiffAroundLine) and the line of code that it was cut from, and, given a single-file diff from the commit -// where that patch came into a new head, this routine will read through the diff and identify the new line number. It -// will only return successful if the line is exactly the same as the original line, but just placed in a new location -// due to added or removed lines in the diff before the target line of code. -func FindAdjustedLineNumber(cutDiff string, originalLine int64, fullDiff io.Reader) (LinePlacement, error) { - cutDiffSplit := strings.Split(cutDiff, "\n") - if len(cutDiffSplit) == 0 { - return LinePlacement{}, errors.New("cutDiff has no contents") - } - endOfCutDiff := cutDiffSplit[len(cutDiffSplit)-1] - - scanner := bufio.NewScanner(fullDiff) - inHunk := false // used to skip header lines before the first hunk - leftLine := int64(-1) - rightLine := int64(-1) - - for scanner.Scan() { - lineText := scanner.Text() - if strings.HasPrefix(lineText, "@@") { - // A map with named groups of our regex to recognize them later more easily - submatches := hunkRegex.FindStringSubmatch(lineText) - groups := make(map[string]string) - for i, name := range hunkRegex.SubexpNames() { - if i != 0 && name != "" { - groups[name] = submatches[i] - } - } - beginLeft, _ := strconv.ParseInt(groups["beginOld"], 10, 64) - beginRight, _ := strconv.ParseInt(groups["beginNew"], 10, 64) - leftLine = beginLeft - rightLine = beginRight - inHunk = true - } else if inHunk { - if leftLine == originalLine { - if lineText != endOfCutDiff { - return LinePlacement{}, fmt.Errorf( - "line was adjusted from index %d to %d, but contents changed from %q to %q: %w", - originalLine, leftLine, endOfCutDiff, lineText, ErrLineNotFound) - } - return LinePlacement{Left: leftLine, Right: rightLine}, nil - } - switch lineText[0] { - case '+': - rightLine++ - case '-': - leftLine++ - case '\\': - // Should be the end-of-file with "\ No newline at end of file" -- nothing to do here. - break - default: - rightLine++ - leftLine++ - } - } - } - if err := scanner.Err(); err != nil { - return LinePlacement{}, err - } - - return LinePlacement{}, fmt.Errorf("line is no longer in diff: %w", ErrLineNotFound) -} - // GetAffectedFiles returns the affected files between two commits func GetAffectedFiles(repo *Repository, oldCommitID, newCommitID string, env []string) ([]string, error) { objectFormat, err := repo.GetObjectFormat() diff --git a/modules/git/diff_compare_test.go b/modules/git/diff_compare_test.go index 34c2017b8a..433497b5c4 100644 --- a/modules/git/diff_compare_test.go +++ b/modules/git/diff_compare_test.go @@ -247,7 +247,7 @@ func TestCheckIfDiffDiffers(t *testing.T) { 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 longer shows the change made to `README`. + // 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. diff --git a/modules/git/diff_test.go b/modules/git/diff_test.go index e4b7ce6ace..9130767c66 100644 --- a/modules/git/diff_test.go +++ b/modules/git/diff_test.go @@ -30,7 +30,7 @@ index d8e4c92..19dc8ad 100644 @@ -1,9 +1,10 @@ --some comment --- some comment 5 -+--some comment 2 ++--some coment 2 +-- some comment 3 create or replace procedure test(p1 varchar2) is @@ -135,7 +135,7 @@ func TestCutDiffAroundLine(t *testing.T) { @@ -1,9 +1,10 @@ --some comment --- some comment 5 -+--some comment 2` ++--some coment 2` assert.Equal(t, expected, minusDiff) // Handle minus diffs properly @@ -148,7 +148,7 @@ func TestCutDiffAroundLine(t *testing.T) { @@ -1,9 +1,10 @@ --some comment --- some comment 5 -+--some comment 2 ++--some coment 2 +-- some comment 3` assert.Equal(t, expected, minusDiff) @@ -167,215 +167,3 @@ func TestParseDiffHunkString(t *testing.T) { assert.Equal(t, 19, rightLine) assert.Equal(t, 5, rightHunk) } - -func TestFindAdjustedLineNumber(t *testing.T) { - commentCutDiff := `diff --git a/file1.md b/file1.md ---- a/file1.md -+++ b/file1.md -@@ -47,7 +47,6 @@ Line 46 - Line 47 - Line 48 - Line 49 --Line 50` - - t.Run("no additional changes", func(t *testing.T) { - diff := `diff --git a/file1.md b/file1.md -index 2d203fb..b21df3f 100644 ---- a/file1.md -+++ b/file1.md -@@ -47,7 +47,6 @@ Line 46 - Line 47 - Line 48 - Line 49 --Line 50 - Line 51 - Line 52 - Line 53` - lineNumber, err := FindAdjustedLineNumber(commentCutDiff, 50, strings.NewReader(diff)) - require.NoError(t, err) - assert.Equal(t, LinePlacement{Left: 50, Right: 50}, lineNumber) - }) - - t.Run("removed lines before location", func(t *testing.T) { - diff := `diff --git a/file1.md b/file1.md -index 2d203fb..c85b903 100644 ---- a/file1.md -+++ b/file1.md -@@ -1,13 +1,3 @@ --Line 1 --Line 2 --Line 3 --Line 4 --Line 5 --Line 6 --Line 7 --Line 8 --Line 9 --Line 10 - Line 11 - Line 12 - Line 13 -@@ -47,7 +37,6 @@ Line 46 - Line 47 - Line 48 - Line 49 --Line 50 - Line 51 - Line 52 - Line 53` - lineNumber, err := FindAdjustedLineNumber(commentCutDiff, 50, strings.NewReader(diff)) - require.NoError(t, err) - assert.Equal(t, LinePlacement{Left: 50, Right: 40}, lineNumber) - }) - - t.Run("added lines before location", func(t *testing.T) { - diff := `diff --git a/file1.md b/file1.md -index 2d203fb..24b1aa6 100644 ---- a/file1.md -+++ b/file1.md -@@ -8,6 +8,11 @@ Line 7 - Line 8 - Line 9 - Line 10 -+Line 10.1 -+Line 10.2 -+Line 10.3 -+Line 10.4 -+Line 10.5 - Line 11 - Line 12 - Line 13 -@@ -47,7 +52,6 @@ Line 46 - Line 47 - Line 48 - Line 49 --Line 50 - Line 51 - Line 52 - Line 53` - lineNumber, err := FindAdjustedLineNumber(commentCutDiff, 50, strings.NewReader(diff)) - require.NoError(t, err) - assert.Equal(t, LinePlacement{Left: 50, Right: 55}, lineNumber) - }) - - t.Run("added and removed in lines before location", func(t *testing.T) { - diff := `diff --git a/file1.md b/file1.md -index 2d203fb..d0cb63f 100644 ---- a/file1.md -+++ b/file1.md -@@ -5,9 +5,11 @@ Line 4 - Line 5 - Line 6 - Line 7 --Line 8 --Line 9 --Line 10 -+Line 10.1 -+Line 10.2 -+Line 10.3 -+Line 10.4 -+Line 10.5 - Line 11 - Line 12 - Line 13 -@@ -47,7 +49,6 @@ Line 46 - Line 47 - Line 48 - Line 49 --Line 50 - Line 51 - Line 52 - Line 53` - lineNumber, err := FindAdjustedLineNumber(commentCutDiff, 50, strings.NewReader(diff)) - require.NoError(t, err) - assert.Equal(t, LinePlacement{Left: 50, Right: 52}, lineNumber) - }) - - t.Run("changes above in the same hunk", func(t *testing.T) { - diff := `diff --git a/file1.md b/file1.md -index 2d203fb..f35a466 100644 ---- a/file1.md -+++ b/file1.md -@@ -42,12 +42,6 @@ Line 41 - Line 42 - Line 43 - Line 44 --Line 45 --Line 46 --Line 47 --Line 48 --Line 49 --Line 50 - Line 51 - Line 52 - Line 53` - lineNumber, err := FindAdjustedLineNumber(commentCutDiff, 50, strings.NewReader(diff)) - require.NoError(t, err) - assert.Equal(t, LinePlacement{Left: 50, Right: 45}, lineNumber) - }) - - t.Run("first line in diff", func(t *testing.T) { - commentCutDiff := `diff --git a/file1.md b/file1.md ---- a/file1.md -+++ b/file1.md -@@ -1,4 +1,3 @@ --Line 1` - diff := `diff --git a/file1.md b/file1.md -index 2d203fb..a490028 100644 ---- a/file1.md -+++ b/file1.md -@@ -1,4 +1,3 @@ --Line 1 - Line 2 - Line 3 - Line 4` - lineNumber, err := FindAdjustedLineNumber(commentCutDiff, 1, strings.NewReader(diff)) - require.NoError(t, err) - assert.Equal(t, LinePlacement{Left: 1, Right: 1}, lineNumber) - }) - - t.Run("adjusted line not found", func(t *testing.T) { - // "Line 50" is present here but it's no longer "-Line 50", so it should not be identified as present - diff := `diff --git a/file1.md b/file1.md -index 2d203fb..09dd95a 100644 ---- a/file1.md -+++ b/file1.md -@@ -42,10 +42,6 @@ Line 41 - Line 42 - Line 43 - Line 44 --Line 45 --Line 46 --Line 47 --Line 48 - Line 49 - Line 50 - Line 51` - _, err := FindAdjustedLineNumber(commentCutDiff, 50, strings.NewReader(diff)) - require.ErrorIs(t, err, ErrLineNotFound) - }) - - t.Run("adjusted line hunk not present - not changed anymore", func(t *testing.T) { - diff := `diff --git a/file1.md b/file1.md -index 2d203fb..d0cb63f 100644 ---- a/file1.md -+++ b/file1.md -@@ -5,9 +5,11 @@ Line 4 - Line 5 - Line 6 - Line 7 --Line 8 --Line 9 --Line 10 -+Line 10.1 -+Line 10.2 -+Line 10.3 -+Line 10.4 -+Line 10.5 - Line 11 - Line 12 - Line 13` - _, err := FindAdjustedLineNumber(commentCutDiff, 50, strings.NewReader(diff)) - require.ErrorIs(t, err, ErrLineNotFound) - }) -} diff --git a/modules/git/fetch_test.go b/modules/git/fetch_test.go index b7ead10d4a..95a1fa387d 100644 --- a/modules/git/fetch_test.go +++ b/modules/git/fetch_test.go @@ -26,7 +26,7 @@ func TestFetch(t *testing.T) { fetchedCommitID, err := repo.Fetch(otherRepoPath, "refs/heads/master") require.NoError(t, err) - assert.Equal(t, "5684d0c8cfdfb17fcd59101826efc9ff54b80df4", fetchedCommitID) + assert.Equal(t, "95d3505f2db273e40be79f84416051ae85e9ea0d", fetchedCommitID) c, err := repo.getCommit(MustIDFromString(fetchedCommitID)) require.NoError(t, err) diff --git a/modules/git/foreachref/format.go b/modules/git/foreachref/format.go index 87c1c9a4ff..97e8ee4724 100644 --- a/modules/git/foreachref/format.go +++ b/modules/git/foreachref/format.go @@ -53,7 +53,7 @@ func (f Format) Flag() string { var formatFlag strings.Builder for i, field := range f.fieldNames { // field key and field value - fmt.Fprintf(&formatFlag, "%s %%(%s)", field, field) + formatFlag.WriteString(fmt.Sprintf("%s %%(%s)", field, field)) if i < len(f.fieldNames)-1 { // note: escape delimiters to allow control characters as @@ -72,12 +72,12 @@ func (f Format) Parser(r io.Reader) *Parser { return NewParser(r, f) } -// hexEscaped produces hex-escaped characters from a string. For example, "\n\0" +// hexEscaped produces hex-escpaed characters from a string. For example, "\n\0" // would turn into "%0a%00". func (f Format) hexEscaped(delim []byte) string { - var escaped strings.Builder - for i := range delim { - escaped.WriteString("%" + hex.EncodeToString([]byte{delim[i]})) + escaped := "" + for i := 0; i < len(delim); i++ { + escaped += "%" + hex.EncodeToString([]byte{delim[i]}) } - return escaped.String() + return escaped } diff --git a/modules/git/git.go b/modules/git/git.go index ea3e2a1320..650fd3b5af 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -10,7 +10,6 @@ import ( "fmt" "os" "os/exec" - "path" "path/filepath" "regexp" "runtime" @@ -201,11 +200,6 @@ func InitFull(ctx context.Context) (err error) { _, err = exec.LookPath("ssh") HasSSHExecutable = err == nil - err = InitDelegateHooks(HomeDir()) - if err != nil { - return err - } - return syncGitConfig() } @@ -235,10 +229,6 @@ func syncGitConfig() (err error) { } } - if err := configSet("core.hooksPath", path.Join(HomeDir(), "hooks")); err != nil { - return err - } - // Set git some configurations - these must be set to these values for forgejo to work correctly if err := configSet("core.quotePath", "false"); err != nil { return err diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go index 5666a425f4..83ddb766af 100644 --- a/modules/git/grep_test.go +++ b/modules/git/grep_test.go @@ -65,7 +65,7 @@ func TestGrepSearch(t *testing.T) { return } - res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{RefName: "95d3505f2db273e40be79f84416051ae85e9ea0d", MatchesPerFile: 1}) + res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{MatchesPerFile: 1}) require.NoError(t, err) assert.Equal(t, []*GrepResult{ { diff --git a/modules/git/hook.go b/modules/git/hook.go index 3b650fe9db..bef4d024c8 100644 --- a/modules/git/hook.go +++ b/modules/git/hook.go @@ -9,7 +9,6 @@ import ( "os" "path" "path/filepath" - "slices" "strings" "forgejo.org/modules/log" @@ -28,7 +27,12 @@ var ErrNotValidHook = errors.New("not a valid Git hook") // IsValidHookName returns true if given name is a valid Git hook. func IsValidHookName(name string) bool { - return slices.Contains(hookNames, name) + for _, hn := range hookNames { + if hn == name { + return true + } + } + return false } // Hook represents a Git hook. diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go index 9b49a18aaa..1d7e74a0d7 100644 --- a/modules/git/last_commit_cache.go +++ b/modules/git/last_commit_cache.go @@ -21,7 +21,7 @@ type Cache interface { } func getCacheKey(repoPath, commitID, entryPath string) string { - hashBytes := sha256.Sum256(fmt.Appendf(nil, "%s:%s:%s", repoPath, commitID, entryPath)) + hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%s", repoPath, commitID, entryPath))) return fmt.Sprintf("last_commit:%x", hashBytes) } diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index 800e83c4a4..50786e7a42 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -346,7 +346,10 @@ func WalkGitLog(ctx context.Context, repo *Repository, head *Commit, treepath st results := make([]string, len(paths)) remaining := len(paths) - nextRestart := min((len(paths)*3)/4, 70) + nextRestart := (len(paths) * 3) / 4 + if nextRestart > 70 { + nextRestart = 70 + } lastEmptyParent := head.ID.String() commitSinceLastEmptyParent := uint64(0) commitSinceNextRestart := uint64(0) diff --git a/modules/git/notes.go b/modules/git/notes.go index 1bc68b6366..a52314bdd7 100644 --- a/modules/git/notes.go +++ b/modules/git/notes.go @@ -8,7 +8,6 @@ import ( "context" "io" "os" - "strings" "forgejo.org/modules/log" ) @@ -34,7 +33,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string) (*Note, err return nil, err } - var path strings.Builder + path := "" tree := ¬es.Tree log.Trace("Found tree with ID %q while searching for git note corresponding to the commit %q", tree.ID, commitID) @@ -44,12 +43,12 @@ func GetNote(ctx context.Context, repo *Repository, commitID string) (*Note, err for len(commitID) > 2 { entry, err = tree.GetTreeEntryByPath(commitID) if err == nil { - path.WriteString(commitID) + path += commitID break } if IsErrNotExist(err) { tree, err = tree.SubTree(commitID[0:2]) - path.WriteString(commitID[0:2] + "/") + path += commitID[0:2] + "/" commitID = commitID[2:] } if err != nil { @@ -81,9 +80,9 @@ func GetNote(ctx context.Context, repo *Repository, commitID string) (*Note, err _ = dataRc.Close() closed = true - lastCommit, err := repo.getCommitByPathWithID(notes.ID, path.String()) + lastCommit, err := repo.getCommitByPathWithID(notes.ID, path) if err != nil { - log.Error("Unable to get the commit for the path %q. Error: %v", path.String(), err) + log.Error("Unable to get the commit for the path %q. Error: %v", path, err) return nil, err } diff --git a/modules/git/parse.go b/modules/git/parse.go index d2d70d4cfa..c7b84d7198 100644 --- a/modules/git/parse.go +++ b/modules/git/parse.go @@ -33,16 +33,16 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) { posEnd += pos } line := data[pos:posEnd] - before, after, ok := bytes.Cut(line, []byte{'\t'}) - if !ok { + posTab := bytes.IndexByte(line, '\t') + if posTab == -1 { return nil, fmt.Errorf("invalid ls-tree output (no tab): %q", line) } entry := new(TreeEntry) entry.ptree = ptree - entryAttrs := before - entryName := after + entryAttrs := line[:posTab] + entryName := line[posTab+1:] entryMode, entryAttrs, _ := bytes.Cut(entryAttrs, sepSpace) _ /* entryType */, entryAttrs, _ = bytes.Cut(entryAttrs, sepSpace) // the type is not used, the mode is enough to determine the type diff --git a/modules/git/pushoptions/pushoptions.go b/modules/git/pushoptions/pushoptions.go index 14e2c5d283..e96ba0a339 100644 --- a/modules/git/pushoptions/pushoptions.go +++ b/modules/git/pushoptions/pushoptions.go @@ -52,7 +52,7 @@ func NewFromMap(o *map[string]string) Interface { func (o *gitPushOptions) ReadEnv() Interface { if pushCount, err := strconv.Atoi(os.Getenv(EnvCount)); err == nil { - for idx := range pushCount { + for idx := 0; idx < pushCount; idx++ { _ = o.Parse(os.Getenv(fmt.Sprintf(EnvFormat, idx))) } } @@ -65,8 +65,12 @@ func (o *gitPushOptions) Parse(data string) bool { value = "true" } switch Key(key) { - case RepoPrivate, RepoTemplate, AgitTopic, AgitForcePush, AgitTitle, AgitDescription: - break + case RepoPrivate: + case RepoTemplate: + case AgitTopic: + case AgitForcePush: + case AgitTitle: + case AgitDescription: default: return false } diff --git a/modules/git/ref.go b/modules/git/ref.go index fdccd2b2e2..1475d4dc5a 100644 --- a/modules/git/ref.go +++ b/modules/git/ref.go @@ -105,8 +105,8 @@ func (ref RefName) IsFor() bool { } func (ref RefName) nameWithoutPrefix(prefix string) string { - if after, ok := strings.CutPrefix(string(ref), prefix); ok { - return after + if strings.HasPrefix(string(ref), prefix) { + return strings.TrimPrefix(string(ref), prefix) } return "" } diff --git a/modules/git/repo.go b/modules/git/repo.go index 8f9b95f1f2..21845d9b55 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -18,6 +18,7 @@ import ( "strings" "time" + "forgejo.org/modules/log" "forgejo.org/modules/proxy" "forgejo.org/modules/setting" "forgejo.org/modules/util" @@ -45,9 +46,9 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, erro return commits, nil } - parts := bytes.SplitSeq(logs, []byte{'\n'}) + parts := bytes.Split(logs, []byte{'\n'}) - for commitID := range parts { + for _, commitID := range parts { commit, err := repo.GetCommit(string(commitID)) if err != nil { return nil, err @@ -140,13 +141,44 @@ func CloneWithArgs(ctx context.Context, args TrustedCmdArgs, from, to string, op envs = proxy.EnvWithProxy(parsedFromURL) } - sanitizedFrom := util.SanitizeCredentialURLs(from) + fromURL := from + sanitizedFrom := from - fromURL, cleanup, err := cmd.AddAuthCredentialHelperForRemote(from) - if err != nil { - return err + // If the clone URL has credentials, build a credential file for usage by git-credential-store + // to prevent credential leak in the process list. + // https://git-scm.com/docs/git-credential-store#_storage_format + // credential.helper adjustment must be set before the git subcommand + if strings.Contains(from, "://") && strings.Contains(from, "@") { + sanitizedFrom = util.SanitizeCredentialURLs(from) + if parsedFromURL != nil { + credentialsFile, err := os.CreateTemp("", "forgejo-clone-credentials-") + if err != nil { + return err + } + credentialsPath := credentialsFile.Name() + + defer func() { + _ = credentialsFile.Close() + if err := util.Remove(credentialsPath); err != nil { + log.Warn("Unable to remove temporary file %q: %v", credentialsPath, err) + } + }() + _, err = credentialsFile.Write([]byte(parsedFromURL.String())) + if err != nil { + return err + } + err = credentialsFile.Close() + if err != nil { + return err + } + + cmd.AddArguments("-c").AddDynamicArguments("credential.helper=store --file=" + credentialsPath) + + // remove the password from the URL argument + parsedFromURL.User = url.User(parsedFromURL.User.Username()) + fromURL = parsedFromURL.String() + } } - defer cleanup() cmd.AddArguments("clone") diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go index 56a86bde14..2b07513162 100644 --- a/modules/git/repo_attribute.go +++ b/modules/git/repo_attribute.go @@ -96,8 +96,8 @@ func (ca GitAttribute) String() string { // sometimes used within gitlab-language: https://docs.gitlab.com/ee/user/project/highlighting.html#override-syntax-highlighting-for-a-file-type func (ca GitAttribute) Prefix() string { s := ca.String() - if before, _, ok := strings.Cut(s, "?"); ok { - return before + if i := strings.IndexByte(s, '?'); i >= 0 { + return s[:i] } return s } diff --git a/modules/git/repo_blame.go b/modules/git/repo_blame.go index d760898ac6..50f4c572d5 100644 --- a/modules/git/repo_blame.go +++ b/modules/git/repo_blame.go @@ -65,97 +65,3 @@ func (repo *Repository) LineBlame(revision, file string, line uint64) (*Commit, return commit, originalLineNo, nil } - -type ReverseLineBlame struct { - CommitID string - LineNumber uint64 - FilePath string -} - -// Reverses the effect of [LineBlame]. If a file was modified at originalLine number in originalRevision, -// ReverseLineBlame will identify the last commit up-to-and-including currentRevision where that line exists, including -// its new path and line number. If the returned commit is not the same as currentRevision, then it indicates that -// content can no longer be located in currentRevision, and the returned commit is the last commit that had it. -func (repo *Repository) ReverseLineBlame(originalRevision, file string, originalLine uint64, currentRevision string) (*ReverseLineBlame, error) { - if originalRevision == currentRevision { - // Would cause an error to run the reverse, "fatal: More than one commit to dig up from, (N) and (N)" - return &ReverseLineBlame{ - CommitID: originalRevision, - LineNumber: originalLine, - FilePath: file, - }, nil - } - - res, _, gitErr := NewCommand(repo.Ctx, "blame"). - AddOptionValues("--reverse"). - AddDynamicArguments(fmt.Sprintf("%s..%s", originalRevision, currentRevision)). - AddOptionFormat("-L %d,%d", originalLine, originalLine). - AddOptionValues("-p"). - AddDashesAndList(file).RunStdString(&RunOpts{Dir: repo.Path}) - if gitErr != nil { - return nil, gitErr - } - - // Example output: - // - // 74be0e8aa338d1374ab7ca0a25a4f594955a69c2 16 9 1 - // author FirstName LastName - // author-mail - // author-time 1775492007 - // author-tz -0600 - // committer FirstName LastName - // committer-mail - // committer-time 1775492007 - // committer-tz -0600 - // summary restore file-in-base to orig, now not present in diff - // filename README.md - // - // Header (https://git-scm.com/docs/git-blame#_the_porcelain_format): - // - 40-byte SHA-1 of the commit the line is attributed to; - // - the line number of the line in the original file; [note: opposite in reverse] - // - the line number of the line in the final file; [note: opposite in reverse] - // - on a line that starts a group of lines from a different commit than the previous one, the number of lines in - // this group. On subsequent lines this field is absent. - - lines := strings.Split(res, "\n") - - header := lines[0] - headerValues := strings.Split(header, " ") - if len(headerValues) < 2 { - return nil, fmt.Errorf("failed to parse blame --reverse header: %q", header) - } - - objectFormat, err := repo.GetObjectFormat() - if err != nil { - return nil, err - } - objectIDLen := objectFormat.FullLength() - objectID := headerValues[0] - if len(objectID) != objectIDLen { - return nil, fmt.Errorf("output of blame is invalid, cannot contain commit ID: %s", objectID) - } - commit, err := repo.GetCommit(objectID) - if err != nil { - return nil, fmt.Errorf("GetCommit: %w", err) - } - - currentLineStr := headerValues[1] - currentLineNo, err := strconv.ParseUint(currentLineStr, 10, 64) - if err != nil { - return nil, fmt.Errorf("strconv.ParseUint: %w", err) - } - - var filename string - for _, otherLine := range lines { - if strings.HasPrefix(otherLine, "filename ") { - filename = otherLine[len("filename "):] - break - } - } - - return &ReverseLineBlame{ - CommitID: commit.ID.String(), - LineNumber: currentLineNo, - FilePath: filename, - }, nil -} diff --git a/modules/git/repo_blame_test.go b/modules/git/repo_blame_test.go index 5803d082f0..4ddd5d9c3f 100644 --- a/modules/git/repo_blame_test.go +++ b/modules/git/repo_blame_test.go @@ -89,23 +89,11 @@ func TestLineBlame(t *testing.T) { assert.Equal(t, firstCommit, commit.ID.String()) assert.EqualValues(t, 1, lineno) - rev, err := gitRepo.ReverseLineBlame(commit.ID.String(), "ANSWER", lineno, secondCommit) - require.NoError(t, err) - assert.Equal(t, secondCommit, rev.CommitID) - assert.Equal(t, "ANSWER", rev.FilePath) - assert.EqualValues(t, 10, rev.LineNumber) - for i := range uint64(9) { commit, lineno, err = gitRepo.LineBlame("HEAD", "ANSWER", i+1) require.NoError(t, err) assert.Equal(t, secondCommit, commit.ID.String()) assert.Equal(t, i+1, lineno) - - rev, err := gitRepo.ReverseLineBlame(commit.ID.String(), "ANSWER", lineno, secondCommit) - require.NoError(t, err) - assert.Equal(t, secondCommit, rev.CommitID) - assert.Equal(t, "ANSWER", rev.FilePath) - assert.Equal(t, i+1, rev.LineNumber) } } @@ -120,66 +108,3 @@ func TestLineBlame(t *testing.T) { }) }) } - -func TestReverseLineBlame(t *testing.T) { - t.Run("single commit", func(t *testing.T) { - tmpDir := t.TempDir() - require.NoError(t, InitRepository(t.Context(), tmpDir, false, Sha1ObjectFormat.Name())) - - gitRepo, err := OpenRepository(t.Context(), tmpDir) - require.NoError(t, err) - defer gitRepo.Close() - - require.NoError(t, os.WriteFile(path.Join(tmpDir, "file1.md"), []byte("abba\n"), 0o666)) - require.NoError(t, AddChanges(tmpDir, true)) - require.NoError(t, CommitChanges(tmpDir, CommitChangesOptions{Message: "abba spelt backwards"})) - - commit, err := gitRepo.GetRefCommitID("HEAD") - require.NoError(t, err) - - blameCommit, lineno, err := gitRepo.LineBlame("HEAD", "file1.md", 1) - require.NoError(t, err) - assert.Equal(t, commit, blameCommit.ID.String()) - assert.EqualValues(t, 1, lineno) - - rev, err := gitRepo.ReverseLineBlame(commit, "file1.md", lineno, commit) - require.NoError(t, err) - assert.Equal(t, commit, rev.CommitID) - assert.Equal(t, "file1.md", rev.FilePath) - assert.EqualValues(t, 1, rev.LineNumber) - }) - - t.Run("move file", func(t *testing.T) { - tmpDir := t.TempDir() - require.NoError(t, InitRepository(t.Context(), tmpDir, false, Sha1ObjectFormat.Name())) - - gitRepo, err := OpenRepository(t.Context(), tmpDir) - require.NoError(t, err) - defer gitRepo.Close() - - require.NoError(t, os.WriteFile(path.Join(tmpDir, "file1.md"), []byte("abba\n"), 0o666)) - require.NoError(t, AddChanges(tmpDir, true)) - require.NoError(t, CommitChanges(tmpDir, CommitChangesOptions{Message: "abba spelt backwards"})) - - firstCommit, err := gitRepo.GetRefCommitID("HEAD") - require.NoError(t, err) - - require.NoError(t, os.Rename(path.Join(tmpDir, "file1.md"), path.Join(tmpDir, "file2.md"))) - require.NoError(t, AddChanges(tmpDir, true)) - require.NoError(t, CommitChanges(tmpDir, CommitChangesOptions{Message: "move file"})) - - secondCommit, err := gitRepo.GetRefCommitID("HEAD") - require.NoError(t, err) - - blameCommit, lineno, err := gitRepo.LineBlame("HEAD", "file2.md", 1) - require.NoError(t, err) - assert.Equal(t, firstCommit, blameCommit.ID.String()) - assert.EqualValues(t, 1, lineno) - - rev, err := gitRepo.ReverseLineBlame(firstCommit, "file1.md", lineno, secondCommit) - require.NoError(t, err) - assert.Equal(t, secondCommit, rev.CommitID) - assert.Equal(t, "file2.md", rev.FilePath) - assert.EqualValues(t, 1, rev.LineNumber) - }) -} diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 99ffac7afb..bd7d5e2014 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -217,15 +217,10 @@ type CommitsByFileAndRangeOptions struct { File string Not string Page int - PageSize int } // CommitsByFileAndRange return the commits according revision file and the page func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) ([]*Commit, error) { - if opts.PageSize <= 0 { - opts.PageSize = setting.Git.CommitsRangeSize - } - skip := (opts.Page - 1) * setting.Git.CommitsRangeSize stdoutReader, stdoutWriter := io.Pipe() @@ -236,7 +231,7 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) go func() { stderr := strings.Builder{} gitCmd := NewCommand(repo.Ctx, "rev-list"). - AddOptionFormat("--max-count=%d", opts.PageSize). + AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize). AddOptionFormat("--skip=%d", skip) gitCmd.AddDynamicArguments(opts.Revision) @@ -478,7 +473,7 @@ func (repo *Repository) GetCommitsFromIDs(commitIDs []string, ignoreExistence bo // 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 assume the commitID is not shortened, we cannot recover the full + // to assumme the commitID is not shortened, we cannot recover the full // commitID. id, err := NewIDFromString(commitID) if err == nil { diff --git a/modules/git/repo_commit_test.go b/modules/git/repo_commit_test.go index 401848a975..99cde92300 100644 --- a/modules/git/repo_commit_test.go +++ b/modules/git/repo_commit_test.go @@ -200,44 +200,6 @@ func TestCommitsByFileAndRange(t *testing.T) { } } -func TestCommitsByFileAndRangeWithPageSize(t *testing.T) { - bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") - bareRepo1, err := openRepositoryWithDefaultContext(bareRepo1Path) - require.NoError(t, err) - defer bareRepo1.Close() - defer test.MockVariableValue(&setting.Git.CommitsRangeSize, 2)() - - testCases := []struct { - File string - Page int - PageSize int - ExpectedCommitCount int - }{ - {"file1.txt", 1, 1, 1}, - {"file2.txt", 1, 1, 1}, - {"file*.txt", 1, 2, 2}, - {"file*.txt", 1, 1, 1}, - {"foo", 1, 2, 2}, - {"foo", 1, 1, 1}, - {"foo", 2, 1, 1}, - {"foo", 3, 0, 0}, - {"foo", 3, 2, 0}, - {"f*", 1, 2, 2}, - {"f*", 2, 2, 2}, - {"f*", 3, 1, 1}, - } - for _, testCase := range testCases { - commits, err := bareRepo1.CommitsByFileAndRange(CommitsByFileAndRangeOptions{ - Revision: "master", - File: testCase.File, - Page: testCase.Page, - PageSize: testCase.PageSize, - }) - require.NoError(t, err) - 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) diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go index 7fc5c573dd..f58757a9a2 100644 --- a/modules/git/repo_index.go +++ b/modules/git/repo_index.go @@ -95,7 +95,7 @@ func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { return nil, err } filelist := make([]string, 0, len(filenames)) - for line := range bytes.SplitSeq(res, []byte{'\000'}) { + for _, line := range bytes.Split(res, []byte{'\000'}) { filelist = append(filelist, string(line)) } diff --git a/modules/git/repo_language_stats.go b/modules/git/repo_language_stats.go index 2832c4f572..ee4beb2f87 100644 --- a/modules/git/repo_language_stats.go +++ b/modules/git/repo_language_stats.go @@ -168,21 +168,11 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err } } - // Don't skip this file if it is explicitly set to be detectable. - // Skip this file if one of the following conditions holds: - // 1. Explicitly set to not be detectable. - // 2. Explicitly set that it is vendored. - // 3. Explicitly set that it is documentation. - // 4. Is not explicitly set to not be vendored and is by heuristic considered to be vendored. - // 5. It is considered to be a dot file. - // 6. It is considered to be a configuration file. - // 7. Is not explicitly set to not be documentation and is by heuristic considered to be documentation. - if !isTrue(isDetectable) && - (isFalse(isDetectable) || isTrue(isVendored) || isTrue(isDocumentation) || - (!isFalse(isVendored) && analyze.IsVendor(f.Name())) || - enry.IsDotFile(f.Name()) || - enry.IsConfiguration(f.Name()) || - (!isFalse(isDocumentation) && enry.IsDocumentation(f.Name()))) { + if isFalse(isDetectable) || isTrue(isVendored) || isTrue(isDocumentation) || + (!isFalse(isVendored) && analyze.IsVendor(f.Name())) || + enry.IsDotFile(f.Name()) || + enry.IsConfiguration(f.Name()) || + (!isFalse(isDocumentation) && enry.IsDocumentation(f.Name())) { continue } diff --git a/modules/git/repo_language_stats_test.go b/modules/git/repo_language_stats_test.go index 2bc57b56c5..e3d8ba1f69 100644 --- a/modules/git/repo_language_stats_test.go +++ b/modules/git/repo_language_stats_test.go @@ -34,16 +34,6 @@ func TestRepository_GetLanguageStats(t *testing.T) { "Python": 67, "Java": 112, }, stats) - - stats, err = gitRepo.GetLanguageStats("5684d0c8cfdfb17fcd59101826efc9ff54b80df4") - require.NoError(t, err) - - assert.Equal(t, map[string]int64{ - "Cobra": 67, - "Python": 67, - "Markdown": 15, - "Java": 112, - }, stats) } func TestMergeLanguageStats(t *testing.T) { diff --git a/modules/git/repo_stats.go b/modules/git/repo_stats.go index 881acdafcf..ef0865e3d3 100644 --- a/modules/git/repo_stats.go +++ b/modules/git/repo_stats.go @@ -98,7 +98,6 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) } switch p { case 1: // Separator - break case 2: // Commit sha-1 stats.CommitCount++ case 3: // Author diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index bd851a3be3..f7f04e1f10 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -42,8 +42,8 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) { return "", err } - tagRefs := strings.SplitSeq(stdout, "\n") - for tagRef := range tagRefs { + tagRefs := strings.Split(stdout, "\n") + for _, tagRef := range tagRefs { if len(strings.TrimSpace(tagRef)) > 0 { fields := strings.Fields(tagRef) if strings.HasPrefix(fields[0], sha) && strings.HasPrefix(fields[1], TagPrefix) { @@ -65,7 +65,7 @@ func (repo *Repository) GetTagID(name string) (string, error) { return "", err } // Make sure exact match is used: "v1" != "release/v1" - for line := range strings.SplitSeq(stdout, "\n") { + for _, line := range strings.Split(stdout, "\n") { fields := strings.Fields(line) if len(fields) == 2 && fields[1] == "refs/tags/"+name { return fields[0], nil diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go index d825dbd973..d88851551f 100644 --- a/modules/git/repo_tag_test.go +++ b/modules/git/repo_tag_test.go @@ -4,7 +4,6 @@ package git import ( - "fmt" "path/filepath" "testing" "time" @@ -151,8 +150,7 @@ func TestRepository_GetAnnotatedTag(t *testing.T) { // Annotated tag's name should fail tag3, err := bareRepo1.GetAnnotatedTag(aTagName) require.Error(t, err) - require.ErrorContains(t, err, - fmt.Sprintf("length %d has no matched object format: %s", len(aTagName), aTagName)) + require.Errorf(t, err, "Length must be 40: %d", len(aTagName)) assert.Nil(t, tag3) // Lightweight Tag should fail diff --git a/modules/git/tag.go b/modules/git/tag.go index 3af062a51a..edbe54b853 100644 --- a/modules/git/tag.go +++ b/modules/git/tag.go @@ -50,20 +50,20 @@ l: switch { case eol > 0: line := data[nextline : nextline+eol] - before, after, _ := bytes.Cut(line, []byte{' '}) - reftype := before + spacepos := bytes.IndexByte(line, ' ') + reftype := line[:spacepos] switch string(reftype) { case "object": - id, err := NewIDFromString(string(after)) + id, err := NewIDFromString(string(line[spacepos+1:])) if err != nil { return nil, err } tag.Object = id case "type": // A commit can have one or more parents - tag.Type = string(after) + tag.Type = string(line[spacepos+1:]) case "tagger": - tag.Tagger = parseSignatureFromCommitLine(util.UnsafeBytesToString(after)) + tag.Tagger = parseSignatureFromCommitLine(util.UnsafeBytesToString(line[spacepos+1:])) } nextline += eol + 1 case eol == 0: diff --git a/modules/git/tests/repos/language_stats_repo/objects/pack/pack-371b1f6c24df14da4898b22c00ff8fb55303ac76.idx b/modules/git/tests/repos/language_stats_repo/objects/pack/pack-371b1f6c24df14da4898b22c00ff8fb55303ac76.idx new file mode 100644 index 0000000000..186136cb12 Binary files /dev/null and b/modules/git/tests/repos/language_stats_repo/objects/pack/pack-371b1f6c24df14da4898b22c00ff8fb55303ac76.idx differ diff --git a/modules/git/tests/repos/language_stats_repo/objects/pack/pack-371b1f6c24df14da4898b22c00ff8fb55303ac76.pack b/modules/git/tests/repos/language_stats_repo/objects/pack/pack-371b1f6c24df14da4898b22c00ff8fb55303ac76.pack new file mode 100644 index 0000000000..046061c688 Binary files /dev/null and b/modules/git/tests/repos/language_stats_repo/objects/pack/pack-371b1f6c24df14da4898b22c00ff8fb55303ac76.pack differ diff --git a/modules/git/tests/repos/language_stats_repo/objects/pack/pack-371b1f6c24df14da4898b22c00ff8fb55303ac76.rev b/modules/git/tests/repos/language_stats_repo/objects/pack/pack-371b1f6c24df14da4898b22c00ff8fb55303ac76.rev new file mode 100644 index 0000000000..7d8c6f3562 Binary files /dev/null and b/modules/git/tests/repos/language_stats_repo/objects/pack/pack-371b1f6c24df14da4898b22c00ff8fb55303ac76.rev differ diff --git a/modules/git/tests/repos/language_stats_repo/objects/pack/pack-7cd0100ad5c382a7e8e1bacada4b66b927f29b72.idx b/modules/git/tests/repos/language_stats_repo/objects/pack/pack-7cd0100ad5c382a7e8e1bacada4b66b927f29b72.idx deleted file mode 100644 index 80e1ee36b5..0000000000 Binary files a/modules/git/tests/repos/language_stats_repo/objects/pack/pack-7cd0100ad5c382a7e8e1bacada4b66b927f29b72.idx and /dev/null differ diff --git a/modules/git/tests/repos/language_stats_repo/objects/pack/pack-7cd0100ad5c382a7e8e1bacada4b66b927f29b72.pack b/modules/git/tests/repos/language_stats_repo/objects/pack/pack-7cd0100ad5c382a7e8e1bacada4b66b927f29b72.pack deleted file mode 100644 index 94b6852051..0000000000 Binary files a/modules/git/tests/repos/language_stats_repo/objects/pack/pack-7cd0100ad5c382a7e8e1bacada4b66b927f29b72.pack and /dev/null differ diff --git a/modules/git/tests/repos/language_stats_repo/objects/pack/pack-7cd0100ad5c382a7e8e1bacada4b66b927f29b72.rev b/modules/git/tests/repos/language_stats_repo/objects/pack/pack-7cd0100ad5c382a7e8e1bacada4b66b927f29b72.rev deleted file mode 100644 index c2e2e3aeae..0000000000 Binary files a/modules/git/tests/repos/language_stats_repo/objects/pack/pack-7cd0100ad5c382a7e8e1bacada4b66b927f29b72.rev and /dev/null differ diff --git a/modules/git/tests/repos/language_stats_repo/packed-refs b/modules/git/tests/repos/language_stats_repo/packed-refs index b046027fce..63e01583a4 100644 --- a/modules/git/tests/repos/language_stats_repo/packed-refs +++ b/modules/git/tests/repos/language_stats_repo/packed-refs @@ -1,2 +1,2 @@ # pack-refs with: peeled fully-peeled sorted -5684d0c8cfdfb17fcd59101826efc9ff54b80df4 refs/heads/master +95d3505f2db273e40be79f84416051ae85e9ea0d refs/heads/master diff --git a/modules/git/tree.go b/modules/git/tree.go index 9a91787c9e..f6201f6cc9 100644 --- a/modules/git/tree.go +++ b/modules/git/tree.go @@ -170,7 +170,7 @@ func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error return nil, err } filelist := make([]string, 0, len(filenames)) - for line := range bytes.SplitSeq(res, []byte{'\000'}) { + for _, line := range bytes.Split(res, []byte{'\000'}) { filelist = append(filelist, string(line)) } diff --git a/modules/git/tree_entry.go b/modules/git/tree_entry.go index 0b3564178b..8b6c4c467c 100644 --- a/modules/git/tree_entry.go +++ b/modules/git/tree_entry.go @@ -5,7 +5,6 @@ package git import ( - "fmt" "io" "sort" "strings" @@ -137,11 +136,11 @@ func (te *TreeEntry) LinkTarget() (string, error) { } // FollowLink returns the entry pointed to by a symlink -func (te *TreeEntry) FollowLink() (*TreeEntry, error) { +func (te *TreeEntry) FollowLink() (*TreeEntry, string, error) { // read the link lnk, err := te.LinkTarget() if err != nil { - return nil, err + return nil, "", err } t := te.ptree @@ -152,33 +151,35 @@ func (te *TreeEntry) FollowLink() (*TreeEntry, error) { } if t == nil { - return nil, ErrBadLink{te.Name(), "points outside of repo"} + return nil, "", ErrBadLink{te.Name(), "points outside of repo"} } target, err := t.GetTreeEntryByPath(lnk) if err != nil { if IsErrNotExist(err) { - return nil, ErrBadLink{te.Name(), "broken link"} + return nil, "", ErrBadLink{te.Name(), "broken link"} } - return nil, err + return nil, "", err } - return target, nil + return target, lnk, nil } // FollowLinks returns the entry ultimately pointed to by a symlink -func (te *TreeEntry) FollowLinks() (*TreeEntry, error) { +func (te *TreeEntry) FollowLinks() (*TreeEntry, string, error) { if !te.IsLink() { - return nil, ErrBadLink{te.Name(), "not a symlink"} + return nil, "", ErrBadLink{te.Name(), "not a symlink"} } entry := te - for range 999 { + entryLink := "" + for i := 0; i < 999; i++ { if entry.IsLink() { - next, err := entry.FollowLink() + next, link, err := entry.FollowLink() + entryLink = link if err != nil { - return nil, err + return nil, "", err } if next.ID == entry.ID { - return nil, ErrBadLink{ + return nil, "", ErrBadLink{ entry.Name(), "recursive link", } @@ -189,12 +190,12 @@ func (te *TreeEntry) FollowLinks() (*TreeEntry, error) { } } if entry.IsLink() { - return nil, ErrBadLink{ + return nil, "", ErrBadLink{ te.Name(), "too many levels of symbolic links", } } - return entry, nil + return entry, entryLink, nil } // returns the Tree pointed to by this TreeEntry, or nil if this is not a tree @@ -207,42 +208,6 @@ func (te *TreeEntry) Tree() *Tree { return t } -// returns the calulcated path within the tree of this TreeEntry, or an error if it can be determined -func (te *TreeEntry) Path() (string, error) { - targetPath := te.Name() - parentTree := te.ptree - if parentTree == nil { - return "", fmt.Errorf("couldn't find the parent tree of the entry") - } - - prevID := parentTree.ID - parentTree = parentTree.ptree - for parentTree != nil { - entries, err := parentTree.ListEntries() - if err != nil { - return "", fmt.Errorf("couldn't list entries: %v", err) - } - - var matchingEntry *TreeEntry - for _, entry := range entries { - if entry.ID == prevID { - matchingEntry = entry - break - } - } - - if matchingEntry == nil { - return "", fmt.Errorf("this shouldn't happen: couldn't find entry (ID: %s) in tree (ID: %s)", prevID, parentTree.ID) - } - - targetPath = matchingEntry.name + "/" + targetPath - prevID = parentTree.ID - parentTree = parentTree.ptree - } - - return targetPath, nil -} - // GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory ) func (te *TreeEntry) GetSubJumpablePathName() string { if te.IsSubmodule() || !te.IsDir() { diff --git a/modules/git/tree_entry_test.go b/modules/git/tree_entry_test.go deleted file mode 100644 index 325d3686f0..0000000000 --- a/modules/git/tree_entry_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015 The Gogs Authors. All rights reserved. -// Copyright 2019 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package git_test - -import ( - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTreeEntry_Path(t *testing.T) { - repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "templates_repo")) - require.NoError(t, err) - defer repo.Close() - - tests := []struct { - name string // description of this test case - path string - }{ - { - name: "Top level dir", - path: ".forgejo", - }, - { - name: "File in subdir", - path: ".forgejo/default_merge_message/MERGE_TEMPLATE.md", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tree, err := repo.GetTree("HEAD^{tree}") - require.NoError(t, err) - - te, err := tree.GetTreeEntryByPath(tt.path) - require.NoError(t, err) - - got, gotErr := te.Path() - require.NoError(t, gotErr, "Path() failed: %v", gotErr) - assert.Equal(t, tt.path, got, "Path() = %v, want %v", got, tt.path) - }) - } -} diff --git a/modules/git/tree_test.go b/modules/git/tree_test.go index 6277154acd..aa092cc56b 100644 --- a/modules/git/tree_test.go +++ b/modules/git/tree_test.go @@ -20,7 +20,7 @@ func TestSubTree_Issue29101(t *testing.T) { require.NoError(t, err) // old code could produce a different error if called multiple times - for range 10 { + for i := 0; i < 10; i++ { _, err = commit.SubTree("file1.txt") require.Error(t, err) assert.True(t, IsErrNotExist(err)) diff --git a/modules/graceful/manager.go b/modules/graceful/manager.go index 3b0115c51c..db5738c94c 100644 --- a/modules/graceful/manager.go +++ b/modules/graceful/manager.go @@ -164,7 +164,6 @@ func (g *Manager) doHammerTime(d time.Duration) { g.lock.Lock() select { case <-g.hammerCtx.Done(): - break default: log.Warn("Setting Hammer condition") g.hammerCtxCancel() @@ -181,7 +180,6 @@ func (g *Manager) doTerminate() { g.lock.Lock() select { case <-g.terminateCtx.Done(): - break default: log.Warn("Terminating") g.terminateCtxCancel() diff --git a/modules/graceful/manager_common.go b/modules/graceful/manager_common.go index 87f18e382f..892957e93f 100644 --- a/modules/graceful/manager_common.go +++ b/modules/graceful/manager_common.go @@ -86,7 +86,6 @@ func (g *Manager) DoGracefulShutdown() { g.lock.Lock() select { case <-g.shutdownRequested: - break default: close(g.shutdownRequested) } diff --git a/modules/graceful/server.go b/modules/graceful/server.go index 0f547a6c30..e812117dbd 100644 --- a/modules/graceful/server.go +++ b/modules/graceful/server.go @@ -85,6 +85,7 @@ func (srv *Server) ListenAndServe(serve ServeFunction, useProxyProtocol bool) er listener = &proxyprotocol.Listener{ Listener: listener, ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout, + AcceptUnknown: setting.ProxyProtocolAcceptUnknown, } } srv.listener = listener @@ -117,6 +118,7 @@ func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFun listener = &proxyprotocol.Listener{ Listener: listener, ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout, + AcceptUnknown: setting.ProxyProtocolAcceptUnknown, } } @@ -128,6 +130,7 @@ func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFun listener = &proxyprotocol.Listener{ Listener: listener, ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout, + AcceptUnknown: setting.ProxyProtocolAcceptUnknown, } } diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index 71318c0f69..ba3ba479d5 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -103,12 +103,7 @@ func Code(fileName, language, code string) (output template.HTML, lexerName stri cache.Add(fileName, lexer) } - lexerName = formatLexerName(lexer.Config().Name) - if lexerName == "Bash" { - lexerName = "Shell" - } - - return CodeFromLexer(lexer, code), lexerName + return CodeFromLexer(lexer, code), formatLexerName(lexer.Config().Name) } // CodeFromLexer returns a HTML version of code string with chroma syntax highlighting classes @@ -188,9 +183,6 @@ func File(fileName, language string, code []byte) ([]template.HTML, string, erro } lexerName := formatLexerName(lexer.Config().Name) - if lexerName == "Bash" { - lexerName = "Shell" - } iterator, err := lexer.Tokenise(nil, string(code)) if err != nil { diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index 96af9ecacb..ed5d190764 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -55,12 +55,6 @@ func TestFile(t *testing.T) { want: lines(""), lexerName: "YAML", }, - { - name: "empty.sh", - code: "", - want: lines(""), - lexerName: "Shell", - }, { name: "tags.txt", code: "<>", diff --git a/modules/hostmatcher/hostmatcher.go b/modules/hostmatcher/hostmatcher.go index 15c6371422..1069310316 100644 --- a/modules/hostmatcher/hostmatcher.go +++ b/modules/hostmatcher/hostmatcher.go @@ -6,7 +6,6 @@ package hostmatcher import ( "net" "path/filepath" - "slices" "strings" ) @@ -39,7 +38,7 @@ func isBuiltin(s string) bool { // ParseHostMatchList parses the host list HostMatchList func ParseHostMatchList(settingKeyHint, hostList string) *HostMatchList { hl := &HostMatchList{SettingKeyHint: settingKeyHint, SettingValue: hostList} - for s := range strings.SplitSeq(hostList, ",") { + for _, s := range strings.Split(hostList, ",") { s = strings.ToLower(strings.TrimSpace(s)) if s == "" { continue @@ -62,7 +61,7 @@ func ParseSimpleMatchList(settingKeyHint, matchList string) *HostMatchList { SettingKeyHint: settingKeyHint, SettingValue: matchList, } - for s := range strings.SplitSeq(matchList, ",") { + for _, s := range strings.Split(matchList, ",") { s = strings.ToLower(strings.TrimSpace(s)) if s == "" { continue @@ -99,8 +98,10 @@ func (hl *HostMatchList) checkPattern(host string) bool { } func (hl *HostMatchList) checkIP(ip net.IP) bool { - if slices.Contains(hl.patterns, "*") { - return true + for _, pattern := range hl.patterns { + if pattern == "*" { + return true + } } for _, builtin := range hl.builtins { switch builtin { diff --git a/modules/httpcache/httpcache.go b/modules/httpcache/httpcache.go index 311f7215b2..7978fc38a1 100644 --- a/modules/httpcache/httpcache.go +++ b/modules/httpcache/httpcache.go @@ -59,7 +59,7 @@ func HandleGenericETagCache(req *http.Request, w http.ResponseWriter, etag strin func checkIfNoneMatchIsValid(req *http.Request, etag string) bool { ifNoneMatch := req.Header.Get("If-None-Match") if len(ifNoneMatch) > 0 { - for item := range strings.SplitSeq(ifNoneMatch, ",") { + for _, item := range strings.Split(ifNoneMatch, ",") { item = strings.TrimPrefix(strings.TrimSpace(item), "W/") // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#directives if item == etag { return true diff --git a/modules/httplib/serve.go b/modules/httplib/serve.go index 4c71437fc5..d385ac21c9 100644 --- a/modules/httplib/serve.go +++ b/modules/httplib/serve.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "io" - "maps" "net/http" "net/url" "path" @@ -87,7 +86,9 @@ func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) { } if opts.AdditionalHeaders != nil { - maps.Copy(header, opts.AdditionalHeaders) + for k, v := range opts.AdditionalHeaders { + header[k] = v + } } } diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go index 8ec3c1181f..14a43cf3be 100644 --- a/modules/indexer/code/git.go +++ b/modules/indexer/code/git.go @@ -129,8 +129,8 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio changes.Updates = append(changes.Updates, updates...) return nil } - lines := strings.SplitSeq(stdout, "\n") - for line := range lines { + lines := strings.Split(stdout, "\n") + for _, line := range lines { line = strings.TrimSpace(line) if len(line) == 0 { continue diff --git a/modules/indexer/code/indexer.go b/modules/indexer/code/indexer.go index a414c40d0e..a3e20e1d5a 100644 --- a/modules/indexer/code/indexer.go +++ b/modules/indexer/code/indexer.go @@ -215,9 +215,7 @@ func Init() { } select { case waitChannel <- time.Since(start): - break case <-graceful.GetManager().IsShutdown(): - break } close(waitChannel) diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go index a17e7192ac..2085251f1c 100644 --- a/modules/indexer/code/search.go +++ b/modules/indexer/code/search.go @@ -7,7 +7,6 @@ import ( "bytes" "context" "html/template" - "slices" "strings" "forgejo.org/modules/highlight" @@ -47,19 +46,6 @@ const ( SearchModeFuzzy = internal.CodeSearchModeFuzzy ) -type Results []*Result - -// Get the set of repo IDs from a list of search results -func (res Results) RepoIDs() []int64 { - ids := make([]int64, len(res)) - for _, r := range res { - if !slices.Contains(ids, r.RepoID) { - ids = append(ids, r.RepoID) - } - } - return ids -} - func indices(content string, selectionStartIndex, selectionEndIndex int) (int, int) { startIndex := selectionStartIndex numLinesBefore := 0 @@ -232,7 +218,7 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res } // PerformSearch perform a search on a repository -func PerformSearch(ctx context.Context, opts *SearchOptions) (int, Results, []*SearchResultLanguages, error) { +func PerformSearch(ctx context.Context, opts *SearchOptions) (int, []*Result, []*SearchResultLanguages, error) { if opts == nil || len(opts.Keyword) == 0 { return 0, nil, nil, nil } diff --git a/modules/indexer/code/search_test.go b/modules/indexer/code/search_test.go index b8fe852bb8..2413ddec0b 100644 --- a/modules/indexer/code/search_test.go +++ b/modules/indexer/code/search_test.go @@ -87,8 +87,8 @@ func TestHighlightSearchResultCode(t *testing.T) { Code: "func main() {\n\tfmt.Println(\"mark this\")\n}", Result: []template.HTML{ "func main() {", - "\tfmt.Println("mark this")", - "}", + "\tfmt.Println("mark this")", + "}", }, }, { @@ -98,8 +98,8 @@ func TestHighlightSearchResultCode(t *testing.T) { Code: "func main() {\n\tfmt.Println(\"mark this 😊\")\n}", Result: []template.HTML{ "func main() {", - "\tfmt.Println("mark this 😊")", - "}", + "\tfmt.Println("mark this 😊")", + "}", }, }, } diff --git a/modules/indexer/internal/bleve/query.go b/modules/indexer/internal/bleve/query.go index ea56fca0f8..e043023671 100644 --- a/modules/indexer/internal/bleve/query.go +++ b/modules/indexer/internal/bleve/query.go @@ -48,15 +48,15 @@ func BoolFieldQuery(value bool, field string) *query.BoolFieldQuery { func NumericRangeInclusiveQuery(min, max optional.Option[int64], field string) *query.NumericRangeQuery { var minF, maxF *float64 var minI, maxI *bool - if has, value := min.Get(); has { + if min.Has() { minF = new(float64) - *minF = float64(value) + *minF = float64(min.Value()) minI = new(bool) *minI = true } - if has, value := max.Get(); has { + if max.Has() { maxF = new(float64) - *maxF = float64(value) + *maxF = float64(max.Value()) maxI = new(bool) *maxI = true } diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index 512788a207..a7c87c8f47 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -13,6 +13,7 @@ import ( "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/analysis/analyzer/custom" + "github.com/blevesearch/bleve/v2/analysis/token/camelcase" "github.com/blevesearch/bleve/v2/analysis/token/lowercase" "github.com/blevesearch/bleve/v2/analysis/token/unicodenorm" "github.com/blevesearch/bleve/v2/analysis/tokenizer/unicode" @@ -23,7 +24,7 @@ import ( const ( issueIndexerAnalyzer = "issueIndexer" issueIndexerDocType = "issueIndexerDocType" - issueIndexerLatestVersion = 7 + issueIndexerLatestVersion = 5 ) const unicodeNormalizeName = "unicodeNormalize" @@ -82,7 +83,7 @@ func generateIssueIndexMapping() (mapping.IndexMapping, error) { docMapping.AddFieldMappingsAt("project_id", numberFieldMapping) docMapping.AddFieldMappingsAt("project_board_id", numberFieldMapping) docMapping.AddFieldMappingsAt("poster_id", numberFieldMapping) - docMapping.AddFieldMappingsAt("assignee_ids", numberFieldMapping) + docMapping.AddFieldMappingsAt("assignee_id", numberFieldMapping) docMapping.AddFieldMappingsAt("mention_ids", numberFieldMapping) docMapping.AddFieldMappingsAt("reviewed_ids", numberFieldMapping) docMapping.AddFieldMappingsAt("review_requested_ids", numberFieldMapping) @@ -99,7 +100,7 @@ func generateIssueIndexMapping() (mapping.IndexMapping, error) { "type": custom.Name, "char_filters": []string{}, "tokenizer": unicode.Name, - "token_filters": []string{unicodeNormalizeName, lowercase.Name}, + "token_filters": []string{unicodeNormalizeName, camelcase.Name, lowercase.Name}, }); err != nil { return nil, err } @@ -195,19 +196,19 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( filters = append(filters, bleve.NewDisjunctionQuery(repoQueries...)) } - if has, value := options.PriorityRepoID.Get(); has { - eq := inner_bleve.NumericEqualityQuery(value, "repo_id") + if options.PriorityRepoID.Has() { + eq := inner_bleve.NumericEqualityQuery(options.PriorityRepoID.Value(), "repo_id") eq.SetBoost(10.0) meh := bleve.NewMatchAllQuery() meh.SetBoost(0) q.AddShould(bleve.NewDisjunctionQuery(eq, meh)) } - if has, value := options.IsPull.Get(); has { - filters = append(filters, inner_bleve.BoolFieldQuery(value, "is_pull")) + if options.IsPull.Has() { + filters = append(filters, inner_bleve.BoolFieldQuery(options.IsPull.Value(), "is_pull")) } - if has, value := options.IsClosed.Get(); has { - filters = append(filters, inner_bleve.BoolFieldQuery(value, "is_closed")) + if options.IsClosed.Has() { + filters = append(filters, inner_bleve.BoolFieldQuery(options.IsClosed.Value(), "is_closed")) } if options.NoLabelOnly { @@ -245,14 +246,14 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( "project_id": options.ProjectID, "project_board_id": options.ProjectColumnID, "poster_id": options.PosterID, - "assignee_ids": options.AssigneeID, + "assignee_id": options.AssigneeID, "mention_ids": options.MentionID, "reviewed_ids": options.ReviewedID, "review_requested_ids": options.ReviewRequestedID, "subscriber_ids": options.SubscriberID, } { - if has, value := val.Get(); has { - filters = append(filters, inner_bleve.NumericEqualityQuery(value, key)) + if val.Has() { + filters = append(filters, inner_bleve.NumericEqualityQuery(val.Value(), key)) } } diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index c4c05257eb..5025d824a5 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -39,10 +39,10 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issues_ // See the comment of issues_model.SearchOptions for the reason why we need to convert convertID := func(id optional.Option[int64]) int64 { - has, value := id.Get() - if !has { + if !id.Has() { return 0 } + value := id.Value() if value == 0 { return db.NoConditionID } @@ -69,8 +69,8 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issues_ IncludeMilestones: nil, SortType: sortType, IssueIDs: nil, - UpdatedAfterUnix: options.UpdatedAfterUnix.ValueOrZeroValue(), - UpdatedBeforeUnix: options.UpdatedBeforeUnix.ValueOrZeroValue(), + UpdatedAfterUnix: options.UpdatedAfterUnix.Value(), + UpdatedBeforeUnix: options.UpdatedBeforeUnix.Value(), PriorityRepoID: 0, IsArchived: optional.None[bool](), Org: nil, @@ -78,9 +78,9 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issues_ User: nil, } - if has, value := options.PriorityRepoID.Get(); has { + if options.PriorityRepoID.Has() { opts.SortType = "priorityrepo" - opts.PriorityRepoID = value + opts.PriorityRepoID = options.PriorityRepoID.Value() } if len(options.MilestoneIDs) == 1 && options.MilestoneIDs[0] == 0 { diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index 123f2a78e4..4a5e667c14 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -18,7 +18,7 @@ import ( ) const ( - issueIndexerLatestVersion = 3 + issueIndexerLatestVersion = 2 // multi-match-types, currently only 2 types are used // Reference: https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-dsl-multi-match-query.html#multi-match-types esMultiMatchTypeBestFields = "best_fields" @@ -69,7 +69,7 @@ const ( "project_id": { "type": "long", "index": true }, "project_board_id": { "type": "long", "index": true }, "poster_id": { "type": "long", "index": true }, - "assignee_ids": { "type": "long", "index": true }, + "assignee_id": { "type": "long", "index": true }, "mention_ids": { "type": "long", "index": true }, "reviewed_ids": { "type": "long", "index": true }, "review_requested_ids": { "type": "long", "index": true }, @@ -184,16 +184,16 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( } query.Must(q) } - if has, value := options.PriorityRepoID.Get(); has { - q := elastic.NewTermQuery("repo_id", value).Boost(10) + if options.PriorityRepoID.Has() { + q := elastic.NewTermQuery("repo_id", options.PriorityRepoID.Value()).Boost(10) query.Should(q) } - if has, value := options.IsPull.Get(); has { - query.Must(elastic.NewTermQuery("is_pull", value)) + if options.IsPull.Has() { + query.Must(elastic.NewTermQuery("is_pull", options.IsPull.Value())) } - if has, value := options.IsClosed.Get(); has { - query.Must(elastic.NewTermQuery("is_closed", value)) + if options.IsClosed.Has() { + query.Must(elastic.NewTermQuery("is_closed", options.IsClosed.Value())) } if options.NoLabelOnly { @@ -221,43 +221,43 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query.Must(elastic.NewTermsQuery("milestone_id", toAnySlice(options.MilestoneIDs)...)) } - if has, value := options.ProjectID.Get(); has { - query.Must(elastic.NewTermQuery("project_id", value)) + if options.ProjectID.Has() { + query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value())) } - if has, value := options.ProjectColumnID.Get(); has { - query.Must(elastic.NewTermQuery("project_board_id", value)) + if options.ProjectColumnID.Has() { + query.Must(elastic.NewTermQuery("project_board_id", options.ProjectColumnID.Value())) } - if has, value := options.PosterID.Get(); has { - query.Must(elastic.NewTermQuery("poster_id", value)) + if options.PosterID.Has() { + query.Must(elastic.NewTermQuery("poster_id", options.PosterID.Value())) } - if has, value := options.AssigneeID.Get(); has { - query.Must(elastic.NewTermQuery("assignee_ids", value)) + if options.AssigneeID.Has() { + query.Must(elastic.NewTermQuery("assignee_id", options.AssigneeID.Value())) } - if has, value := options.MentionID.Get(); has { - query.Must(elastic.NewTermQuery("mention_ids", value)) + if options.MentionID.Has() { + query.Must(elastic.NewTermQuery("mention_ids", options.MentionID.Value())) } - if has, value := options.ReviewedID.Get(); has { - query.Must(elastic.NewTermQuery("reviewed_ids", value)) + if options.ReviewedID.Has() { + query.Must(elastic.NewTermQuery("reviewed_ids", options.ReviewedID.Value())) } - if has, value := options.ReviewRequestedID.Get(); has { - query.Must(elastic.NewTermQuery("review_requested_ids", value)) + if options.ReviewRequestedID.Has() { + query.Must(elastic.NewTermQuery("review_requested_ids", options.ReviewRequestedID.Value())) } - if has, value := options.SubscriberID.Get(); has { - query.Must(elastic.NewTermQuery("subscriber_ids", value)) + if options.SubscriberID.Has() { + query.Must(elastic.NewTermQuery("subscriber_ids", options.SubscriberID.Value())) } if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() { q := elastic.NewRangeQuery("updated_unix") - if has, value := options.UpdatedAfterUnix.Get(); has { - q.Gte(value) + if options.UpdatedAfterUnix.Has() { + q.Gte(options.UpdatedAfterUnix.Value()) } - if has, value := options.UpdatedBeforeUnix.Get(); has { - q.Lte(value) + if options.UpdatedBeforeUnix.Has() { + q.Lte(options.UpdatedBeforeUnix.Value()) } query.Must(q) } diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 2f70cf7a69..dd7102713c 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -61,12 +61,10 @@ func init() { // InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until // all issue index done. -// The return value is a done channel that signals that the indexer can be safely used. -func InitIssueIndexer(syncReindex bool) <-chan struct{} { +func InitIssueIndexer(syncReindex bool) { ctx, _, finished := process.GetManager().AddTypedContext(context.Background(), "Service: IssueIndexer", process.SystemProcessType, false) indexerInitWaitChannel := make(chan time.Duration, 1) - done := make(chan struct{}, 1) // Create the Queue issueIndexerQueue = queue.CreateUniqueQueue(ctx, "issue_indexer", getIssueIndexerQueueHandler(ctx)) @@ -138,15 +136,12 @@ func InitIssueIndexer(syncReindex bool) <-chan struct{} { indexerInitWaitChannel <- time.Since(start) close(indexerInitWaitChannel) - close(done) }() if syncReindex { select { case <-indexerInitWaitChannel: - break case <-graceful.GetManager().IsShutdown(): - break } } else if setting.Indexer.StartupTimeout > 0 { go func() { @@ -166,8 +161,6 @@ func InitIssueIndexer(syncReindex bool) <-chan struct{} { } }() } - - return done } func getIssueIndexerQueueHandler(ctx context.Context) func(items ...*IndexerMetadata) []*IndexerMetadata { diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index 60c46d9a2c..7d4ffa7f02 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -30,7 +30,7 @@ func TestDBSearchIssues(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) defer test.MockVariableValue(&setting.Indexer.IssueType, "db")() - <-InitIssueIndexer(true) + InitIssueIndexer(true) t.Run("search issues with keyword", searchIssueWithKeyword) t.Run("search issues in repo", searchIssueInRepo) @@ -58,13 +58,6 @@ func searchIssueWithKeyword(t *testing.T) { }, []int64{2}, }, - { - "ISSUe2", // case-insensitive search - &SearchOptions{ - RepoIDs: []int64{1}, - }, - []int64{2}, - }, { "first", &SearchOptions{ @@ -426,7 +419,7 @@ func TestBleveDeleteIssue(t *testing.T) { tmp := t.TempDir() defer test.MockVariableValue(&setting.Indexer.IssuePath, filepath.Join(tmp, "indexers/issues.bleve"))() defer test.MockVariableValue(&setting.Indexer.IssueType, "bleve")() - <-InitIssueIndexer(false) + InitIssueIndexer(false) ctx := t.Context() issue := unittest.AssertExistsAndLoadBean(t, &issues.Issue{ID: 1}) diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index 4034f6d0f9..89134dcac7 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -30,7 +30,7 @@ type IndexerData struct { ProjectID int64 `json:"project_id"` ProjectColumnID int64 `json:"project_board_id"` // the key should be kept as project_board_id to keep compatible PosterID int64 `json:"poster_id"` - AssigneeIDs []int64 `json:"assignee_ids"` + AssigneeID int64 `json:"assignee_id"` MentionIDs []int64 `json:"mention_ids"` ReviewedIDs []int64 `json:"reviewed_ids"` ReviewRequestedIDs []int64 `json:"review_requested_ids"` diff --git a/modules/indexer/issues/internal/qstring_test.go b/modules/indexer/issues/internal/qstring_test.go index 66c8a66ca3..2ad924076f 100644 --- a/modules/indexer/issues/internal/qstring_test.go +++ b/modules/indexer/issues/internal/qstring_test.go @@ -11,8 +11,6 @@ import ( "forgejo.org/models/user" "forgejo.org/modules/optional" - _ "forgejo.org/modules/testimport" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index c4a453c9aa..4466bf25a7 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -461,10 +461,10 @@ var cases = []*testIndexerCase{ Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Len(t, result.Hits, 5) for _, v := range result.Hits { - assert.Contains(t, data[v.ID].AssigneeIDs, int64(1)) + assert.Equal(t, int64(1), data[v.ID].AssigneeID) } assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { - return slices.Contains(v.AssigneeIDs, 1) + return v.AssigneeID == 1 }), result.Total) }, }, @@ -479,10 +479,10 @@ var cases = []*testIndexerCase{ Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Len(t, result.Hits, 5) for _, v := range result.Hits { - assert.Equal(t, []int64{0}, data[v.ID].AssigneeIDs) + assert.Equal(t, int64(0), data[v.ID].AssigneeID) } assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { - return slices.Contains(v.AssigneeIDs, 0) + return v.AssigneeID == 0 }), result.Total) }, }, @@ -840,14 +840,6 @@ func generateDefaultIndexerData() []*internal.IndexerData { subscriberIDs[i] = int64(i) + 1 // SubscriberID should not be 0 } - assigneeIDs := make([]int64, 0, 2) - { - if issueIndex%7 == 0 { // If divisible by 7 we insert 1 too to test multiple assignees - assigneeIDs = append(assigneeIDs, 1) - } - assigneeIDs = append(assigneeIDs, issueIndex%10) - } - data = append(data, &internal.IndexerData{ ID: id, Index: issueIndex, @@ -864,7 +856,7 @@ func generateDefaultIndexerData() []*internal.IndexerData { ProjectID: issueIndex % 5, ProjectColumnID: issueIndex % 6, PosterID: id%10 + 1, // PosterID should not be 0 - AssigneeIDs: assigneeIDs, + AssigneeID: issueIndex % 10, MentionIDs: mentionIDs, ReviewedIDs: reviewedIDs, ReviewRequestedIDs: reviewRequestedIDs, diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index 98bcf45b45..82b889f875 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -18,7 +18,7 @@ import ( ) const ( - issueIndexerLatestVersion = 4 + issueIndexerLatestVersion = 3 // TODO: make this configurable if necessary maxTotalHits = 10000 @@ -67,7 +67,7 @@ func NewIndexer(url, apiKey, indexerName string) *Indexer { "project_id", "project_board_id", "poster_id", - "assignee_ids", + "assignee_id", "mention_ids", "reviewed_ids", "review_requested_ids", @@ -116,7 +116,7 @@ func (b *Indexer) Delete(_ context.Context, ids ...int64) error { } for _, id := range ids { - _, err := b.inner.Client.Index(b.inner.VersionedIndexName()).DeleteDocument(strconv.FormatInt(id, 10), nil) + _, err := b.inner.Client.Index(b.inner.VersionedIndexName()).DeleteDocument(strconv.FormatInt(id, 10)) if err != nil { return err } @@ -139,11 +139,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query.And(q) } - if has, value := options.IsPull.Get(); has { - query.And(inner_meilisearch.NewFilterEq("is_pull", value)) + if options.IsPull.Has() { + query.And(inner_meilisearch.NewFilterEq("is_pull", options.IsPull.Value())) } - if has, value := options.IsClosed.Get(); has { - query.And(inner_meilisearch.NewFilterEq("is_closed", value)) + if options.IsClosed.Has() { + query.And(inner_meilisearch.NewFilterEq("is_closed", options.IsClosed.Value())) } if options.NoLabelOnly { @@ -171,41 +171,41 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query.And(inner_meilisearch.NewFilterIn("milestone_id", options.MilestoneIDs...)) } - if has, value := options.ProjectID.Get(); has { - query.And(inner_meilisearch.NewFilterEq("project_id", value)) + if options.ProjectID.Has() { + query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value())) } - if has, value := options.ProjectColumnID.Get(); has { - query.And(inner_meilisearch.NewFilterEq("project_board_id", value)) + if options.ProjectColumnID.Has() { + query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectColumnID.Value())) } - if has, value := options.PosterID.Get(); has { - query.And(inner_meilisearch.NewFilterEq("poster_id", value)) + if options.PosterID.Has() { + query.And(inner_meilisearch.NewFilterEq("poster_id", options.PosterID.Value())) } - if has, value := options.AssigneeID.Get(); has { - query.And(inner_meilisearch.NewFilterEq("assignee_ids", value)) + if options.AssigneeID.Has() { + query.And(inner_meilisearch.NewFilterEq("assignee_id", options.AssigneeID.Value())) } - if has, value := options.MentionID.Get(); has { - query.And(inner_meilisearch.NewFilterEq("mention_ids", value)) + if options.MentionID.Has() { + query.And(inner_meilisearch.NewFilterEq("mention_ids", options.MentionID.Value())) } - if has, value := options.ReviewedID.Get(); has { - query.And(inner_meilisearch.NewFilterEq("reviewed_ids", value)) + if options.ReviewedID.Has() { + query.And(inner_meilisearch.NewFilterEq("reviewed_ids", options.ReviewedID.Value())) } - if has, value := options.ReviewRequestedID.Get(); has { - query.And(inner_meilisearch.NewFilterEq("review_requested_ids", value)) + if options.ReviewRequestedID.Has() { + query.And(inner_meilisearch.NewFilterEq("review_requested_ids", options.ReviewRequestedID.Value())) } - if has, value := options.SubscriberID.Get(); has { - query.And(inner_meilisearch.NewFilterEq("subscriber_ids", value)) + if options.SubscriberID.Has() { + query.And(inner_meilisearch.NewFilterEq("subscriber_ids", options.SubscriberID.Value())) } - if has, value := options.UpdatedAfterUnix.Get(); has { - query.And(inner_meilisearch.NewFilterGte("updated_unix", value)) + if options.UpdatedAfterUnix.Has() { + query.And(inner_meilisearch.NewFilterGte("updated_unix", options.UpdatedAfterUnix.Value())) } - if has, value := options.UpdatedBeforeUnix.Get(); has { - query.And(inner_meilisearch.NewFilterLte("updated_unix", value)) + if options.UpdatedBeforeUnix.Has() { + query.And(inner_meilisearch.NewFilterLte("updated_unix", options.UpdatedBeforeUnix.Value())) } var sortBy []string @@ -233,19 +233,21 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( } var keywords []string - for _, token := range options.Tokens { - if !token.Fuzzy { - // to make it a phrase search, we have to quote the keyword(s) - // https://www.meilisearch.com/docs/reference/api/search#phrase-search - token.Term = doubleQuoteKeyword(token.Term) - } + if len(options.Tokens) != 0 { + for _, token := range options.Tokens { + if !token.Fuzzy { + // to make it a phrase search, we have to quote the keyword(s) + // https://www.meilisearch.com/docs/reference/api/search#phrase-search + token.Term = doubleQuoteKeyword(token.Term) + } - // internal.BoolOptShould (Default, requires no modifications) - // internal.BoolOptMust (Not supported by meilisearch) - if token.Kind == internal.BoolOptNot { - token.Term = "-" + token.Term + // internal.BoolOptShould (Default, requires no modifications) + // internal.BoolOptMust (Not supported by meilisearch) + if token.Kind == internal.BoolOptNot { + token.Term = "-" + token.Term + } + keywords = append(keywords, token.Term) } - keywords = append(keywords, token.Term) } searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()). diff --git a/modules/indexer/issues/util.go b/modules/indexer/issues/util.go index 71ee46e235..31b7e888c0 100644 --- a/modules/indexer/issues/util.go +++ b/modules/indexer/issues/util.go @@ -50,15 +50,6 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD labels = append(labels, label.ID) } - assigneeIDs := make([]int64, 0, len(issue.Assignees)) - if len(issue.Assignees) != 0 { - for _, assignee := range issue.Assignees { - assigneeIDs = append(assigneeIDs, assignee.ID) - } - } else { - assigneeIDs = append(assigneeIDs, 0) - } - mentionIDs, err := issues_model.GetIssueMentionIDs(ctx, issueID) if err != nil { return nil, false, err @@ -117,7 +108,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD ProjectID: projectID, ProjectColumnID: issue.ProjectColumnID(ctx), PosterID: issue.PosterID, - AssigneeIDs: assigneeIDs, + AssigneeID: issue.AssigneeID, MentionIDs: mentionIDs, ReviewedIDs: reviewedIDs, ReviewRequestedIDs: reviewRequestedIDs, diff --git a/modules/issue/template/template.go b/modules/issue/template/template.go index 09dfe10e08..08c1b21c26 100644 --- a/modules/issue/template/template.go +++ b/modules/issue/template/template.go @@ -8,7 +8,6 @@ import ( "fmt" "net/url" "regexp" - "slices" "strconv" "strings" @@ -448,7 +447,12 @@ func (o *valuedOption) IsChecked() bool { case api.IssueFormFieldTypeDropdown: checks := strings.Split(o.field.Get(fmt.Sprintf("form-field-%s", o.field.ID)), ",") idx := strconv.Itoa(o.index) - return slices.Contains(checks, idx) + for _, v := range checks { + if v == idx { + return true + } + } + return false case api.IssueFormFieldTypeCheckboxes: return o.field.Get(fmt.Sprintf("form-field-%s-%d", o.field.ID, o.index)) == "on" } diff --git a/modules/jwtx/signingkey.go b/modules/jwtx/signingkey.go deleted file mode 100644 index f82f694d19..0000000000 --- a/modules/jwtx/signingkey.go +++ /dev/null @@ -1,565 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package jwtx - -import ( - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "fmt" - "math/big" - "os" - "path/filepath" - "slices" - "strings" - - "forgejo.org/modules/log" - "forgejo.org/modules/util" - - "github.com/golang-jwt/jwt/v5" -) - -// The ...KeyCfg types are only used for handover from setting to signingkey -// see comment in setting/security.go - -type SigningKeyCfg struct { - Algorithm string - SecretBytes *[]byte - PrivateKeyPath *string -} - -type KeyCfg struct { - Signing *SigningKeyCfg - // more later -} - -// ErrInvalidAlgorithmType represents an invalid algorithm error. -type ErrInvalidAlgorithmType struct { - Algorithm string -} - -func (err ErrInvalidAlgorithmType) Error() string { - return fmt.Sprintf("JWT signing algorithm is not supported: %s", err.Algorithm) -} - -func jwtHelper(key SigningKey, claims jwt.Claims, opts ...jwt.TokenOption) (string, error) { - jwt := jwt.NewWithClaims(key.SigningMethod(), claims, opts...) - key.PreProcessToken(jwt) - return jwt.SignedString(key.SignKey()) -} - -// SigningKey represents a algorithm/key pair to sign JWTs -type SigningKey interface { - IsSymmetric() bool - SigningMethod() jwt.SigningMethod - SignKey() any - VerifyKey() any - ToJWK() (map[string]string, error) - PreProcessToken(*jwt.Token) - // convenience: jwt.NewWithClaims + PreProcessToken + SignedString - JWT(jwt.Claims, ...jwt.TokenOption) (string, error) -} - -type hmacSigningKey struct { - signingMethod jwt.SigningMethod - secret []byte -} - -func (key hmacSigningKey) IsSymmetric() bool { - return true -} - -func (key hmacSigningKey) SigningMethod() jwt.SigningMethod { - return key.signingMethod -} - -func (key hmacSigningKey) SignKey() any { - return key.secret -} - -func (key hmacSigningKey) VerifyKey() any { - return key.secret -} - -func (key hmacSigningKey) ToJWK() (map[string]string, error) { - return map[string]string{ - "kty": "oct", - "alg": key.SigningMethod().Alg(), - }, nil -} - -func (key hmacSigningKey) PreProcessToken(*jwt.Token) {} - -func (key hmacSigningKey) JWT(claims jwt.Claims, opts ...jwt.TokenOption) (string, error) { - return jwtHelper(key, claims, opts...) -} - -type rsaSigningKey struct { - signingMethod jwt.SigningMethod - key *rsa.PrivateKey - id string -} - -func newRSASigningKey(signingMethod jwt.SigningMethod, key *rsa.PrivateKey) (rsaSigningKey, error) { - kid, err := util.CreatePublicKeyFingerprint(key.Public().(*rsa.PublicKey)) - if err != nil { - return rsaSigningKey{}, err - } - - return rsaSigningKey{ - signingMethod, - key, - base64.RawURLEncoding.EncodeToString(kid), - }, nil -} - -func (key rsaSigningKey) IsSymmetric() bool { - return false -} - -func (key rsaSigningKey) SigningMethod() jwt.SigningMethod { - return key.signingMethod -} - -func (key rsaSigningKey) SignKey() any { - return key.key -} - -func (key rsaSigningKey) VerifyKey() any { - return key.key.Public() -} - -func (key rsaSigningKey) ToJWK() (map[string]string, error) { - pubKey := key.key.Public().(*rsa.PublicKey) - - return map[string]string{ - "kty": "RSA", - "alg": key.SigningMethod().Alg(), - "kid": key.id, - "e": base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pubKey.E)).Bytes()), - "n": base64.RawURLEncoding.EncodeToString(pubKey.N.Bytes()), - }, nil -} - -func (key rsaSigningKey) PreProcessToken(token *jwt.Token) { - token.Header["kid"] = key.id -} - -func (key rsaSigningKey) JWT(claims jwt.Claims, opts ...jwt.TokenOption) (string, error) { - return jwtHelper(key, claims, opts...) -} - -type eddsaSigningKey struct { - signingMethod jwt.SigningMethod - key ed25519.PrivateKey - id string -} - -func newEdDSASigningKey(signingMethod jwt.SigningMethod, key ed25519.PrivateKey) (eddsaSigningKey, error) { - kid, err := util.CreatePublicKeyFingerprint(key.Public().(ed25519.PublicKey)) - if err != nil { - return eddsaSigningKey{}, err - } - - return eddsaSigningKey{ - signingMethod, - key, - base64.RawURLEncoding.EncodeToString(kid), - }, nil -} - -func (key eddsaSigningKey) IsSymmetric() bool { - return false -} - -func (key eddsaSigningKey) SigningMethod() jwt.SigningMethod { - return key.signingMethod -} - -func (key eddsaSigningKey) SignKey() any { - return key.key -} - -func (key eddsaSigningKey) VerifyKey() any { - return key.key.Public() -} - -func (key eddsaSigningKey) ToJWK() (map[string]string, error) { - pubKey := key.key.Public().(ed25519.PublicKey) - - return map[string]string{ - "alg": key.SigningMethod().Alg(), - "kid": key.id, - "kty": "OKP", - "crv": "Ed25519", - "x": base64.RawURLEncoding.EncodeToString(pubKey), - }, nil -} - -func (key eddsaSigningKey) PreProcessToken(token *jwt.Token) { - token.Header["kid"] = key.id -} - -func (key eddsaSigningKey) JWT(claims jwt.Claims, opts ...jwt.TokenOption) (string, error) { - return jwtHelper(key, claims, opts...) -} - -type ecdsaSigningKey struct { - signingMethod jwt.SigningMethod - key *ecdsa.PrivateKey - id string -} - -func newECDSASigningKey(signingMethod jwt.SigningMethod, key *ecdsa.PrivateKey) (ecdsaSigningKey, error) { - kid, err := util.CreatePublicKeyFingerprint(key.Public().(*ecdsa.PublicKey)) - if err != nil { - return ecdsaSigningKey{}, err - } - - return ecdsaSigningKey{ - signingMethod, - key, - base64.RawURLEncoding.EncodeToString(kid), - }, nil -} - -func (key ecdsaSigningKey) IsSymmetric() bool { - return false -} - -func (key ecdsaSigningKey) SigningMethod() jwt.SigningMethod { - return key.signingMethod -} - -func (key ecdsaSigningKey) SignKey() any { - return key.key -} - -func (key ecdsaSigningKey) VerifyKey() any { - return key.key.Public() -} - -func (key ecdsaSigningKey) ToJWK() (map[string]string, error) { - pubKey := key.key.Public().(*ecdsa.PublicKey) - - return map[string]string{ - "kty": "EC", - "alg": key.SigningMethod().Alg(), - "kid": key.id, - "crv": pubKey.Params().Name, - "x": base64.RawURLEncoding.EncodeToString(pubKey.X.Bytes()), //nolint:staticcheck // no easy replacement. JWTX specification mandates marshalling to x, even if unsafe. - "y": base64.RawURLEncoding.EncodeToString(pubKey.Y.Bytes()), //nolint:staticcheck // no easy replacement. JWTX specification mandates marshalling to y, even if unsafe. - }, nil -} - -func (key ecdsaSigningKey) PreProcessToken(token *jwt.Token) { - token.Header["kid"] = key.id -} - -func (key ecdsaSigningKey) JWT(claims jwt.Claims, opts ...jwt.TokenOption) (string, error) { - return jwtHelper(key, claims, opts...) -} - -var allowedAlgorithms = map[string]bool{ - "HS256": true, - "HS384": true, - "HS512": true, - - "RS256": true, - "RS384": true, - "RS512": true, - - "ES256": true, - "ES384": true, - "ES512": true, - "EdDSA": true, -} - -func GetSigningMethod(algorithm string) jwt.SigningMethod { - if !allowedAlgorithms[algorithm] { - return nil - } - return jwt.GetSigningMethod(algorithm) -} - -// CreateSigningKey creates a signing key from an algorithm / key pair. -func CreateSigningKey(algorithm string, key any) (SigningKey, error) { - signingMethod := GetSigningMethod(algorithm) - if signingMethod == nil { - return nil, ErrInvalidAlgorithmType{algorithm} - } - - switch signingMethod.(type) { - case *jwt.SigningMethodEd25519: - privateKey, ok := key.(ed25519.PrivateKey) - if !ok { - return nil, jwt.ErrInvalidKeyType - } - return newEdDSASigningKey(signingMethod, privateKey) - case *jwt.SigningMethodECDSA: - privateKey, ok := key.(*ecdsa.PrivateKey) - if !ok { - return nil, jwt.ErrInvalidKeyType - } - return newECDSASigningKey(signingMethod, privateKey) - case *jwt.SigningMethodRSA: - privateKey, ok := key.(*rsa.PrivateKey) - if !ok { - return nil, jwt.ErrInvalidKeyType - } - return newRSASigningKey(signingMethod, privateKey) - default: - secret, ok := key.([]byte) - if !ok { - return nil, jwt.ErrInvalidKeyType - } - return hmacSigningKey{signingMethod, secret}, nil - } -} - -func createAsymmetricKey(keyPath, algorithm string) error { - key, err := func() (any, error) { - switch { - case strings.HasPrefix(algorithm, "RS"): - var bits int - switch algorithm { - case "RS256": - bits = 2048 - case "RS384": - bits = 3072 - case "RS512": - bits = 4096 - } - return rsa.GenerateKey(rand.Reader, bits) - case algorithm == "EdDSA": - _, pk, err := ed25519.GenerateKey(rand.Reader) - return pk, err - default: - var curve elliptic.Curve - switch algorithm { - case "ES256": - curve = elliptic.P256() - case "ES384": - curve = elliptic.P384() - case "ES512": - curve = elliptic.P521() - } - return ecdsa.GenerateKey(curve, rand.Reader) - } - }() - if err != nil { - return err - } - - bytes, err := x509.MarshalPKCS8PrivateKey(key) - if err != nil { - return err - } - - privateKeyPEM := &pem.Block{Type: "PRIVATE KEY", Bytes: bytes} - - if err := os.MkdirAll(filepath.Dir(keyPath), os.ModePerm); err != nil { - return err - } - - f, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) - if err != nil { - return err - } - defer func() { - if err = f.Close(); err != nil { - log.Error("Close: %v", err) - } - }() - - return pem.Encode(f, privateKeyPEM) -} - -func loadAsymmetricKey(keyPath string) (any, error) { - bytes, err := os.ReadFile(keyPath) - if err != nil { - return nil, err - } - - block, _ := pem.Decode(bytes) - if block == nil { - return nil, fmt.Errorf("no valid PEM data found in %s", keyPath) - } else if block.Type != "PRIVATE KEY" { - return nil, fmt.Errorf("expected PRIVATE KEY, got %s in %s", block.Type, keyPath) - } - - return x509.ParsePKCS8PrivateKey(block.Bytes) -} - -// loadOrCreateAsymmetricKey checks if the configured private key exists. -// If it does not exist a new random key gets generated and saved on the configured path. -func loadOrCreateAsymmetricKey(keyPath, algorithm string) (any, error) { - isExist, err := util.IsExist(keyPath) - if err != nil { - return nil, fmt.Errorf("Unable to check if %s exists. Error: %v", keyPath, err) - } - if !isExist { - err := createAsymmetricKey(keyPath, algorithm) - if err != nil { - return nil, fmt.Errorf("Error generating private key %s: %v", keyPath, err) - } - } - return loadAsymmetricKey(keyPath) -} - -// InitSigningKey creates a signing key from SigningKeyCfg -// cfgP is set to nil to mark that is has been processed -func InitSigningKey(cfgP **SigningKeyCfg) (SigningKey, error) { - cfg := *cfgP - *cfgP = nil - var err error - var key SigningKey - - if IsValidSymmetricAlgorithm(cfg.Algorithm) { - key, err = CreateSigningKey(cfg.Algorithm, *cfg.SecretBytes) - } else if IsValidAsymmetricAlgorithm(cfg.Algorithm) { - key, err = InitAsymmetricSigningKey(*cfg.PrivateKeyPath, cfg.Algorithm) - } else { - // should never happen, setting.loadSigningKeyCfg() ensures - err = ErrInvalidAlgorithmType{Algorithm: cfg.Algorithm} - } - - return key, err -} - -var ( - ValidSymmetricAlgorighms = []string{"HS256", "HS384", "HS512"} - ValidAsymmetricAlgorithms = []string{"RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"} -) - -// IsValidSymmetricAlgorithm checks if the passed in algorithm is a supported symettric algorithm. -func IsValidSymmetricAlgorithm(algorithm string) bool { - return slices.Contains(ValidSymmetricAlgorighms, algorithm) -} - -// IsValidAsymmetricAlgorithm checks if the passed in algorithm is a supported asymmetric algorithm. -func IsValidAsymmetricAlgorithm(algorithm string) bool { - return slices.Contains(ValidAsymmetricAlgorithms, algorithm) -} - -// InitAsymmetricSigningKey creates an asymmetric signing key from settings or creates a random key. -func InitAsymmetricSigningKey(keyPath, algorithm string) (SigningKey, error) { - var err error - var key any - - if !IsValidAsymmetricAlgorithm(algorithm) { - return nil, ErrInvalidAlgorithmType{Algorithm: algorithm} - } - - key, err = loadOrCreateAsymmetricKey(keyPath, algorithm) - if err != nil { - return nil, fmt.Errorf("Error while loading or creating JWT key: %w", err) - } - - signingKey, err := CreateSigningKey(algorithm, key) - if err != nil { - return nil, err - } - - return signingKey, nil -} - -func requiredJWKStr(jwk map[string]any, key string) (string, error) { - vAny, ok := jwk[key] - if !ok { - return "", fmt.Errorf("JWK missing required field %q", key) - } - vStr, ok := vAny.(string) - if !ok { - return "", fmt.Errorf("JWK field %q must be string, but was %T", key, vAny) - } - return vStr, nil -} - -// Reconstructs public key from a JWKS entry (such as those produced by [SigningKey.ToJWK]), parsing the JWK output and -// returning a key object. The key object produced must be usable for [jwt.SigningMethod] interface's [Verify] method, -// for the related signing method -- an [rsa.PublicKey] object, an [ed25519.PublicKey] object, or [ecdsa.PublicKey] -// object, with the currently supported asymmetric algorithms. -func ParseJWKToPublicKey(jwk map[string]any) (any, error) { - kty := jwk["kty"] - - switch kty { - case "RSA": - eStr, err := requiredJWKStr(jwk, "e") - if err != nil { - return nil, err - } - nStr, err := requiredJWKStr(jwk, "n") - if err != nil { - return nil, err - } - eBytes, err := base64.RawURLEncoding.DecodeString(eStr) - if err != nil { - return nil, fmt.Errorf("invalid RSA JWK 'e' field: %w", err) - } - nBytes, err := base64.RawURLEncoding.DecodeString(nStr) - if err != nil { - return nil, fmt.Errorf("invalid RSA JWK 'n' field: %w", err) - } - pubKey := &rsa.PublicKey{ - E: int(new(big.Int).SetBytes(eBytes).Int64()), - N: new(big.Int).SetBytes(nBytes), - } - return pubKey, nil - case "OKP": - if jwk["crv"] != "Ed25519" { - return nil, fmt.Errorf("OKP curve %d is not supported; only Ed25519", jwk["crv"]) - } - xStr, err := requiredJWKStr(jwk, "x") - if err != nil { - return nil, err - } - xBytes, err := base64.RawURLEncoding.DecodeString(xStr) - if err != nil { - return nil, fmt.Errorf("invalid EdDSA JWK 'x' field: %w", err) - } - return ed25519.PublicKey(xBytes), nil - case "EC": - xStr, err := requiredJWKStr(jwk, "x") - if err != nil { - return nil, err - } - yStr, err := requiredJWKStr(jwk, "y") - if err != nil { - return nil, err - } - var curve elliptic.Curve - switch jwk["crv"] { - case "P-256": - curve = elliptic.P256() - case "P-384": - curve = elliptic.P384() - case "P-521": - curve = elliptic.P521() - default: - return nil, fmt.Errorf("unsupported ECDSA curve in JWK: %s", jwk["crv"]) - } - xBytes, err := base64.RawURLEncoding.DecodeString(xStr) - if err != nil { - return nil, fmt.Errorf("invalid ECDSA JWK 'x' field: %w", err) - } - yBytes, err := base64.RawURLEncoding.DecodeString(yStr) - if err != nil { - return nil, fmt.Errorf("invalid ECDSA JWK 'y' field: %w", err) - } - pubKey := &ecdsa.PublicKey{ - Curve: curve, - X: new(big.Int).SetBytes(xBytes), - Y: new(big.Int).SetBytes(yBytes), - } - return pubKey, nil - default: - return nil, fmt.Errorf("unsupported key type in JWK: %s", kty) - } -} diff --git a/modules/jwtx/signingkey_test.go b/modules/jwtx/signingkey_test.go deleted file mode 100644 index 96f6613b84..0000000000 --- a/modules/jwtx/signingkey_test.go +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package jwtx - -import ( - "crypto/ecdsa" - "crypto/ed25519" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "os" - "path/filepath" - "testing" - - "github.com/golang-jwt/jwt/v5" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func testSignVerify(t *testing.T, signKey, verifyKey SigningKey) { - t.Helper() - // test sign and verify - claimsIn := jwt.RegisteredClaims{ - Issuer: "abc", - ID: "0815", - } - token, err := signKey.JWT(claimsIn) - require.NoError(t, err) - require.NotEmpty(t, token) - - var claimsOut jwt.RegisteredClaims - parsed, err := jwt.ParseWithClaims(token, &claimsOut, func(valToken *jwt.Token) (any, error) { - assert.NotNil(t, valToken.Method) - assert.Equal(t, signKey.SigningMethod().Alg(), valToken.Method.Alg()) - assert.Equal(t, verifyKey.SigningMethod().Alg(), valToken.Method.Alg()) - kid, ok := valToken.Header["kid"] - assert.True(t, ok) - assert.NotNil(t, kid) - - return verifyKey.VerifyKey(), nil - }) - require.NoError(t, err) - assert.NotNil(t, parsed) - assert.Equal(t, claimsIn, claimsOut) - assert.Equal(t, &claimsIn, parsed.Claims) -} - -// creates private key -// loads it back from the file -func TestLoadOrCreateAsymmetricKey(t *testing.T) { - loadKey := func(t *testing.T, keyPath, algorithm string) any { - t.Helper() - loadOrCreateAsymmetricKey(keyPath, algorithm) - - fileContent, err := os.ReadFile(keyPath) - require.NoError(t, err) - - block, _ := pem.Decode(fileContent) - assert.NotNil(t, block) - assert.Equal(t, "PRIVATE KEY", block.Type) - - parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) - require.NoError(t, err) - - return parsedKey - } - useKey := func(t *testing.T, keyPath, algorithm string) { - t.Helper() - // duplicates loadKey() to some extent, but uses SigningKey - cfg := &SigningKeyCfg{ - Algorithm: algorithm, - PrivateKeyPath: &keyPath, - } - - key, err := InitSigningKey(&cfg) - require.NoError(t, err) - assert.NotNil(t, key) - assert.Nil(t, cfg) - - testSignVerify(t, key, key) - } - t.Run("RSA-2048", func(t *testing.T) { - keyPath := filepath.Join(t.TempDir(), "jwt-rsa-2048.priv") - algorithm := "RS256" - - parsedKey := loadKey(t, keyPath, algorithm) - - rsaPrivateKey := parsedKey.(*rsa.PrivateKey) - assert.Equal(t, 2048, rsaPrivateKey.N.BitLen()) - t.Run("Use", func(t *testing.T) { - useKey(t, keyPath, algorithm) - }) - - t.Run("Load key with differ specified algorithm", func(t *testing.T) { - algorithm = "EdDSA" - - parsedKey := loadKey(t, keyPath, algorithm) - rsaPrivateKey := parsedKey.(*rsa.PrivateKey) - assert.Equal(t, 2048, rsaPrivateKey.N.BitLen()) - }) - }) - - t.Run("RSA-3072", func(t *testing.T) { - keyPath := filepath.Join(t.TempDir(), "jwt-rsa-3072.priv") - algorithm := "RS384" - - parsedKey := loadKey(t, keyPath, algorithm) - - rsaPrivateKey := parsedKey.(*rsa.PrivateKey) - assert.Equal(t, 3072, rsaPrivateKey.N.BitLen()) - t.Run("Use", func(t *testing.T) { - useKey(t, keyPath, algorithm) - }) - }) - - t.Run("RSA-4096", func(t *testing.T) { - keyPath := filepath.Join(t.TempDir(), "jwt-rsa-4096.priv") - algorithm := "RS512" - - parsedKey := loadKey(t, keyPath, algorithm) - - rsaPrivateKey := parsedKey.(*rsa.PrivateKey) - assert.Equal(t, 4096, rsaPrivateKey.N.BitLen()) - t.Run("Use", func(t *testing.T) { - useKey(t, keyPath, algorithm) - }) - }) - - t.Run("ECDSA-256", func(t *testing.T) { - keyPath := filepath.Join(t.TempDir(), "jwt-ecdsa-256.priv") - algorithm := "ES256" - - parsedKey := loadKey(t, keyPath, algorithm) - - ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey) - assert.Equal(t, 256, ecdsaPrivateKey.Params().BitSize) - t.Run("Use", func(t *testing.T) { - useKey(t, keyPath, algorithm) - }) - }) - - t.Run("ECDSA-384", func(t *testing.T) { - keyPath := filepath.Join(t.TempDir(), "jwt-ecdsa-384.priv") - algorithm := "ES384" - - parsedKey := loadKey(t, keyPath, algorithm) - - ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey) - assert.Equal(t, 384, ecdsaPrivateKey.Params().BitSize) - t.Run("Use", func(t *testing.T) { - useKey(t, keyPath, algorithm) - }) - }) - - t.Run("ECDSA-512", func(t *testing.T) { - keyPath := filepath.Join(t.TempDir(), "jwt-ecdsa-512.priv") - algorithm := "ES512" - - parsedKey := loadKey(t, keyPath, algorithm) - - ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey) - assert.Equal(t, 521, ecdsaPrivateKey.Params().BitSize) - t.Run("Use", func(t *testing.T) { - useKey(t, keyPath, algorithm) - }) - }) - - t.Run("EdDSA", func(t *testing.T) { - keyPath := filepath.Join(t.TempDir(), "jwt-eddsa.priv") - algorithm := "EdDSA" - - parsedKey := loadKey(t, keyPath, algorithm) - - assert.NotNil(t, parsedKey.(ed25519.PrivateKey)) - t.Run("Use", func(t *testing.T) { - useKey(t, keyPath, algorithm) - }) - }) -} - -func TestCannotCreatePrivateKey(t *testing.T) { - _, err := InitAsymmetricSigningKey("/dev/directory-does-not-exist-and-you-should-not-have-permission-to-create/privatekey.pem", "RS256") - require.Error(t, err) - require.ErrorContains(t, err, "Error generating private key") -} diff --git a/modules/keying/keying.go b/modules/keying/keying.go index 14fbaaca98..0d7eecb904 100644 --- a/modules/keying/keying.go +++ b/modules/keying/keying.go @@ -39,10 +39,6 @@ var ( ActionSecret = deriveKey("action_secret") // Used for the `task` table where type == TaskTypeMigrateRepo. MigrateTask = deriveKey("migrate_repo_task") - // Used for the `webhook` table. - Webhook = deriveKey("webhook") - // Used for the `mirror` table. - PullMirror = deriveKey("pullmirror") ) var ( @@ -153,7 +149,7 @@ func ColumnAndID(column string, id int64) []byte { // it's not bound to a particular table. The table should be part of the context // that the key was derived for, in which case it binds through that. Use this // over `ColumnAndID` if you're encrypting data that's stored inside JSON. -// jsonSelector must be a unambiguous selector to the JSON field that stores the +// jsonSelector must be a unambigous selector to the JSON field that stores the // encrypted data. func ColumnAndJSONSelectorAndID(column, jsonSelector string, id int64) []byte { return binary.BigEndian.AppendUint64(append(append([]byte(column), ':'), append([]byte(jsonSelector), ':')...), uint64(id)) diff --git a/modules/label/parser.go b/modules/label/parser.go index b27b2c9ee6..12fc176967 100644 --- a/modules/label/parser.go +++ b/modules/label/parser.go @@ -72,7 +72,7 @@ func parseYamlFormat(fileName string, data []byte) ([]*Label, error) { func parseLegacyFormat(fileName string, data []byte) ([]*Label, error) { lines := strings.Split(string(data), "\n") list := make([]*Label, 0, len(lines)) - for i := range lines { + for i := 0; i < len(lines); i++ { line := strings.TrimSpace(lines[i]) if len(line) == 0 { continue @@ -108,7 +108,7 @@ func LoadTemplateDescription(fileName string) (string, error) { return "", err } - for i := range list { + for i := 0; i < len(list); i++ { if i > 0 { buf.WriteString(", ") } diff --git a/modules/log/color_console_other.go b/modules/log/color_console_other.go index 7505deef61..6573d093a5 100644 --- a/modules/log/color_console_other.go +++ b/modules/log/color_console_other.go @@ -39,7 +39,7 @@ func fileStatDevIno(file *os.File) (uint64, uint64, bool) { // Do a type conversion to uint64, because Dev isn't always uint64 // on every operating system + architecture combination. - return uint64(stat.Dev), stat.Ino, true //nolint:unconvert,nolintlint + return uint64(stat.Dev), stat.Ino, true //nolint:unconvert } func fileIsDevIno(file *os.File, dev, ino uint64) bool { diff --git a/modules/log/event_format.go b/modules/log/event_format.go index 70df2cbce2..6835a4ca5b 100644 --- a/modules/log/event_format.go +++ b/modules/log/event_format.go @@ -208,7 +208,7 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms } } if hasColorValue { - msg = fmt.Appendf(nil, msgFormat, msgArgs...) + msg = []byte(fmt.Sprintf(msgFormat, msgArgs...)) } } // try to reuse the pre-formatted simple text message @@ -227,8 +227,8 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms buf = append(buf, msg...) if event.Stacktrace != "" && mode.StacktraceLevel <= event.Level { - lines := bytes.SplitSeq([]byte(event.Stacktrace), []byte("\n")) - for line := range lines { + lines := bytes.Split([]byte(event.Stacktrace), []byte("\n")) + for _, line := range lines { buf = append(buf, "\n\t"...) buf = append(buf, line...) } diff --git a/modules/log/event_writer_base.go b/modules/log/event_writer_base.go index 561166b2ad..4de2b953c7 100644 --- a/modules/log/event_writer_base.go +++ b/modules/log/event_writer_base.go @@ -80,9 +80,7 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) { if pause := b.GetPauseChan(); pause != nil { select { case <-pause: - break case <-ctx.Done(): - break } } } @@ -180,7 +178,6 @@ func eventWriterStopWait(w EventWriter) { close(w.Base().Queue) select { case <-w.Base().stopped: - break case <-time.After(2 * time.Second): FallbackErrorf("unable to stop log writer %q in time, skip", w.GetWriterName()) } diff --git a/modules/log/event_writer_buffer.go b/modules/log/event_writer_buffer.go index a7557618a8..28857c2189 100644 --- a/modules/log/event_writer_buffer.go +++ b/modules/log/event_writer_buffer.go @@ -5,44 +5,18 @@ package log import ( "bytes" - "sync" ) -type EventWriterBuffer interface { - EventWriter - GetString() string -} - -type eventWriterBuffer struct { +type EventWriterBuffer struct { *EventWriterBaseImpl - buffer *bytes.Buffer - mu sync.RWMutex + Buffer *bytes.Buffer } -var _ EventWriterBuffer = (*eventWriterBuffer)(nil) +var _ EventWriter = (*EventWriterBuffer)(nil) -func (*eventWriterBuffer) Close() error { - return nil -} - -func (o *eventWriterBuffer) Write(p []byte) (n int, err error) { - o.mu.Lock() - defer o.mu.Unlock() - return o.buffer.Write(p) -} - -func (o *eventWriterBuffer) GetString() string { - o.mu.Lock() - defer o.mu.Unlock() - b := o.buffer.Bytes() - s := make([]byte, len(b)) - copy(s, b) - return string(s) -} - -func NewEventWriterBuffer(name string, mode WriterMode) EventWriter { - w := &eventWriterBuffer{EventWriterBaseImpl: NewEventWriterBase(name, "buffer", mode)} - w.buffer = new(bytes.Buffer) - w.OutputWriteCloser = w +func NewEventWriterBuffer(name string, mode WriterMode) *EventWriterBuffer { + w := &EventWriterBuffer{EventWriterBaseImpl: NewEventWriterBase(name, "buffer", mode)} + w.Buffer = new(bytes.Buffer) + w.OutputWriteCloser = nopCloser{w.Buffer} return w } diff --git a/modules/log/event_writer_buffer_test.go b/modules/log/event_writer_buffer_test.go index ac0759ad0b..d1e37c3673 100644 --- a/modules/log/event_writer_buffer_test.go +++ b/modules/log/event_writer_buffer_test.go @@ -29,7 +29,7 @@ func TestBufferLogger(t *testing.T) { MsgSimpleText: expected, }) logger.Close() - assert.Contains(t, bufferWriter.(log.EventWriterBuffer).GetString(), expected) + assert.Contains(t, bufferWriter.Buffer.String(), expected) } func TestBufferLoggerWithExclusion(t *testing.T) { @@ -41,7 +41,7 @@ func TestBufferLoggerWithExclusion(t *testing.T) { Level: level, Prefix: prefix, Exclusion: message, - }).(log.EventWriterBuffer) + }) logger := log.NewLoggerWithWriters(t.Context(), "test", bufferWriter) @@ -50,7 +50,7 @@ func TestBufferLoggerWithExclusion(t *testing.T) { MsgSimpleText: message, }) logger.Close() - assert.NotContains(t, bufferWriter.GetString(), message) + assert.NotContains(t, bufferWriter.Buffer.String(), message) } func TestBufferLoggerWithExpressionAndExclusion(t *testing.T) { @@ -64,7 +64,7 @@ func TestBufferLoggerWithExpressionAndExclusion(t *testing.T) { Prefix: prefix, Expression: expression, Exclusion: exclusion, - }).(log.EventWriterBuffer) + }) logger := log.NewLoggerWithWriters(t.Context(), "test", bufferWriter) @@ -74,6 +74,6 @@ func TestBufferLoggerWithExpressionAndExclusion(t *testing.T) { logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "none"}) logger.Close() - assert.Contains(t, bufferWriter.GetString(), "foo expression") - assert.NotContains(t, bufferWriter.GetString(), "bar") + assert.Contains(t, bufferWriter.Buffer.String(), "foo expression") + assert.NotContains(t, bufferWriter.Buffer.String(), "bar") } diff --git a/modules/log/event_writer_conn_test.go b/modules/log/event_writer_conn_test.go index 6d528a68d1..0cf447149a 100644 --- a/modules/log/event_writer_conn_test.go +++ b/modules/log/event_writer_conn_test.go @@ -63,9 +63,11 @@ func TestConnLogger(t *testing.T) { } expected := fmt.Sprintf("%s%s %s:%d:%s [%c] %s\n", prefix, dateString, event.Filename, event.Line, event.Caller, strings.ToUpper(event.Level.String())[0], event.MsgSimpleText) var wg sync.WaitGroup - wg.Go(func() { + wg.Add(1) + go func() { + defer wg.Done() listenReadAndClose(t, l, expected) - }) + }() logger.SendLogEvent(&event) wg.Wait() diff --git a/modules/log/flags.go b/modules/log/flags.go index c428d58a1d..1e4fe830c1 100644 --- a/modules/log/flags.go +++ b/modules/log/flags.go @@ -124,7 +124,7 @@ func FlagsFromString(from string, def ...uint32) Flags { return Flags{defined: true, flags: def[0]} } flags := uint32(0) - for flag := range strings.SplitSeq(strings.ToLower(from), ",") { + for _, flag := range strings.Split(strings.ToLower(from), ",") { flags |= flagFromString[strings.TrimSpace(flag)] } return Flags{defined: true, flags: flags} diff --git a/modules/log/level_test.go b/modules/log/level_test.go index 73e2355960..e6cacc723b 100644 --- a/modules/log/level_test.go +++ b/modules/log/level_test.go @@ -33,11 +33,11 @@ func TestLevelMarshalUnmarshalJSON(t *testing.T) { require.NoError(t, err) assert.Equal(t, INFO, testLevel.Level) - err = json.Unmarshal(fmt.Appendf(nil, `{"level":%d}`, 2), &testLevel) + err = json.Unmarshal([]byte(fmt.Sprintf(`{"level":%d}`, 2)), &testLevel) require.NoError(t, err) assert.Equal(t, INFO, testLevel.Level) - err = json.Unmarshal(fmt.Appendf(nil, `{"level":%d}`, 10012), &testLevel) + err = json.Unmarshal([]byte(fmt.Sprintf(`{"level":%d}`, 10012)), &testLevel) require.NoError(t, err) assert.Equal(t, INFO, testLevel.Level) @@ -52,5 +52,5 @@ func TestLevelMarshalUnmarshalJSON(t *testing.T) { } func makeTestLevelBytes(level string) []byte { - return fmt.Appendf(nil, `{"level":"%s"}`, level) + return []byte(fmt.Sprintf(`{"level":"%s"}`, level)) } diff --git a/modules/log/logger_impl.go b/modules/log/logger_impl.go index 9f25f9f7fb..76d7e6a821 100644 --- a/modules/log/logger_impl.go +++ b/modules/log/logger_impl.go @@ -58,7 +58,6 @@ func (l *LoggerImpl) SendLogEvent(event *Event) { } select { case w.Base().Queue <- formatted: - break default: bs, _ := json.Marshal(event) FallbackErrorf("log writer %q queue is full, event: %v", w.GetWriterName(), string(bs)) diff --git a/modules/log/logger_impl_test.go b/modules/log/logger_impl_test.go index 1d77ae0c25..59276a83f4 100644 --- a/modules/log/logger_impl_test.go +++ b/modules/log/logger_impl_test.go @@ -23,5 +23,5 @@ func TestLog(t *testing.T) { testGeneric(logger, "I'm the generic value!") logger.Close() - assert.Contains(t, bufferWriter.(EventWriterBuffer).GetString(), ".../logger_impl_test.go:13:testGeneric() [I] Just testing the logging of a generic function I'm the generic value!") + assert.Contains(t, bufferWriter.Buffer.String(), ".../logger_impl_test.go:13:testGeneric() [I] Just testing the logging of a generic function I'm the generic value!") } diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 22dcf93d75..dab6057cf4 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -80,8 +80,8 @@ func newFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca filePath := node.Data[m[6]:m[7]] hash := node.Data[m[8]:m[9]] urlFullSource := urlFull - if before, ok := strings.CutSuffix(filePath, "?display=source"); ok { - filePath = before + 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 } diff --git a/modules/markup/html.go b/modules/markup/html.go index 5bca4c4596..2d569725d7 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -11,7 +11,6 @@ import ( "path" "path/filepath" "regexp" - "slices" "strings" "sync" @@ -53,7 +52,7 @@ var ( // hashCurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae // Although SHA1 hashes are 40 chars long, SHA256 are 64, the regex matches the hash from 7 to 64 chars in length // so that abbreviated hash links can be used as well. This matches git and GitHub usability. - hashCurrentPattern = regexp.MustCompile(`(?:^|\s)[^\w\d]*([0-9a-f]{7,64})[^\w\d]*(?:\s|$)`) + hashCurrentPattern = regexp.MustCompile(`(?:^|\s)[^\w\d]{0,2}([0-9a-f]{7,64})[^\w\d]{0,2}(?:\s|$)`) // shortLinkPattern matches short but difficult to parse [[name|link|arg=test]] syntax shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`) @@ -125,7 +124,13 @@ func CustomLinkURLSchemes(schemes []string) { if !validScheme.MatchString(s) { continue } - without := slices.Contains(xurls.SchemesNoAuthority, s) + without := false + for _, sna := range xurls.SchemesNoAuthority { + if s == sna { + without = true + break + } + } if without { s += ":" } else { @@ -295,7 +300,7 @@ func RenderDescriptionHTML( descriptionLinkProcessor, emojiShortCodeProcessor, emojiProcessor, - }, escapeInlineCodeBlocks(content)) + }, content) } // RenderEmoji for when we want to just process emoji and shortcodes @@ -670,9 +675,9 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { // It makes page handling terrible, but we prefer GitHub syntax // And fall back to MediaWiki only when it is obvious from the look // Of text and link contents - sl := strings.SplitSeq(content, "|") - for v := range sl { - if found := strings.Contains(v, "="); !found { + sl := strings.Split(content, "|") + for _, v := range sl { + if equalPos := strings.IndexByte(v, '='); equalPos == -1 { // There is no equal in this argument; this is a mandatory arg if props["name"] == "" { if IsLinkStr(v) { @@ -694,8 +699,8 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { } else { // There is an equal; optional argument. - before, after, _ := strings.Cut(v, "=") - key, val := before, html.UnescapeString(after) + sep := strings.IndexByte(v, '=') + key, val := v[:sep], html.UnescapeString(v[sep+1:]) // When parsing HTML, x/net/html will change all quotes which are // not used for syntax into UTF-8 quotes. So checking val[0] won't @@ -737,7 +742,6 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { // fast path: empty string, ignore case "": // leave image as false - break case ".jpg", ".jpeg", ".png", ".tif", ".tiff", ".webp", ".gif", ".bmp", ".ico", ".svg": image = true } @@ -1143,7 +1147,7 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) { } // Ensure that every group (m[0]...m[9]) has a match - for i := range 10 { + for i := 0; i < 10; i++ { if m[i] == -1 { return } @@ -1539,9 +1543,3 @@ func optionalRepoSlugAndInstancePath(ctx *RenderContext, text *string, fullURL, } } } - -// escapeInlineCodeBlocks escapes HTML symbols in contents of Markdown inline code blocks -// to prevent clashing with HTML parsing -func escapeInlineCodeBlocks(input string) string { - return InlineCodeBlockRegex.ReplaceAllStringFunc(input, html.EscapeString) -} diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 570e8c5e68..752ffeba6f 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -486,9 +486,6 @@ func TestRegExp_hashCurrentPattern(t *testing.T) { ":abcd3ef", ".abcd3ef", " (abcd3ef). ", - "abcd3ef...", - "...abcd3ef", - "(!...abcd3ef", } falseTestCases := []string{ "test", @@ -498,7 +495,6 @@ func TestRegExp_hashCurrentPattern(t *testing.T) { "abcdefghijklmnopqrstuvwxyzabcdefghijklmO", "commit/abcdefd", "abcd3ef...defabcd", - "f..defabcd", } for _, testCase := range trueTestCases { @@ -615,30 +611,3 @@ func TestRegExp_shortLinkPattern(t *testing.T) { assert.False(t, shortLinkPattern.MatchString(testCase)) } } - -func TestRender_escapeInlineCodeBlocks(t *testing.T) { - test := func(input, expected string) { - result := escapeInlineCodeBlocks(input) - assert.Equal(t, expected, result) - } - test("``", - "`<test>`") - test("", - "") - test("`` ``", - "`<foo>` `<baz>`") - test(" `` ", - " `<bar>` ") - test(" ` ", - " ` ") - test(" `` `", - " `<bar>` `") - test(" `` `", - " `<bar>` `") - test(" `` ``", - " `<bar>` ``") - test("```", - "```") - test("``<`", - "``<`") -} diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 58e230b059..8305ada25e 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -57,10 +57,8 @@ func TestRender_Commits(t *testing.T) { } sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d" - shaWithExtra := "65f1bf27bc3bf70f64657658635e66094edbcb4d..." repo := markup.TestRepoURL commit := util.URLJoin(repo, "commit", sha) - commitWithExtra := util.URLJoin(repo, "commit", shaWithExtra) tree := util.URLJoin(repo, "tree", sha, "src") file := util.URLJoin(repo, "commit", sha, "example.txt") @@ -71,11 +69,9 @@ func TestRender_Commits(t *testing.T) { commitCompareWithHash := commitCompare + "#L2" test(sha, `

65f1bf27bc

`) - test(shaWithExtra, `

65f1bf27bc...

`) test(sha[:7], `

65f1bf2

`) test(sha[:39], `

65f1bf27bc

`) test(commit, `

65f1bf27bc

`) - test(commitWithExtra, `

65f1bf27bc...

`) test(tree, `

65f1bf27bc/src

`) test(file, `

65f1bf27bc/example.txt

`) @@ -809,7 +805,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -843,7 +839,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -922,7 +918,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -953,7 +949,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -986,7 +982,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1011,7 +1007,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1042,7 +1038,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1067,7 +1063,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1092,7 +1088,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1127,7 +1123,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ @@ -1160,7 +1156,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ @@ -1193,7 +1189,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ @@ -1228,7 +1224,7 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1329,42 +1325,3 @@ func TestRender_FilePreview(t *testing.T) { ) }) } - -func TestRenderDescriptionHTML(t *testing.T) { - defer test.MockVariableValue(&setting.AppURL, markup.TestAppURL)() - - test := func(input, expected string) { - buffer, err := markup.RenderDescriptionHTML(&markup.RenderContext{ - Ctx: git.DefaultContext, - }, input) - require.NoError(t, err) - assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) - } - - markup.InitializeSanitizer() - - test( - "https://www.example.com", - `https://www.example.com`) - - test( - "Example repository with `Arc`", - "Example repository with `Arc`") - - test( - "Example repository with `Arc` and tools.", - "Example repository with `Arc` and tools.") - - test( - "`Arc` implements", - "`Arc<Test>` implements") - - test( - "Arc is broken", - "Arc is broken") - - // issue #10770 - test( - "A weird alternative to `Arc>`", - "A weird alternative to `Arc<RwLock<T>>`") -} diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index aec710fd46..1ea3375ab5 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -90,8 +90,6 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa } case *ast.RawHTML: g.transformRawHTML(ctx, v, reader) - case *ast.FencedCodeBlock: - g.transformCodeblockLanguage(v, reader) } return ast.WalkContinue, nil }) diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index 9a112109dd..2b19e0f1c9 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -182,7 +182,10 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) } buf, _ = ExtractMetadataBytes(buf, rc) - metaLength := max(bufWithMetadataLength-len(buf), 0) + metaLength := bufWithMetadataLength - len(buf) + if metaLength < 0 { + metaLength = 0 + } rc.metaLength = metaLength pc.Set(markdownutil.RenderConfigKey, rc) diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index 3aee7a372d..82c2c7fe8c 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -319,7 +319,7 @@ func TestTotal_RenderWiki(t *testing.T) { answers := testAnswers(util.URLJoin(FullURL, "wiki"), util.URLJoin(FullURL, "wiki", "raw")) - for i := range sameCases { + for i := 0; i < len(sameCases); i++ { line, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ @@ -363,7 +363,7 @@ func TestTotal_RenderString(t *testing.T) { answers := testAnswers(util.URLJoin(FullURL, "src", "master"), util.URLJoin(FullURL, "media", "master")) - for i := range sameCases { + for i := 0; i < len(sameCases); i++ { line, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ @@ -1488,61 +1488,3 @@ func TestCallout(t *testing.T) {

Bad stuff is brewing here

`) } - -func TestCodeblockLanguageTransformation(t *testing.T) { - test := func(input, expected string) { - buffer, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, input) - require.NoError(t, err) - assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) - } - - // No transformation - test( - "```rust\n"+ - "fn main() {}\n"+ - "```", - `
fn main() {}
-
`) - - // Comma stripped - test( - "```rust,ignore\n"+ - "fn main() {}\n"+ - "```", - `
fn main() {}
-
`) - - // Pandoc stripping - // https://pandoc.org/MANUAL.html#extension-fenced_code_attributes - test( - "```haskell {.numberLines}\n"+ - "qsort [] = []\n"+ - "qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++\n"+ - " qsort (filter (>= x) xs)\n"+ - "```", - `
qsort []     = []
-qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++
-               qsort (filter (>= x) xs)
-
`) - - // Pandoc language extracting - // https://pandoc.org/MANUAL.html#extension-fenced_code_attributes - test( - "``` { #mycode .numberLines .haskell startFrom=\"100\" } \n"+ - "qsort [] = []\n"+ - "qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++\n"+ - " qsort (filter (>= x) xs)\n"+ - "```", - `
qsort []     = []
-qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++
-               qsort (filter (>= x) xs)
-
`) - - // No language identifier - test( - "```\n"+ - "fn main() {}\n"+ - "```", - `
fn main() {}
-
`) -} diff --git a/modules/markup/markdown/math/block_renderer.go b/modules/markup/markdown/math/block_renderer.go index d27318c623..84817ef1e4 100644 --- a/modules/markup/markdown/math/block_renderer.go +++ b/modules/markup/markdown/math/block_renderer.go @@ -24,7 +24,7 @@ func (r *BlockRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { func (r *BlockRenderer) writeLines(w util.BufWriter, source []byte, n gast.Node) { l := n.Lines().Len() - for i := range l { + for i := 0; i < l; i++ { line := n.Lines().At(i) _, _ = w.Write(util.EscapeHTML(line.Value(source))) } diff --git a/modules/markup/markdown/meta_test.go b/modules/markup/markdown/meta_test.go index 9345dd528a..aaf116ff20 100644 --- a/modules/markup/markdown/meta_test.go +++ b/modules/markup/markdown/meta_test.go @@ -63,7 +63,7 @@ func TestExtractMetadata(t *testing.T) { func TestExtractMetadataBytes(t *testing.T) { t.Run("ValidFrontAndBody", func(t *testing.T) { var meta IssueTemplate - body, err := ExtractMetadataBytes(fmt.Appendf(nil, "%s\n%s\n%s\n%s", sepTest, frontTest, sepTest, bodyTest), &meta) + body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s\n%s", sepTest, frontTest, sepTest, bodyTest)), &meta) require.NoError(t, err) assert.Equal(t, bodyTest, string(body)) assert.Equal(t, metaTest, meta) @@ -72,19 +72,19 @@ func TestExtractMetadataBytes(t *testing.T) { t.Run("NoFirstSeparator", func(t *testing.T) { var meta IssueTemplate - _, err := ExtractMetadataBytes(fmt.Appendf(nil, "%s\n%s\n%s", frontTest, sepTest, bodyTest), &meta) + _, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", frontTest, sepTest, bodyTest)), &meta) require.Error(t, err) }) t.Run("NoLastSeparator", func(t *testing.T) { var meta IssueTemplate - _, err := ExtractMetadataBytes(fmt.Appendf(nil, "%s\n%s\n%s", sepTest, frontTest, bodyTest), &meta) + _, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, bodyTest)), &meta) require.Error(t, err) }) t.Run("NoBody", func(t *testing.T) { var meta IssueTemplate - body, err := ExtractMetadataBytes(fmt.Appendf(nil, "%s\n%s\n%s", sepTest, frontTest, sepTest), &meta) + body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest)), &meta) require.NoError(t, err) assert.Empty(t, string(body)) assert.Equal(t, metaTest, meta) diff --git a/modules/markup/markdown/toc.go b/modules/markup/markdown/toc.go index 53add219f5..dbfab3e9dc 100644 --- a/modules/markup/markdown/toc.go +++ b/modules/markup/markdown/toc.go @@ -44,7 +44,7 @@ func createTOCNode(toc []markup.Header, lang string, detailsAttrs map[string]str } li := ast.NewListItem(currentLevel * 2) a := ast.NewLink() - a.Destination = fmt.Appendf(nil, "#%s", url.QueryEscape(header.ID)) + a.Destination = []byte(fmt.Sprintf("#%s", url.QueryEscape(header.ID))) a.AppendChild(a, ast.NewString([]byte(header.Text))) li.AppendChild(li, a) ul.AppendChild(ul, li) diff --git a/modules/markup/markdown/transform_codeblock_lang.go b/modules/markup/markdown/transform_codeblock_lang.go deleted file mode 100644 index f730265b15..0000000000 --- a/modules/markup/markdown/transform_codeblock_lang.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package markdown - -import ( - "bytes" - - "github.com/alecthomas/chroma/v2/lexers" - "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/text" -) - -func (g *ASTTransformer) transformCodeblockLanguage(v *ast.FencedCodeBlock, reader text.Reader) { - if v.Info == nil { - return - } - src := reader.Source() - info := v.Info.Segment.Value(src) - - // Parse Pandoc style attributes - // https://pandoc.org/MANUAL.html#extension-fenced_code_attributes - // - // For example, - // ```{.haskell .numberLines} - // ... - // ``` - // Should have a language of "haskell", not "{.haskell .numberLines}" - if trimmed := bytes.TrimSpace(info); bytes.HasPrefix(trimmed, []byte{'{'}) && bytes.HasSuffix(trimmed, []byte{'}'}) { - attributes := trimmed[1 : len(trimmed)-1] - for attribute := range bytes.SplitSeq(attributes, []byte{' '}) { - if class, found := bytes.CutPrefix(attribute, []byte{'.'}); found { - if lexer := lexers.Get(string(class)); lexer != nil { - lang := class - langInx := bytes.Index(info, lang) - start := v.Info.Segment.Start + langInx - end := start + len(lang) - v.Info = ast.NewTextSegment(text.NewSegment(start, end)) - return - } - } - } - return - } - - // Strip language after commas - // - // For example, - // ```rust,ignore - // ... - // ``` - // Should have a language of "rust", not "rust,ignore" - if i := bytes.IndexByte(info, ','); i != -1 { - start := v.Info.Segment.Start - v.Info = ast.NewTextSegment(text.NewSegment(start, start+i)) - } -} diff --git a/modules/markup/markdown/transform_heading.go b/modules/markup/markdown/transform_heading.go index 16779d5099..eedaf58556 100644 --- a/modules/markup/markdown/transform_heading.go +++ b/modules/markup/markdown/transform_heading.go @@ -17,7 +17,7 @@ import ( func (g *ASTTransformer) transformHeading(_ *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]markup.Header) { for _, attr := range v.Attributes() { if _, ok := attr.Value.([]byte); !ok { - v.SetAttribute(attr.Name, fmt.Appendf(nil, "%v", attr.Value)) + v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value))) } } txt := mdutil.Text(v, reader.Source()) diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index dd92ca90d0..b9d7b21db0 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -7,7 +7,6 @@ import ( "fmt" "html" "io" - "strconv" "strings" "forgejo.org/modules/highlight" @@ -160,16 +159,6 @@ func (r *Writer) resolveLink(node org.Node) string { switch l.Kind() { case "image", "video": base = r.Ctx.Links.ResolveMediaLink(r.Ctx.IsWiki) - case "regular": - // Convert line search syntax to line links - target, search, found := strings.Cut(link, "::") - if found { - if _, err := strconv.Atoi(search); err == nil { - link = target + "#L" + search - } else { - link = target - } - } } link = util.URLJoin(base, link) diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go index 2cd4eada22..71157dc7c7 100644 --- a/modules/markup/orgmode/orgmode_test.go +++ b/modules/markup/orgmode/orgmode_test.go @@ -89,43 +89,6 @@ func TestRender_BaseLinks(t *testing.T) { `

./src/

`) } -func TestRender_SearchSuffix(t *testing.T) { - setting.AppURL = AppURL - setting.AppSubURL = AppSubURL - - test := func(input, expected string) { - buffer, err := RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - Links: markup.Links{ - Base: setting.AppSubURL, - BranchPath: "branch/main", - }, - }, input) - require.NoError(t, err) - assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) - } - - // `::N` line search becomes an `#L` anchor. - test("[[./file.el::35][line 35]]", - `

line 35

`) - test("[[file:./file.el::35][line 35]]", - `

line 35

`) - - // Other search types are ignored. - test("[[./file.org::*Heading][heading]]", - `

heading

`) - test("[[./file.org::#custom-id][heading]]", - `

heading

`) - test("[[./file.el::/regex/][regex]]", - `

regex

`) - test("[[file:./file.el::][no search]]", - `

no search

`) - - // Absolute URLs that happen to contain `::` are unchanged. - test("[[https://example.com/foo::35][ext]]", - `

ext

`) -} - func TestRender_Media(t *testing.T) { setting.AppURL = AppURL setting.AppSubURL = AppSubURL @@ -190,8 +153,8 @@ func HelloWorld() { #+end_src `, `
// HelloWorld prints "Hello World"
-func HelloWorld() {
+func HelloWorld() {
 	fmt.Println("Hello World")
-}
+}
`) } diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 0a66caf1d5..05dd512815 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -198,28 +198,10 @@ func RegisterRenderer(renderer Renderer) { } } -// FullExtension returns the full extension of path, i.e. everything after and including -// the first period in the basename of path. -func FullExtension(path string) string { - _, extension, found := strings.Cut(strings.ToLower(filepath.Base(path)), ".") - if !found { - return "" - } - return "." + extension -} - -// GetRendererByExtension returns the most specific registered renderer for extension. -func GetRendererByExtension(extension string) Renderer { - _, extension, found := strings.Cut(extension, ".") - checkedExtensions := 0 - for found && checkedExtensions < 10 { - if renderer, ok := extRenderers["."+extension]; ok { - return renderer - } - checkedExtensions++ - _, extension, found = strings.Cut(extension, ".") - } - return nil +// GetRendererByFileName get renderer by filename +func GetRendererByFileName(filename string) Renderer { + extension := strings.ToLower(filepath.Ext(filename)) + return extRenderers[extension] } // GetRendererByType returns a renderer according type @@ -319,19 +301,23 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr _ = pw2.Close() }() - wg.Go(func() { + wg.Add(1) + go func() { err = donotpanic.SafeFuncWithError(func() error { return SanitizeReader(pr2, renderer.Name(), output) }) _ = pr2.Close() - }) + wg.Done() + }() } else { pw2 = nopCloser{output} } - wg.Go(func() { + wg.Add(1) + go func() { err = donotpanic.SafeFuncWithError(func() error { return postProcessOrCopy(ctx, renderer, pr, pw2) }) _ = pr.Close() _ = pw2.Close() - }) + wg.Done() + }() if err1 := renderer.Render(ctx, input, pw); err1 != nil { return err1 @@ -364,20 +350,6 @@ func renderByType(ctx *RenderContext, input io.Reader, output io.Writer) error { return ErrUnsupportedRenderType{ctx.Type} } -// ErrMissingExtension represents the error when a path does not have any extension. -type ErrMissingExtension struct { - Path string -} - -func IsErrMissingExtension(err error) bool { - _, ok := err.(ErrMissingExtension) - return ok -} - -func (err ErrMissingExtension) Error() string { - return fmt.Sprintf("path '%s' does not have an extension", err.Path) -} - // ErrUnsupportedRenderExtension represents the error when extension doesn't supported to render type ErrUnsupportedRenderExtension struct { Extension string @@ -393,11 +365,8 @@ func (err ErrUnsupportedRenderExtension) Error() string { } func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error { - extension := FullExtension(ctx.RelativePath) - if extension == "" { - return ErrMissingExtension{ctx.RelativePath} - } - if renderer := GetRendererByExtension(extension); renderer != nil { + extension := strings.ToLower(filepath.Ext(ctx.RelativePath)) + if renderer, ok := extRenderers[extension]; ok { if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() { if !ctx.InStandalonePage { // for an external render, it could only output its content in a standalone page @@ -412,7 +381,7 @@ func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error { // Type returns if markup format via the filename func Type(filename string) string { - if parser := GetRendererByExtension(FullExtension(filename)); parser != nil { + if parser := GetRendererByFileName(filename); parser != nil { return parser.Name() } return "" @@ -420,7 +389,7 @@ func Type(filename string) string { // IsMarkupFile reports whether file is a markup type file func IsMarkupFile(name, markup string) bool { - if parser := GetRendererByExtension(FullExtension(name)); parser != nil { + if parser := GetRendererByFileName(name); parser != nil { return parser.Name() == markup } return false diff --git a/modules/migration/retry_downloader.go b/modules/migration/retry_downloader.go index ef3d78a5d9..1cacf5f375 100644 --- a/modules/migration/retry_downloader.go +++ b/modules/migration/retry_downloader.go @@ -44,7 +44,6 @@ func (d *RetryDownloader) retry(work func() error) error { case <-d.ctx.Done(): return d.ctx.Err() case <-time.After(time.Second * time.Duration(d.RetryDelay)): - break } } return err diff --git a/modules/nosql/manager.go b/modules/nosql/manager.go index 748afe7587..7eea069e09 100644 --- a/modules/nosql/manager.go +++ b/modules/nosql/manager.go @@ -30,8 +30,6 @@ type Manager struct { // RedisClient is a subset of redis.UniversalClient, it exposes less methods // to avoid generating machine code for unused methods. New method definitions // should be copied from the definitions in the Redis library github.com/redis/go-redis. -// -//mockery:generate: true type RedisClient interface { // redis.GenericCmdable Del(ctx context.Context, keys ...string) *redis.IntCmd diff --git a/modules/nosql/mocks.go b/modules/nosql/mocks.go deleted file mode 100644 index 39d0d1fea3..0000000000 --- a/modules/nosql/mocks.go +++ /dev/null @@ -1,1240 +0,0 @@ -// Code generated by mockery; DO NOT EDIT. -// github.com/vektra/mockery -// template: testify - -package nosql - -import ( - "context" - "time" - - "github.com/redis/go-redis/v9" - mock "github.com/stretchr/testify/mock" -) - -// NewMockRedisClient creates a new instance of MockRedisClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockRedisClient(t interface { - mock.TestingT - Cleanup(func()) -}, -) *MockRedisClient { - mock := &MockRedisClient{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} - -// MockRedisClient is an autogenerated mock type for the RedisClient type -type MockRedisClient struct { - mock.Mock -} - -type MockRedisClient_Expecter struct { - mock *mock.Mock -} - -func (_m *MockRedisClient) EXPECT() *MockRedisClient_Expecter { - return &MockRedisClient_Expecter{mock: &_m.Mock} -} - -// Close provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) Close() error { - ret := _mock.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if returnFunc, ok := ret.Get(0).(func() error); ok { - r0 = returnFunc() - } else { - r0 = ret.Error(0) - } - return r0 -} - -// MockRedisClient_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' -type MockRedisClient_Close_Call struct { - *mock.Call -} - -// Close is a helper method to define mock.On call -func (_e *MockRedisClient_Expecter) Close() *MockRedisClient_Close_Call { - return &MockRedisClient_Close_Call{Call: _e.mock.On("Close")} -} - -func (_c *MockRedisClient_Close_Call) Run(run func()) *MockRedisClient_Close_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *MockRedisClient_Close_Call) Return(err error) *MockRedisClient_Close_Call { - _c.Call.Return(err) - return _c -} - -func (_c *MockRedisClient_Close_Call) RunAndReturn(run func() error) *MockRedisClient_Close_Call { - _c.Call.Return(run) - return _c -} - -// DBSize provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) DBSize(ctx context.Context) *redis.IntCmd { - ret := _mock.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for DBSize") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context) *redis.IntCmd); ok { - r0 = returnFunc(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_DBSize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DBSize' -type MockRedisClient_DBSize_Call struct { - *mock.Call -} - -// DBSize is a helper method to define mock.On call -// - ctx context.Context -func (_e *MockRedisClient_Expecter) DBSize(ctx any) *MockRedisClient_DBSize_Call { - return &MockRedisClient_DBSize_Call{Call: _e.mock.On("DBSize", ctx)} -} - -func (_c *MockRedisClient_DBSize_Call) Run(run func(ctx context.Context)) *MockRedisClient_DBSize_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *MockRedisClient_DBSize_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_DBSize_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_DBSize_Call) RunAndReturn(run func(ctx context.Context) *redis.IntCmd) *MockRedisClient_DBSize_Call { - _c.Call.Return(run) - return _c -} - -// Decr provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) Decr(ctx context.Context, key string) *redis.IntCmd { - ret := _mock.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for Decr") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string) *redis.IntCmd); ok { - r0 = returnFunc(ctx, key) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_Decr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Decr' -type MockRedisClient_Decr_Call struct { - *mock.Call -} - -// Decr is a helper method to define mock.On call -// - ctx context.Context -// - key string -func (_e *MockRedisClient_Expecter) Decr(ctx, key any) *MockRedisClient_Decr_Call { - return &MockRedisClient_Decr_Call{Call: _e.mock.On("Decr", ctx, key)} -} - -func (_c *MockRedisClient_Decr_Call) Run(run func(ctx context.Context, key string)) *MockRedisClient_Decr_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *MockRedisClient_Decr_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_Decr_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_Decr_Call) RunAndReturn(run func(ctx context.Context, key string) *redis.IntCmd) *MockRedisClient_Decr_Call { - _c.Call.Return(run) - return _c -} - -// Del provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) Del(ctx context.Context, keys ...string) *redis.IntCmd { - var tmpRet mock.Arguments - if len(keys) > 0 { - tmpRet = _mock.Called(ctx, keys) - } else { - tmpRet = _mock.Called(ctx) - } - ret := tmpRet - - if len(ret) == 0 { - panic("no return value specified for Del") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, ...string) *redis.IntCmd); ok { - r0 = returnFunc(ctx, keys...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_Del_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Del' -type MockRedisClient_Del_Call struct { - *mock.Call -} - -// Del is a helper method to define mock.On call -// - ctx context.Context -// - keys ...string -func (_e *MockRedisClient_Expecter) Del(ctx any, keys ...any) *MockRedisClient_Del_Call { - return &MockRedisClient_Del_Call{Call: _e.mock.On("Del", - append([]any{ctx}, keys...)...)} -} - -func (_c *MockRedisClient_Del_Call) Run(run func(ctx context.Context, keys ...string)) *MockRedisClient_Del_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 []string - var variadicArgs []string - if len(args) > 1 { - variadicArgs = args[1].([]string) - } - arg1 = variadicArgs - run( - arg0, - arg1..., - ) - }) - return _c -} - -func (_c *MockRedisClient_Del_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_Del_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_Del_Call) RunAndReturn(run func(ctx context.Context, keys ...string) *redis.IntCmd) *MockRedisClient_Del_Call { - _c.Call.Return(run) - return _c -} - -// Exists provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) Exists(ctx context.Context, keys ...string) *redis.IntCmd { - var tmpRet mock.Arguments - if len(keys) > 0 { - tmpRet = _mock.Called(ctx, keys) - } else { - tmpRet = _mock.Called(ctx) - } - ret := tmpRet - - if len(ret) == 0 { - panic("no return value specified for Exists") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, ...string) *redis.IntCmd); ok { - r0 = returnFunc(ctx, keys...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_Exists_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Exists' -type MockRedisClient_Exists_Call struct { - *mock.Call -} - -// Exists is a helper method to define mock.On call -// - ctx context.Context -// - keys ...string -func (_e *MockRedisClient_Expecter) Exists(ctx any, keys ...any) *MockRedisClient_Exists_Call { - return &MockRedisClient_Exists_Call{Call: _e.mock.On("Exists", - append([]any{ctx}, keys...)...)} -} - -func (_c *MockRedisClient_Exists_Call) Run(run func(ctx context.Context, keys ...string)) *MockRedisClient_Exists_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 []string - var variadicArgs []string - if len(args) > 1 { - variadicArgs = args[1].([]string) - } - arg1 = variadicArgs - run( - arg0, - arg1..., - ) - }) - return _c -} - -func (_c *MockRedisClient_Exists_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_Exists_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_Exists_Call) RunAndReturn(run func(ctx context.Context, keys ...string) *redis.IntCmd) *MockRedisClient_Exists_Call { - _c.Call.Return(run) - return _c -} - -// FlushDB provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) FlushDB(ctx context.Context) *redis.StatusCmd { - ret := _mock.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for FlushDB") - } - - var r0 *redis.StatusCmd - if returnFunc, ok := ret.Get(0).(func(context.Context) *redis.StatusCmd); ok { - r0 = returnFunc(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.StatusCmd) - } - } - return r0 -} - -// MockRedisClient_FlushDB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FlushDB' -type MockRedisClient_FlushDB_Call struct { - *mock.Call -} - -// FlushDB is a helper method to define mock.On call -// - ctx context.Context -func (_e *MockRedisClient_Expecter) FlushDB(ctx any) *MockRedisClient_FlushDB_Call { - return &MockRedisClient_FlushDB_Call{Call: _e.mock.On("FlushDB", ctx)} -} - -func (_c *MockRedisClient_FlushDB_Call) Run(run func(ctx context.Context)) *MockRedisClient_FlushDB_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *MockRedisClient_FlushDB_Call) Return(statusCmd *redis.StatusCmd) *MockRedisClient_FlushDB_Call { - _c.Call.Return(statusCmd) - return _c -} - -func (_c *MockRedisClient_FlushDB_Call) RunAndReturn(run func(ctx context.Context) *redis.StatusCmd) *MockRedisClient_FlushDB_Call { - _c.Call.Return(run) - return _c -} - -// Get provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) Get(ctx context.Context, key string) *redis.StringCmd { - ret := _mock.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for Get") - } - - var r0 *redis.StringCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string) *redis.StringCmd); ok { - r0 = returnFunc(ctx, key) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.StringCmd) - } - } - return r0 -} - -// MockRedisClient_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' -type MockRedisClient_Get_Call struct { - *mock.Call -} - -// Get is a helper method to define mock.On call -// - ctx context.Context -// - key string -func (_e *MockRedisClient_Expecter) Get(ctx, key any) *MockRedisClient_Get_Call { - return &MockRedisClient_Get_Call{Call: _e.mock.On("Get", ctx, key)} -} - -func (_c *MockRedisClient_Get_Call) Run(run func(ctx context.Context, key string)) *MockRedisClient_Get_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *MockRedisClient_Get_Call) Return(stringCmd *redis.StringCmd) *MockRedisClient_Get_Call { - _c.Call.Return(stringCmd) - return _c -} - -func (_c *MockRedisClient_Get_Call) RunAndReturn(run func(ctx context.Context, key string) *redis.StringCmd) *MockRedisClient_Get_Call { - _c.Call.Return(run) - return _c -} - -// HDel provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) HDel(ctx context.Context, key string, fields ...string) *redis.IntCmd { - var tmpRet mock.Arguments - if len(fields) > 0 { - tmpRet = _mock.Called(ctx, key, fields) - } else { - tmpRet = _mock.Called(ctx, key) - } - ret := tmpRet - - if len(ret) == 0 { - panic("no return value specified for HDel") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string, ...string) *redis.IntCmd); ok { - r0 = returnFunc(ctx, key, fields...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_HDel_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HDel' -type MockRedisClient_HDel_Call struct { - *mock.Call -} - -// HDel is a helper method to define mock.On call -// - ctx context.Context -// - key string -// - fields ...string -func (_e *MockRedisClient_Expecter) HDel(ctx, key any, fields ...any) *MockRedisClient_HDel_Call { - return &MockRedisClient_HDel_Call{Call: _e.mock.On("HDel", - append([]any{ctx, key}, fields...)...)} -} - -func (_c *MockRedisClient_HDel_Call) Run(run func(ctx context.Context, key string, fields ...string)) *MockRedisClient_HDel_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - var arg2 []string - var variadicArgs []string - if len(args) > 2 { - variadicArgs = args[2].([]string) - } - arg2 = variadicArgs - run( - arg0, - arg1, - arg2..., - ) - }) - return _c -} - -func (_c *MockRedisClient_HDel_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_HDel_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_HDel_Call) RunAndReturn(run func(ctx context.Context, key string, fields ...string) *redis.IntCmd) *MockRedisClient_HDel_Call { - _c.Call.Return(run) - return _c -} - -// HKeys provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) HKeys(ctx context.Context, key string) *redis.StringSliceCmd { - ret := _mock.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for HKeys") - } - - var r0 *redis.StringSliceCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string) *redis.StringSliceCmd); ok { - r0 = returnFunc(ctx, key) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.StringSliceCmd) - } - } - return r0 -} - -// MockRedisClient_HKeys_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HKeys' -type MockRedisClient_HKeys_Call struct { - *mock.Call -} - -// HKeys is a helper method to define mock.On call -// - ctx context.Context -// - key string -func (_e *MockRedisClient_Expecter) HKeys(ctx, key any) *MockRedisClient_HKeys_Call { - return &MockRedisClient_HKeys_Call{Call: _e.mock.On("HKeys", ctx, key)} -} - -func (_c *MockRedisClient_HKeys_Call) Run(run func(ctx context.Context, key string)) *MockRedisClient_HKeys_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *MockRedisClient_HKeys_Call) Return(stringSliceCmd *redis.StringSliceCmd) *MockRedisClient_HKeys_Call { - _c.Call.Return(stringSliceCmd) - return _c -} - -func (_c *MockRedisClient_HKeys_Call) RunAndReturn(run func(ctx context.Context, key string) *redis.StringSliceCmd) *MockRedisClient_HKeys_Call { - _c.Call.Return(run) - return _c -} - -// HSet provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) HSet(ctx context.Context, key string, values ...any) *redis.IntCmd { - var tmpRet mock.Arguments - if len(values) > 0 { - tmpRet = _mock.Called(ctx, key, values) - } else { - tmpRet = _mock.Called(ctx, key) - } - ret := tmpRet - - if len(ret) == 0 { - panic("no return value specified for HSet") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string, ...any) *redis.IntCmd); ok { - r0 = returnFunc(ctx, key, values...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_HSet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HSet' -type MockRedisClient_HSet_Call struct { - *mock.Call -} - -// HSet is a helper method to define mock.On call -// - ctx context.Context -// - key string -// - values ...any -func (_e *MockRedisClient_Expecter) HSet(ctx, key any, values ...any) *MockRedisClient_HSet_Call { - return &MockRedisClient_HSet_Call{Call: _e.mock.On("HSet", - append([]any{ctx, key}, values...)...)} -} - -func (_c *MockRedisClient_HSet_Call) Run(run func(ctx context.Context, key string, values ...any)) *MockRedisClient_HSet_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - var arg2 []any - var variadicArgs []any - if len(args) > 2 { - variadicArgs = args[2].([]any) - } - arg2 = variadicArgs - run( - arg0, - arg1, - arg2..., - ) - }) - return _c -} - -func (_c *MockRedisClient_HSet_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_HSet_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_HSet_Call) RunAndReturn(run func(ctx context.Context, key string, values ...any) *redis.IntCmd) *MockRedisClient_HSet_Call { - _c.Call.Return(run) - return _c -} - -// Incr provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) Incr(ctx context.Context, key string) *redis.IntCmd { - ret := _mock.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for Incr") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string) *redis.IntCmd); ok { - r0 = returnFunc(ctx, key) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_Incr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Incr' -type MockRedisClient_Incr_Call struct { - *mock.Call -} - -// Incr is a helper method to define mock.On call -// - ctx context.Context -// - key string -func (_e *MockRedisClient_Expecter) Incr(ctx, key any) *MockRedisClient_Incr_Call { - return &MockRedisClient_Incr_Call{Call: _e.mock.On("Incr", ctx, key)} -} - -func (_c *MockRedisClient_Incr_Call) Run(run func(ctx context.Context, key string)) *MockRedisClient_Incr_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *MockRedisClient_Incr_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_Incr_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_Incr_Call) RunAndReturn(run func(ctx context.Context, key string) *redis.IntCmd) *MockRedisClient_Incr_Call { - _c.Call.Return(run) - return _c -} - -// LLen provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) LLen(ctx context.Context, key string) *redis.IntCmd { - ret := _mock.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for LLen") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string) *redis.IntCmd); ok { - r0 = returnFunc(ctx, key) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_LLen_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LLen' -type MockRedisClient_LLen_Call struct { - *mock.Call -} - -// LLen is a helper method to define mock.On call -// - ctx context.Context -// - key string -func (_e *MockRedisClient_Expecter) LLen(ctx, key any) *MockRedisClient_LLen_Call { - return &MockRedisClient_LLen_Call{Call: _e.mock.On("LLen", ctx, key)} -} - -func (_c *MockRedisClient_LLen_Call) Run(run func(ctx context.Context, key string)) *MockRedisClient_LLen_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *MockRedisClient_LLen_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_LLen_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_LLen_Call) RunAndReturn(run func(ctx context.Context, key string) *redis.IntCmd) *MockRedisClient_LLen_Call { - _c.Call.Return(run) - return _c -} - -// LPop provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) LPop(ctx context.Context, key string) *redis.StringCmd { - ret := _mock.Called(ctx, key) - - if len(ret) == 0 { - panic("no return value specified for LPop") - } - - var r0 *redis.StringCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string) *redis.StringCmd); ok { - r0 = returnFunc(ctx, key) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.StringCmd) - } - } - return r0 -} - -// MockRedisClient_LPop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LPop' -type MockRedisClient_LPop_Call struct { - *mock.Call -} - -// LPop is a helper method to define mock.On call -// - ctx context.Context -// - key string -func (_e *MockRedisClient_Expecter) LPop(ctx, key any) *MockRedisClient_LPop_Call { - return &MockRedisClient_LPop_Call{Call: _e.mock.On("LPop", ctx, key)} -} - -func (_c *MockRedisClient_LPop_Call) Run(run func(ctx context.Context, key string)) *MockRedisClient_LPop_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - run( - arg0, - arg1, - ) - }) - return _c -} - -func (_c *MockRedisClient_LPop_Call) Return(stringCmd *redis.StringCmd) *MockRedisClient_LPop_Call { - _c.Call.Return(stringCmd) - return _c -} - -func (_c *MockRedisClient_LPop_Call) RunAndReturn(run func(ctx context.Context, key string) *redis.StringCmd) *MockRedisClient_LPop_Call { - _c.Call.Return(run) - return _c -} - -// Ping provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) Ping(ctx context.Context) *redis.StatusCmd { - ret := _mock.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for Ping") - } - - var r0 *redis.StatusCmd - if returnFunc, ok := ret.Get(0).(func(context.Context) *redis.StatusCmd); ok { - r0 = returnFunc(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.StatusCmd) - } - } - return r0 -} - -// MockRedisClient_Ping_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Ping' -type MockRedisClient_Ping_Call struct { - *mock.Call -} - -// Ping is a helper method to define mock.On call -// - ctx context.Context -func (_e *MockRedisClient_Expecter) Ping(ctx any) *MockRedisClient_Ping_Call { - return &MockRedisClient_Ping_Call{Call: _e.mock.On("Ping", ctx)} -} - -func (_c *MockRedisClient_Ping_Call) Run(run func(ctx context.Context)) *MockRedisClient_Ping_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - run( - arg0, - ) - }) - return _c -} - -func (_c *MockRedisClient_Ping_Call) Return(statusCmd *redis.StatusCmd) *MockRedisClient_Ping_Call { - _c.Call.Return(statusCmd) - return _c -} - -func (_c *MockRedisClient_Ping_Call) RunAndReturn(run func(ctx context.Context) *redis.StatusCmd) *MockRedisClient_Ping_Call { - _c.Call.Return(run) - return _c -} - -// RPush provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) RPush(ctx context.Context, key string, values ...any) *redis.IntCmd { - var tmpRet mock.Arguments - if len(values) > 0 { - tmpRet = _mock.Called(ctx, key, values) - } else { - tmpRet = _mock.Called(ctx, key) - } - ret := tmpRet - - if len(ret) == 0 { - panic("no return value specified for RPush") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string, ...any) *redis.IntCmd); ok { - r0 = returnFunc(ctx, key, values...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_RPush_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RPush' -type MockRedisClient_RPush_Call struct { - *mock.Call -} - -// RPush is a helper method to define mock.On call -// - ctx context.Context -// - key string -// - values ...any -func (_e *MockRedisClient_Expecter) RPush(ctx, key any, values ...any) *MockRedisClient_RPush_Call { - return &MockRedisClient_RPush_Call{Call: _e.mock.On("RPush", - append([]any{ctx, key}, values...)...)} -} - -func (_c *MockRedisClient_RPush_Call) Run(run func(ctx context.Context, key string, values ...any)) *MockRedisClient_RPush_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - var arg2 []any - var variadicArgs []any - if len(args) > 2 { - variadicArgs = args[2].([]any) - } - arg2 = variadicArgs - run( - arg0, - arg1, - arg2..., - ) - }) - return _c -} - -func (_c *MockRedisClient_RPush_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_RPush_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_RPush_Call) RunAndReturn(run func(ctx context.Context, key string, values ...any) *redis.IntCmd) *MockRedisClient_RPush_Call { - _c.Call.Return(run) - return _c -} - -// SAdd provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) SAdd(ctx context.Context, key string, members ...any) *redis.IntCmd { - var tmpRet mock.Arguments - if len(members) > 0 { - tmpRet = _mock.Called(ctx, key, members) - } else { - tmpRet = _mock.Called(ctx, key) - } - ret := tmpRet - - if len(ret) == 0 { - panic("no return value specified for SAdd") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string, ...any) *redis.IntCmd); ok { - r0 = returnFunc(ctx, key, members...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_SAdd_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SAdd' -type MockRedisClient_SAdd_Call struct { - *mock.Call -} - -// SAdd is a helper method to define mock.On call -// - ctx context.Context -// - key string -// - members ...any -func (_e *MockRedisClient_Expecter) SAdd(ctx, key any, members ...any) *MockRedisClient_SAdd_Call { - return &MockRedisClient_SAdd_Call{Call: _e.mock.On("SAdd", - append([]any{ctx, key}, members...)...)} -} - -func (_c *MockRedisClient_SAdd_Call) Run(run func(ctx context.Context, key string, members ...any)) *MockRedisClient_SAdd_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - var arg2 []any - var variadicArgs []any - if len(args) > 2 { - variadicArgs = args[2].([]any) - } - arg2 = variadicArgs - run( - arg0, - arg1, - arg2..., - ) - }) - return _c -} - -func (_c *MockRedisClient_SAdd_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_SAdd_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_SAdd_Call) RunAndReturn(run func(ctx context.Context, key string, members ...any) *redis.IntCmd) *MockRedisClient_SAdd_Call { - _c.Call.Return(run) - return _c -} - -// SIsMember provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) SIsMember(ctx context.Context, key string, member any) *redis.BoolCmd { - ret := _mock.Called(ctx, key, member) - - if len(ret) == 0 { - panic("no return value specified for SIsMember") - } - - var r0 *redis.BoolCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string, any) *redis.BoolCmd); ok { - r0 = returnFunc(ctx, key, member) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.BoolCmd) - } - } - return r0 -} - -// MockRedisClient_SIsMember_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SIsMember' -type MockRedisClient_SIsMember_Call struct { - *mock.Call -} - -// SIsMember is a helper method to define mock.On call -// - ctx context.Context -// - key string -// - member any -func (_e *MockRedisClient_Expecter) SIsMember(ctx, key, member any) *MockRedisClient_SIsMember_Call { - return &MockRedisClient_SIsMember_Call{Call: _e.mock.On("SIsMember", ctx, key, member)} -} - -func (_c *MockRedisClient_SIsMember_Call) Run(run func(ctx context.Context, key string, member any)) *MockRedisClient_SIsMember_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - var arg2 any - if args[2] != nil { - arg2 = args[2].(any) - } - run( - arg0, - arg1, - arg2, - ) - }) - return _c -} - -func (_c *MockRedisClient_SIsMember_Call) Return(boolCmd *redis.BoolCmd) *MockRedisClient_SIsMember_Call { - _c.Call.Return(boolCmd) - return _c -} - -func (_c *MockRedisClient_SIsMember_Call) RunAndReturn(run func(ctx context.Context, key string, member any) *redis.BoolCmd) *MockRedisClient_SIsMember_Call { - _c.Call.Return(run) - return _c -} - -// SRem provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) SRem(ctx context.Context, key string, members ...any) *redis.IntCmd { - var tmpRet mock.Arguments - if len(members) > 0 { - tmpRet = _mock.Called(ctx, key, members) - } else { - tmpRet = _mock.Called(ctx, key) - } - ret := tmpRet - - if len(ret) == 0 { - panic("no return value specified for SRem") - } - - var r0 *redis.IntCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string, ...any) *redis.IntCmd); ok { - r0 = returnFunc(ctx, key, members...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.IntCmd) - } - } - return r0 -} - -// MockRedisClient_SRem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SRem' -type MockRedisClient_SRem_Call struct { - *mock.Call -} - -// SRem is a helper method to define mock.On call -// - ctx context.Context -// - key string -// - members ...any -func (_e *MockRedisClient_Expecter) SRem(ctx, key any, members ...any) *MockRedisClient_SRem_Call { - return &MockRedisClient_SRem_Call{Call: _e.mock.On("SRem", - append([]any{ctx, key}, members...)...)} -} - -func (_c *MockRedisClient_SRem_Call) Run(run func(ctx context.Context, key string, members ...any)) *MockRedisClient_SRem_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - var arg2 []any - var variadicArgs []any - if len(args) > 2 { - variadicArgs = args[2].([]any) - } - arg2 = variadicArgs - run( - arg0, - arg1, - arg2..., - ) - }) - return _c -} - -func (_c *MockRedisClient_SRem_Call) Return(intCmd *redis.IntCmd) *MockRedisClient_SRem_Call { - _c.Call.Return(intCmd) - return _c -} - -func (_c *MockRedisClient_SRem_Call) RunAndReturn(run func(ctx context.Context, key string, members ...any) *redis.IntCmd) *MockRedisClient_SRem_Call { - _c.Call.Return(run) - return _c -} - -// Set provides a mock function for the type MockRedisClient -func (_mock *MockRedisClient) Set(ctx context.Context, key string, value any, expiration time.Duration) *redis.StatusCmd { - ret := _mock.Called(ctx, key, value, expiration) - - if len(ret) == 0 { - panic("no return value specified for Set") - } - - var r0 *redis.StatusCmd - if returnFunc, ok := ret.Get(0).(func(context.Context, string, any, time.Duration) *redis.StatusCmd); ok { - r0 = returnFunc(ctx, key, value, expiration) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*redis.StatusCmd) - } - } - return r0 -} - -// MockRedisClient_Set_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Set' -type MockRedisClient_Set_Call struct { - *mock.Call -} - -// Set is a helper method to define mock.On call -// - ctx context.Context -// - key string -// - value any -// - expiration time.Duration -func (_e *MockRedisClient_Expecter) Set(ctx, key, value, expiration any) *MockRedisClient_Set_Call { - return &MockRedisClient_Set_Call{Call: _e.mock.On("Set", ctx, key, value, expiration)} -} - -func (_c *MockRedisClient_Set_Call) Run(run func(ctx context.Context, key string, value any, expiration time.Duration)) *MockRedisClient_Set_Call { - _c.Call.Run(func(args mock.Arguments) { - var arg0 context.Context - if args[0] != nil { - arg0 = args[0].(context.Context) - } - var arg1 string - if args[1] != nil { - arg1 = args[1].(string) - } - var arg2 any - if args[2] != nil { - arg2 = args[2].(any) - } - var arg3 time.Duration - if args[3] != nil { - arg3 = args[3].(time.Duration) - } - run( - arg0, - arg1, - arg2, - arg3, - ) - }) - return _c -} - -func (_c *MockRedisClient_Set_Call) Return(statusCmd *redis.StatusCmd) *MockRedisClient_Set_Call { - _c.Call.Return(statusCmd) - return _c -} - -func (_c *MockRedisClient_Set_Call) RunAndReturn(run func(ctx context.Context, key string, value any, expiration time.Duration) *redis.StatusCmd) *MockRedisClient_Set_Call { - _c.Call.Return(run) - return _c -} diff --git a/modules/optional/option.go b/modules/optional/option.go index 2793f985e8..ccbad259c2 100644 --- a/modules/optional/option.go +++ b/modules/optional/option.go @@ -3,14 +3,7 @@ package optional -import ( - "database/sql" - "database/sql/driver" - "reflect" - "strconv" - - "xorm.io/xorm/schemas" -) +import "strconv" type Option[T any] []T @@ -41,17 +34,9 @@ func (o Option[T]) Has() bool { return o != nil } -func (o Option[T]) Get() (has bool, value T) { - if o != nil { - has = true - value = o[0] - } - return has, value -} - -func (o Option[T]) ValueOrZeroValue() T { - var zeroValue T - return o.ValueOrDefault(zeroValue) +func (o Option[T]) Value() T { + var zero T + return o.ValueOrDefault(zero) } func (o Option[T]) ValueOrDefault(v T) T { @@ -69,45 +54,3 @@ func ParseBool(s string) Option[bool] { } return Some(v) } - -// Option[T] can be used in an xorm bean as a field type for a nullable column. Multiple interfaces must be implemented -// for this to work correctly and won't be checked at compile-time of the bean struct, so they're asserted here in case -// the interface definitions change: -var ( - _ sql.Scanner = (*Option[bool])(nil) // read data from DB - _ driver.Valuer = None[bool]() // write data to DB - _ schemas.SQLTypeDelegator = None[bool]() // represent column field type correctly -) - -// Convert database data into an Option[T]. sql.Null[T] has all the necessary logic to perform Value(), so it is used as -// an implementation. -func (o *Option[T]) Scan(value any) error { - var n sql.Null[T] - if err := n.Scan(value); err != nil { - return err - } - if n.Valid { - *o = Some(n.V) - } else { - *o = None[T]() - } - return nil -} - -// Convert Option[T] into the necessary database data to represent it. sql.Null[T] has all the necessary logic to -// perform Value(), so it is used as an implementation. -func (o Option[T]) Value() (driver.Value, error) { - var n sql.Null[T] - if o.Has() { - n.V = o[0] - n.Valid = true - } else { - n.Valid = false - } - return n.Value() -} - -// Make xorm use whatever SQLType is appropriate for T to represent Option[T] in the database table -func (o Option[T]) DelegateSQLType() reflect.Type { - return reflect.TypeFor[T]() -} diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go index 9d9d839746..a674caf633 100644 --- a/modules/optional/option_test.go +++ b/modules/optional/option_test.go @@ -9,33 +9,32 @@ import ( "forgejo.org/modules/optional" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestOption(t *testing.T) { var uninitialized optional.Option[int] assert.False(t, uninitialized.Has()) - assert.Equal(t, int(0), uninitialized.ValueOrZeroValue()) + assert.Equal(t, int(0), uninitialized.Value()) assert.Equal(t, int(1), uninitialized.ValueOrDefault(1)) none := optional.None[int]() assert.False(t, none.Has()) - assert.Equal(t, int(0), none.ValueOrZeroValue()) + assert.Equal(t, int(0), none.Value()) assert.Equal(t, int(1), none.ValueOrDefault(1)) some := optional.Some(1) assert.True(t, some.Has()) - assert.Equal(t, int(1), some.ValueOrZeroValue()) + assert.Equal(t, int(1), some.Value()) assert.Equal(t, int(1), some.ValueOrDefault(2)) noneBool := optional.None[bool]() assert.False(t, noneBool.Has()) - assert.False(t, noneBool.ValueOrZeroValue()) + assert.False(t, noneBool.Value()) assert.True(t, noneBool.ValueOrDefault(true)) someBool := optional.Some(true) assert.True(t, someBool.Has()) - assert.True(t, someBool.ValueOrZeroValue()) + assert.True(t, someBool.Value()) assert.True(t, someBool.ValueOrDefault(false)) var ptr *int @@ -44,22 +43,19 @@ func TestOption(t *testing.T) { int1 := 1 opt1 := optional.FromPtr(&int1) assert.True(t, opt1.Has()) - _, v := opt1.Get() - assert.Equal(t, int(1), v) + assert.Equal(t, int(1), opt1.Value()) assert.False(t, optional.FromNonDefault("").Has()) opt2 := optional.FromNonDefault("test") assert.True(t, opt2.Has()) - _, vStr := opt2.Get() - assert.Equal(t, "test", vStr) + assert.Equal(t, "test", opt2.Value()) assert.False(t, optional.FromNonDefault(0).Has()) opt3 := optional.FromNonDefault(1) assert.True(t, opt3.Has()) - _, v = opt3.Get() - assert.Equal(t, int(1), v) + assert.Equal(t, int(1), opt3.Value()) } func Test_ParseBool(t *testing.T) { @@ -74,52 +70,3 @@ func Test_ParseBool(t *testing.T) { assert.Equal(t, optional.Some(true), optional.ParseBool("t")) assert.Equal(t, optional.Some(true), optional.ParseBool("True")) } - -func roundtrip[T any](t *testing.T, orig optional.Option[T]) { - // invoke (driver.Valuer).Value to get a DB value - dbValue, err := orig.Value() - require.NoError(t, err) - - // invoke (sql.Scanner).Scan to read the DB value - var scanned optional.Option[T] - err = scanned.Scan(dbValue) - require.NoError(t, err) - - hasOrig, origValue := orig.Get() - hasScanned, scannedValue := scanned.Get() - - if hasOrig { - require.True(t, hasScanned, "must hasScanned") - assert.Equal(t, origValue, scannedValue) - } else { - assert.False(t, hasScanned, "must not hasScanned") - } -} - -func TestOptionValueScan(t *testing.T) { - t.Run("string roundtrip", func(t *testing.T) { - roundtrip(t, optional.Some("hello world")) - }) - t.Run("string null", func(t *testing.T) { - roundtrip(t, optional.None[string]()) - }) - t.Run("int64 roundtrip", func(t *testing.T) { - roundtrip(t, optional.Some(int64(1234))) - }) - t.Run("int64 null", func(t *testing.T) { - roundtrip(t, optional.None[int64]()) - }) - t.Run("bool roundtrip", func(t *testing.T) { - roundtrip(t, optional.Some(false)) - }) - t.Run("bool null", func(t *testing.T) { - roundtrip(t, optional.None[bool]()) - }) -} - -func TestDelegateSQLType(t *testing.T) { - assert.Equal(t, "string", optional.Some("hello world").DelegateSQLType().Name()) - assert.Equal(t, "string", optional.None[string]().DelegateSQLType().Name()) - assert.Equal(t, "int64", optional.Some(int64(123)).DelegateSQLType().Name()) - assert.Equal(t, "int64", optional.None[int64]().DelegateSQLType().Name()) -} diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go index 46e20d95e8..2e2ccd6786 100644 --- a/modules/optional/serialization.go +++ b/modules/optional/serialization.go @@ -19,11 +19,11 @@ func (o *Option[T]) UnmarshalJSON(data []byte) error { } func (o Option[T]) MarshalJSON() ([]byte, error) { - has, v := o.Get() - if !has { + if !o.Has() { return []byte("null"), nil } - return json.Marshal(v) + + return json.Marshal(o.Value()) } func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error { @@ -36,12 +36,11 @@ func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error { } func (o Option[T]) MarshalYAML() (any, error) { - has, v := o.Get() - if !has { + if !o.Has() { return nil, nil } value := new(yaml.Node) - err := value.Encode(v) + err := value.Encode(o.Value()) return value, err } diff --git a/modules/packages/container/metadata.go b/modules/packages/container/metadata.go index 4f332783ba..6cac77b7ff 100644 --- a/modules/packages/container/metadata.go +++ b/modules/packages/container/metadata.go @@ -16,12 +16,11 @@ import ( ) const ( - PropertyRepository = "container.repository" - PropertyRepositoryAutolinkingPending = "container.repository.autolinking-pending" - PropertyDigest = "container.digest" - PropertyMediaType = "container.mediatype" - PropertyManifestTagged = "container.manifest.tagged" - PropertyManifestReference = "container.manifest.reference" + PropertyRepository = "container.repository" + PropertyDigest = "container.digest" + PropertyMediaType = "container.mediatype" + PropertyManifestTagged = "container.manifest.tagged" + PropertyManifestReference = "container.manifest.reference" DefaultPlatform = "linux/amd64" @@ -64,7 +63,6 @@ type Metadata struct { Labels map[string]string `json:"labels,omitempty"` ImageLayers []string `json:"layer_creation,omitempty"` Manifests []*Manifest `json:"manifests,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` } type Manifest struct { diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index 2f83d2ee7b..ed163d30ac 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -58,7 +58,7 @@ type PackageMetadata struct { Time map[string]time.Time `json:"time,omitempty"` Homepage string `json:"homepage,omitempty"` Keywords []string `json:"keywords,omitempty"` - Repository Repository `json:"repository"` + Repository Repository `json:"repository,omitempty"` Author User `json:"author"` ReadmeFilename string `json:"readmeFilename,omitempty"` Users map[string]bool `json:"users,omitempty"` @@ -75,7 +75,7 @@ type PackageMetadataVersion struct { Author User `json:"author"` Homepage string `json:"homepage,omitempty"` License string `json:"license,omitempty"` - Repository Repository `json:"repository"` + Repository Repository `json:"repository,omitempty"` Keywords []string `json:"keywords,omitempty"` Dependencies map[string]string `json:"dependencies,omitempty"` BundleDependencies []string `json:"bundleDependencies,omitempty"` diff --git a/modules/packages/npm/metadata.go b/modules/packages/npm/metadata.go index 0e5bf19ce7..6bb77f302b 100644 --- a/modules/packages/npm/metadata.go +++ b/modules/packages/npm/metadata.go @@ -22,5 +22,5 @@ type Metadata struct { OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"` Bin map[string]string `json:"bin,omitempty"` Readme string `json:"readme,omitempty"` - Repository Repository `json:"repository"` + Repository Repository `json:"repository,omitempty"` } diff --git a/modules/packages/nuget/symbol_extractor.go b/modules/packages/nuget/symbol_extractor.go index dd9fac96c6..992ade7e8f 100644 --- a/modules/packages/nuget/symbol_extractor.go +++ b/modules/packages/nuget/symbol_extractor.go @@ -142,8 +142,8 @@ func ParseDebugHeaderID(r io.ReadSeeker) (string, error) { if _, err := r.Read(b); err != nil { return "", err } - if before, _, ok := bytes.Cut(b, []byte{0}); ok { - buf.Write(before) + if i := bytes.IndexByte(b, 0); i != -1 { + buf.Write(b[:i]) return buf.String(), nil } buf.Write(b) diff --git a/modules/packages/pypi/metadata.go b/modules/packages/pypi/metadata.go index 0aa90aeca5..125728c4f1 100644 --- a/modules/packages/pypi/metadata.go +++ b/modules/packages/pypi/metadata.go @@ -13,26 +13,3 @@ type Metadata struct { License string `json:"license,omitempty"` RequiresPython string `json:"requires_python,omitempty"` } - -type FileHashesJSON struct { - SHA256 string `json:"sha256"` -} - -type FileJSON struct { - Filename string `json:"filename"` - URL string `json:"url"` - Hashes FileHashesJSON `json:"hashes"` - RequiresPython string `json:"requires-python"` - Size int64 `json:"size"` -} - -type PackageMetaJSON struct { - APIVersion string `json:"api-version"` -} - -type PackageJSON struct { - Name string `json:"name"` - Meta PackageMetaJSON `json:"meta"` - Versions []string `json:"versions"` - Files []FileJSON `json:"files"` -} diff --git a/modules/packages/rubygems/marshal.go b/modules/packages/rubygems/marshal.go index 7d498c66b8..191efc7c0e 100644 --- a/modules/packages/rubygems/marshal.go +++ b/modules/packages/rubygems/marshal.go @@ -91,7 +91,7 @@ func (e *MarshalEncoder) marshal(v any) error { val := reflect.ValueOf(v) typ := reflect.TypeOf(v) - if typ.Kind() == reflect.Pointer { + if typ.Kind() == reflect.Ptr { val = val.Elem() typ = typ.Elem() } @@ -250,7 +250,7 @@ func (e *MarshalEncoder) marshalArray(arr reflect.Value) error { return err } - for i := range length { + for i := 0; i < length; i++ { if err := e.marshal(arr.Index(i).Interface()); err != nil { return err } diff --git a/modules/packages/rubygems/metadata.go b/modules/packages/rubygems/metadata.go index 0091569fce..9d3fd18f12 100644 --- a/modules/packages/rubygems/metadata.go +++ b/modules/packages/rubygems/metadata.go @@ -128,11 +128,7 @@ func (r requirement) AsVersionRequirement() []VersionRequirement { continue } version, ok := versionInt.(string) - if !ok { - continue - } - - if restriction == ">=" && version == "0" { + if !ok || version == "0" { continue } diff --git a/modules/packages/rubygems/metadata_test.go b/modules/packages/rubygems/metadata_test.go index 683f8d1df8..cd3a5bbd10 100644 --- a/modules/packages/rubygems/metadata_test.go +++ b/modules/packages/rubygems/metadata_test.go @@ -87,30 +87,3 @@ yjAbmt9LsOMp8xMamFkSQ38fP5EFjdz8LA4do2C69VvqWXAJgrPbKZb58/xZXrKoW6ttW13Bhvzi assert.Equal(t, "~>", rp.Metadata.DevelopmentDependencies[0].Version[0].Restriction) assert.Equal(t, "5.2", rp.Metadata.DevelopmentDependencies[0].Version[0].Version) } - -func TestPessimisticVersioning(t *testing.T) { - content, _ := base64.StdEncoding.DecodeString(`H4sIABmkhGkCA+1WTY/TMBC9+1eYvfSU1G1ZkCxRgYTYCwcEEgcQshxnmnrXX9gO2lz2t2MnTdOy -1YIKYoVEEimZ8WT8/GY8nqIo8BPfVt3cVtcgIr0CTekHB0JupOBRWoMM10CxgxCkliFKUXwDH9KI -NE0RIUS0k+kJVx+HIYTx3oiUi5Igp3jcWK8pzv8g3sat9YGiAm+yYD18baUHiippaukpTm8kwEcm -tlwmN5+/oJrHhGxJls8KsizIJSaE9k9Jxgt/QjU4MDUYIaH3fx/k69GiSziH5UrtlBQyjmtNAztE -Gkw8tdL303AyPjJP02ZNke6L9YuLXsjiQ3QN1560GZklZexcwkZ9a6LUkBTOgwcFPCT1hqsAE9Hs -CMCjAP5FruH2P9d/nesT+/mP8X63/sd4/w3ANQThpcuVkiLQXKq+hr0MbYxdKax1JfcIbkG0kVcq -laBcueA2gslO9qLnzNdWsI0cbaavrdXgeJPWv43RBTqfC1tDBb4prW/mqYw2cG33b9cqFeaLxeJy -hVKw00RD4bt697ZYlaSwRnVIQ+SpfvLMQtU2LAEQN+BZ6+UZ02A8YjzbQbCtF8DyH2f7SEeDaUDZ -5mwPKQRtzo7+6DvTi7MhMmlC5EoxnfZZDh3qo2v7RBmiustF5njc9vFRshqVNctZyB44WI8z+8e8 -PqomP8/pKaNX5WJ2DKIBHR4BCNnD2G3uzNg9OKtyVS6foyCb3I2wG+goCofdy2T6FIVWa+47il/h -3LbgFDv8Zogfvltjctjj4KnHQdn4YF9+B8hhV5g0CQAA`) - rp, err := parseMetadataFile(bytes.NewReader(content)) - require.NoError(t, err) - assert.NotNil(t, rp) - - assert.Len(t, rp.Metadata.RuntimeDependencies, 3) - assert.Equal(t, "implicit-version", rp.Metadata.RuntimeDependencies[0].Name) - assert.Empty(t, rp.Metadata.RuntimeDependencies[0].Version) - assert.Equal(t, "explicit-version", rp.Metadata.RuntimeDependencies[1].Name) - assert.Empty(t, rp.Metadata.RuntimeDependencies[1].Version) - assert.Equal(t, "explicit-pessimistic-version", rp.Metadata.RuntimeDependencies[2].Name) - assert.Len(t, rp.Metadata.RuntimeDependencies[2].Version, 1) - assert.Equal(t, "~>", rp.Metadata.RuntimeDependencies[2].Version[0].Restriction) - assert.Equal(t, "0", rp.Metadata.RuntimeDependencies[2].Version[0].Version) -} diff --git a/modules/packages/swift/metadata.go b/modules/packages/swift/metadata.go index 094fa0c7a4..34fc4f1784 100644 --- a/modules/packages/swift/metadata.go +++ b/modules/packages/swift/metadata.go @@ -47,7 +47,7 @@ type Metadata struct { Keywords []string `json:"keywords,omitempty"` RepositoryURL string `json:"repository_url,omitempty"` License string `json:"license,omitempty"` - Author Person `json:"author"` + Author Person `json:"author,omitempty"` Manifests map[string]*Manifest `json:"manifests,omitempty"` } diff --git a/modules/private/serv.go b/modules/private/serv.go index 1e04b2ce29..fb8496930e 100644 --- a/modules/private/serv.go +++ b/modules/private/serv.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "net/url" - "strings" asymkey_model "forgejo.org/models/asymkey" "forgejo.org/models/perm" @@ -48,18 +47,17 @@ type ServCommandResults struct { // ServCommand preps for a serv call func ServCommand(ctx context.Context, keyID int64, ownerName, repoName string, mode perm.AccessMode, verbs ...string) (*ServCommandResults, ResponseExtra) { - var reqURL strings.Builder - fmt.Fprintf(&reqURL, "%sapi/internal/serv/command/%d/%s/%s?mode=%d", - setting.LocalURL, + reqURL := setting.LocalURL + fmt.Sprintf("api/internal/serv/command/%d/%s/%s?mode=%d", keyID, url.PathEscape(ownerName), url.PathEscape(repoName), - mode) + mode, + ) for _, verb := range verbs { if verb != "" { - fmt.Fprintf(&reqURL, "&verb=%s", url.QueryEscape(verb)) + reqURL += fmt.Sprintf("&verb=%s", url.QueryEscape(verb)) } } - req := newInternalRequest(ctx, reqURL.String(), "GET") + req := newInternalRequest(ctx, reqURL, "GET") return requestJSONResp(req, &ServCommandResults{}) } diff --git a/modules/proxyprotocol/conn.go b/modules/proxyprotocol/conn.go index 8414b8af35..beac5de120 100644 --- a/modules/proxyprotocol/conn.go +++ b/modules/proxyprotocol/conn.go @@ -15,7 +15,6 @@ import ( "time" "forgejo.org/modules/log" - "forgejo.org/modules/setting" ) var ( @@ -47,7 +46,6 @@ func NewConn(conn net.Conn, timeout time.Duration) *Conn { bufReader: bufio.NewReader(conn), conn: conn, proxyHeaderTimeout: timeout, - acceptUnknown: setting.ProxyProtocolAcceptUnknown, } return pConn } @@ -259,10 +257,9 @@ func (p *Conn) readV2ProxyHeader() error { p.localAddr = p.conn.RemoteAddr() return nil case 0x1: - // - \x1 : PROXY : the connection was established on behalf of another node, - // and reflects the original connection endpoints. The receiver must then use - // the information provided in the protocol block to get original the address. - break + // - \x1 : PROXY : the connection was established on behalf of another node, + // and reflects the original connection endpoints. The receiver must then use + // the information provided in the protocol block to get original the address. default: // - other values are unassigned and must not be emitted by senders. Receivers // must drop connections presenting unexpected values here. @@ -459,7 +456,7 @@ func (p *Conn) readV1ProxyHeader() error { // Verify the type is known switch parts[1] { case "UNKNOWN": - if !p.acceptUnknown { + if !p.acceptUnknown || len(parts) != 2 { p.conn.Close() return &ErrBadHeader{[]byte(header)} } @@ -467,9 +464,7 @@ func (p *Conn) readV1ProxyHeader() error { p.localAddr = p.conn.RemoteAddr() return nil case "TCP4": - break case "TCP6": - break default: p.conn.Close() return &ErrBadAddressType{parts[1]} diff --git a/modules/proxyprotocol/conn_test.go b/modules/proxyprotocol/conn_test.go deleted file mode 100644 index 39adc401a3..0000000000 --- a/modules/proxyprotocol/conn_test.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package proxyprotocol_test - -import ( - "io" - "net" - "testing" - "time" - - "forgejo.org/modules/proxyprotocol" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var v2Header = []byte{0xd, 0xa, 0xd, 0xa, 0x0, 0xd, 0xa, 0x51, 0x55, 0x49, 0x54, 0xa} - -func testConnection(t *testing.T, input []byte) *proxyprotocol.Conn { - local, remote := net.Pipe() - conn := proxyprotocol.NewConn(remote, 10*time.Second) - - go func(t *testing.T, conn net.Conn) { - _, err := conn.Write(input) - require.NoError(t, err) - - err = conn.Close() - require.NoError(t, err) - }(t, local) - - return conn -} - -func assertUwu(t *testing.T, conn *proxyprotocol.Conn) { - buf := make([]byte, 3) - read, err := conn.Read(buf) - require.NoError(t, err) - - assert.Equal(t, 3, read) - assert.Equal(t, []byte("uwu"), buf) -} - -func TestProxyProtocolParse(t *testing.T) { - // Basic v4/v6 TCP - ipv4Conn := testConnection(t, []byte("PROXY TCP4 7.3.3.1 1.3.3.7 14231 443\r\nuwu")) - - assertUwu(t, ipv4Conn) - assert.Equal(t, "7.3.3.1:14231", ipv4Conn.RemoteAddr().String()) - assert.Equal(t, "1.3.3.7:443", ipv4Conn.LocalAddr().String()) - - ipv6Conn := testConnection(t, []byte("PROXY TCP6 fe80::2 fe80::1 28512 443\r\nuwu")) - - assertUwu(t, ipv6Conn) - assert.Equal(t, "[fe80::2]:28512", ipv6Conn.RemoteAddr().String()) - assert.Equal(t, "[fe80::1]:443", ipv6Conn.LocalAddr().String()) - - ipv4Conn = testConnection(t, append(v2Header, 0x21, 0x11, 0x0, 0xc, 0x7, 0x3, 0x3, 0x1, 0x1, 0x3, 0x3, 0x7, 0xd9, 0xec, 0x1, 0xbb, 0x75, 0x77, 0x75)) - - assertUwu(t, ipv4Conn) - assert.Equal(t, "7.3.3.1:55788", ipv4Conn.RemoteAddr().String()) - assert.Equal(t, "1.3.3.7:443", ipv4Conn.LocalAddr().String()) - - ipv6Conn = testConnection(t, append(v2Header, 0x21, 0x21, 0x0, 0x24, 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xd9, 0xec, 0x1, 0xbb, 0x75, 0x77, 0x75)) - - assertUwu(t, ipv6Conn) - assert.Equal(t, "[fe80::2]:55788", ipv6Conn.RemoteAddr().String()) - assert.Equal(t, "[fe80::1]:443", ipv6Conn.LocalAddr().String()) - - // Basic unknown - unknownConn := testConnection(t, []byte("PROXY UNKNOWN\r\nuwu")) - _, err := unknownConn.Read([]byte{}) - require.Error(t, err) - - // Accept unknown protocol types - defer test.MockVariableValue(&setting.ProxyProtocolAcceptUnknown, true)() - - unknownConn = testConnection(t, []byte("PROXY UNKNOWN\r\nuwu")) - - assertUwu(t, unknownConn) - assert.Equal(t, "pipe", unknownConn.RemoteAddr().String()) - assert.Equal(t, "pipe", unknownConn.LocalAddr().String()) - - // Discard any unknown information between "UNKNOWN" and CRLF - - unknownConn = testConnection(t, []byte("PROXY UNKNOWN look, I'm hinding in an unknown protocol \\o/\r\nuwu")) - - assertUwu(t, unknownConn) - assert.Equal(t, "pipe", unknownConn.RemoteAddr().String()) - assert.Equal(t, "pipe", unknownConn.LocalAddr().String()) - - // Basic local - unknownConnV2 := testConnection(t, append(v2Header, 0x20, 0x0, 0x0, 0x0, 0x75, 0x77, 0x75)) - - assertUwu(t, unknownConnV2) - assert.Equal(t, "pipe", unknownConnV2.RemoteAddr().String()) - assert.Equal(t, "pipe", unknownConnV2.LocalAddr().String()) -} - -func TestProxyProtocolInvalidHeader(t *testing.T) { - // Short prefix - conn := testConnection(t, []byte("PROXY\r\n")) - _, err := conn.Read([]byte{}) - require.ErrorIs(t, err, io.EOF) - - // Wrong prefix - conn = testConnection(t, []byte("PROXYv1337\r\n")) - _, err = conn.Read([]byte{}) - require.ErrorContains(t, err, "Unexpected proxy header") -} diff --git a/modules/proxyprotocol/listener.go b/modules/proxyprotocol/listener.go index 500181cd6a..ec85c425d3 100644 --- a/modules/proxyprotocol/listener.go +++ b/modules/proxyprotocol/listener.go @@ -18,6 +18,7 @@ import ( type Listener struct { Listener net.Listener ProxyHeaderTimeout time.Duration + AcceptUnknown bool // allow PROXY UNKNOWN } // Accept implements the Accept method in the Listener interface @@ -30,6 +31,7 @@ func (p *Listener) Accept() (net.Conn, error) { } newConn := NewConn(conn, p.ProxyHeaderTimeout) + newConn.acceptUnknown = p.AcceptUnknown return newConn, nil } diff --git a/modules/proxyprotocol/util.go b/modules/proxyprotocol/util.go index 5fc89d80a9..a280663b27 100644 --- a/modules/proxyprotocol/util.go +++ b/modules/proxyprotocol/util.go @@ -5,7 +5,7 @@ package proxyprotocol import "io" -var localHeader = append(v2Prefix, '\x20', '\x00', '\x00', '\x00') +var localHeader = append(v2Prefix, '\x20', '\x00', '\x00', '\x00', '\x00') // WriteLocalHeader will write the ProxyProtocol Header for a local connection to the provided writer func WriteLocalHeader(w io.Writer) error { diff --git a/modules/public/public.go b/modules/public/public.go index 52cb8757a0..a7db5b62e9 100644 --- a/modules/public/public.go +++ b/modules/public/public.go @@ -45,7 +45,7 @@ func FileHandlerFunc() http.HandlerFunc { func parseAcceptEncoding(val string) container.Set[string] { parts := strings.Split(val, ";") types := make(container.Set[string]) - for v := range strings.SplitSeq(parts[0], ",") { + for _, v := range strings.Split(parts[0], ",") { types.Add(strings.TrimSpace(v)) } return types diff --git a/modules/queue/base_levelqueue_common.go b/modules/queue/base_levelqueue_common.go index c57bf8597b..8b4f35c47d 100644 --- a/modules/queue/base_levelqueue_common.go +++ b/modules/queue/base_levelqueue_common.go @@ -83,7 +83,7 @@ func prepareLevelDB(cfg *BaseConfig) (conn string, db *leveldb.DB, err error) { } conn = cfg.ConnStr } - for range 10 { + for i := 0; i < 10; i++ { if db, err = nosql.GetManager().GetLevelDB(conn); err == nil { break } diff --git a/modules/queue/base_redis.go b/modules/queue/base_redis.go index 8b20e0b443..ec3c6dc16d 100644 --- a/modules/queue/base_redis.go +++ b/modules/queue/base_redis.go @@ -49,7 +49,7 @@ func newBaseRedisGeneric(cfg *BaseConfig, unique bool, client nosql.RedisClient) } var err error - for range 10 { + for i := 0; i < 10; i++ { err = client.Ping(graceful.GetManager().ShutdownContext()).Err() if err == nil { break diff --git a/modules/queue/base_redis_test.go b/modules/queue/base_redis_test.go index 297a49229a..bf3ad5b97b 100644 --- a/modules/queue/base_redis_test.go +++ b/modules/queue/base_redis_test.go @@ -7,17 +7,18 @@ import ( "context" "testing" - "forgejo.org/modules/nosql" - queue_mock "forgejo.org/modules/queue/mock" + "forgejo.org/modules/queue/mock" "forgejo.org/modules/setting" "github.com/redis/go-redis/v9" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" + "go.uber.org/mock/gomock" ) type baseRedisUnitTestSuite struct { suite.Suite + + mockController *gomock.Controller } func TestBaseRedis(t *testing.T) { @@ -25,6 +26,7 @@ func TestBaseRedis(t *testing.T) { } func (suite *baseRedisUnitTestSuite) SetupSuite() { + suite.mockController = gomock.NewController(suite.T()) } func (suite *baseRedisUnitTestSuite) TestBasic() { @@ -69,47 +71,39 @@ func (suite *baseRedisUnitTestSuite) TestBasic() { } // Configure expectations. - mockRedisStore := queue_mock.NewInMemoryMockRedis() - redisClient := nosql.NewMockRedisClient(suite.T()) + mockRedisStore := mock.NewInMemoryMockRedis() + redisClient := mock.NewMockRedisClient(suite.mockController) redisClient.EXPECT(). - Ping(mock.Anything). - Return(&redis.StatusCmd{}). - Times(1) + Ping(gomock.Any()). + Times(1). + Return(&redis.StatusCmd{}) redisClient.EXPECT(). - LLen(mock.Anything, testCase.QueueName). - RunAndReturn(mockRedisStore.LLen). - Times(1) + LLen(gomock.Any(), testCase.QueueName). + Times(1). + DoAndReturn(mockRedisStore.LLen) redisClient.EXPECT(). - LPop(mock.Anything, testCase.QueueName). - RunAndReturn(mockRedisStore.LPop). - Times(1) + LPop(gomock.Any(), testCase.QueueName). + Times(1). + DoAndReturn(mockRedisStore.LPop) redisClient.EXPECT(). - RPush(mock.Anything, testCase.QueueName, mock.Anything). - RunAndReturn(func(ctx context.Context, key string, values ...any) *redis.IntCmd { - return mockRedisStore.RPush(ctx, key, values[0].([]byte)) - }). - Times(1) + RPush(gomock.Any(), testCase.QueueName, gomock.Any()). + Times(1). + DoAndReturn(mockRedisStore.RPush) if testCase.Unique { redisClient.EXPECT(). - SAdd(mock.Anything, testCase.QueueName+"_unique", mock.Anything). - RunAndReturn(func(ctx context.Context, key string, members ...any) *redis.IntCmd { - return mockRedisStore.SAdd(ctx, key, members[0].([]byte)) - }). - Times(1) + SAdd(gomock.Any(), testCase.QueueName+"_unique", gomock.Any()). + Times(1). + DoAndReturn(mockRedisStore.SAdd) redisClient.EXPECT(). - SRem(mock.Anything, testCase.QueueName+"_unique", mock.Anything). - RunAndReturn(func(ctx context.Context, key string, members ...any) *redis.IntCmd { - return mockRedisStore.SRem(ctx, key, members[0].([]byte)) - }). - Times(1) + SRem(gomock.Any(), testCase.QueueName+"_unique", gomock.Any()). + Times(1). + DoAndReturn(mockRedisStore.SRem) redisClient.EXPECT(). - SIsMember(mock.Anything, testCase.QueueName+"_unique", mock.Anything). - RunAndReturn(func(ctx context.Context, key string, member any) *redis.BoolCmd { - return mockRedisStore.SIsMember(ctx, key, member.([]byte)) - }). - Times(2) + SIsMember(gomock.Any(), testCase.QueueName+"_unique", gomock.Any()). + Times(2). + DoAndReturn(mockRedisStore.SIsMember) } client, err := newBaseRedisGeneric( diff --git a/modules/queue/base_test.go b/modules/queue/base_test.go index 758faf1459..caa930158c 100644 --- a/modules/queue/base_test.go +++ b/modules/queue/base_test.go @@ -88,7 +88,7 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) // test blocking push if queue is full for i := 0; i < cfg.Length; i++ { - err = q.PushItem(ctx, fmt.Appendf(nil, "item-%d", i)) + err = q.PushItem(ctx, []byte(fmt.Sprintf("item-%d", i))) require.NoError(t, err) } ctxTimed, cancel = context.WithTimeout(ctx, 10*time.Millisecond) diff --git a/modules/queue/manager.go b/modules/queue/manager.go index 9c655b7fdc..8f1a93f273 100644 --- a/modules/queue/manager.go +++ b/modules/queue/manager.go @@ -5,7 +5,6 @@ package queue import ( "context" - "maps" "sync" "time" @@ -69,7 +68,9 @@ func (m *Manager) ManagedQueues() map[int64]ManagedWorkerPoolQueue { defer m.mu.Unlock() queues := make(map[int64]ManagedWorkerPoolQueue, len(m.Queues)) - maps.Copy(queues, m.Queues) + for k, v := range m.Queues { + queues[k] = v + } return queues } diff --git a/modules/queue/mock/redisuniversalclient.go b/modules/queue/mock/redisuniversalclient.go new file mode 100644 index 0000000000..a19e639ac1 --- /dev/null +++ b/modules/queue/mock/redisuniversalclient.go @@ -0,0 +1,344 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: forgejo.org/modules/nosql (interfaces: RedisClient) +// +// Generated by this command: +// +// mockgen -package mock -destination ./modules/queue/mock/redisuniversalclient.go forgejo.org/modules/nosql RedisClient +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + time "time" + + redis "github.com/redis/go-redis/v9" + gomock "go.uber.org/mock/gomock" +) + +// MockRedisClient is a mock of RedisClient interface. +type MockRedisClient struct { + ctrl *gomock.Controller + recorder *MockRedisClientMockRecorder + isgomock struct{} +} + +// MockRedisClientMockRecorder is the mock recorder for MockRedisClient. +type MockRedisClientMockRecorder struct { + mock *MockRedisClient +} + +// NewMockRedisClient creates a new mock instance. +func NewMockRedisClient(ctrl *gomock.Controller) *MockRedisClient { + mock := &MockRedisClient{ctrl: ctrl} + mock.recorder = &MockRedisClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRedisClient) EXPECT() *MockRedisClientMockRecorder { + return m.recorder +} + +// Close mocks base method. +func (m *MockRedisClient) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockRedisClientMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockRedisClient)(nil).Close)) +} + +// DBSize mocks base method. +func (m *MockRedisClient) DBSize(ctx context.Context) *redis.IntCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DBSize", ctx) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// DBSize indicates an expected call of DBSize. +func (mr *MockRedisClientMockRecorder) DBSize(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DBSize", reflect.TypeOf((*MockRedisClient)(nil).DBSize), ctx) +} + +// Decr mocks base method. +func (m *MockRedisClient) Decr(ctx context.Context, key string) *redis.IntCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Decr", ctx, key) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// Decr indicates an expected call of Decr. +func (mr *MockRedisClientMockRecorder) Decr(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decr", reflect.TypeOf((*MockRedisClient)(nil).Decr), ctx, key) +} + +// Del mocks base method. +func (m *MockRedisClient) Del(ctx context.Context, keys ...string) *redis.IntCmd { + m.ctrl.T.Helper() + varargs := []any{ctx} + for _, a := range keys { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Del", varargs...) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// Del indicates an expected call of Del. +func (mr *MockRedisClientMockRecorder) Del(ctx any, keys ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx}, keys...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockRedisClient)(nil).Del), varargs...) +} + +// Exists mocks base method. +func (m *MockRedisClient) Exists(ctx context.Context, keys ...string) *redis.IntCmd { + m.ctrl.T.Helper() + varargs := []any{ctx} + for _, a := range keys { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Exists", varargs...) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// Exists indicates an expected call of Exists. +func (mr *MockRedisClientMockRecorder) Exists(ctx any, keys ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx}, keys...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exists", reflect.TypeOf((*MockRedisClient)(nil).Exists), varargs...) +} + +// FlushDB mocks base method. +func (m *MockRedisClient) FlushDB(ctx context.Context) *redis.StatusCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FlushDB", ctx) + ret0, _ := ret[0].(*redis.StatusCmd) + return ret0 +} + +// FlushDB indicates an expected call of FlushDB. +func (mr *MockRedisClientMockRecorder) FlushDB(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushDB", reflect.TypeOf((*MockRedisClient)(nil).FlushDB), ctx) +} + +// Get mocks base method. +func (m *MockRedisClient) Get(ctx context.Context, key string) *redis.StringCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, key) + ret0, _ := ret[0].(*redis.StringCmd) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockRedisClientMockRecorder) Get(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRedisClient)(nil).Get), ctx, key) +} + +// HDel mocks base method. +func (m *MockRedisClient) HDel(ctx context.Context, key string, fields ...string) *redis.IntCmd { + m.ctrl.T.Helper() + varargs := []any{ctx, key} + for _, a := range fields { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "HDel", varargs...) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// HDel indicates an expected call of HDel. +func (mr *MockRedisClientMockRecorder) HDel(ctx, key any, fields ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, key}, fields...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HDel", reflect.TypeOf((*MockRedisClient)(nil).HDel), varargs...) +} + +// HKeys mocks base method. +func (m *MockRedisClient) HKeys(ctx context.Context, key string) *redis.StringSliceCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HKeys", ctx, key) + ret0, _ := ret[0].(*redis.StringSliceCmd) + return ret0 +} + +// HKeys indicates an expected call of HKeys. +func (mr *MockRedisClientMockRecorder) HKeys(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HKeys", reflect.TypeOf((*MockRedisClient)(nil).HKeys), ctx, key) +} + +// HSet mocks base method. +func (m *MockRedisClient) HSet(ctx context.Context, key string, values ...any) *redis.IntCmd { + m.ctrl.T.Helper() + varargs := []any{ctx, key} + for _, a := range values { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "HSet", varargs...) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// HSet indicates an expected call of HSet. +func (mr *MockRedisClientMockRecorder) HSet(ctx, key any, values ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, key}, values...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HSet", reflect.TypeOf((*MockRedisClient)(nil).HSet), varargs...) +} + +// Incr mocks base method. +func (m *MockRedisClient) Incr(ctx context.Context, key string) *redis.IntCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Incr", ctx, key) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// Incr indicates an expected call of Incr. +func (mr *MockRedisClientMockRecorder) Incr(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Incr", reflect.TypeOf((*MockRedisClient)(nil).Incr), ctx, key) +} + +// LLen mocks base method. +func (m *MockRedisClient) LLen(ctx context.Context, key string) *redis.IntCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LLen", ctx, key) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// LLen indicates an expected call of LLen. +func (mr *MockRedisClientMockRecorder) LLen(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LLen", reflect.TypeOf((*MockRedisClient)(nil).LLen), ctx, key) +} + +// LPop mocks base method. +func (m *MockRedisClient) LPop(ctx context.Context, key string) *redis.StringCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LPop", ctx, key) + ret0, _ := ret[0].(*redis.StringCmd) + return ret0 +} + +// LPop indicates an expected call of LPop. +func (mr *MockRedisClientMockRecorder) LPop(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LPop", reflect.TypeOf((*MockRedisClient)(nil).LPop), ctx, key) +} + +// Ping mocks base method. +func (m *MockRedisClient) Ping(ctx context.Context) *redis.StatusCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Ping", ctx) + ret0, _ := ret[0].(*redis.StatusCmd) + return ret0 +} + +// Ping indicates an expected call of Ping. +func (mr *MockRedisClientMockRecorder) Ping(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockRedisClient)(nil).Ping), ctx) +} + +// RPush mocks base method. +func (m *MockRedisClient) RPush(ctx context.Context, key string, values ...any) *redis.IntCmd { + m.ctrl.T.Helper() + varargs := []any{ctx, key} + for _, a := range values { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RPush", varargs...) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// RPush indicates an expected call of RPush. +func (mr *MockRedisClientMockRecorder) RPush(ctx, key any, values ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, key}, values...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RPush", reflect.TypeOf((*MockRedisClient)(nil).RPush), varargs...) +} + +// SAdd mocks base method. +func (m *MockRedisClient) SAdd(ctx context.Context, key string, members ...any) *redis.IntCmd { + m.ctrl.T.Helper() + varargs := []any{ctx, key} + for _, a := range members { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SAdd", varargs...) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// SAdd indicates an expected call of SAdd. +func (mr *MockRedisClientMockRecorder) SAdd(ctx, key any, members ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, key}, members...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SAdd", reflect.TypeOf((*MockRedisClient)(nil).SAdd), varargs...) +} + +// SIsMember mocks base method. +func (m *MockRedisClient) SIsMember(ctx context.Context, key string, member any) *redis.BoolCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SIsMember", ctx, key, member) + ret0, _ := ret[0].(*redis.BoolCmd) + return ret0 +} + +// SIsMember indicates an expected call of SIsMember. +func (mr *MockRedisClientMockRecorder) SIsMember(ctx, key, member any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SIsMember", reflect.TypeOf((*MockRedisClient)(nil).SIsMember), ctx, key, member) +} + +// SRem mocks base method. +func (m *MockRedisClient) SRem(ctx context.Context, key string, members ...any) *redis.IntCmd { + m.ctrl.T.Helper() + varargs := []any{ctx, key} + for _, a := range members { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SRem", varargs...) + ret0, _ := ret[0].(*redis.IntCmd) + return ret0 +} + +// SRem indicates an expected call of SRem. +func (mr *MockRedisClientMockRecorder) SRem(ctx, key any, members ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, key}, members...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SRem", reflect.TypeOf((*MockRedisClient)(nil).SRem), varargs...) +} + +// Set mocks base method. +func (m *MockRedisClient) Set(ctx context.Context, key string, value any, expiration time.Duration) *redis.StatusCmd { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Set", ctx, key, value, expiration) + ret0, _ := ret[0].(*redis.StatusCmd) + return ret0 +} + +// Set indicates an expected call of Set. +func (mr *MockRedisClientMockRecorder) Set(ctx, key, value, expiration any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockRedisClient)(nil).Set), ctx, key, value, expiration) +} diff --git a/modules/queue/workergroup.go b/modules/queue/workergroup.go index 87f01755aa..3fb821ce69 100644 --- a/modules/queue/workergroup.go +++ b/modules/queue/workergroup.go @@ -56,7 +56,6 @@ func (q *WorkerPoolQueue[T]) doDispatchBatchToWorker(wg *workerGroup[T], flushCh full := false select { case q.batchChan <- batch: - break default: full = true } @@ -77,7 +76,6 @@ func (q *WorkerPoolQueue[T]) doDispatchBatchToWorker(wg *workerGroup[T], flushCh if full { select { case q.batchChan <- batch: - break case flush := <-flushChan: q.doWorkerHandle(batch) q.doFlush(wg, flush) @@ -107,9 +105,7 @@ func (q *WorkerPoolQueue[T]) doWorkerHandle(batch []T) { log.Error("Queue %q failed to handle batch of %d items, backoff for a few seconds", q.GetName(), len(batch)) select { case <-q.ctxRun.Done(): - break case <-time.After(time.Duration(unhandledItemRequeueDuration.Load())): - break } } for _, item := range unhandled { @@ -142,7 +138,11 @@ func (q *WorkerPoolQueue[T]) basePushForShutdown(items ...T) bool { // doStartNewWorker starts a new worker for the queue, the worker reads from worker's channel and handles the items. func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) { - wp.wg.Go(func() { + wp.wg.Add(1) + + go func() { + defer wp.wg.Done() + log.Debug("Queue %q starts new worker", q.GetName()) defer log.Debug("Queue %q stops idle worker", q.GetName()) @@ -170,9 +170,7 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) { t.Reset(workerIdleDuration) select { case <-t.C: - break default: - break } case <-t.C: q.workerNumMu.Lock() @@ -183,7 +181,7 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) { q.workerNumMu.Unlock() } } - }) + }() } // doFlush flushes the queue: it tries to read all items from the queue and handles them. @@ -298,7 +296,6 @@ func (q *WorkerPoolQueue[T]) doRun() { go func() { wg.wg.Wait(); close(workerDone) }() select { case <-workerDone: - break case <-time.After(shutdownTimeout): log.Error("Queue %q is shutting down, but workers are still running after timeout", q.GetName()) } diff --git a/modules/queue/workerqueue.go b/modules/queue/workerqueue.go index c340bafa50..6a71fc4fb4 100644 --- a/modules/queue/workerqueue.go +++ b/modules/queue/workerqueue.go @@ -110,7 +110,6 @@ func (q *WorkerPoolQueue[T]) FlushWithContext(ctx context.Context, timeout time. // if it blocks, it means that there is a flush in progress or the queue hasn't been started yet select { case q.flushChan <- c: - break case <-ctx.Done(): return ctx.Err() case <-q.ctxRun.Done(): diff --git a/modules/queue/workerqueue_test.go b/modules/queue/workerqueue_test.go index da857b9405..8d907ed8cd 100644 --- a/modules/queue/workerqueue_test.go +++ b/modules/queue/workerqueue_test.go @@ -78,17 +78,17 @@ func TestWorkerPoolQueueUnhandled(t *testing.T) { runCount := 2 // we can run these tests even hundreds times to see its stability t.Run("1/1", func(t *testing.T) { - for range runCount { + for i := 0; i < runCount; i++ { test(t, setting.QueueSettings{BatchLength: 1, MaxWorkers: 1}) } }) t.Run("3/1", func(t *testing.T) { - for range runCount { + for i := 0; i < runCount; i++ { test(t, setting.QueueSettings{BatchLength: 3, MaxWorkers: 1}) } }) t.Run("4/5", func(t *testing.T) { - for range runCount { + for i := 0; i < runCount; i++ { test(t, setting.QueueSettings{BatchLength: 4, MaxWorkers: 5}) } }) @@ -97,17 +97,17 @@ func TestWorkerPoolQueueUnhandled(t *testing.T) { func TestWorkerPoolQueuePersistence(t *testing.T) { runCount := 2 // we can run these tests even hundreds times to see its stability t.Run("1/1", func(t *testing.T) { - for range runCount { + for i := 0; i < runCount; i++ { testWorkerPoolQueuePersistence(t, setting.QueueSettings{BatchLength: 1, MaxWorkers: 1, Length: 100}) } }) t.Run("3/1", func(t *testing.T) { - for range runCount { + for i := 0; i < runCount; i++ { testWorkerPoolQueuePersistence(t, setting.QueueSettings{BatchLength: 3, MaxWorkers: 1, Length: 100}) } }) t.Run("4/5", func(t *testing.T) { - for range runCount { + for i := 0; i < runCount; i++ { testWorkerPoolQueuePersistence(t, setting.QueueSettings{BatchLength: 4, MaxWorkers: 5, Length: 100}) } }) @@ -142,7 +142,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett q, _ := newWorkerPoolQueueForTest("pr_patch_checker_test", queueSetting, testHandler, true) stop := runWorkerPoolQueue(q) - for i := range testCount { + for i := 0; i < testCount; i++ { _ = q.Push("task-" + strconv.Itoa(i)) } close(startWhenAllReady) @@ -187,7 +187,7 @@ func TestWorkerPoolQueueActiveWorkers(t *testing.T) { q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 1, Length: 100}, handler, false) stop := runWorkerPoolQueue(q) - for i := range 5 { + for i := 0; i < 5; i++ { require.NoError(t, q.Push(i)) } @@ -203,7 +203,7 @@ func TestWorkerPoolQueueActiveWorkers(t *testing.T) { q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 3, Length: 100}, handler, false) stop = runWorkerPoolQueue(q) - for i := range 15 { + for i := 0; i < 15; i++ { require.NoError(t, q.Push(i)) } @@ -264,12 +264,12 @@ func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) { stop := runWorkerPoolQueue(q) const workloadSize = 12 - for i := range workloadSize { + for i := 0; i < workloadSize; i++ { require.NoError(t, q.Push(i)) } workerIDs := make(map[string]struct{}) - for i := range workloadSize { + for i := 0; i < workloadSize; i++ { c := <-chGoroutineIDs workerIDs[c] = struct{}{} t.Logf("%d workers: overall=%d current=%d", i, len(workerIDs), q.GetWorkerNumber()) diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go index ef998f7916..4b6d4bfe51 100644 --- a/modules/repository/commits_test.go +++ b/modules/repository/commits_test.go @@ -5,6 +5,7 @@ package repository import ( + "strconv" "testing" "time" @@ -12,6 +13,7 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" "forgejo.org/modules/git" + "forgejo.org/modules/setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -124,7 +126,7 @@ func TestPushCommits_AvatarLink(t *testing.T) { } assert.Equal(t, - "/avatars/ab53a2911ddf9b4817ac01ddcd3d975f", + "/avatars/ab53a2911ddf9b4817ac01ddcd3d975f?size="+strconv.Itoa(28*setting.Avatar.RenderedSizeFactor), pushCommits.AvatarLink(db.DefaultContext, "user2@example.com")) assert.Equal(t, diff --git a/modules/git/hook_generate.go b/modules/repository/hooks.go similarity index 60% rename from modules/git/hook_generate.go rename to modules/repository/hooks.go index 2af2487ab3..0f5e3afc34 100644 --- a/modules/git/hook_generate.go +++ b/modules/repository/hooks.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package git +package repository import ( "fmt" @@ -21,25 +21,14 @@ func getHookTemplates() (hookNames, hookTpls, giteaHookTpls []string) { data=$(cat) exitcodes="" hookname=$(basename $0) +GIT_DIR=${GIT_DIR:-$(dirname $0)/..} -for hook in $(dirname $0)/${hookname}.d/*; do +for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do test -x "${hook}" && test -f "${hook}" || continue echo "${data}" | "${hook}" exitcodes="${exitcodes} $?" done -# Custom hooks -custom_hooks_dir="./hooks/${hookname}.d" -if [ -d "${custom_hooks_dir}" ]; then - for hook in ${custom_hooks_dir}/*; do - if [ $(basename "${hook}") != "gitea" ]; then - test -x "${hook}" && test -f "${hook}" || continue - echo "${data}" | "${hook}" - exitcodes="${exitcodes} $?" - fi - done -fi - for i in ${exitcodes}; do [ ${i} -eq 0 ] || exit ${i} done @@ -50,25 +39,14 @@ done # AUTO GENERATED BY GITEA, DO NOT MODIFY exitcodes="" hookname=$(basename $0) +GIT_DIR=${GIT_DIR:-$(dirname $0/..)} -for hook in $(dirname $0)/${hookname}.d/*; do +for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do test -x "${hook}" && test -f "${hook}" || continue "${hook}" $1 $2 $3 exitcodes="${exitcodes} $?" done -# Custom hooks -custom_hooks_dir="./hooks/${hookname}.d" -if [ -d "${custom_hooks_dir}" ]; then - for hook in ${custom_hooks_dir}/*; do - if [ $(basename "${hook}") != "gitea" ]; then - test -x "${hook}" && test -f "${hook}" || continue - "${hook}" $1 $2 $3 - exitcodes="${exitcodes} $?" - fi - done -fi - for i in ${exitcodes}; do [ ${i} -eq 0 ] || exit ${i} done @@ -80,24 +58,14 @@ done data=$(cat) exitcodes="" hookname=$(basename $0) +GIT_DIR=${GIT_DIR:-$(dirname $0)/..} -for hook in $(dirname $0)/${hookname}.d/*; do +for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do test -x "${hook}" && test -f "${hook}" || continue echo "${data}" | "${hook}" exitcodes="${exitcodes} $?" done -# Custom hooks -custom_hooks_dir="./hooks/${hookname}.d" -if [ -d "${custom_hooks_dir}" ]; then - for hook in ${custom_hooks_dir}/*; do - if [ $(basename "${hook}") != "gitea" ]; then - test -x "${hook}" && test -f "${hook}" || continue - echo "${data}" | "${hook}" - exitcodes="${exitcodes} $?" - fi - done -fi for i in ${exitcodes}; do [ ${i} -eq 0 ] || exit ${i} done @@ -136,9 +104,10 @@ done return hookNames, hookTpls, giteaHookTpls } -func InitDelegateHooks(path string) (err error) { +// CreateDelegateHooks creates all the hooks scripts for the repo +func CreateDelegateHooks(repoPath string) (err error) { hookNames, hookTpls, giteaHookTpls := getHookTemplates() - hookDir := filepath.Join(path, "hooks") + hookDir := filepath.Join(repoPath, "hooks") for i, hookName := range hookNames { oldHookPath := filepath.Join(hookDir, hookName) @@ -175,6 +144,14 @@ func InitDelegateHooks(path string) (err error) { return nil } +func checkExecutable(filename string) bool { + fileInfo, err := os.Stat(filename) + if err != nil { + return false + } + return (fileInfo.Mode() & 0o100) > 0 +} + func ensureExecutable(filename string) error { fileInfo, err := os.Stat(filename) if err != nil { @@ -186,3 +163,66 @@ func ensureExecutable(filename string) error { mode := fileInfo.Mode() | 0o100 return os.Chmod(filename, mode) } + +// CheckDelegateHooks checks the hooks scripts for the repo +func CheckDelegateHooks(repoPath string) ([]string, error) { + hookNames, hookTpls, giteaHookTpls := getHookTemplates() + + hookDir := filepath.Join(repoPath, "hooks") + results := make([]string, 0, 10) + + for i, hookName := range hookNames { + oldHookPath := filepath.Join(hookDir, hookName) + newHookPath := filepath.Join(hookDir, hookName+".d", "gitea") + + cont := false + isExist, err := util.IsExist(oldHookPath) + if err != nil { + results = append(results, fmt.Sprintf("unable to check if %s exists. Error: %v", oldHookPath, err)) + } + if err == nil && !isExist { + results = append(results, fmt.Sprintf("old hook file %s does not exist", oldHookPath)) + cont = true + } + isExist, err = util.IsExist(oldHookPath + ".d") + if err != nil { + results = append(results, fmt.Sprintf("unable to check if %s exists. Error: %v", oldHookPath+".d", err)) + } + if err == nil && !isExist { + results = append(results, fmt.Sprintf("hooks directory %s does not exist", oldHookPath+".d")) + cont = true + } + isExist, err = util.IsExist(newHookPath) + if err != nil { + results = append(results, fmt.Sprintf("unable to check if %s exists. Error: %v", newHookPath, err)) + } + if err == nil && !isExist { + results = append(results, fmt.Sprintf("new hook file %s does not exist", newHookPath)) + cont = true + } + if cont { + continue + } + contents, err := os.ReadFile(oldHookPath) + if err != nil { + return results, err + } + if string(contents) != hookTpls[i] { + results = append(results, fmt.Sprintf("old hook file %s is out of date", oldHookPath)) + } + if !checkExecutable(oldHookPath) { + results = append(results, fmt.Sprintf("old hook file %s is not executable", oldHookPath)) + } + contents, err = os.ReadFile(newHookPath) + if err != nil { + return results, err + } + if string(contents) != giteaHookTpls[i] { + results = append(results, fmt.Sprintf("new hook file %s is out of date", newHookPath)) + } + if !checkExecutable(newHookPath) { + results = append(results, fmt.Sprintf("new hook file %s is not executable", newHookPath)) + } + } + return results, nil +} diff --git a/modules/repository/init.go b/modules/repository/init.go index 16d366c6f0..7b1442be93 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -138,6 +138,8 @@ func CheckInitRepository(ctx context.Context, owner, name, objectFormatName stri // Init git bare new repository. if err = git.InitRepository(ctx, repoPath, true, objectFormatName); err != nil { return fmt.Errorf("git.InitRepository: %w", err) + } else if err = CreateDelegateHooks(repoPath); err != nil { + return fmt.Errorf("createDelegateHooks: %w", err) } return nil } @@ -150,7 +152,7 @@ func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg } labels := make([]*issues_model.Label, len(list)) - for i := range list { + for i := 0; i < len(list); i++ { labels[i] = &issues_model.Label{ Name: list[i].Name, Exclusive: list[i].Exclusive, diff --git a/modules/repository/license_test.go b/modules/repository/license_test.go index 5bd3846d61..3195f15dda 100644 --- a/modules/repository/license_test.go +++ b/modules/repository/license_test.go @@ -33,20 +33,11 @@ func Test_getLicense(t *testing.T) { Copyright (c) 2023 Gitea -Permission 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: +Permission 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: -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE 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. +THE 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. `, wantErr: require.NoError, }, diff --git a/modules/session/db.go b/modules/session/db.go index 57f658dfd6..eea7e2136e 100644 --- a/modules/session/db.go +++ b/modules/session/db.go @@ -84,11 +84,6 @@ func (s *DBStore) Flush() error { return nil } -// True if no keys have been set -func (s *DBStore) Empty() bool { - return len(s.data) == 0 -} - // DBProvider represents a DB session provider implementation. type DBProvider struct { maxLifetime int64 diff --git a/modules/session/redis.go b/modules/session/redis.go index 1e8c61da8b..cf84ef21d9 100644 --- a/modules/session/redis.go +++ b/modules/session/redis.go @@ -103,11 +103,6 @@ func (s *RedisStore) Flush() error { return nil } -// True if no keys have been set -func (s *RedisStore) Empty() bool { - return len(s.data) == 0 -} - // RedisProvider represents a redis session provider implementation. type RedisProvider struct { c nosql.RedisClient diff --git a/modules/session/virtual.go b/modules/session/virtual.go index fea7d11a44..1c3e1c778b 100644 --- a/modules/session/virtual.go +++ b/modules/session/virtual.go @@ -64,6 +64,7 @@ func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) { return o.provider.Read(sid) } kv := make(map[any]any) + kv["_old_uid"] = "0" return NewVirtualStore(o, sid, kv), nil } @@ -76,10 +77,7 @@ func (o *VirtualSessionProvider) Exist(sid string) bool { func (o *VirtualSessionProvider) Destroy(sid string) error { o.lock.Lock() defer o.lock.Unlock() - if o.provider.Exist(sid) { - return o.provider.Destroy(sid) - } - return nil + return o.provider.Destroy(sid) } // Regenerate regenerates a session store from old session ID to new one. @@ -161,7 +159,7 @@ func (s *VirtualStore) Release() error { // Now need to lock the provider s.p.lock.Lock() defer s.p.lock.Unlock() - if len(s.data) > 0 { + if oldUID, ok := s.data["_old_uid"]; (ok && (oldUID != "0" || len(s.data) > 1)) || (!ok && len(s.data) > 0) { // Now ensure that we don't exist! realProvider := s.p.provider @@ -198,8 +196,3 @@ func (s *VirtualStore) Flush() error { s.data = make(map[any]any) return nil } - -// True if no keys have been set -func (s *VirtualStore) Empty() bool { - return len(s.data) == 0 -} diff --git a/modules/setting/actions.go b/modules/setting/actions.go index 930e0d0bed..5cdb2cbab4 100644 --- a/modules/setting/actions.go +++ b/modules/setting/actions.go @@ -7,8 +7,6 @@ import ( "fmt" "strings" "time" - - "forgejo.org/modules/jwtx" ) // Actions settings @@ -27,16 +25,12 @@ var ( SkipWorkflowStrings []string `ini:"SKIP_WORKFLOW_STRINGS"` LimitDispatchInputs int64 `ini:"LIMIT_DISPATCH_INPUTS"` ConcurrencyGroupQueueEnabled bool `ini:"CONCURRENCY_GROUP_QUEUE_ENABLED"` - IDTokenExpirationTime int64 `ini:"ID_TOKEN_EXPIRATION_TIME"` - - KeyCfg *jwtx.KeyCfg }{ Enabled: true, DefaultActionsURL: defaultActionsURLForgejo, SkipWorkflowStrings: []string{"[skip ci]", "[ci skip]", "[no ci]", "[skip actions]", "[actions skip]"}, - LimitDispatchInputs: 100, + LimitDispatchInputs: 10, ConcurrencyGroupQueueEnabled: true, - IDTokenExpirationTime: 3600, } ) @@ -74,8 +68,7 @@ func (c logCompression) IsZstd() bool { } func loadActionsFrom(rootCfg ConfigProvider) error { - secName := "actions" - sec := rootCfg.Section(secName) + sec := rootCfg.Section("actions") err := sec.MapTo(&Actions) if err != nil { return fmt.Errorf("failed to map Actions settings: %v", err) @@ -111,9 +104,5 @@ func loadActionsFrom(rootCfg ConfigProvider) error { return fmt.Errorf("invalid [actions] LOG_COMPRESSION: %q", Actions.LogCompression) } - Actions.KeyCfg, err = loadKeyCfg(rootCfg, secName, "ID_TOKEN_", "RS256", "actions_id_token/private.pem", onlyAsymmetric()) - if err != nil { - return err - } return nil } diff --git a/modules/setting/actions_test.go b/modules/setting/actions_test.go index 1bbd4d0251..a3cd5ced44 100644 --- a/modules/setting/actions_test.go +++ b/modules/setting/actions_test.go @@ -7,8 +7,6 @@ import ( "path/filepath" "testing" - "forgejo.org/modules/test" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -157,80 +155,3 @@ DEFAULT_ACTIONS_URL = https://example.com }) } } - -func Test_getIDTokenSettingsForActions(t *testing.T) { - defer test.MockVariableValue(&AppDataPath, "/home/app/data")() - - oldActions := Actions - oldAppURL := AppURL - defer func() { - Actions = oldActions - AppURL = oldAppURL - }() - - iniStr := ` - [actions] - ` - cfg, err := NewConfigProviderFromData(iniStr) - require.NoError(t, err) - require.NoError(t, loadActionsFrom(cfg)) - - assert.Equal(t, "RS256", Actions.KeyCfg.Signing.Algorithm) - assert.Equal(t, "/home/app/data/actions_id_token/private.pem", *Actions.KeyCfg.Signing.PrivateKeyPath) - assert.EqualValues(t, 3600, Actions.IDTokenExpirationTime) - - iniStr = ` - [actions] - ID_TOKEN_SIGNING_ALGORITHM = ES256 - ID_TOKEN_SIGNING_PRIVATE_KEY_FILE = /test/test.pem - ID_TOKEN_EXPIRATION_TIME = 120 - ` - cfg, err = NewConfigProviderFromData(iniStr) - require.NoError(t, err) - require.NoError(t, loadActionsFrom(cfg)) - - assert.Equal(t, "ES256", Actions.KeyCfg.Signing.Algorithm) - assert.Equal(t, "/test/test.pem", *Actions.KeyCfg.Signing.PrivateKeyPath) - assert.EqualValues(t, 120, Actions.IDTokenExpirationTime) - - iniStr = ` - [actions] - ID_TOKEN_SIGNING_ALGORITHM = EdDSA - ID_TOKEN_SIGNING_PRIVATE_KEY_FILE = ./test/test.pem - ID_TOKEN_EXPIRATION_TIME = 123 - ` - cfg, err = NewConfigProviderFromData(iniStr) - require.NoError(t, err) - require.NoError(t, loadActionsFrom(cfg)) - - assert.Equal(t, "EdDSA", Actions.KeyCfg.Signing.Algorithm) - assert.Equal(t, "/home/app/data/test/test.pem", *Actions.KeyCfg.Signing.PrivateKeyPath) - assert.EqualValues(t, 123, Actions.IDTokenExpirationTime) - - iniStr = ` - [actions] - ID_TOKEN_SIGNING_ALGORITHM = HS256 - ` - cfg, err = NewConfigProviderFromData(iniStr) - require.NoError(t, err) - err = loadActionsFrom(cfg) - require.ErrorContains(t, err, "[actions] Unexpected algorithm: ID_TOKEN_SIGNING_ALGORITHM = HS256, needs to be one of [RS256 RS384 RS512 ES256 ES384 ES512 EdDSA]") - - iniStr = ` - [actions] - ID_TOKEN_SECRET = ABC - ` - cfg, err = NewConfigProviderFromData(iniStr) - require.NoError(t, err) - err = loadActionsFrom(cfg) - require.ErrorContains(t, err, "[actions] Invalid config key: ID_TOKEN_SECRET - must be removed") - - iniStr = ` - [actions] - ID_TOKEN_SECRET_URI = ABC - ` - cfg, err = NewConfigProviderFromData(iniStr) - require.NoError(t, err) - err = loadActionsFrom(cfg) - require.ErrorContains(t, err, "[actions] Invalid config key: ID_TOKEN_SECRET_URI - must be removed") -} diff --git a/modules/setting/admin.go b/modules/setting/admin.go index f383a5e382..7a1e071bac 100644 --- a/modules/setting/admin.go +++ b/modules/setting/admin.go @@ -26,8 +26,7 @@ func loadAdminFrom(rootCfg ConfigProvider) { } const ( - UserFeatureDeletion = "deletion" - UserFeatureManageSSHKeys = "manage_ssh_keys" - UserFeatureManageGPGKeys = "manage_gpg_keys" - UserFeatureManagePassword = "manage_password" + UserFeatureDeletion = "deletion" + UserFeatureManageSSHKeys = "manage_ssh_keys" + UserFeatureManageGPGKeys = "manage_gpg_keys" ) diff --git a/modules/setting/authorized_integration.go b/modules/setting/authorized_integration.go deleted file mode 100644 index 0a84f44afe..0000000000 --- a/modules/setting/authorized_integration.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2026 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package setting - -import "time" - -var AuthorizedIntegration = struct { - AllowedDomains string - BlockedDomains string - AllowLocalNetworks bool - RequestTimeout time.Duration - CacheTTL time.Duration -}{} - -func loadAuthorizedIntegrationFrom(rootCfg ConfigProvider) { - sec := rootCfg.Section("authorized_integration") - AuthorizedIntegration.AllowedDomains = sec.Key("ALLOWED_DOMAINS").MustString("") - AuthorizedIntegration.BlockedDomains = sec.Key("BLOCKED_DOMAINS").MustString("") - AuthorizedIntegration.AllowLocalNetworks = sec.Key("ALLOW_LOCALNETWORKS").MustBool(false) - AuthorizedIntegration.RequestTimeout = sec.Key("REQUEST_TIMEOUT").MustDuration(10 * time.Second) - AuthorizedIntegration.CacheTTL = sec.Key("CACHE_TTL").MustDuration(10 * time.Minute) -} diff --git a/modules/setting/cache.go b/modules/setting/cache.go index 519a236599..cdc7e1a971 100644 --- a/modules/setting/cache.go +++ b/modules/setting/cache.go @@ -53,7 +53,6 @@ func loadCacheFrom(rootCfg ConfigProvider) { CacheService.Adapter = sec.Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache", "twoqueue"}) switch CacheService.Adapter { case "memory": - break case "redis", "memcache": CacheService.Conn = strings.Trim(sec.Key("HOST").String(), "\" ") case "twoqueue": diff --git a/modules/setting/config.go b/modules/setting/config.go index 90f3d12d11..6299640e61 100644 --- a/modules/setting/config.go +++ b/modules/setting/config.go @@ -4,7 +4,6 @@ package setting import ( - "strings" "sync" "forgejo.org/modules/log" @@ -24,11 +23,11 @@ type OpenWithEditorApp struct { type OpenWithEditorAppsType []OpenWithEditorApp func (t OpenWithEditorAppsType) ToTextareaString() string { - var ret strings.Builder + ret := "" for _, app := range t { - ret.WriteString(app.DisplayName + " = " + app.OpenURL + "\n") + ret += app.DisplayName + " = " + app.OpenURL + "\n" } - return ret.String() + return ret } func DefaultOpenWithEditorApps() OpenWithEditorAppsType { diff --git a/modules/setting/config_env.go b/modules/setting/config_env.go index 68a7c94db2..458dbb51bb 100644 --- a/modules/setting/config_env.go +++ b/modules/setting/config_env.go @@ -51,10 +51,10 @@ func decodeEnvSectionKey(encoded string) (ok bool, section, key string) { for _, unescapeIdx := range escapeStringIndices { preceding := encoded[last:unescapeIdx[0]] if !inKey { - if before, after, ok := strings.Cut(preceding, "__"); ok { - section += before + if splitter := strings.Index(preceding, "__"); splitter > -1 { + section += preceding[:splitter] inKey = true - key += after + key += preceding[splitter+2:] } else { section += preceding } @@ -77,9 +77,9 @@ func decodeEnvSectionKey(encoded string) (ok bool, section, key string) { } remaining := encoded[last:] if !inKey { - if before, after, ok := strings.Cut(remaining, "__"); ok { - section += before - key += after + if splitter := strings.Index(remaining, "__"); splitter > -1 { + section += remaining[:splitter] + key += remaining[splitter+2:] } else { section += remaining } @@ -113,24 +113,25 @@ func decodeEnvironmentKey(prefixRegexp *regexp.Regexp, suffixFile, envKey string func EnvironmentToConfig(cfg ConfigProvider, envs []string) (changed bool) { prefixRegexp := regexp.MustCompile(EnvConfigKeyPrefixGitea) for _, kv := range envs { - before, after, ok0 := strings.Cut(kv, "=") - if !ok0 { + idx := strings.IndexByte(kv, '=') + if idx < 0 { continue } // parse the environment variable to config section name and key name - envKey := before - keyValue := after + envKey := kv[:idx] + envValue := kv[idx+1:] ok, sectionName, keyName, useFileValue := decodeEnvironmentKey(prefixRegexp, EnvConfigKeySuffixFile, envKey) if !ok { continue } // use environment value as config value, or read the file content as value if the key indicates a file + keyValue := envValue if useFileValue { - fileContent, err := os.ReadFile(keyValue) + fileContent, err := os.ReadFile(envValue) if err != nil { - log.Error("Error reading file for %s : %v", envKey, keyValue, err) + log.Error("Error reading file for %s : %v", envKey, envValue, err) continue } if bytes.HasSuffix(fileContent, []byte("\r\n")) { diff --git a/modules/setting/database.go b/modules/setting/database.go index 4299044d1f..5b8fdaa34c 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -96,7 +96,7 @@ func loadDBSetting(rootCfg ConfigProvider) { Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(0) } Database.ConnMaxIdleTime = sec.Key("CONN_MAX_IDLETIME").MustDuration(0) - Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(30) + Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(100) Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50) Database.LogSQL = sec.Key("LOG_SQL").MustBool(false) diff --git a/modules/setting/f3.go b/modules/setting/f3.go index 2412938a49..31d12294b8 100644 --- a/modules/setting/f3.go +++ b/modules/setting/f3.go @@ -3,9 +3,6 @@ package setting import ( - "os" - "path/filepath" - "forgejo.org/modules/log" ) @@ -13,10 +10,8 @@ import ( var ( F3 = struct { Enabled bool - Path string }{ Enabled: false, - Path: "f3", } ) @@ -25,17 +20,7 @@ func LoadF3Setting() { } func loadF3From(rootCfg ConfigProvider) { - if err := rootCfg.Section("f3").MapTo(&F3); err != nil { + if err := rootCfg.Section("F3").MapTo(&F3); err != nil { log.Fatal("Failed to map F3 settings: %v", err) } - - if !filepath.IsAbs(F3.Path) { - F3.Path = filepath.Join(AppDataPath, F3.Path) - } else { - F3.Path = filepath.Clean(F3.Path) - } - - if err := os.MkdirAll(F3.Path, os.ModePerm); err != nil { - log.Fatal("Failed to create F3 path %s: %v", F3.Path, err) - } } diff --git a/modules/setting/f3_test.go b/modules/setting/f3_test.go deleted file mode 100644 index 76c4fe015c..0000000000 --- a/modules/setting/f3_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2024 The Forgejo Authors. -// SPDX-License-Identifier: GPLv3-or-later - -package setting - -import ( - "fmt" - "testing" - - "forgejo.org/modules/test" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestSettingF3(t *testing.T) { - restoreF3 := test.MockProtect(&F3) - restoreAppDataPath := test.MockProtect(&AppDataPath) - restore := func() { - restoreF3() - restoreAppDataPath() - } - defer restore() - - t.Run("enabled", func(t *testing.T) { - restore() - cfg, err := NewConfigProviderFromData(` -[f3] -ENABLED = true -`) - require.NoError(t, err) - loadF3From(cfg) - assert.True(t, F3.Enabled) - assert.DirExists(t, F3.Path) - }) - - t.Run("disabled by default", func(t *testing.T) { - restore() - cfg, err := NewConfigProviderFromData(` -[f3] -`) - require.NoError(t, err) - loadF3From(cfg) - assert.False(t, F3.Enabled) - }) - - t.Run("default f3 path", func(t *testing.T) { - restore() - cfg, err := NewConfigProviderFromData(` -[f3] -ENABLED = true -`) - require.NoError(t, err) - AppDataPath = t.TempDir() - loadF3From(cfg) - assert.Equal(t, AppDataPath+"/f3", F3.Path) - assert.DirExists(t, F3.Path) - }) - - t.Run("absolute f3 path", func(t *testing.T) { - restore() - other := t.TempDir() - cfg, err := NewConfigProviderFromData(fmt.Sprintf(` -[f3] -ENABLED = true -PATH = %[1]s -`, other)) - require.NoError(t, err) - AppDataPath = t.TempDir() - loadF3From(cfg) - assert.Equal(t, other, F3.Path) - assert.DirExists(t, F3.Path) - }) -} diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index 47fc2e560d..a890a4a328 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -82,7 +82,6 @@ func checkReplyToAddress() error { case 0: return fmt.Errorf("%s must appear in the user part of the address (before the @)", IncomingEmail.TokenPlaceholder) case 1: - break default: return fmt.Errorf("%s must appear only once", IncomingEmail.TokenPlaceholder) } diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go index 948dae0bea..b112a50cfa 100644 --- a/modules/setting/indexer.go +++ b/modules/setting/indexer.go @@ -108,7 +108,7 @@ func loadIndexerFrom(rootCfg ConfigProvider) { // IndexerGlobFromString parses a comma separated list of patterns and returns a glob.Glob slice suited for repo indexing func IndexerGlobFromString(globstr string) []Glob { extarr := make([]Glob, 0, 10) - for expr := range strings.SplitSeq(strings.ToLower(globstr), ",") { + for _, expr := range strings.Split(strings.ToLower(globstr), ",") { expr = strings.TrimSpace(expr) if expr != "" { if g, err := glob.Compile(expr, '.', '/'); err != nil { diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go index a97ed8d40f..452bfae737 100644 --- a/modules/setting/lfs.go +++ b/modules/setting/lfs.go @@ -7,7 +7,7 @@ import ( "fmt" "time" - "forgejo.org/modules/jwtx" + "forgejo.org/modules/generate" ) // LFS represents the server-side configuration for Git LFS. @@ -16,13 +16,13 @@ import ( // Could be refactored in the future while keeping backwards compatibility. var LFS = struct { StartServer bool `ini:"LFS_START_SERVER"` + JWTSecretBytes []byte `ini:"-"` HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"` MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"` LocksPagingNum int `ini:"LFS_LOCKS_PAGING_NUM"` MaxBatchSize int `ini:"LFS_MAX_BATCH_SIZE"` - SigningKey jwtx.SigningKey - Storage *Storage + Storage *Storage }{} // LFSClient represents configuration for Gitea's LFS clients, for example: mirroring upstream Git LFS @@ -77,12 +77,21 @@ func loadLFSFrom(rootCfg ConfigProvider) error { return nil } - keyCfg, err := loadKeyCfg(rootCfg, "server", "LFS_JWT_", "HS256", "lfs/private.pem") - if err == nil { - LFS.SigningKey, err = jwtx.InitSigningKey(&keyCfg.Signing) - } + jwtSecretBase64 := loadSecret(rootCfg.Section("server"), "LFS_JWT_SECRET_URI", "LFS_JWT_SECRET") + LFS.JWTSecretBytes, err = generate.DecodeJwtSecret(jwtSecretBase64) if err != nil { - return fmt.Errorf("lfs key initialization failed: %v", err) + LFS.JWTSecretBytes, jwtSecretBase64 = generate.NewJwtSecret() + + // Save secret + saveCfg, err := rootCfg.PrepareSaving() + if err != nil { + return fmt.Errorf("error saving JWT Secret for custom config: %v", err) + } + rootCfg.Section("server").Key("LFS_JWT_SECRET").SetValue(jwtSecretBase64) + saveCfg.Section("server").Key("LFS_JWT_SECRET").SetValue(jwtSecretBase64) + if err := saveCfg.Save(); err != nil { + return fmt.Errorf("error saving JWT Secret for custom config: %v", err) + } } return nil diff --git a/modules/setting/log.go b/modules/setting/log.go index 7799f8187b..ecc591fd35 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -269,8 +269,8 @@ func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, logger } var eventWriters []log.EventWriter - modes := strings.SplitSeq(modeVal, ",") - for modeName := range modes { + modes := strings.Split(modeVal, ",") + for _, modeName := range modes { modeName = strings.TrimSpace(modeName) if modeName == "" { continue diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index a84758c505..b43484a90f 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -235,7 +235,6 @@ func loadMailerFrom(rootCfg ConfigProvider) { } } case "dummy": // just mention and do nothing - break } if MailService.From != "" { diff --git a/modules/setting/markup.go b/modules/setting/markup.go index 0ece86dfd1..4ab9e7b2d1 100644 --- a/modules/setting/markup.go +++ b/modules/setting/markup.go @@ -85,8 +85,8 @@ func loadMarkupFrom(rootCfg ConfigProvider) { func newMarkupSanitizer(name string, sec ConfigSection) { rule, ok := createMarkupSanitizerRule(name, sec) if ok { - if after, ok0 := strings.CutPrefix(name, "sanitizer."); ok0 { - names := strings.SplitN(after, ".", 2) + if strings.HasPrefix(name, "sanitizer.") { + names := strings.SplitN(strings.TrimPrefix(name, "sanitizer."), ".", 2) name = names[0] } for _, renderer := range ExternalMarkupRenderers { diff --git a/modules/setting/mirror.go b/modules/setting/mirror.go index 083c67db45..58c57c5c95 100644 --- a/modules/setting/mirror.go +++ b/modules/setting/mirror.go @@ -48,7 +48,11 @@ func loadMirrorFrom(rootCfg ConfigProvider) { Mirror.MinInterval = 1 * time.Minute } if Mirror.DefaultInterval < Mirror.MinInterval { - Mirror.DefaultInterval = max(time.Hour*8, Mirror.MinInterval) + if time.Hour*8 < Mirror.MinInterval { + Mirror.DefaultInterval = Mirror.MinInterval + } else { + Mirror.DefaultInterval = time.Hour * 8 + } log.Warn("Mirror.DefaultInterval is less than Mirror.MinInterval, set to %s", Mirror.DefaultInterval.String()) } } diff --git a/modules/setting/oauth2.go b/modules/setting/oauth2.go index 8598b7b24d..9e95e1c6a9 100644 --- a/modules/setting/oauth2.go +++ b/modules/setting/oauth2.go @@ -5,10 +5,10 @@ package setting import ( "math" + "path/filepath" "sync/atomic" "forgejo.org/modules/generate" - "forgejo.org/modules/jwtx" "forgejo.org/modules/log" ) @@ -96,15 +96,18 @@ var OAuth2 = struct { AccessTokenExpirationTime int64 RefreshTokenExpirationTime int64 InvalidateRefreshTokens bool + JWTSigningAlgorithm string `ini:"JWT_SIGNING_ALGORITHM"` + JWTSigningPrivateKeyFile string `ini:"JWT_SIGNING_PRIVATE_KEY_FILE"` MaxTokenLength int DefaultApplications []string EnableAdditionalGrantScopes bool - KeyCfg *jwtx.KeyCfg }{ Enabled: true, AccessTokenExpirationTime: 3600, RefreshTokenExpirationTime: 730, InvalidateRefreshTokens: true, + JWTSigningAlgorithm: "RS256", + JWTSigningPrivateKeyFile: "jwt/private.pem", MaxTokenLength: math.MaxInt16, DefaultApplications: []string{"git-credential-oauth", "git-credential-manager", "tea"}, EnableAdditionalGrantScopes: false, @@ -123,26 +126,30 @@ func loadOAuth2From(rootCfg ConfigProvider) { OAuth2.Enabled = sec.Key("ENABLE").MustBool(OAuth2.Enabled) } + if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) { + OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile) + } + // FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET" // Because this secret is also used as GeneralTokenSigningSecret (as a quick not-that-breaking fix for some legacy problems). // Including: CSRF token, account validation token, etc ... // In main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...) + jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET") if InstallLock { - signingKey, err := loadSymmeticSigningKeyCfg(rootCfg, sec, "JWT_") + jwtSecretBytes, err := generate.DecodeJwtSecret(jwtSecretBase64) if err != nil { - log.Fatal("%v", err) + jwtSecretBytes, jwtSecretBase64 = generate.NewJwtSecret() + saveCfg, err := rootCfg.PrepareSaving() + if err != nil { + log.Fatal("save oauth2.JWT_SECRET failed: %v", err) + } + rootCfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64) + saveCfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64) + if err := saveCfg.Save(); err != nil { + log.Fatal("save oauth2.JWT_SECRET failed: %v", err) + } } - generalSigningSecret.Store(signingKey) - } - - if !OAuth2.Enabled { - return - } - - var err error - OAuth2.KeyCfg, err = loadKeyCfg(rootCfg, "oauth2", "JWT_", "RS256", "jwt/private.pem") - if err != nil { - log.Fatal("oauth2 key initialization failed: %v", err) + generalSigningSecret.Store(&jwtSecretBytes) } } diff --git a/modules/setting/pwa.go b/modules/setting/pwa.go deleted file mode 100644 index 2c17efd715..0000000000 --- a/modules/setting/pwa.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package setting - -import ( - "forgejo.org/modules/json" - "forgejo.org/modules/log" -) - -type PwaConfig struct { - Standalone bool -} - -var PWA = PwaConfig{ - Standalone: false, -} - -func loadPWAFrom(rootCfg ConfigProvider) { - sec := rootCfg.Section("pwa") - if err := sec.MapTo(&PWA); err != nil { - log.Fatal("Failed to map [pwa] settings: %v", err) - } -} - -type manifestIcon struct { - Src string `json:"src"` - Type string `json:"type"` - Sizes string `json:"sizes"` -} - -type manifestJSON struct { - Name string `json:"name"` - ShortName string `json:"short_name"` - StartURL string `json:"start_url"` - Icons []manifestIcon `json:"icons"` - Display string `json:"display,omitempty"` -} - -func GetManifestJSON() ([]byte, error) { - manifest := manifestJSON{ - Name: AppName, - ShortName: AppName, - StartURL: AppURL, - Icons: []manifestIcon{ - { - Src: AbsoluteAssetURL + "/assets/img/logo.png", - Type: "image/png", - Sizes: "512x512", - }, - { - Src: AbsoluteAssetURL + "/assets/img/logo.svg", - Type: "image/svg+xml", - Sizes: "512x512", - }, - }, - } - - if PWA.Standalone { - manifest.Display = "standalone" - } - - return json.Marshal(manifest) -} diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 001032e3cb..7e774f0139 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -96,6 +96,7 @@ var ( DefaultMergeMessageOfficialApproversOnly bool DefaultUpdateStyle string PopulateSquashCommentWithCommitMessages bool + AddCoCommitterTrailers bool RetargetChildrenOnMerge bool } `ini:"repository.pull-request"` @@ -226,6 +227,7 @@ var ( DefaultMergeMessageOfficialApproversOnly bool DefaultUpdateStyle string PopulateSquashCommentWithCommitMessages bool + AddCoCommitterTrailers bool RetargetChildrenOnMerge bool }{ WorkInProgressPrefixes: []string{"WIP:", "[WIP]"}, @@ -241,6 +243,7 @@ var ( DefaultMergeMessageOfficialApproversOnly: true, DefaultUpdateStyle: "merge", PopulateSquashCommentWithCommitMessages: false, + AddCoCommitterTrailers: true, RetargetChildrenOnMerge: true, }, diff --git a/modules/setting/security.go b/modules/setting/security.go index 586989101d..347912f248 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -4,15 +4,12 @@ package setting import ( - "fmt" "net/url" "os" - "path/filepath" "strings" "forgejo.org/modules/auth/password/hash" "forgejo.org/modules/generate" - "forgejo.org/modules/jwtx" "forgejo.org/modules/keying" "forgejo.org/modules/log" ) @@ -42,31 +39,6 @@ var ( DisableQueryAuthToken bool ) -/* - * key loading is a two-stage process to avoid complications for unit tests: - * - * For symmetric keys, we want to add a random key to the configuration. We would - * not want to change the configuration after loading has completed to maintain - * isolation. So from this perspective, we would want to initialize keys only - * during setting.load...From() - * - * For asymmetric keys, we want to create a random private key _file_. - * Doing so during the setting load phase, however, creates key files for unit - * tests, because they usually complete the full settings load phase, but do not - * initialize modules which they do not need. Depending on the test case, it - * might not provide a writable AppDataPath, or it would leave private key files - * in the source tree. All of this can be avoided with - * specifically adjusting the ini loaded for unit tests, but adds considerable - * friction. - * - * So to avoid all this, we split key loading in two phases: - * - settings parse the config and save missing symmetric keys - * - module init takes the parsed config, creates missing asymmetric keys and - * creates the actual signingkey objects - * - * jwtx.SigningKeyCfg and jwtx.KeyCfg are used for handover - */ - // loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set // If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear. func loadSecret(sec ConfigSection, uriKey, verbatimKey string) string { @@ -81,29 +53,16 @@ func loadSecret(sec ConfigSection, uriKey, verbatimKey string) string { if uri == "" { return verbatim } - verbatim, err := loadSecretFromURI(uri) - if err == nil { - return verbatim - } - log.Fatal("%s: %w", uriKey, err) - // unreached - return "" -} -func loadSecretFromURI(uri string) (string, error) { tempURI, err := url.Parse(uri) if err != nil { - return "", fmt.Errorf("Failed to parse %s: %v", uri, err) + log.Fatal("Failed to parse %s (%s): %v", uriKey, uri, err) } switch tempURI.Scheme { case "file": - path := tempURI.RequestURI() - if !filepath.IsAbs(path) { - path = filepath.Join(AppDataPath, path) - } - buf, err := os.ReadFile(path) + buf, err := os.ReadFile(tempURI.RequestURI()) if err != nil { - return "", fmt.Errorf("Failed to read %s: %v", path, err) + log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err) } val := strings.TrimSpace(string(buf)) if val == "" { @@ -111,135 +70,17 @@ func loadSecretFromURI(uri string) (string, error) { // For example: if INTERNAL_TOKEN_URI=file:///empty-file, // Then if the token is re-generated during installation and saved to INTERNAL_TOKEN // Then INTERNAL_TOKEN and INTERNAL_TOKEN_URI both exist, that's a fatal error (they shouldn't) - return "", fmt.Errorf("Failed to read %s: the file is empty", path) + log.Fatal("Failed to read %s (%s): the file is empty", uriKey, tempURI.RequestURI()) } - return val, nil + return val // only file URIs are allowed default: - return "", fmt.Errorf("Unsupported URI-Scheme %q in %q", tempURI.Scheme, uri) + log.Fatal("Unsupported URI-Scheme %q (%q = %q)", tempURI.Scheme, uriKey, uri) + return "" } } -// createSymmeticSigningKey creates a new symmetric signing key and saves it to -// the setting named cfgSecret (usually [PFX_]SECRET) in section cfgSection -func createSymmeticSigningKeyCfg(rootCfg ConfigProvider, cfgSection, cfgSecret string) (*[]byte, error) { - jwtSecretBytes, jwtSecretBase64 := generate.NewJwtSecret() - saveCfg, err := rootCfg.PrepareSaving() - if err == nil { - rootCfg.Section(cfgSection).Key(cfgSecret).SetValue(jwtSecretBase64) - saveCfg.Section(cfgSection).Key(cfgSecret).SetValue(jwtSecretBase64) - err = saveCfg.Save() - } - if err != nil { - return nil, fmt.Errorf("save %s.%s failed: %v", cfgSection, cfgSecret, err) - } - return &jwtSecretBytes, nil -} - -// loadSymmeticSigningKey loads a signing key and creates it unless present -// loads from [pfx]SECRET_URI -// loads from or saves to [pfx]SECRET -// in section sec -func loadSymmeticSigningKeyCfg(rootCfg ConfigProvider, sec ConfigSection, pfx string) (*[]byte, error) { - cfgSecretURI := pfx + "SECRET_URI" - cfgSecret := pfx + "SECRET" - - secretBase64 := loadSecret(sec, cfgSecretURI, cfgSecret) - secret, err := generate.DecodeJwtSecret(secretBase64) - if err == nil { - return &secret, nil - } - - log.Info("[%s] %s or %s failed loading: %v - creating new key", sec.Name(), cfgSecret, cfgSecretURI, err) - return createSymmeticSigningKeyCfg(rootCfg, sec.Name(), cfgSecret) -} - -// loadAsymmeticSigningKey loads a signing key from [pfx]SIGNING_PRIVATE_KEY_FILE -// or creates it if it does not exist -func loadAsymmeticSigningKeyPath(sec ConfigSection, pfx, defaultFile string) *string { - cfgFile := pfx + "SIGNING_PRIVATE_KEY_FILE" - keyPath := sec.Key(cfgFile).MustString(defaultFile) - if !filepath.IsAbs(keyPath) { - keyPath = filepath.Join(AppDataPath, keyPath) - } - return &keyPath -} - -type checkKeyCfg func(rootCfg ConfigProvider, cfgSection, pfx string) error - -func onlyAsymmetric() checkKeyCfg { - return func(rootCfg ConfigProvider, cfgSection, pfx string) error { - sec := rootCfg.Section(cfgSection) - cfgAlg := pfx + "SIGNING_ALGORITHM" - - if sec.HasKey(cfgAlg) { - alg := sec.Key(cfgAlg).String() - if !jwtx.IsValidAsymmetricAlgorithm(alg) { - return fmt.Errorf("Unexpected algorithm: %s = %s, needs to be one of %v", - cfgAlg, alg, jwtx.ValidAsymmetricAlgorithms) - } - } - - noCfg := []string{ - pfx + "SECRET_URI", - pfx + "SECRET", - } - - for _, cfg := range noCfg { - if sec.HasKey(cfg) { - return fmt.Errorf("Invalid config key: %s - must be removed", cfg) - } - } - - return nil - } -} - -// loadSigningKey() loads a or creates signing key based on settings in section cfgSection -// [pfx]SIGNING_ALGORITHM determines the algorithm -// [pfx]SECRET is a literal secret for symmetric algorithms -// [pfx]SECRET_URI is the uri of a secret for symmetric algorithms -// [pfx]SIGNING_PRIVATE_KEY_FILE is a file with a private key for asymmetric algorithms -// -// [pfx]SECRET might get written to literally in the config if needed but not present - -func loadSigningKeyCfg(rootCfg ConfigProvider, cfgSection, pfx, defaultAlg, defaultPrivateKeyFile string, checks ...checkKeyCfg) (*jwtx.SigningKeyCfg, error) { - for _, check := range checks { - err := check(rootCfg, cfgSection, pfx) - if err != nil { - return nil, err - } - } - - sec := rootCfg.Section(cfgSection) - cfgAlg := pfx + "SIGNING_ALGORITHM" - - algorithm := sec.Key(cfgAlg).MustString(defaultAlg) - - cfg := jwtx.SigningKeyCfg{Algorithm: algorithm} - var err error - - if jwtx.IsValidSymmetricAlgorithm(algorithm) { - cfg.SecretBytes, err = loadSymmeticSigningKeyCfg(rootCfg, sec, pfx) - } else if jwtx.IsValidAsymmetricAlgorithm(algorithm) { - cfg.PrivateKeyPath = loadAsymmeticSigningKeyPath(sec, pfx, defaultPrivateKeyFile) - } else { - err = fmt.Errorf("invalid algorithm: %s = %s", cfgAlg, algorithm) - } - - return &cfg, err -} - -func loadKeyCfg(rootCfg ConfigProvider, cfgSection, pfx, defaultAlg, defaultPrivateKeyFile string, checks ...checkKeyCfg) (*jwtx.KeyCfg, error) { - signing, err := loadSigningKeyCfg(rootCfg, cfgSection, pfx, defaultAlg, defaultPrivateKeyFile, checks...) - if err != nil { - err = fmt.Errorf("[%s] %v", cfgSection, err) - return nil, err - } - return &jwtx.KeyCfg{Signing: signing}, nil -} - // generateSaveInternalToken generates and saves the internal token to app.ini func generateSaveInternalToken(rootCfg ConfigProvider) { token, err := generate.NewInternalToken() @@ -273,7 +114,7 @@ func loadSecurityFrom(rootCfg ConfigProvider) { GlobalTwoFactorRequirement = NewTwoFactorRequirementType(sec.Key("GLOBAL_TWO_FACTOR_REQUIREMENT").String()) - CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("persistent") + CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible") ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER") ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL") diff --git a/modules/setting/server.go b/modules/setting/server.go index 11c4ff8212..3ff91d2cde 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -1,10 +1,10 @@ // Copyright 2023 The Gitea Authors. All rights reserved. -// Copyright 2024 The Forgejo Authors. // SPDX-License-Identifier: MIT package setting import ( + "encoding/base64" "net" "net/url" "path" @@ -13,6 +13,7 @@ import ( "strings" "time" + "forgejo.org/modules/json" "forgejo.org/modules/log" "forgejo.org/modules/util" @@ -110,8 +111,50 @@ var ( PerWritePerKbTimeout = 10 * time.Second StaticURLPrefix string AbsoluteAssetURL string + + ManifestData string ) +// MakeManifestData generates web app manifest JSON +func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte { + type manifestIcon struct { + Src string `json:"src"` + Type string `json:"type"` + Sizes string `json:"sizes"` + } + + type manifestJSON struct { + Name string `json:"name"` + ShortName string `json:"short_name"` + StartURL string `json:"start_url"` + Icons []manifestIcon `json:"icons"` + } + + bytes, err := json.Marshal(&manifestJSON{ + Name: appName, + ShortName: appName, + StartURL: appURL, + Icons: []manifestIcon{ + { + Src: absoluteAssetURL + "/assets/img/logo.png", + Type: "image/png", + Sizes: "512x512", + }, + { + Src: absoluteAssetURL + "/assets/img/logo.svg", + Type: "image/svg+xml", + Sizes: "512x512", + }, + }, + }) + if err != nil { + log.Error("unable to marshal manifest JSON. Error: %v", err) + return make([]byte, 0) + } + + return bytes +} + // MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string { parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/")) @@ -268,6 +311,9 @@ func loadServerFrom(rootCfg ConfigProvider) { AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix) AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed) + manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL) + ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes) + var defaultLocalURL string switch Protocol { case HTTPUnix: diff --git a/modules/setting/session.go b/modules/setting/session.go index 1d88ad334c..e9ff9bf0bc 100644 --- a/modules/setting/session.go +++ b/modules/setting/session.go @@ -34,7 +34,7 @@ var SessionConfig = struct { // SameSite declares if your cookie should be restricted to a first-party or same-site context. Valid strings are "none", "lax", "strict". Default is "lax" SameSite http.SameSite }{ - CookieName: "session", + CookieName: "i_like_gitea", Gclifetime: 86400, Maxlifetime: 86400, SameSite: http.SameSiteLaxMode, @@ -48,7 +48,7 @@ func loadSessionFrom(rootCfg ConfigProvider) { if SessionConfig.Provider == "file" && !filepath.IsAbs(SessionConfig.ProviderConfig) { SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig) } - SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("session") + SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea") SessionConfig.CookiePath = AppSubURL if SessionConfig.CookiePath == "" { SessionConfig.CookiePath = "/" diff --git a/modules/setting/setting.go b/modules/setting/setting.go index bd3dec1f84..9644d9b83b 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -113,14 +113,13 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error { // WARNING: don't change the sequence except you know what you are doing. loadRunModeFrom(cfg) loadLogGlobalFrom(cfg) - loadPWAFrom(cfg) loadServerFrom(cfg) loadSSHFrom(cfg) mustCurrentRunUserMatch(cfg) // it depends on the SSH config, only non-builtin SSH server requires this check - loadSecurityFrom(cfg) loadOAuth2From(cfg) + loadSecurityFrom(cfg) if err := loadAttachmentFrom(cfg); err != nil { return err } @@ -167,7 +166,7 @@ func loadRunModeFrom(rootCfg ConfigProvider) { // The following is a purposefully undocumented option. Please do not run Forgejo as root. It will only cause future headaches. // Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly. unsafeAllowRunAsRoot := ConfigSectionKeyBool(rootSec, "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT") - unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || optional.ParseBool(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).ValueOrDefault(false) + unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || optional.ParseBool(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value() RunMode = os.Getenv("GITEA_RUN_MODE") if RunMode == "" { RunMode = rootSec.Key("RUN_MODE").MustString("prod") @@ -226,7 +225,6 @@ func LoadSettings() { loadProjectFrom(CfgProvider) loadMimeTypeMapFrom(CfgProvider) loadF3From(CfgProvider) - loadAuthorizedIntegrationFrom(CfgProvider) } // LoadSettingsForInstall initializes the settings for install diff --git a/modules/setting/setting_test.go b/modules/setting/setting_test.go index 9867b94aaf..1fef9e068a 100644 --- a/modules/setting/setting_test.go +++ b/modules/setting/setting_test.go @@ -8,7 +8,6 @@ import ( "testing" "forgejo.org/modules/json" - "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -30,20 +29,10 @@ func TestMakeAbsoluteAssetURL(t *testing.T) { } func TestMakeManifestData(t *testing.T) { - jsonBytes, err := GetManifestJSON() - require.NoError(t, err) + jsonBytes := MakeManifestData(`Example App '\"`, "https://example.com", "https://example.com/foo/bar") assert.True(t, json.Valid(jsonBytes)) } -func TestMakeManifestDataStandalone(t *testing.T) { - defer test.MockVariableValue(&PWA.Standalone, true)() - - jsonBytes, err := GetManifestJSON() - require.NoError(t, err) - assert.True(t, json.Valid(jsonBytes)) - assert.Contains(t, string(jsonBytes), `"standalone"`) -} - func TestLoadServiceDomainListsForFederation(t *testing.T) { oldAppURL := AppURL oldFederation := Federation diff --git a/modules/setting/storage.go b/modules/setting/storage.go index 93958219a8..532842064c 100644 --- a/modules/setting/storage.go +++ b/modules/setting/storage.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "path/filepath" - "slices" "strings" ) @@ -28,7 +27,12 @@ var storageTypes = []StorageType{ // IsValidStorageType returns true if the given storage type is valid func IsValidStorageType(storageType StorageType) bool { - return slices.Contains(storageTypes, storageType) + for _, t := range storageTypes { + if t == storageType { + return true + } + } + return false } // MinioStorageConfig represents the configuration for a minio storage @@ -132,11 +136,11 @@ func getStorageSectionByType(rootCfg ConfigProvider, typ string) (ConfigSection, targetType := targetSec.Key("STORAGE_TYPE").String() if targetType == "" { if !IsValidStorageType(StorageType(typ)) { - return nil, 0, fmt.Errorf("unknown storage type %q", typ) + return nil, 0, fmt.Errorf("unknow storage type %q", typ) } targetSec.Key("STORAGE_TYPE").SetValue(typ) } else if !IsValidStorageType(StorageType(targetType)) { - return nil, 0, fmt.Errorf("unknown storage type %q for section storage.%v", targetType, typ) + return nil, 0, fmt.Errorf("unknow storage type %q for section storage.%v", targetType, typ) } return targetSec, targetSecIsTyp, nil @@ -162,7 +166,7 @@ func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec Confi } } - // check storage name thirdly + // check stoarge name thirdly targetSec, _ := rootCfg.GetSection(storageSectionName + "." + name) if targetSec != nil { targetType := targetSec.Key("STORAGE_TYPE").String() diff --git a/modules/setting/ui.go b/modules/setting/ui.go index abe902dfc6..61378e0596 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -55,12 +55,10 @@ var UI = struct { } `ini:"ui.csv"` Admin struct { - UserPagingNum int - RepoPagingNum int - NoticePagingNum int - OrgPagingNum int - FederationHostPagingNum int - FederationUserPagingNum int + UserPagingNum int + RepoPagingNum int + NoticePagingNum int + OrgPagingNum int } `ini:"ui.admin"` User struct { RepoPagingNum int @@ -116,19 +114,15 @@ var UI = struct { MaxRows: 2500, }, Admin: struct { - UserPagingNum int - RepoPagingNum int - NoticePagingNum int - OrgPagingNum int - FederationHostPagingNum int - FederationUserPagingNum int + UserPagingNum int + RepoPagingNum int + NoticePagingNum int + OrgPagingNum int }{ - UserPagingNum: 50, - RepoPagingNum: 50, - NoticePagingNum: 25, - OrgPagingNum: 50, - FederationHostPagingNum: 50, - FederationUserPagingNum: 50, + UserPagingNum: 50, + RepoPagingNum: 50, + NoticePagingNum: 25, + OrgPagingNum: 50, }, User: struct { RepoPagingNum int diff --git a/modules/storage/helper_test.go b/modules/storage/helper_test.go index ae32700c09..dd30c9b8ac 100644 --- a/modules/storage/helper_test.go +++ b/modules/storage/helper_test.go @@ -40,7 +40,7 @@ func Test_discardStorage(t *testing.T) { { got, err := tt.URL("path", "name", nil) assert.Nil(t, got) - require.Error(t, err, string(tt)) + require.Errorf(t, err, string(tt)) } { err := tt.IterateObjects("", func(_ string, _ Object) error { return nil }) diff --git a/modules/storage/storage.go b/modules/storage/storage.go index fe9222060f..db081e0768 100644 --- a/modules/storage/storage.go +++ b/modules/storage/storage.go @@ -164,7 +164,6 @@ func initLFS() (err error) { LFS = DiscardStorage("LFS isn't enabled") return nil } - log.Info("Initialising LFS storage with type: %s", setting.LFS.Storage.Type) LFS, err = NewStorage(setting.LFS.Storage.Type, setting.LFS.Storage) return err diff --git a/modules/structs/action.go b/modules/structs/action.go index 82c530cdf9..f47b228d75 100644 --- a/modules/structs/action.go +++ b/modules/structs/action.go @@ -10,14 +10,8 @@ import ( // ActionRunJob represents a job of a run // swagger:model type ActionRunJob struct { - // Identifier of this job. + // the action run job id ID int64 `json:"id"` - // Identifier of the workflow run this job belongs to. - RunID int64 `json:"run_id"` - // How many times the job has been attempted including the current attempt. - Attempt int64 `json:"attempt"` - // Opaque identifier that uniquely identifies a single attempt of a job. - Handle string `json:"handle"` // the repository id RepoID int64 `json:"repo_id"` // the owner id @@ -72,13 +66,13 @@ type ActionRun struct { // the current status of this run Status string `json:"status"` // when the action run was started - Started time.Time `json:"started"` + Started time.Time `json:"started,omitempty"` // when the action run was stopped - Stopped time.Time `json:"stopped"` + Stopped time.Time `json:"stopped,omitempty"` // when the action run was created - Created time.Time `json:"created"` + Created time.Time `json:"created,omitempty"` // when the action run was last updated - Updated time.Time `json:"updated"` + Updated time.Time `json:"updated,omitempty"` // how long the action run ran for Duration time.Duration `json:"duration,omitempty"` // the url of this action run @@ -90,26 +84,3 @@ type ListActionRunResponse struct { Entries []*ActionRun `json:"workflow_runs"` TotalCount int64 `json:"total_count"` } - -// ActionArtifact represents an artifact of a workflow run -// swagger:model -type ActionArtifact struct { - // the artifact's ID - ID int64 `json:"id"` - // the artifact's name - Name string `json:"name"` - // the total size of the artifact in bytes - SizeInBytes int64 `json:"size_in_bytes"` - // the URL to download the artifact zip archive - ArchiveDownloadURL string `json:"archive_download_url"` - // whether the artifact has expired - Expired bool `json:"expired"` - // the ID of the workflow run that produced this artifact - RunID int64 `json:"run_id"` - // swagger:strfmt date-time - CreatedAt time.Time `json:"created_at"` - // swagger:strfmt date-time - UpdatedAt time.Time `json:"updated_at"` - // swagger:strfmt date-time - ExpiresAt time.Time `json:"expires_at"` -} diff --git a/modules/structs/issue.go b/modules/structs/issue.go index 37c71f5736..7b7397dc4b 100644 --- a/modules/structs/issue.go +++ b/modules/structs/issue.go @@ -17,7 +17,7 @@ import ( type StateType string const ( - // StateOpen pr is opened + // StateOpen pr is opend StateOpen StateType = "open" // StateClosed pr is closed StateClosed StateType = "closed" @@ -204,7 +204,7 @@ func (l *IssueTemplateLabels) UnmarshalYAML(value *yaml.Node) error { if err != nil { return err } - for v := range strings.SplitSeq(str, ",") { + for _, v := range strings.Split(str, ",") { if v = strings.TrimSpace(v); v == "" { continue } diff --git a/modules/structs/issue_comment.go b/modules/structs/issue_comment.go index 766442374d..9ecb4a1789 100644 --- a/modules/structs/issue_comment.go +++ b/modules/structs/issue_comment.go @@ -9,48 +9,33 @@ import ( // Comment represents a comment on a commit or issue type Comment struct { - // The identifier of the comment - ID int64 `json:"id"` - // The HTML URL of the comment - HTMLURL string `json:"html_url"` - // The HTML URL of the pull request if the comment is posted on a pull request, else empty string - PRURL string `json:"pull_request_url"` - // The HTML URL of the issue if the comment is posted on an issue, else empty string - IssueURL string `json:"issue_url"` - // The user that posted the comment if it was posted locally - Poster *User `json:"user"` - // The original author that posted the comment if it was not posted locally, else empty string - OriginalAuthor string `json:"original_author"` - // The ID of the original author that posted the comment if it was not posted locally, else 0 - OriginalAuthorID int64 `json:"original_author_id"` - // The body of the comment - Body string `json:"body"` - // The attachments to the comment - Attachments []*Attachment `json:"assets"` - // The time of the comment's creation + ID int64 `json:"id"` + HTMLURL string `json:"html_url"` + PRURL string `json:"pull_request_url"` + IssueURL string `json:"issue_url"` + Poster *User `json:"user"` + OriginalAuthor string `json:"original_author"` + OriginalAuthorID int64 `json:"original_author_id"` + Body string `json:"body"` + Attachments []*Attachment `json:"assets"` // swagger:strfmt date-time Created time.Time `json:"created_at"` - // The time of the comment's update // swagger:strfmt date-time Updated time.Time `json:"updated_at"` } // CreateIssueCommentOption options for creating a comment on an issue type CreateIssueCommentOption struct { - // The body of the comment // required:true Body string `json:"body" binding:"Required"` - // The time of the comment's update, needs admin or repository owner permission // swagger:strfmt date-time Updated *time.Time `json:"updated_at"` } // EditIssueCommentOption options for editing a comment type EditIssueCommentOption struct { - // The body of the comment // required: true Body string `json:"body" binding:"Required"` - // The time of the comment's update, needs admin or repository owner permission // swagger:strfmt date-time Updated *time.Time `json:"updated_at"` } diff --git a/modules/structs/org_team.go b/modules/structs/org_team.go index fd83a75739..a51dcf1d19 100644 --- a/modules/structs/org_team.go +++ b/modules/structs/org_team.go @@ -17,7 +17,7 @@ type Team struct { // // Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions. Units []string `json:"units"` - // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.pulls":"owner","repo.releases":"none","repo.wiki":"admin","repo.ext_wiki":"none","repo.projects":"none","repo.packages":"none","repo.actions":"none"} + // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"} UnitsMap map[string]string `json:"units_map"` CanCreateOrgRepo bool `json:"can_create_org_repo"` } @@ -34,7 +34,7 @@ type CreateTeamOption struct { // // Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions. Units []string `json:"units"` - // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.pulls":"owner","repo.releases":"none","repo.wiki":"admin","repo.ext_wiki":"none","repo.projects":"none","repo.packages":"none","repo.actions":"none"} + // example: {"repo.actions","repo.packages","repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"} UnitsMap map[string]string `json:"units_map"` CanCreateOrgRepo bool `json:"can_create_org_repo"` } @@ -51,7 +51,7 @@ type EditTeamOption struct { // // Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions. Units []string `json:"units"` - // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.pulls":"owner","repo.releases":"none","repo.wiki":"admin","repo.ext_wiki":"none","repo.projects":"none","repo.packages":"none","repo.actions":"none"} + // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"} UnitsMap map[string]string `json:"units_map"` CanCreateOrgRepo *bool `json:"can_create_org_repo"` } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 3fa43ce0cb..01cbf26f61 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -80,45 +80,40 @@ type Repository struct { // swagger:strfmt date-time Created time.Time `json:"created_at"` // swagger:strfmt date-time - Updated time.Time `json:"updated_at"` - ArchivedAt time.Time `json:"archived_at"` - Permissions *Permission `json:"permissions,omitempty"` - HasIssues bool `json:"has_issues"` - InternalTracker *InternalTracker `json:"internal_tracker,omitempty"` - ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"` - // is the wiki enabled - HasWiki bool `json:"has_wiki"` - // have wiki pages ever been created - HasWikiContents bool `json:"has_wiki_contents"` - ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` - WikiBranch string `json:"wiki_branch,omitempty"` - WikiSSHURL string `json:"wiki_ssh_url"` - WikiCloneURL string `json:"wiki_clone_url"` - GloballyEditableWiki bool `json:"globally_editable_wiki"` - HasPullRequests bool `json:"has_pull_requests"` - HasProjects bool `json:"has_projects"` - HasReleases bool `json:"has_releases"` - HasPackages bool `json:"has_packages"` - HasActions bool `json:"has_actions"` - IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` - AllowMerge bool `json:"allow_merge_commits"` - AllowRebase bool `json:"allow_rebase"` - AllowRebaseMerge bool `json:"allow_rebase_explicit"` - AllowSquash bool `json:"allow_squash_merge"` - AllowFastForwardOnly bool `json:"allow_fast_forward_only_merge"` - AllowRebaseUpdate bool `json:"allow_rebase_update"` - DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"` - DefaultMergeStyle string `json:"default_merge_style"` - DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"` - DefaultUpdateStyle string `json:"default_update_style"` - AvatarURL string `json:"avatar_url"` - Internal bool `json:"internal"` - MirrorInterval string `json:"mirror_interval"` + Updated time.Time `json:"updated_at"` + ArchivedAt time.Time `json:"archived_at"` + Permissions *Permission `json:"permissions,omitempty"` + HasIssues bool `json:"has_issues"` + InternalTracker *InternalTracker `json:"internal_tracker,omitempty"` + ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"` + HasWiki bool `json:"has_wiki"` + ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` + WikiBranch string `json:"wiki_branch,omitempty"` + GloballyEditableWiki bool `json:"globally_editable_wiki"` + HasPullRequests bool `json:"has_pull_requests"` + HasProjects bool `json:"has_projects"` + HasReleases bool `json:"has_releases"` + HasPackages bool `json:"has_packages"` + HasActions bool `json:"has_actions"` + IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` + AllowMerge bool `json:"allow_merge_commits"` + AllowRebase bool `json:"allow_rebase"` + AllowRebaseMerge bool `json:"allow_rebase_explicit"` + AllowSquash bool `json:"allow_squash_merge"` + AllowFastForwardOnly bool `json:"allow_fast_forward_only_merge"` + AllowRebaseUpdate bool `json:"allow_rebase_update"` + DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"` + DefaultMergeStyle string `json:"default_merge_style"` + DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"` + DefaultUpdateStyle string `json:"default_update_style"` + AvatarURL string `json:"avatar_url"` + Internal bool `json:"internal"` + MirrorInterval string `json:"mirror_interval"` // ObjectFormatName of the underlying git repository // enum: ["sha1", "sha256"] ObjectFormatName string `json:"object_format_name"` // swagger:strfmt date-time - MirrorUpdated time.Time `json:"mirror_updated"` + MirrorUpdated time.Time `json:"mirror_updated,omitempty"` RepoTransfer *RepoTransfer `json:"repo_transfer"` Topics []string `json:"topics"` } @@ -441,12 +436,3 @@ type UpdateRepoAvatarOption struct { // image must be base64 encoded Image string `json:"image" binding:"Required"` } - -type RepoTargetOption struct { - // Name of user or organisation that owns the repository - // required: true - Owner string `json:"owner"` - // Name of repository - // required: true - Name string `json:"name"` -} diff --git a/modules/structs/repo_actions.go b/modules/structs/repo_actions.go index 324ea474fd..b13f344738 100644 --- a/modules/structs/repo_actions.go +++ b/modules/structs/repo_actions.go @@ -32,54 +32,3 @@ type ActionTaskResponse struct { Entries []*ActionTask `json:"workflow_runs"` TotalCount int64 `json:"total_count"` } - -type RunnerStatus int - -const ( - // RunnerStatusOffline signals that the runner is not connected to Forgejo. - RunnerStatusOffline RunnerStatus = iota - - // RunnerStatusIdle means that the runner is connected to Forgejo and waiting for jobs to run. - RunnerStatusIdle - - // RunnerStatusActive signifies that the runner is connected to Forgejo and running a job. - RunnerStatusActive -) - -var statusName = map[RunnerStatus]string{ - RunnerStatusOffline: "offline", - RunnerStatusIdle: "idle", - RunnerStatusActive: "active", -} - -func (status RunnerStatus) String() string { - return statusName[status] -} - -// ActionRunner represents a runner -// swagger:model -type ActionRunner struct { - // ID uniquely identifies this runner. - ID int64 `json:"id"` - // UUID uniquely identifies this runner. - UUID string `json:"uuid"` - // OwnerID is the identifier of the user or organization this runner belongs to. O if the runner is owned by a - // repository. - OwnerID int64 `json:"owner_id"` - // RepoID is the identifier of the repository this runner belongs to. 0 if the runner belongs to a user or - // organization. - RepoID int64 `json:"repo_id"` - // Name of the runner; not unique. - Name string `json:"name"` - // Status indicates whether this runner is offline, or active, for example. - // enum: ["offline", "idle", "active"] - Status string `json:"status"` - // Version is the self-reported version string of Forgejo Runner. - Version string `json:"version"` - // Labels is a list of labels attached to this runner. - Labels []string `json:"labels"` - // Description provides optional details about this runner. - Description string `json:"description"` - // Indicates if runner is ephemeral runner - Ephemeral bool `json:"ephemeral"` -} diff --git a/modules/structs/runner.go b/modules/structs/runner.go deleted file mode 100644 index 5ac88f0488..0000000000 --- a/modules/structs/runner.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later - -package structs - -// RegisterRunnerOptions declares the accepted options for registering runners. -// swagger:model -type RegisterRunnerOptions struct { - // Name of the runner to register. The name of the runner does not have to be unique. - // - // required: true - Name string `json:"name" binding:"Required"` - - // Description of the runner to register. - // - // required: false - Description string `json:"description"` - - // Register as ephemeral runner https://forgejo.org/docs/latest/admin/actions/security/#ephemeral-runner - // - // required: false - Ephemeral bool `json:"ephemeral"` -} - -// RegisterRunnerResponse contains the details of the just registered runner. -// swagger:model -type RegisterRunnerResponse struct { - ID int64 `json:"id" binding:"Required"` - UUID string `json:"uuid" binding:"Required"` - Token string `json:"token" binding:"Required"` -} diff --git a/modules/structs/secret.go b/modules/structs/secret.go index acd5c36f06..a0673ca08c 100644 --- a/modules/structs/secret.go +++ b/modules/structs/secret.go @@ -14,11 +14,10 @@ type Secret struct { Created time.Time `json:"created_at"` } -// CreateOrUpdateSecretOption defines the properties of the secret to create or update. +// CreateOrUpdateSecretOption options when creating or updating secret // swagger:model type CreateOrUpdateSecretOption struct { - // Data of the secret. Special characters will be retained. Line endings will be normalized to LF to match the - // behaviour of browsers. Encode the data with Base64 if line endings should be retained. + // Data of the secret to update // // required: true Data string `json:"data" binding:"Required"` diff --git a/modules/structs/user.go b/modules/structs/user.go index e0767071d0..49e4c495cf 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -34,9 +34,9 @@ type User struct { // Is the user an administrator IsAdmin bool `json:"is_admin"` // swagger:strfmt date-time - LastLogin time.Time `json:"last_login"` + LastLogin time.Time `json:"last_login,omitempty"` // swagger:strfmt date-time - Created time.Time `json:"created"` + Created time.Time `json:"created,omitempty"` // Is user restricted Restricted bool `json:"restricted"` // Is user active diff --git a/modules/structs/user_app.go b/modules/structs/user_app.go index 357f3aed0a..6592c1cd48 100644 --- a/modules/structs/user_app.go +++ b/modules/structs/user_app.go @@ -16,12 +16,10 @@ type AccessToken struct { Token string `json:"sha1"` TokenLastEight string `json:"token_last_eight"` Scopes []string `json:"scopes"` - // Indicates that an access token only has access to the specified repositories. Will be null if the access token - // is not limited to a set of specified repositories. - Repositories []*RepositoryMeta `json:"repositories"` } // AccessTokenList represents a list of API access token. +// swagger:response AccessTokenList type AccessTokenList []*AccessToken // CreateAccessTokenOption options when create access token @@ -31,8 +29,6 @@ type CreateAccessTokenOption struct { Name string `json:"name" binding:"Required"` // example: ["all", "read:activitypub","read:issue", "write:misc", "read:notification", "read:organization", "read:package", "read:repository", "read:user"] Scopes []string `json:"scopes"` - // If provided and not-empty, creates an access token with access only to specified repositories. - Repositories []*RepoTargetOption `json:"repositories"` } // CreateOAuth2ApplicationOptions holds options to create an oauth2 application diff --git a/modules/structs/user_gpgkey.go b/modules/structs/user_gpgkey.go index deae70de33..ff9b0aea1d 100644 --- a/modules/structs/user_gpgkey.go +++ b/modules/structs/user_gpgkey.go @@ -21,9 +21,9 @@ type GPGKey struct { CanCertify bool `json:"can_certify"` Verified bool `json:"verified"` // swagger:strfmt date-time - Created time.Time `json:"created_at"` + Created time.Time `json:"created_at,omitempty"` // swagger:strfmt date-time - Expires time.Time `json:"expires_at"` + Expires time.Time `json:"expires_at,omitempty"` } // GPGKeyEmail an email attached to a GPGKey diff --git a/modules/structs/user_key.go b/modules/structs/user_key.go index b92552b200..08eed59a89 100644 --- a/modules/structs/user_key.go +++ b/modules/structs/user_key.go @@ -15,7 +15,7 @@ type PublicKey struct { Title string `json:"title,omitempty"` Fingerprint string `json:"fingerprint,omitempty"` // swagger:strfmt date-time - Created time.Time `json:"created_at"` + Created time.Time `json:"created_at,omitempty"` Owner *User `json:"user,omitempty"` ReadOnly bool `json:"read_only,omitempty"` KeyType string `json:"key_type,omitempty"` diff --git a/modules/structs/variable.go b/modules/structs/variable.go index c88aad5138..cc846cf0ec 100644 --- a/modules/structs/variable.go +++ b/modules/structs/variable.go @@ -3,24 +3,21 @@ package structs -// CreateVariableOption defines the properties of the variable to create. +// CreateVariableOption the option when creating variable // swagger:model type CreateVariableOption struct { - // Value of the variable to create. Special characters will be retained. Line endings will be normalized to LF to - // match the behaviour of browsers. Encode the data with Base64 if line endings should be retained. + // Value of the variable to create // // required: true Value string `json:"value" binding:"Required"` } -// UpdateVariableOption defines the properties of the variable to update. +// UpdateVariableOption the option when updating variable // swagger:model type UpdateVariableOption struct { - // New name for the variable. If the field is empty, the variable name won't be updated. Forgejo will convert it to - // uppercase. + // New name for the variable. If the field is empty, the variable name won't be updated. Name string `json:"name"` - // Value of the variable to update. Special characters will be retained. Line endings will be normalized to LF to - // match the behaviour of browsers. Encode the data with Base64 if line endings should be retained. + // Value of the variable to update // // required: true Value string `json:"value" binding:"Required"` diff --git a/modules/templates/eval/eval_test.go b/modules/templates/eval/eval_test.go index 6b13d14007..3e68203638 100644 --- a/modules/templates/eval/eval_test.go +++ b/modules/templates/eval/eval_test.go @@ -13,7 +13,7 @@ import ( ) func tokens(s string) (a []any) { - for v := range strings.FieldsSeq(s) { + for _, v := range strings.Fields(s) { a = append(a, v) } return a diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 44f5da24cb..848d4b4ad4 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -52,17 +52,16 @@ func NewFuncMap() template.FuncMap { // ----------------------------------------------------------------- // html/template related functions - "dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names. - "Eval": Eval, - "TrustHTML": TrustHTML, - "HTMLFormat": HTMLFormat, - "HTMLEscape": HTMLEscape, - "QueryEscape": QueryEscape, - "JSEscape": JSEscapeSafe, - "SanitizeHTML": SanitizeHTML, - "SanitizeHTMLStrict": SanitizeHTMLStrict, - "URLJoin": util.URLJoin, - "DotEscape": DotEscape, + "dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names. + "Eval": Eval, + "SafeHTML": SafeHTML, + "HTMLFormat": HTMLFormat, + "HTMLEscape": HTMLEscape, + "QueryEscape": QueryEscape, + "JSEscape": JSEscapeSafe, + "SanitizeHTML": SanitizeHTML, + "URLJoin": util.URLJoin, + "DotEscape": DotEscape, "PathEscape": url.PathEscape, "PathEscapeSegments": util.PathEscapeSegments, @@ -230,7 +229,6 @@ func HTMLFormat(s string, rawArgs ...any) template.HTML { switch v := v.(type) { case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, template.HTML: // for most basic types (including template.HTML which is safe), just do nothing and use it - break case string: args[i] = template.HTMLEscapeString(v) case fmt.Stringer: @@ -242,8 +240,8 @@ func HTMLFormat(s string, rawArgs ...any) template.HTML { return template.HTML(fmt.Sprintf(s, args...)) } -// TrustHTML render raw as HTML -func TrustHTML(s any) template.HTML { +// SafeHTML render raw as HTML +func SafeHTML(s any) template.HTML { switch v := s.(type) { case string: return template.HTML(v) @@ -258,10 +256,6 @@ func SanitizeHTML(s string) template.HTML { return template.HTML(markup.Sanitize(s)) } -func SanitizeHTMLStrict(s string) template.HTML { - return template.HTML(markup.SanitizeDescription(s)) -} - func HTMLEscape(s any) template.HTML { switch v := s.(type) { case string: diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index 4290e1c29f..d60397df08 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -248,7 +248,7 @@ func extractErrorLine(code []byte, lineNum, posNum int, target string) string { b := bufio.NewReader(bytes.NewReader(code)) var line []byte var err error - for i := range lineNum { + for i := 0; i < lineNum; i++ { if line, err = b.ReadBytes('\n'); err != nil { if i == lineNum-1 && errors.Is(err, io.EOF) { err = nil diff --git a/modules/templates/scopedtmpl/scopedtmpl.go b/modules/templates/scopedtmpl/scopedtmpl.go index d9866b3513..41a8ca86e9 100644 --- a/modules/templates/scopedtmpl/scopedtmpl.go +++ b/modules/templates/scopedtmpl/scopedtmpl.go @@ -7,7 +7,6 @@ import ( "fmt" "html/template" "io" - "maps" "reflect" "sync" texttemplate "text/template" @@ -41,7 +40,9 @@ func (t *ScopedTemplate) Funcs(funcMap template.FuncMap) { panic("cannot add new functions to frozen template set") } t.all.Funcs(funcMap) - maps.Copy(t.parseFuncs, funcMap) + for k, v := range funcMap { + t.parseFuncs[k] = v + } } func (t *ScopedTemplate) New(name string) *template.Template { @@ -158,7 +159,9 @@ func newScopedTemplateSet(all *template.Template, name string) (*scopedTemplateS textTmplPtr.muFuncs.Lock() ts.execFuncs = map[string]reflect.Value{} - maps.Copy(ts.execFuncs, textTmplPtr.execFuncs) + for k, v := range textTmplPtr.execFuncs { + ts.execFuncs[k] = v + } textTmplPtr.muFuncs.Unlock() var collectTemplates func(nodes []parse.Node) @@ -217,7 +220,9 @@ func (ts *scopedTemplateSet) newExecutor(funcMap map[string]any) TemplateExecuto tmpl := texttemplate.New("") tmplPtr := ptr[textTemplate](tmpl) tmplPtr.execFuncs = map[string]reflect.Value{} - maps.Copy(tmplPtr.execFuncs, ts.execFuncs) + for k, v := range ts.execFuncs { + tmplPtr.execFuncs[k] = v + } if funcMap != nil { tmpl.Funcs(funcMap) } diff --git a/modules/templates/util_date.go b/modules/templates/util_date.go index 982a1b8050..839d2701ef 100644 --- a/modules/templates/util_date.go +++ b/modules/templates/util_date.go @@ -75,7 +75,6 @@ func anyToTime(any any) (t time.Time, isZero bool) { switch v := any.(type) { case nil: // it is zero - break case *time.Time: if v != nil { t = *v diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go index 8e1ef71f5d..60f918be47 100644 --- a/modules/templates/util_misc.go +++ b/modules/templates/util_misc.go @@ -17,11 +17,12 @@ import ( asymkey_model "forgejo.org/models/asymkey" repo_model "forgejo.org/models/repo" user_model "forgejo.org/models/user" + "forgejo.org/modules/git" + giturl "forgejo.org/modules/git/url" "forgejo.org/modules/json" "forgejo.org/modules/log" "forgejo.org/modules/repository" "forgejo.org/modules/svg" - mirror_service "forgejo.org/services/mirror" "github.com/editorconfig/editorconfig-core-go/v2" ) @@ -163,11 +164,17 @@ type remoteAddress struct { Password string } -func mirrorRemoteAddress(ctx context.Context, mirror *repo_model.Mirror) remoteAddress { +func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress { ret := remoteAddress{} - u, err := mirror_service.DecryptOrRecoverRemoteAddress(ctx, mirror) + remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName) if err != nil { - log.Error("DecryptOrRecoverRemoteAddress %v", err) + log.Error("GetRemoteURL %v", err) + return ret + } + + u, err := giturl.Parse(remoteURL) + if err != nil { + log.Error("giturl.Parse %v", err) return ret } diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index e1ad83b88d..bec8d5f5e3 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -162,8 +162,8 @@ func RenderLabel(ctx *Context, label *issues_model.Label) template.HTML { // Regular label labelColor := label.Color + hex.EncodeToString([]byte{GetLabelOpacityByte(label.IsArchived())}) - s := fmt.Sprintf("
%s
", - archivedCSSClass, textColor, labelColor, description, description, RenderEmoji(ctx, label.Name)) + s := fmt.Sprintf("
%s
", + archivedCSSClass, textColor, labelColor, description, RenderEmoji(ctx, label.Name)) return template.HTML(s) } @@ -200,11 +200,11 @@ func RenderLabel(ctx *Context, label *issues_model.Label) template.HTML { scopeColor := "#" + hex.EncodeToString(scopeBytes) itemColor := "#" + hex.EncodeToString(itemBytes) - s := fmt.Sprintf(""+ + s := fmt.Sprintf(""+ "
%s
"+ "
%s
"+ "
", - archivedCSSClass, description, description, + archivedCSSClass, description, textColor, scopeColor, scopeText, textColor, itemColor, itemText) return template.HTML(s) @@ -246,8 +246,7 @@ func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //n } func RenderLabels(ctx *Context, labels []*issues_model.Label, repoLink string, isPull bool) template.HTML { - var htmlCode strings.Builder - htmlCode.WriteString(``) + htmlCode := `` for _, label := range labels { // Protect against nil value in labels - shouldn't happen but would cause a panic if so if label == nil { @@ -258,11 +257,11 @@ func RenderLabels(ctx *Context, labels []*issues_model.Label, repoLink string, i if isPull { issuesOrPull = "pulls" } - fmt.Fprintf(&htmlCode, "%s ", + htmlCode += fmt.Sprintf("%s ", repoLink, issuesOrPull, label.ID, RenderLabel(ctx, label)) } - htmlCode.WriteString("") - return template.HTML(htmlCode.String()) + htmlCode += "" + return template.HTML(htmlCode) } func RenderUser(ctx context.Context, user user_model.User) template.HTML { diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 35412989e3..3cfd572491 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -54,7 +54,7 @@ func TestApostrophesInMentions(t *testing.T) { assert.Equal(t, template.HTML("

@mention-user's comment

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

@ThisUserDoesNotExist @mention-user

\n"), rendered) } @@ -229,8 +229,7 @@ func TestRenderLabels(t *testing.T) { 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, "data-tooltip-content='First label'") - assert.Contains(t, rendered, "aria-description='First label'") + 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<") @@ -242,11 +241,11 @@ func TestRenderLabels(t *testing.T) { assert.Contains(t, rendered, "user2/repo1/issues?labels=11") assert.Contains(t, rendered, "> <script>malicious</script> <") assert.Contains(t, rendered, ">'?&<") - assert.Contains(t, rendered, "data-tooltip-content='Malicious label ' <script>malicious</script>'") - assert.Contains(t, rendered, "aria-description='Malicious label ' <script>malicious</script>'") + 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) { diff --git a/modules/test/distant_federation_server_mock.go b/modules/test/distant_federation_server_mock.go index abbc82c196..ea8a69e9b4 100644 --- a/modules/test/distant_federation_server_mock.go +++ b/modules/test/distant_federation_server_mock.go @@ -4,38 +4,29 @@ package test import ( - "bytes" "fmt" "io" "net/http" "net/http/httptest" - "net/url" "strings" "testing" "forgejo.org/modules/util" - - ap "github.com/go-ap/activitypub" - "github.com/go-ap/jsonld" - "github.com/google/uuid" ) -type ApActorMock struct { - PrivKey string - PubKey string -} - type FederationServerMockPerson struct { ID int64 Name string PubKey string PrivKey string } - type FederationServerMockRepository struct { ID int64 } - +type ApActorMock struct { + PrivKey string + PubKey string +} type FederationServerMock struct { ApActor ApActorMock Persons []FederationServerMockPerson @@ -43,35 +34,6 @@ type FederationServerMock struct { LastPost string } -func NewApActorMock() ApActorMock { - priv, pub, _ := util.GenerateKeyPair(1024) - return ApActorMock{ - PrivKey: priv, - PubKey: pub, - } -} - -func (u *ApActorMock) KeyID(host string) string { - return fmt.Sprintf("%s/api/v1/activitypub/actor#main-key", host) -} - -func (u *ApActorMock) marshal(host string) string { - baseID := fmt.Sprintf("http://%s/api/v1/activitypub/actor", host) - - return fmt.Sprintf( - `{ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],`+ - `"id": "%[1]s",`+ - `"type": "Application",`+ - `"preferredUsername": "ghost",`+ - `"publicKey": {`+ - ` "id": "%[1]s#main-key",`+ - ` "owner": "%[1]s",`+ - ` "publicKeyPem": %[2]q }}`, - baseID, - u.PubKey, - ) -} - func NewFederationServerMockPerson(id int64, name string) FederationServerMockPerson { priv, pub, _ := util.GenerateKeyPair(3072) return FederationServerMockPerson{ @@ -86,6 +48,24 @@ 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",`+ @@ -100,12 +80,6 @@ func (p FederationServerMockPerson) marshal(host string) string { `"publicKeyPem":%[4]q}}`, host, p.ID, p.Name, p.PubKey) } -func NewFederationServerMockRepository(id int64) FederationServerMockRepository { - return FederationServerMockRepository{ - ID: id, - } -} - func NewFederationServerMock() *FederationServerMock { return &FederationServerMock{ ApActor: NewApActorMock(), @@ -129,26 +103,6 @@ func (mock *FederationServerMock) recordLastPost(t *testing.T, req *http.Request mock.LastPost = strings.ReplaceAll(buf.String(), req.Host, "DISTANT_FEDERATION_HOST") } -func (mock *FederationServerMock) FollowActorUnsigned(host string, localID int64, uri, inboxURL url.URL) error { - apID := fmt.Sprintf("%s/api/v1/activitypub/user-id/%d", host, localID) - - activity := ap.Follow{} - activity.Type = ap.FollowType - activity.ID = ap.IRI(apID + "/follows/" + uuid.New().String()) - activity.Actor = ap.IRI(apID) - activity.Object = ap.IRI(uri.String()) - - payload, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI)).Marshal(activity) - if err != nil { - return err - } - - reader := bytes.NewReader(payload) - _, err = http.Post(inboxURL.String(), "application/activity+json", reader) - - return err -} - func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server { federatedRoutes := http.NewServeMux() @@ -168,10 +122,6 @@ func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server { }) for _, person := range mock.Persons { - federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/user-id/alias%v", person.ID), - func(res http.ResponseWriter, req *http.Request) { - fmt.Fprint(res, person.marshal(req.Host)) - }) 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 @@ -189,18 +139,10 @@ func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server { mock.recordLastPost(t, req) }) } - - federatedRoutes.HandleFunc("GET /api/v1/activitypub/actor", - func(res http.ResponseWriter, req *http.Request) { - fmt.Fprint(res, mock.ApActor.marshal(req.Host)) - }) - federatedRoutes.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { t.Errorf("Unhandled %v request: %q", req.Method, req.URL.EscapedPath()) }) - federatedSrv := httptest.NewServer(federatedRoutes) - return federatedSrv } diff --git a/modules/test/logchecker.go b/modules/test/logchecker.go index af82ff0461..8e8fc32216 100644 --- a/modules/test/logchecker.go +++ b/modules/test/logchecker.go @@ -53,11 +53,11 @@ func (lc *LogChecker) checkLogEvent(event *log.EventFormatted) { } } -var checkerIndex atomic.Int64 +var checkerIndex int64 func NewLogChecker(namePrefix string, level log.Level) (logChecker *LogChecker, cancel func()) { logger := log.GetManager().GetLogger(namePrefix) - newCheckerIndex := checkerIndex.Add(1) + newCheckerIndex := atomic.AddInt64(&checkerIndex, 1) writerName := namePrefix + "-" + fmt.Sprint(newCheckerIndex) lc := &LogChecker{} diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index 54f0462703..772ae47e71 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -367,7 +367,7 @@ var ignoredErrorMessage = []string{ // Test_CmdForgejo_Actions `DB: No dedicated replica host defined; falling back to primary DSN for replica connections`, - // TestDemoErrorPages + // TestDevtestErrorpages `ErrorPage() [E] Example error: Example error`, } @@ -501,7 +501,7 @@ func PrintCurrentTest(t testing.TB, skip ...int) func() { // Printf takes a format and args and prints the string to os.Stdout func Printf(format string, args ...any) { if log.CanColorStdout { - for i := range args { + for i := 0; i < len(args); i++ { args[i] = log.NewColoredValue(args[i]) } } diff --git a/modules/translation/i18n/i18n_ini_test.go b/modules/translation/i18n/i18n_ini_test.go deleted file mode 100644 index 64c5d167c0..0000000000 --- a/modules/translation/i18n/i18n_ini_test.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package i18n - -import ( - "html/template" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestLocaleStoreINI(t *testing.T) { - testData1 := []byte(` -.dot.name = Dot Name -fmt = %[1]s %[2]s - -[section] -sub = Sub String -mixed = test value; %s -`) - - testData2 := []byte(` -fmt = %[2]s %[1]s - -[section] -sub = Changed Sub String -`) - - ls := NewLocaleStore() - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRuleEnglish, UsedPluralFormsEnglish, testData1, nil)) - require.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", MockPluralRule, UsedPluralFormsMock, testData2, nil)) - ls.SetDefaultLang("lang1") - - lang1, _ := ls.Locale("lang1") - lang2, _ := ls.Locale("lang2") - - result := lang1.TrString("fmt", "a", "b") - assert.Equal(t, "a b", result) - - result = lang2.TrString("fmt", "a", "b") - assert.Equal(t, "b a", result) - - result = lang1.TrString("section.sub") - assert.Equal(t, "Sub String", result) - - result = lang2.TrString("section.sub") - assert.Equal(t, "Changed Sub String", result) - - langNone, _ := ls.Locale("none") - result = langNone.TrString(".dot.name") - assert.Equal(t, "Dot Name", result) - - result2 := lang2.TrHTML("section.mixed", "a&b") - assert.EqualValues(t, `test value; a&b`, result2) - - langs, descs := ls.ListLangNameDesc() - assert.ElementsMatch(t, []string{"lang1", "lang2"}, langs) - assert.ElementsMatch(t, []string{"Lang1", "Lang2"}, descs) - - found := lang1.HasKey("no-such") - assert.False(t, found) - assert.Equal(t, "no-such", lang1.TrString("no-such")) - require.NoError(t, ls.Close()) -} - -func TestLocaleStoreMoreSource(t *testing.T) { - testData1 := []byte(` -a=11 -b=12 -`) - - testData2 := []byte(` -b=21 -c=22 -`) - - ls := NewLocaleStore() - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, UsedPluralFormsMock, testData1, testData2)) - lang1, _ := ls.Locale("lang1") - assert.Equal(t, "11", lang1.TrString("a")) - assert.Equal(t, "21", lang1.TrString("b")) - assert.Equal(t, "22", lang1.TrString("c")) -} - -type stringerPointerReceiver struct { - s string -} - -func (s *stringerPointerReceiver) String() string { - return s.s -} - -type stringerStructReceiver struct { - s string -} - -func (s stringerStructReceiver) String() string { - return s.s -} - -type errorStructReceiver struct { - s string -} - -func (e errorStructReceiver) Error() string { - return e.s -} - -type errorPointerReceiver struct { - s string -} - -func (e *errorPointerReceiver) Error() string { - return e.s -} - -func TestLocaleWithTemplate(t *testing.T) { - ls := NewLocaleStore() - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, UsedPluralFormsMock, []byte(`key=%s`), nil)) - lang1, _ := ls.Locale("lang1") - - tmpl := template.New("test").Funcs(template.FuncMap{"tr": lang1.TrHTML}) - tmpl = template.Must(tmpl.Parse(`{{tr "key" .var}}`)) - - cases := []struct { - in any - want string - }{ - {"", "<str>"}, - {[]byte(""), "[60 98 121 116 101 115 62]"}, - {template.HTML(""), ""}, - {stringerPointerReceiver{""}, "{<stringerPointerReceiver>}"}, - {&stringerPointerReceiver{""}, "<stringerPointerReceiver ptr>"}, - {stringerStructReceiver{""}, "<stringerStructReceiver>"}, - {&stringerStructReceiver{""}, "<stringerStructReceiver ptr>"}, - {errorStructReceiver{""}, "<errorStructReceiver>"}, - {&errorStructReceiver{""}, "<errorStructReceiver ptr>"}, - {errorPointerReceiver{""}, "{<errorPointerReceiver>}"}, - {&errorPointerReceiver{""}, "<errorPointerReceiver ptr>"}, - } - - buf := &strings.Builder{} - for _, c := range cases { - buf.Reset() - require.NoError(t, tmpl.Execute(buf, map[string]any{"var": c.in})) - assert.Equal(t, c.want, buf.String()) - } -} - -func TestLocaleStoreQuirks(t *testing.T) { - const nl = "\n" - q := func(q1, s string, q2 ...string) string { - return q1 + s + strings.Join(q2, "") - } - testDataList := []struct { - in string - out string - hint string - }{ - {` xx`, `xx`, "simple, no quote"}, - {`" xx"`, ` xx`, "simple, double-quote"}, - {`' xx'`, ` xx`, "simple, single-quote"}, - {"` xx`", ` xx`, "simple, back-quote"}, - - {`x\"y`, `x\"y`, "no unescape, simple"}, - {q(`"`, `x\"y`, `"`), `"x\"y"`, "unescape, double-quote"}, - {q(`'`, `x\"y`, `'`), `x\"y`, "no unescape, single-quote"}, - {q("`", `x\"y`, "`"), `x\"y`, "no unescape, back-quote"}, - - {q(`"`, `x\"y`) + nl + "b=", `"x\"y`, "half open, double-quote"}, - {q(`'`, `x\"y`) + nl + "b=", `'x\"y`, "half open, single-quote"}, - {q("`", `x\"y`) + nl + "b=`", `x\"y` + nl + "b=", "half open, back-quote, multi-line"}, - - {`x ; y`, `x ; y`, "inline comment (;)"}, - {`x # y`, `x # y`, "inline comment (#)"}, - {`x \; y`, `x ; y`, `inline comment (\;)`}, - {`x \# y`, `x # y`, `inline comment (\#)`}, - } - - for _, testData := range testDataList { - ls := NewLocaleStore() - err := ls.AddLocaleByIni("lang1", "Lang1", nil, nil, []byte("a="+testData.in), nil) - lang1, _ := ls.Locale("lang1") - require.NoError(t, err, testData.hint) - assert.Equal(t, testData.out, lang1.TrString("a"), testData.hint) - require.NoError(t, ls.Close()) - } - - // TODO: Crowdin needs the strings to be quoted correctly and doesn't like incomplete quotes - // and Crowdin always outputs quoted strings if there are quotes in the strings. - // So, Gitea's `key="quoted" unquoted` content shouldn't be used on Crowdin directly, - // it should be converted to `key="\"quoted\" unquoted"` first. - // TODO: We can not use UnescapeValueDoubleQuotes=true, because there are a lot of back-quotes in en-US.ini, - // then Crowdin will output: - // > key = "`x \" y`" - // Then Gitea will read a string with back-quotes, which is incorrect. - // TODO: Crowdin might generate multi-line strings, quoted by double-quote, it's not supported by LocaleStore - // LocaleStore uses back-quote for multi-line strings, it's not supported by Crowdin. - // TODO: Crowdin doesn't support back-quote as string quoter, it mainly uses double-quote - // so, the following line will be parsed as: value="`first", comment="second`" on Crowdin - // > a = `first; second` -} diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go index 8dfdb36127..ac086d75d9 100644 --- a/modules/translation/i18n/i18n_test.go +++ b/modules/translation/i18n/i18n_test.go @@ -1,9 +1,10 @@ -// Copyright 2024-2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT package i18n import ( + "html/template" "strings" "testing" @@ -36,7 +37,24 @@ var ( UsedPluralFormsMock = []PluralFormIndex{PluralFormZero, PluralFormOne, PluralFormFew, PluralFormOther} ) -func TestLocaleStoreJSON(t *testing.T) { +func TestLocaleStore(t *testing.T) { + testData1 := []byte(` +.dot.name = Dot Name +fmt = %[1]s %[2]s + +[section] +sub = Sub String +mixed = test value; %s +`) + + testData2 := []byte(` +fmt = %[2]s %[1]s + +[section] +sub = Changed Sub String +commits = fallback value for commits +`) + testDataJSON2 := []byte(` { "section.json": "the JSON is %s", @@ -72,18 +90,35 @@ func TestLocaleStoreJSON(t *testing.T) { `) ls := NewLocaleStore() - - // Currently LocaleStore has to be first populated with langcodes via AddLocaleByIni - require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRuleEnglish, UsedPluralFormsEnglish, []byte(""), nil)) - require.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", MockPluralRule, UsedPluralFormsMock, []byte(""), nil)) - + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRuleEnglish, UsedPluralFormsEnglish, testData1, nil)) + require.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", MockPluralRule, UsedPluralFormsMock, testData2, nil)) require.NoError(t, ls.AddToLocaleFromJSON("lang1", testDataJSON1)) require.NoError(t, ls.AddToLocaleFromJSON("lang2", testDataJSON2)) - ls.SetDefaultLang("lang1") + + lang1, _ := ls.Locale("lang1") lang2, _ := ls.Locale("lang2") - result := lang2.TrString("section.json", "valid") + result := lang1.TrString("fmt", "a", "b") + assert.Equal(t, "a b", result) + + result = lang2.TrString("fmt", "a", "b") + assert.Equal(t, "b a", result) + + result = lang1.TrString("section.sub") + assert.Equal(t, "Sub String", result) + + result = lang2.TrString("section.sub") + assert.Equal(t, "Changed Sub String", result) + + langNone, _ := ls.Locale("none") + result = langNone.TrString(".dot.name") + assert.Equal(t, "Dot Name", result) + + result2 := lang2.TrHTML("section.mixed", "a&b") + assert.EqualValues(t, `test value; a&b`, result2) + + result = lang2.TrString("section.json", "valid") assert.Equal(t, "the JSON is valid", result) result = lang2.TrString("nested.outer.inner.json") @@ -92,7 +127,7 @@ func TestLocaleStoreJSON(t *testing.T) { result = lang2.TrString("section.commits") assert.Equal(t, "lots of %d commits", result) - result2 := lang2.TrPluralString(1, "section.commits", 1) + result2 = lang2.TrPluralString(1, "section.commits", 1) assert.EqualValues(t, "one 1 commit", result2) result2 = lang2.TrPluralString(3, "section.commits", 3) @@ -121,34 +156,159 @@ func TestLocaleStoreJSON(t *testing.T) { result2 = lang2.TrPluralString(7, "section.incomplete", 7) assert.EqualValues(t, "[untranslated] some 7 objects", result2) -} -func TestMissingTranslationHandling(t *testing.T) { - ls := NewLocaleStore() + langs, descs := ls.ListLangNameDesc() + assert.ElementsMatch(t, []string{"lang1", "lang2"}, langs) + assert.ElementsMatch(t, []string{"Lang1", "Lang2"}, descs) - // Currently LocaleStore has to be first populated with langcodes via AddLocaleByIni - require.NoError(t, ls.AddLocaleByIni("en-US", "English", MockPluralRuleEnglish, UsedPluralFormsEnglish, []byte(""), nil)) - require.NoError(t, ls.AddLocaleByIni("fun", "Funlang", MockPluralRule, UsedPluralFormsMock, []byte(""), nil)) - - require.NoError(t, ls.AddToLocaleFromJSON("en-US", []byte(` -{ - "incorrect_root_url": "This Forgejo instance...", - "meta.last_line": "Hi there!" -}`))) - require.NoError(t, ls.AddToLocaleFromJSON("fun", []byte(` -{ - "meta.last_line": "This language only has one line that is never used by the UI. It will never have a translation for incorrect_root_url" -}`))) - - ls.SetDefaultLang("en-US") - - // Get "fun" locale, make sure it's available - funLocale, found := ls.Locale("fun") + // Test HasKey for JSON + found := lang2.HasKey("section.json") assert.True(t, found) - // Get translation for a string that this locale doesn't have - s := funLocale.TrString("incorrect_root_url") + // Test HasKey for INI + found = lang2.HasKey("section.sub") + assert.True(t, found) - // Verify fallback to English - assert.True(t, strings.HasPrefix(s, "This Forgejo instance...")) + found = lang1.HasKey("no-such") + assert.False(t, found) + assert.Equal(t, "no-such", lang1.TrString("no-such")) + require.NoError(t, ls.Close()) +} + +func TestLocaleStoreMoreSource(t *testing.T) { + testData1 := []byte(` +a=11 +b=12 +`) + + testData2 := []byte(` +b=21 +c=22 +`) + + ls := NewLocaleStore() + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, UsedPluralFormsMock, testData1, testData2)) + lang1, _ := ls.Locale("lang1") + assert.Equal(t, "11", lang1.TrString("a")) + assert.Equal(t, "21", lang1.TrString("b")) + assert.Equal(t, "22", lang1.TrString("c")) +} + +type stringerPointerReceiver struct { + s string +} + +func (s *stringerPointerReceiver) String() string { + return s.s +} + +type stringerStructReceiver struct { + s string +} + +func (s stringerStructReceiver) String() string { + return s.s +} + +type errorStructReceiver struct { + s string +} + +func (e errorStructReceiver) Error() string { + return e.s +} + +type errorPointerReceiver struct { + s string +} + +func (e *errorPointerReceiver) Error() string { + return e.s +} + +func TestLocaleWithTemplate(t *testing.T) { + ls := NewLocaleStore() + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", MockPluralRule, UsedPluralFormsMock, []byte(`key=%s`), nil)) + lang1, _ := ls.Locale("lang1") + + tmpl := template.New("test").Funcs(template.FuncMap{"tr": lang1.TrHTML}) + tmpl = template.Must(tmpl.Parse(`{{tr "key" .var}}`)) + + cases := []struct { + in any + want string + }{ + {"", "<str>"}, + {[]byte(""), "[60 98 121 116 101 115 62]"}, + {template.HTML(""), ""}, + {stringerPointerReceiver{""}, "{<stringerPointerReceiver>}"}, + {&stringerPointerReceiver{""}, "<stringerPointerReceiver ptr>"}, + {stringerStructReceiver{""}, "<stringerStructReceiver>"}, + {&stringerStructReceiver{""}, "<stringerStructReceiver ptr>"}, + {errorStructReceiver{""}, "<errorStructReceiver>"}, + {&errorStructReceiver{""}, "<errorStructReceiver ptr>"}, + {errorPointerReceiver{""}, "{<errorPointerReceiver>}"}, + {&errorPointerReceiver{""}, "<errorPointerReceiver ptr>"}, + } + + buf := &strings.Builder{} + for _, c := range cases { + buf.Reset() + require.NoError(t, tmpl.Execute(buf, map[string]any{"var": c.in})) + assert.Equal(t, c.want, buf.String()) + } +} + +func TestLocaleStoreQuirks(t *testing.T) { + const nl = "\n" + q := func(q1, s string, q2 ...string) string { + return q1 + s + strings.Join(q2, "") + } + testDataList := []struct { + in string + out string + hint string + }{ + {` xx`, `xx`, "simple, no quote"}, + {`" xx"`, ` xx`, "simple, double-quote"}, + {`' xx'`, ` xx`, "simple, single-quote"}, + {"` xx`", ` xx`, "simple, back-quote"}, + + {`x\"y`, `x\"y`, "no unescape, simple"}, + {q(`"`, `x\"y`, `"`), `"x\"y"`, "unescape, double-quote"}, + {q(`'`, `x\"y`, `'`), `x\"y`, "no unescape, single-quote"}, + {q("`", `x\"y`, "`"), `x\"y`, "no unescape, back-quote"}, + + {q(`"`, `x\"y`) + nl + "b=", `"x\"y`, "half open, double-quote"}, + {q(`'`, `x\"y`) + nl + "b=", `'x\"y`, "half open, single-quote"}, + {q("`", `x\"y`) + nl + "b=`", `x\"y` + nl + "b=", "half open, back-quote, multi-line"}, + + {`x ; y`, `x ; y`, "inline comment (;)"}, + {`x # y`, `x # y`, "inline comment (#)"}, + {`x \; y`, `x ; y`, `inline comment (\;)`}, + {`x \# y`, `x # y`, `inline comment (\#)`}, + } + + for _, testData := range testDataList { + ls := NewLocaleStore() + err := ls.AddLocaleByIni("lang1", "Lang1", nil, nil, []byte("a="+testData.in), nil) + lang1, _ := ls.Locale("lang1") + require.NoError(t, err, testData.hint) + assert.Equal(t, testData.out, lang1.TrString("a"), testData.hint) + require.NoError(t, ls.Close()) + } + + // TODO: Crowdin needs the strings to be quoted correctly and doesn't like incomplete quotes + // and Crowdin always outputs quoted strings if there are quotes in the strings. + // So, Gitea's `key="quoted" unquoted` content shouldn't be used on Crowdin directly, + // it should be converted to `key="\"quoted\" unquoted"` first. + // TODO: We can not use UnescapeValueDoubleQuotes=true, because there are a lot of back-quotes in en-US.ini, + // then Crowdin will output: + // > key = "`x \" y`" + // Then Gitea will read a string with back-quotes, which is incorrect. + // TODO: Crowdin might generate multi-line strings, quoted by double-quote, it's not supported by LocaleStore + // LocaleStore uses back-quote for multi-line strings, it's not supported by Crowdin. + // TODO: Crowdin doesn't support back-quote as string quoter, it mainly uses double-quote + // so, the following line will be parsed as: value="`first", comment="second`" on Crowdin + // > a = `first; second` } diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index d0f5841411..fc27c75d13 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -119,7 +119,7 @@ func (l *locale) LookupNewStyleMessage(trKey string) string { return "" } -func (l *locale) LookupPluralByCount(trKey string, count any, isDefaultLang bool) string { +func (l *locale) LookupPluralByCount(trKey string, count any) string { n, err := util.ToInt64(count) if err != nil { log.Error("Invalid plural count '%s'", count) @@ -127,10 +127,10 @@ func (l *locale) LookupPluralByCount(trKey string, count any, isDefaultLang bool } pluralForm := l.pluralRule(n) - return l.LookupPluralByForm(trKey, pluralForm, isDefaultLang) + return l.LookupPluralByForm(trKey, pluralForm) } -func (l *locale) LookupPluralByForm(trKey string, pluralForm PluralFormIndex, isDefaultLang bool) string { +func (l *locale) LookupPluralByForm(trKey string, pluralForm PluralFormIndex) string { suffix := "" switch pluralForm { case PluralFormZero: @@ -145,7 +145,6 @@ func (l *locale) LookupPluralByForm(trKey string, pluralForm PluralFormIndex, is suffix = PluralFormSeparator + "many" case PluralFormOther: // No suffix for the "other" string. - break default: log.Error("Invalid plural form index %d", pluralForm) return "" @@ -155,14 +154,7 @@ func (l *locale) LookupPluralByForm(trKey string, pluralForm PluralFormIndex, is return result } - // Severify depends on the lang. A missing string in default lang will affect - // all translations, while community translations may just be incomplete - logFunc := log.Debug - if isDefaultLang { - logFunc = log.Error - } - - logFunc("Missing translation for key `%[1]s`, plural form `%[2]s`", trKey, suffix) + log.Error("Missing translation for plural form %s", suffix) return "" } @@ -256,7 +248,6 @@ func PrepareArgsForHTML(trArgs ...any) []any { switch v := v.(type) { case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, template.HTML: // for most basic types (including template.HTML which is safe), just do nothing and use it - break case string: args[i] = template.HTMLEscapeString(v) case fmt.Stringer: @@ -273,11 +264,11 @@ func (l *locale) TrHTML(trKey string, trArgs ...any) template.HTML { } func (l *locale) TrPluralString(count any, trKey string, trArgs ...any) template.HTML { - message := l.LookupPluralByCount(trKey, count, false) + message := l.LookupPluralByCount(trKey, count) if message == "" { if defaultLang, ok := l.store.localeMap[l.store.defaultLang]; ok { - message = defaultLang.LookupPluralByCount(trKey, count, true) + message = defaultLang.LookupPluralByCount(trKey, count) } if message == "" { message = trKey @@ -301,7 +292,7 @@ func (l *locale) TrPluralStringAllForms(trKey string) ([]string, []string) { allPresent := true for i, form := range l.usedPluralForms { - result[i] = l.LookupPluralByForm(trKey, form, false) + result[i] = l.LookupPluralByForm(trKey, form) if result[i] == "" { allPresent = false } @@ -311,7 +302,7 @@ func (l *locale) TrPluralStringAllForms(trKey string) ([]string, []string) { if hasDefaultLang { fallback = make([]string, len(defaultLang.usedPluralForms)) for i, form := range defaultLang.usedPluralForms { - fallback[i] = defaultLang.LookupPluralByForm(trKey, form, true) + fallback[i] = defaultLang.LookupPluralByForm(trKey, form) } } else { log.Error("Plural set for '%s' is incomplete and no fallback language is set.", trKey) diff --git a/modules/translation/localeiter/utils.go b/modules/translation/localeiter/utils.go index cda20df1bb..de398258e2 100644 --- a/modules/translation/localeiter/utils.go +++ b/modules/translation/localeiter/utils.go @@ -56,7 +56,6 @@ func iterateMessagesNextInner(onMsgid func(string, string, string) error, data m pluralSuffix = key case "other": // do nothing - break default: realKey = fullKey } @@ -72,7 +71,6 @@ func iterateMessagesNextInner(onMsgid func(string, string, string) error, data m case nil: // do nothing - break default: return fmt.Errorf("Unexpected JSON type: %s - %T", fullKey, value) diff --git a/modules/translation/plural_rules_test.go b/modules/translation/plural_rules_test.go deleted file mode 100644 index d3a9a15905..0000000000 --- a/modules/translation/plural_rules_test.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package translation - -import ( - "testing" - - "forgejo.org/modules/translation/i18n" - - "github.com/stretchr/testify/assert" -) - -func TestGetPluralRule(t *testing.T) { - assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("en")) - assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("en-US")) - assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("en_UK")) - assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("nds")) - assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("de-DE")) - - assert.Equal(t, PluralRuleOneForm, GetPluralRuleImpl("zh")) - assert.Equal(t, PluralRuleOneForm, GetPluralRuleImpl("ja")) - - assert.Equal(t, PluralRuleBengali, GetPluralRuleImpl("bn")) - - assert.Equal(t, PluralRuleIcelandic, GetPluralRuleImpl("is")) - - assert.Equal(t, PluralRuleFilipino, GetPluralRuleImpl("fil")) - - assert.Equal(t, PluralRuleCzech, GetPluralRuleImpl("cs")) - - assert.Equal(t, PluralRuleRussian, GetPluralRuleImpl("ru")) - - assert.Equal(t, PluralRulePolish, GetPluralRuleImpl("pl")) - - assert.Equal(t, PluralRuleLatvian, GetPluralRuleImpl("lv")) - - assert.Equal(t, PluralRuleLithuanian, GetPluralRuleImpl("lt")) - - assert.Equal(t, PluralRuleFrench, GetPluralRuleImpl("fr")) - - assert.Equal(t, PluralRuleCatalan, GetPluralRuleImpl("ca")) - - assert.Equal(t, PluralRuleSlovenian, GetPluralRuleImpl("sl")) - - assert.Equal(t, PluralRuleArabic, GetPluralRuleImpl("ar")) - - assert.Equal(t, PluralRuleCatalan, GetPluralRuleImpl("pt-PT")) - assert.Equal(t, PluralRuleFrench, GetPluralRuleImpl("pt-BR")) - - assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("invalid")) -} - -func TestApplyPluralRule(t *testing.T) { - testCases := []struct { - expect i18n.PluralFormIndex - pluralRule int - values []int64 - }{ - {i18n.PluralFormOne, PluralRuleDefault, []int64{1}}, - {i18n.PluralFormOther, PluralRuleDefault, []int64{0, 2, 10, 256}}, - - {i18n.PluralFormOther, PluralRuleOneForm, []int64{0, 1, 2}}, - - {i18n.PluralFormOne, PluralRuleBengali, []int64{0, 1}}, - {i18n.PluralFormOther, PluralRuleBengali, []int64{2, 10, 256}}, - - {i18n.PluralFormOne, PluralRuleIcelandic, []int64{1, 21, 31}}, - {i18n.PluralFormOther, PluralRuleIcelandic, []int64{0, 2, 11, 15, 256}}, - - {i18n.PluralFormOne, PluralRuleFilipino, []int64{0, 1, 2, 3, 5, 7, 8, 10, 11, 12, 257}}, - {i18n.PluralFormOther, PluralRuleFilipino, []int64{4, 6, 9, 14, 16, 19, 256}}, - - {i18n.PluralFormOne, PluralRuleCzech, []int64{1}}, - {i18n.PluralFormFew, PluralRuleCzech, []int64{2, 3, 4}}, - {i18n.PluralFormOther, PluralRuleCzech, []int64{5, 0, 12, 78, 254}}, - - {i18n.PluralFormOne, PluralRuleRussian, []int64{1, 21, 31}}, - {i18n.PluralFormFew, PluralRuleRussian, []int64{2, 23, 34}}, - {i18n.PluralFormMany, PluralRuleRussian, []int64{0, 5, 11, 37, 111, 256}}, - - {i18n.PluralFormOne, PluralRulePolish, []int64{1}}, - {i18n.PluralFormFew, PluralRulePolish, []int64{2, 23, 34}}, - {i18n.PluralFormMany, PluralRulePolish, []int64{0, 5, 11, 21, 37, 256}}, - - {i18n.PluralFormZero, PluralRuleLatvian, []int64{0, 10, 11, 17}}, - {i18n.PluralFormOne, PluralRuleLatvian, []int64{1, 21, 71}}, - {i18n.PluralFormOther, PluralRuleLatvian, []int64{2, 7, 22, 23, 256}}, - - {i18n.PluralFormOne, PluralRuleLithuanian, []int64{1, 21, 31}}, - {i18n.PluralFormFew, PluralRuleLithuanian, []int64{2, 5, 9, 23, 34, 256}}, - {i18n.PluralFormMany, PluralRuleLithuanian, []int64{0, 10, 11, 18}}, - - {i18n.PluralFormOne, PluralRuleFrench, []int64{0, 1}}, - {i18n.PluralFormMany, PluralRuleFrench, []int64{1000000, 2000000}}, - {i18n.PluralFormOther, PluralRuleFrench, []int64{2, 4, 10, 256}}, - - {i18n.PluralFormOne, PluralRuleCatalan, []int64{1}}, - {i18n.PluralFormMany, PluralRuleCatalan, []int64{1000000, 2000000}}, - {i18n.PluralFormOther, PluralRuleCatalan, []int64{0, 2, 4, 10, 256}}, - - {i18n.PluralFormOne, PluralRuleSlovenian, []int64{1, 101, 201, 501}}, - {i18n.PluralFormTwo, PluralRuleSlovenian, []int64{2, 102, 202, 502}}, - {i18n.PluralFormFew, PluralRuleSlovenian, []int64{3, 103, 203, 503, 4, 104, 204, 504}}, - {i18n.PluralFormOther, PluralRuleSlovenian, []int64{0, 5, 11, 12, 20, 256}}, - - {i18n.PluralFormZero, PluralRuleArabic, []int64{0}}, - {i18n.PluralFormOne, PluralRuleArabic, []int64{1}}, - {i18n.PluralFormTwo, PluralRuleArabic, []int64{2}}, - {i18n.PluralFormFew, PluralRuleArabic, []int64{3, 4, 9, 10, 103, 104}}, - {i18n.PluralFormMany, PluralRuleArabic, []int64{11, 12, 13, 14, 17, 111, 256}}, - {i18n.PluralFormOther, PluralRuleArabic, []int64{100, 101, 102}}, - } - - for _, tc := range testCases { - for _, n := range tc.values { - assert.Equal(t, tc.expect, PluralRules[tc.pluralRule](n), "Testcase for plural rule %d, value %d", tc.pluralRule, n) - } - } -} diff --git a/modules/translation/translation.go b/modules/translation/translation.go index f2d77ac340..17c7cc068b 100644 --- a/modules/translation/translation.go +++ b/modules/translation/translation.go @@ -1,5 +1,4 @@ // Copyright 2020 The Gitea Authors. All rights reserved. -// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package translation diff --git a/modules/translation/translation_test.go b/modules/translation/translation_test.go index 34b1ebf91f..7584490941 100644 --- a/modules/translation/translation_test.go +++ b/modules/translation/translation_test.go @@ -1,9 +1,10 @@ // Copyright 2023 The Gitea Authors. All rights reserved. -// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package translation +// TODO: make this package friendly to testing + import ( "testing" @@ -47,3 +48,111 @@ func TestPrettyNumber(t *testing.T) { assert.Equal(t, "1,000,000", l.PrettyNumber(1000000)) assert.Equal(t, "1,000,000.1", l.PrettyNumber(1000000.1)) } + +func TestGetPluralRule(t *testing.T) { + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("en")) + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("en-US")) + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("en_UK")) + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("nds")) + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("de-DE")) + + assert.Equal(t, PluralRuleOneForm, GetPluralRuleImpl("zh")) + assert.Equal(t, PluralRuleOneForm, GetPluralRuleImpl("ja")) + + assert.Equal(t, PluralRuleBengali, GetPluralRuleImpl("bn")) + + assert.Equal(t, PluralRuleIcelandic, GetPluralRuleImpl("is")) + + assert.Equal(t, PluralRuleFilipino, GetPluralRuleImpl("fil")) + + assert.Equal(t, PluralRuleCzech, GetPluralRuleImpl("cs")) + + assert.Equal(t, PluralRuleRussian, GetPluralRuleImpl("ru")) + + assert.Equal(t, PluralRulePolish, GetPluralRuleImpl("pl")) + + assert.Equal(t, PluralRuleLatvian, GetPluralRuleImpl("lv")) + + assert.Equal(t, PluralRuleLithuanian, GetPluralRuleImpl("lt")) + + assert.Equal(t, PluralRuleFrench, GetPluralRuleImpl("fr")) + + assert.Equal(t, PluralRuleCatalan, GetPluralRuleImpl("ca")) + + assert.Equal(t, PluralRuleSlovenian, GetPluralRuleImpl("sl")) + + assert.Equal(t, PluralRuleArabic, GetPluralRuleImpl("ar")) + + assert.Equal(t, PluralRuleCatalan, GetPluralRuleImpl("pt-PT")) + assert.Equal(t, PluralRuleFrench, GetPluralRuleImpl("pt-BR")) + + assert.Equal(t, PluralRuleDefault, GetPluralRuleImpl("invalid")) +} + +func TestApplyPluralRule(t *testing.T) { + testCases := []struct { + expect i18n.PluralFormIndex + pluralRule int + values []int64 + }{ + {i18n.PluralFormOne, PluralRuleDefault, []int64{1}}, + {i18n.PluralFormOther, PluralRuleDefault, []int64{0, 2, 10, 256}}, + + {i18n.PluralFormOther, PluralRuleOneForm, []int64{0, 1, 2}}, + + {i18n.PluralFormOne, PluralRuleBengali, []int64{0, 1}}, + {i18n.PluralFormOther, PluralRuleBengali, []int64{2, 10, 256}}, + + {i18n.PluralFormOne, PluralRuleIcelandic, []int64{1, 21, 31}}, + {i18n.PluralFormOther, PluralRuleIcelandic, []int64{0, 2, 11, 15, 256}}, + + {i18n.PluralFormOne, PluralRuleFilipino, []int64{0, 1, 2, 3, 5, 7, 8, 10, 11, 12, 257}}, + {i18n.PluralFormOther, PluralRuleFilipino, []int64{4, 6, 9, 14, 16, 19, 256}}, + + {i18n.PluralFormOne, PluralRuleCzech, []int64{1}}, + {i18n.PluralFormFew, PluralRuleCzech, []int64{2, 3, 4}}, + {i18n.PluralFormOther, PluralRuleCzech, []int64{5, 0, 12, 78, 254}}, + + {i18n.PluralFormOne, PluralRuleRussian, []int64{1, 21, 31}}, + {i18n.PluralFormFew, PluralRuleRussian, []int64{2, 23, 34}}, + {i18n.PluralFormMany, PluralRuleRussian, []int64{0, 5, 11, 37, 111, 256}}, + + {i18n.PluralFormOne, PluralRulePolish, []int64{1}}, + {i18n.PluralFormFew, PluralRulePolish, []int64{2, 23, 34}}, + {i18n.PluralFormMany, PluralRulePolish, []int64{0, 5, 11, 21, 37, 256}}, + + {i18n.PluralFormZero, PluralRuleLatvian, []int64{0, 10, 11, 17}}, + {i18n.PluralFormOne, PluralRuleLatvian, []int64{1, 21, 71}}, + {i18n.PluralFormOther, PluralRuleLatvian, []int64{2, 7, 22, 23, 256}}, + + {i18n.PluralFormOne, PluralRuleLithuanian, []int64{1, 21, 31}}, + {i18n.PluralFormFew, PluralRuleLithuanian, []int64{2, 5, 9, 23, 34, 256}}, + {i18n.PluralFormMany, PluralRuleLithuanian, []int64{0, 10, 11, 18}}, + + {i18n.PluralFormOne, PluralRuleFrench, []int64{0, 1}}, + {i18n.PluralFormMany, PluralRuleFrench, []int64{1000000, 2000000}}, + {i18n.PluralFormOther, PluralRuleFrench, []int64{2, 4, 10, 256}}, + + {i18n.PluralFormOne, PluralRuleCatalan, []int64{1}}, + {i18n.PluralFormMany, PluralRuleCatalan, []int64{1000000, 2000000}}, + {i18n.PluralFormOther, PluralRuleCatalan, []int64{0, 2, 4, 10, 256}}, + + {i18n.PluralFormOne, PluralRuleSlovenian, []int64{1, 101, 201, 501}}, + {i18n.PluralFormTwo, PluralRuleSlovenian, []int64{2, 102, 202, 502}}, + {i18n.PluralFormFew, PluralRuleSlovenian, []int64{3, 103, 203, 503, 4, 104, 204, 504}}, + {i18n.PluralFormOther, PluralRuleSlovenian, []int64{0, 5, 11, 12, 20, 256}}, + + {i18n.PluralFormZero, PluralRuleArabic, []int64{0}}, + {i18n.PluralFormOne, PluralRuleArabic, []int64{1}}, + {i18n.PluralFormTwo, PluralRuleArabic, []int64{2}}, + {i18n.PluralFormFew, PluralRuleArabic, []int64{3, 4, 9, 10, 103, 104}}, + {i18n.PluralFormMany, PluralRuleArabic, []int64{11, 12, 13, 14, 17, 111, 256}}, + {i18n.PluralFormOther, PluralRuleArabic, []int64{100, 101, 102}}, + } + + for _, tc := range testCases { + for _, n := range tc.values { + assert.Equal(t, tc.expect, PluralRules[tc.pluralRule](n), "Testcase for plural rule %d, value %d", tc.pluralRule, n) + } + } +} diff --git a/modules/updatechecker/update_checker.go b/modules/updatechecker/update_checker.go index 8b524b6519..b0932ba663 100644 --- a/modules/updatechecker/update_checker.go +++ b/modules/updatechecker/update_checker.go @@ -60,9 +60,9 @@ func getVersionDNS(domainEndpoint string) (version string, err error) { } for _, record := range records { - if after, ok := strings.CutPrefix(record, "forgejo_versions="); ok { + if strings.HasPrefix(record, "forgejo_versions=") { // Get all supported versions, separated by a comma. - supportedVersions := strings.Split(after, ",") + supportedVersions := strings.Split(strings.TrimPrefix(record, "forgejo_versions="), ",") // For now always return the latest supported version. return supportedVersions[len(supportedVersions)-1], nil } diff --git a/modules/util/remove.go b/modules/util/remove.go index e2cffc92c9..2a65a6b0aa 100644 --- a/modules/util/remove.go +++ b/modules/util/remove.go @@ -12,7 +12,7 @@ import ( // Remove removes the named file or (empty) directory with at most 5 attempts. func Remove(name string) error { var err error - for range 5 { + for i := 0; i < 5; i++ { err = os.Remove(name) if err == nil { break @@ -35,7 +35,7 @@ func Remove(name string) error { // RemoveAll removes the named file or (empty) directory with at most 5 attempts. func RemoveAll(name string) error { var err error - for range 5 { + for i := 0; i < 5; i++ { err = os.RemoveAll(name) if err == nil { break @@ -58,7 +58,7 @@ func RemoveAll(name string) error { // Rename renames (moves) oldpath to newpath with at most 5 attempts. func Rename(oldpath, newpath string) error { var err error - for i := range 5 { + for i := 0; i < 5; i++ { err = os.Rename(oldpath, newpath) if err == nil { break diff --git a/modules/util/rotatingfilewriter/writer_test.go b/modules/util/rotatingfilewriter/writer_test.go index c3664d8c4f..5b3b351667 100644 --- a/modules/util/rotatingfilewriter/writer_test.go +++ b/modules/util/rotatingfilewriter/writer_test.go @@ -24,7 +24,7 @@ func TestCompressOldFile(t *testing.T) { ng, err := os.OpenFile(nonGzip, os.O_CREATE|os.O_WRONLY, 0o660) require.NoError(t, err) - for range 999 { + for i := 0; i < 999; i++ { f.WriteString("This is a test file\n") ng.WriteString("This is a test file\n") } diff --git a/modules/util/timer_test.go b/modules/util/timer_test.go index 1f9a4ac586..602800c248 100644 --- a/modules/util/timer_test.go +++ b/modules/util/timer_test.go @@ -12,19 +12,19 @@ import ( ) func TestDebounce(t *testing.T) { - var c atomic.Int64 + var c int64 d := Debounce(50 * time.Millisecond) - d(func() { c.Add(1) }) - assert.EqualValues(t, 0, c.Load()) - d(func() { c.Add(1) }) - d(func() { c.Add(1) }) + d(func() { atomic.AddInt64(&c, 1) }) + assert.EqualValues(t, 0, atomic.LoadInt64(&c)) + d(func() { atomic.AddInt64(&c, 1) }) + d(func() { atomic.AddInt64(&c, 1) }) time.Sleep(100 * time.Millisecond) - assert.EqualValues(t, 1, c.Load()) - d(func() { c.Add(1) }) - assert.EqualValues(t, 1, c.Load()) - d(func() { c.Add(1) }) - d(func() { c.Add(1) }) - d(func() { c.Add(1) }) + assert.EqualValues(t, 1, atomic.LoadInt64(&c)) + d(func() { atomic.AddInt64(&c, 1) }) + assert.EqualValues(t, 1, atomic.LoadInt64(&c)) + d(func() { atomic.AddInt64(&c, 1) }) + d(func() { atomic.AddInt64(&c, 1) }) + d(func() { atomic.AddInt64(&c, 1) }) time.Sleep(100 * time.Millisecond) - assert.EqualValues(t, 2, c.Load()) + assert.EqualValues(t, 2, atomic.LoadInt64(&c)) } diff --git a/modules/util/truncate.go b/modules/util/truncate.go index 35836f745c..7207a89177 100644 --- a/modules/util/truncate.go +++ b/modules/util/truncate.go @@ -47,7 +47,7 @@ func SplitTrimSpace(input, sep string) []string { input = strings.ReplaceAll(input, "\r\n", "\n") var stringList []string - for s := range strings.SplitSeq(input, sep) { + for _, s := range strings.Split(input, sep) { // trim leading and trailing space stringList = append(stringList, strings.TrimSpace(s)) } diff --git a/modules/util/util.go b/modules/util/util.go index 996a566830..f21936fb5f 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -215,6 +215,11 @@ func ToFloat64(number any) (float64, error) { return value, nil } +// ToPointer returns the pointer of a copy of any given value +func ToPointer[T any](val T) *T { + return &val +} + // Iif is an "inline-if", it returns "trueVal" if "condition" is true, otherwise "falseVal" func Iif[T any](condition bool, trueVal, falseVal T) T { if condition { diff --git a/modules/util/util_test.go b/modules/util/util_test.go index fae4f6673d..a85113b2f4 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -5,10 +5,10 @@ package util_test import ( + "bytes" "crypto/rand" "strings" "testing" - "testing/cryptotest" "forgejo.org/modules/test" "forgejo.org/modules/util" @@ -211,25 +211,42 @@ func TestToTitleCase(t *testing.T) { assert.Equal(t, `Foo Bar Baz`, util.ToTitleCase(`FOO BAR BAZ`)) } +func TestToPointer(t *testing.T) { + assert.Equal(t, "abc", *util.ToPointer("abc")) + assert.Equal(t, 123, *util.ToPointer(123)) + abc := "abc" + assert.NotSame(t, &abc, util.ToPointer(abc)) + val123 := 123 + assert.NotSame(t, &val123, util.ToPointer(val123)) +} + func TestReserveLineBreakForTextarea(t *testing.T) { assert.Equal(t, "test\ndata", util.ReserveLineBreakForTextarea("test\r\ndata")) assert.Equal(t, "test\ndata\n", util.ReserveLineBreakForTextarea("test\r\ndata\r\n")) } const ( - testPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHX8yEKexoMqBPPwG4pGAhhjo5CyiHLiJZ7p3jg0aJZM\n" + testPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAOhB7/zzhC+HXDdGOdLwJln5NYwm6UNXx3chmQSVTG4\n" testPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz -c2gtZWQyNTUxOQAAACB1/MhCnsaDKgTz8BuKRgIYY6OQsohy4iWe6d44NGiWTAAA -AIhNLlZvTS5WbwAAAAtzc2gtZWQyNTUxOQAAACB1/MhCnsaDKgTz8BuKRgIYY6OQ -sohy4iWe6d44NGiWTAAAAEDZh37ObTaKrBpvQZ7GJ8drG/sfo3xBoR6kat1qSNiU -dHX8yEKexoMqBPPwG4pGAhhjo5CyiHLiJZ7p3jg0aJZMAAAAAAECAwQF +c2gtZWQyNTUxOQAAACADoQe/884Qvh1w3RjnS8CZZ+TWMJulDV8d3IZkElUxuAAA +AIggISIjICEiIwAAAAtzc2gtZWQyNTUxOQAAACADoQe/884Qvh1w3RjnS8CZZ+TW +MJulDV8d3IZkElUxuAAAAEAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0e +HwOhB7/zzhC+HXDdGOdLwJln5NYwm6UNXx3chmQSVTG4AAAAAAECAwQF -----END OPENSSH PRIVATE KEY-----` + "\n" ) func TestGeneratingEd25519Keypair(t *testing.T) { defer test.MockProtect(&rand.Reader)() - cryptotest.SetGlobalRandom(t, 0) + + // Only 32 bytes needs to be provided to generate a ed25519 keypair. + // And another 32 bytes are required, which is included as random value + // in the OpenSSH format. + b := make([]byte, 64) + for i := 0; i < 64; i++ { + b[i] = byte(i) + } + rand.Reader = bytes.NewReader(b) publicKey, privateKey, err := util.GenerateSSHKeypair() require.NoError(t, err) diff --git a/modules/validation/binding.go b/modules/validation/binding.go index 23d0622de4..463e7e8f7a 100644 --- a/modules/validation/binding.go +++ b/modules/validation/binding.go @@ -266,17 +266,17 @@ func addEmailBindingRules() { } func portOnly(hostport string) string { - _, after, ok := strings.Cut(hostport, ":") - if !ok { + colon := strings.IndexByte(hostport, ':') + if colon == -1 { return "" } - if _, after, ok := strings.Cut(hostport, "]:"); ok { - return after + if i := strings.Index(hostport, "]:"); i != -1 { + return hostport[i+len("]:"):] } if strings.Contains(hostport, "]") { return "" } - return after + return hostport[colon+len(":"):] } func validPort(p string) bool { diff --git a/modules/validation/helpers.go b/modules/validation/helpers.go index ce451b8ff4..4b28dead03 100644 --- a/modules/validation/helpers.go +++ b/modules/validation/helpers.go @@ -7,7 +7,6 @@ import ( "net" "net/url" "regexp" - "slices" "strings" "forgejo.org/modules/setting" @@ -41,7 +40,12 @@ func IsValidSiteURL(uri string) bool { return false } - return slices.Contains(setting.Service.ValidSiteURLSchemes, u.Scheme) + for _, scheme := range setting.Service.ValidSiteURLSchemes { + if scheme == u.Scheme { + return true + } + } + return false } // IsAPIURL checks if URL is current Gitea instance API URL @@ -98,21 +102,6 @@ var ( // No consecutive or trailing non-alphanumeric chars, catches both cases invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) - - // This is intended to accept any character, in any language, with accent symbols, - // as well as an arbitrary amount of subdomains and an optional port number defined - // through `:12345`. - // - // This is intended to cover username cases from distant servers in the fediverse, which - // can have much laxer requirements than those of Forgejo. It is not intended to check for - // invalid, non-standard compliant domains. - // - // For instance, the following should work: - // @user.όνομαß_21__@subdomain1.subdomain2.example.tld:65536 - // @42@42.example.tld - // @user@example.tld:99999 (presumed to be an impossible case) - // @-@-.tld (also impossible) - validFediverseUsernamePattern = regexp.MustCompile(`^(@[\p{L}\p{M}0-9_\.\-]{1,})(@[\p{L}\p{M}0-9_\.\-]{1,})(:[1-9][0-9]{0,4})?$`) ) // IsValidUsername checks if username is valid @@ -125,11 +114,3 @@ func IsValidUsername(name string) bool { return validUsernamePatternWithoutDots.MatchString(name) && !invalidUsernamePattern.MatchString(name) } - -// IsValidActivityPubUsername checks whether the username can be a valid ActivityPub handle. -// -// Username refers to the Forgejo user account's username for consistency, and not -// e.g. "username" in @username@example.tld. -func IsValidActivityPubUsername(name string) bool { - return validFediverseUsernamePattern.MatchString(name) -} diff --git a/modules/validation/helpers_test.go b/modules/validation/helpers_test.go index 6bca3664e1..7e32184691 100644 --- a/modules/validation/helpers_test.go +++ b/modules/validation/helpers_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 validation @@ -214,109 +213,3 @@ func TestIsValidUsernameBanDots(t *testing.T) { }) } } - -func TestIsValidActivityPubUsername(t *testing.T) { - cases := []struct { - description string - username string - valid bool - }{ - { - description: "Username without domain", - username: "@user", - valid: false, - }, - { - description: "Username with domain", - username: "@user@example.tld", - valid: true, - }, - { - description: "Numeric username with subdomain", - username: "@42@42.example.tld", - valid: true, - }, - { - description: "Username with two subdomains", - username: "@user@forgejo.activitypub.example.tld", - valid: true, - }, - { - description: "Username with domain and without port", - username: "@user@social.example.tld:", - valid: false, - }, - { - description: "Username with domain and invalid port 0", - username: "@user@social.example.tld:0", - valid: false, - }, - { - // We do not validate the port and assume that federationHost.HostPort - // cannot present such invalid ports. That also makes the previous case - // (port: 0) redundant, but it doesn't hurt. - description: "Username with domain and valid port", - username: "@user@social.example.tld:65536", - valid: true, - }, - { - description: "Username with Latin letters and special symbols", - username: "@$username$@example.tld", - valid: false, - }, - { - description: "Strictly numeric handle, domain, TLD", - username: "@0123456789@0123456789.0123456789.123", - valid: true, - }, - { - description: "Handle with Latin characters and dashes", - username: "@0-O@O-O.tld", - valid: true, - }, - // This is an impossible case, but we assume that this will never happen - // to begin with. - { - description: "Handle that only has dashes", - username: "@-@-.-", - valid: true, - }, - { - description: "Username with a mix of Latin and non-Latin letters containing accents", - username: "@usernäme.όνομαß_21__@example.tld", - valid: true, - }, - // Note: Our regex should accept any character, in any language and with accent symbols. - // The list is neither exhaustive, nor does it represent all possible cases. - // I chose some TLDs from https://en.wikipedia.org/wiki/Country_code_top-level_domain, - // although only one test case should suffice in theory. Nevertheless, to play it safe, - // I included four from different geographic regions whose scripts were legible using my - // IDE's default font to play it safe. - { - description: "Username, domain and ccTLD in Greek", - username: "@ευ@ευ.ευ", - valid: true, - }, - { - description: "Username, domain and ccTLD in Georgian (Mkhedruli)", - username: "@გე@გე.გე", - valid: true, - }, - { - description: "Username, domain and ccTLD of Malaysia (Arabic Jawi)", - username: "@مليسيا@ລمليسيا.مليسيا", - valid: true, - }, - { - description: "Username, domain and ccTLD of China (Simplified)", - username: "@中国@中国.中国", - valid: true, - }, - } - - for _, testCase := range cases { - t.Run(testCase.description, func(t *testing.T) { - assert.Equal(t, testCase.valid, IsValidActivityPubUsername(testCase.username)) - }) - } -} diff --git a/modules/validation/validatable.go b/modules/validation/validatable.go index 6c58a9148e..7bcca03bf8 100644 --- a/modules/validation/validatable.go +++ b/modules/validation/validatable.go @@ -6,7 +6,6 @@ package validation import ( "fmt" "reflect" - "slices" "strings" "unicode/utf8" @@ -35,9 +34,9 @@ type Validateable interface { } func IsValid(v Validateable) (bool, error) { - if validationErrors := v.Validate(); len(validationErrors) > 0 { + if valdationErrors := v.Validate(); len(valdationErrors) > 0 { typeof := reflect.TypeOf(v) - errString := strings.Join(validationErrors, "\n") + errString := strings.Join(valdationErrors, "\n") return false, ErrNotValid{fmt.Sprint(typeof, ": ", errString)} } @@ -70,8 +69,6 @@ func ValidateNotEmpty(value any, name string) []string { if v == 0 { isValid = false } - case ap.Typer: - isValid = len(value.(ap.Typer).AsTypes()) > 0 default: isValid = false } @@ -90,8 +87,10 @@ func ValidateMaxLen(value string, maxLen int, name string) []string { } func ValidateOneOf(value any, allowed []any, name string) []string { - if slices.Contains(allowed, value) { - return []string{} + for _, allowedElem := range allowed { + if value == allowedElem { + return []string{} + } } return []string{fmt.Sprintf("Field %s contains the value %v, which is not in allowed subset %v", name, value, allowed)} } diff --git a/modules/web/handler.go b/modules/web/handler.go index e3f0b029fd..4a7f28b1fa 100644 --- a/modules/web/handler.go +++ b/modules/web/handler.go @@ -17,7 +17,7 @@ import ( var responseStatusProviders = map[reflect.Type]func(req *http.Request) types.ResponseStatusProvider{} func RegisterResponseStatusProvider[T any](fn func(req *http.Request) types.ResponseStatusProvider) { - responseStatusProviders[reflect.TypeFor[T]()] = fn + responseStatusProviders[reflect.TypeOf((*T)(nil)).Elem()] = fn } // responseWriter is a wrapper of http.ResponseWriter, to check whether the response has been written @@ -49,9 +49,9 @@ func (r *responseWriter) WriteHeader(statusCode int) { } var ( - httpReqType = reflect.TypeFor[*http.Request]() - respWriterType = reflect.TypeFor[http.ResponseWriter]() - cancelFuncType = reflect.TypeFor[goctx.CancelFunc]() + httpReqType = reflect.TypeOf((*http.Request)(nil)) + respWriterType = reflect.TypeOf((*http.ResponseWriter)(nil)).Elem() + cancelFuncType = reflect.TypeOf((*goctx.CancelFunc)(nil)).Elem() ) // preCheckHandler checks whether the handler is valid, developers could get first-time feedback, all mistakes could be found at startup diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go index 06bf55b571..123eb29015 100644 --- a/modules/web/middleware/binding.go +++ b/modules/web/middleware/binding.go @@ -30,7 +30,7 @@ func AssignForm(form any, data map[string]any) { typ := reflect.TypeOf(form) val := reflect.ValueOf(form) - for typ.Kind() == reflect.Pointer { + for typ.Kind() == reflect.Ptr { typ = typ.Elem() val = val.Elem() } @@ -51,7 +51,7 @@ func AssignForm(form any, data map[string]any) { } func getRuleBody(field reflect.StructField, prefix string) string { - for rule := range strings.SplitSeq(field.Tag.Get("binding"), ";") { + for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { if strings.HasPrefix(rule, prefix) { return rule[len(prefix) : len(rule)-1] } @@ -99,7 +99,7 @@ func Validate(errs binding.Errors, data map[string]any, f any, l translation.Loc typ := reflect.TypeOf(f) - if typ.Kind() == reflect.Pointer { + if typ.Kind() == reflect.Ptr { typ = typ.Elem() } diff --git a/modules/web/middleware/data.go b/modules/web/middleware/data.go index c8bb8276c5..4603e64052 100644 --- a/modules/web/middleware/data.go +++ b/modules/web/middleware/data.go @@ -5,7 +5,6 @@ package middleware import ( "context" - "maps" "time" "forgejo.org/modules/setting" @@ -23,7 +22,9 @@ func (ds ContextData) GetData() ContextData { } func (ds ContextData) MergeFrom(other ContextData) ContextData { - maps.Copy(ds, other) + for k, v := range other { + ds[k] = v + } return ds } diff --git a/modules/web/route.go b/modules/web/route.go index dc83178f74..ceb97ba333 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -107,8 +107,8 @@ func (r *Route) Methods(methods, pattern string, h ...any) { middlewares, handlerFunc := r.wrapMiddlewareAndHandler(h) fullPattern := r.getPattern(pattern) if strings.Contains(methods, ",") { - methods := strings.SplitSeq(methods, ",") - for method := range methods { + methods := strings.Split(methods, ",") + for _, method := range methods { r.R.With(middlewares...).Method(strings.TrimSpace(method), fullPattern, handlerFunc) } } else { diff --git a/options/gitignore/AdventureGameStudio b/options/gitignore/AdventureGameStudio deleted file mode 100644 index 27a089f475..0000000000 --- a/options/gitignore/AdventureGameStudio +++ /dev/null @@ -1,31 +0,0 @@ -# Built things -_Debug/ -Compiled/ - -# AudioCache can be rebuilt from sources -AudioCache/ - -# Lockfile -_OpenInEditor.lock - -# User settings -Game.agf.user -*.crm.user - -# Backups -Game.agf.bak -backup_acsprset.spr - -# Memory dumps -*.dmp - -# Temporary files -# temporarily created during sprite or room background compression -~aclzw.tmp -# temporary, main game data, before getting packed into exe -game28.dta -# temporary build of the game before being moved to Compiled/ folder -*.exe - -# Log files -warnings.log diff --git a/options/gitignore/Alteryx b/options/gitignore/Alteryx index 8fe3c5cd71..a8e1341ffe 100644 --- a/options/gitignore/Alteryx +++ b/options/gitignore/Alteryx @@ -29,7 +29,7 @@ CASS.ini *.gzlc ## gitignore reference sites -# https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#_ignoring +# https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#Ignoring-Files # https://git-scm.com/docs/gitignore # https://help.github.com/articles/ignoring-files/ diff --git a/options/gitignore/Android b/options/gitignore/Android index e5cbb64142..347e252ef1 100644 --- a/options/gitignore/Android +++ b/options/gitignore/Android @@ -12,9 +12,8 @@ local.properties captures/ .externalNativeBuild/ .cxx/ -*.aab *.apk -output-metadata.json +output.json # IntelliJ *.iml diff --git a/options/gitignore/Angular b/options/gitignore/Angular deleted file mode 100644 index 0383c3a577..0000000000 --- a/options/gitignore/Angular +++ /dev/null @@ -1,28 +0,0 @@ -# Angular specific -/dist/ -/out-tsc/ -/tmp/ -/coverage/ -/e2e/test-output/ -/.angular/ -.angular/ - -# Node modules and dependency files -/node_modules/ -/package-lock.json -/yarn.lock - -# Environment files -/.env - -# Angular CLI and build artefacts -/.angular-cli.json -/.ng/ - -# TypeScript cache -*.tsbuildinfo - -# Logs -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/options/gitignore/Ansible b/options/gitignore/Ansible index 7eaa6e286f..a8b42eb6ee 100644 --- a/options/gitignore/Ansible +++ b/options/gitignore/Ansible @@ -1,2 +1 @@ *.retry -.ansible/ diff --git a/options/gitignore/ArchLinuxPackages b/options/gitignore/ArchLinuxPackages index 289fa5c677..b73905529f 100644 --- a/options/gitignore/ArchLinuxPackages +++ b/options/gitignore/ArchLinuxPackages @@ -3,7 +3,6 @@ *.jar *.exe *.msi -*.deb *.zip *.tgz *.log diff --git a/options/gitignore/AutomationStudio b/options/gitignore/AutomationStudio deleted file mode 100644 index b5552b17a0..0000000000 --- a/options/gitignore/AutomationStudio +++ /dev/null @@ -1,31 +0,0 @@ -# gitignore template for B&R Automation Studio (AS) 4 -# website: https://www.br-automation.com/en-us/products/software/automation-software/automation-studio/ - -# AS temporary directories -Binaries/ -Diagnosis/ -Temp/ -TempObjects/ - -# AS transfer files -*artransfer.br -*arTrsfmode.nv - -# 'ignored' directory -ignored/ - -# ARNC0ext -*arnc0ext.br - -# AS File types -*.bak -*.isopen -*.orig -*.log -*.asar -*.csvlog* -*.set -!**/Physical/**/*.set - -# RevInfo variables -*RevInfo.var diff --git a/options/gitignore/Autotools b/options/gitignore/Autotools index 9a47826430..617156f819 100644 --- a/options/gitignore/Autotools +++ b/options/gitignore/Autotools @@ -31,9 +31,7 @@ autom4te.cache # https://www.gnu.org/software/libtool/ -/libtool /ltmain.sh -.libs/ # http://www.gnu.org/software/texinfo diff --git a/options/gitignore/Bazel b/options/gitignore/Bazel index 4e1d5a2ba0..bc3afc20ba 100644 --- a/options/gitignore/Bazel +++ b/options/gitignore/Bazel @@ -6,7 +6,7 @@ /bazel-* # Directories for the Bazel IntelliJ plugin containing the generated -# IntelliJ project files and plugin configuration. Separate directories are +# IntelliJ project files and plugin configuration. Seperate directories are # for the IntelliJ, Android Studio and CLion versions of the plugin. /.ijwb/ /.aswb/ diff --git a/options/gitignore/C b/options/gitignore/C index 845cda6a8a..c6127b38c1 100644 --- a/options/gitignore/C +++ b/options/gitignore/C @@ -50,6 +50,3 @@ modules.order Module.symvers Mkfile.old dkms.conf - -# debug information files -*.dwo diff --git a/options/gitignore/C++ b/options/gitignore/C++ index bdde3b171b..259148fa18 100644 --- a/options/gitignore/C++ +++ b/options/gitignore/C++ @@ -11,18 +11,10 @@ *.gch *.pch -# Linker files -*.ilk - -# Debugger Files -*.pdb - # Compiled Dynamic libraries *.so *.dylib *.dll -*.so.* - # Fortran module files *.mod @@ -38,32 +30,3 @@ *.exe *.out *.app - -# Build directories -build/ -Build/ -build-*/ - -# CMake generated files -CMakeFiles/ -CMakeCache.txt -cmake_install.cmake -Makefile -install_manifest.txt -compile_commands.json - -# Temporary files -*.tmp -*.log -*.bak -*.swp - -# vcpkg -vcpkg_installed/ - -# debug information files -*.dwo - -# test output & cache -Testing/ -.cache/ \ No newline at end of file diff --git a/options/gitignore/CMake b/options/gitignore/CMake index 1f99f9d2f5..11c76431e1 100644 --- a/options/gitignore/CMake +++ b/options/gitignore/CMake @@ -10,10 +10,3 @@ compile_commands.json CTestTestfile.cmake _deps CMakeUserPresets.json - -# CLion -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#cmake-build-* diff --git a/options/gitignore/ColdBox b/options/gitignore/ColdBox deleted file mode 100644 index 93f003fad3..0000000000 --- a/options/gitignore/ColdBox +++ /dev/null @@ -1,24 +0,0 @@ -# Servelet Ignores -WEB-INF - -# Engines + Database + CBFS + Secrets -.tmp/** -.env -.engine/** -.cbfs/** - -# Logs + Test Results -logs/** -tests/results/** - -## Ignored Dependencies -effective-pom.xml -/coldbox/** -/testbox/** -/modules/** -/lib/java/** - -# NPM JS Assets (If applicable) -**/node_modules/* -npm-debug.log -yarn-error.log diff --git a/options/gitignore/Cursor b/options/gitignore/Cursor deleted file mode 100644 index 234f905b0e..0000000000 --- a/options/gitignore/Cursor +++ /dev/null @@ -1,2 +0,0 @@ -.cursorignore -.cursorindexingignore diff --git a/options/gitignore/Dart b/options/gitignore/Dart index 3150b4060c..3a83c2f087 100644 --- a/options/gitignore/Dart +++ b/options/gitignore/Dart @@ -16,11 +16,9 @@ doc/api/ # Avoid committing generated Javascript files: *.dart.js -# Produced by the --dump-info flag. -*.info.json -# When generated by dart2js. Don't specify *.js if your -# project includes source files written in JavaScript. -*.js +*.info.json # Produced by the --dump-info flag. +*.js # When generated by dart2js. Don't specify *.js if your + # project includes source files written in JavaScript. *.js_ *.js.deps *.js.map diff --git a/options/gitignore/Delphi b/options/gitignore/Delphi index 9db64f626d..8df99b676b 100644 --- a/options/gitignore/Delphi +++ b/options/gitignore/Delphi @@ -68,7 +68,6 @@ *.projdata *.tvsconfig *.dsk -*.dsv # Delphi history and backups __history/ diff --git a/options/gitignore/Dotnet b/options/gitignore/Dotnet deleted file mode 100644 index 7282dbf28f..0000000000 --- a/options/gitignore/Dotnet +++ /dev/null @@ -1,57 +0,0 @@ -## A streamlined .gitignore for modern .NET projects -## including temporary files, build results, and -## files generated by popular .NET tools. If you are -## developing with Visual Studio, the VS .gitignore -## https://github.com/github/gitignore/blob/main/VisualStudio.gitignore -## has more thorough IDE-specific entries. -## -## Get latest from https://github.com/github/gitignore/blob/main/Dotnet.gitignore - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg - -# dotenv environment variables file -.env - -# Others -~$* -*~ -CodeCoverage/ - -# MSBuild Binary and Structured Log -*.binlog - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml diff --git a/options/gitignore/Dotter b/options/gitignore/Dotter deleted file mode 100644 index 86e82e8c5f..0000000000 --- a/options/gitignore/Dotter +++ /dev/null @@ -1,6 +0,0 @@ -# local files are for host-specific overrides -.dotter/local.toml - -# ignore caches -.dotter/cache.toml -.dotter/cache diff --git a/options/gitignore/Drupal b/options/gitignore/Drupal index 3856fe4634..faae808384 100644 --- a/options/gitignore/Drupal +++ b/options/gitignore/Drupal @@ -25,15 +25,12 @@ /web/vendor /web/core /web/modules/README.txt -/web/modules/contrib /web/profiles/README.txt -/web/profiles/contrib /web/sites/development.services.yml /web/sites/example.settings.local.php /web/sites/example.sites.php /web/sites/README.txt /web/themes/README.txt -/web/themes/contrib /web/.csslintrc /web/.editorconfig /web/.eslintignore diff --git a/options/gitignore/Eclipse b/options/gitignore/Eclipse index 85723da801..acec74ac06 100644 --- a/options/gitignore/Eclipse +++ b/options/gitignore/Eclipse @@ -48,7 +48,7 @@ local.properties # Annotation Processing .apt_generated/ -.apt_generated_tests/ +.apt_generated_test/ # Scala IDE specific (Scala & Java development for Eclipse) .cache-main diff --git a/options/gitignore/Elisp b/options/gitignore/Elisp index adef969d3d..206569dc66 100644 --- a/options/gitignore/Elisp +++ b/options/gitignore/Elisp @@ -2,13 +2,7 @@ *.elc # Packaging -.cask/ -.eask/ -.eldev/ -.keg/ - -# Built distribution -dist/ +.cask # Backup files *~ diff --git a/options/gitignore/Emacs b/options/gitignore/Emacs index 489b89280c..d40e86599b 100644 --- a/options/gitignore/Emacs +++ b/options/gitignore/Emacs @@ -47,5 +47,3 @@ flycheck_*.el # network security /network-security.data -# undo-tree -*.~undo-tree~ diff --git a/options/gitignore/Expo b/options/gitignore/Expo deleted file mode 100644 index 164986e1ff..0000000000 --- a/options/gitignore/Expo +++ /dev/null @@ -1,32 +0,0 @@ -# .gitignore template for Expo -# website: https://expo.dev/ -# docs: https://docs.expo.dev/workflow/expo-cli/ -# -# Rationale: -# node_modules/ is always ignored -# .expo/, .expo-shared/ are Expo’s local state and project-settings cache (see docs) -#  Metro caches/logs are *.expo, *.tunnel, *.cache, *.tmp, *.log - -# Node modules -node_modules/ - -# Expo local state and caches -.expo/ # runtime state (Metro bundler, dev-client data, tunnels) -.expo-shared/ # shared project settings (app.json edits, etc.) - -# Metro bundler caches/logs -*.expo # generic Expo temp files -*.tunnel # Expo DevTools tunnels -*.cache # Metro cache folder -*.tmp # temp files created during bundling -*.log # build or Metro logs - -# Environment variables -.env -.env.local -.env.*.local - -# Package manager logs -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/options/gitignore/Firebase b/options/gitignore/Firebase deleted file mode 100644 index 55b8b0ea7f..0000000000 --- a/options/gitignore/Firebase +++ /dev/null @@ -1,28 +0,0 @@ -# Firebase build and deployment files -/firebase-debug.log -/firebase-debug.*.log -.firebaserc - -# Firebase Hosting -/firebase.json -*.cache -hosting/.cache - -# Firebase Functions -/functions/node_modules/ -/functions/.env -/functions/package-lock.json - -# Firebase Emulators -/firebase-*.zip -/.firebase/ -/emulator-ui/ - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Environment files (local configs) -/.env.* diff --git a/options/gitignore/Fortran b/options/gitignore/Fortran index bdde3b171b..259148fa18 100644 --- a/options/gitignore/Fortran +++ b/options/gitignore/Fortran @@ -11,18 +11,10 @@ *.gch *.pch -# Linker files -*.ilk - -# Debugger Files -*.pdb - # Compiled Dynamic libraries *.so *.dylib *.dll -*.so.* - # Fortran module files *.mod @@ -38,32 +30,3 @@ *.exe *.out *.app - -# Build directories -build/ -Build/ -build-*/ - -# CMake generated files -CMakeFiles/ -CMakeCache.txt -cmake_install.cmake -Makefile -install_manifest.txt -compile_commands.json - -# Temporary files -*.tmp -*.log -*.bak -*.swp - -# vcpkg -vcpkg_installed/ - -# debug information files -*.dwo - -# test output & cache -Testing/ -.cache/ \ No newline at end of file diff --git a/options/gitignore/GitHubPages b/options/gitignore/GitHubPages index 807c07fc55..493e69ba39 100644 --- a/options/gitignore/GitHubPages +++ b/options/gitignore/GitHubPages @@ -11,7 +11,7 @@ _site/ /vendor # Specific ignore for GitHub Pages -# GitHub Pages will always use its own deployed version of pages-gem +# GitHub Pages will always use its own deployed version of pages-gem # This means GitHub Pages will NOT use your Gemfile.lock and therefore it is # counterproductive to check this file into the repository. # Details at https://github.com/github/pages-gem/issues/768 diff --git a/options/gitignore/Gleam b/options/gitignore/Gleam deleted file mode 100644 index 599be4eb92..0000000000 --- a/options/gitignore/Gleam +++ /dev/null @@ -1,4 +0,0 @@ -*.beam -*.ez -/build -erl_crash.dump diff --git a/options/gitignore/Go b/options/gitignore/Go index aaadf736e5..6f72f89261 100644 --- a/options/gitignore/Go +++ b/options/gitignore/Go @@ -11,11 +11,8 @@ # Test binary, built with `go test -c` *.test -# Code coverage profiles and other test artifacts +# Output of the go coverage tool, specifically when used with LiteIDE *.out -coverage.* -*.coverprofile -profile.cov # Dependency directories (remove the comment below to include it) # vendor/ @@ -26,7 +23,3 @@ go.work.sum # env file .env - -# Editor/IDE -# .idea/ -# .vscode/ diff --git a/options/gitignore/Godot b/options/gitignore/Godot index e00df843c3..d9aac213e7 100644 --- a/options/gitignore/Godot +++ b/options/gitignore/Godot @@ -1,12 +1,10 @@ # Godot 4+ specific ignores .godot/ -.nomedia # Godot-specific ignores .import/ export.cfg -export_credentials.cfg -*.tmp +export_presets.cfg # Imported translations (automatically generated from CSV files) *.translation diff --git a/options/gitignore/Gradle b/options/gitignore/Gradle index 296d3f0021..a5b111377b 100644 --- a/options/gitignore/Gradle +++ b/options/gitignore/Gradle @@ -1,6 +1,6 @@ .gradle **/build/ -!**/src/**/build/ +!src/**/build/ # Ignore Gradle GUI config gradle-app.setting diff --git a/options/gitignore/HIP b/options/gitignore/HIP deleted file mode 100644 index 5f3324cf14..0000000000 --- a/options/gitignore/HIP +++ /dev/null @@ -1,50 +0,0 @@ -# HIP.gitignore -# GitHub gitignore template for AMD HIP (ROCm) projects -# -# Reference: -# Official AMD ROCm HIP .gitignore: https://github.com/ROCm/hip/blob/amd-staging/.gitignore - -# 1. Build directories and files -/build/ # common build directory -/CMakeFiles/ # CMake internal files -/CMakeCache.txt # CMake cache file -/Makefile # autogenerated Makefile -/cmake_install.cmake # install script -/install_manifest.txt # install manifest list -*.ninja-dep # Ninja dependency files -*.ninja_log # Ninja log files -meson-logs/ # Meson log directory - -# 2. Compilation outputs and intermediates -*.o # object files -*.obj # Windows object files -*.so # shared libraries -*.a # static librarie -*.d # dependency files -*.gch # precompiled headers -*.ii # preprocessed output -*.ii.cpp # C++ preprocessed output -*.out # generic executable outputs -*.exe # Windows executables - -# 3. HIP/ROCm specific binaries and intermediates -*.hsaco # ROCm compiled binary -*.s # assembly output -*.kernels.cpp # autogenerated kernel sources -*.hip.cpp.* # hipcc intermediate outputs - -# 4. Official sample binaries and tutorial outputs -bin/hipInfo # sample binary -bin/hipBusBandwidth # sample binary -bin/hipDispatchLatency # sample binary -bin/hipify-clang # sample tool -samples/**/*.out # tutorial outputs -samples/**/*.code # ISA/code dumps -samples/**/*.hsaco # compiled binaries -samples/**/*.co # kernel code outputs - -# 5. Tags, logs and test outputs -tags # ctags index -*.log # log files -/tests_output/ # custom test output directory -/samples_output/ # custom sample output directory diff --git a/options/gitignore/Haxe b/options/gitignore/Haxe deleted file mode 100644 index efafc9e940..0000000000 --- a/options/gitignore/Haxe +++ /dev/null @@ -1,3 +0,0 @@ -.haxelib/ -.haxelsp/recording/ -dump/ diff --git a/options/gitignore/IAR b/options/gitignore/IAR index cb3fdf2cf0..e8938b31a4 100644 --- a/options/gitignore/IAR +++ b/options/gitignore/IAR @@ -12,24 +12,24 @@ thumbs.db *.~* -# IAR Settings -**/settings/*.crun -**/settings/*.dbgdt -**/settings/*.cspy -**/settings/*.cspy.* -**/settings/*.xcl -**/settings/*.dni -**/settings/*.wsdt -**/settings/*.wspos +# IAR Settings +**/settings/*.crun +**/settings/*.dbgdt +**/settings/*.cspy +**/settings/*.cspy.* +**/settings/*.xcl +**/settings/*.dni +**/settings/*.wsdt +**/settings/*.wspos -# IAR Debug Exe -**/Exe/*.sim +# IAR Debug Exe +**/Exe/*.sim -# IAR Debug Obj -**/Obj/*.pbd -**/Obj/*.pbd.* -**/Obj/*.pbi -**/Obj/*.pbi.* +# IAR Debug Obj +**/Obj/*.pbd +**/Obj/*.pbd.* +**/Obj/*.pbi +**/Obj/*.pbi.* # IAR project "Debug" directory Debug/ diff --git a/options/gitignore/JetBrains b/options/gitignore/JetBrains index 0c1302b4c6..3649d6dc25 100644 --- a/options/gitignore/JetBrains +++ b/options/gitignore/JetBrains @@ -1,4 +1,4 @@ -# Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff @@ -63,7 +63,6 @@ atlassian-ide-plugin.xml # SonarLint plugin .idea/sonarlint/ -.idea/sonarlint.xml # see https://community.sonarsource.com/t/is-the-file-idea-idea-idea-sonarlint-xml-intended-to-be-under-source-control/121119 # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml @@ -71,16 +70,8 @@ crashlytics.properties crashlytics-build.properties fabric.properties -# Editor-based HTTP Client +# Editor-based Rest Client .idea/httpRequests -http-client.private.env.json # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser - -# Apifox Helper cache -.idea/.cache/.Apifox_Helper -.idea/ApifoxUploaderProjectSetting.xml - -# Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215 -.idea/**/copilot.data.migration.*.xml diff --git a/options/gitignore/Julia b/options/gitignore/Julia index 285da1ecd2..29126e47b0 100644 --- a/options/gitignore/Julia +++ b/options/gitignore/Julia @@ -21,8 +21,4 @@ docs/site/ # It records a fixed state of all packages used by the project. As such, it should not be # committed for packages, but should be committed for applications that require a static # environment. -Manifest*.toml - -# File generated by the Preferences package to store local preferences -LocalPreferences.toml -JuliaLocalPreferences.toml +Manifest.toml diff --git a/options/gitignore/JupyterNotebooks b/options/gitignore/JupyterNotebooks index f45b39dedb..f27f90d665 100644 --- a/options/gitignore/JupyterNotebooks +++ b/options/gitignore/JupyterNotebooks @@ -8,9 +8,5 @@ profile_default/ ipython_config.py -# Jupyter lab virtual documents -# https://jupyterlab-lsp.readthedocs.io/en/2.x/Configuring.html#virtual_documents_dir -.virtual_documents/ - # Remove previous ipynb_checkpoints # git rm -r .ipynb_checkpoints/ diff --git a/options/gitignore/Katalon b/options/gitignore/Katalon deleted file mode 100644 index 73a4938fc6..0000000000 --- a/options/gitignore/Katalon +++ /dev/null @@ -1,40 +0,0 @@ -# Katalon Test Suite -# Compiled class file -*.class -*.swp -output -!output/.gitkeep -build - -Libs/TempTestCase* -Libs/TempTestSuite* -bin/lib/TempTestCase* -Reports/ -\.classpath -\.project -\.settings/ -bin/lib/ -Libs/ -.svn/ -.gradle - - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* diff --git a/options/gitignore/KiCad b/options/gitignore/KiCad index 9d5df933c9..a63bc0e7f7 100644 --- a/options/gitignore/KiCad +++ b/options/gitignore/KiCad @@ -8,19 +8,14 @@ *.kicad_pcb-bak *.kicad_sch-bak *-backups -*-cache* -*-bak -*-bak* +*.kicad_prl +*.sch-bak *~ -~* _autosave-* -\#auto_saved_files\# *.tmp *-save.pro *-save.kicad_pcb fp-info-cache -~*.lck -\#auto_saved_files# # Netlist files (exported from Eeschema) *.net @@ -32,9 +27,3 @@ fp-info-cache # Exported BOM files *.xml *.csv - -# Archived Backups (KiCad 6.0) -**/*-backups/*.zip - -# Local project settings -*.kicad_prl diff --git a/options/gitignore/Kotlin b/options/gitignore/Kotlin index 566e06bf99..524f0963bd 100644 --- a/options/gitignore/Kotlin +++ b/options/gitignore/Kotlin @@ -22,6 +22,3 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* - -# Kotlin Gradle plugin data, see https://kotlinlang.org/docs/whatsnew20.html#new-directory-for-kotlin-data-in-gradle-projects -.kotlin/ \ No newline at end of file diff --git a/options/gitignore/LangChain b/options/gitignore/LangChain deleted file mode 100644 index c76ebfd922..0000000000 --- a/options/gitignore/LangChain +++ /dev/null @@ -1,6 +0,0 @@ -# gitignore template for LangChain products, e.g., LangGraph, LangSmith -# website: https://www.langchain.com/ -# website: https://www.langchain.com/langgraph - -# LangGraph -.langgraph_api/ diff --git a/options/gitignore/Laravel b/options/gitignore/Laravel index d5673e321c..297959a19e 100644 --- a/options/gitignore/Laravel +++ b/options/gitignore/Laravel @@ -21,10 +21,3 @@ Homestead.yaml Homestead.json /.vagrant .phpunit.result.cache - -/public/build -/storage/pail -.env.backup -.env.production -.phpactor.json -auth.json diff --git a/options/gitignore/Lefthook b/options/gitignore/Lefthook deleted file mode 100644 index 35409f0e02..0000000000 --- a/options/gitignore/Lefthook +++ /dev/null @@ -1,16 +0,0 @@ -# https://lefthook.dev/configuration/#config-file-name -/.lefthook-local.json -/.lefthook-local.toml -/.lefthook-local.yaml -/.lefthook-local.yml -/lefthook-local.json -/lefthook-local.toml -/lefthook-local.yaml -/lefthook-local.yml -/.config/lefthook-local.json -/.config/lefthook-local.toml -/.config/lefthook-local.yaml -/.config/lefthook-local.yml - -# https://lefthook.dev/configuration/source_dir_local.html -/.lefthook-local/ diff --git a/options/gitignore/Linux b/options/gitignore/Linux index 35ea8c6723..b56bf65d85 100644 --- a/options/gitignore/Linux +++ b/options/gitignore/Linux @@ -3,7 +3,7 @@ # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* -# Metadata left by Dolphin file manager, which comes with KDE Plasma +# KDE directory preferences .directory # Linux trash folder which might appear on any partition or disk @@ -11,6 +11,3 @@ # .nfs files are created when an open file is removed but is still being accessed .nfs* - -# Log files created by default by the nohup command -nohup.out diff --git a/options/gitignore/Luau b/options/gitignore/Luau deleted file mode 100644 index f7ecbc96ae..0000000000 --- a/options/gitignore/Luau +++ /dev/null @@ -1,14 +0,0 @@ -# A fast, small, safe, gradually typed embeddable scripting language derived from Lua -# -# https://github.com/luau-lang/luau -# https://luau.org/ - -# Code coverage -coverage.out - -# Profiling -profile.out -profile.svg - -# Time trace -trace.json diff --git a/options/gitignore/MATLAB b/options/gitignore/MATLAB index 92061b1d8e..01d02dd2e4 100644 --- a/options/gitignore/MATLAB +++ b/options/gitignore/MATLAB @@ -1,33 +1,31 @@ -# Autosave files +# Windows default autosave extension *.asv + +# OSX / *nix default autosave extension *.m~ -*.autosave -*.slx.r* -*.mdl.r* -# Derived content-obscured files -*.p - -# Compiled MEX files +# Compiled MEX binaries (all platforms) *.mex* # Packaged app and toolbox files *.mlappinstall *.mltbx -# Deployable archives -*.ctf - # Generated helpsearch folders helpsearch*/ -# Code generation folders +# Simulink code generation folders slprj/ sccprj/ + +# Matlab code generation folders codegen/ -# Cache files +# Simulink autosave extension +*.autosave + +# Simulink cache files *.slxc -# Cloud based storage dotfile -.MATLABDriveTag +# Octave session info +octave-workspace diff --git a/options/gitignore/Maven b/options/gitignore/Maven index 6d706b8df1..2f4353087f 100644 --- a/options/gitignore/Maven +++ b/options/gitignore/Maven @@ -7,7 +7,7 @@ release.properties dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties -# https://maven.apache.org/wrapper/#usage-without-binary-jar +# https://github.com/takari/maven-wrapper#usage-without-binary-jar .mvn/wrapper/maven-wrapper.jar # Eclipse m2e generated files diff --git a/options/gitignore/MetaTrader5 b/options/gitignore/MetaTrader5 deleted file mode 100644 index 0e235ca712..0000000000 --- a/options/gitignore/MetaTrader5 +++ /dev/null @@ -1,57 +0,0 @@ -# MetaTrader 5 and MQL5 gitignore template -# Project homepage: https://www.metatrader5.com/en - -# Compiled MQL5 executables (binaries) -# These are generated from .mq5 source files and should not be committed. -*.ex5 -*.ex4 # For MQL4 compatibility if you also manage MT4 projects in a similar structure - -# Log files -# Terminal logs, strategy tester logs, and custom logs from Print() functions. -*.log -*.slog # Strategy Tester logs - -# Strategy Tester specific files -# History data, optimization results, and temporary files used by the tester. -*.fxt # FXT files (history data for testing) -*.hst # History data files (can be large) -*.ini # Initialization files (often generated by tester or EAs) -*.dat # Data files (various purposes, often temporary) -*.csv # CSV export files (e.g., from tester reports) -*.jrn # Journal files (tester journal) - -# Market Watch sets and profiles -# User-specific lists of symbols in Market Watch, and terminal profiles. -*.set # Market Watch symbol sets -*.tpl # Chart templates -*.chr # Chart settings files (can be generated when saving templates or profiles) - -# External libraries (DLLs) -# If you use custom DLLs, you might want to ignore them if they are built separately -# and not part of your MQL5 source code repository. -*.dll - -# User-specific configuration and credentials -# Files containing sensitive information or local user settings. -.env # Environment variables (e.g., for Python integration credentials) -*.cfg # Configuration files (if not meant to be shared) -*.json # Be careful: if you have config JSONs you *do* want to commit, add specific exceptions. - # Example: !config.json (to include config.json but ignore other *.json) - -# Temporary files and backup files generated by MetaEditor -*.~* # Temporary files (e.g., ~MyScript.mq5) -*.bak # Backup files (e.g., MyScript.mq5.bak) -*.mqh.bak -*.mq5.bak - -# MetaEditor project files -# Project files for MetaEditor workspaces. -.mqproj - -# Python specific ignores (if you also keep Python scripts or Jupyter notebooks in this repository) -# These are relevant if your Git repo root is higher up (e.g., the terminal folder itself) -# or if you mix Python code within your MQL5 structure. -__pycache__/ # Python compiled bytecode cache -.ipynb_checkpoints/ # Jupyter Notebook checkpoints -*.pyc # Python compiled files -*.pyd # Python dynamic modules diff --git a/options/gitignore/Metals b/options/gitignore/Metals index 779e796bb8..516e7e33f2 100644 --- a/options/gitignore/Metals +++ b/options/gitignore/Metals @@ -1,6 +1,5 @@ -# Metals (Scala Language Server) -# Reference: https://scalameta.org/metals/docs/editors/vscode#files-and-directories-to-include-in-your-gitignore + # Generated Metals (Scala Language Server) files + # Reference: https://scalameta.org/metals/ .metals/ .bloop/ -.ammonite/ -metals.sbt +project/metals.sbt diff --git a/options/gitignore/MicrosoftOffice b/options/gitignore/MicrosoftOffice index 6501a7d322..ddcc9cf6e3 100644 --- a/options/gitignore/MicrosoftOffice +++ b/options/gitignore/MicrosoftOffice @@ -2,7 +2,6 @@ # Word temporary ~$*.doc* -~$*.dot* # Word Auto Backup File Backup of *.doc* diff --git a/options/gitignore/Modelica b/options/gitignore/Modelica deleted file mode 100644 index aa2cc996da..0000000000 --- a/options/gitignore/Modelica +++ /dev/null @@ -1,42 +0,0 @@ -# Modelica - an object-oriented language for modeling of cyber-physical systems -# https://modelica.org/ -# Ignore temporary files, build results, simulation files - -## Modelica-specific files -*~ -*.bak -*.bak-mo -*.mof -\#*\# -*.moe -*.mol - -## Build artefacts -*.exe -*.exp -*.o -*.pyc - -## Simulation files -*.mat - -## Package files -*.gz -*.rar -*.tar -*.zip - -## Dymola-specific files -buildlog.txt -dsfinal.txt -dsin.txt -dslog.txt -dsmodel* -dsres.txt -dymosim* -request -stat -status -stop -success -*. diff --git a/options/gitignore/Move b/options/gitignore/Move deleted file mode 100644 index b7d406e7bb..0000000000 --- a/options/gitignore/Move +++ /dev/null @@ -1,6 +0,0 @@ -# Generated by Move -# will have compiled files -build/ - -# Remove possibly saving credentials to the git repository -.aptos/ diff --git a/options/gitignore/NasaSpecsIntact b/options/gitignore/NasaSpecsIntact index 626d18167e..be53af0e0a 100644 --- a/options/gitignore/NasaSpecsIntact +++ b/options/gitignore/NasaSpecsIntact @@ -1,9 +1,9 @@ # gitignore template for Nasa SpecsIntact (SI) # Website: https://specsintact.ksc.nasa.gov/ # -# Recommended: +# Recommended: # MicrosoftOffice.gitignore -# +# # SpecsIntact (SI) Locking file; this would lock everyone out. *.se$ @@ -20,14 +20,14 @@ SUBMVER.* TTLDIFFS.* # SpecsIntact files that change a lot and don't actually affect SI -# PULL.TBL is an auto-generated file to help speed SI loading. +# PULL.TBL is an auto-generated file to help speed SI loading. PULL.TBL pulltbl.bck # Tailoring information. # Keep tailor.tag; it is a list of tailoring options in SI. -# JOB.OTL informs SI where a spec section came from. +# JOB.OTL informs SI where a spec section came from. # Keeping the old one isn't useful in git. JOB.OTL.OLD @@ -35,6 +35,6 @@ JOB.OTL.OLD # notebooks, and if so, OneNote will litter the SI folder with these. *.onetoc* -# Log files, typically tagfix or other auto generated logs that aren't useful +# Log files, typically tagfix or other auto generated logs that aren't useful # outside of the user that made them and clutter up the index. *.log diff --git a/options/gitignore/Nestjs b/options/gitignore/Nestjs deleted file mode 100644 index 845341e446..0000000000 --- a/options/gitignore/Nestjs +++ /dev/null @@ -1,24 +0,0 @@ -# Nestjs specific -/dist -/node_modules -/build -/tmp - -# Logs -logs -*.log -npm-debug.log* -pnpm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# dotenv environment variable files -.env -.env.development -.env.test -.env.production - -# temp directory -.temp -.tmp diff --git a/options/gitignore/Nextjs b/options/gitignore/Nextjs deleted file mode 100644 index 45c1abce86..0000000000 --- a/options/gitignore/Nextjs +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local -.env - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/options/gitignore/Node b/options/gitignore/Node index 2aa8c99a54..c6bba59138 100644 --- a/options/gitignore/Node +++ b/options/gitignore/Node @@ -5,6 +5,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* +.pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json @@ -56,6 +57,12 @@ web_modules/ # Optional stylelint cache .stylelintcache +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + # Optional REPL history .node_repl_history @@ -67,8 +74,10 @@ web_modules/ # dotenv environment variable files .env -.env.* -!.env.example +.env.development.local +.env.test.local +.env.production.local +.env.local # parcel-bundler cache (https://parceljs.org/) .cache @@ -81,7 +90,6 @@ out # Nuxt.js build / generate output .nuxt dist -.output # Gatsby files .cache/ @@ -96,15 +104,6 @@ dist .temp .cache -# Sveltekit cache directory -.svelte-kit/ - -# vitepress build output -**/.vitepress/dist - -# vitepress cache directory -**/.vitepress/cache - # Docusaurus cache and generated files .docusaurus @@ -117,28 +116,15 @@ dist # DynamoDB Local files .dynamodb/ -# Firebase cache directory -.firebase/ - # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test -# pnpm -.pnpm-store - -# yarn v3 +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz .pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions - -# Vite files -vite.config.js.timestamp-* -vite.config.ts.timestamp-* -.vite/ diff --git a/options/gitignore/NotesAndExtendedConfiguration b/options/gitignore/NotesAndExtendedConfiguration index 58c3f71e48..3e0804f299 100644 --- a/options/gitignore/NotesAndExtendedConfiguration +++ b/options/gitignore/NotesAndExtendedConfiguration @@ -30,7 +30,9 @@ # contain metadata (manifest.json), application code (main.js), stylesheets # (styles.css), and user-configuration data (data.json). # We only want to track data.json, so we: -# 1. exclude everything that the plugin folders contain, -# 2. unignore data.json in the plugin folders -.obsidian/plugins/*/** +# 1. exclude everything under the plugins directory recursively, +# 2. unignore the plugin directories themselves, which then allows us to +# 3. unignore the data.json files +.obsidian/plugins/**/* +!.obsidian/plugins/*/ !.obsidian/plugins/*/data.json diff --git a/options/gitignore/OCaml b/options/gitignore/OCaml index 250caf74e7..a18e08402b 100644 --- a/options/gitignore/OCaml +++ b/options/gitignore/OCaml @@ -8,14 +8,7 @@ *.cmxs *.cmxa -# Files containing detailed information about the compilation (generated -# by `ocamlc`/`ocamlopt` when invoked using the option `-bin-annot`). -# These files are typically useful for code inspection tools -# (e.g. Merlin). -*.cmt -*.cmti - -# ocamlbuild and Dune default working directory +# ocamlbuild working directory _build/ # ocamlbuild targets diff --git a/options/gitignore/Octave b/options/gitignore/Octave index 92061b1d8e..01d02dd2e4 100644 --- a/options/gitignore/Octave +++ b/options/gitignore/Octave @@ -1,33 +1,31 @@ -# Autosave files +# Windows default autosave extension *.asv + +# OSX / *nix default autosave extension *.m~ -*.autosave -*.slx.r* -*.mdl.r* -# Derived content-obscured files -*.p - -# Compiled MEX files +# Compiled MEX binaries (all platforms) *.mex* # Packaged app and toolbox files *.mlappinstall *.mltbx -# Deployable archives -*.ctf - # Generated helpsearch folders helpsearch*/ -# Code generation folders +# Simulink code generation folders slprj/ sccprj/ + +# Matlab code generation folders codegen/ -# Cache files +# Simulink autosave extension +*.autosave + +# Simulink cache files *.slxc -# Cloud based storage dotfile -.MATLABDriveTag +# Octave session info +octave-workspace diff --git a/options/gitignore/OpenTofu b/options/gitignore/OpenTofu deleted file mode 100644 index 8a7f7b76a1..0000000000 --- a/options/gitignore/OpenTofu +++ /dev/null @@ -1,42 +0,0 @@ -# Local .terraform directories -**/.terraform/* - -# .tfstate files -*.tfstate -*.tfstate.* - -# Crash log files -crash.log -crash.*.log - -# Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject -# to change depending on the environment. -*.tfvars -*.tfvars.json - -# Ignore override files as they are usually used to override resources locally and so -# are not checked in -override.tf -override.tofu -override.tf.json -override.tofu.json -*_override.tf -*_override.tofu -*_override.tf.json -*_override.tofu.json - -# Ignore transient lock info files created by tofu apply -.terraform.tfstate.lock.info - -# Include override files you do wish to add to version control using negated pattern -# !example_override.tf -# !example_override.tofu - -# Include tfplan files to ignore the plan output of command: tofu plan -out=tfplan -# example: *tfplan* - -# Ignore CLI configuration files -.terraformrc -terraform.rc diff --git a/options/gitignore/Packer b/options/gitignore/Packer index caa24ec789..2cbc1ad079 100644 --- a/options/gitignore/Packer +++ b/options/gitignore/Packer @@ -5,9 +5,9 @@ packer_cache/ crash.log # https://www.packer.io/guides/hcl/variables -# Exclude all .pkrvars.hcl files, which are likely to contain sensitive data, -# such as password, private keys, and other secrets. These should not be part of -# version control as they are data points which are potentially sensitive and +# Exclude all .pkrvars.hcl files, which are likely to contain sensitive data, +# such as password, private keys, and other secrets. These should not be part of +# version control as they are data points which are potentially sensitive and # subject to change depending on the environment. # *.pkrvars.hcl diff --git a/options/gitignore/Perl b/options/gitignore/Perl index 79d77ce623..fb8b193173 100644 --- a/options/gitignore/Perl +++ b/options/gitignore/Perl @@ -33,9 +33,3 @@ inc/ /MANIFEST.bak /pm_to_blib /*.zip - -# Carton/Carmel -/local/ -/.carmel/ -# cpanfile.snapshot should generally be ignored for library code, otherwise included -# cpanfile.snapshot diff --git a/options/gitignore/PlatformIO b/options/gitignore/PlatformIO deleted file mode 100644 index 2de98aba16..0000000000 --- a/options/gitignore/PlatformIO +++ /dev/null @@ -1,6 +0,0 @@ -.pio -.pioenvs -.piolibdeps -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json diff --git a/options/gitignore/Python b/options/gitignore/Python index e15106e38f..82f927558a 100644 --- a/options/gitignore/Python +++ b/options/gitignore/Python @@ -1,6 +1,6 @@ # Byte-compiled / optimized / DLL files __pycache__/ -*.py[codz] +*.py[cod] *$py.class # C extensions @@ -27,8 +27,8 @@ share/python-wheels/ MANIFEST # PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec @@ -46,7 +46,7 @@ htmlcov/ nosetests.xml coverage.xml *.cover -*.py.cover +*.py,cover .hypothesis/ .pytest_cache/ cover/ @@ -92,38 +92,25 @@ ipython_config.py # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. -# Pipfile.lock - -# UV -# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# uv.lock +#Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -# poetry.lock -# poetry.toml +#poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. -# https://pdm-project.org/en/latest/usage/project/#working-with-version-control -# pdm.lock -# pdm.toml +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml .pdm-python .pdm-build/ -# pixi -# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. -# pixi.lock -# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one -# in the .venv directory. It is recommended not to include this directory in version control. -.pixi - # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ @@ -131,25 +118,11 @@ __pypackages__/ celerybeat-schedule celerybeat.pid -# Redis -*.rdb -*.aof -*.pid - -# RabbitMQ -mnesia/ -rabbitmq/ -rabbitmq-data/ - -# ActiveMQ -activemq-data/ - # SageMath parsed files *.sage.py # Environments .env -.envrc .venv env/ venv/ @@ -182,35 +155,8 @@ dmypy.json cython_debug/ # PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -# .idea/ - -# Abstra -# Abstra is an AI-powered process automation framework. -# Ignore directories containing user credentials, local state, and settings. -# Learn more at https://abstra.io/docs -.abstra/ - -# Visual Studio Code -# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore -# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore -# and can be added to the global gitignore or merged into this file. However, if you prefer, -# you could uncomment the following to ignore the entire vscode folder -# .vscode/ - -# Ruff stuff: -.ruff_cache/ - -# PyPI configuration file -.pypirc - -# Marimo -marimo/_static/ -marimo/_lsp/ -__marimo__/ - -# Streamlit -.streamlit/secrets.toml +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/options/gitignore/Rust b/options/gitignore/Rust index ad67955886..d01bd1a990 100644 --- a/options/gitignore/Rust +++ b/options/gitignore/Rust @@ -1,7 +1,11 @@ # Generated by Cargo # will have compiled files and executables -debug -target +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk @@ -9,13 +13,9 @@ target # MSVC Windows builds of rustc generate these, which store debugging information *.pdb -# Generated by cargo mutants -# Contains mutation testing data -**/mutants.out*/ - # RustRover # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +#.idea/ \ No newline at end of file diff --git a/options/gitignore/SBT b/options/gitignore/SBT index 98ee5070d7..5ed6acb657 100644 --- a/options/gitignore/SBT +++ b/options/gitignore/SBT @@ -10,4 +10,3 @@ project/plugins/project/ .history .cache .lib/ -.bsp/ diff --git a/options/gitignore/SSDT-sqlproj b/options/gitignore/SSDT-sqlproj deleted file mode 100644 index 36c16598dd..0000000000 --- a/options/gitignore/SSDT-sqlproj +++ /dev/null @@ -1,31 +0,0 @@ -## Ignore Visual Studio SSDT sqlproj specific temporary files, build results, etc -## -## -## Get latest from https://github.com/github/gitignore/blob/master/SSDT-sqlproj.gitignore -# Build output -bin/ -obj/ - -# DACPAC files -*.dacpac - -# Publish profiles (optional, if environment-specific) -*.publish.xml - -# SQL Server debug files -*.dbmdl -*.sqlcmdvars - -# Visual Studio settings -.vs/ - -# User-specific files -*.user -*.suo -*.userosscache -*.sln.docstates - -# Backup files -*.bak -*.log - diff --git a/options/gitignore/STM32CubeIDE b/options/gitignore/STM32CubeIDE deleted file mode 100644 index fc7ee5c7fd..0000000000 --- a/options/gitignore/STM32CubeIDE +++ /dev/null @@ -1,51 +0,0 @@ -# STM32CubeIDE specific files - -# Project-specific settings. Ignore it if developers have different preferences. -# However, if you want all team members to use the same code formatting and settings, -# consider including the .settings folder in the repository. - -# /.settings/ - -# Ignore Eclipse-based IDE launch configurations. -# Uncomment if you want each developer to have their own unique debug configurations. -#*.launch - -# Ignore any JLink-related files (debug configurations). -# Uncomment if you want each developer to have their own unique debug configurations. -#*.jlink - -# Ignore log files generated by the IDE. -# These are not necessary for version control. -*.log - -# Build files - -# Ignore build output directories. -# These are not needed in the repository as they are generated during the build process. -Debug/ -Release/ - -# Ignore common binary and object files generated during compilation. -# They should not be included in the repository as they are build artifacts. -*.elf -*.map -*.bin -*.hex -*.srec -*.lst -*.o -*.d -*.a -*.su -*.crl - -#TouchGFX files (in case your project has touchGFX) -TouchGFX/generated -TouchGFX/build -TouchGFX/simulator/msvs/.vs - -# Backup files - -# Ignore temporary and backup files generated by the operating system and editor. -# These are not needed in the repository. -*.bak diff --git a/options/gitignore/Salesforce b/options/gitignore/Salesforce deleted file mode 100644 index 3547a96f6d..0000000000 --- a/options/gitignore/Salesforce +++ /dev/null @@ -1,39 +0,0 @@ -# This file is used for Git repositories to specify intentionally untracked files that Git should ignore. -# If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore -# For useful gitignore templates see: https://github.com/github/gitignore - -# Salesforce cache -.sf/ -.sfdx/ -.localdevserver/ -deploy-options.json -.localdev - -# LWC VSCode autocomplete -**/lwc/jsconfig.json - -# LWC Jest coverage reports -coverage/ - -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Eslint cache -.eslintcache - -# Windows system files -Thumbs.db -ehthumbs.db -[Dd]esktop.ini -$RECYCLE.BIN/ - -# Salesforce Analyzer results -sca-results.csv -sfca_results.json - -# Local environment variables -.env diff --git a/options/gitignore/Solidity-Remix b/options/gitignore/Solidity-Remix deleted file mode 100644 index a49555a43a..0000000000 --- a/options/gitignore/Solidity-Remix +++ /dev/null @@ -1,15 +0,0 @@ -# Remix compiler artifacts -**/artifacts/ -**/artifacts/** - -# Remix plugin state folders -deps/ -states/ - -# Debug info -*.dbg.json -*.tsbuildinfo - -# Optional -.env -.env.local \ No newline at end of file diff --git a/options/gitignore/Stata b/options/gitignore/Stata index 288abf8a15..07997bb120 100644 --- a/options/gitignore/Stata +++ b/options/gitignore/Stata @@ -8,7 +8,6 @@ *.smcl *.stpr *.stsem -~*.stswp # Graphic export files from Stata # Stata command graph export: http://www.stata.com/manuals14/g-2graphexport.pdf diff --git a/options/gitignore/TeX b/options/gitignore/TeX index 9308a4b649..a1f5212090 100644 --- a/options/gitignore/TeX +++ b/options/gitignore/TeX @@ -26,9 +26,7 @@ ## Bibliography auxiliary files (bibtex/biblatex/biber): *.bbl -*.bbl-SAVE-ERROR *.bcf -*.bcf-SAVE-ERROR *.blg *-blx.aux *-blx.bib @@ -59,9 +57,6 @@ acs-*.bib # amsthm *.thm -# attachfile2 -*.atfi - # beamer *.nav *.pre @@ -70,7 +65,6 @@ acs-*.bib # changes *.soc -*.loc # comment *.cut @@ -114,11 +108,8 @@ acs-*.bib *.acn *.acr *.glg -*.glg-abr *.glo -*.glo-abr *.gls -*.gls-abr *.glsdefs *.lzo *.lzs @@ -161,9 +152,6 @@ acs-*.bib # *.tikz *-tikzDictionary -# latexindent will create succesive backup files by default -#*.bak* - # listings *.lol @@ -186,7 +174,6 @@ acs-*.bib # minted _minted* -*.data.minted *.pyg # morewrites @@ -214,10 +201,6 @@ _minted* # scrwfile *.wrt -# spelling -*.spell.bad -*.spell.txt - # svg svg-inkscape/ @@ -283,9 +266,6 @@ TSWLatexianTemp* *.bak *.sav -# latexindent.pl -*.bak[0-9]* - # Texpad .texpadtmp diff --git a/options/gitignore/Terraform b/options/gitignore/Terraform index 78e7733b54..2faf43d0a1 100644 --- a/options/gitignore/Terraform +++ b/options/gitignore/Terraform @@ -1,5 +1,5 @@ # Local .terraform directories -.terraform/ +**/.terraform/* # .tfstate files *.tfstate @@ -10,8 +10,8 @@ crash.log crash.*.log # Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject # to change depending on the environment. *.tfvars *.tfvars.json @@ -35,10 +35,3 @@ override.tf.json # Ignore CLI configuration files .terraformrc terraform.rc - -# Optional: ignore graph output files generated by `terraform graph` -# *.dot - -# Optional: ignore plan files saved before destroying Terraform configuration -# Uncomment the line below if you want to ignore planout files. -# planout \ No newline at end of file diff --git a/options/gitignore/TestComplete b/options/gitignore/TestComplete deleted file mode 100644 index 8378b9eedb..0000000000 --- a/options/gitignore/TestComplete +++ /dev/null @@ -1,14 +0,0 @@ -# Test Complete ignore files: https://support.smartbear.com/viewarticle/68002/ - -# Tester-specific Settings -*.tcCFGExtender -*.tcLS - -# Type library declarations -*.tlb - -# Log files -*.tcLogs - -# Backup files -*.bak diff --git a/options/gitignore/TwinCAT3 b/options/gitignore/TwinCAT3 index 6786d5e833..7bd6f87505 100644 --- a/options/gitignore/TwinCAT3 +++ b/options/gitignore/TwinCAT3 @@ -1,57 +1,25 @@ -### TwinCAT3 ### +# gitignore template for TwinCAT3 # website: https://www.beckhoff.com/twincat3/ +# +# Recommended: VisualStudio.gitignore -# TwinCAT PLC -*.plcproj.bak -*.plcproj.orig +# TwinCAT files *.tpy *.tclrs -*.library *.compiled-library *.compileinfo -*.asm -*.core -LineIDs.dbg -LineIDs.dbg.bak - -# TwinCAT C++ and shared types -# ignoring the TMC file is only useful for plain PLC programming -# as soon as shared data types (via tmc), C++ or in general TcCom-Module are used, the TMC file has to be part of the repository +# Don't include the tmc-file rule if either of the following is true: +# 1. You've got TwinCAT C++ projects, as the information in the TMC-file is created manually for the C++ projects (in that case, only (manually) ignore the tmc-files for the PLC projects) +# 2. You've created a standalone PLC-project and added events to it, as these are stored in the TMC-file. *.tmc *.tmcRefac - -# TwinCAT project files -*.tsproj.bak -*.tsproj.b?k -*.tsproj.orig -*.tspproj.bak -*.xti.bak -*.xti.bk? -*.xti.orig -*.xtv -*.xtv.bak -*.xtv.bk? -*.xt?.bk? -*.xt?.orig - -# Multiuser specific -**/.TcGit/ - -# exclude not required folders -**/_Boot/ -**/_CompileInfo/ -**/_Libraries/ -**/_ModuleInstall/ -**/_Deployment/ -**/_Repository/ - - -# To include a specific library directory (i.e. third party/custom libs), -# use pattern `!/**/_Libraries//` i.e. `!/**/_Libraries/www.tcunit.org/` -# - -# VS Shell project specific files and folders -**/.vs/ -*.~u +*.library *.project.~u -*.suo +*.tsproj.bak +*.xti.bak +LineIDs.dbg +LineIDs.dbg.bak +_Boot/ +_CompileInfo/ +_Libraries/ +_ModuleInstall/ \ No newline at end of file diff --git a/options/gitignore/UTAU b/options/gitignore/UTAU deleted file mode 100644 index 173bc781f1..0000000000 --- a/options/gitignore/UTAU +++ /dev/null @@ -1,52 +0,0 @@ -# Adobe Audition -*.pkf - -# UTAU Engines -*.ctspec -*.d4c -*.dio -*.frc -*.frt -*.frq -*.harvest -*.lessaudio -*.llsm -*.mrq -*.pitchtier -*.platinum -*.pmk -*.sc.npz -*.star -*.uspec -*.vs4ufrq - -# UTAU related tools -$read -*.setParam-Scache -*.lbp -*.lbp.caches/* - -# OpenUtau -errors.txt - -# Deepvocal -*.DVModel -*-log.txt -SKC -SKI -SKC_1 -SKC_2 -*.sksd - -# VocalSharp -*.scep -*.vssf -*.vsdx -*.vsdxindex - -# Binary Archive -*.7z -*.zip -*.rar -*.exe - diff --git a/options/gitignore/UiPath b/options/gitignore/UiPath index 0948dcc846..f0c2267b89 100644 --- a/options/gitignore/UiPath +++ b/options/gitignore/UiPath @@ -1,4 +1,4 @@ -# gitignore template for RPA development using UiPath Studio +# gitignore template for RPA development using UiPath Studio # website: https://www.uipath.com/product/studio # # Recommended: n/a diff --git a/options/gitignore/Umbraco b/options/gitignore/Umbraco index 260c741209..1dc3da526c 100644 --- a/options/gitignore/Umbraco +++ b/options/gitignore/Umbraco @@ -21,7 +21,7 @@ ## The [Mm]edia/ folder contains content. Content may vary by environment and should therefore not be added to source control. ## Uncomment this line if you think it fits the way you work on your project. -## **/[Mm]edia/ +## **/[Mm]edia/ # Don't ignore Umbraco packages (VisualStudio.gitignore mistakes this for a NuGet packages folder) # Make sure to include details from VisualStudio.gitignore BEFORE this @@ -43,7 +43,7 @@ *.sqlite.db* #ignore umbraco data/views/settings -**/umbraco/* +**/umbraco/ #include default location for modelsbuilder output !**/umbraco/models diff --git a/options/gitignore/Unity b/options/gitignore/Unity index 9eb70ce1f5..58cbc8256e 100644 --- a/options/gitignore/Unity +++ b/options/gitignore/Unity @@ -2,7 +2,6 @@ # # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore # -.utmp/ /[Ll]ibrary/ /[Tt]emp/ /[Oo]bj/ @@ -10,11 +9,6 @@ /[Bb]uilds/ /[Ll]ogs/ /[Uu]ser[Ss]ettings/ -*.log - -# By default unity supports Blender asset imports, *.blend1 blender files do not need to be commited to version control. -*.blend1 -*.blend1.meta # MemoryCaptures can get excessive in size. # They also could contain extremely sensitive data @@ -28,8 +22,6 @@ # Autogenerated Jetbrains Rider plugin /[Aa]ssets/Plugins/Editor/JetBrains* -# Jetbrains Rider personal-layer settings -*.DotSettings.user # Visual Studio cache directory .vs/ @@ -63,37 +55,18 @@ ExportedObj/ # Unity3D generated file on crash reports sysinfo.txt -# Mono auto generated files -mono_crash.* - # Builds *.apk *.aab *.unitypackage -*.unitypackage.meta *.app # Crashlytics generated file crashlytics-build.properties -# TestRunner generated files -InitTestScene*.unity* +# Packed Addressables +/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* -# Addressables default ignores, before user customizations -/ServerData -/[Aa]ssets/StreamingAssets/aa* -/[Aa]ssets/AddressableAssetsData/link.xml* -/[Aa]ssets/Addressables_Temp* -# By default, Addressables content builds will generate addressables_content_state.bin -# files in platform-specific subfolders, for example: -# /Assets/AddressableAssetsData/OSX/addressables_content_state.bin -/[Aa]ssets/AddressableAssetsData/*/*.bin* - -# Visual Scripting auto-generated files -/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db -/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db.meta -/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers -/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers.meta - -# Auto-generated scenes by play mode tests -/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity* +# Temporary auto-generated Android Assets +/[Aa]ssets/[Ss]treamingAssets/aa.meta +/[Aa]ssets/[Ss]treamingAssets/aa/* diff --git a/options/gitignore/UnrealEngine b/options/gitignore/UnrealEngine index b70ad5aae2..6e0d95fb31 100644 --- a/options/gitignore/UnrealEngine +++ b/options/gitignore/UnrealEngine @@ -40,7 +40,6 @@ *.sdf *.VC.db *.VC.opendb -.vsconfig # Precompiled Assets SourceArt/**/*.png diff --git a/options/gitignore/VBA b/options/gitignore/VBA deleted file mode 100644 index 710dab19de..0000000000 --- a/options/gitignore/VBA +++ /dev/null @@ -1,40 +0,0 @@ - -# Office temporary files -~$* - -# Access database lock files (laccdb, ldb) -*.[lL][aA][cC][cC][dD][bB] -*.[lL][dD][bB] - -# The following sections constitute a list of Office file extensions that support VBA. -# If you want to exclude Office files from your repo, uncomment the corresponding file extensions. - -# Excel (xls, xlsb, xlsm, xlt, xltm, xla, xlam) -#*.[xX][lL][sS] -#*.[xX][lL][sS][bB] -#*.[xX][lL][sS][mM] -#*.[xX][lL][tT] -#*.[xX][lL][tT][mM] -#*.[xX][lL][aA] -#*.[xX][lL][aA][mM] - -# Word (doc, docm, dot, dotm) -#*.[dD][oO][cC] -#*.[dD][oO][cC][mM] -#*.[dD][oO][tT] -#*.[dD][oO][tT][mM] - -# Access (accda, accdb, accde, mdb, mde) -#*.[aA][cC][cC][dD][aA] -#*.[aA][cC][cC][dD][bB] -#*.[aA][cC][cC][dD][eE] -#*.[mM][dD][bB] -#*.[mM][dD][eE] - -# PowerPoint (ppt, pptm, pot, potm, pps, ppsm) -#*.[pP][pP][tT] -#*.[pP][pP][tT][mM] -#*.[pP][oO][tT] -#*.[pP][oO][tT][mM] -#*.[pP][pP][sS] -#*.[pP][pP][sS][mM] diff --git a/options/gitignore/Vim b/options/gitignore/Vim index cb8a049960..19fa63264c 100644 --- a/options/gitignore/Vim +++ b/options/gitignore/Vim @@ -1,7 +1,6 @@ # Swap [._]*.s[a-v][a-z] -# comment out the next line if you don't need vector files -!*.svg +!*.svg # comment out if you don't need vector files [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] diff --git a/options/gitignore/VirtualEnv b/options/gitignore/VirtualEnv index d895d00efe..b2c22f2af7 100644 --- a/options/gitignore/VirtualEnv +++ b/options/gitignore/VirtualEnv @@ -1,5 +1,5 @@ # Virtualenv -# https://realpython.com/python-virtual-environments-a-primer/#the-virtualenv-project +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ .Python [Bb]in [Ii]nclude diff --git a/options/gitignore/VisualStudio b/options/gitignore/VisualStudio index 47a94ef17f..8a30d258ed 100644 --- a/options/gitignore/VisualStudio +++ b/options/gitignore/VisualStudio @@ -9,7 +9,6 @@ *.user *.userosscache *.sln.docstates -*.env # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs @@ -22,37 +21,17 @@ mono_crash.* [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ - -[Dd]ebug/x64/ -[Dd]ebugPublic/x64/ -[Rr]elease/x64/ -[Rr]eleases/x64/ -bin/x64/ -obj/x64/ - -[Dd]ebug/x86/ -[Dd]ebugPublic/x86/ -[Rr]elease/x86/ -[Rr]eleases/x86/ -bin/x86/ -obj/x86/ - +x64/ +x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ -[Aa][Rr][Mm]64[Ee][Cc]/ bld/ +[Bb]in/ [Oo]bj/ -[Oo]ut/ [Ll]og/ [Ll]ogs/ -# Build results on 'Bin' directories -**/[Bb]in/* -# Uncomment if you have tasks that rely on *.refresh files to move binaries -# (https://github.com/github/gitignore/pull/3736) -#!**/[Bb]in/*.refresh - # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot @@ -64,16 +43,12 @@ Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -*.trx # NUnit *.VisualState.xml TestResult.xml nunit-*.xml -# Approval Tests result files -*.received.* - # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ @@ -100,7 +75,6 @@ StyleCopReport.xml *.ilk *.meta *.obj -*.idb *.iobj *.pch *.pdb @@ -108,8 +82,6 @@ StyleCopReport.xml *.pgc *.pgd *.rsp -# but not Directory.Build.rsp, as it configures directory-level build defaults -!Directory.Build.rsp *.sbr *.tlb *.tli @@ -181,7 +153,6 @@ coverage*.info # NCrunch _NCrunch_* -.NCrunch_* .*crunch*.local.xml nCrunchTemp_* @@ -323,6 +294,9 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + # Visual Studio 6 workspace and project file (working project files containing files to include in project) *.dsw *.dsp @@ -340,22 +314,22 @@ node_modules/ _Pvt_Extensions # Paket dependency manager -**/.paket/paket.exe +.paket/paket.exe paket-files/ # FAKE - F# Make -**/.fake/ +.fake/ # CodeRush personal settings -**/.cr/personal +.cr/personal # Python Tools for Visual Studio (PTVS) -**/__pycache__/ +__pycache__/ *.pyc # Cake - Uncomment if you are using it -#tools/** -#!tools/packages.config +# tools/** +# !tools/packages.config # Tabs Studio *.tss @@ -377,19 +351,15 @@ ASALocalRun/ # MSBuild Binary and Structured Log *.binlog -MSBuild_Logs/ - -# AWS SAM Build and Temporary Artifacts folder -.aws-sam # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder -**/.mfractor/ +.mfractor/ # Local History for Visual Studio -**/.localhistory/ +.localhistory/ # Visual Studio History (VSHistory) files .vshistory/ @@ -401,7 +371,7 @@ healthchecksdb MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder -**/.ionide/ +.ionide/ # Fody - auto-generated XML schema FodyWeavers.xsd @@ -412,17 +382,17 @@ FodyWeavers.xsd !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json -!.vscode/*.code-snippets +*.code-workspace # Local History for Visual Studio Code .history/ -# Built Visual Studio Code Extensions -*.vsix - # Windows Installer files from build outputs *.cab *.msi *.msix *.msm *.msp + +# JetBrains Rider +*.sln.iml diff --git a/options/gitignore/VisualStudioCode b/options/gitignore/VisualStudioCode index b72ba8b5bd..45fce1d71c 100644 --- a/options/gitignore/VisualStudioCode +++ b/options/gitignore/VisualStudioCode @@ -4,7 +4,9 @@ !.vscode/launch.json !.vscode/extensions.json !.vscode/*.code-snippets -!*.code-workspace + +# Local History for Visual Studio Code +.history/ # Built Visual Studio Code Extensions *.vsix diff --git a/options/gitignore/Zig b/options/gitignore/Zig index 0180838aed..748837a058 100644 --- a/options/gitignore/Zig +++ b/options/gitignore/Zig @@ -1,3 +1,5 @@ .zig-cache/ zig-out/ -*.o +build/ +build-*/ +docgen_tmp/ \ No newline at end of file diff --git a/options/gitignore/libogc b/options/gitignore/libogc deleted file mode 100644 index facd77526f..0000000000 --- a/options/gitignore/libogc +++ /dev/null @@ -1,91 +0,0 @@ -# Ignore build directories -build/ - -# Ignore Wii-specific metadata files -meta.xml -icon.png - - -# Ignore editor or IDE-specific files -.vscode/ -.idea/ -*.sublime-project -*.sublime-workspace - -# Ignore backup or temporary files -*~ -*.bak -*.swp -*.tmp - -# Ignore log files -*.log - -# Ignore libraries and dependencies -lib/ -deps/ -obj/ - -# Ignore operating system-specific files -$RECYCLE.BIN/ -.Trash-1000/ -.Spotlight-V100/ -.fseventsd/ -.DS_Store - -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj -*.elf -*.o -*.bin - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex -*.dol -*.elf - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf diff --git a/options/gitignore/macOS b/options/gitignore/macOS index a4557fbaea..135767fc07 100644 --- a/options/gitignore/macOS +++ b/options/gitignore/macOS @@ -1,9 +1,10 @@ # General .DS_Store -__MACOSX/ .AppleDouble .LSOverride -Icon[ ] + +# Icon must end with two \r +Icon # Thumbnails ._* diff --git a/options/gitignore/mise b/options/gitignore/mise deleted file mode 100644 index 2f44750e3a..0000000000 --- a/options/gitignore/mise +++ /dev/null @@ -1,11 +0,0 @@ -# https://mise.jdx.dev/configuration.html -# https://mise.jdx.dev/configuration/environments.html -.mise.*.local.toml -.mise.local.toml -mise.*.local.toml -mise.local.toml -.mise/*.local.toml -mise/*.local.toml - -# https://mise.jdx.dev/configuration.html#tool-versions -#.tool-versions diff --git a/options/license/ALGLIB-Documentation b/options/license/ALGLIB-Documentation deleted file mode 100644 index 6b966d4b41..0000000000 --- a/options/license/ALGLIB-Documentation +++ /dev/null @@ -1,17 +0,0 @@ - Copyright 1994-2009 Sergey Bochkanov, ALGLIB Project. All rights reserved. - -Redistribution and use of this document (ALGLIB Reference Manual) with or -without modification, are permitted provided that such redistributions will -retain the above copyright notice, this condition and the following disclaimer -as the first (or last) lines of this file. - -THIS DOCUMENTATION IS PROVIDED BY THE ALGLIB PROJECT "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 ALGLIB PROJECT 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 DOCUMENTATION, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/options/license/Advanced-Cryptics-Dictionary b/options/license/Advanced-Cryptics-Dictionary deleted file mode 100644 index 904b942ae6..0000000000 --- a/options/license/Advanced-Cryptics-Dictionary +++ /dev/null @@ -1,10 +0,0 @@ -License text: Copyright (c) J Ross Beresford 1993-1999. All Rights Reserved. - -The following restriction is placed on the use of this publication: -if The UK Advanced Cryptics Dictionary is used in a software package -or redistributed in any form, the copyright notice must be -prominently displayed and the text of this document must be included -verbatim. - -There are no other restrictions: I would like to see the list -distributed as widely as possible. diff --git a/options/license/Artistic-dist b/options/license/Artistic-dist deleted file mode 100644 index ff0b79e470..0000000000 --- a/options/license/Artistic-dist +++ /dev/null @@ -1,125 +0,0 @@ -The "Artistic License" - - Preamble - -The intent of this document is to state the conditions under which a -Package may be copied, such that the Copyright Holder maintains some -semblance of artistic control over the development of the Package, -while giving the users of the package the right to use and distribute -the Package in a more-or-less customary fashion, plus the right to make -reasonable modifications. - -It also grants you the rights to reuse parts of a Package in your own -programs without transferring this License to those programs, provided -that you meet some reasonable requirements. - -Definitions: - - "Package" refers to the collection of files distributed by the - Copyright Holder, and derivatives of that collection of files - created through textual modification. - - "Standard Version" refers to such a Package if it has not been - modified, or has been modified in accordance with the wishes - of the Copyright Holder as specified below. - - "Copyright Holder" is whoever is named in the copyright or - copyrights for the package. - - "You" is you, if you're thinking about copying or distributing - this Package. - - "Reasonable copying fee" is whatever you can justify on the - basis of media cost, duplication charges, time of people involved, - and so on. (You will not be required to justify it to the - Copyright Holder, but only to the computing community at large - as a market that must bear the fee.) - - "Freely Available" means that no fee is charged for the item - itself, though there may be fees involved in handling the item. - It also means that recipients of the item may redistribute it - under the same conditions they received it. - -1. You may make and give away verbatim copies of the source form of the -Standard Version of this Package without restriction, provided that you -duplicate all of the original copyright notices and associated disclaimers. - -2. You may apply bug fixes, portability fixes and other modifications -derived from the Public Domain or from the Copyright Holder. A Package -modified in such a way shall still be considered the Standard Version. - -3. You may otherwise modify your copy of this Package in any way, provided -that you insert a prominent notice in each changed file stating how and -when you changed that file, and provided that you do at least ONE of the -following: - - a) place your modifications in the Public Domain or otherwise make them - Freely Available, such as by posting said modifications to Usenet or - an equivalent medium, or placing the modifications on a major archive - site such as uunet.uu.net, or by allowing the Copyright Holder to include - your modifications in the Standard Version of the Package. - - b) use the modified Package only within your corporation or organization. - - c) rename any non-standard executables so the names do not conflict - with standard executables, which must also be provided, and provide - a separate manual page for each non-standard executable that clearly - documents how it differs from the Standard Version. - - d) make other distribution arrangements with the Copyright Holder. - -4. You may distribute the programs of this Package in object code or -executable form, provided that you do at least ONE of the following: - - a) distribute a Standard Version of the executables and library files, - together with instructions (in the manual page or equivalent) on where - to get the Standard Version. - - b) accompany the distribution with the machine-readable source of - the Package with your modifications. - - c) give non-standard executables non-standard names, and clearly - document the differences in manual pages (or equivalent), together - with instructions on where to get the Standard Version. - - d) make other distribution arrangements with the Copyright Holder. - -5. You may charge a reasonable copying fee for any distribution of this -Package. You may charge any fee you choose for support of this -Package. You may not charge a fee for this Package itself. However, -you may distribute this Package in aggregate with other (possibly -commercial) programs as part of a larger (possibly commercial) software -distribution provided that you do not advertise this Package as a -product of your own. - -6. The scripts and library files supplied as input to or produced as -output from the programs of this Package do not automatically fall -under the copyright of this Package, but belong to whoever generated -them, and may be sold commercially, and may be aggregated with this -Package. If such scripts or library files are aggregated with this -Package via the so-called "undump" or "unexec" methods of producing a -binary executable image, then distribution of such an image shall -neither be construed as a distribution of this Package nor shall it -fall under the restrictions of Paragraphs 3 and 4, provided that you do -not represent such an executable image as a Standard Version of this -Package. - -7. You may reuse parts of this Package in your own programs, provided that -you explicitly state where you got them from, in the source code (and, left -to your courtesy, in the documentation), duplicating all the associated -copyright notices and disclaimers. Besides your changes, if any, must be -clearly marked as such. Parts reused that way will no longer fall under this -license if, and only if, the name of your program(s) have no immediate -connection with the name of the Package itself or its associated programs. -You may then apply whatever restrictions you wish on the reused parts or -choose to place them in the Public Domain--this will apply only within the -context of your package. - -8. The name of the Copyright Holder may not be used to endorse or promote -products derived from this software without specific prior written permission. - -9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED -WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - -THE END diff --git a/options/license/Aspell-RU b/options/license/Aspell-RU deleted file mode 100644 index 5cec17629b..0000000000 --- a/options/license/Aspell-RU +++ /dev/null @@ -1,4 +0,0 @@ -Permission to use, copy, redistribute is granted. -Permission to redistribute modifications in patch form is granted. -Permission to redistribute binaries made of modified sources is granted. -All other rights reserved. diff --git a/options/license/BSD-2-Clause-pkgconf-disclaimer b/options/license/BSD-2-Clause-pkgconf-disclaimer deleted file mode 100644 index 4c3b8ae80e..0000000000 --- a/options/license/BSD-2-Clause-pkgconf-disclaimer +++ /dev/null @@ -1,15 +0,0 @@ -Copyright © 2001-2025 Audacious developers and others - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions, and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions, and the following disclaimer in the - documentation provided with the distribution. - -This software is provided “as is” and without any warranty, express or -implied. In no event shall the authors be liable for any damages arising -from the use of this software. diff --git a/options/license/BSD-3-Clause-Tso b/options/license/BSD-3-Clause-Tso deleted file mode 100644 index 3f70dabf39..0000000000 --- a/options/license/BSD-3-Clause-Tso +++ /dev/null @@ -1,11 +0,0 @@ -Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, and the entire permission notice in its entirety, including the disclaimer of warranties. - -2. 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. - -3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 NOT ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/options/license/BSD-Mark-Modifications b/options/license/BSD-Mark-Modifications deleted file mode 100644 index 995bcb1eb9..0000000000 --- a/options/license/BSD-Mark-Modifications +++ /dev/null @@ -1,35 +0,0 @@ -License text: Copyright 1993, Geoff Kuenning, Granada Hills, CA -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -2. 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. - -3. All modifications to the source code must be clearly marked as -such. Binary redistributions based on modified source code -must be clearly marked as modified versions in the documentation -and/or other materials provided with the distribution. -(clause 4 removed with permission from Geoff Kuenning) - -4. The name of Geoff Kuenning may not be used to endorse or promote -products derived from this software without specific prior -written permission. - -THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING 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 GEOFF KUENNING 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. diff --git a/options/license/Boehm-GC-without-fee b/options/license/Boehm-GC-without-fee deleted file mode 100644 index 354d47017e..0000000000 --- a/options/license/Boehm-GC-without-fee +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (c) 2000 -SWsoft company - -Modifications copyright (c) 2001, 2013. Oracle and/or its affiliates. -All rights reserved. - -This material is provided "as is", with absolutely no warranty expressed -or implied. Any use is at your own risk. - -Permission to use or copy this software for any purpose is hereby granted -without fee, provided the above notices are retained on all copies. -Permission to modify the code and to distribute modified code is granted, -provided the above notices are retained, and a notice that the code was -modified is included with the above copyright notice. diff --git a/options/license/Buddy b/options/license/Buddy deleted file mode 100644 index fe750b2e32..0000000000 --- a/options/license/Buddy +++ /dev/null @@ -1,26 +0,0 @@ - Copyright (C) 1996-2002 by Jorn Lind-Nielsen - All rights reserved - -Permission is hereby granted, without written agreement and without -license or royalty fees, to use, reproduce, prepare derivative -works, distribute, and display this software and its documentation -for any purpose, provided that (1) the above copyright notice and -the following two paragraphs appear in all copies of the source code -and (2) redistributions, including without limitation binaries, -reproduce these notices in the supporting documentation. Substantial -modifications to this software may be copyrighted by their authors -and need not follow the licensing terms described here, provided -that the new terms are clearly indicated in all files where they apply. - -IN NO EVENT SHALL JORN LIND-NIELSEN, OR DISTRIBUTORS OF THIS -SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, -INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS -SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE -ABOVE PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -JORN LIND-NIELSEN SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO -OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR -MODIFICATIONS. diff --git a/options/license/CAPEC-tou b/options/license/CAPEC-tou deleted file mode 100644 index fe6ddd466e..0000000000 --- a/options/license/CAPEC-tou +++ /dev/null @@ -1,6 +0,0 @@ -LICENSE -The MITRE Corporation (MITRE) hereby grants you a non-exclusive, royalty-free license to use Common Attack Pattern Enumeration and Classification (CAPEC™) for research, development, and commercial purposes. Any copy you make for such purposes is authorized provided that you reproduce MITRE’s copyright designation and this license in any such copy. - -DISCLAIMERS - -ALL DOCUMENTS AND THE INFORMATION CONTAINED THEREIN ARE PROVIDED ON AN "AS IS" BASIS AND THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE MITRE CORPORATION, ITS BOARD OF TRUSTEES, OFFICERS, AGENTS, AND EMPLOYEES, DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION THEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. diff --git a/options/license/CC-BY-ND-2.5 b/options/license/CC-BY-ND-2.5 index 69e476834d..2f6b29acfa 100644 --- a/options/license/CC-BY-ND-2.5 +++ b/options/license/CC-BY-ND-2.5 @@ -1,4 +1,4 @@ -Creative Commons Attribution-NoDerivs 2.5 Generic +Creative Commons Attribution-NoDerivs 2.5 CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. diff --git a/options/license/CC-PDM-1.0 b/options/license/CC-PDM-1.0 deleted file mode 100644 index 1dc4e63b87..0000000000 --- a/options/license/CC-PDM-1.0 +++ /dev/null @@ -1,27 +0,0 @@ -No Copyright - -This work has been identified as being free of known restrictions under -copyright law, including all related and neighboring rights. - - -You can copy, modify, distribute and perform the work, even for commercial -purposes, all without asking permission. See Other Information below. - -Other Information - -The work may not be free of known copyright restrictions in all jurisdictions . - -Persons may have other rights in or related to the work, such as patent or -trademark rights, and others may have rights in how the work is used, such as -publicity or privacy rights. - -In some jurisdictions moral rights of the author may persist beyond the term of -copyright. These rights may include the right to be identified as the author -and the right to object to derogatory treatments. - -Unless expressly stated otherwise, the person who identified the work makes no -warranties about the work, and disclaims liability for all uses of the work, to -the fullest extent permitted by applicable law. - -When using or citing the work, you should not imply endorsement by the author -or the person who identified the work. diff --git a/options/license/CC-SA-1.0 b/options/license/CC-SA-1.0 deleted file mode 100644 index 1a810feaec..0000000000 --- a/options/license/CC-SA-1.0 +++ /dev/null @@ -1,198 +0,0 @@ - Creative Commons Legal Code - - ShareAlike 1.0 - -CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL -SERVICES. DISTRIBUTION OF THIS DRAFT LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT -RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. -CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND -DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. - -License - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE -BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS -CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND -CONDITIONS. - -1. Definitions - - a. "Collective Work" means a work, such as a periodical issue, anthology or - encyclopedia, in which the Work in its entirety in unmodified form, along - with a number of other contributions, constituting separate and independent - works in themselves, are assembled into a collective whole. A work that - constitutes a Collective Work will not be considered a Derivative Work (as - defined below) for the purposes of this License. - b. "Derivative Work" means a work based upon the Work or upon the Work and - other pre-existing works, such as a translation, musical arrangement, - dramatization, fictionalization, motion picture version, sound recording, - art reproduction, abridgment, condensation, or any other form in which the - Work may be recast, transformed, or adapted, except that a work that - constitutes a Collective Work will not be considered a Derivative Work for - the purpose of this License. - c. "Licensor" means the individual or entity that offers the Work under the - terms of this License. - d. "Original Author" means the individual or entity who created the Work. - e. "Work" means the copyrightable work of authorship offered under the terms - of this License. - f. "You" means an individual or entity exercising rights under this License - who has not previously violated the terms of this License with respect to - the Work, or who has received express permission from the Licensor to - exercise rights under this License despite a previous violation. - -2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or -restrict any rights arising from fair use, first sale or other limitations on -the exclusive rights of the copyright owner under copyright law or other -applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, Licensor -hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the -duration of the applicable copyright) license to exercise the rights in the -Work as stated below: - - a. to reproduce the Work, to incorporate the Work into one or more Collective - Works, and to reproduce the Work as incorporated in the Collective Works; - b. to create and reproduce Derivative Works; - c. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio transmission the - Work including as incorporated in Collective Works; - d. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio transmission - Derivative Works; - -The above rights may be exercised in all media and formats whether now known or -hereafter devised. The above rights include the right to make such -modifications as are technically necessary to exercise the rights in other -media and formats. All rights not expressly granted by Licensor are hereby -reserved. - -4. Restrictions. The license granted in Section 3 above is expressly made -subject to and limited by the following restrictions: - - a. You may distribute, publicly display, publicly perform, or publicly - digitally perform the Work only under the terms of this License, and You - must include a copy of, or the Uniform Resource Identifier for, this - License with every copy or phonorecord of the Work You distribute, publicly - display, publicly perform, or publicly digitally perform. You may not offer - or impose any terms on the Work that alter or restrict the terms of this - License or the recipients' exercise of the rights granted hereunder. You - may not sublicense the Work. You must keep intact all notices that refer to - this License and to the disclaimer of warranties. You may not distribute, - publicly display, publicly perform, or publicly digitally perform the Work - with any technological measures that control access or use of the Work in a - manner inconsistent with the terms of this License Agreement. The above - applies to the Work as incorporated in a Collective Work, but this does not - require the Collective Work apart from the Work itself to be made subject - to the terms of this License. If You create a Collective Work, upon notice - from any Licensor You must, to the extent practicable, remove from the - Collective Work any reference to such Licensor or the Original Author, as - requested. If You create a Derivative Work, upon notice from any Licensor - You must, to the extent practicable, remove from the Derivative Work any - reference to such Licensor or the Original Author, as requested. - b. You may distribute, publicly display, publicly perform, or publicly - digitally perform a Derivative Work only under the terms of this License, - and You must include a copy of, or the Uniform Resource Identifier for, - this License with every copy or phonorecord of each Derivative Work You - distribute, publicly display, publicly perform, or publicly digitally - perform. You may not offer or impose any terms on the Derivative Works that - alter or restrict the terms of this License or the recipients' exercise of - the rights granted hereunder, and You must keep intact all notices that - refer to this License and to the disclaimer of warranties. You may not - distribute, publicly display, publicly perform, or publicly digitally - perform the Derivative Work with any technological measures that control - access or use of the Work in a manner inconsistent with the terms of this - License Agreement. The above applies to the Derivative Work as incorporated - in a Collective Work, but this does not require the Collective Work apart - from the Derivative Work itself to be made subject to the terms of this - License. - -5. Representations, Warranties and Disclaimer - - a. By offering the Work for public release under this License, Licensor - represents and warrants that, to the best of Licensor's knowledge after - reasonable inquiry: - i. Licensor has secured all rights in the Work necessary to grant the - license rights hereunder and to permit the lawful exercise of the - rights granted hereunder without You having any obligation to pay any - royalties, compulsory license fees, residuals or any other payments; - ii. The Work does not infringe the copyright, trademark, publicity rights, - common law rights or any other right of any third party or constitute - defamation, invasion of privacy or other tortious injury to any third - party. - b. EXCEPT AS EXPRESSLY STATED IN THIS LICENSE OR OTHERWISE AGREED IN WRITING - OR REQUIRED BY APPLICABLE LAW, THE WORK IS LICENSED ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, - WITHOUT LIMITATION, ANY WARRANTIES REGARDING THE CONTENTS OR ACCURACY OF - THE WORK. - -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, -AND EXCEPT FOR DAMAGES ARISING FROM LIABILITY TO A THIRD PARTY RESULTING FROM -BREACH OF THE WARRANTIES IN SECTION 5, IN NO EVENT WILL LICENSOR BE LIABLE TO -YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR -EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF -LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. Termination - - a. This License and the rights granted hereunder will terminate automatically - upon any breach by You of the terms of this License. Individuals or - entities who have received Derivative Works or Collective Works from You - under this License, however, will not have their licenses terminated - provided such individuals or entities remain in full compliance with those - licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of - this License. - b. Subject to the above terms and conditions, the license granted here is - perpetual (for the duration of the applicable copyright in the Work). - Notwithstanding the above, Licensor reserves the right to release the Work - under different license terms or to stop distributing the Work at any time; - provided, however that any such election will not serve to withdraw this - License (or any other license that has been, or is required to be, granted - under the terms of this License), and this License will continue in full - force and effect unless terminated as stated above. - -8. Miscellaneous - - a. Each time You distribute or publicly digitally perform the Work or a - Collective Work, the Licensor offers to the recipient a license to the Work - on the same terms and conditions as the license granted to You under this - License. - b. Each time You distribute or publicly digitally perform a Derivative Work, - Licensor offers to the recipient a license to the original Work on the same - terms and conditions as the license granted to You under this License. - c. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of the - remainder of the terms of this License, and without further action by the - parties to this agreement, such provision shall be reformed to the minimum - extent necessary to make such provision valid and enforceable. - d. No term or provision of this License shall be deemed waived and no breach - consented to unless such waiver or consent shall be in writing and signed - by the party to be charged with such waiver or consent. - e. This License constitutes the entire agreement between the parties with - respect to the Work licensed here. There are no understandings, agreements - or representations with respect to the Work not specified here. Licensor - shall not be bound by any additional provisions that may appear in any - communication from You. This License may not be modified without the mutual - written agreement of the Licensor and You. - -Creative Commons is not a party to this License, and makes no warranty -whatsoever in connection with the Work. Creative Commons will not be liable to -You or any party on any legal theory for any damages whatsoever, including -without limitation any general, special, incidental or consequential damages -arising in connection to this license. Notwithstanding the foregoing two (2) -sentences, if Creative Commons has expressly identified itself as the Licensor -hereunder, it shall have all rights and obligations of Licensor. - -Except for the limited purpose of indicating to the public that the Work is -licensed under the CCPL, neither party will use the trademark "Creative -Commons" or any related trademark or logo of Creative Commons without the prior -written consent of Creative Commons. Any permitted use will be in compliance -with Creative Commons' then-current trademark usage guidelines, as may be -published on its website or otherwise made available upon request from time to -time. - -Creative Commons may be contacted at http://creativecommons.org/. diff --git a/options/license/CGAL-linking-exception b/options/license/CGAL-linking-exception deleted file mode 100644 index c6dbd55ca6..0000000000 --- a/options/license/CGAL-linking-exception +++ /dev/null @@ -1,4 +0,0 @@ -As a special exception, you have permission to link this library -with the CGAL library (http://www.cgal.org) and distribute executables, -as long as you follow the requirements of the GNU GPL in regard to -all of the software in the executable aside from CGAL. diff --git a/options/license/Classpath-exception-2.0-short b/options/license/Classpath-exception-2.0-short deleted file mode 100644 index 2e7df40484..0000000000 --- a/options/license/Classpath-exception-2.0-short +++ /dev/null @@ -1,11 +0,0 @@ -As a special exception, the copyright holders of this library give -you permission to link this library with independent modules to -produce an executable, regardless of the license terms of these -independent modules, and to copy and distribute the resulting -executable under terms of your choice, provided that you also -meet, for each linked independent module, the terms and conditions -of the license of that module. An independent module is a module -which is not derived from or based on this library. If you modify -this library, you may extend this exception to your version of -the library, but you are not obligated to do so. If you do not -wish to do so, delete this exception statement from your version. diff --git a/options/license/CryptoSwift b/options/license/CryptoSwift deleted file mode 100644 index 71603206c5..0000000000 --- a/options/license/CryptoSwift +++ /dev/null @@ -1,11 +0,0 @@ -Copyright (C) 2014-3099 Marcin Krzyżanowski -This software is provided 'as-is', without any express or implied warranty. - -In no event will the authors be held liable for any damages arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: - -- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. -- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -- This notice may not be removed or altered from any source or binary distribution. -- Redistributions of any form whatsoever must retain the following acknowledgment: 'This product includes software developed by the "Marcin Krzyzanowski" (http://krzyzanowskim.com/).' diff --git a/options/license/Digia-Qt-LGPL-exception-1.1 b/options/license/Digia-Qt-LGPL-exception-1.1 deleted file mode 100644 index 66cddd38aa..0000000000 --- a/options/license/Digia-Qt-LGPL-exception-1.1 +++ /dev/null @@ -1,9 +0,0 @@ -Digia Qt LGPL Exception version 1.1 - -As a special exception to the GNU Lesser General Public License version 2.1, -the object code form of a "work that uses the Library" may incorporate material -from a header file that is part of the Library. You may distribute such object -code under terms of your choice, provided that the incorporated material (i) -does not exceed more than 5% of the total size of the Library; and (ii) is -limited to numerical parameters, data structure layouts, accessors, macros, -inline functions and templates. diff --git a/options/license/DocBook-DTD b/options/license/DocBook-DTD deleted file mode 100644 index a110d23e6f..0000000000 --- a/options/license/DocBook-DTD +++ /dev/null @@ -1,24 +0,0 @@ -Copyright 1992-2002 HaL Computer Systems, Inc., -O'Reilly & Associates, Inc., ArborText, Inc., Fujitsu Software -Corporation, Norman Walsh, Sun Microsystems, Inc., and the -Organization for the Advancement of Structured Information -Standards (OASIS). - -$Id: sdbcent.mod,v 1.13 2005/04/01 21:02:17 nwalsh Exp $ - -Permission to use, copy, modify and distribute the DocBook XML DTD -and its accompanying documentation for any purpose and without fee -is hereby granted in perpetuity, provided that the above copyright -notice and this paragraph appear in all copies. The copyright -holders make no representation about the suitability of the DTD for -any purpose. It is provided "as is" without expressed or implied -warranty. - -If you modify the Simplified DocBook DTD in any way, except for -declaring and referencing additional sets of general entities and -declaring additional notations, label your DTD as a variant of -DocBook. See the maintenance documentation for more information. - -Please direct all questions, bug reports, or suggestions for -changes to the docbook@lists.oasis-open.org mailing list. For more -information, see http://www.oasis-open.org/docbook/. diff --git a/options/license/DocBook-Schema b/options/license/DocBook-Schema deleted file mode 100644 index 56203a0878..0000000000 --- a/options/license/DocBook-Schema +++ /dev/null @@ -1,22 +0,0 @@ -Copyright 1992-2011 HaL Computer Systems, Inc., -O'Reilly & Associates, Inc., ArborText, Inc., Fujitsu Software -Corporation, Norman Walsh, Sun Microsystems, Inc., and the -Organization for the Advancement of Structured Information -Standards (OASIS). - -Permission to use, copy, modify and distribute the DocBook schema -and its accompanying documentation for any purpose and without fee -is hereby granted in perpetuity, provided that the above copyright -notice and this paragraph appear in all copies. The copyright -holders make no representation about the suitability of the schema -for any purpose. It is provided "as is" without expressed or implied -warranty. - -If you modify the DocBook schema in any way, label your schema as a -variant of DocBook. See the reference documentation -(http://docbook.org/tdg5/en/html/ch05.html#s-notdocbook) -for more information. - -Please direct all questions, bug reports, or suggestions for changes -to the docbook@lists.oasis-open.org mailing list. For more -information, see http://www.oasis-open.org/docbook/. diff --git a/options/license/DocBook-XML b/options/license/DocBook-XML deleted file mode 100644 index 9553feee6b..0000000000 --- a/options/license/DocBook-XML +++ /dev/null @@ -1,48 +0,0 @@ -Copyright ---------- -Copyright (C) 1999-2007 Norman Walsh -Copyright (C) 2003 Jiří Kosek -Copyright (C) 2004-2007 Steve Ball -Copyright (C) 2005-2014 The DocBook Project -Copyright (C) 2011-2012 O'Reilly Media - -Permission 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: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -Except as contained in this notice, the names of individuals -credited with contribution to this software shall not be used in -advertising or otherwise to promote the sale, use or other -dealings in this Software without prior written authorization -from the individuals in question. - -Any stylesheet derived from this Software that is publically -distributed will be identified with a different name and the -version strings in any derived Software will be changed so that -no possibility of confusion between the derived package and this -Software will exist. - -Warranty --------- -THE 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 NORMAN WALSH OR ANY OTHER -CONTRIBUTOR 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. - -Contacting the Author ---------------------- -The DocBook XSL stylesheets are maintained by Norman Walsh, -, and members of the DocBook Project, - diff --git a/options/license/ESA-PL-permissive-2.4 b/options/license/ESA-PL-permissive-2.4 deleted file mode 100644 index 5e25adc372..0000000000 --- a/options/license/ESA-PL-permissive-2.4 +++ /dev/null @@ -1,240 +0,0 @@ -European Space Agency Public License (ESA-PL) Permissive (Type 3) – v2.4 - - - -1 Definitions - - - -1.1 “Contributor” means (a) the individual or legal entity that originally creates or later modifies the Software and (b) each subsequent individual or legal entity that creates or contributes to the creation of Modifications. - - - -1.2 “Contributor Version” means the version of the Software on which the Contributor based its Modifications. - - - -1.3 “Distribution” and “Distribute” means any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, physically or electronically or by any other means, copies of the Software or Modifications. - - - -1.4 “ESA” means the European Space Agency. - - - -1.5 “License” means this document. - - - -1.6 “Licensor” means the individual or legal entity that Distributes the Software under the License to You. - - - -1.7 “Modification” means any work or software created that is based upon or derived from the Software (or portions thereof) or a modification of the Software (or portions thereof). For the avoidance of doubt, linking a library to the Software results in a Modification. - - - -1.8 “Object Code” means any non-Source Code form of the Software and/or Modifications. - - - -1.9 “Patent Claims” (of a Contributor) means any patent claim(s), owned at the time of the Distribution or subsequently acquired, including without limitation, method, process and apparatus claims, in any patent licensable by a Contributor which would be infringed by making use of the rights granted under Sec. 2.1, including but not limited to make, have made, use, sell, offer for sale or import of the Contributor Version and/or such Contributor’s Modifications (if any), either alone or in combination with the Contributor Version. “Licensable” means having the right to grant, whether at the time of the Distribution or subsequently acquired, the rights conveyed herein. - - - -1.10 “Software” means the software Distributed under this License by the Licensor, in Source Code and/or Object Code form. - - - -1.11 “Source Code” means the preferred, usually human readable form of the Software and/or Modifications in which modifications are made and the associated documentation included in or with such code. - - - -1.12 “You” means an individual or legal entity exercising rights under this License (the licensee). - - - -2 Grant of Rights - - - -2.1 Copyright - - - -The Licensor, and each Contributor in respect of such Contributor’s Modifications, hereby grants You a world-wide, royalty-free, non-exclusive license under Copyright, subject to the terms and conditions of this License, to: - -· use the Software; - -· reproduce the Software by any or all means and in any or all form; - -· Modify the Software and create works based on the Software; - -· communicate to the public, including making available, display or perform the Software or copies thereof to the public; - -· Distribute, sublicense, lend and rent the Software. - - - -The license grant is perpetual and irrevocable, unless terminated pursuant to Sec. 8. - - - -2.2 Patents - - - -Each Contributor in respect of such Contributor’s Modifications, hereby grants You a world-wide, royalty-free, non-exclusive, sub-licensable license under Patent Claims to the extent necessary to make use of the rights granted under Sec. 2.1, including but not limited to make, have made, use, sell, offer for sale, import, export and Distribute such Contributor’s Modifications and the combination of such Contributor’s Modifications with the Contributor Version (collectively called the “Patent Licensed Version” of the Software). - - - -No patent license is granted for claims that are infringed: - -· only as a consequence of further modification of the Patent Licensed Version; or - -· by the combination of the Patent Licensed Version with other software or other devices or hardware, unless such combination was an intended use case of the Patent Licensed Version (e.g. a general purpose library is intended to be used with other software, a satellite navigation software is intended to be used with appropriate hardware); or - -· by a Modification under Patent Claims in the absence of the Contributor’s Modifications or by a combination of the Contributor’s Modifications with software other than the Patent Licensed Version or Modifications thereof. - - - -2.3 Trademark - - - -This License does not grant permission to use trade names, trademarks, services marks, logos or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Software and as reasonable necessary to comply with the obligations of this License (e.g. by reproducing the content of the notices). For the avoidance of doubt, upon Distribution of Modifications You must not use the Licensor’s or ESA’s trademarks, names or logos in any way that states or implies, or can be interpreted as stating or implying, that the final product is endorsed or created by the Licensor or ESA. - - - -3 Distribution - - - -3.1 No Copyleft - - - -You may Distribute the Software and/or Modifications, as Source Code or Object Code, under any license terms, provided that - -(a) notice is given of the use of the Software and the applicability of this License to the Software; and - -(b) You make best efforts to ensure that further Distribution of the Software and/or Modifications (including further Modifications) is subject to the obligations set forth in this Sec. 3.1 (a) and (b). - - - -4 Notices - - - -The following obligations apply in the event of any Distribution of the Software and/or Modifications as Source Code and/or Object Code: - - - -4.1 You must include a copy of this License and all of the notices set out in this Sec. 4. - - - -4.2 You may not remove or alter any copyright, patent, trademark and attribution notices nor any of the notices set out in this Sec. 4, except as necessary for your compliance with this License or otherwise permitted by this License, except for those notices that do not pertain to the Modifications You Distribute. - - - -4.3 Each Contributor must cause its Modification carrying prominent notices stating that the Software has been modified and the date of modification and identify itself as the originator of its Modifications in a manner that reasonably allows identification and contact with the Contributor. The aforementioned notices must at a minimum be in a text file included with the Distribution titled “CHANGELOG”. - - - -4.4 The Software may include a "NOTICE" text file containing general notices. Any Contributor can create such a NOTICE file or add notices to it, alongside or as an addendum to the original text, provided that such notices cannot be construed as modifying the License. - - - -4.5 Each Contributor must identify all of its Patent Claims by providing at a minimum the patent number and identification and contact information in a text file included with the Distribution titled "LEGAL". - - - -5 Warranty and Liability - - - -5.1 Each Contributor warrants and represents that it has sufficient rights to grant the rights to its Modifications conveyed by this License. - - - -5.2 Except as expressly set forth in this License, the Software is provided to You on an “as is” basis and without warranties of any kind, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy or non-infringement of intellectual property rights. Mandatory statutory warranty claims, e.g. in the event of wilful deception or fraudulent misrepresentation, shall remain unaffected. - - - -5.3 Except as expressly set forth in this License, neither Licensor nor any Contributor shall be liable, including, without limitation, for direct, indirect, incidental, or consequential damages (including without limitation loss of profit), however caused and on any theory of liability, arising in any way out of the use or Distribution of the Software or the exercise of any rights under this License, even if You have been advised of the possibility of such damages. Mandatory statutory liability claims, e.g. in the event of wilful misconduct, wilful deception or fraudulent misrepresentation, shall remain unaffected. - - - -6 Additional Agreements - - - -While Distributing the Software or Modifications, You may choose to conclude additional agreements, for free or for charge, regarding for example support, warranty, indemnity, liability or liability obligations and/or rights, provided such additional agreements are consistent with this License and do not effectively restrict the recipient’s rights under this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor or Licensor, and only if You agree to indemnify, defend, and hold each Contributor or Licensor harmless for any liability incurred by, or claims asserted against, such Contributor or Licensor by reason of your accepting any such warranty or additional liability. - - - -7 Infringements - - - -You acknowledge that continuing to use the Software knowing that such use infringes third party rights (e.g. after receiving a third party notification of infringement) would expose you to the risk of being considered as intentionally infringing third party rights. In such event You should acquire the respective rights or modify the Software so that the Modification is non-infringing. - - - -8 Termination - - - -8.1 This License and the rights granted hereunder will terminate automatically upon any breach by You with the terms of this License if you fail to cure such breach within 30 days of becoming aware of the breach. - - - -8.2 If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Software constitutes direct or contributory patent infringement, then any patent and copyright licenses granted to You under this License for the Software shall terminate as of the date such litigation is filed. - - - -8.3 Any licenses validly granted by You under the License prior to termination shall continue and survive termination. - - - -9 Applicable Law, Arbitration and Compliance - - - -9.1 This License is governed by the laws of the ESA Member State where the Licensor resides or has his registered office. “Member States” are the members of the European Space Agency pursuant to Art. 1 of the ESA Convention[1]. This licence shall be governed by German law if a dispute arises with the ESA as a Licensor or if the Licensor has no residence or registered office inside a Member State. - - - -9.2 Any dispute arising out of this License shall be finally settled in accordance with the Rules of Arbitration of the International Chamber of Commerce by one or more arbitrators designated in conformity with those rules. Arbitration proceedings shall take place in Cologne, Germany. The award shall be final and binding on the parties, no appeal shall lie against it. The enforcement of the award shall be governed by the rules of procedure in force in the state/country in which it is to be executed. - - - -9.3 For the avoidance of doubt, You are solely responsible for compliance with current applicable requirements of national laws. The Software can be subject to export control laws. If You export the Software it is your responsibility to comply with all export control laws. This may include different requirements, as e.g. registering the Software with the local authorities. - - - -9.4 If it is impossible for You to comply with any of the terms of this License due to statute, judicial order or regulation You must: - -(a) comply with the terms of this License to the maximum extent possible; and - -(b) describe the limitations and the Object Code and/or Source Code they affect. Such description must be included in the LEGAL notice described in Section 4. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for an average recipient to be able to understand it. - - - -10 Miscellaneous - - - -10.1 Only ESA has the right to modify or publish new versions of this License. ESA may assign this right to other individuals or legal entities. Each version will be given a distinguishing version number. - - - -10.2 This License represents the complete agreement concerning subject matter hereof. - - - -10.3 If any provision of this License is held invalid or unenforceable, the remaining provisions of this License shall not be affected. The invalid or unenforceable provision shall be construed and/or reformed to the extent necessary to make it enforceable and valid. - - -[1] As of January 2020 the Member States are Austria, Belgium, Czech Republic, Denmark, Estonia, Finland, France, Germany, Greece, Hungary, Ireland, Italy, Luxembourg, The Netherlands, Norway, Poland, Portugal, Romania, Slovenia, Spain, Sweden, Switzerland and the United Kingdom. diff --git a/options/license/ESA-PL-strong-copyleft-2.4 b/options/license/ESA-PL-strong-copyleft-2.4 deleted file mode 100644 index a007898657..0000000000 --- a/options/license/ESA-PL-strong-copyleft-2.4 +++ /dev/null @@ -1,200 +0,0 @@ -European Space Agency Public License (ESA-PL) Strong Copyleft (Type 1) – v2.4 - - - -1 Definitions - - - -1.1 “Contributor” means (a) the individual or legal entity that originally creates or later modifies the Software and (b) each subsequent individual or legal entity that creates or contributes to the creation of Modifications. - -1.2 “Contributor Version” means the version of the Software on which the Contributor based its Modifications. - -1.3 “Distribution” and “Distribute” means any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, physically or electronically or by any other means, copies of the Software or Modifications. - -1.4 “ESA” means the European Space Agency. - -1.5 “License” means this document. - -1.6 “Licensor” means the individual or legal entity that Distributes the Software under the License to You. - -1.7 “Modification” means any work or software created that is based upon or derived from the Software (or portions thereof) or a modification of the Software (or portions thereof). For the avoidance of doubt, linking a library to the Software results in a Modification. - -1.8 “Object Code” means any non-Source Code form of the Software and/or Modifications. - -1.9 “Patent Claims” (of a Contributor) means any patent claim(s), owned at the time of the Distribution or subsequently acquired, including without limitation, method, process and apparatus claims, in any patent licensable by a Contributor which would be infringed by making use of the rights granted under Sec. 2.1, including but not limited to make, have made, use, sell, offer for sale or import of the Contributor Version and/or such Contributor’s Modifications (if any), either alone or in combination with the Contributor Version. “Licensable” means having the right to grant, whether at the time of the Distribution or subsequently acquired, the rights conveyed herein. - -1.10 “Software” means the software Distributed under this License by the Licensor, in Source Code and/or Object Code form. - -1.11 “Source Code” means the preferred, usually human readable form of the Software and/or Modifications in which modifications are made and the associated documentation included in or with such code. - -1.12 “You” means an individual or legal entity exercising rights under this License (the licensee). - - - -2 Grant of Rights - - - -2.1 Copyright - -The Licensor, and each Contributor in respect of such Contributor’s Modifications, hereby grants You a world-wide, royalty-free, non-exclusive license under Copyright, subject to the terms and conditions of this License, to: - -- use the Software; -- reproduce the Software by any or all means and in any or all form; -- Modify the Software and create works based on the Software; -- communicate to the public, including making available, display or perform the Software or copies thereof to the public; -- Distribute, sublicense, lend and rent the Software. - - -The license grant is perpetual and irrevocable, unless terminated pursuant to Sec. 8. - - - -2.2 Patents - -Each Contributor in respect of such Contributor’s Modifications, hereby grants You a world-wide, royalty-free, non-exclusive, sub-licensable license under Patent Claims to the extent necessary to make use of the rights granted under Sec. 2.1, including but not limited to make, have made, use, sell, offer for sale, import, export and Distribute such Contributor’s Modifications and the combination of such Contributor’s Modifications with the Contributor Version (collectively called the “Patent Licensed Version” of the Software). - - - -No patent license is granted for claims that are infringed: - -- only as a consequence of further modification of the Patent Licensed Version; or -- by the combination of the Patent Licensed Version with other software or other devices or hardware, unless such combination was an intended use case of the Patent Licensed Version (e.g. a general purpose library is intended to be used with other software, a satellite navigation software is intended to be used with appropriate hardware); or -- by a Modification under Patent Claims in the absence of the Contributor’s Modifications or by a combination of the Contributor’s Modifications with software other than the Patent Licensed Version or Modifications thereof. - - -2.3 Trademark - -This License does not grant permission to use trade names, trademarks, services marks, logos or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Software and as reasonable necessary to comply with the obligations of this License (e.g. by reproducing the content of the notices). For the avoidance of doubt, upon Distribution of Modifications You must not use the Licensor’s or ESA’s trademarks, names or logos in any way that states or implies, or can be interpreted as stating or implying, that the final product is endorsed or created by the Licensor or ESA. - - - -3 Distribution - - - -3.1 Copyleft Clause - -All Distribution of the Software and/or Modifications, as Source Code or Object Code, must be, as a whole, either under (a) the terms of this License or (b) any later version of this License unless the Software is expressly Distributed only under a specific version of the License by a Contributor. - - - -3.2 Copyleft exceptions - -3.2.1 Compilations. In the event of the Distribution of a compilation of Software and/or Modifications with other separate and independent works (for example in or on a volume of a storage or distribution medium), which are not by their nature extensions or other modifications of the Software and/or the Modifications, and which are not combined with it such as to form a larger program, Distribution of the compilation does not cause this License to apply to the other parts of the compilation. - -3.2.2 System Libraries. System Libraries used by a Modification need not be Distributed under the terms of this License and need not be included as part of the Source Code pursuant to Sec. 3.3. “System Library” means anything that is normally distributed (in either source or binary form) with the major components (kernel, window system etc.) of the operating system(s) on which the Software or Modification runs, or a compiler used to produce the Object Code, or an object code interpreter used to run it. - -3.2.3 External Modules. You may create a Modification by combining Software with an external module enabling supplementary functions or services and Distribute the external module under different license terms, provided that the external module and the Software run in separate address spaces, with one calling the other, or each other interfacing, when they are run. - - - -3.3 Communication of the Source Code - -If You Distribute the Software and/or Modifications as Object Code, You must: - -provide in addition a copy of the Source Code of the Software and/or Modifications to each recipient; or -make the Source Code of the Software and/or Modifications freely accessible by reasonable means for anyone who possesses the Object Code or received the Software and/or Modifications from You, and inform recipients how to obtain a copy of the Source Code. Such information needs to be included at a minimum in the “NOTICE” file pursuant to Sec. 4.4 You are obliged to make the Source Code accessible in accordance with this Section for as long as You continue to Distribute the Software and/or Modifications and at a minimum for a three year period following Your last Distribution of the Software and/or Modifications. - - -3.4 Service Provision - -If You provide access to the Software and/or Modifications or make its functionality available by any means or use it to provide services for any individual or legal entity other than You, e.g. by provision of software-as-a-service, You are obliged to make the Source Code of the Software and/or Modifications freely accessible by reasonable means to those individuals or legal entities and provide information on how to obtain a copy of the Source Code. You are obliged to make the Source Code accessible in accordance with this Section for as long as You continue to provide access to the Software and/or Modifications. - - - -3.5 Dual Licensing - -This License gives no permission to license the Software or Modifications in any other way, but it does not invalidate such permission if You have separately received it. - - - -4 Notices - -The following obligations apply in the event of any Distribution of the Software and/or Modifications as Source Code and/or Object Code: - - - -4.1 You must include a copy of this License and all of the notices set out in this Sec. 4. - -4.2 You may not remove or alter any copyright, patent, trademark and attribution notices nor any of the notices set out in this Sec. 4, except as necessary for your compliance with this License or otherwise permitted by this License, except for those notices that do not pertain to the Modifications You Distribute. - -4.3 Each Contributor must cause its Modification carrying prominent notices stating that the Software has been modified and the date of modification and identify itself as the originator of its Modifications in a manner that reasonably allows identification and contact with the Contributor. The aforementioned notices must at a minimum be in a text file included with the Distribution titled “CHANGELOG”. - -4.4 The Software may include a "NOTICE" text file containing general notices. Any Contributor can create such a NOTICE file or add notices to it, alongside or as an addendum to the original text, provided that such notices cannot be construed as modifying the License. - -4.5 Each Contributor must identify all of its Patent Claims by providing at a minimum the patent number and identification and contact information in a text file included with the Distribution titled "LEGAL". - - - -5 Warranty and Liability - - - -5.1 Each Contributor warrants and represents that it has sufficient rights to grant the rights to its Modifications conveyed by this License. - -5.2 Except as expressly set forth in this License, the Software is provided to You on an “as is” basis and without warranties of any kind, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy or non-infringement of intellectual property rights. Mandatory statutory warranty claims, e.g. in the event of wilful deception or fraudulent misrepresentation, shall remain unaffected. - -5.3 Except as expressly set forth in this License, neither Licensor nor any Contributor shall be liable, including, without limitation, for direct, indirect, incidental, or consequential damages (including without limitation loss of profit), however caused and on any theory of liability, arising in any way out of the use or Distribution of the Software or the exercise of any rights under this License, even if You have been advised of the possibility of such damages. Mandatory statutory liability claims, e.g. in the event of wilful misconduct, wilful deception or fraudulent misrepresentation, shall remain unaffected. - - - -6 Additional Agreements - - - -While Distributing the Software or Modifications, You may choose to conclude additional agreements, for free or for charge, regarding for example support, warranty, indemnity, liability or liability obligations and/or rights, provided such additional agreements are consistent with this License and do not effectively restrict the recipient’s rights under this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor or Licensor, and only if You agree to indemnify, defend, and hold each Contributor or Licensor harmless for any liability incurred by, or claims asserted against, such Contributor or Licensor by reason of your accepting any such warranty or additional liability. - - - -7 Infringements - - - -7.1 You acknowledge that continuing to use the Software knowing that such use infringes third party rights (e.g. after receiving a third party notification of infringement) would expose you to the risk of being considered as intentionally infringing third party rights. In such event You should acquire the respective rights or modify the Software so that the Modification is non-infringing. - - - -8 Termination - - - -8.1 This License and the rights granted hereunder will terminate automatically upon any breach by You with the terms of this License if you fail to cure such breach within 30 days of becoming aware of the breach. - -8.2 If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Software constitutes direct or contributory patent infringement, then any patent and copyright licenses granted to You under this License for the Software shall terminate as of the date such litigation is filed. - -8.3 Any licenses validly granted by You under the License prior to termination shall continue and survive termination. - - - -9 Applicable Law, Arbitration and Compliance - - - -9.1 This License is governed by the laws of the ESA Member State where the Licensor resides or has his registered office. “Member States” are the members of the European Space Agency pursuant to Art. 1 of the ESA Convention[1]. This licence shall be governed by German law if a dispute arises with the ESA as a Licensor or if the Licensor has no residence or registered office inside a Member State. - -9.2 Any dispute arising out of this License shall be finally settled in accordance with the Rules of Arbitration of the International Chamber of Commerce by one or more arbitrators designated in conformity with those rules. Arbitration proceedings shall take place in Cologne, Germany. The award shall be final and binding on the parties, no appeal shall lie against it. The enforcement of the award shall be governed by the rules of procedure in force in the state/country in which it is to be executed. - -9.3 For the avoidance of doubt, You are solely responsible for compliance with current applicable requirements of national laws. The Software can be subject to export control laws. If You export the Software it is your responsibility to comply with all export control laws. This may include different requirements, as e.g. registering the Software with the local authorities. - - - -9.4 If it is impossible for You to comply with any of the terms of this License due to statute, judicial order or regulation You must: - -comply with the terms of this License to the maximum extent possible; and -describe the limitations and the Object Code and/or Source Code they affect. Such description must be included in the LEGAL notice described in Section 4. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for an average recipient to be able to understand it. - - -10 Miscellaneous - - - -10.1 Only ESA has the right to modify or publish new versions of this License. ESA may assign this right to other individuals or legal entities. Each version will be given a distinguishing version number. - -10.2 This License represents the complete agreement concerning subject matter hereof. - -10.3 If any provision of this License is held invalid or unenforceable, the remaining provisions of this License shall not be affected. The invalid or unenforceable provision shall be construed and/or reformed to the extent necessary to make it enforceable and valid. - - -[1] As of June 2025 the Member States are Austria, Belgium, Czech Republic, Denmark, Estonia, Finland, France, Germany, Greece, Hungary, Ireland, Italy, Luxembourg, The Netherlands, Norway, Poland, Portugal, Romania, Slovenia, Spain, Sweden, Switzerland and the United Kingdom. diff --git a/options/license/ESA-PL-weak-copyleft-2.4 b/options/license/ESA-PL-weak-copyleft-2.4 deleted file mode 100644 index 7c27dc2506..0000000000 --- a/options/license/ESA-PL-weak-copyleft-2.4 +++ /dev/null @@ -1,193 +0,0 @@ -European Space Agency Public License (ESA-PL) Weak Copyleft (Type 2) – v2.4 - - - -1 Definitions - - - -1.1 “Contributor” means (a) the individual or legal entity that originally creates or later modifies the Software and (b) each subsequent individual or legal entity that creates or contributes to the creation of Modifications. - -1.2 “Contributor Version” means the version of the Software on which the Contributor based its Modifications. - -1.3 “Distribution” and “Distribute” means any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, physically or electronically or by any other means, copies of the Software or Modifications. - -1.4 “ESA” means the European Space Agency. - -1.5 “License” means this document. - -1.6 “Licensor” means the individual or legal entity that Distributes the Software under the License to You. - -1.7 “Modification” means any work or software created that is based upon or derived from the Software (or portions thereof) or a modification of the Software (or portions thereof). For the avoidance of doubt, linking a library to the Software results in a Modification. - -1.8 “Object Code” means any non-Source Code form of the Software and/or Modifications. - -1.9 “Patent Claims” (of a Contributor) means any patent claim(s), owned at the time of the Distribution or subsequently acquired, including without limitation, method, process and apparatus claims, in any patent licensable by a Contributor which would be infringed by making use of the rights granted under Sec. 2.1, including but not limited to make, have made, use, sell, offer for sale or import of the Contributor Version and/or such Contributor’s Modifications (if any), either alone or in combination with the Contributor Version. “Licensable” means having the right to grant, whether at the time of the Distribution or subsequently acquired, the rights conveyed herein. - -1.10 “Software” means the software Distributed under this License by the Licensor, in Source Code and/or Object Code form. - -1.11 “Source Code” means the preferred, usually human readable form of the Software and/or Modifications in which modifications are made and the associated documentation included in or with such code. - -1.12 “You” means an individual or legal entity exercising rights under this License (the licensee). - - - -2 Grant of Rights - - - -2.1 Copyright - -The Licensor, and each Contributor in respect of such Contributor’s Modifications, hereby grants You a world-wide, royalty-free, non-exclusive license under Copyright, subject to the terms and conditions of this License, to: - -use the Software; -reproduce the Software by any or all means and in any or all form; -Modify the Software and create works based on the Software; -communicate to the public, including making available, display or perform the Software or copies thereof to the public; -Distribute, sublicense, lend and rent the Software. -The license grant is perpetual and irrevocable, unless terminated pursuant to Sec. 8. - - - -2.2 Patents - -Each Contributor in respect of such Contributor’s Modifications, hereby grants You a world-wide, royalty-free, non-exclusive, sub-licensable license under Patent Claims to the extent necessary to make use of the rights granted under Sec. 2.1, including but not limited to make, have made, use, sell, offer for sale, import, export and Distribute such Contributor’s Modifications and the combination of such Contributor’s Modifications with the Contributor Version (collectively called the “Patent Licensed Version” of the Software). - -No patent license is granted for claims that are infringed: - -only as a consequence of further modification of the Patent Licensed Version; or -by the combination of the Patent Licensed Version with other software or other devices or hardware, unless such combination was an intended use case of the Patent Licensed Version (e.g. a general purpose library is intended to be used with other software, a satellite navigation software is intended to be used with appropriate hardware); or -by a Modification under Patent Claims in the absence of the Contributor’s Modifications or by a combination of the Contributor’s Modifications with software other than the Patent Licensed Version or Modifications thereof. - - -2.3 Trademark - -This License does not grant permission to use trade names, trademarks, services marks, logos or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Software and as reasonable necessary to comply with the obligations of this License (e.g. by reproducing the content of the notices). For the avoidance of doubt, upon Distribution of Modifications You must not use the Licensor’s or ESA’s trademarks, names or logos in any way that states or implies, or can be interpreted as stating or implying, that the final product is endorsed or created by the Licensor or ESA. - - - -3 Distribution - - - -3.1 Copyleft Clause - -All Distribution of the Software and/or Modifications, as Source Code or Object Code, must be, as a whole, either under (a) the terms of this License or the ESA-PL Strong Copyleft license v2.4 or (b) any later version of these Licenses unless the Software is expressly Distributed only under a specific version of the License by a Contributor or (c) the terms of a compatible license as listed in Appendix A to this License. Any obligation in this License to Distribute under the terms of this License, in particular as set out in Sec. 3.2, shall be construed as referring to “this License or a compatible license”. - -3.2 Copyleft exceptions - -3.2.1 Compilations. In the event of the Distribution of a compilation of Software and/or Modifications with other separate and independent works (for example in or on a volume of a storage or distribution medium), which are not by their nature extensions or other modifications of the Software and/or the Modifications, and which are not combined with it such as to form a single larger program, Distribution of the compilation does not cause this License to apply to the other parts of the compilation. - -3.2.2 System Libraries. System Libraries used by a Modification need not be Distributed under the terms of this License and need not be included as part of the Source Code pursuant to Sec. 3.3. “System Library” means anything that is normally distributed (in either source or binary form) with the major components (kernel, window system etc.) of the operating system(s) on which the Software or Modification runs, or a compiler used to produce the Object Code, or an object code interpreter used to run it. - -3.2.3 External Modules. You may create a Modification by combining Software with an external module enabling supplementary functions or services and Distribute the external module under different license terms, provided that the external module and the Software run in separate address spaces, with one calling the other, or each other interfacing, when they are run. - -3.2.4 Combinations. You may create a Modification (the “Combination”) by combining or linking the Software or Modifications thereof (the “Covered Code”) with additional code or software (the “External Code”) not governed by the terms of this License and Distribute the Combination - -in Object Code form under any license terms, and/or -in Source Code form with the External Code’s Source Code under any license terms and the Covered Code’s Source Code under this License, -provided that: -the Covered Code will be governed by this License and the different license terms effectively do not restrict the rights granted by this License; and -the External Code and its license terms are clearly identified and notice is given of the use of Covered Code and the applicability of this License; and -the External Code’s Source Code is clearly separated from the Covered Code’s Source Code (usually contained in different files); and -You communicate the Covered Code’s Source Code in accordance with Sec. 3.3. - - -3.3 Communication of the Source Code - -If You Distribute the Software and/or Modifications as Object Code, You must: - -provide in addition a copy of the Source Code of the Software and/or Modifications to each recipient; or -make the Source Code of the Software and/or Modifications freely accessible by reasonable means for anyone who possesses the Object Code or received the Software and/or Modifications from You, and inform recipients how to obtain a copy of the Source Code. Such information needs to be included at a minimum in the “NOTICE” file pursuant to Sec. 4.4 You are obliged to make the Source Code accessible in accordance with this Section for as long as You continue to Distribute the Software and/or Modifications and at a minimum for a three year period following Your last Distribution of the Software and/or Modifications. - - -3.4 Dual Licensing - -This License gives no permission to license the Software or Modifications in any other way, but it does not invalidate such permission if You have separately received it. - - - -4 Notices - -The following obligations apply in the event of any Distribution of the Software and/or Modifications as Source Code and/or Object Code: - -4.1 You must include a copy of this License and all of the notices set out in this Sec. 4. - -4.2 You may not remove or alter any copyright, patent, trademark and attribution notices nor any of the notices set out in this Sec. 4, except as necessary for your compliance with this License or otherwise permitted by this License, except for those notices that do not pertain to the Modifications You Distribute. - -4.3 Each Contributor must cause its Modification carrying prominent notices stating that the Software has been modified and the date of modification and identify itself as the originator of its Modifications in a manner that reasonably allows identification and contact with the Contributor. The aforementioned notices must at a minimum be in a text file included with the Distribution titled “CHANGELOG”. - -4.4 The Software may include a "NOTICE" text file containing general notices. Any Contributor can create such a NOTICE file or add notices to it, alongside or as an addendum to the original text, provided that such notices cannot be construed as modifying the License. - -4.5 Each Contributor must identify all of its Patent Claims by providing at a minimum the patent number and identification and contact information in a text file included with the Distribution titled "LEGAL". - - - -5 Warranty and Liability - -5.1 Each Contributor warrants and represents that it has sufficient rights to grant the rights to its Modifications conveyed by this License. - -5.2 Except as expressly set forth in this License, the Software is provided to You on an “as is” basis and without warranties of any kind, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy or non-infringement of intellectual property rights. Mandatory statutory warranty claims, e.g. in the event of wilful deception or fraudulent misrepresentation, shall remain unaffected. - -5.3 Except as expressly set forth in this License, neither Licensor nor any Contributor shall be liable, including, without limitation, for direct, indirect, incidental, or consequential damages (including without limitation loss of profit), however caused and on any theory of liability, arising in any way out of the use or Distribution of the Software or the exercise of any rights under this License, even if You have been advised of the possibility of such damages. Mandatory statutory liability claims, e.g. in the event of wilful misconduct, wilful deception or fraudulent misrepresentation, shall remain unaffected. - - - -6 Additional Agreements - -While Distributing the Software or Modifications, You may choose to conclude additional agreements, for free or for charge, regarding for example support, warranty, indemnity, liability or liability obligations and/or rights, provided such additional agreements are consistent with this License and do not effectively restrict the recipient’s rights under this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor or Licensor, and only if You agree to indemnify, defend, and hold each Contributor or Licensor harmless for any liability incurred by, or claims asserted against, such Contributor or Licensor by reason of your accepting any such warranty or additional liability. - - - -7 Infringements - -7.1 You acknowledge that continuing to use the Software knowing that such use infringes third party rights (e.g. after receiving a third party notification of infringement) would expose you to the risk of being considered as intentionally infringing third party rights. In such event You should acquire the respective rights or modify the Software so that the Modification is non-infringing. - - - -8 Termination - -8.1 This License and the rights granted hereunder will terminate automatically upon any breach by You with the terms of this License if you fail to cure such breach within 30 days of becoming aware of the breach. - -8.2 If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Software constitutes direct or contributory patent infringement, then any patent and copyright licenses granted to You under this License for the Software shall terminate as of the date such litigation is filed. - -8.3 Any licenses validly granted by You under the License prior to termination shall continue and survive termination. - - - -9 Applicable Law, Arbitration and Compliance - -9.1 This License is governed by the laws of the ESA Member State where the Licensor resides or has his registered office. “Member States” are the members of the European Space Agency pursuant to Art. 1 of the ESA Convention[1]. This licence shall be governed by German law if a dispute arises with the ESA as a Licensor or if the Licensor has no residence or registered office inside a Member State. - -9.2 Any dispute arising out of this License shall be finally settled in accordance with the Rules of Arbitration of the International Chamber of Commerce by one or more arbitrators designated in conformity with those rules. Arbitration proceedings shall take place in Cologne, Germany. The award shall be final and binding on the parties, no appeal shall lie against it. The enforcement of the award shall be governed by the rules of procedure in force in the state/country in which it is to be executed. - -9.3 For the avoidance of doubt, You are solely responsible for compliance with current applicable requirements of national laws. The Software can be subject to export control laws. If You export the Software it is your responsibility to comply with all export control laws. This may include different requirements, as e.g. registering the Software with the local authorities. - -9.4 If it is impossible for You to comply with any of the terms of this License due to statute, judicial order or regulation You must: - -comply with the terms of this License to the maximum extent possible; and -describe the limitations and the Object Code and/or Source Code they affect. Such description must be included in the LEGAL notice described in Section 4. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for an average recipient to be able to understand it. - - -10 Miscellaneous - -10.1 Only ESA has the right to modify or publish new versions of this License. ESA may assign this right to other individuals or legal entities. Each version will be given a distinguishing version number. - -10.2 This License represents the complete agreement concerning subject matter hereof. - -10.3 If any provision of this License is held invalid or unenforceable, the remaining provisions of this License shall not be affected. The invalid or unenforceable provision shall be construed and/or reformed to the extent necessary to make it enforceable and valid. - - - -Appendix A – List of compatible licenses - - - -Compatible licenses are: - -GNU General Public License (GPL) version 2 and any subsequent version -CeCILL version 2 and any subsequent version - - - -[1] As of January 2020 the Member States are Austria, Belgium, Czech Republic, Denmark, Estonia, Finland, France, Germany, Greece, Hungary, Ireland, Italy, Luxembourg, The Netherlands, Norway, Poland, Portugal, Romania, Spain, Sweden, Switzerland and the United Kingdom. diff --git a/options/license/Elastic-2.0 b/options/license/Elastic-2.0 index 9496955678..809108b857 100644 --- a/options/license/Elastic-2.0 +++ b/options/license/Elastic-2.0 @@ -2,18 +2,18 @@ Elastic License 2.0 URL: https://www.elastic.co/licensing/elastic-license -Acceptance +## Acceptance By using the software, you agree to all of the terms and conditions below. -Copyright License +## Copyright License The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations and conditions below. -Limitations +## Limitations You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of @@ -27,7 +27,7 @@ You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law. -Patents +## Patents The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for @@ -40,7 +40,7 @@ the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company. -Notices +## Notices You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms. @@ -53,7 +53,7 @@ software prominent notices stating that you have modified the software. These terms do not imply any licenses other than those expressly granted in these terms. -Termination +## Termination If you use the software in violation of these terms, such use is not licensed, and your licenses will automatically terminate. If the licensor provides you @@ -63,31 +63,31 @@ reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your licenses to terminate automatically and permanently. -No Liability +## No Liability -As far as the law allows, the software comes as is, without any warranty or +*As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of -legal claim. +legal claim.* -Definitions +## Definitions -The licensor is the entity offering these terms, and the software is the +The **licensor** is the entity offering these terms, and the **software** is the software the licensor makes available under these terms, including any portion of it. -you refers to the individual or entity agreeing to these terms. +**you** refers to the individual or entity agreeing to these terms. -your company is any legal entity, sole proprietorship, or other kind of +**your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that -organization. control means ownership of substantially all the assets of an +organization. **control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect. -your licenses are all the licenses granted to you for the software under +**your licenses** are all the licenses granted to you for the software under these terms. -use means anything you do with the software requiring one of your licenses. +**use** means anything you do with the software requiring one of your licenses. -trademark means trademarks, service marks, and similar rights. +**trademark** means trademarks, service marks, and similar rights. diff --git a/options/license/FSFULLRSD b/options/license/FSFULLRSD deleted file mode 100644 index abc104563a..0000000000 --- a/options/license/FSFULLRSD +++ /dev/null @@ -1,4 +0,0 @@ -This file is free software; the Free Software Foundation -gives unlimited permission to copy and/or distribute it, -with or without modifications, as long as this notice is preserved. -This file is offered as-is, without any warranty. diff --git a/options/license/FSL-1.1-ALv2 b/options/license/FSL-1.1-ALv2 deleted file mode 100644 index 386880ecd6..0000000000 --- a/options/license/FSL-1.1-ALv2 +++ /dev/null @@ -1,105 +0,0 @@ -# Functional Source License, Version 1.1, ALv2 Future License - -## Abbreviation - -FSL-1.1-ALv2 - -## Notice - -Copyright ${year} ${licensor name} - -## Terms and Conditions - -### Licensor ("We") - -The party offering the Software under these Terms and Conditions. - -### The Software - -The "Software" is each version of the software that we make available under -these Terms and Conditions, as indicated by our inclusion of these Terms and -Conditions with the Software. - -### License Grant - -Subject to your compliance with this License Grant and the Patents, -Redistribution and Trademark clauses below, we hereby grant you the right to -use, copy, modify, create derivative works, publicly perform, publicly display -and redistribute the Software for any Permitted Purpose identified below. - -### Permitted Purpose - -A Permitted Purpose is any purpose other than a Competing Use. A Competing Use -means making the Software available to others in a commercial product or -service that: - -1. substitutes for the Software; - -2. substitutes for any other product or service we offer using the Software - that exists as of the date we make the Software available; or - -3. offers the same or substantially similar functionality as the Software. - -Permitted Purposes specifically include using the Software: - -1. for your internal use and access; - -2. for non-commercial education; - -3. for non-commercial research; and - -4. in connection with professional services that you provide to a licensee - using the Software in accordance with these Terms and Conditions. - -### Patents - -To the extent your use for a Permitted Purpose would necessarily infringe our -patents, the license grant above includes a license under our patents. If you -make a claim against any party that the Software infringes or contributes to -the infringement of any patent, then your patent license to the Software ends -immediately. - -### Redistribution - -The Terms and Conditions apply to all copies, modifications and derivatives of -the Software. - -If you redistribute any copies, modifications or derivatives of the Software, -you must include a copy of or a link to these Terms and Conditions and not -remove any copyright notices provided in or with the Software. - -### Disclaimer - -THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR -PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT. - -IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE -SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, -EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE. - -### Trademarks - -Except for displaying the License Details and identifying us as the origin of -the Software, you have no right under these Terms and Conditions to use our -trademarks, trade names, service marks or product names. - -## Grant of Future License - -We hereby irrevocably grant you an additional license to use the Software under -the Apache License, Version 2.0 that is effective on the second anniversary of -the date we make the Software available. On or after that date, you may use the -Software under the Apache License, Version 2.0, in which case the following -will apply: - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. - -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. diff --git a/options/license/FSL-1.1-MIT b/options/license/FSL-1.1-MIT deleted file mode 100644 index 571aa38073..0000000000 --- a/options/license/FSL-1.1-MIT +++ /dev/null @@ -1,110 +0,0 @@ -# Functional Source License, Version 1.1, MIT Future License - -## Abbreviation - -FSL-1.1-MIT - -## Notice - -Copyright ${year} ${licensor name} - -## Terms and Conditions - -### Licensor ("We") - -The party offering the Software under these Terms and Conditions. - -### The Software - -The "Software" is each version of the software that we make available under -these Terms and Conditions, as indicated by our inclusion of these Terms and -Conditions with the Software. - -### License Grant - -Subject to your compliance with this License Grant and the Patents, -Redistribution and Trademark clauses below, we hereby grant you the right to -use, copy, modify, create derivative works, publicly perform, publicly display -and redistribute the Software for any Permitted Purpose identified below. - -### Permitted Purpose - -A Permitted Purpose is any purpose other than a Competing Use. A Competing Use -means making the Software available to others in a commercial product or -service that: - -1. substitutes for the Software; - -2. substitutes for any other product or service we offer using the Software - that exists as of the date we make the Software available; or - -3. offers the same or substantially similar functionality as the Software. - -Permitted Purposes specifically include using the Software: - -1. for your internal use and access; - -2. for non-commercial education; - -3. for non-commercial research; and - -4. in connection with professional services that you provide to a licensee - using the Software in accordance with these Terms and Conditions. - -### Patents - -To the extent your use for a Permitted Purpose would necessarily infringe our -patents, the license grant above includes a license under our patents. If you -make a claim against any party that the Software infringes or contributes to -the infringement of any patent, then your patent license to the Software ends -immediately. - -### Redistribution - -The Terms and Conditions apply to all copies, modifications and derivatives of -the Software. - -If you redistribute any copies, modifications or derivatives of the Software, -you must include a copy of or a link to these Terms and Conditions and not -remove any copyright notices provided in or with the Software. - -### Disclaimer - -THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR -PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT. - -IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE -SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, -EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE. - -### Trademarks - -Except for displaying the License Details and identifying us as the origin of -the Software, you have no right under these Terms and Conditions to use our -trademarks, trade names, service marks or product names. - -## Grant of Future License - -We hereby irrevocably grant you an additional license to use the Software under -the MIT license that is effective on the second anniversary of the date we make -the Software available. On or after that date, you may use the Software under -the MIT license, in which case the following will apply: - -Permission 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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE 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. diff --git a/options/license/Game-Programming-Gems b/options/license/Game-Programming-Gems deleted file mode 100644 index 25549ebb38..0000000000 --- a/options/license/Game-Programming-Gems +++ /dev/null @@ -1,8 +0,0 @@ -Original version Copyright (C) Scott Bilas, 2000. -All rights reserved worldwide. - -This software is provided "as is" without express or implied -warranties. You may freely copy and compile this source into -applications you distribute provided that the copyright text -below is included in the resulting source code, for example: -"Portions Copyright (C) Scott Bilas, 2000" diff --git a/options/license/HDF5 b/options/license/HDF5 deleted file mode 100644 index b4cb77559c..0000000000 --- a/options/license/HDF5 +++ /dev/null @@ -1,96 +0,0 @@ -Copyright Notice and License Terms for -HDF5 (Hierarchical Data Format 5) Software Library and Utilities ------------------------------------------------------------------------------ - -HDF5 (Hierarchical Data Format 5) Software Library and Utilities -Copyright 2006 by The HDF Group. - -NCSA HDF5 (Hierarchical Data Format 5) Software Library and Utilities -Copyright 1998-2006 by The Board of Trustees of the University of Illinois. - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted for any purpose (including commercial purposes) -provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions, and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions, and the following disclaimer in the documentation - and/or materials provided with the distribution. - -3. Neither the name of The HDF Group, the name of the University, nor the - name of any Contributor may be used to endorse or promote products derived - from this software without specific prior written permission from - The HDF Group, the University, or the Contributor, respectively. - -DISCLAIMER: -THIS SOFTWARE IS PROVIDED BY THE HDF GROUP AND THE CONTRIBUTORS -"AS IS" WITH NO WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED. IN NO -EVENT SHALL THE HDF GROUP OR THE CONTRIBUTORS BE LIABLE FOR ANY DAMAGES -SUFFERED BY THE USERS ARISING OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -You are under no obligation whatsoever to provide any bug fixes, patches, or -upgrades to the features, functionality or performance of the source code -("Enhancements") to anyone; however, if you choose to make your Enhancements -available either publicly, or directly to The HDF Group, without imposing a -separate written license agreement for such Enhancements, then you hereby -grant the following license: a non-exclusive, royalty-free perpetual license -to install, use, modify, prepare derivative works, incorporate into other -computer software, distribute, and sublicense such enhancements or derivative -works thereof, in binary and source code form. - ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ - -Contributors: National Center for Supercomputing Applications (NCSA) at -the University of Illinois, Fortner Software, Unidata Program Center -(netCDF), The Independent JPEG Group (JPEG), Jean-loup Gailly and Mark Adler -(gzip), and Digital Equipment Corporation (DEC). - ------------------------------------------------------------------------------ - -Portions of HDF5 were developed with support from the Lawrence Berkeley -National Laboratory (LBNL) and the United States Department of Energy -under Prime Contract No. DE-AC02-05CH11231. - ------------------------------------------------------------------------------ - -Portions of HDF5 were developed with support from Lawrence Livermore -National Laboratory and the United States Department of Energy under -Prime Contract No. DE-AC52-07NA27344. - ------------------------------------------------------------------------------ - -Portions of HDF5 were developed with support from the University of -California, Lawrence Livermore National Laboratory (UC LLNL). -The following statement applies to those portions of the product and must -be retained in any redistribution of source code, binaries, documentation, -and/or accompanying materials: - - This work was partially produced at the University of California, - Lawrence Livermore National Laboratory (UC LLNL) under contract - no. W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy - (DOE) and The Regents of the University of California (University) - for the operation of UC LLNL. - - DISCLAIMER: - THIS WORK WAS PREPARED AS AN ACCOUNT OF WORK SPONSORED BY AN AGENCY OF - THE UNITED STATES GOVERNMENT. NEITHER THE UNITED STATES GOVERNMENT NOR - THE UNIVERSITY OF CALIFORNIA NOR ANY OF THEIR EMPLOYEES, MAKES ANY - WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY OR RESPONSIBILITY - FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, - APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE - WOULD NOT INFRINGE PRIVATELY- OWNED RIGHTS. REFERENCE HEREIN TO ANY - SPECIFIC COMMERCIAL PRODUCTS, PROCESS, OR SERVICE BY TRADE NAME, - TRADEMARK, MANUFACTURER, OR OTHERWISE, DOES NOT NECESSARILY CONSTITUTE - OR IMPLY ITS ENDORSEMENT, RECOMMENDATION, OR FAVORING BY THE UNITED - STATES GOVERNMENT OR THE UNIVERSITY OF CALIFORNIA. THE VIEWS AND - OPINIONS OF AUTHORS EXPRESSED HEREIN DO NOT NECESSARILY STATE OR REFLECT - THOSE OF THE UNITED STATES GOVERNMENT OR THE UNIVERSITY OF CALIFORNIA, - AND SHALL NOT BE USED FOR ADVERTISING OR PRODUCT ENDORSEMENT PURPOSES. - ------------------------------------------------------------------------------ diff --git a/options/license/HIDAPI b/options/license/HIDAPI deleted file mode 100644 index e0b5d70c04..0000000000 --- a/options/license/HIDAPI +++ /dev/null @@ -1,2 +0,0 @@ -This software may be used by anyone for any reason so long -as the copyright notice in the source files remains intact. diff --git a/options/license/HPND-Netrek b/options/license/HPND-Netrek deleted file mode 100644 index 5c3cb650f4..0000000000 --- a/options/license/HPND-Netrek +++ /dev/null @@ -1,10 +0,0 @@ -Copyright (C) 1995 S. M. Patel (smpatel@wam.umd.edu) - -Permission to use, copy, modify, and distribute this -software and its documentation for any purpose and without -fee is hereby granted, provided that the above copyright -notice appear in all copies and that both that copyright -notice and this permission notice appear in supporting -documentation. No representations are made about the -suitability of this software for any purpose. It is -provided "as is" without express or implied warranty. diff --git a/options/license/HPND-SMC b/options/license/HPND-SMC deleted file mode 100644 index 79c1e948e4..0000000000 --- a/options/license/HPND-SMC +++ /dev/null @@ -1,15 +0,0 @@ -Copyright 2000, Mojam Media, Inc., all rights reserved. Author: Skip Montanaro - -Copyright 1999, Bioreason, Inc., all rights reserved. Author: Andrew Dalke - -Copyright 1995-1997, Automatrix, Inc., all rights reserved. Author: Skip Montanaro - -Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved. - -Permission to use, copy, modify, and distribute this Python software and its -associated documentation for any purpose without fee is hereby granted, -provided that the above copyright notice appears in all copies, and that both -that copyright notice and this permission notice appear in supporting -documentation, and that the name of neither Automatrix, Bioreason or Mojam -Media be used in advertising or publicity pertaining to distribution of the -software without specific, written prior permission. diff --git a/options/license/HPND-sell-variant-critical-systems b/options/license/HPND-sell-variant-critical-systems deleted file mode 100644 index b71219d4c2..0000000000 --- a/options/license/HPND-sell-variant-critical-systems +++ /dev/null @@ -1,20 +0,0 @@ -Alan Cox -Permission to use, copy, modify, distribute, and sell this software and its -documentation for any purpose is hereby granted without fee, provided that -the above copyright notice appear in all copies and that both that -copyright notice and this permission notice appear in supporting -documentation, and that the names of Red Hat, Alan Cox and Henrik Harmsen -not be used in advertising or publicity pertaining to distribution of the -software without specific, written prior permission. Th authors make no -representations about the suitability of this software for any purpose. -It is provided "as is" without express or implied warranty. -THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO -EVENT SHALL RICHARD HECKER BE LIABLE FOR ANY SPECIAL, 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. - -THIS SOFTWARE IS NOT DESIGNED FOR USE IN SAFETY CRITICAL SYSTEMS OF -ANY KIND OR FORM. diff --git a/options/license/ISO-permission b/options/license/ISO-permission deleted file mode 100644 index fc0ddeab3b..0000000000 --- a/options/license/ISO-permission +++ /dev/null @@ -1,4 +0,0 @@ -(C) International Organization for Standardization 1986 -Permission to copy in any form is granted for use with -conforming SGML systems and applications as defined -in ISO 8879, provided this notice is included in all copies. diff --git a/options/license/Independent-modules-exception b/options/license/Independent-modules-exception deleted file mode 100644 index 8f66dba6ab..0000000000 --- a/options/license/Independent-modules-exception +++ /dev/null @@ -1,18 +0,0 @@ -This is the file COPYING.FPC, it applies to the Free Pascal Run-Time Library -(RTL) and packages (packages) distributed by members of the Free Pascal -Development Team. - -The source code of the Free Pascal Runtime Libraries and packages are -distributed under the Library GNU General Public License -(see the file COPYING) with the following modification: - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent modules, -and to copy and distribute the resulting executable under terms of your choice, -provided that you also meet, for each linked independent module, the terms -and conditions of the license of that module. An independent module is a module -which is not derived from or based on this library. If you modify this -library, you may extend this exception to your version of the library, but you are -not obligated to do so. If you do not wish to do so, delete this exception -statement from your version. diff --git a/options/license/InnoSetup b/options/license/InnoSetup deleted file mode 100644 index 337584e6d1..0000000000 --- a/options/license/InnoSetup +++ /dev/null @@ -1,27 +0,0 @@ -Inno Setup License -================== - -Except where otherwise noted, all of the documentation and software included in the Inno Setup -package is copyrighted by Jordan Russell. - -Copyright (C) 1997-2024 Jordan Russell. All rights reserved. -Portions Copyright (C) 2000-2024 Martijn Laan. All rights reserved. - -This software is provided "as-is," without any express or implied warranty. In no event shall the -author be held liable for any damages arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, including commercial -applications, and to alter and redistribute it, provided that the following conditions are met: - -1. All redistributions of source code files must retain all copyright notices that are currently in - place, and this list of conditions without modification. - -2. All redistributions in binary form must retain all occurrences of the above copyright notice and - web site addresses that are currently in place (for example, in the About boxes). - -3. The origin of this software must not be misrepresented; you must not claim that you wrote the - original software. If you use this software to distribute a product, an acknowledgment in the - product documentation would be appreciated but is not required. - -4. Modified versions in source or binary form must be plainly marked as such, and must not be - misrepresented as being the original software. diff --git a/options/license/MIPS b/options/license/MIPS deleted file mode 100644 index cf57a05639..0000000000 --- a/options/license/MIPS +++ /dev/null @@ -1,4 +0,0 @@ -Copyright (c) 1992, 1991, 1990 MIPS Computer Systems, Inc. -MIPS Computer Systems, Inc. grants reproduction and use -rights to all parties, PROVIDED that this comment is -maintained in the copy. diff --git a/options/license/MIT b/options/license/MIT index d817195dad..2071b23b0e 100644 --- a/options/license/MIT +++ b/options/license/MIT @@ -2,17 +2,8 @@ MIT License Copyright (c) -Permission 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: +Permission 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: -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE 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. +THE 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. diff --git a/options/license/MIT-STK b/options/license/MIT-STK deleted file mode 100644 index 40397a37b1..0000000000 --- a/options/license/MIT-STK +++ /dev/null @@ -1,27 +0,0 @@ -The Synthesis ToolKit in C++ (STK) - -Copyright (c) 1995-2023 Perry R. Cook and Gary P. Scavone - -Permission 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: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -Any person wishing to distribute modifications to the Software is -asked to send the modifications to the original developer so that they -can be incorporated into the canonical version. This is, however, not -a binding provision of this license. - -THE 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. diff --git a/options/license/MIT-open-group b/options/license/MIT-open-group index 18ee4889bd..ff185d30ed 100644 --- a/options/license/MIT-open-group +++ b/options/license/MIT-open-group @@ -12,10 +12,10 @@ in all copies or substantial portions of the Software. THE 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 OPEN GROUP 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. +IN NO EVENT SHALL 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. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use diff --git a/options/license/MMPL-1.0.1 b/options/license/MMPL-1.0.1 deleted file mode 100644 index 6556291ea2..0000000000 --- a/options/license/MMPL-1.0.1 +++ /dev/null @@ -1,87 +0,0 @@ -Minecraft Mod Public License -============================ - -Version 1.0.1 - -0. Definitions --------------- - -Minecraft: Denotes a copy of the Minecraft game licensed by Mojang AB - -User: Anybody that interacts with the software in one of the following ways: - - play - - decompile - - recompile or compile - - modify - - distribute - -Mod: The mod code designated by the present license, in source form, binary -form, as obtained standalone, as part of a wider distribution or resulting from -the compilation of the original or modified sources. - -Dependency: Code required for the mod to work properly. This includes -dependencies required to compile the code as well as any file or modification -that is explicitely or implicitely required for the mod to be working. - -1. Scope --------- - -The present license is granted to any user of the mod. As a prerequisite, -a user must own a legally acquired copy of Minecraft - -2. Liability ------------- - -This mod is provided 'as is' with no warranties, implied or otherwise. The owner -of this mod takes no responsibility for any damages incurred from the use of -this mod. This mod alters fundamental parts of the Minecraft game, parts of -Minecraft may not work with this mod installed. All damages caused from the use -or misuse of this mad fall on the user. - -3. Play rights --------------- - -The user is allowed to install this mod on a client or a server and to play -without restriction. - -4. Modification rights ----------------------- - -The user has the right to decompile the source code, look at either the -decompiled version or the original source code, and to modify it. - -5. Derivation rights --------------------- - -The user has the rights to derive code from this mod, that is to say to -write code that extends or instanciate the mod classes or interfaces, refer to -its objects, or calls its functions. This code is known as "derived" code, and -can be licensed under a license different from this mod. - -6. Distribution of original or modified copy rights ---------------------------------------------------- - -Is subject to distribution rights this entire mod in its various forms. This -include: - - original binary or source forms of this mod files - - modified versions of these binaries or source files, as well as binaries - resulting from source modifications - - patch to its source or binary files - - any copy of a portion of its binary source files - -The user is allowed to redistribute this mod partially, in totality, or -included in a distribution. - -When distributing binary files, the user must provide means to obtain its -entire set of sources or modified sources at no costs. - -All distributions of this mod must remain licensed under the MMPL. - -All dependencies that this mod have on other mods or classes must be licensed -under conditions comparable to this version of MMPL, with the exception of the -Minecraft code and the mod loading framework (e.g. ModLoader, ModLoaderMP or -Bukkit). - -Modified version of binaries and sources, as well as files containing sections -copied from this mod, should be distributed under the terms of the present -license. diff --git a/options/license/Motosoto b/options/license/Motosoto index a25cff026e..4add8c6a39 100644 --- a/options/license/Motosoto +++ b/options/license/Motosoto @@ -1,372 +1,110 @@ MOTOSOTO OPEN SOURCE LICENSE - Version 0.9.1 -This Motosoto Open Source License (the "License") applies to "Community Portal Server" and related -software products as well as any updatesor maintenance releases of that software ("Motosoto -Products") that are distributed by Motosoto.Com B.V. ("Licensor"). Any Motosoto Product licensed -pursuant to this License is a "Licensed Product." Licensed Product, in its entirety, is protected -by Dutch copyright law. This License identifies the terms under which you may use, copy, distribute -or modify Licensed Product and has been submitted to the Open Software Initiative (OSI) for -approval. +This Motosoto Open Source License (the "License") applies to "Community Portal Server" and related software products as well as any updatesor maintenance releases of that software ("Motosoto Products") that are distributed by Motosoto.Com B.V. ("Licensor"). Any Motosoto Product licensed pursuant to this License is a "Licensed Product." Licensed Product, in its entirety, is protected by Dutch copyright law. This License identifies the terms under which you may use, copy, distribute or modify Licensed Product and has been submitted to the Open Software Initiative (OSI) for approval. Preamble -This Preamble is intended to describe, in plain English, the nature and scope of this License. -However, this Preamble is not a part of this license. The legal effect of this License is dependent -only upon the terms of the License and not this Preamble. This License complies with the Open -Source Definition and has been approved by Open Source Initiative. Software distributed under this -License may be marked as "OSI Certified Open Source Software." +This Preamble is intended to describe, in plain English, the nature and scope of this License. However, this Preamble is not a part of this license. The legal effect of this License is dependent only upon the terms of the License and not this Preamble. This License complies with the Open Source Definition and has been approved by Open Source Initiative. Software distributed under this License may be marked as "OSI Certified Open Source Software." This License provides that: -1. You may use, sell or give away the Licensed Product, alone or as a component of an aggregate -software distribution containing programs from several different sources. No royalty or other fee -is required. +1. You may use, sell or give away the Licensed Product, alone or as a component of an aggregate software distribution containing programs from several different sources. No royalty or other fee is required. -2. Both Source Code and executable versions of the Licensed Product, including Modifications made -by previous Contributors, are available for your use. (The terms "Licensed Product," -"Modifications," "Contributors" and "Source Code" are defined in the License.) +2. Both Source Code and executable versions of the Licensed Product, including Modifications made by previous Contributors, are available for your use. (The terms "Licensed Product," "Modifications," "Contributors" and "Source Code" are defined in the License.) -3. You are allowed to make Modifications to the Licensed Product, and you can create Derivative -Works from it. (The term "Derivative Works" is defined in the License.) +3. You are allowed to make Modifications to the Licensed Product, and you can create Derivative Works from it. (The term "Derivative Works" is defined in the License.) -4. By accepting the Licensed Product under the provisions of this License, you agree that any -Modifications you make to the Licensed Product and then distribute are governed by the provisions -of this License. In particular, you must make the Source Code of your Modifications available to -others. +4. By accepting the Licensed Product under the provisions of this License, you agree that any Modifications you make to the Licensed Product and then distribute are governed by the provisions of this License. In particular, you must make the Source Code of your Modifications available to others. -5. You may use the Licensed Product for any purpose, but the Licensor is not providing you any -warranty whatsoever, nor is the Licensor accepting any liability in the event that the Licensed -Product doesn't work properly or causes you any injury or damages. +5. You may use the Licensed Product for any purpose, but the Licensor is not providing you any warranty whatsoever, nor is the Licensor accepting any liability in the event that the Licensed Product doesn't work properly or causes you any injury or damages. -6. If you sublicense the Licensed Product or Derivative Works, you may charge fees for warranty or -support, or for accepting indemnity or liability obligations to your customers. You cannot charge -for the Source Code. +6. If you sublicense the Licensed Product or Derivative Works, you may charge fees for warranty or support, or for accepting indemnity or liability obligations to your customers. You cannot charge for the Source Code. -7. If you assert any patent claims against the Licensor relating to the Licensed Product, or if you -breach any terms of the License, your rights to the Licensed Product under this License -automatically terminate. +7. If you assert any patent claims against the Licensor relating to the Licensed Product, or if you breach any terms of the License, your rights to the Licensed Product under this License automatically terminate. -You may use this License to distribute your own Derivative Works, in which case the provisions of -this License will apply to your Derivative Works just as they do to the original Licensed Product. +You may use this License to distribute your own Derivative Works, in which case the provisions of this License will apply to your Derivative Works just as they do to the original Licensed Product. -Alternatively, you may distribute your Derivative Works under any other OSI-approved Open Source -license, or under a proprietary license of your choice. If you use any license other than this -License, however, you must continue to fulfill the requirements of this License (including the -provisions relating to publishing the Source Code) for those portions of your Derivative Works that -consist of the Licensed Product, including the files containing Modifications. +Alternatively, you may distribute your Derivative Works under any other OSI-approved Open Source license, or under a proprietary license of your choice. If you use any license other than this License, however, you must continue to fulfill the requirements of this License (including the provisions relating to publishing the Source Code) for those portions of your Derivative Works that consist of the Licensed Product, including the files containing Modifications. -New versions of this License may be published from time to time. You may choose to continue to use -the license terms in this version of the License or those from the new version. However, only the -Licensor has the right to change the License terms as they apply to the Licensed Product. This -License relies on precise definitions for certain terms. Those terms are defined when they are -first used, and the definitions are repeated for your convenience in a Glossary at the end of the -License. +New versions of this License may be published from time to time. You may choose to continue to use the license terms in this version of the License or those from the new version. However, only the Licensor has the right to change the License terms as they apply to the Licensed Product. This License relies on precise definitions for certain terms. Those terms are defined when they are first used, and the definitions are repeated for your convenience in a Glossary at the end of the License. License Terms 1. Grant of License From Licensor. -Licensor hereby grants you a world-wide, royalty-free, non-exclusive license, subject to third -party intellectual property claims, to do the following: +Licensor hereby grants you a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, to do the following: - a. Use, reproduce, modify, display, perform, sublicense and distribute Licensed Product or -portions thereof (including Modifications as hereinafter defined), in both Source Code or as an -executable program. "Source Code" means the preferred form for making modifications to the Licensed -Product, including all modules contained therein, plus any associated interface definition files, -scripts used to control compilation and installation of an executable program, or a list of -differential comparisons against the Source Code of the Licensed Product. + a. Use, reproduce, modify, display, perform, sublicense and distribute Licensed Product or portions thereof (including Modifications as hereinafter defined), in both Source Code or as an executable program. "Source Code" means the preferred form for making modifications to the Licensed Product, including all modules contained therein, plus any associated interface definition files, scripts used to control compilation and installation of an executable program, or a list of differential comparisons against the Source Code of the Licensed Product. - b. Create Derivative Works (as that term is defined under Dutch copyright law) of Licensed -Product by adding to or deleting from the substance or structure of said Licensed Product. + b. Create Derivative Works (as that term is defined under Dutch copyright law) of Licensed Product by adding to or deleting from the substance or structure of said Licensed Product. - c. Under claims of patents now or hereafter owned or controlled by Licensor, to make, use, -sell, offer for sale, have made, and/or otherwise dispose of Licensed Product or portions thereof, -but solely to the extent that any such claim is necessary to enable you to make, use, sell, offer -for sale, have made, and/or otherwise dispose of Licensed Product or portions thereof or Derivative -Works thereof. + c. Under claims of patents now or hereafter owned or controlled by Licensor, to make, use, sell, offer for sale, have made, and/or otherwise dispose of Licensed Product or portions thereof, but solely to the extent that any such claim is necessary to enable you to make, use, sell, offer for sale, have made, and/or otherwise dispose of Licensed Product or portions thereof or Derivative Works thereof. 2. Grant of License to Modifications From Contributor. -"Modifications" means any additions to or deletions from the substance or structure of (i) a file -containing Licensed Product, or (ii) any new file that contains any part of Licensed Product. -Hereinafter in this License, the term "Licensed Product" shall include all previous Modifications -that you receive from any Contributor. By application of the provisions in Section 4(a) below, each -person or entity who created or contributed to the creation of, and distributed, a Modification (a -"Contributor") hereby grants you a world-wide, royalty-free, non-exclusive license, subject to -third party intellectual property claims, to do the following: +"Modifications" means any additions to or deletions from the substance or structure of (i) a file containing Licensed Product, or (ii) any new file that contains any part of Licensed Product. Hereinafter in this License, the term "Licensed Product" shall include all previous Modifications that you receive from any Contributor. By application of the provisions in Section 4(a) below, each person or entity who created or contributed to the creation of, and distributed, a Modification (a "Contributor") hereby grants you a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, to do the following: - a. Use, reproduce, modify, display, perform, sublicense and distribute any Modifications -created by such Contributor or portions thereof, in both Source Code or as an executable program, -either on an unmodified basis or as part of Derivative Works. + a. Use, reproduce, modify, display, perform, sublicense and distribute any Modifications created by such Contributor or portions thereof, in both Source Code or as an executable program, either on an unmodified basis or as part of Derivative Works. - b. Under claims of patents now or hereafter owned or controlled by Contributor, to make, use, -sell, offer for sale, have made, and/or otherwise dispose of Modifications or portions thereof, but -solely to the extent that any such claim is necessary to enable you to make, use, sell, offer for -sale, have made, and/or otherwise dispose of Modifications or portions thereof or Derivative Works -thereof. + b. Under claims of patents now or hereafter owned or controlled by Contributor, to make, use, sell, offer for sale, have made, and/or otherwise dispose of Modifications or portions thereof, but solely to the extent that any such claim is necessary to enable you to make, use, sell, offer for sale, have made, and/or otherwise dispose of Modifications or portions thereof or Derivative Works thereof. 3. Exclusions From License Grant. -Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, -trade secrets or any other intellectual property of Licensor or any Contributor except as expressly -stated herein. No patent license is granted separate from the Licensed Product, for code that you -delete from the Licensed Product, or for combinations of the Licensed Product with other software -or hardware. No right is granted to the trademarks of Licensor or any Contributor even if such -marks are included in the Licensed Product. Nothing in this License shall be interpreted to -prohibit Licensor from licensing under different terms from this License any code that Licensor -otherwise would have a right to license. +Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor or any Contributor except as expressly stated herein. No patent license is granted separate from the Licensed Product, for code that you delete from the Licensed Product, or for combinations of the Licensed Product with other software or hardware. No right is granted to the trademarks of Licensor or any Contributor even if such marks are included in the Licensed Product. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any code that Licensor otherwise would have a right to license. 4. Your Obligations Regarding Distribution. - a. Application of This License to Your Modifications. As an express condition for your use of -the Licensed Product, you hereby agree that any Modifications that you create or to which you -contribute, and which you distribute, are governed by the terms of this License including, without -limitation, Section 2. Any Modifications that you create or to which you contribute may be -distributed only under the terms of this License or a future version of this License released under -Section 7. You must include a copy of this License with every copy of the Modifications you -distribute. You agree not to offer or impose any terms on any Source Code or executable version of -the Licensed Product or Modifications that alter or restrict the applicable version of this License -or the recipients' rights hereunder. However, you may include an additional document offering the -additional rights described in Section 4(e). + a. Application of This License to Your Modifications. As an express condition for your use of the Licensed Product, you hereby agree that any Modifications that you create or to which you contribute, and which you distribute, are governed by the terms of this License including, without limitation, Section 2. Any Modifications that you create or to which you contribute may be distributed only under the terms of this License or a future version of this License released under Section 7. You must include a copy of this License with every copy of the Modifications you distribute. You agree not to offer or impose any terms on any Source Code or executable version of the Licensed Product or Modifications that alter or restrict the applicable version of this License or the recipients' rights hereunder. However, you may include an additional document offering the additional rights described in Section 4(e). - b. Availability of Source Code. You must make available, under the terms of this License, the -Source Code of the Licensed Product and any Modifications that you distribute, either on the same -media as you distribute any executable or other form of the Licensed Product, or via a mechanism -generally accepted in the software development community for the electronic transfer of data (an -"Electronic Distribution Mechanism"). The Source Code for any version of Licensed Product or -Modifications that you distribute must remain available for at least twelve (12) months after the -date it initially became available, or at least six (6) months after a subsequent version of said -Licensed Product or Modifications has been made available. You are responsible for ensuring that -the Source Code version remains available even if the Electronic Distribution Mechanism is -maintained by a third party. + b. Availability of Source Code. You must make available, under the terms of this License, the Source Code of the Licensed Product and any Modifications that you distribute, either on the same media as you distribute any executable or other form of the Licensed Product, or via a mechanism generally accepted in the software development community for the electronic transfer of data (an "Electronic Distribution Mechanism"). The Source Code for any version of Licensed Product or Modifications that you distribute must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of said Licensed Product or Modifications has been made available. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. - c. Description of Modifications. You must cause any Modifications that you create or to which -you contribute, and which you distribute, to contain a file documenting the additions, changes or -deletions you made to create or contribute to those Modifications, and the dates of any such -additions, changes or deletions. You must include a prominent statement that the Modifications are -derived, directly or indirectly, from the Licensed Product and include the names of the Licensor -and any Contributor to the Licensed Product in (i) the Source Code and (ii) in any notice displayed -by a version of the Licensed Product you distribute or in related documentation in which you -describe the origin or ownership of the Licensed Product. You may not modify or delete any -preexisting copyright notices in the Licensed Product. + c. Description of Modifications. You must cause any Modifications that you create or to which you contribute, and which you distribute, to contain a file documenting the additions, changes or deletions you made to create or contribute to those Modifications, and the dates of any such additions, changes or deletions. You must include a prominent statement that the Modifications are derived, directly or indirectly, from the Licensed Product and include the names of the Licensor and any Contributor to the Licensed Product in (i) the Source Code and (ii) in any notice displayed by a version of the Licensed Product you distribute or in related documentation in which you describe the origin or ownership of the Licensed Product. You may not modify or delete any preexisting copyright notices in the Licensed Product. d. Intellectual Property Matters. - i. Third Party Claims. If you have knowledge that a license to a third party's -intellectual property right is required to exercise the rights granted by this License, you must -include a text file with the Source Code distribution titled "LEGAL" that describes the claim and -the party making the claim in sufficient detail that a recipient will know whom to contact. If you -obtain such knowledge after you make any Modifications available as described in Section 4(b), you -shall promptly modify the LEGAL file in all copies you make available thereafter and shall take -other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to -inform those who received the Licensed Product from you that new knowledge has been obtained. + i. Third Party Claims. If you have knowledge that a license to a third party's intellectual property right is required to exercise the rights granted by this License, you must include a text file with the Source Code distribution titled "LEGAL" that describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after you make any Modifications available as described in Section 4(b), you shall promptly modify the LEGAL file in all copies you make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Licensed Product from you that new knowledge has been obtained. - ii. Contributor APIs. If your Modifications include an application programming interface -("API") and you have knowledge of patent licenses that are reasonably necessary to implement that -API, you must also include this information in the LEGAL file. + ii. Contributor APIs. If your Modifications include an application programming interface ("API") and you have knowledge of patent licenses that are reasonably necessary to implement that API, you must also include this information in the LEGAL file. - iii. Representations. You represent that, except as disclosed pursuant to 4(d)(i) above, -you believe that any Modifications you distribute are your original creations and that you have -sufficient rights to grant the rights conveyed by this License. + iii. Representations. You represent that, except as disclosed pursuant to 4(d)(i) above, you believe that any Modifications you distribute are your original creations and that you have sufficient rights to grant the rights conveyed by this License. - e. Required Notices. You must duplicate this License in any documentation you provide along -with the Source Code of any Modifications you create or to which you contribute, and which you -distribute, wherever you describe recipients' rights relating to Licensed Product. You must -duplicate the notice contained in Exhibit A (the "Notice") in each file of the Source Code of any -copy you distribute of the Licensed Product. If you created a Modification, you may add your name -as a Contributor to the Notice. If it is not possible to put the Notice in a particular Source Code -file due to its structure, then you must include such Notice in a location (such as a relevant -directory file) where a user would be likely to look for such a notice. You may choose to offer, -and charge a fee for, warranty, support, indemnity or liability obligations to one or more -recipients of Licensed Product. However, you may do so only on your own behalf, and not on behalf -of the Licensor or any Contributor. You must make it clear that any such warranty, support, -indemnity or liability obligation is offered by you alone, and you hereby agree to indemnify the -Licensor and every Contributor for any liability incurred by the Licensor or such Contributor as a -result of warranty, support, indemnity or liability terms you offer. + e. Required Notices. You must duplicate this License in any documentation you provide along with the Source Code of any Modifications you create or to which you contribute, and which you distribute, wherever you describe recipients' rights relating to Licensed Product. You must duplicate the notice contained in Exhibit A (the "Notice") in each file of the Source Code of any copy you distribute of the Licensed Product. If you created a Modification, you may add your name as a Contributor to the Notice. If it is not possible to put the Notice in a particular Source Code file due to its structure, then you must include such Notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Licensed Product. However, you may do so only on your own behalf, and not on behalf of the Licensor or any Contributor. You must make it clear that any such warranty, support, indemnity or liability obligation is offered by you alone, and you hereby agree to indemnify the Licensor and every Contributor for any liability incurred by the Licensor or such Contributor as a result of warranty, support, indemnity or liability terms you offer. - f. Distribution of Executable Versions. You may distribute Licensed Product as an executable -program under a license of your choice that may contain terms different from this License provided -(i) you have satisfied the requirements of Sections 4(a) through 4(e) for that distribution, (ii) -you include a conspicuous notice in the executable version, related documentation and collateral -materials stating that the Source Code version of the Licensed Product is available under the terms -of this License, including a description of how and where you have fulfilled the obligations of -Section 4(b), (iii) you retain all existing copyright notices in the Licensed Product, and (iv) you -make it clear that any terms that differ from this License are offered by you alone, not by -Licensor or any Contributor. You hereby agree to indemnify the Licensor and every Contributor for -any liability incurred by Licensor or such Contributor as a result of any terms you offer. + f. Distribution of Executable Versions. You may distribute Licensed Product as an executable program under a license of your choice that may contain terms different from this License provided (i) you have satisfied the requirements of Sections 4(a) through 4(e) for that distribution, (ii) you include a conspicuous notice in the executable version, related documentation and collateral materials stating that the Source Code version of the Licensed Product is available under the terms of this License, including a description of how and where you have fulfilled the obligations of Section 4(b), (iii) you retain all existing copyright notices in the Licensed Product, and (iv) you make it clear that any terms that differ from this License are offered by you alone, not by Licensor or any Contributor. You hereby agree to indemnify the Licensor and every Contributor for any liability incurred by Licensor or such Contributor as a result of any terms you offer. - g. Distribution of Derivative Works. You may create Derivative Works (e.g., combinations of -some or all of the Licensed Product with other code) and distribute the Derivative Works as -products under any other license you select, with the proviso that the requirements of this License -are fulfilled for those portions of the Derivative Works that consist of the Licensed Product or -any Modifications thereto. + g. Distribution of Derivative Works. You may create Derivative Works (e.g., combinations of some or all of the Licensed Product with other code) and distribute the Derivative Works as products under any other license you select, with the proviso that the requirements of this License are fulfilled for those portions of the Derivative Works that consist of the Licensed Product or any Modifications thereto. 5. Inability to Comply Due to Statute or Regulation. -If it is impossible for you to comply with any of the terms of this License with respect to some or -all of the Licensed Product due to statute, judicial order, or regulation, then you must (i) comply -with the terms of this License to the maximum extent possible, (ii) cite the statute or regulation -that prohibits you from adhering to the License, and (iii) describe the limitations and the code -they affect. Such description must be included in the LEGAL file described in Section 4(d), and -must be included with all distributions of the Source Code. Except to the extent prohibited by -statute or regulation, such description must be sufficiently detailed for a recipient of ordinary -skill at computer programming to be able to understand it. +If it is impossible for you to comply with any of the terms of this License with respect to some or all of the Licensed Product due to statute, judicial order, or regulation, then you must (i) comply with the terms of this License to the maximum extent possible, (ii) cite the statute or regulation that prohibits you from adhering to the License, and (iii) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 4(d), and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill at computer programming to be able to understand it. 6. Application of This License. -This License applies to code to which Licensor or Contributor has attached the Notice in Exhibit A, -which is incorporated herein by this reference. +This License applies to code to which Licensor or Contributor has attached the Notice in Exhibit A, which is incorporated herein by this reference. 7. Versions of This License. - a. Version. The Motosoto Open Source License is derived from the Jabber Open Source License. -All changes are related to applicable law and the location of court. + a. Version. The Motosoto Open Source License is derived from the Jabber Open Source License. All changes are related to applicable law and the location of court. - b. New Versions. Licensor may publish from time to time revised and/or new versions of the -License. + b. New Versions. Licensor may publish from time to time revised and/or new versions of the License. - c. Effect of New Versions. Once Licensed Product has been published under a particular version -of the License, you may always continue to use it under the terms of that version. You may also -choose to use such Licensed Product under the terms of any subsequent version of the License -published by Licensor. No one other than Lic ensor has the right to modify the terms applicable to -Licensed Product created under this License. + c. Effect of New Versions. Once Licensed Product has been published under a particular version of the License, you may always continue to use it under the terms of that version. You may also choose to use such Licensed Product under the terms of any subsequent version of the License published by Licensor. No one other than Lic ensor has the right to modify the terms applicable to Licensed Product created under this License. - d. Derivative Works of this License. If you create or use a modified version of this License, -which you may do only in order to apply it to software that is not already a Licensed Product under -this License, you must rename your license so that it is not confusingly similar to this License, -and must make it clear that your license contains terms that differ from this License. In so naming -your license, you may not use any trademark of Licensor or any Contributor. + d. Derivative Works of this License. If you create or use a modified version of this License, which you may do only in order to apply it to software that is not already a Licensed Product under this License, you must rename your license so that it is not confusingly similar to this License, and must make it clear that your license contains terms that differ from this License. In so naming your license, you may not use any trademark of Licensor or any Contributor. 8. Disclaimer of Warranty. -LICENSED PRODUCT IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE LICENSED PRODUCT IS -FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE LICENSED PRODUCT IS WITH YOU. SHOULD LICENSED PRODUCT PROVE -DEFECTIVE IN ANY RESPECT, YOU (AND NOT THE LICENSOR OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF -ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -PART OF THIS LICENSE. NO USE OF LICENSED PRODUCT IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS -DISCLAIMER. +LICENSED PRODUCT IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE LICENSED PRODUCT IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LICENSED PRODUCT IS WITH YOU. SHOULD LICENSED PRODUCT PROVE DEFECTIVE IN ANY RESPECT, YOU (AND NOT THE LICENSOR OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF LICENSED PRODUCT IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 9. Termination. - a. Automatic Termination Upon Breach. This license and the rights granted hereunder will -terminate automatically if you fail to comply with the terms herein and fail to cure such breach -within thirty (30) days of becoming aware of the breach. All sublicenses to the Licensed Product -that are properly granted shall survive any termination of this license. Provisions that, by their -nature, must remain in effect beyond the termination of this License, shall survive. + a. Automatic Termination Upon Breach. This license and the rights granted hereunder will terminate automatically if you fail to comply with the terms herein and fail to cure such breach within thirty (30) days of becoming aware of the breach. All sublicenses to the Licensed Product that are properly granted shall survive any termination of this license. Provisions that, by their nature, must remain in effect beyond the termination of this License, shall survive. - b. Termination Upon Assertion of Patent Infringement. If you initiate litigation by asserting -a patent infringement claim (excluding declaratory judgment actions) against Licensor or a -Contributor (Licensor or Contributor against whom you file such an action is referred to herein as -"Respondent") alleging that Licensed Product directly or indirectly infringes any patent, then any -and all rights granted by such Respondent to you under Sections 1 or 2 of this License shall -terminate prospectively upon sixty (60) days notice from Respondent (the "Notice Period") unless -within that Notice Period you either agree in writing (i) to pay Respondent a mutually agreeable -reasonably royalty for your past or future use of Licensed Product made by such Respondent, or (ii) -withdraw your litigation claim with respect to Licensed Product against such Respondent. If within -said Notice Period a reasonable royalty and payment arrangement are not mutually agreed upon in -writing by the parties or the litigation claim is not withdrawn, the rights granted by Licensor to -you under Sections 1 and 2 automatically terminate at the expiration of said Notice Period. + b. Termination Upon Assertion of Patent Infringement. If you initiate litigation by asserting a patent infringement claim (excluding declaratory judgment actions) against Licensor or a Contributor (Licensor or Contributor against whom you file such an action is referred to herein as "Respondent") alleging that Licensed Product directly or indirectly infringes any patent, then any and all rights granted by such Respondent to you under Sections 1 or 2 of this License shall terminate prospectively upon sixty (60) days notice from Respondent (the "Notice Period") unless within that Notice Period you either agree in writing (i) to pay Respondent a mutually agreeable reasonably royalty for your past or future use of Licensed Product made by such Respondent, or (ii) withdraw your litigation claim with respect to Licensed Product against such Respondent. If within said Notice Period a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Licensor to you under Sections 1 and 2 automatically terminate at the expiration of said Notice Period. - c. Reasonable Value of This License. If you assert a patent infringement claim against -Respondent alleging that Licensed Product directly or indirectly infringes any patent where such -claim is resolved (such as by license or settlement) prior to the initiation of patent infringement -litigation, then the reasonable value of the licenses granted by said Respondent under Sections 1 -and 2 shall be taken into account in determining the amount or value of any payment or license. + c. Reasonable Value of This License. If you assert a patent infringement claim against Respondent alleging that Licensed Product directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by said Respondent under Sections 1 and 2 shall be taken into account in determining the amount or value of any payment or license. - d. No Retroactive Effect of Termination. In the event of termination under Sections 9(a) or -9(b) above, all end user license agreements (excluding licenses to distributors and reselle rs) -that have been validly granted by you or any distributor hereunder prior to termination shall -survive termination. + d. No Retroactive Effect of Termination. In the event of termination under Sections 9(a) or 9(b) above, all end user license agreements (excluding licenses to distributors and reselle rs) that have been validly granted by you or any distributor hereunder prior to termination shall survive termination. 10. Limitation of Liability. -UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR -OTHERWISE, SHALL THE LICENSOR, ANY CONTRIBUTOR, OR ANY DISTRIBUTOR OF LICENSED PRODUCT, OR ANY -SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR -CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, -WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, -EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF -LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY's -NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE  -EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY  -NOT APPLY TO YOU. - -11. Responsibility for Claims.  - -As between Licensor and Contributors, each party is responsible for claims and damages arising,  -directly or indirectly, out of its utilization of rights under this License. You agree to work with  -Licensor and Contributors to distribute such responsibility on an equitable basis. Nothing herein is  -intended or shall be deemed to constitute any admission of liability. - -12. U.S. Government End Users.  - -The Licensed Product is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995),  -consisting of "commercial computer software" and "commercial computer software documentation,"  -as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and  -48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire  -Licensed Product with only those rights set forth herein. - -13. Miscellaneous.  -This License represents the complete agreement concerning the subject matter hereof. If any  -provision of this License is held to be unenforceable, such provision shall be reformed only  -to the extent necessary to make it enforceable. This License shall be governed by Dutch law  -provisions. The application of the United Nations Convention on Contracts for the International  -Sale of Goods is expressly excluded. You and Licensor expressly waive any rights to a jury trial  -in any litigation concerning Licensed Product or this License. Any law or regulation that provides  -that the language of a contract shall be construed against the drafter shall not apply to this License. - -14. Definition of "You" in This License.  -"You" throughout this License, whether in upper or lower case, means an individual or a legal entity  -exercising rights under, and complying with all of the terms of, this License or a future version of  -this License issued under Section 7. For legal entities, "you" includes any entity that controls, is  -controlled by, or is under common control with you. For purposes of this definition, "control" means  -(i) the power, direct or indirect, to cause the direction or management of such entity, whether by  -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares,  -or (iii) beneficial ownership of such entity. - -15. Glossary. -All defined terms in this License that are used in more than one Section of this License are  -repeated here, in alphabetical order, for the convenience of the reader. The Section of this  -License in which each defined term is first used is shown in parentheses.  - -Contributor: Each person or entity who created or contributed to the creation of, and distributed, a Modification. (See Section 2) - -Derivative Works: That term as used in this License is defined under Dutch copyright law. (See Section 1(b)) - -License: This Motosoto Open Source License. (See first paragraph of License) - -Licensed Product: Any Motosoto Product licensed pursuant to this License. The term -"Licensed Product" includes all previous Modifications from any Contributor that you receive.  -(See first paragraph of License and Section 2) - -Licensor: Motosoto.Com B.V.. (See first paragraph of License) - -Modifications: Any additions to or deletions from the substance or structure of (i) a file  -containing Licensed Product, or (ii) any new file that contains any part of Licensed Product. (See Section 2) - -Notice: The notice contained in Exhibit A. (See Section 4(e)) - -Source Code: The preferred form for making modifications to the Licensed Product, including  -all modules contained therein, plus any associated interface definition files, scripts used  -to control compilation and installation of an executable program, or a list of differential  -comparisons against the Source Code of the Licensed Product. (See Section 1(a)) - -You: This term is defined in Section 14 of this License. -  -EXHIBIT A -The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product or any Modifications thereto. Contributors to any Modifications may add their own copyright notices to identify their own contributions. - -License: -The contents of this file are subject to the Motosoto Open Source License Version 0.9 (the "License"). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License at http://www.motosoto.com/license/ or at http://www.opensource.org/. - -Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - -Copyrights: -Portions created by or assigned to Motosoto.com B.V. are Copyright (c) 2000-2001 Motosoto.com B.V. -All Rights Reserved. Contact information for Motosoto.com B.V. is available at http://www.motosoto.com/. - -Acknowledgements -Special thanks to the Motosoto Open Source Contributors for their suggestions and support of Motosoto. - -Modifications: +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE LICENSOR, ANY CONTRIBUTOR, OR ANY DISTRIBUTOR OF LICENSED PRODUCT, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY diff --git a/options/license/NIST-PD-TNT b/options/license/NIST-PD-TNT deleted file mode 100644 index 51202893ac..0000000000 --- a/options/license/NIST-PD-TNT +++ /dev/null @@ -1,3 +0,0 @@ -This software was developed at the National Institute of Standards and Technology (NIST) by employees of the Federal Government in the course of their official duties. Pursuant to title 17 Section 105 of the United States Code this software is not subject to copyright protection and is in the public domain. NIST assumes no responsibility whatsoever for its use by other parties, and makes no guarantees, expressed or implied, about its quality, reliability, or any other characteristic. - -We would appreciate acknowledgement if the software is incorporated in redistributable libraries or applications. diff --git a/options/license/NTIA-PD b/options/license/NTIA-PD deleted file mode 100644 index f451790073..0000000000 --- a/options/license/NTIA-PD +++ /dev/null @@ -1,13 +0,0 @@ -SOFTWARE DISCLAIMER / RELEASE - -This software was developed by employees of the National Telecommunications and Information Administration (NTIA), an agency of the Federal Government and is provided to you as a public service. Pursuant to Title 15 United States Code Section 105, works of NTIA employees are not subject to copyright protection within the United States. - -The software is provided by NTIA “AS IS.” NTIA MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA ACCURACY. NTIA does not warrant or make any representations regarding the use of the software or the results thereof, including but not limited to the correctness, accuracy, reliability or usefulness of the software. - -To the extent that NTIA holds rights in countries other than the United States, you are hereby granted the non-exclusive irrevocable and unconditional right to print, publish, prepare derivative works and distribute the NTIA software, in any medium, or authorize others to do so on your behalf, on a royalty-free basis throughout the World. - -You may improve, modify, and create derivative works of the software or any portion of the software, and you may copy and distribute such modifications or works. Modified works should carry a notice stating that you changed the software and should note the date and nature of any such change. - -You are solely responsible for determining the appropriateness of using and distributing the software and you assume all risks associated with its use, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and the unavailability or interruption of operation. This software is not intended to be used in any situation where a failure could cause risk of injury or damage to property. - -Please provide appropriate acknowledgments of NTIA’s creation of the software in any copies or derivative works of this software. diff --git a/options/license/Net-SNMP b/options/license/Net-SNMP new file mode 100644 index 0000000000..9ec271072f --- /dev/null +++ b/options/license/Net-SNMP @@ -0,0 +1,107 @@ + ---- Part 1: CMU/UCD copyright notice: (BSD like) ----- + + Copyright 1989, 1991, 1992 by Carnegie Mellon University + + Derivative Work - 1996, 1998-2000 Copyright 1996, 1998-2000 The Regents of the University of California + + All Rights Reserved + +Permission to use, copy, modify and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of CMU and The Regents of the University of California not be used in advertising or publicity pertaining to distribution of the software without specific written permission. + +CMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU OR THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE 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. + +---- Part 2: Networks Associates Technology, Inc copyright notice (BSD) ----- + +Copyright (c) 2001-2003, Networks Associates Technology, Inc All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the Networks Associates Technology, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS 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 HOLDERS 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. + +---- Part 3: Cambridge Broadband Ltd. copyright notice (BSD) ----- + +Portions of this code are copyright (c) 2001-2003, Cambridge Broadband Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * The name of Cambridge Broadband Ltd. may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 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. + +---- Part 4: Sun Microsystems, Inc. copyright notice (BSD) ----- + +Copyright © 2003 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved. + +Use is subject to license terms below. + +This distribution may include materials developed by third parties. + +Sun, Sun Microsystems, the Sun logo and Solaris are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the Sun Microsystems, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS 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 HOLDERS 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. + +---- Part 5: Sparta, Inc copyright notice (BSD) ----- + +Copyright (c) 2003-2009, Sparta, Inc All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Sparta, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS 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 HOLDERS 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. + +---- Part 6: Cisco/BUPTNIC copyright notice (BSD) ----- + +Copyright (c) 2004, Cisco, Inc and Information Network Center of Beijing University of Posts and Telecommunications. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Cisco, Inc, Beijing University of Posts and Telecommunications, nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS 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 HOLDERS 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. + +---- Part 7: Fabasoft R&D Software GmbH & Co KG copyright notice (BSD) ----- + +Copyright (c) Fabasoft R&D Software GmbH & Co KG, 2003 oss@fabasoft.com Author: Bernhard Penz + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + * 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. + + * The name of Fabasoft R&D Software GmbH & Co KG or any of its subsidiaries, brand or product names may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 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. + +---- Part 8: Apple Inc. copyright notice (BSD) ----- + +Copyright (c) 2007 Apple Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Apple Inc. ("Apple") nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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. + +---- Part 9: ScienceLogic, LLC copyright notice (BSD) ----- + +Copyright (c) 2009, ScienceLogic, LLC All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of ScienceLogic, LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS 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 HOLDERS 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. diff --git a/options/license/OSSP b/options/license/OSSP deleted file mode 100644 index 92a1d4705d..0000000000 --- a/options/license/OSSP +++ /dev/null @@ -1,26 +0,0 @@ -COPYRIGHT AND LICENSE - - Copyright (c) 2001-2005 Ralf S. Engelschall - Copyright (c) 2001-2005 The OSSP Project - Copyright (c) 2001-2005 Cable & Wireless - - This file is part of OSSP var, a variable expansion - library which can be found at http://www.ossp.org/pkg/lib/var/. - - Permission to use, copy, modify, and 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. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 AUTHORS AND COPYRIGHT HOLDERS AND THEIR - 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. diff --git a/options/license/OpenMDW-1.0 b/options/license/OpenMDW-1.0 deleted file mode 100644 index 5166ede9b6..0000000000 --- a/options/license/OpenMDW-1.0 +++ /dev/null @@ -1,49 +0,0 @@ -OpenMDW License Agreement, version 1.0 (OpenMDW-1.0) - -By exercising rights granted to you under this agreement, you accept and agree -to its terms. - -As used in this agreement, "Model Materials" means the materials provided to -you under this agreement, consisting of: (1) one or more machine learning -models (including architecture and parameters); and (2) all related artifacts -(including associated data, documentation and software) that are provided to -you hereunder. - -Subject to your compliance with this agreement, permission is hereby granted, -free of charge, to deal in the Model Materials without restriction, including -under all copyright, patent, database, and trade secret rights included or -embodied therein. - -If you distribute any portion of the Model Materials, you shall retain in your -distribution (1) a copy of this agreement, and (2) all copyright notices and -other notices of origin included in the Model Materials that are applicable to -your distribution. - -If you file, maintain, or voluntarily participate in a lawsuit against any -person or entity asserting that the Model Materials directly or indirectly -infringe any patent, then all rights and grants made to you hereunder are -terminated, unless that lawsuit was in response to a corresponding lawsuit -first brought against you. - -This agreement does not impose any restrictions or obligations with respect to -any use, modification, or sharing of any outputs generated by using the Model -Materials. - -THE MODEL MATERIALS ARE 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, TITLE, NONINFRINGEMENT, ACCURACY, OR THE -ABSENCE OF LATENT OR OTHER DEFECTS OR ERRORS, WHETHER OR NOT DISCOVERABLE, ALL -TO THE GREATEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW. - -YOU ARE SOLELY RESPONSIBLE FOR (1) CLEARING RIGHTS OF OTHER PERSONS THAT MAY -APPLY TO THE MODEL MATERIALS OR ANY USE THEREOF, INCLUDING WITHOUT LIMITATION -ANY PERSON'S COPYRIGHTS OR OTHER RIGHTS INCLUDED OR EMBODIED IN THE MODEL -MATERIALS; (2) OBTAINING ANY NECESSARY CONSENTS, PERMISSIONS OR OTHER RIGHTS -REQUIRED FOR ANY USE OF THE MODEL MATERIALS; OR (3) PERFORMING ANY DUE -DILIGENCE OR UNDERTAKING ANY OTHER INVESTIGATIONS INTO THE MODEL MATERIALS OR -ANYTHING INCORPORATED OR EMBODIED THEREIN. - -IN NO EVENT SHALL THE PROVIDERS OF THE MODEL MATERIALS 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 MODEL MATERIALS, THE -USE THEREOF OR OTHER DEALINGS THEREIN. diff --git a/options/license/ParaType-Free-Font-1.3 b/options/license/ParaType-Free-Font-1.3 deleted file mode 100644 index 21f8140887..0000000000 --- a/options/license/ParaType-Free-Font-1.3 +++ /dev/null @@ -1,24 +0,0 @@ -ParaType Free Font Licensing Agreement - -Copyright (c) 2009, ParaType Ltd. All Rights Reserved. - -LICENSING AGREEMENT -for the fonts with Original Name: PT Sans, PT Serif, PT Mono -Version 1.3 - January 20, 2012 - -GRANT OF LICENSE -ParaType Ltd grants you the right to use, copy, modify the fonts and distribute modified and unmodified copies of the fonts by any means, including placing on Web servers for free downloading, embedding in documents and Web pages, bundling with commercial and non commercial products, if it does not conflict with the conditions listed below: - -- You may bundle the fonts with commercial software, but you may not sell the fonts by themselves. They are free. - -- You may distribute the fonts in modified or unmodified versions only together with this Licensing Agreement and with above copyright notice. You have no right to modify the text of Licensing Agreement. It can be placed in a separate text file or inserted into the font file, but it must be easily viewed by users. - -- You may not distribute modified version of the font under the Original name or а combination of Original name with any other words without explicit written permission from ParaType. - -TERMINATION & TERRITORY -This license has no limits on time and territory, but it becomes null and void if any of the above conditions are not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL PARATYPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. - -ParaType Ltd diff --git a/options/license/Ruby-pty b/options/license/Ruby-pty deleted file mode 100644 index c817762f84..0000000000 --- a/options/license/Ruby-pty +++ /dev/null @@ -1,10 +0,0 @@ -(c) Copyright 1998 by Akinori Ito. - -This software may be redistributed freely for this purpose, in full -or in part, provided that this entire copyright notice is included -on any copies of this software and applications and derivations thereof. - -This software is provided on an "as is" basis, without warranty of any -kind, either expressed or implied, as to any matter including, but not -limited to warranty of fitness of purpose, or merchantability, or -results obtained from use of this software. diff --git a/options/license/SGMLUG-PM b/options/license/SGMLUG-PM deleted file mode 100644 index 552f8be94a..0000000000 --- a/options/license/SGMLUG-PM +++ /dev/null @@ -1,43 +0,0 @@ -LICENSE AND DISCLAIMER OF WARRANTIES - - Standard Generalized Markup Language Users' Group (SGMLUG) - SGML Parser Materials - - 1. License - -SGMLUG hereby grants to any user: (1) an irrevocable royalty-free, -worldwide, non-exclusive license to use, execute, reproduce, display, -perform and distribute copies of, and to prepare derivative works -based upon these materials; and (2) the right to authorize others to -do any of the foregoing. - - 2. Disclaimer of Warranties - -(a) The SGML Parser Materials are provided "as is" to any USER. USER -assumes responsibility for determining the suitability of the SGML -Parser Materials for its use and for results obtained. SGMLUG makes -no warranty that any errors have been eliminated from the SGML Parser -Materials or that they can be eliminated by USER. SGMLUG shall not -provide any support maintenance or other aid to USER or its licensees -with respect to SGML Parser Materials. SGMLUG shall not be -responsible for losses of any kind resulting from use of the SGML -Parser Materials including (without limitation) any liability for -business expense, machine downtime, or damages caused to USER or third -parties by any deficiency, defect, error, or malfunction. - -(b) SGMLUG DISCLAIMS ALL WARRANTIES, EXPRESSED OR IMPLIED, ARISING OUT -OF OR RELATING TO THE SGML PARSER MATERIALS OR ANY USE THEREOF, -INCLUDING (WITHOUT LIMITATION) ANY WARRANTY WHATSOEVER AS TO THE -FITNESS FOR A PARTICULAR USE OR THE MERCHANTABILITY OF THE SGML PARSER -MATERIALS. - -(c) In no event shall SGMLUG be liable to USER or third parties -licensed by USER for any indirect, special, incidental, or -consequential damages (including lost profits). -(d) SGMLUG has no knowledge of any conditions that would impair its right -to license the SGML Parser Materials. Notwithstanding the foregoing, -SGMLUG does not make any warranties or representations that the -SGML Parser Materials are free of claims by third parties of patent, -copyright infringement or the like, nor does SGMLUG assume any -liability in respect of any such infringement of rights of third -parties due to USER's operation under this license. diff --git a/options/license/SMAIL-GPL b/options/license/SMAIL-GPL deleted file mode 100644 index be799ec39d..0000000000 --- a/options/license/SMAIL-GPL +++ /dev/null @@ -1,144 +0,0 @@ -SMAIL GENERAL PUBLIC LICENSE - (Clarified 11 Feb 1988) - - Copyright (C) 1988 Landon Curt Noll & Ronald S. Karr - Copyright (C) 1992 Ronald S. Karr - Copyleft (GNU) 1988 Landon Curt Noll & Ronald S. Karr - - Everyone is permitted to copy and distribute verbatim copies - of this license, but changing it is not allowed. You can also - use this wording to make the terms for other programs. - - The license agreements of most software companies keep you at the -mercy of those companies. By contrast, our general public license is -intended to give everyone the right to share SMAIL. To make sure that -you get the rights we want you to have, we need to make restrictions -that forbid anyone to deny you these rights or to ask you to surrender -the rights. Hence this license agreement. - - Specifically, we want to make sure that you have the right to give -away copies of SMAIL, that you receive source code or else can get it -if you want it, that you can change SMAIL or use pieces of it in new -free programs, and that you know you can do these things. - - To make sure that everyone has such rights, we have to forbid you to -deprive anyone else of these rights. For example, if you distribute -copies of SMAIL, you must give the recipients all the rights that you -have. You must make sure that they, too, receive or can get the -source code. And you must tell them their rights. - - Also, for our own protection, we must make certain that everyone -finds out that there is no warranty for SMAIL. If SMAIL is modified by -someone else and passed on, we want its recipients to know that what -they have is not what we distributed, so that any problems introduced -by others will not reflect on our reputation. - - Therefore we (Landon Curt Noll and Ronald S. Karr) make the following -terms which say what you must do to be allowed to distribute or change -SMAIL. - - - COPYING POLICIES - - 1. You may copy and distribute verbatim copies of SMAIL source code -as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy a valid copyright notice "Copyright -(C) 1988 Landon Curt Noll & Ronald S. Karr" (or with whatever year is -appropriate); keep intact the notices on all files that refer to this -License Agreement and to the absence of any warranty; and give any -other recipients of the SMAIL program a copy of this License -Agreement along with the program. You may charge a distribution fee -for the physical act of transferring a copy. - - 2. You may modify your copy or copies of SMAIL or any portion of it, -and copy and distribute such modifications under the terms of -Paragraph 1 above, provided that you also do the following: - - a) cause the modified files to carry prominent notices stating - that you changed the files and the date of any change; and - - b) cause the whole of any work that you distribute or publish, - that in whole or in part contains or is a derivative of SMAIL or - any part thereof, to be licensed at no charge to all third - parties on terms identical to those contained in this License - Agreement (except that you may choose to grant more extensive - warranty protection to some or all third parties, at your option). - - c) You may charge a distribution fee for the physical act of - transferring a copy, and you may at your option offer warranty - protection in exchange for a fee. - -Mere aggregation of another unrelated program with this program (or its -derivative) on a volume of a storage or distribution medium does not bring -the other program under the scope of these terms. - - 3. You may copy and distribute SMAIL (or a portion or derivative of it, -under Paragraph 2) in object code or executable form under the terms of -Paragraphs 1 and 2 above provided that you also do one of the following: - - a) accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Paragraphs 1 and 2 above; or, - - b) accompany it with a written offer, valid for at least three - years, to give any third party free (except for a nominal - shipping charge) a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of - Paragraphs 1 and 2 above; or, - - c) accompany it with the information you received as to where the - corresponding source code may be obtained. (This alternative is - allowed only for non-commercial distribution and only if you - received the program in object code or executable form alone.) - -For an executable file, complete source code means all the source code for -all modules it contains; but, as a special exception, it need not include -source code for modules which are standard libraries that accompany the -operating system on which the executable file runs. - - 4. You may not copy, sublicense, distribute or transfer SMAIL -except as expressly provided under this License Agreement. Any attempt -otherwise to copy, sublicense, distribute or transfer SMAIL is void and -your rights to use the program under this License agreement shall be -automatically terminated. However, parties who have received computer -software programs from you with this License Agreement will not have -their licenses terminated so long as such parties remain in full compliance. - - 5. If you wish to incorporate parts of SMAIL into other free -programs whose distribution conditions are different, write to Landon -Curt Noll & Ronald S. Karr via the Free Software Foundation at 51 -Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. We have not yet -worked out a simple rule that can be stated here, but we will often -permit this. We will be guided by the two goals of preserving the -free status of all derivatives of our free software and of promoting -the sharing and reuse of software. - -Your comments and suggestions about our licensing policies and our -software are welcome! This contract was based on the contract made by -the Free Software Foundation. Please contact the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, -USA, or call (617) 542-5942 for details on copylefted material in -general. - - NO WARRANTY - - BECAUSE SMAIL IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO -WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING, LANDON CURT NOLL & RONALD S. KARR AND/OR -OTHER PARTIES PROVIDE SMAIL "AS IS" WITHOUT WARRANTY OF ANY KIND, -EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF SMAIL IS WITH -YOU. SHOULD SMAIL PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL -NECESSARY SERVICING, REPAIR OR CORRECTION. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL LANDON CURT NOLL & -RONALD S. KARR AND/OR ANY OTHER PARTY WHO MAY MODIFY AND REDISTRIBUTE -SMAIL AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -LOST PROFITS, LOST MONIES, OR OTHER SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE -(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED -INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A FAILURE OF THE -PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) SMAIL, EVEN IF YOU HAVE -BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY -ANY OTHER PARTY. diff --git a/options/license/SOFA b/options/license/SOFA deleted file mode 100644 index d9529497c3..0000000000 --- a/options/license/SOFA +++ /dev/null @@ -1,90 +0,0 @@ -Copyright (C) 2023 -Standards of Fundamental Astronomy Board -of the International Astronomical Union. - -===================== -SOFA Software License -===================== - -NOTICE TO USER: - -BY USING THIS SOFTWARE YOU ACCEPT THE FOLLOWING SIX TERMS AND -CONDITIONS WHICH APPLY TO ITS USE. - -1. The Software is owned by the IAU SOFA Board ("SOFA"). - -2. Permission is granted to anyone to use the SOFA software for any -purpose, including commercial applications, free of charge and -without payment of royalties, subject to the conditions and -restrictions listed below. - -3. You (the user) may copy and distribute SOFA source code to others, -and use and adapt its code and algorithms in your own software, -on a world-wide, royalty-free basis. That portion of your -distribution that does not consist of intact and unchanged copies -of SOFA source code files is a "derived work" that must comply -with the following requirements: - - a) Your work shall be marked or carry a statement that it - (i) uses routines and computations derived by you from - software provided by SOFA under license to you; and - (ii) does not itself constitute software provided by and/or - endorsed by SOFA. - - b) The source code of your derived work must contain descriptions - of how the derived work is based upon, contains and/or differs - from the original SOFA software. - - c) The names of all routines in your derived work shall not - include the prefix "iau" or "sofa" or trivial modifications - thereof such as changes of case. - - d) The origin of the SOFA components of your derived work must - not be misrepresented; you must not claim that you wrote the - original software, nor file a patent application for SOFA - software or algorithms embedded in the SOFA software. - - e) These requirements must be reproduced intact in any source - distribution and shall apply to anyone to whom you have - granted a further right to modify the source code of your - derived work. - - Note that, as originally distributed, the SOFA software is - intended to be a definitive implementation of the IAU standards, - and consequently third-party modifications are discouraged. All - variations, no matter how minor, must be explicitly marked as - such, as explained above. - - 4. You shall not cause the SOFA software to be brought into - disrepute, either by misuse, or use for inappropriate tasks, or - by inappropriate modification. - - 5. The SOFA software is provided "as is" and SOFA makes no warranty - as to its use or performance. SOFA does not and cannot warrant - the performance or results which the user may obtain by using the - SOFA software. SOFA makes no warranties, express or implied, as - to non-infringement of third party rights, merchantability, or - fitness for any particular purpose. In no event will SOFA be - liable to the user for any consequential, incidental, or special - damages, including any lost profits or lost savings, even if a - SOFA representative has been advised of such damages, or for any - claim by any third party. - - 6. The provision of any version of the SOFA software under the terms - and conditions specified herein does not imply that future - versions will also be made available under the same terms and - conditions. - - In any published work or commercial product which uses the SOFA - software directly, acknowledgement (see www.iausofa.org) is - appreciated. - - Correspondence concerning SOFA software should be addressed as - follows: - By email: sofa@ukho.gov.uk - By post: IAU SOFA Center - HM Nautical Almanac Office - UK Hydrographic Office - Admiralty Way, Taunton - Somerset, TA1 2DN - United Kingdom diff --git a/options/license/SUL-1.0 b/options/license/SUL-1.0 deleted file mode 100644 index 119672697d..0000000000 --- a/options/license/SUL-1.0 +++ /dev/null @@ -1,73 +0,0 @@ -Sustainable Use License - -Version 1.0 - -Acceptance - -By using the software, you agree to all of the terms and conditions below. - -Copyright License - -The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license -to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject -to the limitations below. - -Limitations - -You may use or modify the software only for your own internal business purposes or for non-commercial or -personal use. You may distribute the software or provide it to others only if you do so free of charge for -non-commercial purposes. You may not alter, remove, or obscure any licensing, copyright, or other notices of -the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law. - -Patents - -The licensor grants you a license, under any patent claims the licensor can license, or becomes able to -license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case -subject to the limitations and conditions in this license. This license does not cover any patent claims that -you cause to be infringed by modifications or additions to the software. If you or your company make any -written claim that the software infringes or contributes to infringement of any patent, your patent license -for the software granted under these terms ends immediately. If your company makes such a claim, your patent -license ends immediately for work on behalf of your company. - -Notices - -You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these -terms. If you modify the software, you must include in any modified copies of the software a prominent notice -stating that you have modified the software. - -No Other Rights - -These terms do not imply any licenses other than those expressly granted in these terms. - -Termination - -If you use the software in violation of these terms, such use is not licensed, and your license will -automatically terminate. If the licensor provides you with a notice of your violation, and you cease all -violation of this license no later than 30 days after you receive that notice, your license will be reinstated -retroactively. However, if you violate these terms after such reinstatement, any additional violation of these -terms will cause your license to terminate automatically and permanently. - -No Liability - -As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will -not be liable to you for any damages arising out of these terms or the use or nature of the software, under -any kind of legal claim. - -Definitions - -The “licensor” is the entity offering these terms. - -The “software” is the software the licensor makes available under these terms, including any portion of it. - -“You” refers to the individual or entity agreeing to these terms. - -“Your company” is any legal entity, sole proprietorship, or other kind of organization that you work for, plus -all organizations that have control over, are under the control of, or are under common control with that -organization. Control means ownership of substantially all the assets of an entity, or the power to direct its -management and policies by vote, contract, or otherwise. Control can be direct or indirect. - -“Your license” is the license granted to you for the software under these terms. - -“Use” means anything you do with the software requiring your license. - -“Trademark” means trademarks, service marks, and similar rights. diff --git a/options/license/Sendmail-Open-Source-1.1 b/options/license/Sendmail-Open-Source-1.1 deleted file mode 100644 index 054f719ee5..0000000000 --- a/options/license/Sendmail-Open-Source-1.1 +++ /dev/null @@ -1,75 +0,0 @@ -SENDMAIL OPEN SOURCE LICENSE - -The following license terms and conditions apply to this open source -software ("Software"), unless a different license is obtained directly -from Sendmail, Inc. ("Sendmail") located at 6475 Christie Ave, Suite 350, -Emeryville, CA 94608, USA. - -Use, modification and redistribution (including distribution of any -modified or derived work) of the Software in source and binary forms is -permitted only if each of the following conditions of 1-6 are met: - -1. Redistributions of the Software qualify as "freeware" or "open - source software" under one of the following terms: - - (a) Redistributions are made at no charge beyond the reasonable - cost of materials and delivery; or - - (b) Redistributions are accompanied by a copy of the modified - Source Code (on an acceptable machine-readable medium) or by an - irrevocable offer to provide a copy of the modified Source Code - (on an acceptable machine-readable medium) for up to three years - at the cost of materials and delivery. Such redistributions must - allow further use, modification, and redistribution of the Source - Code under substantially the same terms as this license. For - the purposes of redistribution "Source Code" means the complete - human-readable, compilable, linkable, and operational source - code of the redistributed module(s) including all modifications. - -2. Redistributions of the Software Source Code must retain the - copyright notices as they appear in each Source Code file, these - license terms and conditions, and the disclaimer/limitation of - liability set forth in paragraph 6 below. Redistributions of the - Software Source Code must also comply with the copyright notices - and/or license terms and conditions imposed by contributors on - embedded code. The contributors' license terms and conditions - and/or copyright notices are contained in the Source Code - distribution. - -3. Redistributions of the Software in binary form must reproduce the - Copyright Notice described below, these license terms and conditions, - and the disclaimer/limitation of liability set forth in paragraph - 6 below, in the documentation and/or other materials provided with - the binary distribution. For the purposes of binary distribution, - "Copyright Notice" refers to the following language: "Copyright (c) - 1998-2009 Sendmail, Inc. All rights reserved." - -4. Neither the name, trademark or logo of Sendmail, Inc. (including - without limitation its subsidiaries or affiliates) or its contributors - may be used to endorse or promote products, or software or services - derived from this Software without specific prior written permission. - The name "sendmail" is a registered trademark and service mark of - Sendmail, Inc. - -5. We reserve the right to cancel this license if you do not comply with - the terms. This license is governed by California law and both of us - agree that for any dispute arising out of or relating to this Software, - that jurisdiction and venue is proper in San Francisco or Alameda - counties. These license terms and conditions reflect the complete - agreement for the license of the Software (which means this supercedes - prior or contemporaneous agreements or representations). If any term - or condition under this license is found to be invalid, the remaining - terms and conditions still apply. - -6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY - SENDMAIL AND ITS CONTRIBUTORS "AS IS" WITHOUT WARRANTY OF ANY KIND - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT AND FITNESS FOR A - PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED. IN NO EVENT SHALL SENDMAIL - OR ITS 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 - WITHOUT LIMITATION NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. diff --git a/options/license/Simple-Library-Usage-exception b/options/license/Simple-Library-Usage-exception deleted file mode 100644 index 2db2bbe10f..0000000000 --- a/options/license/Simple-Library-Usage-exception +++ /dev/null @@ -1,33 +0,0 @@ -EXCEPTION NOTICE - -1. As a special exception, the copyright holders of this library give -permission for additional uses of the text contained in this release -of the library as licensed under the Simple Library Usage License, -applying either version 1 of the License, or (at your option) any -later version of the License as published by the copyright holders of -version 1 of the License document. - -2. The exception is that you may combine or link a "work that uses the -Library" (as defined by the LGPL) with the Library to produce a work -containing portions of the Library in binary form, and distribute that -work under terms of your choice, provided that: - - a) You give prominent notice with each copy of the work that the - Library is used in it. - - b) If the work during execution displays copyright notices, you must - include the copyright notice for the Library among them. - -3. If you copy code from files distributed under the terms of the GNU -General Public License or the GNU Lesser General Public License into a -copy of this library, as this license permits, the exception does not -apply to the code that you add in this way. To avoid misleading -anyone as to the status of such modified files, you must delete this -exception notice from such code and/or adjust the licensing conditions -notice accordingly. - -4. If you write modifications of your own for this library, it is your -choice whether to permit this exception to apply to your -modifications. If you do not wish that, you must delete the exception -notice from such code and/or adjust the licensing conditions notice -accordingly. diff --git a/options/license/TekHVC b/options/license/TekHVC deleted file mode 100644 index 6a3958717a..0000000000 --- a/options/license/TekHVC +++ /dev/null @@ -1,34 +0,0 @@ -Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc. - All Rights Reserved - -This file is a component of an X Window System-specific implementation -of Xcms based on the TekColor Color Management System. TekColor is a -trademark of Tektronix, Inc. The term "TekHVC" designates a particular -color space that is the subject of U.S. Patent No. 4,985,853 (equivalent -foreign patents pending). Permission is hereby granted to use, copy, -modify, sell, and otherwise distribute this software and its -documentation for any purpose and without fee, provided that: - -1. This copyright, permission, and disclaimer notice is reproduced in - all copies of this software and any modification thereof and in - supporting documentation; -2. Any color-handling application which displays TekHVC color - cooordinates identifies these as TekHVC color coordinates in any - interface that displays these coordinates and in any associated - documentation; -3. The term "TekHVC" is always used, and is only used, in association - with the mathematical derivations of the TekHVC Color Space, - including those provided in this file and any equivalent pathways and - mathematical derivations, regardless of digital (e.g., floating point - or integer) representation. - -Tektronix makes no representation about the suitability of this software -for any purpose. It is provided "as is" and with all faults. - -TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE, -INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE. IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY -SPECIAL, 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 THE PERFORMANCE OF THIS SOFTWARE. diff --git a/options/license/ThirdEye b/options/license/ThirdEye deleted file mode 100644 index ce75b566e3..0000000000 --- a/options/license/ThirdEye +++ /dev/null @@ -1,7 +0,0 @@ -(C) Copyright 1984 by Third Eye Software, Inc. - -Third Eye Software, Inc. grants reproduction and use rights to -all parties, PROVIDED that this comment is maintained in the copy. - -Third Eye makes no claims about the applicability of this -symbol table to a particular use. diff --git a/options/license/Ubuntu-font-1.0 b/options/license/Ubuntu-font-1.0 deleted file mode 100644 index ae78a8f94e..0000000000 --- a/options/license/Ubuntu-font-1.0 +++ /dev/null @@ -1,96 +0,0 @@ -------------------------------- -UBUNTU FONT LICENCE Version 1.0 -------------------------------- - -PREAMBLE -This licence allows the licensed fonts to be used, studied, modified and -redistributed freely. The fonts, including any derivative works, can be -bundled, embedded, and redistributed provided the terms of this licence -are met. The fonts and derivatives, however, cannot be released under -any other licence. The requirement for fonts to remain under this -licence does not require any document created using the fonts or their -derivatives to be published under this licence, as long as the primary -purpose of the document is not to be a vehicle for the distribution of -the fonts. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this licence and clearly marked as such. This may -include source files, build scripts and documentation. - -"Original Version" refers to the collection of Font Software components -as received under this licence. - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to -a new environment. - -"Copyright Holder(s)" refers to all individuals and companies who have a -copyright ownership of the Font Software. - -"Substantially Changed" refers to Modified Versions which can be easily -identified as dissimilar to the Font Software by users of the Font -Software comparing the Original Version with the Modified Version. - -To "Propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification and with or without charging -a redistribution fee), making available to the public, and in some -countries other activities as well. - -PERMISSION & CONDITIONS -This licence does not grant any rights under trademark law and all such -rights are reserved. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of the Font Software, to propagate the Font Software, subject to -the below conditions: - -1) Each copy of the Font Software must contain the above copyright -notice and this licence. These can be included either as stand-alone -text files, human-readable headers or in the appropriate machine- -readable metadata fields within text or binary files as long as those -fields can be easily viewed by the user. - -2) The font name complies with the following: -(a) The Original Version must retain its name, unmodified. -(b) Modified Versions which are Substantially Changed must be renamed to -avoid use of the name of the Original Version or similar names entirely. -(c) Modified Versions which are not Substantially Changed must be -renamed to both (i) retain the name of the Original Version and (ii) add -additional naming elements to distinguish the Modified Version from the -Original Version. The name of such Modified Versions must be the name of -the Original Version, with "derivative X" where X represents the name of -the new work, appended to that name. - -3) The name(s) of the Copyright Holder(s) and any contributor to the -Font Software shall not be used to promote, endorse or advertise any -Modified Version, except (i) as required by this licence, (ii) to -acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with -their explicit written permission. - -4) The Font Software, modified or unmodified, in part or in whole, must -be distributed entirely under this licence, and must not be distributed -under any other licence. The requirement for fonts to remain under this -licence does not affect any document created using the Font Software, -except any version of the Font Software extracted from a document -created using the Font Software may only be distributed under this -licence. - -TERMINATION -This licence becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF -COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER -DEALINGS IN THE FONT SOFTWARE. diff --git a/options/license/UnRAR b/options/license/UnRAR deleted file mode 100644 index 645b6071c8..0000000000 --- a/options/license/UnRAR +++ /dev/null @@ -1,41 +0,0 @@ -****** ***** ****** UnRAR - free utility for RAR archives - ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ****** ******* ****** License for use and distribution of - ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ** ** ** ** ** ** FREE portable version - ~~~~~~~~~~~~~~~~~~~~~ - - The source code of UnRAR utility is freeware. This means: - - 1. All copyrights to RAR and the utility UnRAR are exclusively - owned by the author - Alexander Roshal. - - 2. UnRAR source code may be used in any software to handle - RAR archives without limitations free of charge, but cannot be - used to develop RAR (WinRAR) compatible archiver and to - re-create RAR compression algorithm, which is proprietary. - Distribution of modified UnRAR source code in separate form - or as a part of other software is permitted, provided that - full text of this paragraph, starting from "UnRAR source code" - words, is included in license, or in documentation if license - is not available, and in source code comments of resulting package. - - 3. The UnRAR utility may be freely distributed. It is allowed - to distribute UnRAR inside of other software packages. - - 4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS". - NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT - YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS, - DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING - OR MISUSING THIS SOFTWARE. - - 5. Installing and using the UnRAR utility signifies acceptance of - these terms and conditions of the license. - - 6. If you don't agree with terms of the license you must remove - UnRAR files from your storage devices and cease to use the - utility. - - Thank you for your interest in RAR and UnRAR. - - Alexander L. Roshal diff --git a/options/license/Unlicense-libtelnet b/options/license/Unlicense-libtelnet deleted file mode 100644 index 18d1788030..0000000000 --- a/options/license/Unlicense-libtelnet +++ /dev/null @@ -1 +0,0 @@ -The author or authors of this code dedicate any and all copyright interest in this code to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this code under copyright law. diff --git a/options/license/Unlicense-libwhirlpool b/options/license/Unlicense-libwhirlpool deleted file mode 100644 index 11ead25c2d..0000000000 --- a/options/license/Unlicense-libwhirlpool +++ /dev/null @@ -1,8 +0,0 @@ -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any means. - -THE 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. diff --git a/options/license/Vixie-Cron b/options/license/Vixie-Cron deleted file mode 100644 index 3fefb02f27..0000000000 --- a/options/license/Vixie-Cron +++ /dev/null @@ -1,4 +0,0 @@ -Copyright 1988,1990,1993 by Paul Vixie -All rights reserved - -Distribute freely, except: don't remove my name from the source or documentation (don't take credit for my work), mark your changes (don't get me blamed for your possible bugs), don't alter or remove this notice. May be sold if buildable source is provided to buyer. No warrantee of any kind, express or implied, is included with this software; use at your own risk, responsibility for damages (if any) to anyone resulting from the use of this software rests entirely with the user. diff --git a/options/license/WTFNMFPL b/options/license/WTFNMFPL deleted file mode 100644 index e76917a9bc..0000000000 --- a/options/license/WTFNMFPL +++ /dev/null @@ -1,17 +0,0 @@ -DO WHAT THE FUCK YOU WANT TO BUT IT'S NOT MY FAULT PUBLIC LICENSE - Version 1, October 2013 - - Copyright (C) 2013 Ben McGinnes - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO BUT IT'S NOT MY FAULT PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. - - 1. Do not hold the author(s), creator(s), developer(s) or - distributor(s) liable for anything that happens or goes wrong - with your use of the work. diff --git a/options/license/WordNet b/options/license/WordNet deleted file mode 100644 index b937fa5cb3..0000000000 --- a/options/license/WordNet +++ /dev/null @@ -1,12 +0,0 @@ -WordNet Release 3.0 -This software and database is being provided to you, the LICENSEE, by Princeton University under the following license. - -By obtaining, using and/or copying this software and database, you agree that you have read, understood, and will comply with these terms and conditions.: - -Permission to use, copy, modify and distribute this software and database and its documentation for any purpose and without fee or royalty is hereby granted, provided that you agree to comply with the following copyright notice and statements, including the disclaimer, and that the same appear on ALL copies of the software, database and documentation, including modifications that you make for internal use or for distribution. - -WordNet 3.0 Copyright 2006 by Princeton University. All rights reserved. - -THIS SOFTWARE AND DATABASE IS PROVIDED "AS IS" AND PRINCETON UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PRINCETON UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE, DATABASE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. - -The name of Princeton University or Princeton may not be used in advertising or publicity pertaining to distribution of the software and/or database. Title to copyright in this software, database and any associated documentation shall at all times remain with Princeton University and LICENSEE agrees to preserve same. diff --git a/options/license/X11-no-permit-persons b/options/license/X11-no-permit-persons deleted file mode 100644 index 504b2eb7bd..0000000000 --- a/options/license/X11-no-permit-persons +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts. - -Permission 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. - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE 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 -DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING, -BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL 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. - -Except as contained in this notice, the name of Digital Equipment Corporation -shall not be used in advertising or otherwise to promote the sale, use or other -dealings in this Software without prior written authorization from Digital -Equipment Corporation. diff --git a/options/license/X11-swapped b/options/license/X11-swapped deleted file mode 100644 index b023bd546e..0000000000 --- a/options/license/X11-swapped +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2008-2010 Derick Eddington. All rights reserved. - -Permission 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: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -Except as contained in this notice, the name(s) of the above copyright -holders shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written authorization. - -THE 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. diff --git a/options/license/any-OSI-perl-modules b/options/license/any-OSI-perl-modules deleted file mode 100644 index 108db04581..0000000000 --- a/options/license/any-OSI-perl-modules +++ /dev/null @@ -1,11 +0,0 @@ -This software may be redistributed under the terms of the GPL, LGPL, -modified BSD, or Artistic license, or any of the other OSI approved -licenses listed at http://www.opensource.org/licenses/alphabetical. -Distribution is allowed under all of these licenses, or any smaller -subset of multiple or just one of these licenses. - -When using a packaged version, please refer to the package metadata to see -under which license terms it was distributed. Alternatively, a distributor -may choose to replace the LICENSE section of the documentation and/or -include a LICENSE file to reflect the license(s) they chose to redistribute -under. diff --git a/options/license/erlang-otp-linking-exception b/options/license/erlang-otp-linking-exception deleted file mode 100644 index ca8b775480..0000000000 --- a/options/license/erlang-otp-linking-exception +++ /dev/null @@ -1,11 +0,0 @@ -If you modify this Program, or any covered work, by linking or -combining it with runtime libraries of Erlang/OTP as released by -Ericsson on https://www.erlang.org (or a modified version of these -libraries), containing parts covered by the terms of the Erlang Public -License (https://www.erlang.org/EPLICENSE), the licensors of this -Program grant you additional permission to convey the resulting work -without the need to license the runtime libraries of Erlang/OTP under -the GNU Affero General Public License. Corresponding Source for a -non-source form of such a combination shall include the source code -for the parts of the runtime libraries of Erlang/OTP used as well as -that of the covered work. diff --git a/options/license/generic-xts b/options/license/generic-xts deleted file mode 100644 index bf08a2b421..0000000000 --- a/options/license/generic-xts +++ /dev/null @@ -1,17 +0,0 @@ -Copyright (C) 2008, Damien Miller -Copyright (C) 2011, Alex Hornung - -Permission to use, copy, and modify this software with or without fee -is hereby granted, provided that this entire notice is included in -all copies of any software which is or includes a copy or -modification of this software. -You may use this code under the GNU public license if you so wish. Please -contribute changes back to the authors under this freer than GPL license -so that we may further the use of strong encryption without limitations to -all. - -THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR -IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY -REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE -MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR -PURPOSE. diff --git a/options/license/harbour-exception b/options/license/harbour-exception deleted file mode 100644 index 25d75e9fc7..0000000000 --- a/options/license/harbour-exception +++ /dev/null @@ -1,23 +0,0 @@ -As a special exception, the Harbour Project gives permission for -additional uses of the text contained in its release of Harbour. - -The exception is that, if you link the Harbour libraries with other -files to produce an executable, this does not by itself cause the -resulting executable to be covered by the GNU General Public License. -Your use of that executable is in no way restricted on account of -linking the Harbour library code into it. - -This exception does not however invalidate any other reasons why -the executable file might be covered by the GNU General Public License. - -This exception applies only to the code released by the Harbour -Project under the name Harbour. If you copy code from other -Harbour Project or Free Software Foundation releases into a copy of -Harbour, as the General Public License permits, the exception does -not apply to the code that you add in this way. To avoid misleading -anyone as to the status of such modified files, you must delete -this exception notice from them. - -If you write modifications of your own for Harbour, it is your choice -whether to permit this exception to apply to your modifications. -If you do not wish that, delete this exception notice. diff --git a/options/license/hyphen-bulgarian b/options/license/hyphen-bulgarian deleted file mode 100644 index 6642d1c676..0000000000 --- a/options/license/hyphen-bulgarian +++ /dev/null @@ -1,8 +0,0 @@ -This software may be used, modified, copied, distributed, and sold, -both in source and binary form provided that the above copyright -notice and these terms are retained. The name of the author may not -be used to endorse or promote products derived from this software -without prior permission. THIS SOFTWARE IS PROVIDES "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED. IN NO EVENT -SHALL THE AUTHOR BE LIABLE FOR ANY DAMAGES ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE. diff --git a/options/license/jove b/options/license/jove deleted file mode 100644 index edcc31667a..0000000000 --- a/options/license/jove +++ /dev/null @@ -1,4 +0,0 @@ -This program is Copyright (C) 1986-2002 by Jonathan Payne. JOVE is -provided by Jonathan and Jovehacks without charge and without -warranty. You may copy, modify, and/or distribute JOVE, provided that -this notice is included in all the source files and documentation. diff --git a/options/license/kvirc-openssl-exception b/options/license/kvirc-openssl-exception deleted file mode 100644 index beb605b8bd..0000000000 --- a/options/license/kvirc-openssl-exception +++ /dev/null @@ -1,30 +0,0 @@ - _OpenSSL Exception_ - -0. Definitions - -"KVIrc" means KVIrc software licensed under version 2 or any later -version of the GNU General Public License (collectively, "GPL"), or a -work based on such software and licensed under the GPL. - -"OpenSSL" means OpenSSL toolkit software distributed by the OpenSSL -Project and licensed under the OpenSSL Licenses, or a work based on such -software and licensed under the OpenSSL Licenses. - -"OpenSSL Licenses" means the OpenSSL License and Original SSLeay License -under which the OpenSSL Project distributes the OpenSSL toolkit software, -as those licenses appear in the file LICENSE-OPENSSL. - -1. Exception - -You have permission to copy, modify, propagate, and distribute a work -formed by combining OpenSSL with KVIrc, or a work derivative of such a -combination, even if such copying, modification, propagation, or -distribution would otherwise violate the terms of the GPL. You must -comply with the GPL in all respects for all of the code used other than -OpenSSL. - -You may include this OpenSSL Exception and its grant of permissions when -you distribute KVIrc. Inclusion of this notice with such a -distribution constitutes a grant of such permission. If you do not wish -to grant these permissions, remove this section entitled "OpenSSL -Exception" from your distribution. diff --git a/options/license/libpng-1.6.35 b/options/license/libpng-1.6.35 deleted file mode 100644 index cf3b98dfdb..0000000000 --- a/options/license/libpng-1.6.35 +++ /dev/null @@ -1,96 +0,0 @@ -PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) ------------------------------------------------------------------------ - -libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are -Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are -derived from libpng-1.0.6, and are distributed according to the same -disclaimer and license as libpng-1.0.6 with the following individuals -added to the list of Contributing Authors: - - Simon-Pierre Cadieux - Eric S. Raymond - Mans Rullgard - Cosmin Truta - Gilles Vollant - James Yu - Mandar Sahastrabuddhe - Google Inc. - Vadim Barkov - -and with the following additions to the disclaimer: - - There is no warranty against interference with your enjoyment of - the library or against infringement. There is no warranty that our - efforts or the library will fulfill any of your particular purposes - or needs. This library is provided with all faults, and the entire - risk of satisfactory quality, performance, accuracy, and effort is - with the user. - -Some files in the "contrib" directory and some configure-generated -files that are distributed with libpng have other copyright owners, and -are released under other open source licenses. - -libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are -Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from -libpng-0.96, and are distributed according to the same disclaimer and -license as libpng-0.96, with the following individuals added to the -list of Contributing Authors: - - Tom Lane - Glenn Randers-Pehrson - Willem van Schaik - -libpng versions 0.89, June 1996, through 0.96, May 1997, are -Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, -and are distributed according to the same disclaimer and license as -libpng-0.88, with the following individuals added to the list of -Contributing Authors: - - John Bowler - Kevin Bracey - Sam Bushell - Magnus Holmgren - Greg Roelofs - Tom Tanner - -Some files in the "scripts" directory have other copyright owners, -but are released under this license. - -libpng versions 0.5, May 1995, through 0.88, January 1996, are -Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. - -For the purposes of this copyright and license, "Contributing Authors" -is defined as the following set of individuals: - - Andreas Dilger - Dave Martindale - Guy Eric Schalnat - Paul Schmidt - Tim Wegner - -The PNG Reference Library is supplied "AS IS". The Contributing -Authors and Group 42, Inc. disclaim all warranties, expressed or -implied, including, without limitation, the warranties of -merchantability and of fitness for any purpose. The Contributing -Authors and Group 42, Inc. assume no liability for direct, indirect, -incidental, special, exemplary, or consequential damages, which may -result from the use of the PNG Reference Library, even if advised of -the possibility of such damage. - -Permission is hereby granted to use, copy, modify, and distribute this -source code, or portions hereof, for any purpose, without fee, subject -to the following restrictions: - - 1. The origin of this source code must not be misrepresented. - - 2. Altered versions must be plainly marked as such and must not - be misrepresented as being the original source. - - 3. This Copyright notice may not be removed or altered from any - source or altered source distribution. - -The Contributing Authors and Group 42, Inc. specifically permit, -without fee, and encourage the use of this source code as a component -to supporting the PNG file format in commercial products. If you use -this source code in a product, acknowledgment is not required but would -be appreciated. diff --git a/options/license/man2html b/options/license/man2html deleted file mode 100644 index 42d61d3af5..0000000000 --- a/options/license/man2html +++ /dev/null @@ -1,2 +0,0 @@ -Permission is granted to distribute, modify and use this program -as long as this comment is not removed or changed. diff --git a/options/license/mxml-exception b/options/license/mxml-exception deleted file mode 100644 index 32928e8dd6..0000000000 --- a/options/license/mxml-exception +++ /dev/null @@ -1,16 +0,0 @@ -Mini-XML - -Copyright © 2003-2024 by Michael R Sweet - - -(Optional) Exceptions to the Apache 2.0 License: -================================================ - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 or LGPLv2 (“Combined Software”) and if -a court of competent jurisdiction determines that the patent provision (Section -3), the indemnity provision (Section 9) or other Section of the License -conflicts with the conditions of the GPLv2 or LGPLv2, you may retroactively and -prospectively choose to deem waived or otherwise exclude such Section(s) of the -License, but only in their entirety and only with respect to the Combined -Software. diff --git a/options/license/ngrep b/options/license/ngrep deleted file mode 100644 index 6b970e466b..0000000000 --- a/options/license/ngrep +++ /dev/null @@ -1,38 +0,0 @@ -Copyright (c) 2017 Jordan Ritter. All rights reserved. - -Permission is granted to anyone to use this software for any purpose on -any computer system, and to alter it and redistribute it, subject -to the following restrictions: - -1. The origin of this software must not be misrepresented, either by - explicit claim or by omission. - -2. Altered versions must be plainly marked as such, and must not be - misrepresented as being the original software. Any altered version - must clearly and properly represent the origin of this software in - any accompanying documentation. - -3. All advertising materials which relate specifically to derivate - works of this software must display the following acknowledgement: - This product includes software developed by Jordan Ritter. - -4. The name of the Author may not be used to endorse or promote - products derived from this software without specific prior written - permission. - -5. This notice, and any references to this notice, in any original or - derived source distribution of or documentation for this software, - may not be removed or altered. - - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. diff --git a/options/license/polyparse-exception b/options/license/polyparse-exception deleted file mode 100644 index 55ed918bb9..0000000000 --- a/options/license/polyparse-exception +++ /dev/null @@ -1,7 +0,0 @@ -As a relaxation of clause 6 of the LGPL, the copyright holders of this -library give permission to use, copy, link, modify, and distribute, -binary-only object-code versions of an executable linked with the -original unmodified Library, without requiring the supply of any -mechanism to modify or replace the Library and relink (clauses 6a, -6b, 6c, 6d, 6e), provided that all the other terms of clause 6 are -complied with. diff --git a/options/license/romic-exception b/options/license/romic-exception deleted file mode 100644 index 57def44818..0000000000 --- a/options/license/romic-exception +++ /dev/null @@ -1,6 +0,0 @@ -Additional permission under the GNU Affero GPL version 3 section 7: - -If you modify this Program, or any covered work, by linking or -combining it with other code, such other code is not for that reason -alone subject to any of the requirements of the GNU Affero GPL -version 3. diff --git a/options/license/rsync-linking-exception b/options/license/rsync-linking-exception deleted file mode 100644 index ddad55cb28..0000000000 --- a/options/license/rsync-linking-exception +++ /dev/null @@ -1,6 +0,0 @@ -In addition, as a special exception, the copyright holders give -permission to dynamically link rsync with the OpenSSL and xxhash -libraries when those libraries are being distributed in compliance -with their license terms, and to distribute a dynamically linked -combination of rsync and these libraries. This is also considered -to be covered under the GPL's System Libraries exception. diff --git a/options/license/wwl b/options/license/wwl deleted file mode 100644 index 12486ff638..0000000000 --- a/options/license/wwl +++ /dev/null @@ -1,5 +0,0 @@ -db@FreeBSD.ORG wrote this file. As long as you retain this notice you -can do whatever you want with this code, except you may not -license it under any form of the GPL. -A postcard or QSL card showing me you appreciate -this code would be nice. Diane Bruce va3db diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 587ec0490c..3130600db6 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -1,7 +1,9 @@ [common] language = لغة passcode = رمز المرور +webauthn_error_timeout = طال وقت الوصول قبل أن يُقرأ مفتاحك. من فضلك أعد تحميل الصفحة وحاول مجدداً. cancel = ألغِ +webauthn_sign_in = اضغط الزر على مفتاحك الأمني إذا لم يكن لدى مفتاح الأمن الخاص بك أي زر، إعادة تشغيله. captcha = كابتشا create_new = أنشئ… preview = عاين @@ -20,6 +22,7 @@ mirrors = المرايا explore = إكتشف return_to_forgejo = العودة إلى فورجيو write = اكتب +webauthn_error_unknown = حدث خطأ غير معروف. من فضلك حاول مجدداً. twofa = المصادقة الثنائية version = الإصدار copy_success = تم النسخ! @@ -27,12 +30,14 @@ help = مساعدة loading = جارٍ التحميل… copy_type_unsupported = هذا النوع من الملفات لا يمكن نسخه pin = ثبّت +webauthn_error_empty = يلزم أن تضع اسم لهذا المفتاح. confirm_delete_selected = تأكيد حذف جميع العناصر المحددة؟ name = الاسم artifacts = الآثار sign_in_or = أو show_log_seconds = إظهار الثواني show_full_screen = املأ الشاشة +webauthn_use_twofa = استخدم رمز المصادقة الموجود على هاتفك unpin = ألغِ التثبيت edit = حرر concept_code_repository = المستودع @@ -55,10 +60,12 @@ admin_panel = إدارة الموقع copy_error = فشل النسخ new_mirror = مرآة جديدة re_type = تأكيد كلمة المرور +webauthn_unsupported_browser = متصفحك لا يدعم ويب آوثن حالياً. copy = انسخ enabled = مُفَعَّل rerun = أعِد التشغيل milestones = أهداف +webauthn_error_insecure = ويب آوثن يدعم فقط الاتصالات الآمنة. للاختبار على HTTP، يمكنك استخدام "localhost" أو "127.0.0.1" show_timestamps = إظهار الطوابع الزمنية rss_feed = موجز RSS never = أبدًا @@ -72,6 +79,7 @@ sources = المصادر notifications = التنبيهات pull_requests = طلبات السحب repository = مستودع +webauthn_error = تعذر قراءة مفتاحك الأمني. add_all = أضف الكل new_fork = اشتقاق جديد لمستودع new_project_column = عمود جديد @@ -81,13 +89,17 @@ organization = منظمة save = احفظ sign_in_with_provider = سجل الدخول بـ %s ok = وافق +webauthn_error_unable_to_process = الخادم لا يمكنه معالجة طلبك. register = سجل mirror = مرآة username = إسم المستخدم access_token = رمز الوصول download_logs = تنزيل السجلات +webauthn_insert_key = أدخل مفتاحك الأمني password = كلمة المرور +webauthn_error_duplicated = المفتاح الأمني غير مسموح له هذا الطلب. يرجى التأكد من أن المفتاح غير مسجل بالفعل. template = قالب +webauthn_press_button = من فضلك اضغط الزر على مفتاحك الأمني… unknown = مجهول signed_in_as = مسجل كـ sign_up = سجل @@ -260,7 +272,7 @@ buttons.list.task.tooltip = أضف قائمة مهام buttons.enable_monospace_font = فعّل الخط الثابت العرض buttons.mention.tooltip = اذكر مستخدمًا أو فريقًا buttons.italic.tooltip = أضف نصًا مائلًا (Ctrl+I / ⌘I) -buttons.link.tooltip = اضف رابط. +buttons.link.tooltip = اضف رابط buttons.disable_monospace_font = عطّل الخط الثابت العرض buttons.unindent.tooltip = ‪عناصر غير متساوية من نفس المستوى buttons.indent.tooltip = تداخل العناصر بنفس المستوى @@ -607,6 +619,7 @@ quota.rule.exceeded.helper = لقد تجاوز الحجم الإجمالي لل quota.rule.exceeded = تم تجاوزه quota.applies_to_user = تنطبق قواعد الحصص التالية على حسابك quota.sizes.wiki = الموسوعة + ssh_token_help_ssh_agent = أو، إذا كنت تستخدم وكيل SSH (مع تعيين متغير SSH_AUTH_SOCK): [org] @@ -710,9 +723,12 @@ issues.blocked_by_user = لا يمكنك أن ترسل مسألة في هذا ا mirror_sync = متزامن settings.archive.mirrors_unavailable = المرايا ليست متاحة إذا تم أرشفة المستودع. pulls.blocked_by_user = لا يمكنك أن ترسل طلب سحب في هذا المستودع لأنك محظور من قبل مالك المستودع. +migrate.migrating_milestones = ترحيل الأهداف +migrate_items_milestones = أهداف repo_size = حجم المستودع object_format = تنسيق الكائنات use_template = استخدم هذا القالب +migrate_items_merge_requests = طلبات الدمج repo_name = اسم المستودع template = القالب projects.modify = عدّل المشروع @@ -730,19 +746,23 @@ template.git_hooks = خطاطيف Git template_description = المستودع القالب هو مستودع يستخدمه المستخدمون لتوليد مستودعات جديدة لها الإعدادات والملفات وهيكلة المجلدات نفسها. projects.edit = عدّل المشروع template.avatar = الصورة الرمزية +migrate_items_wiki = الموسوعة repo_desc = الوصف template_select = اختر قالبا repo_name_helper = الأسماء الحسنة للمستودعات تستخدم كلمات مفتاحية قصيرة وسهلة التذكر وفريدة. default_branch = الفرع الافتراضي all_branches = كل الفروع +migrate_items_issues = البلاغات projects.deletion_desc = حذف مشروع يحذف كل المسائل المرتبطة به. أتريد الاستمرار؟ repo_desc_helper = أدخل وصفاً قصيراً (اختياري) create_repo = إنشاء مستودع +migrate_items_releases = الإصدارات already_forked = لقد اشتققت %s بالفعل license_helper = اختر ملف ترخيص. 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 projects.edit_success = حدِّث المشروع "%s". @@ -866,6 +886,7 @@ release.publish = انشر الإصدار issues.lock_duplicate = لا يمكن إقفال مسألة مقفلة. issues.filter_type.assigned_to_you = كُلِّفت بها issues.save = احفظ +migrate_items_labels = تصنيفات issues.add_assignee_at = `كلّفه %s بها %s` milestones.filter_sort.least_complete = الأقل اكتمالا branch.create_branch = أنشئ الفرع %s @@ -1487,6 +1508,7 @@ 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 = (لم يتم تعديله) @@ -1505,12 +1527,16 @@ form.reach_limit_of_creation_1 = لقد وصل المالك بالفعل إلى 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 = الاستشهاد بهذا المستودع @@ -1524,6 +1550,8 @@ 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 @@ -1538,6 +1566,8 @@ create_new_repo_command = إنشاء مستودع جديد على موجّه ا fork = اشتقاق migrate.migrating_failed_no_addr = أخفق الترحيل. unstar = إزالة النجمة +migrate.migrating_issues = ترحيل البلاغات +migrate.migrating_pulls = ترحيل طلبات السحب code = الكود migrate.invalid_lfs_endpoint = نقطة نهاية LFS غير صالحة. migrate.migrating_failed = فشل الترحيل من %s. @@ -1552,15 +1582,19 @@ find_tag = البحث عن علامة commit_graph = الرسم البياني للإيداع editor.edit_this_file = عدل الملف editor.signoff_desc = أضف مقطورة موقّعة من قِبل المُجرّد في نهاية رسالة سجل الدخول. +n_release_one = %s إصدار symbolic_link = رابط رمزي editor.cannot_edit_lfs_files = لا يمكن تحرير ملفات LFS في واجهة الويب. editor.this_file_locked = الملف مقفل +n_commit_few = %s إيداعات editor.file_delete_success = تم حذف الملف "%s". editor.edit_file = عدّل الملف commit.contained_in_default_branch = هذا الإيداع جزء من الفرع الافتراضي line = سطر lines = أسطر normal_view = عرض عادي +n_branch_one = %s فرع +n_commit_one = %s إيداع view_git_blame = عرض مسؤول تعديل git editor.add_tmpl.filename = اسم الملف editor.name_your_file = اسم ملفك… @@ -1572,9 +1606,14 @@ no_eol.tooltip = هذا الملف لا يحتوي على نهاية لخط ال stored_lfs = مخزن مع Git LFS commit.contained_in = هذا الإيداع موجود في: file_view_rendered = عرض المُخرج النهائي +n_branch_few = %s فروع +n_tag_one = %s علامة +n_tag_few = ‪%s علامات +n_release_few = %s إصدارات file.title = %s عند %s vendored = مضمن file_follow = متابعة الروابط الرمزية + unescape_control_characters = الهروب blame = لوم editor.file_is_a_symlink = `"%s" هو رابط رمزي. لا يمكن تعديل الروابط الرمزية في محرر الويب.` @@ -1793,7 +1832,41 @@ use_onetime_code = استخدم رمزًا لمرة واحدة unauthorized_credentials = بيانات الاعتماد غير صحيحة أو انتهت صلاحيتها. أعد محاولة تنفيذ الأمر أو راجع %s لمزيد من المعلومات [packages] +rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة. +rpm.repository.architectures = بنيات +rpm.repository = معلومات المستودع +settings.delete.notice = أنت على وشك حذف %s (%s). هذه العملية لا رجعة فيها، هل أنت متأكد؟ +settings.link.select = اختر المستودع +settings.link.button = حدّث رابط المستودع +swift.install = اضف الحزمة إلى ملف Package.swift: +settings.delete = حذف الحزمة +settings.link.success = تم تحديث رابط المستودع بنجاح. +title = حزم +details.project_site = موقع المشروع +filter.type = النوع +details.author = الكاتب +details.repository_site = موقع المستودع +settings.link.description = اذا ربطت حزمة مع مستودع، الحزمة سوف تُدرع تحت قائمة الحزم لدى المستودع. +versions = الاصدارات +requirements = المتطلبات +installation = التثبيت +settings.delete.success = تم حذف الحزمة. +keywords = الكلمات المفتاحية +settings.link = اربط هذه الحزمة بمستودع +details.license = الترخيص +filter.type.all = الكل +settings.delete.error = فشل حذف الحزمة. +details = التفاصيل +about = عن هذه الحزمة +settings.link.error = فشل تحديث رابط المستودع. +empty = لا يوجد حزم بعد. +dependency.version = الاصدار +settings.delete.description = إن حذف الحزمة إجراء نهائي ولا يمكن عكسه. desc = إدارة حزم المستودع. +alpine.registry.key = نزّل مفتاح RSA العام للتسجيل في المجلد /etc/apk/keys/ للتحقق من توقيع الفهرس: +generic.download = نزّل الحزمة عبر سطر الأوامر: +filter.container.untagged = غير موسوم +filter.container.tagged = موسوم [heatmap] less = أقل @@ -1813,6 +1886,7 @@ dashboard.sync_repo_tags = زامن الوسوم من بيانات جِت إلى self_check = فحص ذاتي self_check.database_collation_case_insensitive = تستخدم قاعدة البيانات تجميع %s ، وهو تجميع غير حساس. على الرغم من أن فورجيو يمكن أن يعمل معها قد تكون هناك حالات نادرة لا تعمل كما هو متوقع. monitor.process.cancel_desc = قد يسبب إلغاء العملية فقدانًا للبيانات +monitor.queue.type = النوع monitor.process.cancel_notices = أتريد إلغاء: %s؟ dashboard.operations = عمليات الصيانة repositories = المستودعات @@ -1823,6 +1897,7 @@ monitor.desc = الوصف monitor.start = وقت البدء dashboard.system_status = حالة النظام dashboard.delete_generated_repository_avatars = احذف الصورة الرمزية المولّدة للمستودع +monitor.queue.name = الاسم monitor.process.cancel = ألغِ العملية monitor.last_execution_result = النتيجة users = حسابات المستخدمين @@ -1831,11 +1906,14 @@ dashboard.operation_name = اسم العملية notices.type_1 = المستودع notices.desc = الوصف notices.type = النوع +monitor.queue.settings.submit = حدّث الإعدادات +monitor.queue.settings.changed = تم تحديث الإعدادات auths.security_protocol = بروتوكول الأمان auths.port = المنفذ auths.attribute_username_placeholder = اتركه فارغاً لاستخدام اسم المستخدم المُدخل في فورجيو. auths.host = المضيف auths.domain = النطاق +users.list_status_filter.is_restricted = مقيَّد users.reserved = محجوز systemhooks.add_webhook = إضافة خطاف ويب النظام repos.owner = المالك @@ -1863,6 +1941,7 @@ auths.type = النوع users.purge_help = حذف مستخدم بالقوة وكل مستودعاته ومنظماته وحزمه. كل تعليقاته والمسائل التي أنشأها ستُحذف أيضا. users.admin = المدير emails.email_manage_panel = إدارة بريد المستخدم +users.list_status_filter.not_restricted = غير مقيد users.name = اسم المستخدم packages.repository = المستودع orgs.teams = الفِرق @@ -1880,14 +1959,18 @@ packages.package_manage_panel = إدارة الحزم auths.auth_name = اسم الاستيثاق users.details = تفاصيل المستخدم orgs.name = الاسم +users.list_status_filter.is_active = مفعّل users.repos = المستودعات defaulthooks.update_webhook = تحديث خطاف الويب المبدئي users.allow_git_hook = يستطيع عمل خطاطيف جت users.password_helper = اترك كلمة المرور فارغة لإبقائها بلا تغيير. dashboard.update_checker = فاحص التحديث +users.list_status_filter.not_active = معطّل users.update_profile = حدّث حساب المستخدم +users.list_status_filter.not_admin = غير مدير systemhooks = خطاطيف ويب النظام users.never_login = لم يلج قَط +users.list_status_filter.is_admin = مدير users.allow_create_organization = يستطيع إنشاء منظمات users.restricted = مقيَّد users.delete_account = احذف حساب المستخدم @@ -2036,9 +2119,70 @@ relevant_repositories = يتم اظهار المستودعات المتعلقة code_last_indexed_at = فُهرس آخر مرة %s [actions] +variables.none = لا توجد متغيرات بعد. +variables.deletion = أزل المتغير +runners.task_list.run = شغّل +runners.task_list.status = الحالة +runners.task_list.no_tasks = لا توجد مهام بعد. +variables.creation = أضف متغيرا +runners.runner_title = مشغّل +runners.task_list.commit = الإيداع +runners.task_list = المهام الأخيرة على هذا المشغّل +variables.management = إدارة المتغيرات +runners.task_list.repository = المستودع +variables = المتغيرات +variables.deletion.description = إزالة المتغيرات عملية نهائية لا يمكن التراجع عنها. أتريد الاستمرار؟ +status.failure = فشل +runners.status.idle = خامل +runners.task_list.done_at = تم عند +status.running = يعمل +runners.status.active = نشيط +runners.status = الحالة +runners.description = الوصف +runners.update_runner = حدّث التغييرات +runners.name = الاسم +runners.version = النسخة +runs.status = الحالة +status.unknown = مجهول +runners.owner_type = النوع +status.waiting = ينتظر +runners.labels = التصنيفات +runners.status.unspecified = مجهول +runs.commit = إيداع +status.success = نجح runs.empty_commit_message = (رسالة إيداع فارغة) +status.cancelled = ملغي +runs.status_no_select = كل الحالات +runs.scheduled = مُجدوَل +variables.edit = عدّل المتغير +variables.update.success = عُدِّل المتغير. +variables.update.failed = فشل تعديل المتغير. +variables.deletion.failed = فشل حذف المتغير. +variables.creation.failed = فشل إضافة المتغير. +variables.creation.success = تم إضافة المتغير "%s". +variables.deletion.success = تم حذف المتغير. variables.id_not_exist = المتغير ذو المعرّف %d ليس موجودا. +actions = الإجراءات unit.desc = أدر الإجراءات +status.skipped = متخطى +runners = المشغلون +runners.runner_manage_panel = إدارة المشغلين +runners.new = أنشئ مشغلا جديدا +runners.new_notice = كيف تبدأ مشغلا (بالإنجليزية) +runners.id = المعرّف +runners.last_online = آخر مرة كان متصلا +runners.none = لا مشغّل متاح +runners.status.offline = غير متصل +runs.pushed_by = دفعه +runs.no_matching_online_runner_helper = لا يوجد +runners.edit_runner = عدّل المشغّل +runners.update_runner_success = نجح تحديث المشغّل +runners.update_runner_failed = تعذر تحديث المشغّل +runners.delete_runner = احذف هذا المشغّل +runners.delete_runner_success = نجح حذف المشغّل +runners.delete_runner_failed = تعذر حذف المشغّل +runners.delete_runner_header = تأكيد حذف هذا المشغّل +variables.description = تمرر المتغيرات إلى إجراءات معينة ولا يمكن قراءتها بطريقة أخرى. [modal] no = لا @@ -2060,8 +2204,21 @@ changed_filemode = %[1]s → %[2]s symbolic_link = رابط رمزي [dropzone] +invalid_input_type = لا يمكنك رفع ملفات من هذا النوع. +default_message = اسحب الملفات أو اضغط هنا لرفعها. +file_too_big = حجم الملف ({{filesize}} مب) يتعدى الحد الأقصى ({{maxFilesize}} مب). +remove_file = أزل الملف [notification] +notifications = الإشعارات +unread = إلغِ القراءة +pin = ثبت التنبية +mark_as_unread = علّم كغير مقروء +no_read = لا يوجد تنبيهات مقروءه. +mark_as_read = علّم كمقروء +read = اقرأ +no_unread = لا يوجد تنبيهات غير مقروءه. +mark_all_as_read = علّم الكل كمقروء [tool] hours = %d ساعات @@ -2115,6 +2272,12 @@ error.no_unit_allowed_repo = ليس مسموحا لك الوصول إلى أي error.unit_not_allowed = ليس مسموحا لك الوصول إلى هذا القسم في المستودع. [gpg] +default_key = موقّع بالمفتاح المبدئي +error.extract_sign = تعذّر استخراج التوقيع +error.generate_hash = تعذّر إنشاء بصمة الإيداع +error.no_committer_account = لا حساب مرتبط ببريد المودِع +error.not_signed_commit = ليس إيداعًا موقّعًا +error.failed_retrieval_gpg_keys = تعذّر جلب مفتاح مرتبط بحساب المودِع [graphs] component_loading = يحمّل %s... diff --git a/options/locale/locale_arq.ini b/options/locale/locale_arq.ini deleted file mode 100644 index 8b13789179..0000000000 --- a/options/locale/locale_arq.ini +++ /dev/null @@ -1 +0,0 @@ - diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 7e931f061d..6cf85867bc 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -111,6 +111,8 @@ toggle_menu = Превключване на менюто confirm_delete_artifact = Сигурни ли сте, че искате да изтриете артефакта „%s“? more_items = Още елементи twofa_scratch = Резервен код за двуфакторно удостоверяване +webauthn_use_twofa = Използвайте двуфакторен код от телефона си +webauthn_error_insecure = WebAuthn поддържа само сигурни връзки. За тестване през HTTP можете да използвате произход „localhost“ или „127.0.0.1“ error413 = Изчерпали сте квотата си. go_back = Връщане invalid_data = Невалидни данни: %v @@ -121,13 +123,25 @@ show_full_screen = Показване на цял екран show_timestamps = Показване на времеви отпечатъци rerun = Повторно изпълнение copy_type_unsupported = Този тип файл не може да бъде копиран +webauthn_error_unknown = Възникна неизвестна грешка. Моля, опитайте отново. +webauthn_error_unable_to_process = Сървърът не можа да обработи заявката ви. +webauthn_error_empty = Трябва да зададете име за този ключ. +webauthn_error_timeout = Времето за изчакване изтече преди ключът ви да бъде прочетен. Моля, презаредете страницата и опитайте отново. return_to_forgejo = Връщане към Forgejo unknown = Неизвестно confirm_delete_selected = Потвърждавате ли изтриването на всички избрани елементи? +webauthn_insert_key = Поставете вашия ключ за сигурност +webauthn_press_button = Моля, натиснете бутона на вашия ключ за сигурност… +webauthn_sign_in = Натиснете бутона на вашия ключ за сигурност. Ако ключът ви за сигурност няма бутон, поставете го отново. +webauthn_error = Неуспешно прочитане на вашия ключ за сигурност. +webauthn_unsupported_browser = Вашият браузър в момента не поддържа WebAuthn. +webauthn_error_duplicated = Ключът за сигурност не е разрешен за тази заявка. Моля, уверете се, че ключът не е вече регистриран. tracked_time_summary = Обобщение на проследеното време въз основа на филтрите в списъка със задачи + active_stopwatch = Активен тракер за време access_token = Токен за достъп passcode = Паскод + rerun_all = Повторно изпълнение на всички задания download_logs = Изтегляне на дневниците @@ -372,6 +386,7 @@ quota.sizes.wiki = Уики quota.sizes.all = Всички quota.sizes.repos.all = Хранилища quota.sizes.assets.attachments.issues = Прикачени файлове към задачи + openid_deletion = Премахване на OpenID адрес openid_deletion_desc = Премахването на този OpenID адрес от вашия акаунт ще ви попречи да влизате с него. Продължаване? openid_deletion_success = OpenID адресът е премахнат. @@ -430,8 +445,8 @@ access_token_regeneration = Повторно генериране на токе access_token_regeneration_desc = Повторното генериране на токен ще отнеме достъпа до вашия акаунт за приложенията, които го използват. Това не може да бъде отменено. Продължаване? regenerate_token_success = Токенът е генериран повторно. Приложенията, които го използват, вече нямат достъп до вашия акаунт и трябва да бъдат обновени с новия токен. select_permissions = Избор на разрешения -at_least_one_permission = Трябва да изберете поне едно разрешение, за да създадете токен access_token_desc = Избраните разрешения на токена ограничават упълномощаването само до съответните API маршрути. Прочетете документацията за повече информация. +at_least_one_permission = Трябва да изберете поне едно разрешение, за да създадете токен oauth2_confidential_client = Поверителен клиент. Изберете за приложения, които пазят тайната поверителна, като например уеб приложения. Не избирайте за нейтив приложения (настолни и мобилни). oauth2_redirect_uris = URI за пренасочване. Моля, използвайте нов ред за всеки URI. oauth2_client_id = ID на клиента @@ -456,11 +471,168 @@ remove_account_link_success = Свързаният акаунт е премах delete_with_all_comments = Вашият акаунт е създаден преди по-малко от %s. За да се избегнат коментари-призраци, всички коментари към задачи/заявки за сливане ще бъдат изтрити заедно с него. [packages] +container.labels.value = Стойност +alpine.repository.repositories = Хранилища +dependency.version = Версия +title = Пакети +empty = Все още няма пакети. +empty.documentation = За повече информация относно регистъра на пакетите вижте документацията. +container.labels.key = Ключ +requirements = Изисквания +details = Подробности +details.license = Лиценз +container.labels = Етикети +versions = Версии +empty.repo = Качихте ли пакет, но той не се показва тук? Отидете в настройките за пакети и го свържете към това хранилище. +keywords = Ключови думи +details.author = Автор +about = Относно този пакет +settings.delete.success = Пакетът е изтрит. +settings.delete = Изтриване на пакета +container.details.platform = Платформа +settings.delete.error = Неуспешно изтриване на пакет. +installation = Инсталация +versions.view_all = Вижте всички +dependencies = Зависимости +published_by_in = Публикуван %[1]s от %[3]s в %[5]s +published_by = Публикуван %[1]s от %[3]s +generic.download = Изтеглете пакета от командния ред: +container.details.type = Тип образ +alpine.repository = За хранилището +container.images.title = Образи +arch.version.description = Описание +search_in_external_registry = Търсене в %s +filter.type = Тип +filter.container.untagged = Без маркер +filter.type.all = Всички +registry.documentation = За повече информация относно регистъра %s, вижте документацията. +filter.no_result = Вашият филтър не даде резултати. +filter.container.tagged = С маркер +arch.pacman.repo.multi = %s има същата версия в различни дистрибуции. +arch.pacman.helper.gpg = Добавете доверителен сертификат за pacman: +alpine.repository.architectures = Архитектури +arch.version.provides = Доставя +arch.version.groups = Група +details.project_site = Уебсайт на проекта +arch.pacman.conf = Добавете сървър със свързаната дистрибуция и архитектура към /etc/pacman.conf : +arch.pacman.sync = Синхронизирайте пакета с pacman: +details.repository_site = Уебсайт на хранилището +arch.version.depends = Зависимости +arch.version.optdepends = Допълнителни зависимости +arch.version.replaces = Заменя +go.install = Инсталирайте пакета от командния ред: +cargo.registry = Настройте този регистър в конфигурационния файл на Cargo (например ~/.cargo/config.toml): +cargo.install = За да инсталирате пакета с Cargo, изпълнете следната команда: +details.documentation_site = Уебсайт на документацията +arch.version.conflicts = В конфликт +alpine.repository.branches = Клонове +arch.pacman.repo.multi.item = Конфигурация за %s +container.multi_arch = ОС / Архитектура +rpm.repository = Информация за хранилището +container.pull = Издърпайте образа от командния ред: +helm.registry = Настройте този регистър от командния ред: +debian.repository.distributions = Дистрибуции +npm.dependencies.optional = Опционални зависимости +owner.settings.cargo.title = Индекс на регистъра на Cargo +owner.settings.cleanuprules.keep.pattern.container = Версията latest винаги се запазва за Container пакети. +owner.settings.cleanuprules.remove.pattern = Премахване на версии, съответстващи на +rpm.distros.suse = на дистрибуции, базирани на SUSE +owner.settings.cleanuprules.preview.overview = %d пакета са насрочени за премахване. +owner.settings.cleanuprules.preview = Преглед на правило за почистване +arch.version.properties = Свойства на версията +conan.registry = Настройте този регистър от командния ред: conan.details.repository = Хранилище +composer.install = За да инсталирате пакета с Composer, изпълнете следната команда: +chef.install = За да инсталирате пакета, изпълнете следната команда: +chef.registry = Настройте този регистър във вашия файл ~/.chef/config.rb: +pub.install = За да инсталирате пакета с Dart, изпълнете следната команда: +npm.details.tag = Маркер +npm.install = За да инсталирате пакета с npm, изпълнете следната команда: +maven.registry = Настройте този регистър във файла на вашия проект pom.xml: +debian.repository.components = Компоненти +debian.install = За да инсталирате пакета, изпълнете следната команда: +cran.install = За да инсталирате пакета, изпълнете следната команда: +cran.registry = Настройте този регистър във вашия файл Rprofile.site: +rpm.distros.redhat = на дистрибуции, базирани на RedHat +alt.registry = Настройте този регистър от командния ред: +rpm.repository.architectures = Архитектури +alt.registry.install = За да инсталирате пакета, изпълнете следната команда: +alt.setup = Добавете хранилище към списъка със свързани хранилища (изберете необходимата архитектура вместо „_arch_“): +alt.repository = Информация за хранилището +owner.settings.cargo.initialize.error = Неуспешно инициализиране на индекса на Cargo: %v +owner.settings.cargo.initialize = Инициализиране на индекс +settings.delete.description = Изтриването на пакет е трайно и не може да бъде отменено. +alt.repository.multiple_groups = Този пакет е наличен в няколко групи. +alt.repository.architectures = Архитектури +owner.settings.chef.title = Регистър на Chef +owner.settings.cleanuprules.remove.days = Премахване на версии, по-стари от +owner.settings.cleanuprules.keep.pattern = Запазване на версии, съответстващи на owner.settings.cleanuprules.keep.count.n = %d версии на пакет owner.settings.cleanuprules.keep.count.1 = 1 версия на пакет +owner.settings.cleanuprules.keep.count = Запазване на най-новите owner.settings.cleanuprules.enabled = Включено +owner.settings.cleanuprules.preview.none = Правилото за почистване не съвпада с нито един пакет. +owner.settings.cleanuprules.none = Все още няма правила за почистване. +owner.settings.cleanuprules.add = Добавяне на правило за почистване +owner.settings.cleanuprules.title = Правила за почистване +owner.settings.cargo.rebuild.success = Индексът на Cargo беше успешно преизграден. +alpine.registry.key = Изтеглете публичния RSA ключ на регистъра в папката /etc/apk/keys/, за да проверите подписа на индекса: +alpine.registry.info = Изберете $branch и $repository от списъка по-долу. +arch.version.checkdepends = Зависимости за проверката +composer.dependencies = Зависимости +swift.install = Добавете пакета във вашия файл Package.swift: +settings.link.error = Неуспешно обновяване на връзката на хранилището. +swift.install2 = и изпълнете следната команда: +rpm.repository.multiple_groups = Този пакет е наличен в няколко групи. +conda.registry = Настройте този регистър като Conda хранилище във вашия файл .condarc: +conda.install = За да инсталирате пакета с Conda, изпълнете следната команда: +owner.settings.cargo.rebuild.error = Неуспешно преизграждане на индекса на Cargo: %v +owner.settings.cargo.rebuild = Преизграждане на индекс +settings.link.button = Обновяване на връзката на хранилището +settings.link.select = Изберете хранилище +debian.repository.architectures = Архитектури +rpm.registry = Настройте този регистър от командния ред: +debian.registry = Настройте този регистър от командния ред: +helm.install = За да инсталирате пакета, изпълнете следната команда: +swift.registry = Настройте този регистър от командния ред: +settings.link = Свързване на този пакет с хранилище +settings.link.description = Ако свържете пакет с хранилище, пакетът се изброява в списъка с пакети на хранилището. +settings.link.success = Връзката на хранилището беше успешно обновена. +owner.settings.cleanuprules.pattern_full_match = Прилагане на шаблона към пълното име на пакета +owner.settings.cleanuprules.keep.title = Версиите, които съответстват на тези правила, се запазват, дори ако съответстват на правило за премахване по-долу. +debian.repository = Информация за хранилището +maven.install = За да използвате пакета, включете следното в блока dependencies във файла pom.xml: +nuget.install = За да инсталирате пакета с NuGet, изпълнете следната команда: +alt.install = Инсталиране на пакет +owner.settings.cleanuprules.edit = Редактиране на правилото за почистване +rpm.install = За да инсталирате пакета, изпълнете следната команда: +pypi.install = За да инсталирате пакета с pip, изпълнете следната команда: +arch.version.makedepends = Зависимости за изграждането +alpine.install = За да инсталирате пакета, изпълнете следната команда: desc = Управление на пакетите на хранилището. +owner.settings.cargo.rebuild.no_index = Не може да се преизгради, няма инициализиран индекс. +owner.settings.cargo.rebuild.description = Преизграждането може да бъде полезно, ако индексът не е синхронизиран със съхранените Cargo пакети. +owner.settings.cargo.initialize.description = Необходимо е специално Git хранилище за индекс, за да се използва регистърът на Cargo. Използването на тази опция ще (пре)създаде хранилището и ще го конфигурира автоматично. +pypi.requires = Изисква Python +debian.registry.info = Изберете $distribution и $component от списъка по-долу. +alpine.registry = Настройте този регистър, като добавите URL адреса във вашия файл /etc/apk/repositories: +owner.settings.cargo.initialize.success = Индексът на Cargo беше успешно създаден. +npm.registry = Настройте този регистър във файла на вашия проект .npmrc: +owner.settings.chef.keypair = Генериране на двойка ключове +owner.settings.chef.keypair.description = Заявките, изпратени до регистъра на Chef, трябва да бъдат криптографски подписани като средство за удостоверяване. При генериране на двойка ключове, само публичният ключ се съхранява във Forgejo. Частният ключ ви се предоставя, за да се използва с knife. Генерирането на нова двойка ключове ще презапише предишната. +owner.settings.cleanuprules.remove.title = Версиите, които съответстват на тези правила, се премахват, освен ако правило по-горе не казва да се запазят. +nuget.registry = Настройте този регистър от командния ред: +owner.settings.cleanuprules.success.update = Правилото за почистване е обновено. +settings.delete.notice = На път сте да изтриете %s (%s). Тази операция е необратима, сигурни ли сте? +npm.install2 = или го добавете във файла package.json: +owner.settings.cleanuprules.success.delete = Правилото за почистване е изтрито. +vagrant.install = За да добавите Vagrant box, изпълнете следната команда: +nuget.dependency.framework = Целева платформа +maven.install2 = Изпълнете през командния ред: +maven.download = За да изтеглите зависимостта, изпълнете през командния ред: +container.layers = Слоеве на образа +conan.install = За да инсталирате пакета с Conan, изпълнете следната команда: +composer.registry = Настройте този регистър във вашия файл ~/.composer/config.json: [tool] hours = %d часа @@ -533,6 +705,7 @@ forks = Разклонения editor.or = или issues.new_label_desc_placeholder = Описание watch_guest_user = Влезте, за да наблюдавате това хранилище. +migrate_items_milestones = Етапи unstar = Премахване на звездата owner = Притежател issues.num_comments_1 = %d коментар @@ -610,6 +783,7 @@ readme_helper_desc = Това е мястото, където можете да repo_gitignore_helper = Изберете .gitignore шаблони auto_init = Да се инициализира хранилище template.issue_labels = Етикети за задачите +migrate_items_labels = Етикети issues.label_templates.title = Зареждане на предв. зададен набор от етикети issues.label_templates.helper = Изберете предв. зададен набор от етикети projects.template.desc = Шаблон @@ -643,6 +817,8 @@ milestones.new = Нов етап milestones.cancel = Отказ settings.http_method = HTTP метод clone_helper = Нуждаете се от помощ за клониране? Посетете Помощ. +migrate_items_pullrequests = Заявки за сливане +migrate_items_wiki = Уики quick_guide = Бързо ръководство clone_this_repo = Клонирайте това хранилище push_exist_repo = Изтласкване на съществуващо хранилище от командния ред @@ -708,6 +884,7 @@ wiki.desc = Пишете и споделяйте документация със wiki.default_commit_message = Напишете бележка относно това обновяване на страницата (опционално). release.releases = Издания wiki.last_commit_info = %s редактира тази страница %s +migrate_items_releases = Издания release = Издание releases = Издания settings.desc = Настройките са мястото, където можете да управлявате настройките за хранилището @@ -751,7 +928,7 @@ issues.filter_poster = Автор issues.commented_at = `коментира %s` settings.transfer_desc = Прехвърлете това хранилище на потребител или на организация, за които имате администраторски права. settings.archive.button = Архивиране на хранилището -issues.role.owner_helper = Този потребител е притежател на това хранилище. +issues.role.owner_helper = Този потребител е притежателят на това хранилище. settings.delete_notices_2 = - Тази операция ще изтрие перманентно хранилището %s, включително кода, задачите, коментарите, данните на уикито и настройките за сътрудници. settings.admin_settings = Администраторски настройки issues.role.owner = Притежател @@ -887,6 +1064,8 @@ download_tar = Изтегляне на TAR.GZ desc.public = Публично desc.archived = Архивирано desc.internal = Вътрешно +migrate_items_merge_requests = Заявки за сливане +migrate_items_issues = Задачи fork_guest_user = Влезте, за да разклоните това хранилище. actions = Действия more_operations = Още операции @@ -1170,10 +1349,16 @@ activity.git_stats_pushed_1 = е изтласкал activity.git_stats_push_to_branch = към %s и contributors.contribution_type.commits = Подавания stars = Звезди +n_commit_few = %s подавания +n_branch_one = %s клон +n_branch_few = %s клона +n_tag_one = %s маркер +n_tag_few = %s маркера commit_graph = Граф с подавания commits.renamed_from = Преименувано от %s commits.view_path = Преглед на този момент в историята commits.search_branch = Този клон +n_commit_one = %s подаване release.ahead.commits = %d подавания release.stable = Стабилно commits.gpg_key_id = ID на GPG ключ @@ -1296,6 +1481,8 @@ issues.review.option.show_outdated_comments = Показване на остар issues.content_history.delete_from_history_confirm = Да се изтрие ли от историята? project = Проекти issues.content_history.delete_from_history = Изтриване от историята +n_release_few = %s издания +n_release_one = %s издание editor.cannot_edit_non_text_files = Двоични файлове не могат да се редактират през уеб интерфейса. settings.mirror_settings.push_mirror.copy_public_key = Копиране на публичния ключ activity.published_tag_label = Маркер @@ -1424,6 +1611,7 @@ file_follow = Последване на символната връзка commitstatus.failure = Неуспех issues.filter_label_exclude = Използвайте Alt + Click, за да изключите етикети migrate.migrating_failed = Мигрирането от %s е неуспешно. +migrate.migrating_issues = Мигриране на задачи mirror_from = огледално на fork_from_self = Не можете да разклоните хранилище, което притежавате. commit_graph.hide_pr_refs = Скриване на заявките за сливане @@ -1440,9 +1628,12 @@ form.reach_limit_of_creation_1 = Притежателят вече е дости editor.patching = Прилагане на кръпка: editor.fail_to_apply_patch = Неуспешно прилагане на кръпка „%s“ commits.no_commits = Няма общи подавания. „%s“ и „%s“ имат напълно различни истории. +migrate.migrating_pulls = Мигриране на заявки за сливане +migrate.migrating_topics = Мигриране на теми projects.desc = Управлявайте задачи и заявки за сливане в проектни табла. issues.choose.invalid_templates = %v невалидни шаблона са намерени pulls.edit.already_changed = Неуспешно запазване на промените в заявката за сливане. Изглежда съдържанието вече е променено от друг потребител. Моля, презаредете страницата и опитайте да редактирате отново, за да избегнете презаписването на техните промени +migrate.migrating_git = Мигриране на Git данни commits.newer = По-нови issues.choose.blank_about = Създаване на задача от стандартен шаблон. issues.filter_no_results = Няма резултати @@ -1452,6 +1643,8 @@ transfer.no_permission_to_accept = Нямате разрешение да при transfer.no_permission_to_reject = Нямате разрешение да отхвърлите това прехвърляне. editor.file_changed_while_editing = Съдържанието на файла е променено, откакто сте го отворили. Щракнете тук, за да го видите, или Подайте промените отново, за да ги презапишете. sync_fork.button = Синхронизиране +migrate.migrating_labels = Мигриране на етикети +migrate.migrating_releases = Мигриране на издания editor.push_rejected_no_message = Промяната беше отхвърлена от сървъра без съобщение. Моля, проверете Git куките. issues.choose.open_external_link = Отваряне comments.edit.already_changed = Неуспешно запазване на промените в коментара. Изглежда съдържанието вече е променено от друг потребител. Моля, презаредете страницата и опитайте да редактирате отново, за да избегнете презаписването на техните промени @@ -1478,6 +1671,7 @@ editor.commit_id_not_matching = Файлът е променен, докато editor.user_no_push_to_branch = Потребителят не може да изтласква в клона archive.pull.noreview = Това хранилище е архивирано. Не можете да рецензирате заявки за сливане. migrate.migrating_failed.error = Неуспешно мигриране: %s +migrate.migrating_milestones = Мигриране на етапи migrate.failed = Мигрирането е неуспешно: %v pulls.nothing_to_compare_and_allow_empty_pr = Тези клонове са равни. Тази заявка за сливане ще бъде празна. pulls.has_pull_request = `Вече съществува заявка за сливане между тези клонове: %[2]s#%[3]d` @@ -1518,6 +1712,7 @@ issues.time_spent_from_all_authors = `Общо изразходвано врем issues.attachment.download = `Щракнете, за да изтеглите „%s“` issues.attachment.open_tab = `Щракнете, за да видите „%s“ в нов раздел` pulls.update_branch = Обновяване на клона чрез сливане +migrate_items = Елементи за мигриране commit.load_referencing_branches_and_tags = Зареждане на клонове и маркери, препращащи към това подаване pulls.files_conflicted = Тази заявка за сливане има промени, които са в конфликт с целевия клон. pulls.still_in_progress = Все още е в процес на работа? @@ -1539,6 +1734,8 @@ 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 = Не можете да създавате задачи в това хранилище, защото сте блокирани от притежателя на хранилището. @@ -1702,9 +1899,12 @@ diff.image.side_by_side = Едно до друго release.summary_card_alt = Карта с обобщение на издание със заглавие „%s“ в хранилище %s release.asset_external_url = Външен URL адрес error.csv.too_large = Не може да се визуализира този файл, защото е твърде голям. + +commit.cherry-pick = Отбиране pulls.cmd_instruction_checkout_title = Изтегляне pulls.cmd_instruction_merge_title = Сливане -commit.cherry-pick = Отбиране +settings.branches.switch_default_branch = Превключване на стандартния клон +settings.branches.add_new_rule = Добавяне на ново правило settings.pulls.ignore_whitespace = Игнориране на празните знаци при конфликти settings.pulls.enable_autodetect_manual_merge = Включване на автоматично откриване на ръчно сливане (Бележка: В някои специални случаи може да възникнат грешни преценки) settings.pulls.allow_rebase_update = Включване на обновяването на клон на заявка за сливане чрез пребазиране @@ -1766,8 +1966,6 @@ settings.update_githook = Обновяване на куката settings.add_webhook_desc = Forgejo ще изпраща POST заявки с определен Content-Type до целевия URL адрес. Прочетете повече в ръководството за уеб-куки. settings.payload_url = Целеви URL адрес settings.secret = Тайна -settings.branches.switch_default_branch = Превключване на стандартния клон -settings.branches.add_new_rule = Добавяне на ново правило settings.protected_branch.save_rule = Запазване на правилото settings.protected_branch.delete_rule = Изтриване на правилото settings.protect_disable_push = Изключване на изтласкването @@ -1788,6 +1986,7 @@ settings.chat_id = ID на чата settings.thread_id = ID на нишката settings.matrix.room_id = ID на стаята settings.matrix.message_type = Тип съобщение + issues.label_open_issues = %d отворени задачи/заявки за сливане issues.summary_card_alt = Карта с обобщение на задача със заглавие „%s“ в хранилище %s release.type_attachment = Прикачен файл @@ -1811,7 +2010,7 @@ buttons.list.task.tooltip = Добавяне на списък със задач buttons.enable_monospace_font = Включване на равноширок шрифт buttons.mention.tooltip = Споменаване на потребител или екип buttons.italic.tooltip = Добавяне на курсив текст (Ctrl+I / ⌘I) -buttons.link.tooltip = Добавяне на връзка (Ctrl+K / ⌘K) +buttons.link.tooltip = Добавяне на връзка buttons.disable_monospace_font = Изключване на равноширокия шрифт buttons.ref.tooltip = Препратка към задача или заявка за сливане table_modal.label.columns = Колони @@ -1825,6 +2024,7 @@ link_modal.header = Добавяне на връзка buttons.indent.tooltip = Вмъкване на елементи с едно ниво buttons.unindent.tooltip = Изваждане на елементи с едно ниво link_modal.paste_reminder = Подсказка: С URL адрес в клипборда можете да поставите директно в редактора, за да създадете връзка. + link_modal.url = Адрес [org] @@ -2037,6 +2237,7 @@ reply = или отговорете директно на това ел. пис reset_password.text = Ако това сте вие, моля, щракнете върху следната връзка, за да възстановите акаунта си в рамките на %s: primary_mail_change.subject = Основният ви адрес за ел. поща е променен account_security_caution.text_2 = Ако това не сте били вие, акаунтът ви е компрометиран. Моля, свържете се с администраторите на този сайт. + totp_disabled.subject = TOTP е изключен totp_disabled.text_1 = Еднократната парола, базирана на време (TOTP), за вашия акаунт току-що беше изключена. totp_disabled.no_2fa = Вече няма конфигурирани други 2FA методи, което означава, че вече не е необходимо да влизате в акаунта си с 2FA. @@ -2148,6 +2349,7 @@ auths.port = Порт auths.type = Тип config.ssh_config = SSH конфигурация monitor.stats = Статистика +monitor.queue = Опашка: %s config = Конфигурация config.mailer_user = Потребител config.enable_captcha = Включване на CAPTCHA @@ -2157,6 +2359,7 @@ config.git_config = Git конфигурация config.mailer_protocol = Протокол users.bot = Бот config.db_path = Път +monitor.queues = Опашки config.server_config = Сървърна конфигурация packages.size = Размер settings = Админ. настройки @@ -2181,7 +2384,9 @@ packages.total_size = Общ размер: %s dashboard.new_version_hint = Forgejo %s вече е наличен, вие изпълнявате %s. Проверете блога за повече подробности. total = Общо: %d config.db_type = Тип +monitor.queue.type = Тип notices.type = Тип + users.prohibit_login = Замразен акаунт [error] @@ -2271,6 +2476,7 @@ repository_files_already_exist.delete = Вече съществуват файл invalid_gpg_key = Не може да се потвърди вашият GPG ключ: %s git_ref_name_error = ` трябва да е правилно форматирано име на Git препратка.` last_org_owner = Не можете да премахнете последния потребител от екипа на „притежателите“. Трябва да има поне един притежател за организация. + AccessToken = Токен за достъп CommitChoice = Избор на подаване username_claiming_cooldown = Потребителското име не може да бъде взето, тъй като периодът му на изчакване все още не е приключил. То може да бъде взето на %[1]s. @@ -2369,6 +2575,7 @@ change_unconfirmed_email_error = Неуспешна промяна на адре resend_mail = Щракнете тук, за повторно изпращане на ел. писмо за активация change_unconfirmed_email_summary = Промяна на адреса, на който се изпраща ел. писмо за активация. change_unconfirmed_email = Ако сте въвели грешен адрес за ел. поща по време на регистрацията, можете да го промените по-долу и потвърждение ще бъде изпратено на новия адрес. + prohibit_login = Акаунтът е замразен prohibit_login_desc = Вашият акаунт е замразен и не може да взаимодейства с инстанцията. Свържете се с администратора, за да възстановите достъпа си. non_local_account = Нелокални потребители не могат да обновяват паролата си чрез уеб интерфейса на Forgejo. @@ -2409,6 +2616,18 @@ platform_desc = Forgejo работи на свободни операционн license_desc = Вземете Forgejo! Присъединете се към нас, допринасяйки, за да направите този проект още по-добър. Не се колебайте да сътрудничите! [notification] +subscriptions = Абонаменти +unread = Непрочетени +no_subscriptions = Няма абонаменти +mark_as_unread = Отбелязване като непрочетено +no_read = Няма прочетени известия. +mark_as_read = Отбелязване като прочетено +notifications = Известия +read = Прочетени +watching = Наблюдавани +no_unread = Няма непрочетени известия. +mark_all_as_read = Отбелязване на всички като прочетени +pin = Закачване на известието [explore] go_to = Отиване към @@ -2421,8 +2640,34 @@ relevant_repositories = Показани са само подходящи хра relevant_repositories_tooltip = Хранилищата, които са разклонения или нямат тема, икона или описание, са скрити. [actions] +runners.version = Версия +variables = Променливи +runners.labels = Етикети +actions = Действия +variables.none = Все още няма променливи. +variables.creation.failed = Неуспешно добавяне на променлива. +variables.update.failed = Неуспешно редактиране на променлива. +variables.creation.success = Променливата „%s“ е добавена. +variables.deletion.success = Променливата е премахната. +variables.edit = Редактиране на променливата +variables.deletion = Премахване на променливата +variables.update.success = Променливата е редактирана. +variables.creation = Добавяне на променлива +variables.deletion.failed = Неуспешно премахване на променлива. +runners.task_list.repository = Хранилище +runners.description = Описание runs.no_workflows.help_no_write_access = За да научите повече за Forgejo Actions, вижте документацията. +variables.management = Управление на променливи +variables.not_found = Променливата не е открита. variables.id_not_exist = Променлива с идентификатор %d не съществува. +runners.owner_type = Тип +status.cancelled = Отменено +status.running = Изпълнява се +status.success = Успешно +status.waiting = Изчаква се +status.unknown = Неизвестно +status.failure = Неуспешно +status.skipped = Пропуснато unit.desc = Управление на интегрирани CI/CD pipelines с Forgejo Actions. [heatmap] @@ -2444,6 +2689,10 @@ submodule = Подмодул [dropzone] +default_message = Пуснете файлове тук или щракнете, за качване. +remove_file = Премахване на файла +file_too_big = Размерът на файла ({{filesize}} MB) надвишава максималния размер от ({{maxFilesize}} MB). +invalid_input_type = Не можете да качвате файлове от този тип. [graphs] component_loading_failed = Неуспешно зареждане на %s @@ -2484,17 +2733,37 @@ keyword_search_unavailable = Търсенето по ключова дума в union_tooltip = Включване на резултати, които съвпадат с някоя от ключовите думи, разделени с интервал union = Обединение type_tooltip = Тип търсене + runner_kind = Търсене на изпълнители… [markup] +filepreview.lines = Редове от %[1]d до %[2]d в %[3]s +filepreview.line = Ред %[1]d в %[2]s [munits.data] +b = Б +kib = КиБ +mib = МиБ +gib = ГиБ +tib = ТиБ +pib = ПиБ +eib = ЕиБ + [translation_meta] test = окей [gpg] +default_key = Подписано с ключ по подразбиране +error.no_gpg_keys_found = Не е намерен известен ключ за този подпис в базата данни +error.not_signed_commit = Не е подписано подаване +error.generate_hash = Неуспешно генериране на хеш на подаването +error.extract_sign = Неуспешно извличане на подпис +error.probable_bad_signature = ВНИМАНИЕ! Въпреки че има ключ с това ID в базата данни, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. +error.failed_retrieval_gpg_keys = Неуспешно извличане на ключ, свързан с акаунта на подаващия +error.probable_bad_default_signature = ВНИМАНИЕ! Въпреки че ключът по подразбиране има това ID, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. +error.no_committer_account = Няма акаунт, свързан с адреса за ел. поща на подаващия [repo.permissions] projects.read = Четене: Достъп до проектните табла на хранилището. diff --git a/options/locale/locale_bn.ini b/options/locale/locale_bn.ini index 1b90534602..ebcad77f97 100644 --- a/options/locale/locale_bn.ini +++ b/options/locale/locale_bn.ini @@ -6,6 +6,6 @@ explore = এক্সপ্লোর logo = লোগো sign_in = সাইন ইন sign_in_or = বা -sign_out = সাইন আউট sign_in_with_provider = %s দিয়ে সাইন-ইন করুন +sign_out = সাইন আউট sign_up = নিবন্ধন করুন \ No newline at end of file diff --git a/options/locale/locale_ca.ini b/options/locale/locale_ca.ini index fc77e81569..be758e072c 100644 --- a/options/locale/locale_ca.ini +++ b/options/locale/locale_ca.ini @@ -37,6 +37,17 @@ captcha = CAPTCHA twofa = Autenticació de doble factor 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_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_empty = S'ha d'anomenar aquesta clau. repository = Repositori organization = Organització mirror = Mirall @@ -74,6 +85,7 @@ disabled = Deshabilitat filter.public = Públic filter.private = Privat show_full_screen = Mostra a pantalla completa +webauthn_error_timeout = Temps d'espera finalitzar abans que la seva clau pogués ser llegida. Siusplau recarregueu la pàgina i torneu-ho a intentar. remove_label_str = Esborra l'element "%s" error413 = Ha exhaurit la quota. cancel = Canceŀlar @@ -402,7 +414,7 @@ buttons.quote.tooltip = Citar text buttons.enable_monospace_font = Habilitar la font monoespai buttons.disable_monospace_font = Deshabilita la font monoespai buttons.code.tooltip = Afegir codi -buttons.link.tooltip = Afegir un enllaç (Ctrl+K / ⌘K) +buttons.link.tooltip = Afegir un enllaç buttons.list.unordered.tooltip = Afegir un llista de punts buttons.list.ordered.tooltip = Afegir una llista enumerada buttons.list.task.tooltip = Afegir una llista de tasques @@ -604,12 +616,15 @@ username_error_no_dots = ` només pot contenir caràcters alfanumèrics ("0-9"," username_claiming_cooldown = No es pot reclamar el nom d'usuari perquè el seu període de temps de recuperació encara no ha acabat. Es podrà reclamar el %[1]s. invalid_group_team_map_error = ` el mapatge no és vàlid: %s` repository_force_private = S'ha activat "Forçar privat": els repositoris privats no es poden fer públics. + 2fa_auth_required = L'accés remot requereix una autenticació de doble factor. + visit_rate_limit = S'ha sobrepassat la taxa de visita remota. unset_password = L'usuari no ha establert una contrasenya. -invalid_ssh_principal = Principal invàlid: %s + team_no_units_error = Permet l'accés a una secció del repositori com a mínim. unsupported_login_type = El tipus d'accés no permet eliminar el compte. +invalid_ssh_principal = Principal invàlid: %s [settings] pronouns = Pronoms @@ -874,6 +889,7 @@ visibility.public = Públic visibility.limited = Limitat visibility.private = Privat primary = Principal + lookup_avatar_by_mail = Cercar l'avatar amb l'adreça de correu electrònic access_token_desc = Els permisos de testimoni seleccionats es limiten a les rutes API corresponents. Llegiu la documentació per a més informació. oauth2_confidential_client = Client confidencial. Seleccioneu aquesta opció per a aplicacions que mantinguin el secret confidencial, com les aplicacions web. No la seleccioneu pas per a aplicacions nadiues, tant d'escriptori com mòbils. @@ -894,19 +910,12 @@ quota.rule.no_limit = Il·limitat quota.sizes.all = Tot quota.sizes.assets.all = Recursos quota.sizes.wiki = Wiki + user_block_yourself = No us podeu bloquejar. quota.applies_to_user = Les regles de quota següents s'apliquen al vostre compte quota.applies_to_org = Les regles de quota següents s'apliquen a aquesta organització quota.rule.exceeded = Sobrepassat -manage_ssh_principals = Gestiona els Certificats de Principals SSH -principal_desc = Aquests certificats principals d'SSH són associats al vostre compte i permeten accés complet als vostres repositoris. -add_new_principal = Afegir principal -ssh_principal_been_used = Aquest principal ja s'ha afegit al servidor. -add_principal_success = S'ha afegit el certificat principal SSH "%S". -ssh_principal_deletion = Eliminar Certificat Principal SSH -ssh_principal_deletion_desc = Eliminar un Certificat Principal SSH revoca el seu accés al vostre compte. Continuar? -ssh_principal_deletion_success = S'ha eliminat el principal. -principal_state_desc = Aquest principal s'ha usat en els darrers 7 dies + change_username_redirect_prompt.with_cooldown.one = El nom d'usuari antic estarà disponible per a tothom després d'%[1]d dia. Podeu reclamar-lo abans que passi aquest temps. change_username_redirect_prompt.with_cooldown.few = El nom d'usuari antic estarà disponible per a tothom després de %[1]d dies. Podeu reclamar-lo abans que passi aquest temps. additional_repo_units_hint_description = Mostra un suggeriment per "Habilitar-ne més" pels repositoris que no tenen habilitades totes les unitats. @@ -916,13 +925,22 @@ primary_email = Fer-la principal activate_email = Enviar l'activació email_preference_set_success = S'ha configurat correctament la preferència de correu electrònic. keep_email_private_popup = La vostra adreça de correu electrònic no es mostrarà al vostre perfil i no serà la predeterminada pels commits fets mitjançat la interfície web, com ara pujades de fitxers, modificacions i commits de fusió. En el seu lloc, una adreça especial %s es pot fer servir per enllaçar commits al vostre compte. Aquesta opció no afectarà els commits ja existents. +manage_ssh_principals = Gestiona els Certificats de Principals SSH add_key = Afegir una clau +principal_desc = Aquests certificats principals d'SSH són associats al vostre compte i permeten accés complet als vostres repositoris. +add_new_principal = Afegir principal +ssh_principal_been_used = Aquest principal ja s'ha afegit al servidor. gpg_key_matched_identities_long = Les identitats incrustades en aquesta clau coincideixen amb les següents adreces de correu electrònic activades per aquest usuari. Els commits coincidents amb aquestes adreces de correu electrònic es poden verificar amb aquesta clau. gpg_token_signature = Firma GPG blindada ssh_token_signature = Firma SSH blindada +add_principal_success = S'ha afegit el certificat principal SSH "%S". +ssh_principal_deletion = Eliminar Certificat Principal SSH ssh_key_deletion_desc = Eliminar una clau SSH en revocarà l'accés al vostre compte. Voleu continuar? gpg_key_deletion_desc = Eliminar una clau GPG des-verificarà els commits firmats per ella. Voleu continuar? +ssh_principal_deletion_desc = Eliminar un Certificat Principal SSH revoca el seu accés al vostre compte. Continuar? +ssh_principal_deletion_success = S'ha eliminat el principal. valid_until_date = Vàlid fins %s +principal_state_desc = Aquest principal s'ha usat en els darrers 7 dies repo_and_org_access = Accés al repositori i a l'organització webauthn_delete_key = Eliminar la clau de seguretat webauthn_alternative_tip = Segurament voldreu configurar un mètode d'autenticació addicional. @@ -1118,6 +1136,11 @@ template.webhooks = Webhooks template.topics = Temes template.avatar = Avatar need_auth = Autorització +migrate_items_wiki = Wiki +migrate_items_milestones = Fites +migrate_items_labels = Etiquetes +migrate_items_issues = Problemes +migrate_items_releases = Publicacions unwatch = Deixa de seguir watch = Segueix unstar = Treu l'estrella @@ -1454,7 +1477,7 @@ issues.ref_closing_from = `ha fet referència a aquesta incidèn issues.ref_reopening_from = `ha fet referència a aquesta incidència en una sol·licitud d'extracció %[3]s que la reobrirà, %[1]s` issues.author.tooltip.issue = Aquest usuari és l'autor d'aquesta incidència. issues.author.tooltip.pr = Aquest usuari és l'autor d'aquesta sol·licitud d'extracció. -issues.role.owner_helper = Aquest usuari és un propietari d'aquest repositori. +issues.role.owner_helper = Aquest usuari és el propietari d'aquest repositori. issues.role.member_helper = Aquest usuari és membre de la organització a la qual pertany aquest repositori. issues.role.collaborator_helper = S'ha convidat aquest usuari a col·laborar al repositori. issues.role.first_time_contributor = Col·laborador per primera vegada @@ -1604,6 +1627,7 @@ activity.git_stats_addition_n = %d addicions activity.git_stats_deletion_1 = %d eliminació activity.git_stats_deletion_n = %d eliminacions settings.mirror_settings.docs = Configureu el vostre repositori per sincronitzar commits, etiquetes i branques automàticament amb un altre repositori. + rss.must_be_on_branch = Heu d'estar en una branca per a tenir un canal RSS. admin.manage_flags = Gestiona les marques admin.enabled_flags = Marques habilitades del repositori: @@ -1635,6 +1659,8 @@ form.string_too_long = El text introduït té més de %d caràcters. migrate_options = Opcions de migració migrate_options_mirror_helper = Aquest repositori serà un mirall migrate_options_lfs_endpoint.description.local = També s'accepta un camí al servidor local. +migrate_items = Elements de migració + open_with_editor = Obre amb %s mirror_prune_desc = Elimina les referències de seguiment remot obsoletes mirror_sync_on_commit = Sincronitza quan es pugin commits @@ -1661,10 +1687,388 @@ migrate_options_lfs = Migra els fitxers LFS migrate_options_lfs_endpoint.label = Punt final LFS migrate_options_lfs_endpoint.description = En migrar, s'intentarà utilitzar el vostre remot git per a determinar el servidor LFS. També podeu especificar un punt final personalitzat si les dades LFS del repositori són en un altre lloc. migrate_options_lfs_endpoint.placeholder = Si ho deixeu en blanc, el punt final derivarà de l'URL de clonació +migrate_items_pullrequests = Pull requests + +fork_repo = Bifurca el repositori +fork_from = Bifurcar des de +repo_gitignore_helper_desc = Escolliu de quins fitxers no s'ha de fer seguiment d'una llista de plantilles per llenguatges comuns. Els artefactes típics generats per les eines de construcció de cada llenguatge estan incloses al .gitignore de manera predeterminada. +readme_helper_desc = Aquí podeu escriure una descripció completa del vostre projecte. +default_branch_helper = La branca per defecte és la branca base pels pull requests i els commits. +mirror_interval = Interval de rèplica (les unitats de temps vàlides són "h", "m", "s"). 0 per desactivar la sincronització periòdica. (Interval mínim: %s) +mirror_use_ssh.helper = Si seleccioneu aquesta opció, Forgejo replicarà el repositori mitjançant Git per SSH i us crearà una parella de claus. Heu d'assegurar-vos que la clau pública generada estigui autoritzada per publicar al repositori de destí. No podreu fer servir autenticació basada en contrasenyes. +delete_preexisting_success = S'ha suprimit els fitxers no-adoptats a %s +blame_prior = Veure el «blame» anterior a aquest canvi +blame.ignore_revs = S'ignoren les revisions a .git-blame-ignore-revs. Fes clic aquí per veure-les. +blame.ignore_revs.failed = No s'han pogut ignorar les revisions a .git-blame-ignore-revs. +author_search_tooltip = Es mostra un màxim de 30 usuaris +summary_card_alt = Resum del repositori %s +tree_path_not_found.commit = El camí %[1]s no existeix al commit %[2]s +tree_path_not_found.branch = El camí %[1]s no existeix a la branca %[2]s +tree_path_not_found.tag = El camí %[1]s no existeix a l'etiqueta %[2]s +transfer.accept = Accepta la transferència +transfer.accept_desc = Transfereix a "%s" +transfer.reject = Denega la transferència +transfer.reject_desc = Cancel·la la transferència a "%s" +transfer.no_permission_to_accept = No teniu permisos per acceptar aquesta transferència. +transfer.no_permission_to_reject = No teniu permisos per denegar aquesta transferència. +template.items = Ítems de la plantilla +template.git_content = Contingut Git (Branca predeterminada) +template.git_hooks = Ganxos Git +migrate_items_merge_requests = Sol·licituds de fusió +migrate_repo = Migra el repositori +migrate.repo_desc_helper = Deixa-ho buit per importar la descripció existent +migrate.clone_address = Migra / Clona des d'una URL +migrate.clone_address_desc = L'URL d'HTTP(S) o de Git "clone" d'un repositori ja existent +migrate.github_token_desc = Podeu posar un o més testimonis separats amb comes per fer que la migració sigui més ràpida, evitant així el límit de taxa de l'API de GitHub. ATENCIÓ: Abusar aquesta funció pot anar en contra de la política de servei del proveïdor i pot comportar el bloqueig del/s vostre/s compte/s. +migrate.clone_local_path = o el camí a un servidor local +migrate.permission_denied = No podeu importar repositoris locals. +migrate.permission_denied_blocked = No podeu importar des d'amfitrions rebutjats, si us plau, demaneu a l'administrador que comprovi les configuracions ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. +migrate.invalid_local_path = El camí local és invàlid. No existeix o no és un directori. +migrate.invalid_lfs_endpoint = El punt final LFS no és vàlid. +migrate.failed = La migració ha fallat: %v +migrate.migrate_items_options = Cal un testimoni d'accés per migrar els ítems addicionals +migrated_from = Migrat des de %[2]s +migrated_from_fake = Migrat des de %[1]s +migrate.migrate = Migra des de %s +migrate.migrating = S'està migrant des de %s … +migrate.migrating_failed = La migració des de %s ha fallat. +migrate.migrating_failed.error = No s'ha pogut migrar: %s +migrate.migrating_failed_no_addr = La migració ha fallat. +migrate.migrating_git = Migrant dades Git +migrate.migrating_topics = Migrant tòpics +migrate.migrating_milestones = Migrant fites +migrate.migrating_labels = Migrant etiquetes +migrate.migrating_releases = Migrant publicacions +migrate.migrating_issues = Migrant problemes +migrate.migrating_pulls = Migrant «pull requests» +migrate.cancel_migrating_title = Cancel·la la migració +migrate.cancel_migrating_confirm = Voleu cancel·lar aquesta migració? +mirror_from = mirall de +forked_from = bifurcat des de generated_from = generat des de +fork_from_self = No podeu bifurcar un repositori que us pertany. +fork_guest_user = Inicia sessió per bifurcar aquest repositori. +watch_guest_user = Inicia sessió per vigilar aquest repositori. +star_guest_user = Inicia sessió per destacar aquest repositori. +subscribe.issue.guest.tooltip = Inicia sessió per subscriure't a aquest problema. +subscribe.pull.guest.tooltip = Inicia sessió per subscriure't a aquesta «pull request». +more_operations = Més operacions +no_desc = Sense descripció +quick_guide = Guia ràpida +clone_this_repo = Clona aquest repositori +cite_this_repo = Cita aquest repositori +create_new_repo_command = Crea un nou repositori a la línia de comandes +push_exist_repo = Puja un repositori ja existent des de la línia de comandes +empty_message = Aquest repositori està buit. +broken_message = Les dades Git d'aquest repositori no es poden llegir. Contacta amb l'administrador d'aquesta instància o suprimeix aquest repositori. +code.desc = Accedeix al codi font, els fitxers, els commits i les branques. +clear_ref = `Esborra la referència actual` +filter_branch_and_tag = Filtra per branca o etiqueta +find_tag = Cerca etiqueta +n_commit_one = %s commit +n_commit_few = %s commits +n_branch_one = Branca %s +n_branch_few = Branques %s +n_tag_one = Etiqueta %s +n_tag_few = Etiquetes %s +n_release_one = Publicació %s +n_release_few = Publicacions %s +file.title = %s a %s +file_follow = Seguir l'enllaç simbòlic +file_view_rendered = Veure renderitzat +file_view_raw = Veure cru +escape_control_characters =Escapar +commit_graph = Gràfic de commits +commit.contained_in = Aquest commit és a: +no_eol.text = Sense EOL +editor.commit_signed_changes = Fes un commit signat dels canvis +editor.commit_changes = Fes un commit dels canvis +editor.add_tmpl.filename = nom del fitxer editor.update = Actualitzar %s -diff.whitespace_ignore_at_eol = Ignorar els canvis en els espais en blanc al final de la línia -diff.stats_desc = %d fitxers modificats amb %d afegits i %d supressions +editor.patch = Aplicar pedaç +editor.patching = Aplicant el pedaç a: +editor.signoff_desc = Afegeix un "Signat per" seguit de l'autor al final del missatge del commit. +editor.commit_directly_to_this_branch = Feu un commit directament a la branca %[1]s. +editor.new_branch_name = Anomena la nova branca per a aquest commit +editor.file_already_exists = Ja hi ha un fitxer anomenat "%S" en aquest repositori. +editor.commit_id_not_matching = El fitxer ha canviat mentre l'editaves. Fes un commit a una nova branca i llavors fusiona-la. +editor.push_out_of_date = La pujada està desactualitzada. +editor.commit_empty_file_header = Fes un commit d'un fitxer buit +editor.user_no_push_to_branch = L'usuari no pot pujar a la branca +editor.cherry_pick = Triar a dit %s a: +editor.revert = Retorna %s a: +editor.commit_email = Adreça electrònica del commit +commits.no_commits = No hi ha commits en comú. "%s" i "%s" tenen històries completament diferents. +commits.signed_by_untrusted_user_unmatched = Signat per un usuari no fiable que no coincideix amb l'autor del commit +commits.view_path = Veure en aquest punt de la història +commits.view_single_diff = Veure els canvis fets en aquest fitxer introduïts en aquest commit +commit.revert-header = Retorna: %s +commit.revert-content = Selecciona una branca per retornar-hi: +commit.cherry-pick = Triar a dit +commit.cherry-pick-header = Triar a dit: %s +commit.cherry-pick-content = Seleccioneu una branca per triar-hi: +projects.new_subheader = Coordineu, feu seguiment, i actualitzeu el vostre treball en un sol lloc, per tal que els projectes es mantinguin transparents i dins del calendari previst. +projects.type.basic_kanban = Kanban bàsic +projects.type.bug_triage = Triatge d'errors +projects.column.set_default = Definir com a predeterminada +projects.column.set_default_desc = Estableix aquesta columna com a predeterminada per als problemes i pulls sense categoria +projects.column.assigned_to = Assignat a +projects.card_type.desc = Previsualització de targeta +issues.filter_assignees = Filtrar assignats +issues.filter_reviewers = Filtrar revisor +issues.new.open_projects = Projectes oberts +issues.new.open_milestone = Fites obertes +issues.new.clear_assignees = Esborra assignats +issues.new.no_assignees = Sense assignats +issues.new.assign_to_me = Assigna-m'ho +issues.new.no_reviewers = Sense revisors +issues.edit.already_changed = No s'han pogut desar els canvis al problema. Sembla que un altre usuari n'ha modificat el contingut. Si us plau, refresqueu la pàgina i intenteu-ho un altre cop per evitar reescriure els seus canvis +issues.choose.get_started = Començar +issues.choose.blank_about = Crea un problema a partir de la plantilla per defecte. +issues.choose.invalid_config = La configuració del problema té errors: +issues.label_templates.use = Utilitza una etiqueta predeterminada +issues.label_templates.fail_to_load_file = No s'ha pogut carregar el fitxer d'etiqueta predeterminada "%s": %v +issues.add_label = s'ha afegit l'etiqueta %s %s +issues.add_labels = s'han afegit les etiquetes %s %s +issues.remove_label = s'ha esborrat l'etiqueta %s %s +issues.remove_labels = s'han esborrat les etiquetes %s %s +issues.add_remove_labels = s'han afegit %s etiquetes i esborrat %s %s +issues.add_milestone_at = `s'ha afegit a la fita %s %s` +issues.add_project_at = `s'ha afegit al projecte %s %s` +issues.change_milestone_at = `s'ha modificat la fita des de %s a %s %s` +issues.change_project_at = `s'ha modificat el projecte des de %s a %s %s` +issues.remove_milestone_at = `s'ha eliminat de la fita %s %s` +issues.remove_project_at = `s'ha eliminat del projecte %s %s` +issues.self_assign_at = `s'ha auto-assignat aquest %s` +issues.add_assignee_at = `va ser assignat per %s %s` +issues.remove_assignee_at = `va ser desassignat per %s %s` +issues.remove_self_assignment = `s'ha eliminat la seva tasca %s` +issues.change_title_at = `s'ha canviat el títol de %s a %s %s` +issues.change_ref_at = `s'ha canviat la referència de %s a %s %s` +issues.remove_ref_at = `s'ha eliminat la referència %s %s` +issues.add_ref_at = `s'ha afegit la referència %s %s` +issues.delete_branch_at = `s'ha eliminat la branca %s %s` +issues.filter_label_no_select = Totes les etiquetes +issues.filter_label_select_no_label = Sense etiqueta +issues.filter_milestone_none = Sense fites +issues.filter_milestone_open = Fites obertes +issues.filter_milestone_closed = Fites tancades +issues.filter_project_all = Tots els projectes +issues.filter_project_none = Sense projecte +issues.filter_assginee_no_select = Tots els assignats +issues.filter_assginee_no_assignee = Sense assignat +issues.filter_poster_no_select = Tots els autors +issues.filter_type.all_pull_requests = Totes les «pull requests» +issues.filter_type.all_issues = Tots els problemes +issues.filter_type.assigned_to_you = Tasques que tens assignades +issues.filter_type.created_by_you = Creat per tu +issues.filter_type.mentioning_you = Que et mencionen +issues.filter_type.review_requested = S'ha demanat la revisió +issues.filter_type.reviewed_by_you = Revisats per tu +issues.filter_sort.relevance = Rellevància +issues.filter_sort.recentupdate = Actualitzats recentment +issues.filter_sort.leastupdate = Actualitzats menys recentment +issues.filter_sort.mostcomment = Les més comentades +issues.filter_sort.leastcomment = Menys comentats +issues.filter_sort.nearduedate = A prop de la data de venciment +issues.filter_sort.farduedate = Lluny de la data de venciment +issues.filter_sort.moststars = Més destacats +issues.filter_sort.feweststars = Menys destacats +issues.filter_sort.mostforks = Més bifurcacions +issues.filter_sort.fewestforks = Menys bifurcacions +issues.action_assignee_no_select = Sense assignat +pulls.merged_by = per %[3]s s'ha fusionat %[1]s +issues.closed_by = per %[3]s s'ha tancat %[1]s +issues.context.menu = Menú de comentari +issues.comment_pull_merged_at = s'ha fusionat el commit %[1]s a %[2]s %[3]s +issues.comment_manually_pull_merged_at = s'ha fusionat el commit %[1]s manualment a %[2]s %[3]s +issues.role.contributor_helper = Aquest usuari ja ha fet commits en aquest repositori. +issues.is_stale = Hi ha hagut canvis a aquest PR des de la revisió +issues.label_exclusive_desc = Doneu nom a l'àmbit/ítem de l'etiqueta per fer-la mútuament exclusiva amb altres àmbits/ etiquetes. +issues.label_exclusive_warning = Qualsevol etiqueta amb àmbits conflictius s'esborrarà quan s'editin les etiquetes d'un problema o «pull request». +issues.archived_label_description = (Arxivats) %s +issues.label.filter_sort.reverse_alphabetically = Capgira-ho alfabèticament +issues.label.filter_sort.by_size = Mida més petita +issues.label.filter_sort.reverse_by_size = Mida més gran +issues.num_participants_one = %d participant +issues.num_participants_few = %d participants +issues.tracker = Rastrejador de temps +issues.start_tracking_short = Inicia el temporitzador +issues.start_tracking = Inicia el rastreig de temps +issues.start_tracking_history = `s'ha començat %s` +issues.tracker_auto_close = El temporitzador s'aturarà automàticament quan el problema s'hagi resolt +issues.tracking_already_started = `Ja has començat a rastrejar el temps en un altre problema!` +issues.stop_tracking = Atura el temporitzador +issues.stop_tracking_history = `s'ha aturat %s` +issues.cancel_tracking_history = `s'ha cancel·lat el rastreig de temps %s` +issues.add_time = Afegeix el temps manualment +issues.del_time = Elimina aquest registre de temps +issues.add_time_short = Afegeix temps +issues.add_time_history = `s'ha afegit temps passat %s` +issues.del_time_history = `s'ha eliminat temps passat %s` +issues.add_time_sum_to_small = No s'ha introduït temps. +issues.time_spent_from_all_authors = `Temps total: %s` +issues.due_date = Data de venciment +issues.push_commit_1 = s'ha afegit %d commit %s +issues.push_commits_n = s'ha afegit %d commits %s +issues.force_push_codes = `s'ha forçat la pujada %[1]s des de %[2]s %[8]s a %[4]s %[9]s %[6]s` +issues.due_date_not_set = No s'ha establert una data de venciment. +issues.due_date_added = s'ha afegit la data de venciment %s %s +issues.due_date_modified = s'ha modificat la data de venciment de %[2]s a %[1]s %[3]s +issues.due_date_remove = s'ha eliminat la data de venciment %s %s +issues.due_date_invalid = La data de venciment no és vàlida o és fora de rang. Si us plau, useu el format "aaaa-mm-dd". +issues.dependency.issue_no_dependencies = No s'han establert dependències. +issues.dependency.pr_no_dependencies = No s'ha establert cap dependència. +issues.dependency.no_permission_1 = No teniu permisos per llegir %d dependència +issues.dependency.no_permission_n = No teniu permisos per llegir %d dependències +issues.dependency.no_permission.can_remove = No teniu permisos per llegir aquesta dependència, però la podeu eliminar +issues.dependency.add = Afegir dependència… +issues.dependency.remove_info = Eliminar aquesta dependència +issues.dependency.added_dependency = `s'ha afegit una nova dependència %s` +issues.dependency.removed_dependency = `s'ha eliminat una dependència %s` +issues.dependency.pr_closing_blockedby = No es pot tancar aquesta «pull request» perquè té els següents problemes +issues.dependency.issue_closing_blockedby = No es pot tancar aquest problema perquè té els següents problemes +issues.dependency.issue_close_blocks = Aquest problema bloqueja el tancament d'aquests altres problemes +issues.dependency.pr_close_blocks = Aquesta «pull request» bloqueja el tancament d'aquests problemes +issues.dependency.issue_close_blocked = Heu de tancar tots els problemes que en bloquegen el tancament d'aquest. +issues.dependency.issue_batch_close_blocked = No es poden tancar tots els problemes marcats perquè el problema #%d encara té dependències obertes +issues.dependency.pr_close_blocked = Heu de tancar tots els problemes que bloquegen aquesta «pull request» abans de fusionar-la. +issues.dependency.blocked_by_short = Depèn de +issues.dependency.remove_header = Eliminar dependència +issues.dependency.issue_remove_text = Això eliminarà la dependència d'aquest problema. Continuar? +issues.dependency.pr_remove_text = Això eliminarà la dependència d'aquesta «pull request». Continuar? +issues.dependency.setting = Activa les dependències per a problemes i «pull requests» +issues.dependency.add_error_same_issue = No podeu fer que un problema depengui d'ell mateix. +issues.dependency.add_error_dep_issue_not_exist = El problema que en depèn no existeix. +issues.dependency.add_error_dep_not_exist = La dependència no existeix. +issues.dependency.add_error_dep_exists = La dependència ja existeix. +issues.dependency.add_error_cannot_create_circular = No podeu crear una dependència amb dos problemes que es bloquegen mútuament. +issues.dependency.add_error_dep_not_same_repo = Els dos problemes han d'ésser al mateix repositori. +issues.review.self.approval = No podeu aprovar la vostra «pull request». +issues.review.self.rejection = No podeu sol·licitar canvis en la vostra «pull request». +issues.review.approve = ha aprovat aquests canvis %s +issues.review.comment = ha revisat %s +issues.review.dismissed = ha rebutjat la revisió de %s %s +issues.review.left_comment = ha fet un comentari +issues.review.content.empty = Heu de fer un comentari indicant els canvis que sol·liciteu. +issues.review.reject = canvis sol·licitats %s +issues.review.add_review_request = ha sol·licitat una revisió de %[1]s %[2]s +issues.review.add_review_requests = ha sol·licitat revisions de %[1]s %[2]s +issues.review.remove_review_request = ha eliminat la sol·licitud de revisió per a %[1]s %[2]s +issues.review.remove_review_requests = ha eliminat les sol·licituds de revisió per a %[1]s %[2]s +issues.review.remove_review_request_self = ha rebutjat la revisió %s +issues.review.add_remove_review_requests = ha demanat la revisió a %[1]s i ha eliminat les peticions de revisió a %[2]s %[3]s +issues.review.pending.tooltip = Actualment, la resta d'usuaris no poden veure aquest comentari. Per a publicar els comentaris pendents, selecciona "%s" -> "%s/%s/%s" més amunt. +issues.review.outdated_description = El contingut ha canviat des que es va publicar aquest comentari +issues.review.option.show_outdated_comments = Mostra els comentaris antics +issues.review.option.hide_outdated_comments = Amaga els comentaris antics +issues.review.show_outdated = Mostrar antics +issues.review.hide_outdated = Amagar antics +issues.review.show_resolved = Mostrar resolts +issues.review.hide_resolved = Amagar resolts +issues.review.resolve_conversation = Resol la conversa +issues.review.un_resolve_conversation = Desfés la resolució de la conversa +issues.review.resolved_by = ha marcat aquesta conversa com a resolta +issues.content_history.delete_from_history = Eliminar de la història +issues.content_history.delete_from_history_confirm = Eliminar de la història? +issues.blocked_by_user = No podeu crear problemes en aquest repositori perquè el seu propietari us ha bloquejat. +comment.blocked_by_user = No podeu fer comentaris perquè el propietari o autor del repositori us ha bloquejat. +issues.summary_card_alt = Targeta de resum d'un problema titulat "%s" al repositori %s +pulls.edit.already_changed = No s'han pogut desar els canvis a la «pull request». Sembla que un altre usuari n'ha modificat el contingut. Si us plau, refresqueu la pàgina i intenteu-ho un altre cop per evitar reescriure els seus canvis +pulls.viewed_files_label = s'ha revisat %[1]d / %[2]d fitxers +pulls.filter_branch = Filtra branca +pulls.showing_only_single_commit = Només es mostren els canvis del commit %[1]s +pulls.showing_specified_commit_range = Només es mostren els canvis entre %[1]s..%[2]s +pulls.filter_changes_by_commit = Filtrar per commit +pulls.tab_files = Fitxers modificats +pulls.title_wip_desc = `Comença el títol amb %s per evitar que la «pull request» sigui fusionada accidentalment.` +pulls.still_in_progress = Encara hi treballeu? +pulls.add_prefix = Afegeix el prefix %s +pulls.ready_for_review = A punt per revisar? +pulls.remove_prefix = Elimina el prefix %s +pulls.data_broken = Aquesta «pull request» és malmesa perquè hi falta la informació de la bifurcació. +pulls.required_status_check_failed = Algunes comprovacions no han sigut satisfactòries. +pulls.required_status_check_missing = Falten algunes comprovacions. +pulls.wrong_commit_id = L'ID del commit ha d'ésser a la branca objectiu +pulls.rebase_merge_pull_request = Canvia de base i avança ràpid +pulls.rebase_merge_commit_pull_request = Fes «rebase» i després el commit de fusió +pulls.squash_merge_pull_request = Fes un «squash commit» +pulls.fast_forward_only_merge_pull_request = Només avança ràpid +pulls.rebase_conflict = La fusió ha fallat: Hi ha hagut un conflicte mentre es feia el «rebase» del commit: %[1]s. Consell: Intenteu una altra estratègia +pulls.unrelated_histories = La fusió ha fallat: El cap (head) de la fusió i la base no tenen una història comú. Consell: Intenteu una altra estratègia +pulls.merge_out_of_date = La fusió ha fallat: La base s'ha actualitzat mentre es generava la fusió. Consell: Torneu-ho a intentar. +pulls.head_out_of_date = La fusió ha fallat: El cap (head) s'ha actualitzat mentre es generava la fusió. Consell: Torneu-ho a intentar. +pulls.has_merged = Fallit: La «pull request» ja s'ha fusionat, no podeu fusionar-la de nou ni canviar la branca objectiu. +pulls.push_rejected = La pujada ha fallat: La pujada (push) s'ha rebutjat. Revisa els ganxos Git d'aquest repositori. +pulls.push_rejected_summary = Missatge complet del rebuig +pulls.push_rejected_no_message = La pujada ha fallat: La pujada (push) s'ha rebutjat però no hi havia un missatge remot. Revisa els ganxos Git d'aquest repositori +pulls.open_unmerged_pull_exists = `No podeu reobrir perquè hi ha una «pull request» pendent (#%d) amb les mateixes característiques.` +pulls.status_checking = Hi ha algunes comprovacions pendents +pulls.status_checks_success = Totes les comprovacions s'han resolt +pulls.status_checks_warning = Algunes comprovacions tenen advertències +pulls.status_checks_failure = Algunes comprovacions han fallat +pulls.status_checks_error = Algunes comprovacions han donat errors +pulls.status_checks_hide_all = Amaga totes les comprovacions +pulls.status_checks_show_all = Mostra totes les comprovacions +pulls.update_branch = Actualitza la branca fusionant +pulls.update_branch_rebase = Actualitza la branca fent «rebase» +pulls.update_branch_success = S'ha actualitzat la branca correctament +pulls.update_not_allowed = No teniu permisos per actualitzar la branca +pulls.outdated_with_base_branch = Aquesta branca està desactualitzada amb la branca base +pulls.close = Tancar «pull request» +pulls.closed_at = `ha tancat aquesta «pull request» %s` +pulls.reopened_at = `ha reobert aquesta «pull request» %s` +pulls.commit_ref_at = `ha fet referència a aquesta «pull request» des d'un commit %s` +pulls.cmd_instruction_hint = Veure les instruccions de la línia de comandes +pulls.cmd_instruction_checkout_title = Canviar branca +pulls.cmd_instruction_checkout_desc = Des del repositori del vostre projecte, canvia a una nova branca i comprova els canvis. +pulls.cmd_instruction_merge_desc = Fusionar els canvis i actualitzar-los a Forgejo. +pulls.cmd_instruction_merge_warning = Atenció: La configuració de "Detecta automàticament la fusió manual" no està activa en aquest repositori, haureu de marcar després aquesta «pull request» com a fusionada manualment. +pulls.clear_merge_message = Neteja el missatge de fusió +pulls.clear_merge_message_hint = Netejar el missatge de fusió només esborrarà el contingut del missatge de commit i mantindrà la firma generada de git com "Co-Authored-By …". +pulls.reopen_failed.head_branch = La «pull request» no es pot reobrir perquè el cap (head) de la branca ja no existeix. +pulls.reopen_failed.base_branch = La «pull request» no es pot reobrir perquè la base de la branca ja no existeix. +pulls.agit_explanation = Creat amb el flux de treball d'AGit. L'AGit permet que els contribuents proposin canvis amb "git push" sense crear una bifurcació o branca. +pulls.editable = Editable +pulls.editable_explanation = Els mantenidors poden editar aquesta «pull request». Podeu editar-la directament. +pulls.auto_merge_button_when_succeed = (Quan les comprovacions siguin satisfactòries) +pulls.auto_merge_when_succeed = Fusiona automàticament quan totes les comprovacions siguin satisfactòries +pulls.auto_merge_newly_scheduled = S'ha programat la «pull request» per a ser fusionada quan totes les comprovacions siguin satisfactòries. +pulls.auto_merge_has_pending_schedule = %[1]s ha programat aquesta «pull request» per a ser fusionada quan totes les comprovacions siguin satisfactòries %[2]s. +pulls.auto_merge_cancel_schedule = Cancel·la la fusió automàtica +pulls.auto_merge_not_scheduled = Aquesta «pull request» no es fusionarà automàticament. +pulls.auto_merge_canceled_schedule = S'ha cancel·lat la fusió automàtica per a aquesta «pull request». +pulls.auto_merge_newly_scheduled_comment = `ha programat aquesta «pull request» per a ser fusionada quan totes les comprovacions siguin satisfactòries. %[1]s` +pulls.auto_merge_canceled_schedule_comment = `ha cancel·lat la fusió automàtica d'aquesta «pull request» quan totes les comprovacions siguin satisfactòries %[1]s` +pulls.delete_after_merge.head_branch.is_default = La branca cap (head) que voleu eliminar és la branca predeterminada i no es pot eliminar. +pulls.delete_after_merge.head_branch.is_protected = La branca cap (head) que voleu eliminar està protegida i no es pot eliminar. +pulls.delete_after_merge.head_branch.insufficient_branch = No teniu permisos per eliminar la branca cap (head). +pulls.delete.title = Eliminar aquesta «pull request»? +pulls.delete.text = Realment voleu eliminar aquesta «pull request»? (Això eliminarà permanentment tot el contingut. Considereu millor tancar-la, si la voleu arxivar) +pulls.recently_pushed_new_branches = Heu pujat a la branca %[1]s %[2]s +comments.edit.already_changed = No s'han pogut desar els canvis al comentari. Sembla que un altre usuari n'ha modificat el contingut. Si us plau, refresqueu la pàgina i intenteu-ho un altre cop per evitar reescriure els seus canvis +milestones.new = Nova fita +milestones.closed = Tancat %s +milestones.update_ago = Actualitzat %s +milestones.no_due_date = Sense data de venciment +milestones.new_subheader = Les fites us poden ajudar a organitzar els problemes i fer-ne el seguiment. +milestones.completeness = %d%% Completat +milestones.create = Crear fita +milestones.due_date = Data de venciment (opcional) +milestones.invalid_due_date_format = El format de la data de venciment ha de ser "aaaa-mm-dd". +milestones.create_success = S'ha creat la fita "%s". +milestones.edit = Editar fita +milestones.edit_subheader = Les fites organitzen els problemes i en fan el seguiment. +milestones.modify = Actualitzar fita +milestones.edit_success = S'ha actualitzat la fita "%s". +milestones.deletion = Eliminar fita +milestones.deletion_desc = Eliminar una fita n'esborra tots els problemes. Continuar? +milestones.deletion_success = S'ha eliminat la fita. +milestones.filter_sort.name = Nom +milestones.filter_sort.earliest_due_data = A prop de la data de venciment +milestones.filter_sort.latest_due_date = Lluny de la data de venciment +milestones.filter_sort.least_complete = Menys completa +milestones.filter_sort.most_complete = Més completa diff.git-notes = Notes diff.git-notes.add = Afegir nota diff.git-notes.remove-header = Eliminar nota @@ -1678,6 +2082,8 @@ diff.show_unified_view = Vista única diff.whitespace_show_everything = Mostrar tots els canvis diff.whitespace_ignore_all_whitespace = Ignora els espais en blanc quan es comparin línies diff.whitespace_ignore_amount_changes = Ignora els canvis en el nombre d'espais en blanc +diff.whitespace_ignore_at_eol = Ignorar els canvis en els espais en blanc al final de la línia +diff.stats_desc = %d fitxers modificats amb %d afegits i %d supressions diff.stats_desc_file = %d canvis: %d afegits i %d supressions diff.bin_not_shown = No es mostra el fitxer binari. diff.view_file = Visualitza el fitxer @@ -1747,366 +2153,7 @@ release.tag_name_already_exist = Ja hi ha una publicació amb aquest nom d'etiqu release.tag_name_invalid = El nom d'etiqueta no és vàlid. release.tag_name_protected = El nom d'etiqueta és protegit. release.downloads = Baixades -fork_repo = Bifurca el repositori -fork_from = Bifurcar des de -create_new_repo_command = Crea un nou repositori a la línia de comandes -push_exist_repo = Puja un repositori ja existent des de la línia de comandes -empty_message = Aquest repositori està buit. -broken_message = Les dades Git d'aquest repositori no es poden llegir. Contacta amb l'administrador d'aquesta instància o suprimeix aquest repositori. -code.desc = Accedeix al codi font, els fitxers, els commits i les branques. -clear_ref = `Esborra la referència actual` -filter_branch_and_tag = Filtra per branca o etiqueta -find_tag = Cerca etiqueta -delete_preexisting_success = S'ha suprimit els fitxers no-adoptats a %s -author_search_tooltip = Es mostra un màxim de 30 usuaris -summary_card_alt = Resum del repositori %s -tree_path_not_found.commit = El camí %[1]s no existeix al commit %[2]s -tree_path_not_found.branch = El camí %[1]s no existeix a la branca %[2]s -tree_path_not_found.tag = El camí %[1]s no existeix a l'etiqueta %[2]s -transfer.accept = Accepta la transferència -transfer.accept_desc = Transfereix a "%s" -transfer.reject = Denega la transferència -transfer.reject_desc = Cancel·la la transferència a "%s" -transfer.no_permission_to_accept = No teniu permisos per acceptar aquesta transferència. -transfer.no_permission_to_reject = No teniu permisos per denegar aquesta transferència. -template.items = Ítems de la plantilla -template.git_content = Contingut Git (Branca predeterminada) -template.git_hooks = Ganxos Git -migrate_repo = Migra el repositori -migrate.repo_desc_helper = Deixa-ho buit per importar la descripció existent -migrate.clone_address = Migra / Clona des d'una URL -migrate.clone_address_desc = L'URL d'HTTP(S) o de Git "clone" d'un repositori ja existent -migrate.permission_denied = No podeu importar repositoris locals. -migrate.invalid_local_path = El camí local és invàlid. No existeix o no és un directori. -migrate.invalid_lfs_endpoint = El punt final LFS no és vàlid. -migrate.failed = La migració ha fallat: %v -migrate.migrate_items_options = Cal un testimoni d'accés per migrar els ítems addicionals -migrated_from = Migrat des de %[2]s -migrated_from_fake = Migrat des de %[1]s -migrate.migrate = Migra des de %s -migrate.migrating = S'està migrant des de %s … -migrate.migrating_failed = La migració des de %s ha fallat. -migrate.migrating_failed.error = No s'ha pogut migrar: %s -migrate.migrating_failed_no_addr = La migració ha fallat. -mirror_from = mirall de -forked_from = bifurcat des de -fork_from_self = No podeu bifurcar un repositori que us pertany. -fork_guest_user = Inicia sessió per bifurcar aquest repositori. -watch_guest_user = Inicia sessió per vigilar aquest repositori. -star_guest_user = Inicia sessió per destacar aquest repositori. -subscribe.issue.guest.tooltip = Inicia sessió per subscriure't a aquest problema. -subscribe.pull.guest.tooltip = Inicia sessió per subscriure't a aquesta «pull request». -more_operations = Més operacions -no_desc = Sense descripció -quick_guide = Guia ràpida -clone_this_repo = Clona aquest repositori -cite_this_repo = Cita aquest repositori -repo_gitignore_helper_desc = Escolliu de quins fitxers no s'ha de fer seguiment d'una llista de plantilles per llenguatges comuns. Els artefactes típics generats per les eines de construcció de cada llenguatge estan incloses al .gitignore de manera predeterminada. -readme_helper_desc = Aquí podeu escriure una descripció completa del vostre projecte. -mirror_interval = Interval de rèplica (les unitats de temps vàlides són "h", "m", "s"). 0 per desactivar la sincronització periòdica. (Interval mínim: %s) -mirror_use_ssh.helper = Si seleccioneu aquesta opció, Forgejo replicarà el repositori mitjançant Git per SSH i us crearà una parella de claus. Heu d'assegurar-vos que la clau pública generada estigui autoritzada per publicar al repositori de destí. No podreu fer servir autenticació basada en contrasenyes. -migrate.github_token_desc = Podeu posar un o més testimonis separats amb comes per fer que la migració sigui més ràpida, evitant així el límit de taxa de l'API de GitHub. ATENCIÓ: Abusar aquesta funció pot anar en contra de la política de servei del proveïdor i pot comportar el bloqueig del/s vostre/s compte/s. -migrate.clone_local_path = o el camí a un servidor local -migrate.permission_denied_blocked = No podeu importar des d'amfitrions rebutjats, si us plau, demaneu a l'administrador que comprovi les configuracions ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. -file.title = %s a %s -file_view_rendered = Veure renderitzat -file_view_raw = Veure cru -editor.commit_directly_to_this_branch = Feu un commit directament a la branca %[1]s. -editor.new_branch_name = Anomena la nova branca per a aquest commit -editor.file_already_exists = Ja hi ha un fitxer anomenat "%S" en aquest repositori. -editor.commit_id_not_matching = El fitxer ha canviat mentre l'editaves. Fes un commit a una nova branca i llavors fusiona-la. -editor.push_out_of_date = La pujada està desactualitzada. -editor.commit_empty_file_header = Fes un commit d'un fitxer buit -editor.user_no_push_to_branch = L'usuari no pot pujar a la branca -editor.revert = Retorna %s a: -editor.commit_email = Adreça electrònica del commit -commits.no_commits = No hi ha commits en comú. "%s" i "%s" tenen històries completament diferents. -commits.view_path = Veure en aquest punt de la història -commits.view_single_diff = Veure els canvis fets en aquest fitxer introduïts en aquest commit -commit.revert-header = Retorna: %s -commit.revert-content = Selecciona una branca per retornar-hi: -projects.type.basic_kanban = Kanban bàsic -projects.column.set_default_desc = Estableix aquesta columna com a predeterminada per als problemes i pulls sense categoria -projects.card_type.desc = Previsualització de targeta -issues.filter_assignees = Filtrar assignats -issues.filter_reviewers = Filtrar revisor -issues.new.open_projects = Projectes oberts -issues.new.open_milestone = Fites obertes -issues.new.clear_assignees = Esborra assignats -issues.new.no_assignees = Sense assignats -issues.new.assign_to_me = Assigna-m'ho -issues.new.no_reviewers = Sense revisors -issues.edit.already_changed = No s'han pogut desar els canvis al problema. Sembla que un altre usuari n'ha modificat el contingut. Si us plau, refresqueu la pàgina i intenteu-ho un altre cop per evitar reescriure els seus canvis -issues.choose.get_started = Començar -issues.choose.blank_about = Crea un problema a partir de la plantilla per defecte. -issues.choose.invalid_config = La configuració del problema té errors: -issues.label_templates.use = Utilitza una etiqueta predeterminada -issues.label_templates.fail_to_load_file = No s'ha pogut carregar el fitxer d'etiqueta predeterminada "%s": %v -issues.add_label = s'ha afegit l'etiqueta %s %s -issues.add_labels = s'han afegit les etiquetes %s %s -issues.remove_label = s'ha esborrat l'etiqueta %s %s -issues.remove_labels = s'han esborrat les etiquetes %s %s -issues.add_remove_labels = s'han afegit %s etiquetes i esborrat %s %s -issues.add_milestone_at = `s'ha afegit a la fita %s %s` -issues.add_project_at = `s'ha afegit al projecte %s %s` -issues.change_milestone_at = `s'ha modificat la fita des de %s a %s %s` -issues.change_project_at = `s'ha modificat el projecte des de %s a %s %s` -issues.remove_milestone_at = `s'ha eliminat de la fita %s %s` -issues.remove_project_at = `s'ha eliminat del projecte %s %s` -issues.self_assign_at = `s'ha auto-assignat aquest %s` -issues.add_assignee_at = `va ser assignat per %s %s` -issues.remove_assignee_at = `va ser desassignat per %s %s` -issues.remove_self_assignment = `s'ha eliminat la seva tasca %s` -issues.change_title_at = `s'ha canviat el títol de %s a %s %s` -issues.change_ref_at = `s'ha canviat la referència de %s a %s %s` -issues.remove_ref_at = `s'ha eliminat la referència %s %s` -issues.add_ref_at = `s'ha afegit la referència %s %s` -issues.delete_branch_at = `s'ha eliminat la branca %s %s` -issues.filter_label_no_select = Totes les etiquetes -issues.filter_label_select_no_label = Sense etiqueta -issues.filter_milestone_none = Sense fites -issues.filter_milestone_open = Fites obertes -issues.filter_milestone_closed = Fites tancades -issues.filter_project_all = Tots els projectes -issues.filter_project_none = Sense projecte -issues.filter_assginee_no_select = Tots els assignats -issues.filter_assginee_no_assignee = Sense assignat -issues.filter_poster_no_select = Tots els autors -issues.filter_type.all_pull_requests = Totes les «pull requests» -issues.filter_type.all_issues = Tots els problemes -issues.filter_type.assigned_to_you = Tasques que tens assignades -issues.filter_type.created_by_you = Creat per tu -issues.filter_type.mentioning_you = Que et mencionen -issues.filter_type.review_requested = S'ha demanat la revisió -issues.filter_type.reviewed_by_you = Revisats per tu -issues.filter_sort.relevance = Rellevància -issues.filter_sort.recentupdate = Actualitzats recentment -issues.filter_sort.leastupdate = Actualitzats menys recentment -issues.filter_sort.leastcomment = Menys comentats -issues.filter_sort.nearduedate = A prop de la data de venciment -issues.filter_sort.farduedate = Lluny de la data de venciment -issues.filter_sort.moststars = Més destacats -issues.filter_sort.feweststars = Menys destacats -issues.action_assignee_no_select = Sense assignat -pulls.merged_by = per %[3]s s'ha fusionat %[1]s -issues.closed_by = per %[3]s s'ha tancat %[1]s -issues.context.menu = Menú de comentari -issues.comment_pull_merged_at = s'ha fusionat el commit %[1]s a %[2]s %[3]s -issues.comment_manually_pull_merged_at = s'ha fusionat el commit %[1]s manualment a %[2]s %[3]s -issues.is_stale = Hi ha hagut canvis a aquest PR des de la revisió -issues.archived_label_description = (Arxivats) %s -editor.signoff_desc = Afegeix un "Signat per" seguit de l'autor al final del missatge del commit. -issues.role.contributor_helper = Aquest usuari ja ha fet commits en aquest repositori. -issues.label.filter_sort.reverse_alphabetically = Capgira-ho alfabèticament -issues.label.filter_sort.by_size = Mida més petita -issues.label.filter_sort.reverse_by_size = Mida més gran -issues.num_participants_one = %d participant -issues.num_participants_few = %d participants -issues.tracker = Rastrejador de temps -issues.start_tracking_short = Inicia el temporitzador -issues.start_tracking = Inicia el rastreig de temps -issues.start_tracking_history = `s'ha començat %s` -issues.tracker_auto_close = El temporitzador s'aturarà automàticament quan el problema s'hagi resolt -issues.tracking_already_started = `Ja has començat a rastrejar el temps en un altre problema!` -issues.stop_tracking = Atura el temporitzador -issues.stop_tracking_history = `s'ha aturat %s` -issues.cancel_tracking_history = `s'ha cancel·lat el rastreig de temps %s` -issues.add_time = Afegeix el temps manualment -issues.del_time = Elimina aquest registre de temps -issues.add_time_short = Afegeix temps -issues.add_time_history = `s'ha afegit temps passat %s` -issues.del_time_history = `s'ha eliminat temps passat %s` -issues.add_time_sum_to_small = No s'ha introduït temps. -issues.time_spent_from_all_authors = `Temps total: %s` -issues.due_date = Data de venciment -issues.push_commit_1 = s'ha afegit %d commit %s -issues.push_commits_n = s'ha afegit %d commits %s -issues.force_push_codes = `s'ha forçat la pujada %[1]s des de %[2]s %[8]s a %[4]s %[9]s %[6]s` -issues.due_date_not_set = No s'ha establert una data de venciment. -issues.due_date_added = s'ha afegit la data de venciment %s %s -issues.due_date_modified = s'ha modificat la data de venciment de %[2]s a %[1]s %[3]s -issues.due_date_remove = s'ha eliminat la data de venciment %s %s -issues.due_date_invalid = La data de venciment no és vàlida o és fora de rang. Si us plau, useu el format "aaaa-mm-dd". -issues.dependency.issue_no_dependencies = No s'han establert dependències. -issues.dependency.no_permission_1 = No teniu permisos per llegir %d dependència -issues.dependency.no_permission_n = No teniu permisos per llegir %d dependències -issues.dependency.no_permission.can_remove = No teniu permisos per llegir aquesta dependència, però la podeu eliminar -issues.dependency.add = Afegir dependència… -issues.dependency.remove_info = Eliminar aquesta dependència -issues.dependency.added_dependency = `s'ha afegit una nova dependència %s` -issues.dependency.removed_dependency = `s'ha eliminat una dependència %s` -issues.dependency.pr_closing_blockedby = No es pot tancar aquesta «pull request» perquè té els següents problemes -issues.dependency.issue_closing_blockedby = No es pot tancar aquest problema perquè té els següents problemes -issues.dependency.issue_close_blocks = Aquest problema bloqueja el tancament d'aquests altres problemes -issues.dependency.pr_close_blocks = Aquesta «pull request» bloqueja el tancament d'aquests problemes -issues.dependency.issue_close_blocked = Heu de tancar tots els problemes que en bloquegen el tancament d'aquest. -issues.dependency.issue_batch_close_blocked = No es poden tancar tots els problemes marcats perquè el problema #%d encara té dependències obertes -issues.dependency.pr_close_blocked = Heu de tancar tots els problemes que bloquegen aquesta «pull request» abans de fusionar-la. -issues.dependency.blocked_by_short = Depèn de -issues.dependency.pr_remove_text = Això eliminarà la dependència d'aquesta «pull request». Continuar? -issues.dependency.setting = Activa les dependències per a problemes i «pull requests» -issues.dependency.add_error_same_issue = No podeu fer que un problema depengui d'ell mateix. -issues.dependency.add_error_dep_issue_not_exist = El problema que en depèn no existeix. -issues.dependency.add_error_dep_not_exist = La dependència no existeix. -issues.dependency.add_error_dep_exists = La dependència ja existeix. -issues.dependency.add_error_cannot_create_circular = No podeu crear una dependència amb dos problemes que es bloquegen mútuament. -issues.dependency.add_error_dep_not_same_repo = Els dos problemes han d'ésser al mateix repositori. -issues.review.self.approval = No podeu aprovar la vostra «pull request». -issues.review.self.rejection = No podeu sol·licitar canvis en la vostra «pull request». -issues.review.approve = ha aprovat aquests canvis %s -issues.review.comment = ha revisat %s -issues.review.dismissed = ha rebutjat la revisió de %s %s -issues.review.left_comment = ha fet un comentari -issues.review.content.empty = Heu de fer un comentari indicant els canvis que sol·liciteu. -issues.review.reject = canvis sol·licitats %s -issues.review.add_review_request = ha sol·licitat una revisió de %[1]s %[2]s -issues.review.add_review_requests = ha sol·licitat revisions de %[1]s %[2]s -issues.review.remove_review_request = ha eliminat la sol·licitud de revisió per a %[1]s %[2]s -issues.review.remove_review_requests = ha eliminat les sol·licituds de revisió per a %[1]s %[2]s -issues.review.option.show_outdated_comments = Mostra els comentaris antics -issues.review.option.hide_outdated_comments = Amaga els comentaris antics -issues.review.show_outdated = Mostrar antics -issues.review.hide_outdated = Amagar antics -issues.review.show_resolved = Mostrar resolts -issues.review.hide_resolved = Amagar resolts -issues.review.resolve_conversation = Resol la conversa -issues.review.un_resolve_conversation = Desfés la resolució de la conversa -issues.review.resolved_by = ha marcat aquesta conversa com a resolta -issues.content_history.delete_from_history = Eliminar de la història -issues.content_history.delete_from_history_confirm = Eliminar de la història? -issues.blocked_by_user = No podeu crear problemes en aquest repositori perquè el seu propietari us ha bloquejat. -comment.blocked_by_user = No podeu fer comentaris perquè el propietari o autor del repositori us ha bloquejat. -pulls.filter_branch = Filtra branca -pulls.showing_only_single_commit = Només es mostren els canvis del commit %[1]s -pulls.showing_specified_commit_range = Només es mostren els canvis entre %[1]s..%[2]s -pulls.tab_files = Fitxers modificats -pulls.title_wip_desc = `Comença el títol amb %s per evitar que la «pull request» sigui fusionada accidentalment.` -pulls.add_prefix = Afegeix el prefix %s -pulls.ready_for_review = A punt per revisar? -pulls.remove_prefix = Elimina el prefix %s -pulls.required_status_check_failed = Algunes comprovacions no han sigut satisfactòries. -pulls.required_status_check_missing = Falten algunes comprovacions. -pulls.wrong_commit_id = L'ID del commit ha d'ésser a la branca objectiu -blame_prior = Veure el «blame» anterior a aquest canvi -blame.ignore_revs = S'ignoren les revisions a .git-blame-ignore-revs. Fes clic aquí per veure-les. -blame.ignore_revs.failed = No s'han pogut ignorar les revisions a .git-blame-ignore-revs. -commit_graph = Gràfic de commits -commit.contained_in = Aquest commit és a: -no_eol.text = Sense EOL -editor.commit_signed_changes = Fes un commit signat dels canvis -editor.commit_changes = Fes un commit dels canvis -editor.add_tmpl.filename = nom del fitxer -editor.patch = Aplicar pedaç -editor.patching = Aplicant el pedaç a: -editor.cherry_pick = Triar a dit %s a: -commit.cherry-pick = Triar a dit -commit.cherry-pick-header = Triar a dit: %s -commit.cherry-pick-content = Seleccioneu una branca per triar-hi: -issues.filter_sort.mostforks = Més bifurcacions -issues.filter_sort.fewestforks = Menys bifurcacions -issues.label_exclusive_desc = Doneu nom a l'àmbit/ítem de l'etiqueta per fer-la mútuament exclusiva amb altres àmbits/ etiquetes. -issues.label_exclusive_warning = Qualsevol etiqueta amb àmbits conflictius s'esborrarà quan s'editin les etiquetes d'un problema o «pull request». -issues.dependency.pr_no_dependencies = No s'ha establert cap dependència. -issues.dependency.remove_header = Eliminar dependència -issues.dependency.issue_remove_text = Això eliminarà la dependència d'aquest problema. Continuar? -issues.review.remove_review_request_self = ha rebutjat la revisió %s -issues.review.add_remove_review_requests = ha demanat la revisió a %[1]s i ha eliminat les peticions de revisió a %[2]s %[3]s -issues.review.pending.tooltip = Actualment, la resta d'usuaris no poden veure aquest comentari. Per a publicar els comentaris pendents, selecciona "%s" -> "%s/%s/%s" més amunt. -issues.review.outdated_description = El contingut ha canviat des que es va publicar aquest comentari -issues.summary_card_alt = Targeta de resum d'un problema titulat "%s" al repositori %s -pulls.edit.already_changed = No s'han pogut desar els canvis a la «pull request». Sembla que un altre usuari n'ha modificat el contingut. Si us plau, refresqueu la pàgina i intenteu-ho un altre cop per evitar reescriure els seus canvis -pulls.viewed_files_label = s'ha revisat %[1]d / %[2]d fitxers -pulls.still_in_progress = Encara hi treballeu? -pulls.data_broken = Aquesta «pull request» és malmesa perquè hi falta la informació de la bifurcació. -pulls.rebase_merge_commit_pull_request = Fes «rebase» i després el commit de fusió -pulls.squash_merge_pull_request = Fes un «squash commit» -pulls.rebase_conflict = La fusió ha fallat: Hi ha hagut un conflicte mentre es feia el «rebase» del commit: %[1]s. Consell: Intenteu una altra estratègia -pulls.unrelated_histories = La fusió ha fallat: El cap (head) de la fusió i la base no tenen una història comú. Consell: Intenteu una altra estratègia -pulls.merge_out_of_date = La fusió ha fallat: La base s'ha actualitzat mentre es generava la fusió. Consell: Torneu-ho a intentar. -pulls.head_out_of_date = La fusió ha fallat: El cap (head) s'ha actualitzat mentre es generava la fusió. Consell: Torneu-ho a intentar. -pulls.has_merged = Fallit: La «pull request» ja s'ha fusionat, no podeu fusionar-la de nou ni canviar la branca objectiu. -pulls.push_rejected = La pujada ha fallat: La pujada (push) s'ha rebutjat. Revisa els ganxos Git d'aquest repositori. -pulls.push_rejected_summary = Missatge complet del rebuig -pulls.push_rejected_no_message = La pujada ha fallat: La pujada (push) s'ha rebutjat però no hi havia un missatge remot. Revisa els ganxos Git d'aquest repositori -pulls.open_unmerged_pull_exists = `No podeu reobrir perquè hi ha una «pull request» pendent (#%d) amb les mateixes característiques.` -pulls.status_checking = Hi ha algunes comprovacions pendents -pulls.status_checks_success = Totes les comprovacions s'han resolt -pulls.status_checks_warning = Algunes comprovacions tenen advertències -pulls.status_checks_failure = Algunes comprovacions han fallat -pulls.status_checks_error = Algunes comprovacions han donat errors -pulls.status_checks_hide_all = Amaga totes les comprovacions -pulls.status_checks_show_all = Mostra totes les comprovacions -pulls.update_branch = Actualitza la branca fusionant -pulls.update_branch_rebase = Actualitza la branca fent «rebase» -pulls.update_branch_success = S'ha actualitzat la branca correctament -pulls.update_not_allowed = No teniu permisos per actualitzar la branca -pulls.outdated_with_base_branch = Aquesta branca està desactualitzada amb la branca base -pulls.close = Tancar «pull request» -pulls.closed_at = `ha tancat aquesta «pull request» %s` -pulls.reopened_at = `ha reobert aquesta «pull request» %s` -pulls.commit_ref_at = `ha fet referència a aquesta «pull request» des d'un commit %s` -pulls.cmd_instruction_hint = Veure les instruccions de la línia de comandes -pulls.cmd_instruction_merge_desc = Fusionar els canvis i actualitzar-los a Forgejo. -pulls.cmd_instruction_merge_warning = Atenció: La configuració de "Detecta automàticament la fusió manual" no està activa en aquest repositori, haureu de marcar després aquesta «pull request» com a fusionada manualment. -pulls.clear_merge_message = Neteja el missatge de fusió -pulls.clear_merge_message_hint = Netejar el missatge de fusió només esborrarà el contingut del missatge de commit i mantindrà la firma generada de git com "Co-Authored-By …". -pulls.reopen_failed.head_branch = La «pull request» no es pot reobrir perquè el cap (head) de la branca ja no existeix. -pulls.reopen_failed.base_branch = La «pull request» no es pot reobrir perquè la base de la branca ja no existeix. -pulls.rebase_merge_pull_request = Canvia de base i avança ràpid -pulls.fast_forward_only_merge_pull_request = Només avança ràpid -pulls.cmd_instruction_checkout_title = Canviar branca -pulls.filter_changes_by_commit = Filtrar per commit -pulls.cmd_instruction_checkout_desc = Des del repositori del vostre projecte, canvia a una nova branca i comprova els canvis. -pulls.agit_explanation = Creat amb el flux de treball d'AGit. L'AGit permet que els contribuents proposin canvis amb "git push" sense crear una bifurcació o branca. -pulls.editable = Editable -pulls.editable_explanation = Els mantenidors poden editar aquesta «pull request». Podeu editar-la directament. -pulls.auto_merge_button_when_succeed = (Quan les comprovacions siguin satisfactòries) -pulls.auto_merge_when_succeed = Fusiona automàticament quan totes les comprovacions siguin satisfactòries -pulls.auto_merge_newly_scheduled = S'ha programat la «pull request» per a ser fusionada quan totes les comprovacions siguin satisfactòries. -pulls.auto_merge_has_pending_schedule = %[1]s ha programat aquesta «pull request» per a ser fusionada quan totes les comprovacions siguin satisfactòries %[2]s. -pulls.auto_merge_cancel_schedule = Cancel·la la fusió automàtica -pulls.auto_merge_not_scheduled = Aquesta «pull request» no es fusionarà automàticament. -pulls.auto_merge_canceled_schedule = S'ha cancel·lat la fusió automàtica per a aquesta «pull request». -pulls.auto_merge_newly_scheduled_comment = `ha programat aquesta «pull request» per a ser fusionada quan totes les comprovacions siguin satisfactòries. %[1]s` -pulls.auto_merge_canceled_schedule_comment = `ha cancel·lat la fusió automàtica d'aquesta «pull request» quan totes les comprovacions siguin satisfactòries %[1]s` -pulls.delete_after_merge.head_branch.is_default = La branca cap (head) que voleu eliminar és la branca predeterminada i no es pot eliminar. -pulls.delete_after_merge.head_branch.is_protected = La branca cap (head) que voleu eliminar està protegida i no es pot eliminar. -pulls.delete_after_merge.head_branch.insufficient_branch = No teniu permisos per eliminar la branca cap (head). -pulls.delete.title = Eliminar aquesta «pull request»? -pulls.delete.text = Realment voleu eliminar aquesta «pull request»? (Això eliminarà permanentment tot el contingut. Considereu millor tancar-la, si la voleu arxivar) -pulls.recently_pushed_new_branches = Heu pujat a la branca %[1]s %[2]s -comments.edit.already_changed = No s'han pogut desar els canvis al comentari. Sembla que un altre usuari n'ha modificat el contingut. Si us plau, refresqueu la pàgina i intenteu-ho un altre cop per evitar reescriure els seus canvis -milestones.new = Nova fita -milestones.closed = Tancat %s -milestones.update_ago = Actualitzat %s -milestones.no_due_date = Sense data de venciment -milestones.new_subheader = Les fites us poden ajudar a organitzar els problemes i fer-ne el seguiment. -milestones.completeness = %d%% Completat -milestones.create = Crear fita -milestones.due_date = Data de venciment (opcional) -milestones.invalid_due_date_format = El format de la data de venciment ha de ser "aaaa-mm-dd". -milestones.create_success = S'ha creat la fita "%s". -milestones.edit = Editar fita -milestones.edit_subheader = Les fites organitzen els problemes i en fan el seguiment. -milestones.modify = Actualitzar fita -milestones.edit_success = S'ha actualitzat la fita "%s". -milestones.deletion = Eliminar fita -milestones.deletion_desc = Eliminar una fita n'esborra tots els problemes. Continuar? -milestones.deletion_success = S'ha eliminat la fita. -milestones.filter_sort.name = Nom -milestones.filter_sort.earliest_due_data = A prop de la data de venciment -milestones.filter_sort.latest_due_date = Lluny de la data de venciment -milestones.filter_sort.least_complete = Menys completa -milestones.filter_sort.most_complete = Més completa -default_branch_helper = La branca per defecte és la branca base pels pull requests i els commits. -file_follow = Seguir l'enllaç simbòlic -escape_control_characters =Escapar -commits.signed_by_untrusted_user_unmatched = Signat per un usuari no fiable que no coincideix amb l'autor del commit -projects.new_subheader = Coordineu, feu seguiment, i actualitzeu el vostre treball en un sol lloc, per tal que els projectes es mantinguin transparents i dins del calendari previst. -projects.type.bug_triage = Triatge d'errors -projects.column.set_default = Definir com a predeterminada -projects.column.assigned_to = Assignat a -issues.filter_sort.mostcomment = Les més comentades + signing.wont_sign.parentsigned = El commit no se signarà perquè el commit pare no està signat. signing.wont_sign.headsigned = La fusió no se signarà perquè el commit cap (head) no està signat. wiki.create_first_page = Crea la primera pàgina @@ -2214,12 +2261,14 @@ settings.add_webhook.invalid_path = El camí no pot contenir ".", ".." o una cad settings.webhook_deletion = Eliminar webhook settings.webhook_deletion_desc = Eliminar un webhook n'elimina la configuració i la història de lliurament. Continuar? settings.webhook_deletion_success = S'ha eliminat el webhook. + unescape_control_characters = Des-escapar view_git_blame = Veure git blame vendored = Proveïdor editor.propose_file_change = Proposa canvis en el fitxer editor.invalid_commit_mail = Adreça de correu electrònic incorrecta per crear un commit. editor.file_changed_while_editing = El contingut del fitxer ha canviat des que el vau obrir. Feu clic aquí per veure'l o feu commit dels canvis un altre cop per sobreescriure'l. +editor.commit_empty_file_text = L'arxiu que s'està a punt de cometre és buit. Procedir? editor.push_rejected_no_message = El servidor ha rebutjat el canvi sense més detalls. Si us plau, reviseu els ganxos de Git. editor.push_rejected = El servidor ha rebutjat el canvi. Si us plau, reviseu els ganxos de Git. editor.cannot_commit_to_protected_branch = No es pot fer commit a la branca protegida "%s". @@ -2253,6 +2302,7 @@ pulls.reject_count_1 = %d sol·licitud de canvi pulls.reject_count_n = %s sol·licituds de canvi pulls.waiting_count_1 = %d revisió en espera pulls.waiting_count_n = %d revisions en espera +pulls.no_merge_not_ready = Aquesta «pull request» no està llesta per a ser fusionada, comprova l'estat de revisió i les comprovacions d'estat. pulls.merge_pull_request = Crear un commit de fusió pulls.merge_commit_id = L'ID del commit de fusió pulls.require_signed_wont_sign = La branca requereix commits signats, però aquesta fusió no estarà signada @@ -2273,6 +2323,7 @@ wiki.back_to_wiki = Tornar a la pàgina de la wiki wiki.reserved_page = El nom de pàgina wiki "%s" està reservat. wiki.last_updated = Actualitzat per última vegada %s wiki.page_name_desc = Introduïu un nom per aquesta pàgina de la Wiki. Alguns noms especials són: "Home", "_Sidebar" i "_Footer". +activity.navbar.pulse = Pols activity.navbar.recent_commits = Commits recents activity.published_prerelease_label = Pre-publicació activity.git_stats_commit_1 = %d commit @@ -2348,6 +2399,14 @@ settings.event_pull_request_review_request = se sol·liciti revisió settings.event_pull_request_review_request_desc = se sol·licitin revisions a la «pull request» o se n'elimini la sol·licitud. settings.event_pull_request_approvals = s'accepti una «pull request» settings.event_pull_request_merge = es fusioni una «pull request» +settings.event_header_action = Esdeveniments d'execució d'accions +settings.event_action_failure = Fallada +settings.event_action_failure_desc = L'Acció ha fallat. +settings.event_action_recover_desc = La execució de l'acció ha finalitzat amb èxit després de que la última execució en el mateix flux fallés. +settings.event_action_success_desc = La execució de l'acció ha tingut èxit. +settings.event_package_desc = Paquet creat o eliminat en un repositori. +settings.branch_filter = Filtre de branca +settings.branch_filter_desc = Llista blanca de la branca per als esdeveniments de pujada i creació/eliminació de branques, especificada com un patró «glob». Si és buida o *, s'enregistraran els esdeveniments de totes les branques. Vegeu la documentació %[2]s per a la sintaxi. Per exemple: master, {master,release*}. settings.authorization_header = Capçalera d'Autorització settings.authorization_header_desc = S'inclourà com a capçalera d'autorització per a les «requests» quan hi sigui. Exemples: %s. settings.active_helper = La informació sobre els esdeveniments disparats s'enviarà a aquesta URL webhook. @@ -2359,6 +2418,9 @@ settings.recent_deliveries = Lliuraments recents settings.hook_type = Tipus de ganxo settings.add_web_hook_desc = Integrar %s al vostre repositori. settings.graphql_url = URL GraphQL +settings.web_hook_name_feishu = Feishu / Lark Suite +settings.web_hook_name_larksuite_only = Lark Suite +settings.web_hook_name_wechatwork = WeCom (Wechat Work) settings.packagist_username = Nom d'usuari Packagist settings.packagist_api_token = Testimoni API settings.packagist_package_url = URL del paquet Packagist @@ -2391,6 +2453,7 @@ settings.protect_enable_merge = Activar fusió settings.protect_enable_merge_desc = Tothom amb accés d'escriptura podrà fusionar «pull requests» a la branca. settings.protect_whitelist_committers = Pujada restringida settings.protect_whitelist_committers_desc = Només els usuaris i equips indicats a la llista blanca podran pujar (push) a la branca (però no «force push»). +settings.protect_whitelist_deploy_keys = Posar les claus d'implementació amb accés d'escriptura per pujar (push) a la llista blanca. settings.protect_whitelist_users = Llista blanca d'usuaris per pujar settings.protect_whitelist_teams = Llista blanca d'equips per pujar settings.protect_merge_whitelist_committers = Activar llista blanca de fusió @@ -2402,9 +2465,13 @@ settings.protect_status_check_patterns_desc = Introdueix patrons per especificar settings.protect_check_status_contexts_desc = Requerir comprovació d'estat abans de fusionar. Quan s'activa, els commits s'han de pujar primer en una altra branca, i llavors es podran fusionar o pujar directament a la branca que indica aquesta regla, un cop hagin passat les comprovacions. Si no s'indica cap context, l'últim commit ha de ser satisfactori, sigui quin sigui el context. settings.protect_check_status_contexts_list = Comprovacions d'estat de la darrera setmana d'aquest repositori settings.protect_invalid_status_check_pattern = Patró de comprovació invàlid: "%s". -settings.event_header_action = Esdeveniments d'execució d'accions -settings.event_action_failure = Fallada -settings.event_action_failure_desc = L'Acció ha fallat. +settings.protect_no_valid_status_check_patterns = Sense patrons de comprovació d'estat vàlids. +settings.protect_required_approvals = Aprovacions necessàries +settings.protect_required_approvals_desc = Permet fusionar només «pull requests» amb prou revisions positives. +settings.protect_approvals_whitelist_enabled = Limita les aprovacions als usuaris o equips de la llista blanca +settings.protect_approvals_whitelist_enabled_desc = Només comptaran cap al total d'aprovacions necessàries les revisions d'usuaris i equips de la llista blanca. Si la llista és buida, en comptaran les revisions de tothom amb accés d'escriptura. +settings.protect_approvals_whitelist_users = Revisors en la llista blanca +settings.protect_approvals_whitelist_teams = Equips en la llista blanca per revisions settings.dismiss_stale_approvals = Ignora les aprovacions estancades settings.dismiss_stale_approvals_desc = Quan es facin commits nous a la branca que canviïn el contingut de la «pull request», les aprovacions antigues es descartaran. settings.ignore_stale_approvals = Ignorar aprovacions estancades @@ -2423,8 +2490,21 @@ settings.remove_protected_branch_failed = No s'ha pogut eliminar la regla de pro settings.protected_branch_deletion = Protecció d'eliminació de branca settings.protected_branch_deletion_desc = Desactivar la protecció de branca permet als usuaris amb accés d'escriptura pujar a la branca. Continua? settings.block_rejected_reviews = Bloqueja la fusió en revisions rebutjades +settings.block_rejected_reviews_desc = No es podrà fusionar quan els revisors oficials en sol·licitin canvis, encara que hi hagi prou aprovacions. +settings.block_on_official_review_requests = Bloquejar fusió en sol·licituds de revisió oficials +settings.block_on_official_review_requests_desc = No es podrà fusionar quan tingui sol·licituds de revisió oficials, encara que hi hagi prou aprovacions. +settings.block_outdated_branch = Bloquejar fusió si la «pull request» és obsoleta +settings.block_outdated_branch_desc = No es podrà fusionar quan la branca cap (head) sigui darrera la branca base. +settings.enforce_on_admins = Aplica aquesta regla als administradors del repositori +settings.enforce_on_admins_desc = Els repositoris del repositori no poden saltar-se aquesta regla. +settings.default_branch_desc = Tria una branca per defecte del repositori per a «pull requests» i commits: settings.merge_style_desc = Estils de fusió settings.default_merge_style_desc = Estil de fusió predeterminat +settings.choose_branch = Tria una branca… +settings.no_protected_branch = No hi ha branques protegides. +settings.protected_branch_required_rule_name = Nom de regla obligatori +settings.protected_branch_duplicate_rule_name = Ja hi ha una regla per a aquest set de branques +settings.protected_branch_required_approvals_min = El nombre d'aprovacions necessàries no pot ser negatiu. settings.tags.protection = Protecció d'etiquetes settings.tags.protection.pattern = Patró d'etiquetes settings.tags.protection.allowed.users = Usuaris permesos @@ -2451,35 +2531,6 @@ settings.unarchive.text = Des-arxivar el repositori farà possible rebre-hi comm settings.unarchive.success = El repositori s'ha des-arxivat satisfactòriament. settings.unarchive.error = Hi ha hagut un error en des-arxivar el repositori. Vegeu el log per més detalls. settings.update_avatar_success = S'ha actualitzat l'avatar del repositori. -editor.commit_empty_file_text = L'arxiu que s'està a punt de cometre és buit. Procedir? -pulls.no_merge_not_ready = Aquesta «pull request» no està llesta per a ser fusionada, comprova l'estat de revisió i les comprovacions d'estat. -activity.navbar.pulse = Pols -settings.event_action_recover_desc = La execució de l'acció ha finalitzat amb èxit després de que la última execució en el mateix flux fallés. -settings.event_action_success_desc = La execució de l'acció ha tingut èxit. -settings.event_package_desc = Paquet creat o eliminat en un repositori. -settings.branch_filter = Filtre de branca -settings.branch_filter_desc = Llista blanca de la branca per als esdeveniments de pujada i creació/eliminació de branques, especificada com un patró «glob». Si és buida o *, s'enregistraran els esdeveniments de totes les branques. Vegeu la documentació %[2]s per a la sintaxi. Per exemple: master, {master,release*}. -settings.protect_whitelist_deploy_keys = Posar les claus d'implementació amb accés d'escriptura per pujar (push) a la llista blanca. -settings.protect_no_valid_status_check_patterns = Sense patrons de comprovació d'estat vàlids. -settings.protect_required_approvals = Aprovacions necessàries -settings.protect_required_approvals_desc = Permet fusionar només «pull requests» amb prou revisions positives. -settings.protect_approvals_whitelist_enabled = Limita les aprovacions als usuaris o equips de la llista blanca -settings.protect_approvals_whitelist_enabled_desc = Només comptaran cap al total d'aprovacions necessàries les revisions d'usuaris i equips de la llista blanca. Si la llista és buida, en comptaran les revisions de tothom amb accés d'escriptura. -settings.protect_approvals_whitelist_users = Revisors en la llista blanca -settings.protect_approvals_whitelist_teams = Equips en la llista blanca per revisions -settings.block_rejected_reviews_desc = No es podrà fusionar quan els revisors oficials en sol·licitin canvis, encara que hi hagi prou aprovacions. -settings.block_on_official_review_requests = Bloquejar fusió en sol·licituds de revisió oficials -settings.block_on_official_review_requests_desc = No es podrà fusionar quan tingui sol·licituds de revisió oficials, encara que hi hagi prou aprovacions. -settings.block_outdated_branch = Bloquejar fusió si la «pull request» és obsoleta -settings.block_outdated_branch_desc = No es podrà fusionar quan la branca cap (head) sigui darrera la branca base. -settings.enforce_on_admins = Aplica aquesta regla als administradors del repositori -settings.enforce_on_admins_desc = Els repositoris del repositori no poden saltar-se aquesta regla. -settings.default_branch_desc = Tria una branca per defecte del repositori per a «pull requests» i commits: -settings.choose_branch = Tria una branca… -settings.no_protected_branch = No hi ha branques protegides. -settings.protected_branch_required_rule_name = Nom de regla obligatori -settings.protected_branch_duplicate_rule_name = Ja hi ha una regla per a aquest set de branques -settings.protected_branch_required_approvals_min = El nombre d'aprovacions necessàries no pot ser negatiu. settings.lfs_filelist = Fitxers LFS desats en aquest repositori settings.lfs_no_lfs_files = No hi ha fitxers LFS desats en aquest repositori settings.lfs_findcommits = Cercar commits @@ -2497,10 +2548,12 @@ settings.lfs_lock_path = Camí al fitxer per bloquejar… settings.lfs_locks_no_locks = Sense bloqueigs settings.lfs_lock_file_no_exist = El fitxer bloquejat no existeix en la branca per defecte settings.lfs_force_unlock = Forçar el desbloqueig +settings.lfs_pointers.found = S'ha trobat %d punters de blob - %d associats, %d no associats (%d no són a l'emmagatzematge) settings.lfs_pointers.sha = Hash del «blob» settings.lfs_pointers.inRepo = En repositori settings.lfs_pointers.exists = Existeix en magatzem settings.lfs_pointers.accessible = Accessible per l'usuari +settings.lfs_pointers.associateAccessible = Associar %d OIDs accessibles settings.rename_branch_failed_protected = No es pot canviar el nom de la branca %s perquè està protegida. settings.rename_branch_failed_exist = No es pot canviar el nom de la branca perquè la branca objectiu %s existeix. settings.rename_branch_failed_not_exist = No es pot canviar el nom de la branca %s perquè no existeix. @@ -2521,13 +2574,13 @@ release.asset_name = Nom d'actiu release.asset_external_url = URL externa release.add_external_asset = Afegeix actiu extern release.invalid_external_url = URL externa invàlida: "%s" +release.summary_card_alt = Panell de resum d'una publicació titulada "%s" al repositori %s branch.name = Nom de branca branch.already_exists = Ja existeix una branca anomenada "%s". branch.delete = Eliminar branca "%s" branch.delete_html = Eliminar branca -branch.deletion_success = S'ha eliminat la branca "%s". -release.summary_card_alt = Panell de resum d'una publicació titulada "%s" al repositori %s branch.delete_desc = Eliminar una branca és permanent. Malgrat la branca eliminada pot seguir existint durant una estona abans d'eliminar-se completament, aquesta acció NO es pot desfer en la majoria de casos. Continuar? +branch.deletion_success = S'ha eliminat la branca "%s". branch.deletion_failed = No s'ha pogut eliminar la branca "%s". branch.delete_branch_has_new_commits = No s'ha pogut eliminar la branca "%s" perquè s'han afegit nou commits després de fusionar-la. branch.create_branch = Crear branca %s @@ -2567,13 +2620,10 @@ find_file.no_matching = No s'ha trobat un fitxer coincident error.csv.too_large = No es pot mostrar aquest fitxer perquè és massa gran. error.csv.unexpected = No es pot mostrar aquest fitxer perquè conté un caràcter inesperat a la línia %d i columna %d. error.csv.invalid_field_count = No es pot mostrar aquest fitxer perquè té un nombre d'entrades incorrecte a la línia %d. -settings.web_hook_name_feishu = Feishu / Lark Suite -settings.web_hook_name_larksuite_only = Lark Suite -settings.web_hook_name_wechatwork = WeCom (Wechat Work) -settings.lfs_pointers.found = S'ha trobat %d punters de blob - %d associats, %d no associats (%d no són a l'emmagatzematge) -settings.lfs_pointers.associateAccessible = Associar %d OIDs accessibles + settings.matrix.access_token_helper = Es recomana muntar un compte de Matrix dedicat per això. El testimoni d'accés el podeu trobar al client web Element (en una pestanya privada/d'incògnit) > User menu (cantó superior esquerre) > All settings > Help & About > Advanced > Access Token (sota de l'URL del homeserver). Tanqueu la pestanya privada/d'incògnit (tancar la sessió invalidaria el testimoni). settings.matrix.room_id_helper = L'ID de sala el podeu trobar al client web Element > Room Settings > Advanced > Internal room ID. Per exemple: %s. + diff.parent = pare [user] @@ -2616,16 +2666,22 @@ public_activity.visibility_hint.admin_private = Aquesta activitat és visible pe public_activity.visibility_hint.self_private_profile = La vostra activitat és visible només per vós i pels administradors de la instància perquè el vostre perfil és privat. Configurar. change_avatar = Canvieu el vostre avatar… joined_on = S'ha unit el %s + starred = Repositoris destacats [git.filemode] executable_file = Fitxer executable symbolic_link = Enllaç simbòlic submodule = Submòdul -normal_file = Fitxer normal + directory = Directori +normal_file = Fitxer normal [markup] +filepreview.truncated = La vista prèvia s'ha truncat + +filepreview.line = Línia %[1]d a %[2]s +filepreview.lines = Línies %[1]d a %[2]d en %[3]s [translation_meta] test = Aquest és un text de prova. No es mostra a l'interfície d'usuari de Forgejo, però s'utilitza amb finalitats de prova. Pots introduir "d'acord" per a estalviar temps (o alguna frase divertida al teu gust) i arribar a la dolça fita del 100% :) @@ -2682,16 +2738,16 @@ teams.add_team_member = Afegir un membre a l'equip teams.invite_team_member = Convidar a %s teams.invite_team_member.list = Invitacions pendents teams.delete_team_title = Eliminar l'equip -org_desc = Descripció -team_desc = Descripció -team_name = Nom de l'equip -lower_members = membres + members = Membres teams = Equips code = Codi +lower_members = membres lower_repositories = repositoris -settings.repoadminchangeteam = L'administrador del repositori pot donar-ne i treure'n l'accés als equips -members.leave = Marxar +org_desc = Descripció +team_name = Nom de l'equip +team_desc = Descripció + org_name_holder = Nom de l'organització org_full_name_holder = Nom complet de l'organització org_name_helper = Els noms d'organització han de ser curts i memorables. @@ -2709,12 +2765,14 @@ follow_blocked_user = No podeu seguir aquesta organització perquè us ha bloque form.name_reserved = El nom d'organització "%s" és reservat. form.name_pattern_not_allowed = El patró "%s" no està permès en un nom d'organització. form.create_org_not_allowed = No teniu permisos per crear una organització. +settings.repoadminchangeteam = L'administrador del repositori pot donar-ne i treure'n l'accés als equips settings.change_orgname_prompt = Nota: Canviar el nom de l'organització també en canviarà l'URL i alliberarà el nom antic. settings.change_orgname_redirect_prompt.with_cooldown.one = El nom antic de l'organització serà disponible a tothom després d'un període de protecció de %[1]d dia. Encara podreu reclamar el nom antic durant el període de protecció. settings.change_orgname_redirect_prompt.with_cooldown.few = El nom antic de l'organització serà disponible a tothom després d'un període de protecció de %[1]d dies. Encara podreu reclamar el nom antic durant el període de protecció. settings.hooks_desc = Afegir webhooks que s'activaran a tots els repositoris en aquesta organització. members.public_helper = Ocultar members.private_helper = Fer visible +members.leave = Marxar teams.admin_access_helper = Els membres poden pujar (push) i baixar (pull) als repositoris de l'equip i afegir-hi col·laboradors. teams.delete_team_desc = Eliminar un equip fa que els seus membres perdin accés al repositori. Continuar? teams.delete_team_success = S'ha eliminat l'equip. @@ -2743,51 +2801,53 @@ dashboard.delete_missing_repos = Suprimir tots els repositoris que no tinguin el dashboard.delete_missing_repos.started = S'ha iniciat la tasca per suprimir tots els repositoris que no tinguin els fitxers de Git. dashboard.update_mirrors = Actualitzar les rèpliques dashboard.archive_cleanup = Suprimir els arxius de repositori antics +dashboard.memory_allocate_times = Assignacions de memòria +repositories = Repositoris +hooks = Webhooks +users.name = Nom d'usuari +users.full_name = Nom complet +users.activated = Activat +users.admin = Administrador +users.list_status_filter.is_admin = Administrador +emails.activated = Activat +emails.filter_sort.email = Correu electrònic +emails.filter_sort.name = Nom d'usuari orgs.name = Nom orgs.teams = Equips +orgs.members = Membres +orgs.new_orga = Nova organització repos.name = Nom repos.issues = Problemes repos.size = Mida -auths.name = Nom -config.db_name = Nom -config.mailer_name = Nom -monitor.name = Nom -notices.type = Tipus -config.db_type = Tipus -auths.type = Tipus +packages.name = Nom +packages.version = Versió packages.type = Tipus packages.repository = Repositori -notices.type_1 = Repositori packages.size = Mida +auths.name = Nom +auths.type = Tipus auths.enabled = Habilitat -config.ssh_enabled = Habilitat -config.lfs_enabled = Habilitat -config.mailer_enabled = Habilitat -config.db_host = Amfitrió auths.host = Amfitrió auths.port = Port +config.ssh_enabled = Habilitat config.ssh_port = Port -users.name = Nom d'usuari -emails.filter_sort.name = Nom d'usuari +config.lfs_enabled = Habilitat +config.db_type = Tipus +config.db_host = Amfitrió +config.db_name = Nom config.db_user = Nom d'usuari config.db_schema = Esquema config.db_ssl_mode = SSL config.db_path = Camí -users.full_name = Nom complet -users.activated = Activat -emails.activated = Activat -emails.filter_sort.email = Correu electrònic -users.admin = Administrador +config.mailer_enabled = Habilitat +config.mailer_name = Nom +monitor.name = Nom +monitor.queue.name = Nom +monitor.queue.type = Tipus +notices.type = Tipus +notices.type_1 = Repositori notices.desc = Descripció -packages.name = Nom -packages.version = Versió -orgs.members = Membres -orgs.new_orga = Nova organització -repositories = Repositoris -hooks = Webhooks -dashboard.delete_generated_repository_avatars = Suprimir els avatars de repositori generats -dashboard.sync_repo_tags = Sincronitzar les etiquetes de les dades de Git a la base de dades -dashboard.repo_health_check = Comprovar la salut de tots els repositoris + dashboard = Tauler self_check = Comprovació automàtica identity_access = Identitat i accés @@ -2811,6 +2871,12 @@ dashboard.operations = Operacions de manteniment dashboard.system_status = Estat del sistema dashboard.operation_name = Nom d'operació dashboard.clean_unbind_oauth = Neteja les connexions OAuth desenllaçades +dashboard.delete_generated_repository_avatars = Suprimir els avatars de repositori generats +dashboard.sync_repo_tags = Sincronitzar les etiquetes de les dades de Git a la base de dades +dashboard.repo_health_check = Comprovar la salut de tots els repositoris + +notices = Notificacions del sistema +dashboard.operation_switch = Intercanvia dashboard.operation_run = Executar dashboard.clean_unbind_oauth_success = S'han eliminat totes les connexions OAuth desenllaçades. dashboard.task.started = Tasca iniciada: %[1]s @@ -2820,12 +2886,13 @@ dashboard.task.error = Error en la tasca: %[1]s: %[3]s dashboard.task.finished = Tasca: %[1] iniciada per %[2s] ha finalitzat dashboard.task.unknown = Tasca desconeguda: %[1]s dashboard.cron.started = Cron iniciat: %[1]s +dashboard.cron.process = Cron: %[1]s dashboard.cron.cancelled = Cron: %[1]s cancel·lat: %[3]s dashboard.cron.error = Error amb Cron: %s: %[3]s dashboard.sync_repo_branches = La sincronització ha ignorat branques de les dades Git a la base de dades dashboard.check_repo_stats = Comprovar totes les estadístiques del repositori dashboard.deleted_branches_cleanup = Netejar branques eliminades -dashboard.update_migration_poster_id =Actualitza les ID de l'autor de migració +dashboard.update_migration_poster_id = Actualitza les ID de l'autor de migració dashboard.git_gc_repos = Recull la brossa de tots els repositoris dashboard.resync_all_sshkeys = Actualitzar el fitxer ".ssh/authorized_keys" amb les claus SSH de Forgejo. dashboard.resync_all_sshprincipals = Actualitzar el fitxer ".ssh/authorized_principals" amb els principals SSH de Forgejo. @@ -2835,6 +2902,30 @@ dashboard.sync_external_users = Sincronitzar dades d'usuari externes dashboard.cleanup_hook_task_table = Netejar la taula hook_task dashboard.cleanup_packages = Netejar paquets caducats dashboard.cleanup_actions = Netejar registres i artefactes d'accions caducats +dashboard.server_uptime = Temps de funcionament del servidor +dashboard.current_goroutine = Goroutines actuals +dashboard.current_memory_usage = Ús de memòria actual +dashboard.total_memory_allocated = Total de memòria adjudicada +dashboard.memory_obtained = Memòria rebuda +dashboard.pointer_lookup_times = Temps de consulta dels punters +dashboard.memory_free_times = Alliberaments de memòria +dashboard.current_heap_usage = Ús del heap actual +dashboard.heap_memory_obtained = Memòria de heap rebuda +dashboard.heap_memory_idle = Memòria del heap en repòs +dashboard.heap_memory_in_use = Memòria del heap en ús +dashboard.heap_memory_released = Memòria del heap alliberada +dashboard.heap_objects = Objectes del heap +dashboard.bootstrap_stack_usage = Ús de l'arrancada de la pila (stack) +dashboard.stack_memory_obtained = Memòria de la pila (stack) rebuda +dashboard.mspan_structures_usage = Ús de les estructures MSpan +dashboard.mspan_structures_obtained = Estructures MSpan rebudes +dashboard.mcache_structures_usage = Ús de les estructures MCache +dashboard.mcache_structures_obtained = Estructures MCache rebudes +dashboard.gc_metadata_obtained = Metadades del GC rebudes +dashboard.next_gc_recycle = Proper cicle de GC +dashboard.last_gc_time = Temps des del darrer GC +dashboard.total_gc_pause = Pausa total de GC +dashboard.last_gc_pause = Darrera pausa de GC dashboard.delete_old_actions = Eliminar totes les activitats antigues de la base de dades dashboard.delete_old_actions.started = Eliminar totes les activitats antigues des que s'ha iniciat la base de dades. dashboard.delete_old_system_notices = Eliminar totes les notificacions de sistema antigues de la base de dades @@ -2844,8 +2935,6 @@ dashboard.stop_endless_tasks = Aturar tasques d'accions infinites dashboard.cancel_abandoned_jobs = Cancel·lar feines d'accions abandonades dashboard.sync_branch.started = S'ha iniciat la sincronització de branca dashboard.sync_tag.started = S'ha iniciat la sincronització d'etiquetes -dashboard.cron.process = Cron: %[1]s -notices = Notificacions del sistema dashboard.rebuild_issue_indexer = Reconstruir l'indexador d'incidències users.user_manage_panel = Gestionar comptes d'usuari users.new_account = Crear compte d'usuari @@ -2853,6 +2942,7 @@ users.restricted = Restringit users.reserved = Reservat users.bot = Bot users.remote = Remot +users.2fa = A2F users.repos = Repos users.created = Creats users.last_login = Darrer inici de sessió @@ -2891,6 +2981,18 @@ users.purge = Purgar usuari users.purge_help = Elimina forçosament l'usuari i tots els seus repositoris, organitzacions i paquets. També s'eliminaran tots els seus comentaris i incidències. users.still_own_packages = Aquest usuari encara té un o més paquets. Elimineu-los abans. users.deletion_success = S'ha eliminat l'usuari. +users.reset_2fa = Restableix l'A2F +users.list_status_filter.menu_text = Filtrar +users.list_status_filter.reset = Restaurar +users.list_status_filter.is_active = Actiu +users.list_status_filter.not_active = Inactiu +users.list_status_filter.not_admin = No administrador +users.list_status_filter.is_restricted = Restringit +users.list_status_filter.not_restricted = No restringit +users.list_status_filter.is_prohibit_login = Inici de sessió prohibit +users.list_status_filter.not_prohibit_login = Inici de sessió permès +users.list_status_filter.is_2fa_enabled = A2F activada +users.list_status_filter.not_2fa_enabled = A2F desactivada users.details = Detalls d'usuari emails.email_manage_panel = Gestionar correus d'usuari emails.primary = Principal @@ -2934,6 +3036,10 @@ auths.updated = Actualitzat auths.auth_type = Tipus d'autenticació auths.auth_name = Nom d'autenticació auths.security_protocol = Protocol de seguretat +auths.bind_dn = DN bind +auths.bind_password = Contrasenya bind +auths.user_base = Base de cerca d'usuari +auths.user_dn = DN d'usuari auths.attribute_username = Atribut de nom d'usuari auths.attribute_username_placeholder = Deixeu-ho buit per usar el nom d'usuari de Forgejo. auths.attribute_name = Atribut de nom @@ -2941,6 +3047,9 @@ auths.attribute_surname = Atribut de cognom auths.attribute_mail = Atribut de correu electrònic auths.attribute_ssh_public_key = Atribut de clau pública SSH auths.attribute_avatar = Atribut d'avatar +auths.attributes_in_bind = Recupera els atributs del context bind DN +auths.default_domain_name = Nom de domini per defecte usat per l'adreça de correu +auths.allow_deactivate_all = Permet que un resultat de cerca buit desactivi tots els usuaris auths.use_paged_search = Usar cerca en pàgines auths.search_page_size = Mida de pàgina auths.filter = Filtre d'usuari @@ -2948,6 +3057,11 @@ auths.admin_filter = Filtre d'administrador auths.restricted_filter = Filtre restringit auths.restricted_filter_helper = Deixeu-ho buit per no marcar cap usuari com a restringit. Useu un asterisc ("*") per marcar tots els usuaris que no coincideixin amb el Filtre d'administrador com a restringits. auths.verify_group_membership = Verificar l'adhesió de grup a LDAP (deixeu el filtre buit per ignorar-ho) +auths.group_search_base = DN base de cerca de grup +auths.group_attribute_list_users = Atribut de grup amb la llista d'usuaris +auths.user_attribute_in_group = Atribut d'usuari llistat en el grup +auths.map_group_to_team = Assigna els grups LDAP a equips d'organització (deixeu-ho buit per saltar-ho) +auths.map_group_to_team_removal = Elimina els usuaris dels equips sincronitzats si no pertanyen al grup LDAP corresponent auths.enable_ldap_groups = Activar grups LDAP auths.smtp_auth = Tipus d'autenticació SMTP auths.smtphost = Amfitrió SMTP @@ -2957,6 +3071,9 @@ auths.allowed_domains_helper = Deixeu-ho buit per permetre tots els dominis. Sep auths.skip_tls_verify = Salta't la verificació TLS auths.force_smtps = Força l'SMTPS auths.force_smtps_helper = L'SMTPS sempre usa el port 465. Indiqueu això per forçar l'SMTPS en un altre port. (Sinó s'usarà l'STARTTLS en altres ports, sempre que l'amfitrió ho suporti.) +auths.helo_hostname = Hostname HELO +auths.helo_hostname_helper = Hostname enviat amb HELO. Deixeu-ho en blanc per enviar el hostname actual. +auths.disable_helo = Deshabilita HELO auths.pam_service_name = Nom de servei PAM auths.pam_email_domain = Domini de correu PAM (opcional) auths.oauth2_provider = Proveïdor OAuth2 @@ -2969,6 +3086,26 @@ auths.oauth2_tokenURL = URL de testimoni (token) auths.oauth2_authURL = URL d'autorització auths.oauth2_profileURL = URL de perfil auths.oauth2_emailURL = URL de correu +auths.skip_local_two_fa = Salta l'A2F local +auths.skip_local_two_fa_helper = No marcar aquesta opció farà que els usuaris que tenen l'A2F definida igualment hauran de passar l'A2F per iniciar sessió +auths.oauth2_tenant = Tenant +auths.oauth2_scopes = Àmbits addicionals +auths.oauth2_required_claim_value_helper = Assigneu aquest valor per restringir l'inici de sessió des d'aquesta font als usuaris que declarin aquest nom i valor +auths.oauth2_map_group_to_team_removal = Elimina els usuaris dels equips sincronitzats si no pertanyen al grup corresponent. +auths.tips = Consells +auths.tips.gmail_settings = Configuracions de Gmail: +auths.tips.oauth2.general = Autenticació OAuth2 +auths.tips.oauth2.general.tip = Quan registreu una nova autenticació OAuth2, l'URL de crida/redirecció serà: +auths.tip.oauth2_provider = Proveïdor OAuth2 +auths.tip.bitbucket = Registreu un nou consumidor OAuth2 a %s i afegiu els permisos "Compte" - "Lectura" +auths.tip.nextcloud = Registreu un nou consumidor OAuth en la vostra instància amb el menú "Configuració -> Seguretat -> Client OAuth 2.0" +auths.tip.dropbox = Crea una nova aplicació a %s +auths.tip.facebook = Registreu una nova aplicació a %s i afegiu el producte "Facebook Login" +auths.tip.github = Registra una aplicació OAuth nova a %s +auths.tip.gitlab_new = Registra una aplicació nova a %s +auths.tip.google_plus = Obté les credencials del client OAuth2 amb la consola del Google API a %s +auths.tip.openid_connect = Usa l'URL d'OpenID Connect Discovery (/.well-known/openid-configuration) per a especificar els endpoints +auths.tip.mastodon = Introduïu l'URL a una instància diferent de Mastodon amb la que voleu autenticar-vos (o bé useu la instància per defecte) auths.edit = Editar font d'autenticació auths.activated = S'ha activat aquesta font d'autenticació auths.new_success = S'ha afegit l'autenticació "%s". @@ -2980,43 +3117,27 @@ auths.delete_auth_desc = Eliminar una font d'autenticació fa que els usuaris qu auths.still_in_used = Aquesta font d'autenticació encara s'utilitza. Convertiu o elimineu els usuaris que en fan ús abans. auths.deletion_success = S'ha eliminat la font d'autenticació. auths.login_source_exist = La font d'autenticació "%s" ja existeix. +auths.unable_to_initialize_openid = No s'ha pogut inicialitzar el proveïdor d'OpenID Connect: %s +auths.invalid_openIdConnectAutoDiscoveryURL = URL d'Auto Discovery invàlida (ha de ser una URL vàlida començant amb http:// o https://) config.server_config = Configuració de servidor config.app_name = Títol de la instància config.app_slogan = Eslògan de la instància config.app_ver = Versió de Forgejo config.app_url = URL base config.custom_conf = Camí al fitxer de configuració +config.custom_file_root_path = Camí arrel dels fitxers personalitzada config.domain = Domini del servidor config.offline_mode = Mode local -config.git_version = Versió de Git -config.ssh_config = Configuració SSH -config.run_user = Executa com a usuari -auths.tips.oauth2.general = Autenticació OAuth2 -auths.tips.oauth2.general.tip = Quan registreu una nova autenticació OAuth2, l'URL de crida/redirecció serà: -auths.tip.oauth2_provider = Proveïdor OAuth2 -auths.tip.bitbucket = Registreu un nou consumidor OAuth2 a %s i afegiu els permisos "Compte" - "Lectura" -auths.tip.nextcloud = Registreu un nou consumidor OAuth en la vostra instància amb el menú "Configuració -> Seguretat -> Client OAuth 2.0" -auths.tip.dropbox = Crea una nova aplicació a %s -auths.tip.facebook = Registreu una nova aplicació a %s i afegiu el producte "Facebook Login" -config.repo_root_path = Camí arrel del repositori -auths.bind_dn = DN bind -auths.bind_password = Contrasenya bind -auths.user_base = Base de cerca d'usuari -auths.user_dn = DN d'usuari -auths.attributes_in_bind = Recupera els atributs del context bind DN -auths.default_domain_name = Nom de domini per defecte usat per l'adreça de correu -auths.allow_deactivate_all = Permet que un resultat de cerca buit desactivi tots els usuaris -auths.oauth2_required_claim_value_helper = Assigneu aquest valor per restringir l'inici de sessió des d'aquesta font als usuaris que declarin aquest nom i valor -auths.tip.mastodon = Introduïu l'URL a una instància diferent de Mastodon amb la que voleu autenticar-vos (o bé useu la instància per defecte) -auths.unable_to_initialize_openid = No s'ha pogut inicialitzar el proveïdor d'OpenID Connect: %s -auths.invalid_openIdConnectAutoDiscoveryURL = URL d'Auto Discovery invàlida (ha de ser una URL vàlida començant amb http:// o https://) -config.custom_file_root_path = Camí arrel dels fitxers personalitzada config.disable_router_log = Desactiva els registres de l'encaminador +config.run_user = Executa com a usuari config.run_mode = Mode d'execució +config.git_version = Versió de Git config.app_data_path = Camí a les dades d'aplicació +config.repo_root_path = Camí arrel del repositori config.log_file_root_path = Camí de registres config.script_type = Tipus d'script config.reverse_auth_user = Usuari d'autenticació al servidor intermediari revers +config.ssh_config = Configuració SSH config.ssh_start_builtin_server = Usa el servidor integrat config.ssh_domain = Domini del servidor SSH config.ssh_listen_port = Port d'escolta @@ -3071,9 +3192,9 @@ config.send_test_mail_submit = Envia config.test_mail_failed = No s'ha pogut enviar el correu de prova a "%s": %v config.test_mail_sent = S'ha enviat un correu de prova a "%s". config.cache_config = -config.cache_adapter =Adaptador de la memòria cau -config.cache_interval =Interval de la memòria cau -config.cache_conn =Connexió de la memòria cau +config.cache_adapter = Adaptador de la memòria cau +config.cache_interval = Interval de la memòria cau +config.cache_conn = Connexió de la memòria cau config.cache_item_ttl = TTL dels ítems de la memòria cau config.cache_test = Prova la memòria cau config.cache_test_failed = No s'ha pogut examinar la memòria cau: %v. @@ -3089,39 +3210,64 @@ config.https_only = Només HTTPS config.picture_config = Configuració d'imatge i avatar config.disable_gravatar = Deshabilita Gravatar config.enable_federated_avatar = Habilita els avatars federats -dashboard.operation_switch = Intercanvia -users.2fa = A2F -users.reset_2fa = Restableix l'A2F -auths.group_search_base = DN base de cerca de grup -auths.group_attribute_list_users = Atribut de grup amb la llista d'usuaris -auths.user_attribute_in_group = Atribut d'usuari llistat en el grup -auths.map_group_to_team = Assigna els grups LDAP a equips d'organització (deixeu-ho buit per saltar-ho) -auths.map_group_to_team_removal = Elimina els usuaris dels equips sincronitzats si no pertanyen al grup LDAP corresponent -auths.helo_hostname = Hostname HELO -auths.helo_hostname_helper = Hostname enviat amb HELO. Deixeu-ho en blanc per enviar el hostname actual. -auths.disable_helo = Deshabilita HELO -auths.skip_local_two_fa = Salta l'A2F local -auths.skip_local_two_fa_helper = No marcar aquesta opció farà que els usuaris que tenen l'A2F definida igualment hauran de passar l'A2F per iniciar sessió -auths.oauth2_tenant = Tenant -auths.oauth2_scopes = Àmbits addicionals -auths.oauth2_map_group_to_team_removal = Elimina els usuaris dels equips sincronitzats si no pertanyen al grup corresponent. -auths.tips = Consells -auths.tips.gmail_settings = Configuracions de Gmail: -auths.tip.github = Registra una aplicació OAuth nova a %s -auths.tip.gitlab_new = Registra una aplicació nova a %s -auths.tip.google_plus = Obté les credencials del client OAuth2 amb la consola del Google API a %s -auths.tip.openid_connect = Usa l'URL d'OpenID Connect Discovery (/.well-known/openid-configuration) per a especificar els endpoints + dashboard.update_checker = Comprovador d'actualització [actions] +runners.name = Nom +runners.owner_type = Tipus +runners.description = Descripció +runners.labels = Etiquetes +runners.task_list.repository = Repositori +runners.task_list.commit = Commit +runners.version = Versió +runs.commit = Commit runs.no_workflows.help_write_access = No sabeu com començar amb Forgejo Actions? Feu un cop d'ull als primers passos a la documentació d'usuari per escriure el vostre primer flux de treball, llavors configureu un runner Forgejo per a executar els vostres treballs. runs.no_workflows.help_no_write_access = Per saber-ne més sobre Forgejo Actions, vegeu la documentació. +variables = Variables +variables.management = Gestionar variables +variables.creation = Afegir variables +variables.none = Encara no hi ha variables. +variables.deletion = Eliminar variable +variables.deletion.description = Eliminar una variable és permanent i no es pot desfer. Continua? +variables.description = Les variables es passaran a certes accions i no es poden llegir altrament. +variables.edit = Editar variable +variables.not_found = No s'ha trobat la variable. +variables.deletion.failed = No s'ha pogut eliminar la variable. +variables.deletion.success = S'ha canviar el nom de la variable. +variables.creation.failed = No s'ha pogut afegir la variable. +variables.creation.success = S'ha afegit la variable "%s". +variables.update.failed = No s'ha pogut editar la variable. +variables.update.success = S'ha editat la variable. -[projects] -deleted.display_name = Projecte eliminat -type-1.display_name = Projecte individual -type-2.display_name = Projecte del repositori -type-3.display_name = Projecte de l'organització +[repo.permissions] +code.read = Lectura: Accedir i clonar el codi del repositori. +code.write = Escriptura: Pujar (push) al repositori, crear branques i etiquetes. +issues.read = Lectura: Llegir i crear incidències i comentaris. +issues.write = Escriptura: Tancar incidències i gestionar metadades com etiquetes, fites, assignacions, terminis i dependències. +pulls.read = Lectura: Llegir i crear «pull requests». +pulls.write = Escriptura: Tancar «pull requests» i gestionar metadades com etiquetes, fites, assignacions, terminis i dependències. +releases.read = Lectura: Veure i baixar publicacions. +releases.write = Escriptura: Publicar, editar i eliminar publicacions i els seus recursos. +wiki.read = Lectura: Llegir la wiki integrada i el seu historial. +wiki.write = Escriptura: Crear, modificar i eliminar pàgines en la wiki integrada. +projects.read = Lectura: Accedir els taulells de projecte del repositori. +projects.write = Escriptura: Crear projectes i columnes, i editar-los. +packages.read = Lectura: Llegir i baixar paquets assignats al repositori. +packages.write = Escriptura: Publicar i eliminar paquets assignats al repositori. +actions.read = Lectura: Veure l'execució dels fluxos de treball i els seus registres (logs). +actions.write = Escriptura: Activar, reiniciar i cancel·lar fluxos de treball. Gestionar la delegació de confiança als autors de «pull requests». +ext_issues = Accedir a l'enllaç a un gestor d'incidències extern. Els permisos es gestionen allà. +ext_wiki = Accedir a l'enllaç a una wiki externa. Els permisos es gestionen allà. + +[graphs] +component_loading = Carregant %s… +component_loading_failed = No s'ha pogut carregar %s +component_loading_info = Això trigarà una estona… +component_failed_to_load = Ha passat un error inesperat. +code_frequency.what = freqüència de codi +contributors.what = contribucions +recent_commits.what = commits recents [secrets] secrets = Secrets @@ -3136,31 +3282,8 @@ deletion.success = S'ha eliminat el secret. deletion.failed = No s'ha pogut eliminar el secret. management = Gestionar secrets -[repo.permissions] -code.read = Lectura: Accedir i clonar el codi del repositori. -issues.read = Lectura: Llegir i crear incidències i comentaris. -pulls.read = Lectura: Llegir i crear «pull requests». -releases.read = Lectura: Veure i baixar publicacions. -wiki.read = Lectura: Llegir la wiki integrada i el seu historial. -projects.read = Lectura: Accedir els taulells de projecte del repositori. -packages.read = Lectura: Llegir i baixar paquets assignats al repositori. -actions.read = Lectura: Veure l'execució dels fluxos de treball i els seus registres (logs). -code.write = Escriptura: Pujar (push) al repositori, crear branques i etiquetes. -issues.write = Escriptura: Tancar incidències i gestionar metadades com etiquetes, fites, assignacions, terminis i dependències. -pulls.write = Escriptura: Tancar «pull requests» i gestionar metadades com etiquetes, fites, assignacions, terminis i dependències. -releases.write = Escriptura: Publicar, editar i eliminar publicacions i els seus recursos. -wiki.write = Escriptura: Crear, modificar i eliminar pàgines en la wiki integrada. -projects.write = Escriptura: Crear projectes i columnes, i editar-los. -packages.write = Escriptura: Publicar i eliminar paquets assignats al repositori. -actions.write = Escriptura: Activar, reiniciar i cancel·lar fluxos de treball. Gestionar la delegació de confiança als autors de «pull requests». -ext_issues = Accedir a l'enllaç a un gestor d'incidències extern. Els permisos es gestionen allà. -ext_wiki = Accedir a l'enllaç a una wiki externa. Els permisos es gestionen allà. - -[graphs] -component_loading = Carregant %s… -component_loading_failed = No s'ha pogut carregar %s -component_loading_info = Això trigarà una estona… -component_failed_to_load = Ha passat un error inesperat. -code_frequency.what = freqüència de codi -contributors.what = contribucions -recent_commits.what = commits recents \ No newline at end of file +[projects] +deleted.display_name = Projecte eliminat +type-1.display_name = Projecte individual +type-2.display_name = Projecte del repositori +type-3.display_name = Projecte de l'organització \ No newline at end of file diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 95457b6dcb..9a59271ac1 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -37,6 +37,18 @@ twofa=Dvoufázové ověření twofa_scratch=Dvoufaktorový kód passcode=Přístupový kód +webauthn_insert_key=Vložte svůj bezpečnostní klíč +webauthn_sign_in=Stiskněte tlačítko na svém bezpečnostním klíči. Pokud bezpečnostní klíč nemá žádné tlačítko, vložte jej znovu. +webauthn_press_button=Stiskněte tlačítko na bezpečnostním klíči… +webauthn_use_twofa=Použít dvoufaktorový kód z vašeho telefonu +webauthn_error=Nepodařilo se přečíst váš bezpečnostní klíč. +webauthn_unsupported_browser=Váš prohlížeč momentálně nepodporuje WebAuthn. +webauthn_error_unknown=Došlo k neznámé chybě. Opakujte akci. +webauthn_error_insecure=WebAuthn podporuje pouze zabezpečená připojení. Pro testování přes HTTP můžete použít výchozí „localhost“ nebo „127.0.0.1“ +webauthn_error_unable_to_process=Server nemohl zpracovat váš požadavek. +webauthn_error_duplicated=Bezpečnostní klíč není pro tento požadavek povolen. Ujistěte se prosím, zda klíč již není registrován. +webauthn_error_empty=Musíte nastavit název tohoto klíče. +webauthn_error_timeout=Požadavek vypršel dříve, než se podařilo přečíst váš klíč. Znovu načtěte tuto stránku a akci opakujte. repository=Repozitář organization=Organizace mirror=Zrcadlo @@ -169,7 +181,7 @@ buttons.bold.tooltip=Přidat tučný text (Ctrl+B / ⌘B) buttons.italic.tooltip=Přidat kurzívu (Ctrl+I / ⌘I) buttons.quote.tooltip=Citace buttons.code.tooltip=Přidat kód -buttons.link.tooltip=Přidat odkaz (Ctrl+K / ⌘K) +buttons.link.tooltip=Přidat odkaz buttons.list.unordered.tooltip=Přidat odrážkový seznam buttons.list.ordered.tooltip=Přidat číslovaný seznam buttons.list.task.tooltip=Přidat seznam úkolů @@ -1142,6 +1154,14 @@ migrate_options_lfs_endpoint.label=Endpoint LFS migrate_options_lfs_endpoint.description=Migrace se pokusí použít váš vzdálený Git pro určení LFS serveru. Můžete také zadat vlastní koncový bod, pokud jsou data LFS repozitáře uložena někde jinde. migrate_options_lfs_endpoint.description.local=Podporována je také cesta k lokálnímu serveru. migrate_options_lfs_endpoint.placeholder=Ponecháte-li prázdné, koncový bod bude odvozen z adresy URL klonu +migrate_items=Položky pro migrování +migrate_items_wiki=Wiki +migrate_items_milestones=Milníky +migrate_items_labels=Štítky +migrate_items_issues=Problémy +migrate_items_pullrequests=Žádosti o sloučení +migrate_items_merge_requests=Sloučit žádosti +migrate_items_releases=Vydání migrate_repo=Migrovat repozitář migrate.clone_address=Migrovat / klonovat z URL migrate.clone_address_desc=HTTP(S) nebo URL Git „clone“ existujícího repozitáře @@ -1160,6 +1180,16 @@ migrate.migrating=Probíhá migrace z %s … migrate.migrating_failed=Migrace z %s se nezdařila. migrate.migrating_failed.error=Nepodařilo se migrovat: %s migrate.migrating_failed_no_addr=Migrace se nezdařila. +migrate.migrating_git=Migrace dat z Gitu +migrate.migrating_topics=Migrace témat +migrate.migrating_milestones=Migrace milníků +migrate.migrating_labels=Migrace štítků +migrate.migrating_releases=Migrace vydání +migrate.migrating_issues=Migrace problémů +migrate.migrating_pulls=Migrace žádostí o sloučení +migrate.cancel_migrating_title=Zrušit migraci +migrate.cancel_migrating_confirm=Chcete zrušit tuto migraci? + mirror_from=zrcadlo forked_from=forknuto z generated_from=generováno z @@ -2351,7 +2381,7 @@ settings.matrix.room_id=ID místnosti settings.matrix.message_type=Typ zprávy settings.archive.button=Archivovat repozitář settings.archive.header=Archivovat tento repozitář -settings.archive.text = Archivováním repozitáře jej celý převedete do stavu pouze pro čtení. Bude skryt z nástěnky. Nikdo (ani vy!) nebude moci vytvářet nové revize ani otevírat problémy a žádosti o sloučení. Je doporučena dokumentace důvodu archivace pro budoucí vývojáře, kteří chtějí vytvořit fork repozitáře. +settings.archive.text = Archivováním repozitáře jej celý převedete do stavu pouze pro čtení. Bude skryt z nástěnky. Nikdo (ani vy!) nebude moci vytvářet nové revize ani otevírat problémy a žádosti o sloučení. settings.archive.success=Repozitář byl úspěšně archivován. settings.archive.error=Nastala chyba při archivování repozitáře. Prohlédněte si záznam pro více detailů. settings.archive.error_ismirror=Nemůžete archivovat zrcadlený repozitář. @@ -2617,6 +2647,12 @@ pulls.ready_for_review = Připraveni na posouzení? settings.rename_branch_failed_protected = Nepodařilo se přejmenovat větev %s, jelikož se jedná o chráněnou větev. editor.push_out_of_date = Push je nejspíše zastaralý. stars = Oblíbení +n_commit_one = %s revize +n_commit_few = %s revizí +n_branch_one = %s větev +n_tag_one = %s značka +n_tag_few = %s značek +n_branch_few = %s větví settings.event_pull_request_enforcement = Vynucení settings.enforce_on_admins = Vynutit toto pravidlo pro správce repozitáře settings.enforce_on_admins_desc = Správci repozitáře nemohou obejít toto pravidlo. @@ -2640,6 +2676,8 @@ settings.transfer.button = Převést vlastnictví settings.transfer.modal.title = Převést vlastnictví wiki.search = Hledat na wiki wiki.no_search_results = Žádné výsledky +n_release_one = %s vydání +n_release_few = %s vydání settings.federation_settings = Nastavení federace settings.federation_apapiurl = Adresa URL federace tohoto repozitáře. Zkopírujte a vložte tuto adresu do nastavení federace jiného repozitáře jako adresu sledovaného repozitáře. settings.federation_not_enabled = Na vaší instanci není dostupná federace. @@ -2923,6 +2961,34 @@ dashboard.reinit_missing_repos=Znovu inicializovat všechny chybějící repozit dashboard.sync_external_users=Synchronizovat externí uživatelská data dashboard.cleanup_hook_task_table=Vyčistit tabulku hook_task dashboard.cleanup_packages=Vyčistit prošlé balíčky +dashboard.server_uptime=Doba provozu serveru +dashboard.current_goroutine=Aktuální goroutines +dashboard.current_memory_usage=Aktuální využití paměti +dashboard.total_memory_allocated=Celková přidělená paměť +dashboard.memory_obtained=Získaná paměť +dashboard.pointer_lookup_times=Časy vyhledávání ukazatelů +dashboard.memory_allocate_times=Alokace paměti +dashboard.memory_free_times=Uvolnění paměti +dashboard.current_heap_usage=Aktuální využití paměti zásobníku +dashboard.heap_memory_obtained=Získaná paměť zásobníku +dashboard.heap_memory_idle=Nečinná paměť zásobníku +dashboard.heap_memory_in_use=Používaná paměť zásobníku +dashboard.heap_memory_released=Uvolněná paměť zásobníku +dashboard.heap_objects=Objekty zásobníku +dashboard.bootstrap_stack_usage=Využití zásobníku prvotního zavedení +dashboard.stack_memory_obtained=Celková získaná pamět zásobníku +dashboard.mspan_structures_usage=Užití struktur MSpan +dashboard.mspan_structures_obtained=Získané struktury MSpan +dashboard.mcache_structures_usage=Využití struktur MCache +dashboard.mcache_structures_obtained=Získané struktury MCache +dashboard.profiling_bucket_hash_table_obtained=Získaná profilovací bucket hash tabulka +dashboard.gc_metadata_obtained=Získaná metadata GC +dashboard.other_system_allocation_obtained=Získaná alokace ostatních systémových prostředků +dashboard.next_gc_recycle=Příští recyklace GC +dashboard.last_gc_time=Doba od posledního GC +dashboard.total_gc_pause=Celková pauza GC +dashboard.last_gc_pause=Poslední pauza GC +dashboard.gc_times=Časy GC dashboard.delete_old_actions=Odstranit všechny staré aktivity z databáze dashboard.delete_old_actions.started=Spuštěno odstraňování všech starých aktivit z databáze. dashboard.update_checker=Kontrola aktualizací @@ -2979,6 +3045,18 @@ users.purge_help=Vynuceně odstranit uživatele a všechny repositáře, organiz users.still_own_packages=Tento uživatel stále vlastní jeden nebo více balíčků, nejprve odstraňte tyto balíčky. users.deletion_success=Uživatelský účet byl smazán. users.reset_2fa=Resetovat 2FA +users.list_status_filter.menu_text=Filtr +users.list_status_filter.reset=Obnovit +users.list_status_filter.is_active=Aktivní +users.list_status_filter.not_active=Neaktivní +users.list_status_filter.is_admin=Administrátor +users.list_status_filter.not_admin=Není administrátor +users.list_status_filter.is_restricted=Omezeno +users.list_status_filter.not_restricted=Není omezen +users.list_status_filter.is_prohibit_login=Zakázat přihlášení +users.list_status_filter.not_prohibit_login=Povolit přihlášení +users.list_status_filter.is_2fa_enabled=2FA povoleno +users.list_status_filter.not_2fa_enabled=2FA zakázáno users.details=Podrobnosti o uživateli emails.email_manage_panel=Správa uživatelských e-mailů @@ -3292,6 +3370,24 @@ monitor.process.cancel_desc=Zrušení procesu může způsobit ztrátu dat monitor.process.cancel_notices=Zrušit: %s? monitor.process.children=Potomek +monitor.queues=Fronty +monitor.queue=Fronta: %s +monitor.queue.name=Název +monitor.queue.type=Typ +monitor.queue.exemplar=Typ vzoru +monitor.queue.numberworkers=Počet workerů +monitor.queue.maxnumberworkers=Maximální počet workerů +monitor.queue.numberinqueue=Číslo ve frontě +monitor.queue.review_add=Posoudit / přidat workery +monitor.queue.settings.title=Nastavení poolu +monitor.queue.settings.maxnumberworkers=Maximální počet workerů +monitor.queue.settings.maxnumberworkers.placeholder=V současné době %[1]d +monitor.queue.settings.maxnumberworkers.error=Maximální počet workerů musí být číslo +monitor.queue.settings.submit=Upravit nastavení +monitor.queue.settings.changed=Nastavení upravena +monitor.queue.settings.remove_all_items=Odstranit vše +monitor.queue.settings.remove_all_items_done=Všechny položky ve frontě byly odstraněny. + notices.system_notice_list=Systémová oznámení notices.view_detail_header=Podrobnosti oznámení notices.operations=Operace @@ -3307,6 +3403,7 @@ notices.desc=Popis notices.op=Op. notices.delete_success=Systémové upozornění bylo smazáno. dashboard.sync_repo_branches = Synchronizovat vynechané větve z dat Gitu do databáze +monitor.queue.activeworkers = Aktivní workery defaulthooks.desc = Webhooky automaticky vytvářejí žádosti HTTP POST na server, kde se spustí určité události Forgejo. Webhooky zde definované jsou výchozí a budou zkopírovány do všech nových repozitářů. Více informací zjistíte v návodu webhooků. systemhooks.desc = Webhooky automaticky vytvářejí žádosti HTTP POST na server, kde se spustí určité události Forgejo. Webhooky zde definované budou aktivní u všech repozitářů v systému, zvažte tedy prosím všechny vlivy na výkon, které může tato funkce způsobit. Více informací zjistíte v návodu webhooků. assets = Assety kódu @@ -3321,6 +3418,8 @@ self_check.database_fix_mysql=Pro uživatele MySQL/MariaDB můžete použít př self_check = Vlastní kontrola self_check.database_collation_case_insensitive=Databáze používá collation %s, což je collation nerozlišující velká a malá písmena. Ačkoli s ní Forgejo může pracovat, mohou se vyskytnout vzácné případy, kdy nebude fungovat podle očekávání. auths.oauth2_map_group_to_team = Zmapovat zabrané skupiny u týmů organizací (volitelné - vyžaduje název claimu výše) +monitor.queue.settings.desc = Pooly dynamicky rostou podle blokování fronty jejich workerů. + auths.tips.gmail_settings = Nastavení služby Gmail: config_summary = Souhrn config.open_with_editor_app_help = Editory v nabídce „Otevřít pomocí“ v nabídce klonování. Ponechte prázdné pro použití výchozího editoru (zobrazíte jej rozšířením). @@ -3398,10 +3497,35 @@ raw_seconds=sekund raw_minutes=minut [dropzone] +default_message=Přetáhněte soubory nebo klikněte sem pro nahrání. +invalid_input_type=Nemůžete nahrávat soubory tohoto typu. +file_too_big=Velikost souboru ({{filesize}} MB) je vyšší než maximální velikost ({{maxFilesize}} MB). +remove_file=Smazat soubor [notification] +notifications=Oznámení +unread=Nepřečtené +read=Přečtené +no_unread=Žádná nepřečtená oznámení. +no_read=Žádná přečtená oznámení. +pin=Připnout upozornění +mark_as_read=Označit jako přečtené +mark_as_unread=Označit jako nepřečtené +mark_all_as_read=Označit vše jako přečtené +subscriptions=Odběry +watching=Sledované +no_subscriptions=Žádné odběry [gpg] +default_key=Podepsáno výchozím klíčem +error.extract_sign=Selhalo získání podpisu +error.generate_hash=Selhalo vygenerování hashe revize +error.no_committer_account=Žádný účet není propojen s e-mailovou adresou přispěvatele +error.no_gpg_keys_found=V databázi nebyl nalezen žádný známý klíč pro tento podpis +error.not_signed_commit=Nepodepsaná revize +error.failed_retrieval_gpg_keys=Nepodařilo se získat žádný klíč propojený s účtem přispěvatele +error.probable_bad_signature=VAROVÁNÍ! Přestože v databázi existuje klíč s tímto ID, tato revize jím není ověřena! Tato revize je PODEZŘELÁ. +error.probable_bad_default_signature=VAROVÁNÍ! Přestože má výchozí klíč toto ID, tato revize jím není ověřena! Tato revize je PODEZŘELÁ. [units] unit=Jednotka @@ -3409,11 +3533,183 @@ error.no_unit_allowed_repo=Nejste oprávněni přistupovat k žádné části to error.unit_not_allowed=Nejste oprávněni přistupovat k této části repozitáře. [packages] +title=Balíčky desc=Správa balíčků repozitáře. +empty=Zatím zde nejsou žádné balíčky. +empty.documentation=Další informace o registru balíčků naleznete v dokumentaci. +empty.repo=Nahráli jste balíček, ale nezobrazil se zde? Přejděte na nastavení balíčku a propojte jej s tímto repozitářem. +registry.documentation=Další informace o registru %s naleznete v dokumentaci. +filter.type=Typ +filter.type.all=Vše +filter.no_result=Váš filtr nepřinesl žádné výsledky. +filter.container.tagged=Označeno +filter.container.untagged=Neoznačeno +published_by=Zveřejněno %[1]s od %[3]s +published_by_in=Zveřejněno %[1]s od %[3]s v %[5]s +installation=Instalace +about=O tomto balíčku +requirements=Požadavky +dependencies=Závislosti +keywords=Klíčová slova +details=Podrobnosti +details.author=Autor +details.project_site=Web projektu +details.repository_site=Web repositáře +details.documentation_site=Web dokumentace +details.license=Licence +assets=Prostředky +versions=Verze +versions.view_all=Zobrazit všechny +dependency.id=ID +dependency.version=Verze +alpine.registry=Nastavte tento registr přidáním URL do /etc/apk/repositories: +alpine.registry.key=Stáhněte si veřejný RSA klíč registru do složky /etc/apk/keys/ pro ověření podpisu indexu: +alpine.registry.info=Vyberte $branch a $repository ze seznamu níže. +alpine.install=Pro instalaci balíčku spusťte následující příkaz: +alpine.repository=Informace o repozitáři +alpine.repository.branches=Větve +alpine.repository.repositories=Repozitáře +alpine.repository.architectures=Architektury +cargo.registry=Nastavte tento registr v konfiguračním souboru Cargo (například ~/.cargo/config.toml): +cargo.install=Chcete-li nainstalovat balíček pomocí Cargo, spusťte následující příkaz: +chef.registry=Nastavit tento registr v souboru ~/.chef/config.rb: +chef.install=Pro instalaci balíčku spusťte následující příkaz: +composer.registry=Nastavit tento registr v souboru ~/.composer/config.json: +composer.install=Pro instalaci balíčku pomocí Compposer spusťte následující příkaz: +composer.dependencies=Závislosti +composer.dependencies.development=Vývojové závislosti conan.details.repository=Repozitář +conan.registry=Nastavte tento registr z příkazového řádku: +conan.install=Pro instalaci balíčku pomocí Conan spusťte následující příkaz: +conda.registry=Nastavte tento registr jako Conda repozitář ve vašem .condarc: +conda.install=Pro instalaci balíčku pomocí Conda spusťte následující příkaz: +container.details.type=Typ obrazu +container.details.platform=Platforma +container.pull=Stáhnout obraz z příkazové řádky: +container.digest=Výběr +container.multi_arch=OS/architektura +container.layers=Vrstvy obrazu +container.labels=Štítky +container.labels.key=Klíč +container.labels.value=Hodnota +cran.registry=Nastavte tento registr v souboru Rprofile.site: +cran.install=Pro instalaci balíčku spusťte následující příkaz: +debian.registry=Nastavte tento registr z příkazového řádku: +debian.registry.info=Vyberte $distribution a $component ze seznamu níže. +debian.install=Pro instalaci balíčku spusťte následující příkaz: +debian.repository=Informace o repozitáři +debian.repository.distributions=Distribuce +debian.repository.components=Komponenty +debian.repository.architectures=Architektury +generic.download=Stáhnout balíček z příkazové řádky: +go.install=Nainstalovat balíček z příkazové řádky: +helm.registry=Nastavte tento registr z příkazového řádku: +helm.install=Pro instalaci balíčku spusťte následující příkaz: +maven.registry=Nastavte tento registr ve vašem projektu pom.xml souboru: +maven.install=Pro použití balíčku uveďte následující v bloku dependencies v souboru pom.xml: +maven.install2=Spustit pomocí příkazové řádky: +maven.download=Chcete-li stáhnout závislost, spusťte přes příkazový řádek: +nuget.registry=Nastavte tento registr z příkazového řádku: +nuget.install=Chcete-li nainstalovat balíček pomocí NuGet, spusťte následující příkaz: +nuget.dependency.framework=Cílový Framework +npm.registry=Nastavte tento registr ve vašem projektu v souboru .npmrc: +npm.install=Pro instalaci balíčku pomocí npm spusťte následující příkaz: +npm.install2=nebo ho přidejte do souboru package.json: +npm.dependencies=Závislosti +npm.dependencies.development=Vývojové závislosti +npm.dependencies.peer=Vzájemné závislosti +npm.dependencies.optional=Volitelné závislosti +npm.details.tag=Značka +pub.install=Chcete-li nainstalovat balíček pomocí Dart, spusťte následující příkaz: +pypi.requires=Vyžaduje Python +pypi.install=Pro instalaci balíčku pomocí pip spusťte následující příkaz: +rpm.registry=Nastavte tento registr z příkazového řádku: +rpm.distros.redhat=na distribuce založené na RedHat +rpm.distros.suse=na distribuce založené na SUSE +rpm.install=Pro instalaci balíčku spusťte následující příkaz: +rpm.repository=Informace o repozitáři +rpm.repository.architectures=Architektury +rpm.repository.multiple_groups = Tento balíček je dostupný v několika skupinách. +rubygems.install=Pro instalaci balíčku pomocí gem spusťte následující příkaz: +rubygems.install2=nebo ho přidejte do Gemfie: +rubygems.dependencies.runtime=Běhové závislosti +rubygems.dependencies.development=Vývojové závislosti +rubygems.required.ruby=Vyžaduje verzi Ruby +rubygems.required.rubygems=Vyžaduje verzi RubyGem +swift.registry=Nastavte tento registr z příkazového řádku: +swift.install=Přidejte balíček do svého Package.swift souboru: +swift.install2=a spustit následující příkaz: +vagrant.install=Pro přidání Vagrant box spusťte následující příkaz: +settings.link=Propojit tento balíček s repozitářem +settings.link.description=Pokud propojíte balíček s repozitářem, je tento balíček uveden v seznamu balíčků repozitáře. +settings.link.select=Vybrat repozitář +settings.link.button=Aktualizovat odkaz na repozitář +settings.link.success=Odkaz na repozitář byl úspěšně aktualizován. +settings.link.error=Nepodařilo se aktualizovat odkaz na repozitář. +settings.delete=Odstranit balíček +settings.delete.description=Smazání balíčku je trvalé a nelze ho vrátit zpět. +settings.delete.notice=Chystáte se odstranit %s (%s). Tato operace je nevratná, jste si jisti? +settings.delete.success=Balíček byl odstraněn. +settings.delete.error=Nepodařilo se odstranit balíček. +owner.settings.cargo.title=Index registru Cargo +owner.settings.cargo.initialize=Inicializovat index +owner.settings.cargo.initialize.description=Pro použití registru Cargo je zapotřebí speciální index Git. Použití této možnosti (znovu) vytvoří repozitář a automaticky jej nastaví. +owner.settings.cargo.initialize.error=Nepodařilo se inicializovat Cargo index: %v +owner.settings.cargo.initialize.success=Index Cargo byl úspěšně vytvořen. +owner.settings.cargo.rebuild=Znovu vytvořit index +owner.settings.cargo.rebuild.error=Obnovení Cargo indexu se nezdařilo: %v +owner.settings.cargo.rebuild.success=Index Cargo byl úspěšně znovu sestaven. +owner.settings.cleanuprules.title=Pravidla čištění +owner.settings.cleanuprules.add=Přidat pravidlo pro čištění +owner.settings.cleanuprules.edit=Upravit pravidlo pro čištění +owner.settings.cleanuprules.none=Zatím nejsou k dispozici žádná pravidla čištění. +owner.settings.cleanuprules.preview=Náhled pravidla pro čištění +owner.settings.cleanuprules.preview.overview=%d balíčků má být odstraněno. +owner.settings.cleanuprules.preview.none=Pravidlo čištění neodpovídá žádným balíčkům. owner.settings.cleanuprules.enabled=Povolený +owner.settings.cleanuprules.pattern_full_match=Použít vzor na úplný název balíčku +owner.settings.cleanuprules.keep.title=Verze, které odpovídají těmto pravidlům, jsou zachovány, i když odpovídají níže uvedenému pravidlu pro odstranění. +owner.settings.cleanuprules.keep.count=Zachovat nejnovější owner.settings.cleanuprules.keep.count.1=1 verze na balíček owner.settings.cleanuprules.keep.count.n=%d verzí na balíček +owner.settings.cleanuprules.keep.pattern=Ponechat odpovídající verze +owner.settings.cleanuprules.keep.pattern.container=U balíčků Container je vždy zachována nejnovější verze. +owner.settings.cleanuprules.remove.title=Verze, které odpovídají těmto pravidlům, jsou odstraněny, pokud výše uvedené pravidlo neukládá jejich zachování. +owner.settings.cleanuprules.remove.days=Odstranit verze starší než +owner.settings.cleanuprules.remove.pattern=Odstranit odpovídající verze +owner.settings.cleanuprules.success.update=Pravidlo pro čištění bylo aktualizováno. +owner.settings.cleanuprules.success.delete=Pravidlo pro čištění bylo odstraněno. +owner.settings.chef.title=Registr Chef +owner.settings.chef.keypair=Generovat pár klíčů +owner.settings.chef.keypair.description=Žádosti odeslané do registru Chef musí být kryptograficky podepsané jako způsob ověření. Při generování páru klíčů je ve službě Forgejo uložen pouze veřejný klíč. Soukromý klíč je poskytnut vám, abyste jej mohli použít s programem knife. Vygenerováním nového páru klíčů přepíšete ten předchozí. +owner.settings.cargo.rebuild.description = Opětovné sestavení může být užitečné, pokud není index synchronizován s uloženými balíčky Cargo. +owner.settings.cargo.rebuild.no_index = Opětovné vytvoření selhalo, nebyl inicializován žádný index. +npm.dependencies.bundle = Přidružené závislosti +arch.pacman.helper.gpg = Přidat certifikát důvěryhodnosti do nástroje pacman: +arch.pacman.repo.multi = %s má stejnou verzi v různých distribucích. +arch.pacman.repo.multi.item = Nastavení pro %s +arch.pacman.conf = Přidejte server s odpovídající distribucí a architekturou do /etc/pacman.conf : +arch.pacman.sync = Synchronizace balíčku nástrojem pacman: +arch.version.properties = Vlastnosti verze +arch.version.description = Popis +arch.version.provides = Poskytuje +arch.version.groups = Skupina +arch.version.depends = Závislosti +arch.version.optdepends = Volitelné závislosti +arch.version.makedepends = Závislosti Make +arch.version.checkdepends = Závislosti Check +arch.version.conflicts = Konflikty +arch.version.replaces = Nahrazuje +arch.version.backup = Záloha +container.images.title = Obrázky +search_in_external_registry = Hledat v %s +alt.install = Instalovat balíček +alt.setup = Přidejte repozitář do seznamu připojených repozitářů (místo „_arch_“ zvolte potřebnou architekturu): +alt.repository = Informace o repozitáři +alt.repository.architectures = Architektury +alt.repository.multiple_groups = Tento balíček je dostupný v několika skupinách. +alt.registry = Nastavit tento registr z příkazové řádky: +alt.registry.install = Pro instalaci balíčku spusťte následující příkaz: [secrets] secrets=Tajné klíče @@ -3431,8 +3727,64 @@ deletion.failed=Nepodařilo se odstranit tajný klíč. management=Správa tajných klíčů [actions] +actions=Akce + unit.desc=Spravovat integrované pipeliny CI/CD pomocí funkce Forgejo Actions. +status.unknown=Neznámý +status.waiting=Čekání +status.running=Probíhá +status.success=Úspěch +status.failure=Chyba +status.cancelled=Zrušeno +status.skipped=Přeskočeno +status.blocked=Blokováno + +runners.new=Vytvořit nový runner +runners.new_notice=Jak spustit runner +runners.status=Status +runners.id=ID +runners.name=Název +runners.owner_type=Typ +runners.description=Popis +runners.labels=Štítky +runners.last_online=Naposledy online +runners.runner_title=Runner +runners.task_list=Nedávné úlohy na tomto runneru +runners.task_list.no_tasks=Zatím zde nejsou žádné úlohy. +runners.task_list.run=Spustit +runners.task_list.status=Status +runners.task_list.repository=Repozitář +runners.task_list.commit=Revize +runners.task_list.done_at=Dokončeno v +runners.edit_runner=Upravit Runner +runners.update_runner=Aktualizovat změny +runners.update_runner_success=Runner byl úspěšně aktualizován +runners.update_runner_failed=Aktualizace runneru se nezdařila +runners.delete_runner=Odstranit tento runner +runners.delete_runner_success=Runner byl úspěšně odstraněn +runners.delete_runner_failed=Odstranění runneru selhalo +runners.delete_runner_header=Potvrdit odstranění tohoto runneru +runners.delete_runner_notice=Pokud na tomto runneru běží úloha, bude ukončena a označena jako neúspěšná. Může dojít k přerušení vytváření workflow. +runners.status.unspecified=Neznámý +runners.status.idle=Nečinný +runners.status.active=Aktivní +runners.status.offline=Offline +runners.version=Verze +runners.reset_registration_token=Resetovat registrační token +runners.reset_registration_token_success=Registrační token runneru byl úspěšně obnoven + +runs.all_workflows=Všechny workflowy +runs.commit=Revize +runs.scheduled=Naplánováno +runs.invalid_workflow_helper=Konfigurační soubor pracovního postupu je neplatný. Zkontrolujte prosím konfigurační soubor: %s +runs.no_matching_online_runner_helper=Žádný odpovídající online runner s popiskem: %s +runs.actor=Aktér +runs.status=Status +runs.actors_no_select=Všichni aktéři +runs.status_no_select=Všechny stavy +runs.no_results=Nebyly nalezeny žádné výsledky. +runs.no_workflows=Zatím nebyly vytvořeny žádné pracovní postupy. runs.no_runs=Pracovní postup zatím nebyl spuštěn. runs.empty_commit_message=(prázdná zpráva revize) @@ -3443,8 +3795,29 @@ workflow.enable_success=Workflow „%s“ byl úspěšně aktivován. workflow.disabled=Workflow je zakázán. +variables=Proměnné +variables.management=Správa proměnných +variables.creation=Přidat proměnnou +variables.none=Zatím zde nejsou žádné proměnné. +variables.deletion=Odstranit proměnnou +variables.deletion.description=Odstranění proměnné je trvalé a nelze jej vrátit zpět. Pokračovat? +variables.description=Proměnné budou předány určitým akcím a nelze je přečíst jinak. variables.id_not_exist = Proměnná s id %d neexistuje. +variables.edit=Upravit proměnnou +variables.deletion.failed=Nepodařilo se odstranit proměnnou. +variables.deletion.success=Proměnná byla odstraněna. +variables.creation.failed=Přidání proměnné se nezdařilo. +variables.creation.success=Proměnná „%s“ byla přidána. +variables.update.failed=Úprava proměnné se nezdařila. +variables.update.success=Proměnná byla upravena. +runners.none = Nejsou dostupné žádné runnery +runs.workflow = Workflow +runners = Runnery +runs.pushed_by = pushnuta uživatelem need_approval_desc = Potřebovat schválení pro spouštění workflowů pro žádosti o sloučení forků. +runners.runner_manage_panel = Správa runnerů +runs.no_job_without_needs = Workflow musí obsahovat alespoň jednu práci bez závislostí. +runs.no_job = Workflow musí obsahovat alespoň jednu úlohu workflow.dispatch.use_from = Použít workflow z workflow.dispatch.run = Spustit workflow workflow.dispatch.input_required = Vyžadovaná hodnota pro vstup „%s“. @@ -3455,6 +3828,7 @@ workflow.dispatch.success = Žádost o spuštění workflow byla úspěšně ode runs.expire_log_message = Protokoly byly smazány, protože byly příliš staré. runs.no_workflows.help_no_write_access = Pro více informací o Forgejo Actions se podívejte do dokumentace. runs.no_workflows.help_write_access = Nevíte, jak začít s Forgejo Actions? Podívejte se na rychlý začátek v uživatelské dokumentaci pro vytvoření vašeho prvního workflow. Poté nastavte runner Forgejo pro provádění vašich úloh. +variables.not_found = Nepodařilo se najít proměnnou. [projects] type-1.display_name=Samostatný projekt @@ -3500,8 +3874,19 @@ regexp = RegExp regexp_tooltip = Interpretovat hledaný výraz jako regulární výraz [markup] +filepreview.lines = Řádky %[1]d až %[2]d v souboru %[3]s +filepreview.line = Řádek %[1]d v souboru %[2]s +filepreview.truncated = Náhled byl zkrácen [munits.data] +b = B +kib = KiB +mib = MiB +gib = GiB +tib = TiB +pib = PiB +eib = EiB + [translation_meta] test = diky vsem za pomoc :) diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index 6efa9bcec3..f2d4852f55 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -35,6 +35,14 @@ re_type = Bekræft adgangskode captcha = CAPTCHA twofa = To-faktor autentificering twofa_scratch = To-faktor skrabekode +webauthn_sign_in = Tryk på knappen på din sikkerhedsnøgle. Hvis din sikkerhedsnøgle ikke har nogen knap, skal du indsætte den igen. +webauthn_use_twofa = Brug en tofaktorkode fra din telefon +webauthn_error = Kunne ikke læse din sikkerhedsnøgle. +webauthn_unsupported_browser = Din browser understøtter i øjeblikket ikke WebAuthn. +webauthn_error_unknown = Der opstod en ukendt fejl. Prøv venligst igen. +webauthn_error_unable_to_process = Serveren kunne ikke behandle din anmodning. +webauthn_error_duplicated = Sikkerhedsnøglen er ikke tilladt for denne anmodning. Sørg for, at nøglen ikke allerede er registreret. +webauthn_error_empty = Du skal angive et navn for denne nøgle. organization = Organisation mirror = Mirror new_mirror = Ny mirror @@ -104,7 +112,11 @@ show_timestamps = Vis tidsstempler show_log_seconds = Vis sekunder tracked_time_summary = Opsummering af sporet tid baseret på filtre af problemliste signed_in_as = Logget ind som +webauthn_error_insecure = WebAuthn understøtter kun sikre forbindelser. Til test over HTTP kan du bruge oprindelsen "localhost" eller "127.0.0.1" invalid_data = Ugyldige data: %v +webauthn_insert_key = Indsæt din sikkerhedsnøgle +webauthn_press_button = Tryk venligst på knappen på din sikkerhedsnøgle… +webauthn_error_timeout = Timeout nået, før din nøgle kunne læses. Genindlæs denne side og prøv igen. enabled = Aktiveret locked = Låst copy_hash = Kopiér hash @@ -1122,6 +1134,14 @@ migrate_options_mirror_helper = Dette depot vil være et spejl migrate_options_lfs = Migrer LFS-filer migrate_options_lfs_endpoint.label = LFS endepunkt migrate_options_lfs_endpoint.description.local = En lokal serversti understøttes også. +migrate_items = Migration Elementer +migrate_items_wiki = Wiki +migrate_items_milestones = Milepæle +migrate_items_labels = Etiketter +migrate_items_issues = Problemmer +migrate_items_pullrequests = Pull-anmodninger +migrate_items_merge_requests = Flet anmodninger +migrate_items_releases = Udgivelser migrate_repo = Migrer depot migrate.clone_address_desc = HTTP(S) eller Git "klone" URL'en for et eksisterende depot migrate.clone_local_path = eller en lokal serversti @@ -1137,6 +1157,14 @@ migrate.migrating = Migrerer fra %s … migrate.migrating_failed = Migrering fra %s mislykkedes. migrate.migrating_failed.error = Kunne ikke migrere: %s migrate.migrating_failed_no_addr = Migration mislykkedes. +migrate.migrating_git = Migrering af Git-data +migrate.migrating_topics = Migrering af emner +migrate.migrating_milestones = Migrerende milepæle +migrate.migrating_labels = Migrering af etiketter +migrate.migrating_releases = Migrering af udgivelser +migrate.migrating_issues = Migrering af problemer +migrate.migrating_pulls = Migrering af pull-anmodninger +migrate.cancel_migrating_title = Annuller migrering mirror_from = spejl af forked_from = forked fra generated_from = genereret fra @@ -1147,6 +1175,7 @@ migrate_options_lfs_endpoint.description = Migration vil forsøge at bruge din f form.name_pattern_not_allowed = Mønsteret "%s" er ikke tilladt i et depotnavn. migrate_options_lfs_endpoint.placeholder = Hvis det efterlades tomt, vil endepunktet blive afledt fra klonens URL migrate.clone_address = Migrer / Klon fra URL +migrate.cancel_migrating_confirm = Vil du annullere denne migrering? more_operations = Flere operationer new_from_template = Brug en skabelon new_from_template_description = Du kan vælge en eksisterende depotskabelon på denne instans og anvende dens indstillinger. @@ -1240,10 +1269,15 @@ packages = Pakker actions = Handlinger labels = Etiketter milestones = Milepæle +n_commit_few = %s commits +n_branch_one = %s gren +n_branch_few = %s grene org_labels_desc_manage = Styr commits = Commits commit = Commit org_labels_desc = Etiketter på organisationsniveau, der kan bruges med alle depoter under denne organisation +n_commit_one = %s commit +n_release_few = %s udgivelser released_this = udgivet dette file.title = %s ved %s file_raw = Rå @@ -1253,6 +1287,9 @@ editor.fail_to_update_file_summary = Fejlmeddelelse: editor.push_rejected_summary = Fuldstændig afvisningsmeddelelse: editor.add_subdir = Tilføj en mappe… editor.unable_to_upload_files = Kunne ikke uploade filer til "%s" med fejl: %v +n_tag_one = %s tag +n_tag_few = %s tags +n_release_one = %s udgivelse file_history = Historie file_view_source = Se kilde file_view_rendered = Vis gengivet @@ -1469,7 +1506,7 @@ issues.ref_closing_from = `henviste til dette problem fra en pul issues.author.tooltip.issue = Denne bruger er forfatteren til dette problem. issues.author.tooltip.pr = Denne bruger er forfatteren af denne pull-anmodning. issues.role.owner = Ejer -issues.role.owner_helper = Denne bruger er en ejer af dette depot. +issues.role.owner_helper = Denne bruger er ejeren af dette depot. issues.role.member = Medlem issues.filter_label = Etiket issues.filter_label_no_select = Alle etiketter @@ -2664,6 +2701,18 @@ settings.event_action_recover = Gendan issues.filter_type.all_pull_requests = Alle pull-anmodninger [notification] +watching = Overvåger +read = Læs +notifications = Notifikationer +no_unread = Ingen ulæste notifikationer. +unread = Ulæst +mark_as_read = Markér som læst +no_read = Ingen læste notifikationer. +mark_all_as_read = Markér alle som læste +mark_as_unread = Markér som ulæst +subscriptions = Abonnementer +no_subscriptions = Ingen abonnementer +pin = Fastgør notifikation [action] watched_repo = begyndte at overvåge %[2]s @@ -2842,6 +2891,7 @@ config.db_name = Navn users.full_name = Fulde navn users.activated = Aktiveret repos.name = Navn +monitor.queue.name = Navn repos.private = Privat config.default_enable_timetracking = Aktiver tidsregistrering som standard config.enable_timetracking = Aktiver tidsregistrering @@ -2850,6 +2900,11 @@ config.allow_dots_in_usernames = Tillad brugere at bruge prikker i deres brugern auths.oauth2_icon_url = Icon URL users.edit = Redigere users.auth_source = Godkendelseskilde +monitor.queue.settings.maxnumberworkers.placeholder = I øjeblikket %[1]d +monitor.queue.settings.submit = Opdater indstillinger +monitor.queue.settings.changed = Indstillinger opdateret +monitor.queue.settings.remove_all_items = Slet alle +monitor.queue.settings.remove_all_items_done = Alle varer i køen er blevet fjernet. notices.system_notice_list = Systemmeddelelser dashboard.delete_repo_archives = Slet alle depoters arkiver (ZIP, TAR.GZ osv..) organizations = Organisationer @@ -2880,6 +2935,8 @@ dashboard.clean_unbind_oauth = Rens ubundne OAuth-forbindelser dashboard.delete_inactive_accounts.started = Slet alle uaktiverede konti opgave startet. dashboard.delete_missing_repos = Slet alle depoter, der mangler deres Git-filer dashboard.update_migration_poster_id = Opdater migrationsplakat-id'er +dashboard.memory_obtained = Hukommelse opnået +dashboard.pointer_lookup_times = Pointer-opslagstider hooks = Webhooks dashboard.cron.finished = Cron: %[1]s er færdig dashboard.delete_inactive_accounts = Slet alle uaktiverede konti @@ -2888,6 +2945,10 @@ notices = Systemmeddelelser config_summary = Oversigt dashboard.system_status = System status dashboard.update_mirrors = Opdater spejle +dashboard.server_uptime = Server oppetid +dashboard.current_goroutine = Nuværende goroutiner +dashboard.current_memory_usage = Aktuel hukommelsesbrug +dashboard.total_memory_allocated = Samlet hukommelse tildelt integrations = Integrationer dashboard.operations = Vedligeholdelses operationer dashboard.repo_health_check = Sundhedstjek alle depoter @@ -2923,6 +2984,16 @@ users.update_profile = Opdater brugerkonto users.still_has_org = Denne bruger er medlem af en organisation. Fjern først brugeren fra enhver organisation. users.purge_help = Tvangsslet brugeren og eventuelle depoter, organisationer og pakker, der ejes af brugeren. Alle kommentarer og problemer indsendt af denne bruger vil også blive slettet. users.is_admin = Administrator konto +dashboard.mspan_structures_obtained = Mspan strukturer opnået +dashboard.mcache_structures_usage = MCache strukturer brug +dashboard.mspan_structures_usage = MSpan strukturer brug +dashboard.mcache_structures_obtained = MCache-strukturer opnået +dashboard.profiling_bucket_hash_table_obtained = Profilering bucket hash tabel opnået +dashboard.gc_metadata_obtained = GC-metadata opnået +dashboard.other_system_allocation_obtained = Anden systemallokering opnået +dashboard.next_gc_recycle = Næste GC genbrug +dashboard.total_gc_pause = Total GC-pause +dashboard.last_gc_time = Tid siden sidste GC dashboard.delete_old_system_notices = Slet alle gamle systemmeddelelser fra databasen dashboard.gc_lfs = Affaldssamler LFS-metaobjekter dashboard.start_schedule_tasks = Start planlæg handlingsopgaver @@ -2934,6 +3005,12 @@ users.created = Oprettet users.max_repo_creation = Maksimalt antal depoter users.prohibit_login = Suspenderet konto users.is_restricted = Begrænset konto +dashboard.memory_allocate_times = Hukommelsestildelinger +dashboard.memory_free_times = Hukommelses frigørelse +dashboard.current_heap_usage = Nuværende heap-brug +dashboard.heap_memory_obtained = Heap-hukommelse opnået +dashboard.heap_objects = Heap genstande +dashboard.bootstrap_stack_usage = Brug af bootstrap-stak dashboard.update_checker = Opdateringskontrol dashboard.delete_old_actions.started = Slet alle gamle aktiviteter fra den påbegyndte database. dashboard.stop_zombie_tasks = Stop zombiehandlingsopgaver @@ -2947,10 +3024,19 @@ users.organization_creation.description = Tillad oprettelse af nye organisatione users.delete_account = Slet brugerkonto users.cannot_delete_self = Du kan ikke slette dig selv users.still_own_repo = Denne bruger ejer stadig et eller flere arkiver. Slet eller overfør disse depoter først. +users.list_status_filter.is_admin = Admin users.block.description = Bloker denne bruger i at interagere med denne tjeneste via deres konto, og forbyd at logge ind. users.restricted.description = Tillad kun interaktion med de depoter og organisationer, hvor denne bruger er tilføjet som en samarbejdspartner. Dette forhindrer adgang til offentlige arkiver i denne instans. +users.list_status_filter.menu_text = Filter +dashboard.heap_memory_idle = Heap hukommelse inaktiv +dashboard.heap_memory_in_use = Heap hukommelse i brug +dashboard.heap_memory_released = Heap-hukommelse frigivet +dashboard.stack_memory_obtained = Stakhukommelse opnået +dashboard.last_gc_pause = Sidste GC-pause +dashboard.gc_times = GC times dashboard.delete_old_actions = Slet alle gamle aktiviteter fra databasen users.allow_create_organization = Kan skabe organisationer +users.list_status_filter.not_admin = Ikke admin users.allow_import_local = Kan importere lokale depoter users.send_register_notify = Giv besked om tilmelding via e-mail users.local = Lokal @@ -2965,6 +3051,9 @@ users.max_repo_creation_desc = (Indtast -1 for at bruge den globale standardgræ users.is_activated = Aktiveret konto users.edit_account = Rediger brugerkonto packages.version = Version +users.list_status_filter.reset = Nulstil +users.list_status_filter.is_active = Aktiv +users.list_status_filter.not_active = Inaktiv users.purge = Udrens bruger users.user_manage_panel = Administrer brugerkonti users.new_account = Opret brugerkonto @@ -2978,19 +3067,25 @@ emails.not_updated = Kunne ikke opdatere den anmodede e-mailadresse: %v packages.package_manage_panel = Administrer pakker packages.total_size = Samlet størrelse: %s packages.unreferenced_size = Ikke-referencestørrelse: %s +users.list_status_filter.is_2fa_enabled = 2FA aktiveret +users.list_status_filter.not_2fa_enabled = 2FA deaktiveret emails.filter_sort.email_reverse = E-mail (omvendt) emails.filter_sort.name_reverse = Brugernavn (omvendt) emails.delete = Slet e-mail emails.delete_desc = Er du sikker på, at du vil slette denne e-mailadresse? emails.deletion_success = E-mailadressen er blevet slettet. +users.list_status_filter.is_restricted = Begrænset emails.duplicate_active = Denne e-mailadresse er allerede aktiv for en anden bruger. emails.change_email_header = Opdater e-mail-egenskaber emails.change_email_text = Er du sikker på, at du vil opdatere denne e-mailadresse? repos.issues = Problemer repos.size = Størrelse repos.lfs_size = LFS størrelse +users.list_status_filter.not_restricted = Ikke begrænset users.details = Brugeroplysninger emails.email_manage_panel = Administrer bruger-e-mails +users.list_status_filter.is_prohibit_login = Forbyd login +users.list_status_filter.not_prohibit_login = Tillad login emails.delete_primary_email_error = Du kan ikke slette den primære e-mail. orgs.org_manage_panel = Administrer organisationer repos.repo_manage_panel = Administrer depoter @@ -3055,6 +3150,8 @@ self_check.database_collation_case_insensitive = Databasen bruger en sortering % config.git_max_diff_line_characters = Maks. diff-tegn pr. linje config.access_log_template = Skabelon til adgangslog monitor.process.children = Børn +monitor.queues = Køer +monitor.queue.settings.desc = Puljer vokser dynamisk som reaktion på deres blokering af arbejderkø. auths.auth_manage_panel = Administrer godkendelseskilder auths.auth_type = Godkendelsestype auths.map_group_to_team_removal = Fjern brugere fra synkroniserede teams, hvis brugeren ikke tilhører den tilsvarende LDAP-gruppe @@ -3081,6 +3178,8 @@ monitor.stacktrace = Stakspor monitor.execute_time = Udførelsestid monitor.last_execution_result = Resultat monitor.process.cancel_notices = Annuller: %s? +monitor.queue.type = Type +monitor.queue.exemplar = Eksempler type auths.auth_name = Godkendelsesnavn auths.attribute_username = Brugernavn attribut auths.attribute_username_placeholder = Lad stå tomt for at bruge brugernavnet indtastet i Forgejo. @@ -3123,11 +3222,13 @@ config.cache_test_slow = Cachetest lykkedes, men svaret er langsomt: %s. config.gc_interval_time = GC interval tid config.git_max_diff_files = Max diff filer vist config.git_gc_args = GC argumenter +monitor.queue.settings.maxnumberworkers.error = Max antal arbejdere skal være et tal notices.inverse_selection = Omvendt valg notices.delete_selected = Slet valgte auths.allowed_domains_helper = Lad være tomt for at tillade alle domæner. Adskil flere domæner med et komma (","). auths.skip_local_two_fa = Spring over lokal 2FA auths.oauth2_required_claim_value_helper = Indstil denne værdi for at begrænse login fra denne kilde til brugere med et krav med dette navn og denne værdi +monitor.queue.settings.maxnumberworkers = Max antal arbejdere notices.delete_all = Slet alle meddelelser auths.smtp_auth = SMTP-godkendelsestype config.default_visibility_organization = Standardsynlighed for nye organisationer @@ -3226,6 +3327,7 @@ auths.attributes_in_bind = Hent attributter i bind DN-kontekst config.app_name = Instans titel config.app_slogan = instans slogan config.cache_interval = Cache interval +monitor.queue.settings.title = Pool indstillinger notices.type = Type notices.type_2 = Opgave config.db_schema = Skematisk @@ -3244,6 +3346,12 @@ monitor.execute_times = Udførelser monitor.download_diagnosis_report = Hent diagnoserapport monitor.process.cancel = Annuller processen monitor.process.cancel_desc = Annullering af en proces kan medføre tab af data +monitor.queue = Kø: %s +monitor.queue.numberworkers = Antal arbejdere +monitor.queue.activeworkers = Aktive arbejdere +monitor.queue.maxnumberworkers = Max antal arbejdere +monitor.queue.numberinqueue = Nummer i kø +monitor.queue.review_add = Gennemgå / tilføj arbejdere config.git_pull_timeout = Pull Operation timeout config.git_clone_timeout = Klone Operation timeout config.git_gc_timeout = GC Operation timeout @@ -3269,17 +3377,235 @@ auths.restricted_filter = Begrænset filter auths.user_attribute_in_group = Brugerattribut angivet i gruppen [packages] +arch.version.description = Beskrivelse +container.labels = Etiketter +rubygems.dependencies.development = Udviklingsafhængigheder conan.details.repository = Depot +conan.registry = Konfigurer dette register fra kommandolinjen: +rubygems.dependencies.runtime = Kørselsafhængigheder +rubygems.install = For at installere pakken ved hjælp af gem skal du køre følgende kommando: +debian.repository = Depot info +npm.details.tag = Tag +chef.install = For at installere pakken skal du køre følgende kommando: +alpine.repository.architectures = Arkitekturer +composer.dependencies.development = Udviklingsafhængigheder +alt.repository.multiple_groups = Denne pakke er tilgængelig i flere grupper. owner.settings.cleanuprules.enabled = Aktiveret +helm.registry = Konfigurer dette register fra kommandolinjen: +alt.registry.install = For at installere pakken skal du køre følgende kommando: +helm.install = For at installere pakken skal du køre følgende kommando: +alt.repository.architectures = Arkitekturer +swift.registry = Konfigurer dette register fra kommandolinjen: +npm.dependencies.bundle = Samlede afhængigheder +debian.registry = Konfigurer dette register fra kommandolinjen: +cran.install = For at installere pakken skal du køre følgende kommando: +debian.install = For at installere pakken skal du køre følgende kommando: +pub.install = For at installere pakken ved hjælp af Dart skal du køre følgende kommando: +pypi.requires = Kræver Python +nuget.registry = Konfigurer dette register fra kommandolinjen: +debian.repository.distributions = Fordelinger +debian.repository.components = Komponenter +debian.repository.architectures = Arkitekturer +rpm.repository.multiple_groups = Denne pakke er tilgængelig i flere grupper. +rubygems.install2 = eller føj det til Gemfilen: +npm.dependencies.development = Udviklingsafhængigheder +npm.dependencies.peer = Peer-afhængigheder +npm.dependencies.optional = Valgfrie afhængigheder +rpm.registry = Konfigurer dette register fra kommandolinjen: +rpm.install = For at installere pakken skal du køre følgende kommando: +alpine.install = For at installere pakken skal du køre følgende kommando: +alpine.repository = Depot info +pypi.install = For at installere pakken ved hjælp af pip skal du køre følgende kommando: +rpm.repository = Depot info +rpm.repository.architectures = Arkitekturer +alt.registry = Konfigurer dette register fra kommandolinjen: +alt.repository = Depot info +alpine.repository.repositories = Depoter +search_in_external_registry = Søg i %s +dependency.version = Version +alpine.registry = Konfigurer dette register ved at tilføje url'en i din /etc/apk/repositories fil: +alpine.registry.key = Download den offentlige RSA-nøgle til registreringsdatabasen i mappen /etc/apk/keys/ for at bekræfte indekssignaturen: +alpine.registry.info = Vælg $branch og $repository fra listen nedenfor. +empty = Der er ingen pakker endnu. +filter.type.all = Alle +filter.container.untagged = Umærket +about = Om denne pakke +filter.no_result = Dit filter gav ingen resultater. +dependencies = Afhængigheder +empty.documentation = For mere information om pakkeregistret, se dokumentationen. +filter.type = Type +registry.documentation = For mere information om %s registreringsdatabasen, se dokumentationen. +title = Pakker desc = Administrer depotpakker. +empty.repo = Har du uploadet en pakke, men den vises ikke her? Gå til pakkeindstillinger og link den til denne repo. +filter.container.tagged = Tagget +published_by = Udgivet %[1]s af %[3]s +published_by_in = Udgivet %[1]s af %[3]s i %[5]s +installation = Installation +requirements = Krav +cran.registry = Konfigurer dette register i din Rprofile.site fil: +rubygems.required.rubygems = Kræver RubyGem version +owner.settings.chef.title = Kokkeregister +owner.settings.chef.keypair = Generer nøglepar +arch.pacman.repo.multi.item = Konfiguration for %s +arch.pacman.sync = Synkroniser pakke med pacman: +arch.version.properties = Versionsegenskaber +arch.version.provides = Forsyner +arch.version.checkdepends = Check afhænger +arch.version.replaces = Erstatter +conan.install = For at installere pakken ved hjælp af Conan skal du køre følgende kommando: +conda.registry = Konfigurer dette register som et Conda-depot i din .condarc-fil: +conda.install = For at installere pakken ved hjælp af Conda skal du køre følgende kommando: +container.images.title = Billeder +container.details.type = Billedtype +container.details.platform = Platform +container.pull = Træk billedet fra kommandolinjen: +container.digest = Fordøje +alt.setup = Tilføj et depot til listen over tilsluttede arkiver (vælg den nødvendige arkitektur i stedet for "_arch_"): +vagrant.install = For at tilføje en Vagrant-boks skal du køre følgende kommando: +swift.install2 = og kør følgende kommando: +settings.link = Link denne pakke til et depot +settings.link.success = Depotlinket blev opdateret. +owner.settings.cargo.initialize.description = Et særligt indeks Git-depot er nødvendigt for at bruge Cargo-registret. Brug af denne mulighed vil (gen-)oprette depotet og konfigurere det automatisk. +settings.delete.notice = Du er ved at slette %s (%s). Denne operation er uigenkaldeligt, er du sikker? +owner.settings.cargo.title = Lastregisterindeks +owner.settings.cargo.initialize = Initialiser indeks +owner.settings.cleanuprules.preview.none = Oprydningsreglen matcher ikke nogen pakker. +owner.settings.cleanuprules.none = Der er endnu ingen oprydningsregler. owner.settings.cleanuprules.keep.count.1 = 1 version pr. pakke +owner.settings.cleanuprules.preview.overview = %d pakker er planlagt til at blive fjernet. +owner.settings.cleanuprules.keep.pattern.container = Den seneste version bevares altid for containerpakker. +settings.delete = Slet pakke +settings.delete.description = Sletning af en pakke er permanent og kan ikke fortrydes. owner.settings.cleanuprules.keep.count.n = %d versioner pr. pakke +arch.version.makedepends = Gør afhænger +alt.install = Installer pakken +composer.registry = Konfigurer dette register i din ~/.composer/config.json fil: +composer.dependencies = Afhængigheder +settings.delete.success = Pakken er blevet slettet. +settings.delete.error = Kunne ikke slette pakken. +owner.settings.cargo.rebuild.error = Kunne ikke genopbygge Cargo-indeks: %v +owner.settings.cargo.rebuild = Genopbyg indeks +owner.settings.cleanuprules.preview = Forhåndsvisning af oprydningsregel +owner.settings.cleanuprules.keep.count = Behold den nyeste +owner.settings.cleanuprules.keep.pattern = Hold versionerne matchende +owner.settings.chef.keypair.description = Anmodninger sendt til Chef-registret skal være kryptografisk signeret som et middel til godkendelse. Når et nøglepar genereres, gemmes kun den offentlige nøgle på Forgejo. Den private nøgle gives til dig til brug med Knife. Generering af et nyt nøglepar vil overskrive det forrige. +maven.install = For at bruge pakken skal du inkludere følgende i blokken afhængigheder i filen pom.xml: +details = Detaljer +cargo.registry = Konfigurer dette register i Cargo-konfigurationsfilen (for eksempel ~/.cargo/config.toml): +cargo.install = For at installere pakken ved hjælp af Cargo skal du køre følgende kommando: +composer.install = For at installere pakken ved hjælp af Composer skal du køre følgende kommando: +container.multi_arch = OS / Arch +rubygems.required.ruby = Kræver Ruby version +swift.install = Tilføj pakken i din Package.swift-fil: +settings.link.select = Vælg depot +settings.link.button = Opdater depot link +settings.link.error = Kunne ikke opdatere depotlinket. +owner.settings.cargo.initialize.success = Cargo-indekset blev oprettet. +owner.settings.cargo.rebuild.description = Genopbygning kan være nyttig, hvis indekset ikke er synkroniseret med de lagrede Cargo-pakker. +owner.settings.cargo.rebuild.success = Cargo-indekset blev genopbygget med succes. +owner.settings.cleanuprules.add = Tilføj oprydningsregel +owner.settings.cleanuprules.edit = Rediger oprydningsregel +owner.settings.cleanuprules.title = Oprydningsregler +maven.registry = Konfigurer denne registreringsdatabasen i din projekt pom.xml fil: +npm.install2 = eller føj det til filen package.json: +nuget.dependency.framework = Mål Framework +npm.registry = Konfigurer denne registreringsdatabase i din projekt-.npmrc-fil: +nuget.install = For at installere pakken ved hjælp af NuGet skal du køre følgende kommando: +npm.dependencies = Afhængigheder +settings.link.description = Hvis du forbinder en pakke med et depot, vises pakken i depotets pakkeliste. +owner.settings.cargo.initialize.error = Kunne ikke initialisere Cargo index: %v +owner.settings.cleanuprules.keep.title = Versioner, der matcher disse regler, bevares, selvom de matcher en fjernelsesregel nedenfor. +generic.download = Download pakken fra kommandolinjen: +go.install = Installer pakken fra kommandolinjen: +container.layers = Billedlag +container.labels.key = Nøgle +container.labels.value = Værdi +debian.registry.info = Vælg $distribution og $component fra listen nedenfor. +maven.download = For at downloade afhængigheden skal du køre via kommandolinjen: +rpm.distros.suse = på SUSE-baserede distributioner +rpm.distros.redhat = på RedHat-baserede distributioner +owner.settings.cleanuprules.pattern_full_match = Anvend mønster på det fulde pakkenavn +details.author = Forfatter +details.repository_site = Depots hjemmeside +details.documentation_site = Dokumentations hjemmeside +details.license = Licens +assets = Aktiver +versions = Versioner +details.project_site = Projektets hjemmeside +versions.view_all = Se alle +dependency.id = ID +alpine.repository.branches = Grene +arch.version.optdepends = Valgfri afhænger +owner.settings.cleanuprules.remove.title = Versioner, der matcher disse regler, fjernes, medmindre en regel ovenfor siger, at de skal beholdes. +owner.settings.cleanuprules.remove.days = Fjern versioner ældre end +owner.settings.cleanuprules.remove.pattern = Fjern matchende versioner +owner.settings.cleanuprules.success.update = Oprydningsreglen er blevet opdateret. +owner.settings.cleanuprules.success.delete = Oprydningsregel er blevet slettet. +arch.version.backup = Backup +chef.registry = Konfigurer dette register i din ~/.chef/config.rb fil: +npm.install = For at installere pakken ved hjælp af npm skal du køre følgende kommando: +owner.settings.cargo.rebuild.no_index = Kan ikke genopbygge, intet indeks er initialiseret. +maven.install2 = Kør via kommandolinje: +keywords = Keywords +arch.pacman.helper.gpg = Tilføj tillidscertifikat til pacman: +arch.pacman.repo.multi = %s har den samme version i forskellige distributioner. +arch.pacman.conf = Tilføj server med relateret distribution og arkitektur til /etc/pacman.conf: +arch.version.groups = Gruppe +arch.version.depends = Afhænger +arch.version.conflicts = Konflikter [actions] +runners.description = Beskrivelse +runners.labels = Etiketter +runners.name = Navn +runners.task_list.repository = Depot +runners.status.active = Aktiv +runners.status.offline = Offline +runners.version = Version +runners.owner_type = Type +runners = Runners unit.desc = Administrer integrerede CI/CD-pipelines med Forgejo Actions. +status.unknown = Ukendt +runners.runner_title = Runner +runners.task_list = Seneste opgaver på denne løber +runners.task_list.run = Kør +runners.task_list.commit = Commit +runners.edit_runner = Rediger Runner +runs.commit = Commit +runs.scheduled = Planlagt +runs.pushed_by = pushed af +status.running = Kører +status.waiting = Venter +runners.new_notice = Hvordan man starter en runner +status.success = Succes +variables.not_found = Variablen kunne ikke findes. +runs.workflow = Arbejdsgang +runners.last_online = Sidste online tid +runners.task_list.done_at = Udført kl +runners.update_runner = Opdater ændringer +runners.update_runner_success = Runner blev opdateret +runners.update_runner_failed = Løberen kunne ikke opdateres +runners.delete_runner_failed = Runner kunne ikke slettes +runners.delete_runner_header = Bekræft for at slette denne runner +runners.status.idle = Tomgang +runs.no_job_without_needs = Arbejdsgangen skal indeholde mindst ét job uden afhængigheder. +runs.no_job = Arbejdsgangen skal indeholde mindst ét job +runs.no_results = Ingen resultater matchede. +runs.no_workflows = Der er endnu ingen arbejdsgange. workflow.enable = Aktiver arbejdsgang workflow.enable_success = Arbejdsgangen "%s" blev aktiveret. +variables.none = Der er ingen variabler endnu. +variables.edit = Rediger variabel +variables.deletion.success = Variablen er blevet fjernet. +variables.creation.failed = Kunne ikke tilføje variabel. +runners.delete_runner_notice = Hvis en opgave kører på denne runner, vil den blive afsluttet og markeret som mislykket. Det kan bryde bygningens arbejdsgang. runs.no_workflows.help_write_access = Ved du ikke, hvordan du starter med Forgejo Actions? Tjek hurtigstarten i brugerdokumentationen for at skrive dit første workflow, og opsæt en Forgejo-løber til at udføre dine opgaver. +runners.delete_runner_success = Runner blev slettet +variables.update.success = Variablen er blevet redigeret. +status.cancelled = Annulleret +status.skipped = Oversprunget +status.blocked = Blokeret workflow.disable_success = Arbejdsgangen "%s" blev deaktiveret. workflow.disable = Deaktiver arbejdsgang workflow.dispatch.use_from = Brug arbejdsgangen fra @@ -3289,12 +3615,41 @@ workflow.dispatch.warn_input_limit = Viser kun de første %d input. workflow.dispatch.success = Kørsel af arbejdsgang blev anmodet om. workflow.dispatch.input_required = Kræv værdi for input "%s". workflow.dispatch.invalid_input_type = Ugyldig inputtype "%s". +variables.creation = Tilføj variabel need_approval_desc = Har brug for godkendelse for at køre arbejdsgange for fork pull-anmodning. +runners.delete_runner = Slet denne runner +runners.status.unspecified = Ukendt +runners.reset_registration_token_success = Runner registreringstoken blev nulstillet +runs.all_workflows = Alle arbejdsgange +runners.reset_registration_token = Nulstil registreringstoken runs.empty_commit_message = (tom commit besked) runs.expire_log_message = Logfiler er blevet renset, fordi de var for gamle. +variables = Variabler +runs.actor = Aktør +actions = Handlinger +runners.status = Status +runners.task_list.status = Status +runners.id = ID +runners.task_list.no_tasks = Der er ingen opgave endnu. +runs.status = Status +runs.actors_no_select = Alle aktører +runs.status_no_select = Alle status +runners.none = Ingen runners tilgængelige +variables.management = Administrer variabler +variables.creation.success = Variablen "%s" er blevet tilføjet. +variables.update.failed = Variablen kunne ikke redigeres. runs.no_workflows.help_no_write_access = For at lære om Forgejo Actions, se dokumentationen. +variables.deletion = Fjern variabel variables.id_not_exist = Variabel med ID %d findes ikke. +variables.deletion.description = Fjernelse af en variabel er permanent og kan ikke fortrydes. Vil du fortsætte? +variables.description = Variabler vil blive videregivet til visse handlinger og kan ikke læses på anden vis. +variables.deletion.failed = Variablen kunne ikke fjernes. +runs.no_matching_online_runner_helper = Ingen matchende online-runner med etiket: %s +runners.runner_manage_panel = Administrer runners +runners.new = Opret ny runner workflow.dispatch.run = Kør arbejdsgang +runs.invalid_workflow_helper = Workflow-konfigurationsfilen er ugyldig. Tjek venligst din konfigurationsfil: %s +status.failure = Fiasko runs.no_runs = Workflowet har ingen kørsler endnu. [tool] @@ -3362,10 +3717,30 @@ none = Der er ingen hemmeligheder endnu. creation.failed = Kunne ikke tilføje hemmelighed. [dropzone] +invalid_input_type = Filer af denne type må ikke uploades. +remove_file = Fjern fil +default_message = Slip filer eller klik her for at uploade. +file_too_big = Filstørrelsen ({{filesize}} MB) overstiger den maksimale størrelse på ({{maxFilesize}} MB). [gpg] +default_key = Underskrevet med standardnøglen +error.generate_hash = Kunne ikke generere hash af commit +error.no_committer_account = Ingen konto knyttet til committers e-mailadresse +error.probable_bad_default_signature = ADVARSEL! Selvom standardnøglen har dette ID, bekræfter den ikke denne commit! Denne commit er MISTÆNLIG. +error.no_gpg_keys_found = Ingen kendt nøgle fundet for denne signatur i databasen +error.not_signed_commit = Ikke en underskrevet commit +error.failed_retrieval_gpg_keys = Kunne ikke hente nogen nøgle knyttet til committerens konto +error.probable_bad_signature = ADVARSEL! Selvom der er en nøgle med dette ID i databasen, bekræfter den ikke denne commit! Denne commit er MISTÆNLIG. +error.extract_sign = Kunne ikke udtrække signatur [munits.data] +kib = KiB +b = B +pib = PiB +mib = MiB +tib = TiB +eib = EiB +gib = GiB [units] error.no_unit_allowed_repo = Du har ikke tilladelse til at få adgang til nogen sektion af dette depot. @@ -3379,6 +3754,9 @@ deleted.display_name = Slettet projekt type-1.display_name = Individuelt projekt [markup] +filepreview.line = Linje %[1]d i %[2]s +filepreview.lines = Linjer %[1]d til %[2]d i %[3]s +filepreview.truncated = Forhåndsvisningen er blevet afkortet [git.filemode] symbolic_link = Symbolsk link diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 4a356b1f6e..89380d3424 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -36,6 +36,18 @@ twofa=Zwei-Faktor-Authentifizierung twofa_scratch=Zwei-Faktor-Einmalpasswort passcode=PIN +webauthn_insert_key=Hardware-Sicherheitsschlüssel einstecken +webauthn_sign_in=Drücke den Knopf auf deinem Sicherheitsschlüssel. Wenn dein Sicherheitsschlüssel keinen Knopf hat, stecke ihn erneut ein. +webauthn_press_button=Drücke den Knopf auf deinem Sicherheitsschlüssel … +webauthn_use_twofa=Zwei-Faktor-Authentifizierung via Handy verwenden +webauthn_error=Dein Sicherheitsschlüssel konnte nicht gelesen werden. +webauthn_unsupported_browser=Dein Browser unterstützt derzeit kein WebAuthn. +webauthn_error_unknown=Ein unbekannter Fehler ist aufgetreten. Bitte versuche es erneut. +webauthn_error_insecure=WebAuthn unterstützt nur sichere Verbindungen. Zum Testen über HTTP kannst du „localhost“ oder „127.0.0.1“ als Host verwenden +webauthn_error_unable_to_process=Der Server konnte deine Anfrage nicht bearbeiten. +webauthn_error_duplicated=Für diese Anfrage ist der Sicherheitsschlüssel nicht erlaubt. Bitte stell sicher, dass er nicht bereits registriert ist. +webauthn_error_empty=Du musst einen Namen für diesen Schlüssel festlegen. +webauthn_error_timeout=Das Zeitlimit wurde erreicht, bevor dein Schlüssel gelesen werden konnte. Bitte lade die Seite erneut. repository=Repository organization=Organisation mirror=Spiegel @@ -170,7 +182,7 @@ buttons.bold.tooltip=Fettschrift hinzufügen (Strg+B / ⌘B) buttons.italic.tooltip=Kursivschrift hinzufügen (Strg+I / ⌘I) buttons.quote.tooltip=Text zitieren buttons.code.tooltip=Code hinzufügen -buttons.link.tooltip=Link hinzufügen (Strg+K / ⌘K) +buttons.link.tooltip=Link hinzufügen buttons.list.unordered.tooltip=Liste hinzufügen buttons.list.ordered.tooltip=Nummerierte Liste hinzufügen buttons.list.task.tooltip=Aufgabenliste hinzufügen @@ -1141,6 +1153,14 @@ migrate_options_lfs_endpoint.label=LFS-Endpunkt migrate_options_lfs_endpoint.description=Migration wird versuchen, über den entfernten Git-Server den LFS-Server zu bestimmen. Du kannst auch einen eigenen Endpunkt angeben, wenn die LFS-Dateien woanders gespeichert werden. migrate_options_lfs_endpoint.description.local=Ein lokaler Serverpfad wird ebenfalls unterstützt. migrate_options_lfs_endpoint.placeholder=Wenn leer gelassen, wird der Endpunkt von der Klon-URL abgeleitet +migrate_items=Migrationselemente +migrate_items_wiki=Wiki +migrate_items_milestones=Meilensteine +migrate_items_labels=Labels +migrate_items_issues=Issues +migrate_items_pullrequests=Pull-Requests +migrate_items_merge_requests=Merge-Requests +migrate_items_releases=Releases migrate_repo=Repository migrieren migrate.clone_address=Migrations-/Klon-URL migrate.clone_address_desc=Die HTTP(S)- oder „git clone“-URL eines bereits existierenden Repositorys @@ -1159,6 +1179,16 @@ migrate.migrating=Migriere von %s … migrate.migrating_failed=Migrieren von %s fehlgeschlagen. migrate.migrating_failed.error=Migration fehlgeschlagen: %s migrate.migrating_failed_no_addr=Migration fehlgeschlagen. +migrate.migrating_git=Git-Daten werden migriert +migrate.migrating_topics=Themen werden migriert +migrate.migrating_milestones=Meilensteine werden migriert +migrate.migrating_labels=Labels werden migriert +migrate.migrating_releases=Releases werden migriert +migrate.migrating_issues=Issues werden migriert +migrate.migrating_pulls=Pull-Requests werden migriert +migrate.cancel_migrating_title=Migration abbrechen +migrate.cancel_migrating_confirm=Möchtest du diese Migration abbrechen? + mirror_from=Spiegel von forked_from=geforkt von generated_from=erzeugt von @@ -1534,7 +1564,7 @@ issues.ref_reopening_from=`referenzierte dieses Issue aus einem issues.ref_from=`von %[1]s` issues.author=Autor issues.role.owner=Besitzer -issues.role.owner_helper=Dieser Benutzer ist ein Besitzer dieses Repositorys. +issues.role.owner_helper=Dieser Benutzer ist der Besitzer dieses Repositorys. issues.role.member=Mitglied issues.role.member_helper=Dieser Benutzer ist Mitglied der Organisation, die dieses Repository besitzt. issues.role.collaborator=Mitarbeiter @@ -2352,7 +2382,7 @@ settings.matrix.room_id=Raum-ID settings.matrix.message_type=Nachrichtentyp settings.archive.button=Repo archivieren settings.archive.header=Dieses Repo archivieren -settings.archive.text=Durch das Archivieren wird ein Repo vollständig schreibgeschützt. Es wird von der Übersichtsseite versteckt. Niemand (nicht einmal du!) wird in der Lage sein, neue Commits zu erstellen oder Issues oder Pull-Requests zu öffnen. Es wird empfohlen, den Archivierungsgrund zu dokumentieren, um zukünftigen Entwicklern, die planen, das Repository zu forken, zu helfen. +settings.archive.text=Durch das Archivieren wird ein Repo vollständig schreibgeschützt. Es wird von der Übersichtsseite versteckt. Niemand (nicht einmal du!) wird in der Lage sein, neue Commits zu erstellen oder Issues oder Pull-Requests zu öffnen. settings.archive.success=Das Repo wurde erfolgreich archiviert. settings.archive.error=Beim Versuch, das Repository zu archivieren, ist ein Fehler aufgetreten. Weitere Details finden sich im Log. settings.archive.error_ismirror=Du kannst kein gespiegeltes Repo archivieren. @@ -2617,7 +2647,13 @@ pulls.ready_for_review = Bereit zur Sichtung? settings.rename_branch_failed_protected = Branch %s kann nicht umbenannt werden, weil er ein geschützter Branch ist. editor.commit_id_not_matching = Die Datei wurde geändert, während du sie bearbeitet hast. Committe in einen neuen Branch, dann führe einen Merge durch. editor.push_out_of_date = Der Push scheint veraltet zu sein. +n_commit_few = %s Commits +n_branch_one = %s Branch +n_branch_few = %s Branches +n_tag_one = %s Tag +n_tag_few = %s Tags stars = Favorisierungen +n_commit_one = %s Commit issues.num_participants_one = %d Beteiligter settings.enforce_on_admins_desc = Repositoryadministratoren können diese Regel nicht umgehen. settings.enforce_on_admins = Erzwinge diese Regel für alle Repositoryadministratoren @@ -2641,6 +2677,8 @@ settings.transfer.button = Besitz übertragen settings.transfer.modal.title = Besitz übertragen wiki.no_search_results = Keine Ergebnisse wiki.search = Wiki durchsuchen +n_release_one = %s freigegeben +n_release_few = %s Veröffentlichungen form.string_too_long = Die Zeichenkette ist länger als %d Zeichen. settings.federation_settings = Föderationseinstellungen settings.federation_following_repos = URLs folgender Repositorys. Durch „;“ getrennt, keine Leerzeichen. @@ -2924,6 +2962,34 @@ dashboard.sync_external_users=Externe Benutzerdaten synchronisieren dashboard.cleanup_hook_task_table=Hook-Task-Tabelle bereinigen dashboard.cleanup_packages=Veraltete Pakete bereinigen dashboard.cleanup_actions=Abgelaufene Logs und Artefakte von Actions bereinigen +dashboard.server_uptime=Server-Uptime +dashboard.current_goroutine=Aktuelle Goroutinen +dashboard.current_memory_usage=Aktuelle Speichernutzung +dashboard.total_memory_allocated=Zugeteilter Gesamtspeicher +dashboard.memory_obtained=Erhaltener Speicher +dashboard.pointer_lookup_times=Anzahl Zeigerlookups +dashboard.memory_allocate_times=Speicheranforderungen +dashboard.memory_free_times=Speicherfreigaben +dashboard.current_heap_usage=Aktuelle Heap-Auslastung +dashboard.heap_memory_obtained=Erhaltener Heap-Arbeitsspeicher +dashboard.heap_memory_idle=Unbenutzter Heap-Arbeitsspeicher +dashboard.heap_memory_in_use=Benutzter-Heap-Arbeitsspeicher +dashboard.heap_memory_released=Freigegebener Heap-Arbeitsspeicher +dashboard.heap_objects=Heap-Objekte +dashboard.bootstrap_stack_usage=Bootstrap-Stack-Auslastung +dashboard.stack_memory_obtained=Erhaltener Stack-Arbeitsspeicher +dashboard.mspan_structures_usage=MSpan-Structures-Auslastung +dashboard.mspan_structures_obtained=Erhaltene MSpan-Structures +dashboard.mcache_structures_usage=MCache-Structures-Auslastung +dashboard.mcache_structures_obtained=Erhaltene MCache-Structures +dashboard.profiling_bucket_hash_table_obtained=Erhaltene Analysesatz-Hashtabellen +dashboard.gc_metadata_obtained=Erhaltene GC-Metadaten +dashboard.other_system_allocation_obtained=Andere erhaltene System-Allokationen +dashboard.next_gc_recycle=Nächster GC-Zyklus +dashboard.last_gc_time=Seit letztem GC-Zyklus +dashboard.total_gc_pause=Gesamte GC-Pause +dashboard.last_gc_pause=Letzte GC-Pause +dashboard.gc_times=GC-Zeiten dashboard.delete_old_actions=Alle alten Aktivitäten aus der Datenbank löschen dashboard.delete_old_actions.started=Löschen aller alten Aktivitäten aus der Datenbank gestartet. dashboard.update_checker=Update-Checker @@ -2980,6 +3046,18 @@ users.purge_help=Das Löschen des Benutzers inklusive all seiner Repositorys, Or users.still_own_packages=Dieser Benutzer besitzt noch ein oder mehrere Pakete, lösche diese Pakete zuerst. users.deletion_success=Das Konto wurde gelöscht. users.reset_2fa=2FA zurücksetzen +users.list_status_filter.menu_text=Filter +users.list_status_filter.reset=Zurücksetzen +users.list_status_filter.is_active=Aktiv +users.list_status_filter.not_active=Inaktiv +users.list_status_filter.is_admin=Administrator +users.list_status_filter.not_admin=Nicht-Administrator +users.list_status_filter.is_restricted=Eingeschränkt +users.list_status_filter.not_restricted=Unbegrenzt +users.list_status_filter.is_prohibit_login=Anmelden verbieten +users.list_status_filter.not_prohibit_login=Anmelden erlaubt +users.list_status_filter.is_2fa_enabled=2FA aktiviert +users.list_status_filter.not_2fa_enabled=2FA deaktiviert users.details=Benutzerdetails emails.email_manage_panel=Benutzer-E-Mails verwalten @@ -3297,6 +3375,26 @@ monitor.process.cancel_desc=Abbrechen eines Prozesses kann Datenverlust verursac monitor.process.cancel_notices=Abbrechen: %s? monitor.process.children=Subprozesse +monitor.queues=Warteschlangen +monitor.queue=Warteschlange: %s +monitor.queue.name=Name +monitor.queue.type=Typ +monitor.queue.exemplar=Beispieltyp +monitor.queue.numberworkers=Anzahl der Worker +monitor.queue.activeworkers=Aktive Worker +monitor.queue.maxnumberworkers=Maximale Anzahl der Worker +monitor.queue.numberinqueue=Anzahl in der Warteschlange +monitor.queue.review_add=Worker hinzufügen/prüfen +monitor.queue.settings.title=Pool-Einstellungen +monitor.queue.settings.desc=Pools wachsen dynamisch basierend auf der Blockierung der Arbeitswarteschlange. +monitor.queue.settings.maxnumberworkers=Maximale Anzahl an Workern +monitor.queue.settings.maxnumberworkers.placeholder=Derzeit %[1]d +monitor.queue.settings.maxnumberworkers.error=Die Anzahl der Worker muss eine Zahl sein +monitor.queue.settings.submit=Einstellungen aktualisieren +monitor.queue.settings.changed=Einstellungen aktualisiert +monitor.queue.settings.remove_all_items=Alle entfernen +monitor.queue.settings.remove_all_items_done=Alle Elemente in der Warteschlange wurden entfernt. + notices.system_notice_list=Systemmitteilungen notices.view_detail_header=Meldungsdetails ansehen notices.operations=Operationen @@ -3399,10 +3497,35 @@ raw_seconds=Sekunden raw_minutes=Minuten [dropzone] +default_message=Zum Hochladen hier klicken oder Datei ablegen. +invalid_input_type=Dateien dieses Dateityps können nicht hochgeladen werden. +file_too_big=Dateigröße ({{filesize}} MB) überschreitet die Maximalgröße ({{maxFilesize}} MB). +remove_file=Datei entfernen [notification] +notifications=Nachrichten +unread=Ungelesen +read=Gelesen +no_unread=Keine ungelesenen Benachrichtigungen. +no_read=Keine gelesenen Benachrichtigungen. +pin=Benachrichtigung pinnen +mark_as_read=Als gelesen markieren +mark_as_unread=Als ungelesen markieren +mark_all_as_read=Alle als gelesen markieren +subscriptions=Abonnements +watching=Beobachtet +no_subscriptions=Keine Abonnements [gpg] +default_key=Mit Standardschlüssel signiert +error.extract_sign=Die Signatur konnte nicht extrahiert werden +error.generate_hash=Es konnte kein Hash vom Commit generiert werden +error.no_committer_account=Es ist kein Account mit der E-Mail-Adresse des Committers verbunden +error.no_gpg_keys_found=Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden +error.not_signed_commit=Kein signierter Commit +error.failed_retrieval_gpg_keys=Fehler beim Abrufen eines Schlüssels des Committer-Kontos +error.probable_bad_signature=WARNHINWEIS! Obwohl ein Schlüssel mit dieser ID in der Datenbank existiert, verifiziert er nicht diesen Commit! Dieser Commit ist VERDÄCHTIG. +error.probable_bad_default_signature=WARNHINWEIS! Obwohl der Standardschlüssel diese ID hat, verifiziert er nicht diesen Commit! Dieser Commit ist VERDÄCHTIG. [units] unit=Einheit @@ -3410,11 +3533,183 @@ error.no_unit_allowed_repo=Du hast keine Berechtigung, auf etwas in diesem Repos error.unit_not_allowed=Du hast keine Berechtigung, um auf diesen Repository-Bereich zuzugreifen. [packages] +title=Pakete desc=Repository-Pakete verwalten. +empty=Noch keine Pakete vorhanden. +empty.documentation=Weitere Informationen zur Paket-Registry findest du in der Dokumentation. +empty.repo=Hast du ein Paket hochgeladen, das hier nicht angezeigt wird? Gehe zu den Paketeinstellungen und verlinke es mit diesem Repo. +registry.documentation=Für weitere Informationen zur %s-Registry, schaue in der Dokumentation nach. +filter.type=Typ +filter.type.all=Alle +filter.no_result=Keine Ergebnisse mit diesen Kriterien gefunden. +filter.container.tagged=Getaggt +filter.container.untagged=Nicht getaggt +published_by=%[1]s von %[3]s veröffentlicht +published_by_in=%[1]s von %[3]s in %[5]s veröffentlicht +installation=Installation +about=Über dieses Paket +requirements=Voraussetzungen +dependencies=Abhängigkeiten +keywords=Schlüsselwörter +details=Details +details.author=Autor +details.project_site=Projektwebseite +details.repository_site=Repository-Webseite +details.documentation_site=Dokumentationswebseite +details.license=Lizenz +assets=Assets +versions=Versionen +versions.view_all=Alle anzeigen +dependency.id=ID +dependency.version=Version +alpine.registry=Richte diese Registry ein, indem Du die URL in die /etc/apk/repositories-Datei hinzufügst: +alpine.registry.key=Lade den öffentlichen RSA-Key der Registry in den /etc/apk/keys/-Ordner, um die Signatur zu überprüfen: +alpine.registry.info=Wähle $branch und $repository aus der Liste unten. +alpine.install=Nutze folgenden Befehl, um das Paket zu installieren: +alpine.repository=Repository-Informationen +alpine.repository.branches=Branches +alpine.repository.repositories=Repositorys +alpine.repository.architectures=Architekturen +cargo.registry=Richte diese Registry in der Cargo-Konfigurationsdatei ein (z.B. ~/.cargo/config.toml): +cargo.install=Um das Paket mit Cargo zu installieren, führe den folgenden Befehl aus: +chef.registry=Richte diese Registry in deiner ~/.chef/config.rb-Datei ein: +chef.install=Nutze folgenden Befehl, um das Paket zu installieren: +composer.registry=Setze diese Paketverwaltung in deiner ~/.composer/config.json-Datei auf: +composer.install=Nutze folgenden Befehl, um das Paket mit Composer zu installieren: +composer.dependencies=Abhängigkeiten +composer.dependencies.development=Entwicklungsabhängigkeiten conan.details.repository=Repository +conan.registry=Diese Registry über die Kommandozeile einrichten: +conan.install=Um das Paket mit Conan zu installieren, führe den folgenden Befehl aus: +conda.registry=Richte diese Registry als Conda-Repository in deiner .condarc-Datei ein: +conda.install=Um das Paket mit Conda zu installieren, führe den folgenden Befehl aus: +container.details.type=Abbildtyp +container.details.platform=Plattform +container.pull=Lade das Container-Image von der Kommandozeile aus herunter: +container.digest=Prüfsumme +container.multi_arch=Betriebsystem/Architektur +container.layers=Abbildebenen +container.labels=Labels +container.labels.key=Schlüssel +container.labels.value=Wert +cran.registry=Richte diese Registry in deiner Rprofile.site-Datei ein: +cran.install=Nutze folgenden Befehl, um das Paket zu installieren: +debian.registry=Diese Registry über die Kommandozeile einrichten: +debian.registry.info=Wähle $distribution und $component aus der Liste unten. +debian.install=Nutze folgenden Befehl, um das Paket zu installieren: +debian.repository=Repository-Informationen +debian.repository.distributions=Distributionen +debian.repository.components=Komponenten +debian.repository.architectures=Architekturen +generic.download=Lade das Paket mit der Kommandozeile herunter: +go.install=Installiere das Paket über die Kommandozeile: +helm.registry=Diese Paketverwaltung über die Kommandozeile einrichten: +helm.install=Nutze folgenden Befehl, um das Paket zu installieren: +maven.registry=Setze diese Paketverwaltung in der pom.xml deines Projektes auf: +maven.install=Um das Paket zu verwenden, nimm Folgendes in den dependencies-Block in der pom.xml-Datei auf: +maven.install2=Über die Kommandozeile ausführen: +maven.download=Nutze folgendes Kommando, um die Abhängigkeit herunterzuladen: +nuget.registry=Diese Registry über die Kommandozeile einrichten: +nuget.install=Um das Paket mit NuGet zu installieren, führe den folgenden Befehl aus: +nuget.dependency.framework=Zielframework +npm.registry=Setze diese Paketverwaltung in der .npmrc deines Projektes auf: +npm.install=Um das Paket mit npm zu installieren, führe den folgenden Befehl aus: +npm.install2=oder füge es zur package.json-Datei hinzu: +npm.dependencies=Abhängigkeiten +npm.dependencies.development=Entwicklungsabhängigkeiten +npm.dependencies.peer=Peer-Abhängigkeiten +npm.dependencies.optional=Optionale Abhängigkeiten +npm.details.tag=Tag +pub.install=Um das Paket mit Dart zu installieren, führe den folgenden Befehl aus: +pypi.requires=Erfordert Python +pypi.install=Nutze folgenden Befehl, um das Paket mit pip zu installieren: +rpm.registry=Diese Registry über die Kommandozeile einrichten: +rpm.distros.redhat=auf RedHat-basierten Distributionen +rpm.distros.suse=auf SUSE-basierten Distributionen +rpm.install=Nutze folgenden Befehl, um das Paket zu installieren: +rpm.repository = Repository-Info +rpm.repository.architectures = Architekturen +rubygems.install=Um das Paket mit gem zu installieren, führe den folgenden Befehl aus: +rubygems.install2=oder füg es zum Gemfile hinzu: +rubygems.dependencies.runtime=Laufzeitabhängigkeiten +rubygems.dependencies.development=Entwicklungsabhängigkeiten +rubygems.required.ruby=Benötigt Ruby-Version +rubygems.required.rubygems=Benötigt RubyGem-Version +swift.registry=Diese Registry über die Kommandozeile einrichten: +swift.install=Füge das Paket deiner Package.swift-Datei hinzu: +swift.install2=und führe den folgenden Befehl aus: +vagrant.install=Um eine Vagrant-Box hinzuzufügen, führe den folgenden Befehl aus: +settings.link=Dieses Paket einem Repository zuweisen +settings.link.description=Wenn du ein Paket mit einem Repository verknüpfst, wird es in der Paketliste des Repositorys angezeigt. +settings.link.select=Repository auswählen +settings.link.button=Repository-Link aktualisieren +settings.link.success=Repository-Link wurde erfolgreich aktualisiert. +settings.link.error=Fehler beim Aktualisieren des Repository-Links. +settings.delete=Paket löschen +settings.delete.description=Das Löschen eines Pakets ist dauerhaft und kann nicht rückgängig gemacht werden. +settings.delete.notice=Du bist dabei, %s (%s) zu löschen. Dieser Vorgang ist unwiderruflich. Bist du sicher? +settings.delete.success=Das Paket wurde gelöscht. +settings.delete.error=Löschen des Pakets fehlgeschlagen. +owner.settings.cargo.title=Cargo-Registry-Index +owner.settings.cargo.initialize=Index initialisieren +owner.settings.cargo.initialize.description=Ein spezielles Index-Repository wird benötigt, um die Cargo-Registry zu nutzen. Diese Option wird dieses Repository (neu) erstellen und automatisch konfigurieren. +owner.settings.cargo.initialize.error=Cargo-Index konnte nicht initialisiert werden: %v +owner.settings.cargo.initialize.success=Der Cargo-Index wurde erfolgreich erstellt. +owner.settings.cargo.rebuild=Index neu erstellen +owner.settings.cargo.rebuild.description=Neubauen kann hilfreich sein, wenn der Index nicht mit den gespeicherten Cargo-Paketen synchronisiert ist. +owner.settings.cargo.rebuild.error=Cargo-Index konnte nicht neu erstellt werden: %v +owner.settings.cargo.rebuild.success=Der Cargo-Index wurde erfolgreich neu erstellt. +owner.settings.cleanuprules.title=Bereinigungsregeln +owner.settings.cleanuprules.add=Bereinigungsregel hinzufügen +owner.settings.cleanuprules.edit=Bereinigungsregel bearbeiten +owner.settings.cleanuprules.none=Es bestehen derzeit keine Bereinigungsregeln. +owner.settings.cleanuprules.preview=Vorschau der Bereinigungsregel +owner.settings.cleanuprules.preview.overview=%d Pakete sollen entfernt werden. +owner.settings.cleanuprules.preview.none=Bereinigungsregel stimmt mit keinem Paket überein. owner.settings.cleanuprules.enabled=Aktiviert +owner.settings.cleanuprules.pattern_full_match=Muster auf den vollständigen Paketnamen anwenden +owner.settings.cleanuprules.keep.title=Versionen, die diesen Regeln entsprechen, werden beibehalten, auch wenn sie mit einer Entfernungsregel unten übereinstimmen. +owner.settings.cleanuprules.keep.count=Behalte die aktuellsten owner.settings.cleanuprules.keep.count.1=1 Version pro Paket owner.settings.cleanuprules.keep.count.n=%d Versionen pro Paket +owner.settings.cleanuprules.keep.pattern=Behalte übereinstimmende Versionen +owner.settings.cleanuprules.keep.pattern.container=Die Version latest bei Container-Paketen wird immer behalten. +owner.settings.cleanuprules.remove.title=Versionen, die diesen Regeln entsprechen, werden entfernt, es sei denn, eine obige Regel besagt, sie zu behalten. +owner.settings.cleanuprules.remove.days=Entferne Versionen älter als +owner.settings.cleanuprules.remove.pattern=Entferne übereinstimmende Versionen +owner.settings.cleanuprules.success.update=Bereinigungsregel wurde aktualisiert. +owner.settings.cleanuprules.success.delete=Bereinigungsregel wurde gelöscht. +owner.settings.chef.title=Chef-Registry +owner.settings.chef.keypair=Schlüsselpaar generieren +owner.settings.chef.keypair.description=Anfragen an die Chef-Registry müssen zur Authentifizierung kryptografisch signiert werden. Beim Erstellen eines Schlüsselpaars wird nur der öffentliche Schlüssel in Forgejo gespeichert. Der private Schlüssel wird dir für die Verwendung mit knife bereitgestellt. Das Generieren eines neuen Schlüsselpaars überschreibt das vorherige. +rpm.repository.multiple_groups = Dieses Paket ist in mehreren Gruppen verfügbar. +owner.settings.cargo.rebuild.no_index = Kann nicht erneut erzeugen, es wurde kein Index initialisiert. +npm.dependencies.bundle = Gebündelte Abhängigkeiten +arch.pacman.helper.gpg = Trust-Zertifikat für pacman hinzufügen: +arch.pacman.repo.multi = %s hat die gleiche Version in verschiedenen Distributionen. +arch.pacman.repo.multi.item = Konfiguration für %s +arch.pacman.conf = Server mit verwandter Distribution und Architektur zu /etc/pacman.conf hinzufügen: +arch.pacman.sync = Paket mit pacman synchronisieren: +arch.version.properties = Versionseigenschaften +arch.version.description = Beschreibung +arch.version.provides = Bietet +arch.version.groups = Gruppe +arch.version.depends = Hängt ab von +arch.version.makedepends = Make-Abhängigkeit +arch.version.checkdepends = Prüfungs-Abhängigkeit +arch.version.conflicts = Konflikte +arch.version.replaces = Ersetzt +arch.version.backup = Backup +arch.version.optdepends = Optionale Abhängigkeit +container.images.title = Bilder +search_in_external_registry = In %s suchen +alt.registry = Diese Registry von der Befehlszeile aus einrichten: +alt.registry.install = Um das Paket zu installieren, folgenden Befehl ausführen: +alt.install = Paket installieren +alt.setup = Ein Repository zur Liste der verbundenen Repositorys hinzufügen (wähle die nötige Architektur anstelle von „_arch_“): +alt.repository = Repository-Infos +alt.repository.architectures = Architekturen +alt.repository.multiple_groups = Dieses Paket ist in verschiedenen Gruppen verfügbar. [secrets] secrets=Geheimnisse @@ -3432,8 +3727,66 @@ deletion.failed=Geheimnis konnte nicht entfernt werden. management=Geheimnisse verwalten [actions] +actions=Actions + unit.desc=Integrierte CI/CD-Pipelines mit Forgejo-Actions verwalten. +status.unknown=Unbekannt +status.waiting=Wartend +status.running=Laufend +status.success=Erfolg +status.failure=Fehler +status.cancelled=Abgebrochen +status.skipped=Übersprungen +status.blocked=Blockiert + +runners=Runner +runners.runner_manage_panel=Runner verwalten +runners.new=Neuen Runner erstellen +runners.new_notice=Wie man einen Runner startet +runners.status=Status +runners.id=ID +runners.name=Name +runners.owner_type=Typ +runners.description=Beschreibung +runners.labels=Labels +runners.last_online=Letzte Online-Zeit +runners.runner_title=Runner +runners.task_list=Letzte Aufgaben dieses Runners +runners.task_list.no_tasks=Es gibt noch keine Aufgabe. +runners.task_list.run=Ausführen +runners.task_list.status=Status +runners.task_list.repository=Repository +runners.task_list.commit=Commit +runners.task_list.done_at=Fertig um +runners.edit_runner=Runner bearbeiten +runners.update_runner=Änderungen anwenden +runners.update_runner_success=Runner erfolgreich aktualisiert +runners.update_runner_failed=Der Runner konnte nicht aktualisiert werden +runners.delete_runner=Diesen Runner löschen +runners.delete_runner_success=Runner erfolgreich gelöscht +runners.delete_runner_failed=Der Runner konnte nicht gelöscht werden +runners.delete_runner_header=Bestätigen, um diesen Runner zu löschen +runners.delete_runner_notice=Wenn eine Aufgabe auf diesem Runner ausgeführt wird, wird sie beendet und als fehlgeschlagen markiert. Dies könnte Workflows zerstören. +runners.none=Keine Runner verfügbar +runners.status.unspecified=Unbekannt +runners.status.idle=Inaktiv +runners.status.active=Aktiv +runners.status.offline=Offline +runners.version=Version +runners.reset_registration_token=Registrierungs-Token zurücksetzen +runners.reset_registration_token_success=Runner-Registrierungstoken erfolgreich zurückgesetzt + +runs.all_workflows=Alle Workflows +runs.commit=Commit +runs.scheduled=Geplant +runs.pushed_by=gepusht von +runs.invalid_workflow_helper=Die Workflow-Konfigurationsdatei ist ungültig. Bitte überprüfe deine Konfigurationsdatei: %s +runs.actor=Initiator +runs.status=Status +runs.actors_no_select=Alle Initiatoren +runs.status_no_select=Alle Status +runs.no_results=Keine passenden Ergebnisse gefunden. runs.no_runs=Der Workflow hat noch keine Ausführungen. workflow.disable=Workflow deaktivieren @@ -3444,8 +3797,27 @@ workflow.disabled=Workflow ist deaktiviert. need_approval_desc=Um Workflows für den Pull-Request eines Forks auszuführen, ist eine Genehmigung erforderlich. +variables=Variablen +variables.management=Variablen verwalten +variables.creation=Variable hinzufügen +variables.none=Es gibt noch keine Variablen. +variables.deletion=Variable entfernen +variables.deletion.description=Das Entfernen einer Variable ist dauerhaft und kann nicht rückgängig gemacht werden. Fortfahren? +variables.description=Variablen werden an bestimmte Aktionen übergeben und können nicht anderweitig gelesen werden. +variables.edit=Variable bearbeiten +variables.deletion.failed=Fehler beim Entfernen der Variable. +variables.deletion.success=Die Variable wurde entfernt. +variables.creation.failed=Fehler beim Hinzufügen der Variable. +variables.creation.success=Die Variable „%s“ wurde hinzugefügt. +variables.update.failed=Fehler beim Bearbeiten der Variable. +variables.update.success=Die Variable wurde bearbeitet. +runs.no_matching_online_runner_helper = Es existiert kein passender Online-Runner mit dem Label: %s +runs.no_workflows = Es existieren noch keine Workflows. runs.empty_commit_message = (leere Commit-Nachricht) variables.id_not_exist = Variable mit ID %d existiert nicht. +runs.workflow = Workflow +runs.no_job_without_needs = Der Workflow muss mindestens einen Job ohne Abhängigkeiten enthalten. +runs.no_job = Der Workflow muss mindestens einen Job enthalten workflow.dispatch.use_from = Workflow benutzen von workflow.dispatch.run = Workflow ausführen workflow.dispatch.input_required = Wert für Eingabe „%s“ erfordern. @@ -3456,6 +3828,7 @@ workflow.dispatch.success = Ausführung des Workflows wurde erfolgreich angefrag runs.expire_log_message = Logs wurden gelöscht, weil sie zu alt waren. runs.no_workflows.help_write_access = Keine Ahnung, wie man mit Forgejo Actions anfangen soll? Schau im Schnellstart in der Benutzerdokumentation vorbei, um deinen ersten Workflow zu schreiben, dann setze einen Forgejo-Runner auf, um deine Jobs auszuführen. runs.no_workflows.help_no_write_access = Um mehr über Forgejo Actions zu erfahren, siehe die Dokumentation. +variables.not_found = Die Variable wurde nicht gefunden. [projects] type-1.display_name=Individuelles Projekt @@ -3501,8 +3874,19 @@ regexp = RegExp regexp_tooltip = Suchbegriff als regulären Ausdruck interpretieren [markup] +filepreview.line = Zeile %[1]d in %[2]s +filepreview.truncated = Vorschau wurde gekürzt +filepreview.lines = Zeilen %[1]d bis %[2]d in %[3]s [munits.data] +gib = GiB +b = B +kib = KiB +tib = TiB +pib = PiB +mib = MiB +eib = EiB + [translation_meta] test = ok diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 4ad57c7bb5..6e11db0cbf 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -37,6 +37,18 @@ twofa=Πιστοποίηση δύο παραγόντων twofa_scratch=Κωδικός μίας χρήσης (για πιστοποίηση δύο παραγόντων / 2FA) passcode=Κωδικός +webauthn_insert_key=Εισάγετε το κλειδί ασφαλείας σας +webauthn_sign_in=Πατήστε το κουμπί στο κλειδί ασφαλείας σας. Αν το κλειδί ασφαλείας σας δεν έχει κουμπί, αποσυνδέστε το και συνδέστε το ξανά. +webauthn_press_button=Παρακαλώ πατήστε το κουμπί στο κλειδί ασφαλείας… +webauthn_use_twofa=Χρησιμοποιήστε έναν κωδικό δύο παραγόντων από το τηλέφωνό σας +webauthn_error=Δεν ήταν δυνατή η επικοινωνία με το κλειδί ασφαλείας σας. +webauthn_unsupported_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει επί του παρόντος WebAuthn. +webauthn_error_unknown=Παρουσιάστηκε ένα άγνωστο σφάλμα. Παρακαλώ προσπαθήστε ξανά. +webauthn_error_insecure=Το WebAuthn υποστηρίζει μόνο ασφαλές συνδέσεις. Αν θέλετε να διεξάγετε δοκιμές μέσω HTTP, μπορείτε να χρησιμοποιήσετε την προέλευση «localhost» ή «127.0.0.1» +webauthn_error_unable_to_process=Ο διακομιστής δεν μπόρεσε να επεξεργαστεί το αίτημά σας. +webauthn_error_duplicated=Το κλειδί ασφαλείας δεν επιτρέπεται για αυτό το αίτημα. Βεβαιωθείτε ότι το κλειδί δεν έχει ήδη καταχωρηθεί. +webauthn_error_empty=Πρέπει να ορίσετε ένα όνομα για αυτό το κλειδί. +webauthn_error_timeout=Το χρονικό όριο έφτασε πριν το κλειδί να διαβαστεί. Παρακαλώ ανανεώστε τη σελίδα και προσπαθήστε ξανά. repository=Αποθετήριο organization=Οργανισμός mirror=Αντίγραφο @@ -170,7 +182,7 @@ buttons.bold.tooltip=Προσθήκη έντονου κειμένου (Ctrl+B / buttons.italic.tooltip=Προσθήκη πλαγίου κειμένου (Ctrl+I / ⌘I) buttons.quote.tooltip=Παράθεση κειμένου buttons.code.tooltip=Προσθήκη κώδικα -buttons.link.tooltip=Προσθήκη συνδέσμου (Ctrl+K / ⌘K) +buttons.link.tooltip=Προσθήκη συνδέσμου buttons.list.unordered.tooltip=Προσθήκη απλής λίστας buttons.list.ordered.tooltip=Προσθήκη αριθμημένης λίστας buttons.list.task.tooltip=Προσθήκη λίστας εργασιών @@ -1142,6 +1154,14 @@ migrate_options_lfs_endpoint.label=Άκρο LFS migrate_options_lfs_endpoint.description=Η μεταφορά θα προσπαθήσει να χρησιμοποιήσει το Git remote για να καθορίσει τον διακομιστή LFS. Μπορείτε επίσης να καθορίσετε ένα δικό σας endpoint αν τα δεδομένα LFS του αποθετηρίου αποθηκεύονται κάπου αλλού. migrate_options_lfs_endpoint.description.local=Μια διαδρομή στο τοπικό διακομιστή επίσης υποστηρίζεται. migrate_options_lfs_endpoint.placeholder=Αν αφεθεί κενό, το άκρο θα προκύψει από το URL του κλώνου +migrate_items=Αντικείμενα μεταφοράς +migrate_items_wiki=Wiki +migrate_items_milestones=Ορόσημα +migrate_items_labels=Σήματα +migrate_items_issues=Ζητήματα +migrate_items_pullrequests=Pull requests +migrate_items_merge_requests=Merge requests +migrate_items_releases=Κυκλοφορίες migrate_repo=Μεταφορά αποθετηρίου migrate.clone_address=Μεταφορά / Κλωνοποίηση από το URL migrate.clone_address_desc=Το HTTP(S) ή το Git URL «κλωνοποίησης» ενός υπάρχοντος αποθετηρίου @@ -1160,6 +1180,16 @@ migrate.migrating=Γίνεται μεταφορά από το %s… migrate.migrating_failed=Η μεταφορά από το %s απέτυχε. migrate.migrating_failed.error=Αποτυχία μεταφοράς: %s migrate.migrating_failed_no_addr=Η μεταφορά απέτυχε. +migrate.migrating_git=Τα δεδομένα Git μεταφέρονται +migrate.migrating_topics=Τα θέματα μεταφέρονται +migrate.migrating_milestones=Τα ορόσημα μεταφέρονται +migrate.migrating_labels=Τα σήματα μεταφέρονται +migrate.migrating_releases=Οι κυκλοφορίες μεταφέρονται +migrate.migrating_issues=Τα ζητήματα μεταφέρονται +migrate.migrating_pulls=Μεταφέρονται τα pull requests +migrate.cancel_migrating_title=Ακύρωση μεταφοράς +migrate.cancel_migrating_confirm=Θέλετε να ακυρώσετε αυτή τη μεταφορά; + mirror_from=είδωλο του forked_from=forked από generated_from=παραγμένο από @@ -1535,7 +1565,7 @@ issues.ref_reopening_from=`αναφέρθηκε σε αυτό τ issues.ref_from=`από %[1]s` issues.author=Συγγραφέας issues.role.owner=Ιδιοκτήτης -issues.role.owner_helper=Αυτός ο χρήστης είναι ένας ιδιοκτήτης αυτού του αποθετηρίου. +issues.role.owner_helper=Αυτός ο χρήστης είναι ο ιδιοκτήτης αυτού του αποθετηρίου. issues.role.member=Μέλος issues.role.member_helper=Αυτός ο χρήστης είναι μέλος του οργανισμού, του οποίου ανήκει το repository. issues.role.collaborator=Συνεργάτης @@ -2604,8 +2634,14 @@ activity.navbar.pulse = Παλμός settings.add_collaborator_blocked_them = Δεν είναι δυνατή η προσθήκη του χρήστη ως συνεργάτη, καθώς έχει αποκλείσει τον κάτοχο του αποθετηρίου. settings.wiki_rename_branch_main_notices_2 = Θα αλλάξει το όνομα του εσωτερικού κλάδου για το wiki του αποθετηρίου %s. Οι υπάρχουσες υποβολές θα πρέπει να ενημερωθούν. settings.add_collaborator_blocked_our = Δεν είναι δυνατή η προσθήκη του χρήστη ως συνεργάτη, καθώς ο κάτοχος του αποθετηρίου τον έχει αποκλείσει. +n_branch_few = %s κλάδοι +n_tag_one = %s ετικέτα +n_tag_few = %s ετικέτες +n_commit_one = %s υποβολή stars = Αστέρια +n_branch_one = %s κλάδος commits.search_branch = Αυτός ο κλάδος +n_commit_few = %s υποβολές settings.sourcehut_builds.secrets = Μυστικά settings.add_webhook.invalid_path = Η τοποθεσία του αρχείου δεν μπορεί να περιέχει κενά, «.» ή «..». Δεν μπορεί να αρχίζει ή να τελειώνει με μία κάθετο. commits.browse_further = Περιήγηση περισσοτέρων @@ -2654,6 +2690,8 @@ settings.transfer.button = Παραχώρηση ιδιοκτησίας settings.matrix.room_id_helper = Το ID δωματίου μπορείτε να το βρείτε στο πρόγραμμα Element > Room Settings > Advanced > Internal room ID. Παράδειγμα: %s. wiki.search = Αναζήτηση wiki wiki.no_search_results = Κανένα αποτέλεσμα +n_release_one = %s κυκλοφορία +n_release_few = %s κυκλοφορίες release.hide_archive_links_helper = Απόκρυψη των αρχείων πηγαίου κώδικα που δημιουργούνται αυτόματα για αυτή την δημιοσίευση. Αυτή η ρύθμιση είναι χρήσιμη αν ανεβάζετε τα δικά σας αρχεία. release.type_attachment = Συνημμένο activity.published_prerelease_label = Προδημοσίευση @@ -2923,6 +2961,34 @@ dashboard.sync_external_users=Συγχρονισμός δεδομένων εξω dashboard.cleanup_hook_task_table=Εκκαθάριση πίνακα hook_task dashboard.cleanup_packages=Εκκαθάριση ληγμένων πακέτων dashboard.cleanup_actions=Καθαρισμός ληγμένων καταγραφών και συνημμένων από τις δράσεις +dashboard.server_uptime=Uptime διακομιστή +dashboard.current_goroutine=Τρέχουσες goroutines +dashboard.current_memory_usage=Τρέχουσα χρήση μνήμης +dashboard.total_memory_allocated=Συνολική χρησιμοποιούμενη μνήμη +dashboard.memory_obtained=Μνήμη που λαμβάνεται +dashboard.pointer_lookup_times=Πλήθος αναζητήσεων δείκτη +dashboard.memory_allocate_times=Κατανομές μνήμης +dashboard.memory_free_times=Ελευθερώσεις μνήμης +dashboard.current_heap_usage=Τρέχουσα χρήση heap +dashboard.heap_memory_obtained=Μνήμη heap που λαμβάνεται +dashboard.heap_memory_idle=Αδρανής μνήμη heap +dashboard.heap_memory_in_use=Μνήμη heap σε χρήση +dashboard.heap_memory_released=Μνήμη heap που απελευθερώθηκε +dashboard.heap_objects=Αντικείμενα στο heap +dashboard.bootstrap_stack_usage=Χρήση στοίβας bootstrap +dashboard.stack_memory_obtained=Μνήμη στοίβας που λαμβάνεται +dashboard.mspan_structures_usage=Χρήση δομών MSpan +dashboard.mspan_structures_obtained=Δομές MSpan που έχουν ληφθεί +dashboard.mcache_structures_usage=Χρήση δομών MCache +dashboard.mcache_structures_obtained=Δομές MCache που έχουν ληφθεί +dashboard.profiling_bucket_hash_table_obtained=Profiling bucket hash table που έχει ληφθεί +dashboard.gc_metadata_obtained=Μεταδεδομένα GC που έχουν ληφθεί +dashboard.other_system_allocation_obtained=Άλλες κατανομές συστήματος που έχουν ληφθεί +dashboard.next_gc_recycle=Επόμενη ανακύκλωση GC +dashboard.last_gc_time=Χρόνος από την τελευταία φορά που έγινε GC +dashboard.total_gc_pause=Σύνολο παύσης GC +dashboard.last_gc_pause=Τελευταία παύση GC +dashboard.gc_times=Χρόνοι GC dashboard.delete_old_actions=Διαγραφή όλων των παλιών δραστηριοτήτων από τη βάση δεδομένων dashboard.delete_old_actions.started=Ξεκίνησε η διαγραφή όλων των παλιών δραστηριοτήτων από τη βάση δεδομένων. dashboard.update_checker=Ελεγκτής ενημερώσεων @@ -2979,6 +3045,18 @@ users.purge_help=Εξαναγκαστική διαγραφή χρήστη καθ users.still_own_packages=Αυτός ο χρήστης εξακολουθεί να κατέχει ένα ή περισσότερα πακέτα, διαγράψτε αυτά τα πακέτα πρώτα. users.deletion_success=Ο λογαριασμός χρήστη έχει διαγραφεί. users.reset_2fa=Επαναφορά 2FA +users.list_status_filter.menu_text=Φίλτρο +users.list_status_filter.reset=Επαναφορά +users.list_status_filter.is_active=Ενεργό +users.list_status_filter.not_active=Ανενεργό +users.list_status_filter.is_admin=Διαχειριστής +users.list_status_filter.not_admin=Χωρίς δικαιώματα διαχειριστή +users.list_status_filter.is_restricted=Περιορισμένος +users.list_status_filter.not_restricted=Χωρίς περιορισμούς +users.list_status_filter.is_prohibit_login=Απαγορευμένη σύνδεση +users.list_status_filter.not_prohibit_login=Επιτρέπεται η σύνδεση +users.list_status_filter.is_2fa_enabled=Με ενεργοποιημένο 2FA +users.list_status_filter.not_2fa_enabled=Χωρίς 2FA users.details=Λεπτομέρειες χρήστη emails.email_manage_panel=Διαχείριση email χρηστών @@ -3298,6 +3376,26 @@ monitor.process.cancel_desc=Η ακύρωση μιας διαδικασίας μ monitor.process.cancel_notices=Ακύρωση: %s; monitor.process.children=Θυγατρικές +monitor.queues=Ουρές +monitor.queue=Ουρά: %s +monitor.queue.name=Όνομα +monitor.queue.type=Τύπος +monitor.queue.exemplar=Τύπος Υποδείγματος +monitor.queue.numberworkers=Αριθμός εργατών +monitor.queue.activeworkers=Ενεργοί εργάτες +monitor.queue.maxnumberworkers=Μέγιστος αριθμός εργατών +monitor.queue.numberinqueue=Πλήθος ουράς +monitor.queue.review_add=Εξέταση / προσθήκη εργατών +monitor.queue.settings.title=Ρυθμίσεις δεξαμενής +monitor.queue.settings.desc=Οι δεξαμενές αυξάνονται δυναμικά όταν υπάρχει φραγή της ουράς των εργατών τους. +monitor.queue.settings.maxnumberworkers=Μέγιστος Αριθμός Εργατών +monitor.queue.settings.maxnumberworkers.placeholder=Αυτή τη στιγμή %[1]d +monitor.queue.settings.maxnumberworkers.error=Ο μέγιστος αριθμός εργατών πρέπει να είναι αριθμός +monitor.queue.settings.submit=Ενημέρωση ρυθμίσεων +monitor.queue.settings.changed=Οι ρυθμίσεις ενημερώθηκαν +monitor.queue.settings.remove_all_items=Αφαίρεση όλων +monitor.queue.settings.remove_all_items_done=Όλα τα αντικείμενα στην ουρά αφαιρέθηκαν. + notices.system_notice_list=Ειδοποιήσεις συστήματος notices.view_detail_header=Προβολή λεπτομερειών ειδοποίησης notices.operations=Λειτουργίες @@ -3398,10 +3496,35 @@ raw_seconds=δευτερόλεπτα raw_minutes=λεπτά [dropzone] +default_message=Σύρετε αρχεία ή κάντε κλικ εδώ για να τα ανεβάσετε. +invalid_input_type=Δεν μπορείτε να ανεβάσετε αρχεία αυτού του τύπου. +file_too_big=Το μέγεθος αρχείου ({{filesize}} MB) υπερβαίνει το μέγιστο μέγεθος ({{maxFilesize}} MB). +remove_file=Αφαίρεση αρχείου [notification] +notifications=Ειδοποιήσεις +unread=Μη αναγνωσμένες +read=Αναγνωσμένες +no_unread=Καμία μη αναγνωσμένη ειδοποίηση. +no_read=Δεν υπάρχουν αναγνωσμένες ειδοποιήσεις. +pin=Καρφίτσωμα ειδοποίησης +mark_as_read=Σήμανση ως αναγνωσμένη +mark_as_unread=Σήμανση ως μη αναγνωσμένη +mark_all_as_read=Σήμανση όλων ως αναγνωσμένες +subscriptions=Συνδρομές +watching=Παρακολούθηση +no_subscriptions=Καμία συνδρομή [gpg] +default_key=Υπογραφή με το προεπιλεγμένο κλειδί +error.extract_sign=Αποτυχία εξαγωγής υπογραφής +error.generate_hash=Αποτυχία δημιουργίας hash της υποβολής +error.no_committer_account=Δεν υπάρχει λογαριασμός που συσχετίζεται με τη διεύθυνση email του υποβάλλοντα +error.no_gpg_keys_found=Δεν βρέθηκε γνωστό κλειδί για αυτήν την υπογραφή στη βάση δεδομένων +error.not_signed_commit=Η υποβολή δεν είναι υπογεγραμμένη +error.failed_retrieval_gpg_keys=Αποτυχία ανάκτησης κλειδιού που είναι συνδεδεμένο στο λογαριασμό του υποβάλλοντα +error.probable_bad_signature=ΠΡΟΣΟΧΗ! Αν και υπάρχει ένα κλειδί με αυτό το ID στη βάση δεδομένων δεν επαληθεύει αυτή την υποβολή! Αυτή η υποβολή είναι ΥΠΟΠΤΗ. +error.probable_bad_default_signature=ΠΡΟΣΟΧΗ! Αν και το προεπιλεγμένο κλειδί έχει αυτό το ID, δεν επαληθεύει αυτή την υποβολή! Αυτή η υποβολή είναι ΥΠΟΠΤΗ. [units] unit=Μονάδα @@ -3409,11 +3532,183 @@ error.no_unit_allowed_repo=Δεν σας επιτρέπεται η πρόσβα error.unit_not_allowed=Δεν σας επιτρέπεται η πρόσβαση σε αυτήν την μονάδα αποθετηρίου. [packages] +title=Πακέτα desc=Διαχείριση πακέτων μητρώου. +empty=Δεν υπάρχουν πακέτα ακόμα. +empty.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο πακέτων, συμβουλευτείτε τον οδηγό. +empty.repo=Μήπως ανεβάσατε ένα πακέτο, αλλά δεν εμφανίζεται εδώ; Πηγαίνετε στις ρυθμίσεις πακέτων και συνδέστε το σε αυτό το repository. +registry.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο %s, συμβουλευτείτε τον οδηγό. +filter.type=Τύπος +filter.type.all=Όλα +filter.no_result=Το φίλτρο δεν παρήγαγε αποτελέσματα. +filter.container.tagged=Επισημάνθηκαν +filter.container.untagged=Χωρίς επισήμανση +published_by=Δημοσιεύθηκε %[1]s από %[3]s +published_by_in=Δημοσιεύθηκε %[1]s κατά %[3]s σε %[5]s +installation=Εγκατάσταση +about=Σχετικά με αυτό το πακέτο +requirements=Απαιτήσεις +dependencies=Εξαρτήσεις +keywords=Λέξεις κλειδιά +details=Λεπτομέρειες +details.author=Συγγραφέας +details.project_site=Ιστοσελίδα έργου +details.repository_site=Ιστοσελίδα αποθετηρίου +details.documentation_site=Ιστοσελίδα τεκμηρίωσης +details.license=Άδεια +assets=Πόροι +versions=Εκδόσεις +versions.view_all=Προβολή όλων +dependency.id=ID +dependency.version=Έκδοση +alpine.registry=Ρυθμίστε αυτό το μητρώο προσθέτοντας το url στο αρχείο /etc/apk/repositories: +alpine.registry.key=Αποθηκεύστε το δημόσιο κλειδί RSA του μητρώου στο φάκελο /etc/apk/keys/ για να επαληθεύσετε την υπογραφή ευρετηρίου: +alpine.registry.info=Επιλέξτε $branch και $repository από την παρακάτω λίστα. +alpine.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +alpine.repository=Πληροφορίες αποθετηρίου +alpine.repository.branches=Κλάδοι +alpine.repository.repositories=Αποθετήρια +alpine.repository.architectures=Αρχιτεκτονικές +cargo.registry=Ρυθμίστε αυτό το μητρώο στις ρυθμίσεις του Cargo (για παράδειγμα ~/.cargo/config.toml): +cargo.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το Cargo, εκτελέστε την ακόλουθη εντολή: +chef.registry=Ρυθμίστε αυτό το μητρώο στο αρχείο ~/.chef/config.rb: +chef.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +composer.registry=Ρυθμίστε αυτό το μητρώο στο αρχείο ~/.composer/config.json: +composer.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το Composer, εκτελέστε την ακόλουθη εντολή: +composer.dependencies=Εξαρτήσεις +composer.dependencies.development=Εξαρτήσεις ανάπτυξης conan.details.repository=Repository +conan.registry=Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: +conan.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το Conan, εκτελέστε την ακόλουθη εντολή: +conda.registry=Ρυθμίστε αυτό το μητρώο ως repository Conda στο αρχείο .condarc: +conda.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το Conda, εκτελέστε την ακόλουθη εντολή: +container.details.type=Τύπος εικόνας +container.details.platform=Πλατφόρμα +container.pull=Κατεβάστε την εικόνα από τη γραμμή εντολών: +container.digest=Σύνοψη +container.multi_arch=ΛΣ / Αρχιτεκτονική +container.layers=Στρώματα εικόνας +container.labels=Ετικέτες +container.labels.key=Κλειδί +container.labels.value=Τιμή +cran.registry=Ρυθμίστε αυτό το μητρώο στο αρχείο Rprofile.site: +cran.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +debian.registry=Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: +debian.registry.info=Επιλέξτε $distribution και $component από την παρακάτω λίστα. +debian.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +debian.repository=Πληροφορίες αποθετηρίου +debian.repository.distributions=Διανομές +debian.repository.components=Συστατικά +debian.repository.architectures=Αρχιτεκτονικές +generic.download=Λήψη πακέτου από τη γραμμή εντολών: +go.install=Εγκαταστήστε το πακέτο από τη γραμμή εντολών: +helm.registry=Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: +helm.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +maven.registry=Ρυθμίστε αυτό το μητρώο στο αρχείο pom.xml του έργου σας: +maven.install=Για να χρησιμοποιήσετε το πακέτο, συμπεριλάβετε τα ακόλουθα στη περιοχή dependencies στο αρχείο pom.xml: +maven.install2=Εκτέλεση μέσω γραμμής εντολών: +maven.download=Για να κατεβάσετε την εξάρτηση, εκτελέστε μέσω της γραμμής εντολών: +nuget.registry=Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: +nuget.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το NuGet, εκτελέστε την ακόλουθη εντολή: +nuget.dependency.framework=Πλαίσιο Ανάπτυξης +npm.registry=Ρυθμίστε αυτό το μητρώο στο αρχείο .npmrc του έργου σας: +npm.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας npm, εκτελέστε την ακόλουθη εντολή: +npm.install2=ή προσθέστε το στο αρχείο package.json: +npm.dependencies=Εξαρτήσεις +npm.dependencies.development=Εξαρτήσεις ανάπτυξης +npm.dependencies.peer=Εξαρτήσεις ομότιμου +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=Αρχιτεκτονικές +rubygems.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το gem, εκτελέστε την ακόλουθη εντολή: +rubygems.install2=ή προσθέστε το στο Gemfile: +rubygems.dependencies.runtime=Εξαρτήσεις κατά την εκτέλεση +rubygems.dependencies.development=Εξαρτήσεις ανάπτυξης +rubygems.required.ruby=Απαιτεί την έκδοση Ruby +rubygems.required.rubygems=Απαιτεί έκδοση RubyGem +swift.registry=Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: +swift.install=Προσθέστε το πακέτο στο αρχείο Package.swift: +swift.install2=και εκτελέστε την ακόλουθη εντολή: +vagrant.install=Για προσθήκη ενός κυτίου Vagrant, εκτελέστε την ακόλουθη εντολή: +settings.link=Σύνδεση αυτού του πακέτου με ένα repository +settings.link.description=Εάν συνδέσετε ένα πακέτο με ένα repository, το πακέτο περιλαμβάνεται στη λίστα πακέτων του repository. +settings.link.select=Επιλογή αποθετηρίου +settings.link.button=Ενημέρωση συνδέσμου αποθετηρίου +settings.link.success=Ο σύνδεσμος αποθετηρίου ενημερώθηκε επιτυχώς. +settings.link.error=Αποτυχία ενημέρωσης συνδέσμου αποθετηρίου. +settings.delete=Διαγραφή πακέτου +settings.delete.description=Η διαγραφή ενός πακέτου είναι μόνιμη και δεν μπορεί να αναιρεθεί. +settings.delete.notice=Πρόκειται να διαγράψετε το %s (%s). Αυτή η διαδικασία είναι μη αναστρέψιμη, είστε σίγουροι; +settings.delete.success=Το πακέτο έχει διαγραφεί. +settings.delete.error=Αποτυχία διαγραφής του πακέτου. +owner.settings.cargo.title=Ευρετήριο μητρώου Cargo +owner.settings.cargo.initialize=Αρχικοποίηση ευρετηρίου +owner.settings.cargo.initialize.description=Απαιτείται ένα ειδικό repository ευρετηρίου Git για τη χρήση του μητρώου Cargo. Χρησιμοποιώντας αυτή την επιλογή θα δημιουργηθεί ξανά το repository και θα ρυθμιστεί αυτόματα. +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.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=Η τελευταία έκδοση διατηρείται πάντα για τα πακέτα 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. Η αναδημιουργία ενός νέου ζεύγους κλειδιών αντικαθιστεί τα προηγούμενα. +rpm.repository.multiple_groups = Αυτό το πακέτο είναι διαθέσιμο σε πολλαπλά group. +owner.settings.cargo.rebuild.no_index = Η ανανέωση δεν είναι δυνατή, επειδή το ευρετήριο δεν έρχει αρχικοποιηθεί. +arch.version.conflicts = Συγκρούεται με +arch.version.replaces = Αντικαταστά +arch.pacman.repo.multi = Το %s έχει την ίδια έκδοση σε διαφορετικές διανομές. +arch.pacman.repo.multi.item = Ρυθμίσεις για %s +arch.pacman.helper.gpg = Προσθέστε το trust certificate για το pacman: +arch.pacman.conf = Προσθέστε τον server με τις σχετικές πληροφορίες διανομής και αρχιτεκτονικής επεξεργαστή στο /etc/pacman.conf: +arch.pacman.sync = «Συγχρονίστε» το πακέτο με το pacman: +arch.version.properties = Πληροφορίες έκδοσης +arch.version.description = Περιγραφή +arch.version.provides = Προσφέρει +arch.version.groups = Γκρουπ +arch.version.depends = Εξαρτάται +arch.version.optdepends = Εξαρτάται προαιρετικά από +arch.version.makedepends = Εξαρτήσεις make +arch.version.checkdepends = Εξαρτήσεις ελέγχου +container.images.title = Εικόνες +npm.dependencies.bundle = Ενσωματωμένες εξαρτήσεις +arch.version.backup = Αντίγραφο +alt.install = Εγκατάσταση πακέτου +alt.repository = Πληροφορίες αποθετηρίου +alt.repository.architectures = Αρχιτεκτονικές +alt.repository.multiple_groups = Αυτό το πακέτο είναι διαθέσιμο σε πολλαπλές ομάδες. +alt.registry.install = Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +alt.registry = Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: +alt.setup = Προσθήκη ενός αποθετηρίου στη λίστα των συνδεδεμένων αποθετηρίων (επιλέξτε την απαραίτητη αρχιτεκτονική αντί του "_arch_"): +search_in_external_registry = Αναζήτηση σε %s [secrets] secrets=Μυστικά @@ -3431,8 +3726,68 @@ deletion.failed=Αποτυχία αφαίρεσης μυστικού. management=Διαχείριση μυστικών [actions] +actions=Actions + unit.desc=Διαχείριση δράσεων +status.unknown=Απροσδιόριστη +status.waiting=Σε αναμονή +status.running=Σε εκτέλεση +status.success=Επιτυχία +status.failure=Αποτυχία +status.cancelled=Ακυρώθηκε +status.skipped=Παρακάμφθηκε +status.blocked=Αποκλείστηκε + +runners=Εκτελεστές +runners.runner_manage_panel=Διαχείριση εκτελεστών +runners.new=Δημιουργία νέου εκτελεστή +runners.new_notice=Πώς να ξεκινήσετε έναν εκτελεστή +runners.status=Κατάσταση +runners.id=ID +runners.name=Όνομα +runners.owner_type=Τύπος +runners.description=Περιγραφή +runners.labels=Ετικέτες +runners.last_online=Τελευταία ώρα σύνδεσης +runners.runner_title=Εκτελεστής +runners.task_list=Πρόσφατες εργασίες στον εκτελεστή +runners.task_list.no_tasks=Δεν υπάρχει καμία εργασία ακόμα. +runners.task_list.run=Εκτέλεση +runners.task_list.status=Κατάσταση +runners.task_list.repository=Αποθετήριο +runners.task_list.commit=Υποβολή +runners.task_list.done_at=Έτοιμο στις +runners.edit_runner=Επεξεργασία Εκτελεστή +runners.update_runner=Ενημέρωση αλλαγών +runners.update_runner_success=Ο εκτελεστής ενημερώθηκε επιτυχώς +runners.update_runner_failed=Αποτυχία ενημέρωσης εκτελεστή +runners.delete_runner=Διαγραφή του εκτελεστή +runners.delete_runner_success=Ο εκτελεστής διαγράφηκε επιτυχώς +runners.delete_runner_failed=Αποτυχία διαγραφής εκτελεστή +runners.delete_runner_header=Επιβεβαιώστε για τη διαγραφή του εκτελεστή +runners.delete_runner_notice=Αν μια εργασία εκτελείται σε αυτόν τον εκτελεστή, θα τερματιστεί και θα επισημανθεί ως αποτυχημένη. Μπορεί να χαλάσει τη ροή εργασίας του χτισίματος. +runners.none=Δεν υπάρχουν διαθέσιμοι εκτελεστές +runners.status.unspecified=Άγνωστη +runners.status.idle=Αδρανής +runners.status.active=Ενεργό +runners.status.offline=Χωρίς Σύνδεση +runners.version=Έκδοση +runners.reset_registration_token=Επαναφορά διακριτικού εγγραφής +runners.reset_registration_token_success=Επιτυχής επανέκδοση διακριτικού εγγραφής του εκτελεστή + +runs.all_workflows=Όλες οι ροές εργασίας +runs.commit=Υποβολή +runs.scheduled=Προγραμματισμένα +runs.pushed_by=ωθήθηκε από +runs.invalid_workflow_helper=Το αρχείο ροής εργασίας δεν είναι έγκυρο. Ελέγξτε το αρχείο σας: %s +runs.no_matching_online_runner_helper=Κανένας δικτυακός δρομέας με ετικέτα: %s +runs.actor=Φορέας +runs.status=Κατάσταση +runs.actors_no_select=Όλοι οι φορείς +runs.status_no_select=Όλες οι καταστάσεις +runs.no_results=Δεν βρέθηκαν αποτελέσματα. +runs.no_workflows=Δεν υπάρχουν ροές εργασίας ακόμα. runs.no_runs=Η ροή εργασίας δεν έχει τρέξει ακόμα. runs.empty_commit_message=(κενό μήνυμα υποβολής) @@ -3444,7 +3799,24 @@ workflow.disabled=Η ροή εργασιών είναι απενεργοποιη need_approval_desc=Πρέπει να εγκριθεί η εκτέλεση ροών εργασίας για pull request από fork. +variables=Μεταβλητές +variables.management=Διαχείριση μεταβλητών +variables.creation=Προσθήκη μεταβλητή +variables.none=Δεν υπάρχουν ακόμα μεταβλητές. +variables.deletion=Αφαίρεση μεταβλητής +variables.deletion.description=Η αφαίρεση μιας μεταβλητής είναι μόνιμη και δεν μπορεί να αναιρεθεί. Συνέχεια; +variables.description=Η μεταβλητές θα δίνονται σε ορισμένες δράσεις και δεν μπορούν να διαβαστούν αλλιώς. +variables.edit=Επεξεργασία Μεταβλητής +variables.deletion.failed=Αποτυχία αφαίρεσης της μεταβλητής. +variables.deletion.success=Η μεταβλητή έχει αφαιρεθεί. +variables.creation.failed=Αποτυχία προσθήκης μεταβλητής. +variables.creation.success=Η μεταβλητή «%s» προστέθηκε. +variables.update.failed=Αποτυχία επεξεργασίας μεταβλητής. +variables.update.success=Η μεταβλητή έχει τροποποιηθεί. variables.id_not_exist = Η μεταβλητή με id %d δεν υπάρχει. +runs.workflow = Ροή εργασίας +runs.no_job_without_needs = Η ροή εργασίας πρέπει να περιέχει τουλάχιστον ένα έργο που δεν εξαρτάται από κάποιο άλλο έργο. +runs.no_job = Η ροή εργασιών πρέπει να περιέχει τουλάχιστον μία εργασία (job) workflow.dispatch.trigger_found = Αυτή η ροή εργασίας έχει ένα «event trigger» workflow_dispatch. workflow.dispatch.success = Η εκτέλεση της ροής εργασίας αιτήθηκε επιτυχώς. workflow.dispatch.input_required = Απαιτείται τιμή για την είσοδο «%s». @@ -3455,6 +3827,7 @@ workflow.dispatch.invalid_input_type = Μη έγκυρο είδος εισόδο runs.no_workflows.help_write_access = Δεν γνωρίζεται πώς να ξεκινήσετε με τις Δράσεις Forgejo; Δείτε την γρήγορη εκκίνηση στην τεκμηρίωση χρήστη για να γράψετε την πρώτη σας ροή εργασίας, στη συνέχεια ορίστε έναν εκτελεστή Forgejo για να εκτελέσετε τις εργασίες σας. runs.no_workflows.help_no_write_access = Για να μάθετε για τις Δράσεις Forgejo, δείτε την τεκμηρίωση. workflow.dispatch.warn_input_limit = Προβολή μόνο των πρώτων %d εισόδων. +variables.not_found = Αποτυχία εύρεσης της μεταβλητής. [projects] type-1.display_name=Ατομικό έργο @@ -3500,8 +3873,18 @@ regexp = Κανονική Έκφραση regexp_tooltip = Ερμηνεία του όρου αναζήτησης ως κανονική έκφραση [munits.data] +mib = MiB +gib = GiB +tib = TiB +pib = PiB +eib = EiB +b = B +kib = KiB [markup] +filepreview.line = Γραμμή %[1]d στο αρχείο %[2]s +filepreview.lines = Γραμμές %[1]d έως %[2]d στο αρχείο %[3]s +filepreview.truncated = Η προεπισκόπηση έχει περικοπεί [translation_meta] test = ok diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a74cf843e9..ad560f9beb 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -38,6 +38,19 @@ twofa = Two-factor authentication twofa_scratch = Two-factor scratch code passcode = Passcode +webauthn_insert_key = Insert your security key +webauthn_sign_in = Press the button on your security key. If your security key has no button, re-insert it. +webauthn_press_button = Please press the button on your security key… +webauthn_use_twofa = Use a two-factor code from your phone +webauthn_error = Could not read your security key. +webauthn_unsupported_browser = Your browser does not currently support WebAuthn. +webauthn_error_unknown = An unknown error occurred. Please retry. +webauthn_error_insecure = WebAuthn only supports secure connections. For testing over HTTP, you can use the origin "localhost" or "127.0.0.1" +webauthn_error_unable_to_process = The server could not process your request. +webauthn_error_duplicated = The security key is not permitted for this request. Please make sure that the key is not already registered. +webauthn_error_empty = You must set a name for this key. +webauthn_error_timeout = Timeout reached before your key could be read. Please reload this page and retry. + repository = Repository new_fork = New repository fork new_project_column = New column @@ -192,7 +205,7 @@ buttons.bold.tooltip = Add bold text (Ctrl+B / ⌘B) buttons.italic.tooltip = Add italic text (Ctrl+I / ⌘I) buttons.quote.tooltip = Quote text buttons.code.tooltip = Add code -buttons.link.tooltip = Add a link (Ctrl+K / ⌘K) +buttons.link.tooltip = Add a link buttons.list.unordered.tooltip = Add a bullet list buttons.list.ordered.tooltip = Add a numbered list buttons.list.task.tooltip = Add a list of tasks @@ -912,7 +925,7 @@ regenerate_token = Regenerate access_token_regeneration = Regenerate access token access_token_regeneration_desc = Regenerating a token will revoke access to your account for applications using it. This cannot be undone. Continue? regenerate_token_success = The token has been regenerated. Applications that use it no longer have access to your account and must be updated with the new token. -repo_and_org_access = Repository and organization access +repo_and_org_access = Repository and Organization Access permissions_public_only = Public only permissions_access_all = All (public, private, and limited) select_permissions = Select permissions @@ -1195,6 +1208,14 @@ migrate_options_lfs_endpoint.label = LFS endpoint migrate_options_lfs_endpoint.description = Migration will attempt to use your Git remote to determine the LFS server. You can also specify a custom endpoint if the repository LFS data is stored somewhere else. migrate_options_lfs_endpoint.description.local = A local server path is supported too. migrate_options_lfs_endpoint.placeholder = If left blank, the endpoint will be derived from the clone URL +migrate_items = Migration items +migrate_items_wiki = Wiki +migrate_items_milestones = Milestones +migrate_items_labels = Labels +migrate_items_issues = Issues +migrate_items_pullrequests = Pull requests +migrate_items_merge_requests = Merge requests +migrate_items_releases = Releases migrate_repo = Migrate repository migrate.repo_desc_helper = Leave empty to import existing description migrate.clone_address = Migrate / Clone from URL @@ -1214,6 +1235,16 @@ migrate.migrating = Migrating from %s … migrate.migrating_failed = Migrating from %s failed. migrate.migrating_failed.error = Failed to migrate: %s migrate.migrating_failed_no_addr = Migration failed. +migrate.migrating_git = Migrating Git data +migrate.migrating_topics = Migrating topics +migrate.migrating_milestones = Migrating milestones +migrate.migrating_labels = Migrating labels +migrate.migrating_releases = Migrating releases +migrate.migrating_issues = Migrating issues +migrate.migrating_pulls = Migrating pull requests +migrate.cancel_migrating_title = Cancel migration +migrate.cancel_migrating_confirm = Do you want to cancel this migration? + mirror_from = mirror of forked_from = forked from generated_from = generated from @@ -1258,6 +1289,15 @@ milestones = Milestones org_labels_desc = Organization level labels that can be used with all repositories under this organization org_labels_desc_manage = manage +n_commit_one=%s commit +n_commit_few=%s commits +n_branch_one=%s branch +n_branch_few=%s branches +n_tag_one=%s tag +n_tag_few=%s tags +n_release_one = %s release +n_release_few = %s releases + released_this = released this file.title = %s at %s file_raw = Raw @@ -1515,6 +1555,7 @@ issues.remove_ref_at = `removed reference %s %s` issues.add_ref_at = `added reference %s %s` issues.delete_branch_at = `deleted branch %s %s` issues.filter_label = Label +issues.filter_label_exclude = Use Alt + Click to exclude labels issues.filter_label_no_select = All labels issues.filter_label_select_no_label = No label issues.filter_milestone = Milestone @@ -1610,7 +1651,7 @@ issues.author = Author issues.author.tooltip.issue = This user is the author of this issue. issues.author.tooltip.pr = This user is the author of this pull request. issues.role.owner = Owner -issues.role.owner_helper = This user is an owner of this repository. +issues.role.owner_helper = This user is the owner of this repository. issues.role.member = Member issues.role.member_helper = This user is a member of the organization owning this repository. issues.role.collaborator = Collaborator @@ -2529,7 +2570,7 @@ settings.matrix.access_token_helper = It is recommended to setup a dedicated Mat settings.matrix.room_id_helper = The Room ID can be retrieved from the Element web client > Room Settings > Advanced > Internal room ID. Example: %s. settings.archive.button = Archive repo settings.archive.header = Archive this repo -settings.archive.text = Archiving the repo will make it entirely read-only. It will be hidden from the dashboard. Nobody (not even you!) will be able to make new commits, or open any issues or pull requests. Documenting the archival reason is recommended to guide future developers who plan to fork the repository. +settings.archive.text = Archiving the repo will make it entirely read-only. It will be hidden from the dashboard. Nobody (not even you!) will be able to make new commits, or open any issues or pull requests. settings.archive.success = The repo was successfully archived. settings.archive.error = An error occurred while trying to archive the repo. See the log for more details. settings.archive.error_ismirror = You cannot archive a mirrored repo. @@ -2784,6 +2825,7 @@ team_name = Team name team_desc = Description team_name_helper = Team names should be short and memorable. team_desc_helper = Describe the purpose or role of the team. +team_access_desc = Repository access team_permission_desc = Permission team_unit_desc = Allow access to repository sections team_unit_disabled = (Disabled) @@ -2940,6 +2982,34 @@ dashboard.sync_external_users = Synchronize external user data dashboard.cleanup_hook_task_table = Clean up hook_task table dashboard.cleanup_packages = Clean up expired packages dashboard.cleanup_actions = Clean up expired logs and artifacts from actions +dashboard.server_uptime = Server uptime +dashboard.current_goroutine = Current goroutines +dashboard.current_memory_usage = Current memory usage +dashboard.total_memory_allocated = Total memory allocated +dashboard.memory_obtained = Memory obtained +dashboard.pointer_lookup_times = Pointer lookup times +dashboard.memory_allocate_times = Memory allocations +dashboard.memory_free_times = Memory frees +dashboard.current_heap_usage = Current heap usage +dashboard.heap_memory_obtained = Heap memory obtained +dashboard.heap_memory_idle = Heap memory idle +dashboard.heap_memory_in_use = Heap memory in use +dashboard.heap_memory_released = Heap memory released +dashboard.heap_objects = Heap objects +dashboard.bootstrap_stack_usage = Bootstrap stack usage +dashboard.stack_memory_obtained = Stack memory obtained +dashboard.mspan_structures_usage = MSpan structures usage +dashboard.mspan_structures_obtained = MSpan structures obtained +dashboard.mcache_structures_usage = MCache structures usage +dashboard.mcache_structures_obtained = MCache structures obtained +dashboard.profiling_bucket_hash_table_obtained = Profiling bucket hash table obtained +dashboard.gc_metadata_obtained = GC metadata obtained +dashboard.other_system_allocation_obtained = Other system allocation obtained +dashboard.next_gc_recycle = Next GC recycle +dashboard.last_gc_time = Time since last GC +dashboard.total_gc_pause = Total GC pause +dashboard.last_gc_pause = Last GC pause +dashboard.gc_times = GC times dashboard.delete_old_actions = Delete all old activities from database dashboard.delete_old_actions.started = Delete all old activities from database started. dashboard.update_checker = Update checker @@ -3003,6 +3073,18 @@ users.purge_help = Forcibly delete user and any repositories, organizations, and users.still_own_packages = This user still owns one or more packages, delete these packages first. users.deletion_success = The user account has been deleted. users.reset_2fa = Reset 2FA +users.list_status_filter.menu_text = Filter +users.list_status_filter.reset = Reset +users.list_status_filter.is_active = Active +users.list_status_filter.not_active = Inactive +users.list_status_filter.is_admin = Admin +users.list_status_filter.not_admin = Not admin +users.list_status_filter.is_restricted = Restricted +users.list_status_filter.not_restricted = Not restricted +users.list_status_filter.is_prohibit_login = Prohibit login +users.list_status_filter.not_prohibit_login = Allow login +users.list_status_filter.is_2fa_enabled = 2FA enabled +users.list_status_filter.not_2fa_enabled = 2FA disabled users.details = User details emails.email_manage_panel = Manage user emails @@ -3053,13 +3135,13 @@ packages.published = Published defaulthooks = Default webhooks defaulthooks.desc = Webhooks automatically make HTTP POST requests to a server when certain Forgejo events trigger. Webhooks defined here are defaults and will be copied into all new repositories. Read more in the webhooks guide. -defaulthooks.add_webhook = Add default webhook -defaulthooks.update_webhook = Update default webhook +defaulthooks.add_webhook = Add Default Webhook +defaulthooks.update_webhook = Update Default Webhook systemhooks = System webhooks systemhooks.desc = Webhooks automatically make HTTP POST requests to a server when certain Forgejo events trigger. Webhooks defined here will act on all repositories on the system, so please consider any performance implications this may have. Read more in the webhooks guide. -systemhooks.add_webhook = Add system webhook -systemhooks.update_webhook = Update system webhook +systemhooks.add_webhook = Add System Webhook +systemhooks.update_webhook = Update System Webhook auths.auth_manage_panel = Manage authentication sources auths.new = Add authentication source @@ -3322,6 +3404,26 @@ monitor.process.cancel = Cancel process monitor.process.cancel_desc = Canceling a process may cause data loss monitor.process.cancel_notices = Cancel: %s? +monitor.queues = Queues +monitor.queue = Queue: %s +monitor.queue.name = Name +monitor.queue.type = Type +monitor.queue.exemplar = Exemplar Type +monitor.queue.numberworkers = Number of workers +monitor.queue.activeworkers = Active workers +monitor.queue.maxnumberworkers = Max Number of workers +monitor.queue.numberinqueue = Number in queue +monitor.queue.review_add = Review / add workers +monitor.queue.settings.title = Pool settings +monitor.queue.settings.desc = Pools dynamically grow in response to their worker queue blocking. +monitor.queue.settings.maxnumberworkers = Max Number of workers +monitor.queue.settings.maxnumberworkers.placeholder = Currently %[1]d +monitor.queue.settings.maxnumberworkers.error = Max number of workers must be a number +monitor.queue.settings.submit = Update settings +monitor.queue.settings.changed = Settings updated +monitor.queue.settings.remove_all_items = Remove all +monitor.queue.settings.remove_all_items_done = All items in the queue have been removed. + notices.system_notice_list = System notices notices.view_detail_header = Notice details notices.operations = Operations @@ -3337,7 +3439,7 @@ notices.desc = Description notices.op = Op. notices.delete_success = The system notices have been deleted. -self_check.no_problem_found = No problems found yet. +self_check.no_problem_found = No problem found yet. self_check.database_collation_mismatch = Expect database to use collation: %s self_check.database_collation_case_insensitive = Database is using a collation %s, which is an insensitive collation. Although Forgejo could work with it, there might be some rare cases which don't work as expected. self_check.database_inconsistent_collation_columns = Database is using collation %s, but these columns are using mismatched collations. It might cause some unexpected problems. @@ -3397,26 +3499,232 @@ raw_seconds = seconds raw_minutes = minutes [munits.data] +b = B +kib = KiB +mib = MiB +gib = GiB +tib = TiB +pib = PiB +eib = EiB [dropzone] +default_message = Drop files or click here to upload. +invalid_input_type = You cannot upload files of this type. +file_too_big = File size ({{filesize}} MB) exceeds the maximum size of ({{maxFilesize}} MB). +remove_file = Remove file [notification] +notifications = Notifications +unread = Unread +read = Read +no_unread = No unread notifications. +no_read = No read notifications. +pin = Pin notification +mark_as_read = Mark as read +mark_as_unread = Mark as unread +mark_all_as_read = Mark all as read +subscriptions = Subscriptions +watching = Watching +no_subscriptions = No subscriptions [gpg] +default_key=Signed with default key +error.extract_sign = Failed to extract signature +error.generate_hash = Failed to generate hash of commit +error.no_committer_account = No account linked to committer's email address +error.no_gpg_keys_found = No known key found for this signature in database +error.not_signed_commit = Not a signed commit +error.failed_retrieval_gpg_keys = Failed to retrieve any key attached to the committer's account +error.probable_bad_signature = WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS. +error.probable_bad_default_signature = WARNING! Although the default key has this ID it does not verify this commit! This commit is SUSPICIOUS. [units] unit = Unit error.no_unit_allowed_repo = You are not allowed to access any section of this repository. [packages] +title = Packages +empty = There are no packages yet. +empty.documentation = For more information on the package registry, see the documentation. +empty.repo = Did you upload a package, but it's not shown here? Go to package settings and link it to this repo. +registry.documentation = For more information on the %s registry, see the documentation. +filter.type = Type +filter.type.all = All +filter.no_result = Your filter produced no results. +filter.container.tagged = Tagged +filter.container.untagged = Untagged +published_by = Published %[1]s by %[3]s +published_by_in = Published %[1]s by %[3]s in %[5]s +installation = Installation +about = About this package +requirements = Requirements +dependencies = Dependencies +keywords = Keywords +details = Details +details.author = Author +details.project_site = Project website +details.repository_site = Repository website +details.documentation_site = Documentation website +details.license = License +assets = Assets +versions = Versions +versions.view_all = View all +dependency.id = ID +dependency.version = Version +search_in_external_registry = Search in %s +alpine.registry = Setup this registry by adding the url in your /etc/apk/repositories file: +alpine.registry.key = Download the registry public RSA key into the /etc/apk/keys/ folder to verify the index signature: +alpine.registry.info = Choose $branch and $repository from the list below. +alpine.install = To install the package, run the following command: +alpine.repository = Repository info +alpine.repository.branches = Branches +alpine.repository.repositories = Repositories +alpine.repository.architectures = Architectures +arch.pacman.helper.gpg = Add trust certificate for pacman: +arch.pacman.repo.multi = %s has the same version in different distributions. +arch.pacman.repo.multi.item = Configuration for %s +arch.pacman.conf = Add server with related distribution and architecture to /etc/pacman.conf : +arch.pacman.sync = Sync package with pacman: +arch.version.properties = Version properties +arch.version.description = Description +arch.version.provides = Provides +arch.version.groups = Group +arch.version.depends = Depends +arch.version.optdepends = Optional depends +arch.version.makedepends = Make depends +arch.version.checkdepends = Check depends +arch.version.conflicts = Conflicts +arch.version.replaces = Replaces +arch.version.backup = Backup +cargo.registry = Setup this registry in the Cargo configuration file (for example ~/.cargo/config.toml): +cargo.install = To install the package using Cargo, run the following command: +chef.registry = Setup this registry in your ~/.chef/config.rb file: +chef.install = To install the package, run the following command: +composer.registry = Setup this registry in your ~/.composer/config.json file: +composer.install = To install the package using Composer, run the following command: +composer.dependencies = Dependencies +composer.dependencies.development = Development dependencies +conan.registry = Setup this registry from the command line: +conan.install = To install the package using Conan, run the following command: +conda.registry = Setup this registry as a Conda repository in your .condarc file: +conda.install = To install the package using Conda, run the following command: +container.images.title = Images +container.details.type = Image type +container.details.platform = Platform +container.pull = Pull the image from the command line: +container.digest = Digest +container.multi_arch = OS / Arch +container.layers = Image layers +container.labels = Labels +container.labels.key = Key +container.labels.value = Value +cran.registry = Setup this registry in your Rprofile.site file: +cran.install = To install the package, run the following command: +debian.registry = Setup this registry from the command line: +debian.registry.info = Choose $distribution and $component from the list below. +debian.install = To install the package, run the following command: +debian.repository = Repository info +debian.repository.distributions = Distributions +debian.repository.components = Components +debian.repository.architectures = Architectures +generic.download = Download package from the command line: +go.install = Install the package from the command line: +helm.registry = Setup this registry from the command line: +helm.install = To install the package, run the following command: +maven.registry = Setup this registry in your project pom.xml file: +maven.install = To use the package include the following in the dependencies block in the pom.xml file: +maven.install2 = Run via command line: +maven.download = To download the dependency, run via command line: +nuget.registry = Setup this registry from the command line: +nuget.install = To install the package using NuGet, run the following command: +nuget.dependency.framework = Target Framework +npm.registry = Setup this registry in your project .npmrc file: +npm.install = To install the package using npm, run the following command: +npm.install2 = or add it to the package.json file: +npm.dependencies = Dependencies +npm.dependencies.development = Development dependencies +npm.dependencies.bundle = Bundled dependencies +npm.dependencies.peer = Peer dependencies +npm.dependencies.optional = Optional dependencies +npm.details.tag = Tag +pub.install = To install the package using Dart, run the following command: +pypi.requires = Requires Python +pypi.install = To install the package using pip, run the following command: +rpm.registry = Setup this registry from the command line: +rpm.distros.redhat = on RedHat based distributions +rpm.distros.suse = on SUSE based distributions +rpm.install = To install the package, run the following command: +rpm.repository = Repository info +rpm.repository.architectures = Architectures +rpm.repository.multiple_groups = This package is available in multiple groups. +alt.registry = Setup this registry from the command line: +alt.registry.install = To install the package, run the following command: +alt.install = Install package +alt.setup = Add a repository to the list of connected repositories (choose the necessary architecture instead of "_arch_"): +alt.repository = Repository info +alt.repository.architectures = Architectures +alt.repository.multiple_groups = This package is available in multiple groups. +rubygems.install = To install the package using gem, run the following command: +rubygems.install2 = or add it to the Gemfile: +rubygems.dependencies.runtime = Runtime dependencies +rubygems.dependencies.development = Development dependencies +rubygems.required.ruby = Requires Ruby version +rubygems.required.rubygems = Requires RubyGem version +swift.registry = Setup this registry from the command line: +swift.install = Add the package in your Package.swift file: +swift.install2 = and run the following command: +vagrant.install = To add a Vagrant box, run the following command: +settings.link = Link this package to a repository +settings.link.description = If you link a package with a repository, the package is listed in the repository's package list. +settings.link.select = Select repository +settings.link.button = Update repository link +settings.link.success = Repository link was successfully updated. +settings.link.error = Failed to update repository link. +settings.delete = Delete package +settings.delete.description = Deleting a package is permanent and cannot be undone. +settings.delete.notice = You are about to delete %s (%s). This operation is irreversible, are you sure? +settings.delete.success = The package has been deleted. +settings.delete.error = Failed to delete the package. +owner.settings.cargo.title = Cargo registry index +owner.settings.cargo.initialize = Initialize index +owner.settings.cargo.initialize.description = A special index Git repository is needed to use the Cargo registry. Using this option will (re-)create the repository and configure it automatically. +owner.settings.cargo.initialize.error = Failed to initialize Cargo index: %v +owner.settings.cargo.initialize.success = The Cargo index was successfully created. +owner.settings.cargo.rebuild = Rebuild index +owner.settings.cargo.rebuild.description = Rebuilding can be useful if the index is not synchronized with the stored Cargo packages. +owner.settings.cargo.rebuild.error = Failed to rebuild Cargo index: %v +owner.settings.cargo.rebuild.success = The Cargo index was successfully rebuilt. +owner.settings.cargo.rebuild.no_index = Cannot rebuild, no index is initialized. +owner.settings.cleanuprules.title = Cleanup rules +owner.settings.cleanuprules.add = Add cleanup rule +owner.settings.cleanuprules.edit = Edit cleanup rule +owner.settings.cleanuprules.none = There are no cleanup rules yet. +owner.settings.cleanuprules.preview = Cleanup rule preview +owner.settings.cleanuprules.preview.overview = %d packages are scheduled to be removed. +owner.settings.cleanuprules.preview.none = Cleanup rule does not match any packages. +owner.settings.cleanuprules.pattern_full_match = Apply pattern to full package name +owner.settings.cleanuprules.keep.title = Versions that match these rules are kept, even if they match a removal rule below. +owner.settings.cleanuprules.keep.count = Keep the most recent owner.settings.cleanuprules.keep.count.1 = 1 version per package owner.settings.cleanuprules.keep.count.n = %d versions per package +owner.settings.cleanuprules.keep.pattern = Keep versions matching +owner.settings.cleanuprules.keep.pattern.container = The latest version is always kept for Container packages. +owner.settings.cleanuprules.remove.title = Versions that match these rules are removed, unless a rule above says to keep them. +owner.settings.cleanuprules.remove.days = Remove versions older than +owner.settings.cleanuprules.remove.pattern = Remove versions matching +owner.settings.cleanuprules.success.update = Cleanup rule has been updated. +owner.settings.cleanuprules.success.delete = Cleanup rule has been deleted. +owner.settings.chef.title = Chef registry +owner.settings.chef.keypair = Generate key pair +owner.settings.chef.keypair.description = Requests sent to the Chef registry must be cryptographically signed as a means of authentication. When generating a keypair, only the public key is stored on Forgejo. The private key is provided to you to be used with knife. Generating a new keypair will overwrite the previous one. [secrets] secrets = Secrets description = Secrets will be passed to certain actions and cannot be read otherwise. none = There are no secrets yet. creation = Add secret +creation.name_placeholder = case-insensitive, alphanumeric characters or underscores only, cannot start with GITEA_ or GITHUB_ +creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted. creation.success = The secret "%s" has been added. creation.failed = Failed to add secret. deletion = Remove secret @@ -3426,6 +3734,69 @@ deletion.failed = Failed to remove secret. management = Manage secrets [actions] +actions = Actions + +status.unknown = Unknown +status.waiting = Waiting +status.running = Running +status.success = Success +status.failure = Failure +status.cancelled = Canceled +status.skipped = Skipped +status.blocked = Blocked + +runners = Runners +runners.runner_manage_panel = Manage runners +runners.new = Create new runner +runners.new_notice = How to start a runner +runners.status = Status +runners.id = ID +runners.name = Name +runners.owner_type = Type +runners.description = Description +runners.labels = Labels +runners.last_online = Last online time +runners.runner_title = Runner +runners.task_list = Recent tasks on this runner +runners.task_list.no_tasks = There is no task yet. +runners.task_list.run = Run +runners.task_list.status = Status +runners.task_list.repository = Repository +runners.task_list.commit = Commit +runners.task_list.done_at = Done at +runners.edit_runner = Edit Runner +runners.update_runner = Update changes +runners.update_runner_success = Runner updated successfully +runners.update_runner_failed = Failed to update runner +runners.delete_runner = Delete this runner +runners.delete_runner_success = Runner deleted successfully +runners.delete_runner_failed = Failed to delete runner +runners.delete_runner_header = Confirm to delete this runner +runners.delete_runner_notice = If a task is running on this runner, it will be terminated and marked as failed. It may break building workflow. +runners.none = No runners available +runners.status.unspecified = Unknown +runners.status.idle = Idle +runners.status.active = Active +runners.status.offline = Offline +runners.version = Version +runners.reset_registration_token = Reset registration token +runners.reset_registration_token_success = Runner registration token reset successfully + +runs.all_workflows = All workflows +runs.commit = Commit +runs.scheduled = Scheduled +runs.pushed_by = pushed by +runs.workflow = Workflow +runs.invalid_workflow_helper = Workflow config file is invalid. Please check your config file: %s +runs.no_matching_online_runner_helper = No matching online runner with label: %s +runs.no_job_without_needs = The workflow must contain at least one job without dependencies. +runs.no_job = The workflow must contain at least one job +runs.actor = Actor +runs.status = Status +runs.actors_no_select = All actors +runs.status_no_select = All status +runs.no_results = No results matched. +runs.no_workflows = There are no workflows yet. runs.no_workflows.help_write_access = Don't know how to start with Forgejo Actions? Check out the quick start in the user documentation to write your first workflow, then set up a Forgejo runner to execute your jobs. runs.no_workflows.help_no_write_access = To learn about Forgejo Actions, see the documentation. runs.no_runs = The workflow has no runs yet. @@ -3447,6 +3818,22 @@ workflow.dispatch.warn_input_limit = Only displaying the first %d inputs. need_approval_desc = Need approval to run workflows for fork pull request. +variables = Variables +variables.management = Manage variables +variables.creation = Add variable +variables.none = There are no variables yet. +variables.deletion = Remove variable +variables.deletion.description = Removing a variable is permanent and cannot be undone. Continue? +variables.description = Variables will be passed to certain actions and cannot be read otherwise. +variables.edit = Edit Variable +variables.not_found = Failed to find the variable. +variables.deletion.failed = Failed to remove variable. +variables.deletion.success = The variable has been removed. +variables.creation.failed = Failed to add variable. +variables.creation.success = The variable "%s" has been added. +variables.update.failed = Failed to edit variable. +variables.update.success = The variable has been edited. + [projects] deleted.display_name = Deleted project type-1.display_name = Individual project @@ -3462,6 +3849,9 @@ symbolic_link = Symbolic link submodule = Submodule [markup] +filepreview.line = Line %[1]d in %[2]s +filepreview.lines = Lines %[1]d to %[2]d in %[3]s +filepreview.truncated = Preview has been truncated [translation_meta] test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :) diff --git a/options/locale/locale_eo.ini b/options/locale/locale_eo.ini index be27ad3749..16cb2d2de4 100644 --- a/options/locale/locale_eo.ini +++ b/options/locale/locale_eo.ini @@ -2,6 +2,8 @@ tracked_time_summary = Resumo de trakita tempo bazita sur filtriloj de temolisto language = Lingvo passcode = Paskodo +webauthn_error_timeout = Tempo elĉerpiĝis antaŭ legiĝo de via ŝlosilo. Bonvolu reenlegi la retpaĝon kaj reprovi. +webauthn_sign_in = Premu la butonon sur via sekurŝlosilo. Se mankas butono, elprenu kaj reenmetu vian sekurŝlosilon. captcha = Testo de homeco create_new = Krei… licenses = Permesiloj @@ -9,10 +11,13 @@ sign_in = Saluti user_profile_and_more = Profilo kaj agordoj… explore = Esplori return_to_forgejo = Reiri al Forgejo +webauthn_error_unknown = Eraris pro nekonata kialo. Bonvolu reprovi. twofa = Duobla aŭtentikigo version = Versio help = Helpo +webauthn_error_empty = Vi devas agordi nomon por la ŝlosilo. sign_in_or = aŭ +webauthn_use_twofa = Uzi dumanieran salutkodon de via poŝtelefono page = Paĝo link_account = Ligi konton your_profile = Profilo @@ -21,16 +26,23 @@ settings = Agordoj logo = Emblemo toc = Enhavotabelo admin_panel = Retejadministrado +webauthn_unsupported_browser = Via retfoliumilo ne jam subtenas la salutmanieron WebAuthn. +webauthn_error_insecure = La salutmaniero WebAuthn sole subtenas sekurajn konektiĝojn. Testante HTTP’e, oni uzu la retadreson «localhost» aŭ «127.0.0.1» new_project = Novan projekton notifications = Sciigoj repository = Deponejo +webauthn_error = Ne povis legi vian sekurŝlosilon. active_stopwatch = Aktiva tempotrakilo organization = Organizaĵo sign_in_with_provider = Saluti per %s +webauthn_error_unable_to_process = La servilo ne povis trakti vian peton. register = Registriĝi username = Uzantonomo +webauthn_insert_key = Enmetu vian sekurŝlosilon password = Pasvorto +webauthn_error_duplicated = La sekurŝlosilo ne rajtas fari tiun ĉi peton. Bonvolu certigi ke la ŝlosilo estas ne jam registrita. template = Ŝablono +webauthn_press_button = Bonvolu premi la butonon sur via sekurŝlosilo… signed_in_as = Salutinta kiel sign_up = Registriĝi enable_javascript = Ĉi tiu retejo bezonas JavaSkripton. @@ -486,6 +498,7 @@ issue.action.review_dismissed = @%[1]s maldungis la lastan revizion de %[ repo.transfer.subject_to_you = %s volas reposedigi la deponejon "%s" al vi totp_enrolled.subject = Vi aktivigis TOTP-n kiel 2FA metodo issue_assigned.issue = @%[1]s asignis al vi ĉi tiun eraron %[2]s en la deponejo %[3]s. + repo.transfer.subject_to = %s volas transigi deponejon "%s" al %s [form] @@ -564,7 +577,9 @@ Location = Kieo To = Branĉonomo AccessToken = Atingoĵetono required_prefix = La enigaĵo devas komenciĝi per "%s" + username_error = ` enhavu sole literojn («a–z», «A–Z»), numerojn («0–9«), strekojn («-»), substrekojn («_») kaj punktojn («.»). Gi ne povas komenci kun ne-alfanumeraj signoj, kaj sinsekva ne-alfanumeraj signoj ankaŭ estas malpermesitaj.` + PayloadUrl = Utilaĵ-URL CommitChoice = Enmeto elekton @@ -867,6 +882,8 @@ form.name_pattern_not_allowed = La ŝablono "%s" ne estas permesata en uzantnomo editor.add_file = Aldoni dosieron settings.tags = Etikedoj archive.title_date = Ĉi tiu deponejo arĥiviĝis je %s. Vi povas vidi kaj elŝuti dosierojn, sed ne povas puŝi nek raporti problemojn nek tirpeti. +migrate_items_pullrequests = Tirpetoj +migrate.migrating_pulls = Migrante tirpetojn unwatch = Malspuri watch = Spuri star = Stelumi @@ -905,12 +922,15 @@ settings.default_branch_desc = Elekti implicutan deponejan branĉon por tirpetoj archive.title = Ĉi tiu deponejo estas arĥivigita. Vi povas vidi kaj elŝuti dosierojn, sed ne povas puŝi nek raporti problemojn nek tirpeti. activity.active_prs_count_n = %d aktivaj tirpetoj settings.archive.text = Arĥivigi la deponejon igus ĝin sole legebla. Ĝi kaŝiĝos de la labortablo. Neniu (ne eĉ vi!) povos fari enmetojn, raportojn, kaj tirpetojn. +migrate_items_releases = Eldonoj commits.commits = Enmetoj rss.must_be_on_branch = Vi devas esti en branĉo por havi RSS-fluon. + repo_name = Deponejonomo size_format = %[1]s: %[2]s; %[3]s: %[4]s template = Ŝablono template_select = Elektu ŝablonon + editor.name_your_file = Nomu vian dosieron… editor.or = aŭ editor.add_tmpl.filename = dosiernomo @@ -921,6 +941,8 @@ settings = Agordoj teams.settings = Agordoj [packages] +npm.details.tag = Etikedo + [search] search = Serĉi… @@ -950,8 +972,14 @@ runner_kind = Serĉi rulantojn… pull_kind = Serĉi tirpetojn… [markup] +filepreview.truncated = La superrigardo estas mallongigita +filepreview.line = Linio %[1]d en %[2]s +filepreview.lines = Linioj %[1]d ĝis %[2]d en %[3]s [actions] +variables.creation.success = La variablo "%s" estas aldonita. +variables.update.failed = Ne eblas redakti la variablon. +variables.update.success = La variablo estas redaktita. [projects] deleted.display_name = Forigita projekto diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index b0a9f6ce09..bb77f40d99 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -36,6 +36,18 @@ twofa=Autenticación de doble factor twofa_scratch=Código de respaldo passcode=Código de acceso +webauthn_insert_key=Introduzca su clave de seguridad +webauthn_sign_in=Presione el botón en su clave de seguridad. Si su clave de seguridad no tiene ningún botón, vuelva a insertarla. +webauthn_press_button=Por favor, presione el botón en su clave de seguridad… +webauthn_use_twofa=Utilice un código de doble factor desde su teléfono móvil +webauthn_error=No se pudo leer su llave de seguridad. +webauthn_unsupported_browser=Actualmente su navegador no soporta WebAuthn. +webauthn_error_unknown=Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo. +webauthn_error_insecure=WebAuthn sólo soporta conexiones seguras. Para probar sobre HTTP, puede utilizar el origen "localhost" o "127.0.0.1" +webauthn_error_unable_to_process=El servidor no pudo procesar su solicitud. +webauthn_error_duplicated=La clave de seguridad no está permitida para esta solicitud. Por favor, asegúrese de que la clave no está ya registrada. +webauthn_error_empty=Debe establecer un nombre para esta clave. +webauthn_error_timeout=Tiempo de espera máximo alcanzado antes de que su clave pudiese ser leída. Por favor, cargue la página y vuelva a intentarlo. repository=Repositorio organization=Organización mirror=Réplica @@ -170,7 +182,7 @@ buttons.bold.tooltip=Añadir texto en negrita (Ctrl+B / ⌘B) buttons.italic.tooltip=Añadir texto en cursiva (Ctrl+I / ⌘I) buttons.quote.tooltip=Citar texto buttons.code.tooltip=Añadir código -buttons.link.tooltip=Añadir un enlace (Ctrl+K / ⌘K) +buttons.link.tooltip=Añadir un enlace buttons.list.unordered.tooltip=Añadir una lista buttons.list.ordered.tooltip=Añadir una lista numerada buttons.list.task.tooltip=Añadir una lista de tareas @@ -1010,6 +1022,7 @@ regenerate_token_success = El token se ha regenerado. Las aplicaciones que lo ut quota.applies_to_user = Las siguientes reglas de cuota se aplican a tu cuenta quota.applies_to_org = Las siguientes reglas de cuota se aplican a esta organización quota.rule.exceeded.helper = El tamaño total de los objetos para esta regla ha superado la cuota. + ssh_token_help_ssh_agent = o, si está usando un agente SSH (con la variable de entorno SSH_AUTH_SOCK): [repo] @@ -1141,6 +1154,14 @@ migrate_options_lfs_endpoint.label=Destino LFS migrate_options_lfs_endpoint.description=Migración intentará usar su mando Git para determinar el servidor LFS. También puede especificar un punto final personalizado si los datos LFS del repositorio se almacenan en otro lugar. migrate_options_lfs_endpoint.description.local=También se admite una ruta del servidor local. migrate_options_lfs_endpoint.placeholder=Si se deja en blanco, el punto final se derivará de la URL de clonación +migrate_items=Objetos de migración +migrate_items_wiki=Wiki +migrate_items_milestones=Hitos +migrate_items_labels=Etiquetas +migrate_items_issues=Incidencias +migrate_items_pullrequests=Solicitudes de extracción +migrate_items_merge_requests=Solicitudes de fusión +migrate_items_releases=Lanzamientos migrate_repo=Migrar repositorio migrate.clone_address=Migrar / Clonar desde URL migrate.clone_address_desc=La URL HTTP(S) o de Git "clone" de un repositorio existente @@ -1159,6 +1180,16 @@ migrate.migrating=Migrando desde %s… migrate.migrating_failed=La migración desde %s ha fallado. migrate.migrating_failed.error=Error al migrar: %s migrate.migrating_failed_no_addr=Migración fallida. +migrate.migrating_git=Migrando datos de Git +migrate.migrating_topics=Migrando temas +migrate.migrating_milestones=Migrando hitos +migrate.migrating_labels=Migrando etiquetas +migrate.migrating_releases=Migrar versiones +migrate.migrating_issues=Migrando incidencias +migrate.migrating_pulls=Migrando solicitudes de incorporación de cambios +migrate.cancel_migrating_title=Cancelar la migración +migrate.cancel_migrating_confirm=¿Quiere cancelar esta migración? + mirror_from=réplica de forked_from=bifurcado de generated_from=generado desde @@ -1534,7 +1565,7 @@ issues.ref_reopening_from=`hizo referencia a esta incidencia des issues.ref_from=`de %[1]s` issues.author=Autor issues.role.owner=Propietario -issues.role.owner_helper=Este usuario es propietario de este repositorio. +issues.role.owner_helper=Este usuario es el dueño de este repositorio. issues.role.member=Miembro issues.role.member_helper=Este usuario es miembro de la organización propietaria de este repositorio. issues.role.collaborator=Colaborador @@ -2353,7 +2384,7 @@ settings.matrix.room_id=ID de sala settings.matrix.message_type=Tipo de mensaje settings.archive.button=Archivar repositorio settings.archive.header=Archivar este repositorio -settings.archive.text=Archivar el repositorio lo hará de sólo lectura. Se ocultará del tablero. Nadie (¡ni siquiera tú!) será capaz de hacer nuevas confirmaciones, o abrir nuevas incidencias o solicitudes de incorporación de cambios. Se recomienda documentar el motivo del archivado para orientar a futuros desarrolladores que planeen bifurcar el repositorio. +settings.archive.text=Archivar el repositorio lo hará de sólo lectura. Se ocultará del tablero. Nadie (¡ni siquiera tú!) será capaz de hacer nuevas confirmaciones, o abrir nuevas incidencias o solicitudes de incorporación de cambios. settings.archive.success=El repositorio ha sido archivado exitosamente. settings.archive.error=Ha ocurrido un error al intentar archivar el repositorio. Vea el registro para más detalles. settings.archive.error_ismirror=No puede archivar un repositorio replicado. @@ -2553,16 +2584,24 @@ error.csv.invalid_field_count=No se puede procesar este archivo porque tiene un rss.must_be_on_branch = Debes estar en una rama para tener un feed RSS. desc.sha256 = SHA256 issues.num_participants_one = %d participante +n_commit_few = %s commits +n_branch_few = %s ramas +n_tag_one = %s etiqueta +n_tag_few = %s etiquetas file_follow = Seguir enlace simbólico vendored = Vendored open_with_editor = Abrir con %s +n_branch_one = %s rama issues.archived_label_description = (Archivado) %s +n_commit_one = %s commit generated = Generado pulls.nothing_to_compare_have_tag = Las ramas/etiquetas seleccionadas son iguales. commits.search_branch = Esta rama commits.renamed_from = Renombrado de %s form.string_too_long = El texto introducido tiene más de %d caracteres. object_format = Formato de objetos +n_release_one = %s lanzamiento +n_release_few = %s versiones stars = Estrellas editor.invalid_commit_mail = Correo no válido para crear un commit. project = Proyectos @@ -2702,6 +2741,7 @@ settings.pull_mirror_sync_quota_exceeded = Cuota excedida, no se empujan los cam archive.nocomment = No es posible hacer comentarios porque el repositorio está archivado. sync_fork.branch_behind_one = Esta rama esta %[1]d cambios detrás de %[2]s sync_fork.branch_behind_few = Esta rama está %[1]d confirmaciones detrás de %[2]s + migrate.repo_desc_helper = Deje vacío para importar la descripción existente commits.view_single_diff = Ver los cambios introducidos por la confirmación para este archivo issues.filter_type.all_pull_requests = Todas las solicitudes de incorporación de cambios @@ -2715,9 +2755,11 @@ settings.default_update_style_desc = El modo por defecto utilizado para actualiz settings.wiki_branch_rename_success = La wiki de la rama del repositorio ha sido normalizada correctamente. settings.wiki_branch_rename_failure = Hubo un error al normalizar el nombre de la rama de la wiki del repositorio. release.hide_archive_links_helper = Ocultar el archivo de código generado automáticamente para este lanzamiento. Por ejemplo, si estás subiendo el tuyo propio. + settings.event_action_recover = Recuperar settings.event_action_success = Éxito settings.event_action_success_desc = La ejecución de la acción se ejecutó con éxito. + settings.event_header_action = Eventos de la ejecución de la acción settings.event_action_failure = Fallo settings.event_action_failure_desc = La ejecución de la acción terminó con fallos. @@ -2730,6 +2772,7 @@ settings.enforce_on_admins = Aplicar esta regla para los administradores del rep settings.matrix.access_token_helper = Se recomienda configurar una cuenta de Matrix dedicada para esto. El token de acceso puede ser obtenido desde el cliente web Element (en una pestaña privada/incógnito) > Menú de usuario (arriba izquierda) > Todos los ajustes > Ayuda & Acerca de > Avanzado > Token de acceso (a la derecha, debajo de URL del servidor). Cierra la pestaña privada/incógnito (desconectarse invalidaría el token). settings.archive.mirrors_unavailable = Los espejos no están disponibles en repos archivados. release.summary_card_alt = Tarjeta de resumen de una release titulada "%s" en el repositorio %s + settings.matrix.room_id_helper = El ID de Sala puede ser obtenido desde el cliente web Element > Ajustes de Sala > Avanzado > ID de sala interna. Ejemplo: %s. [graphs] @@ -2923,6 +2966,34 @@ dashboard.sync_external_users=Sincronizar datos de usuario externo dashboard.cleanup_hook_task_table=Limpiar la tabla hook_task dashboard.cleanup_packages=Limpiar paquetes caducados dashboard.cleanup_actions=Acciones de limpieza de registros expirados y artefactos +dashboard.server_uptime=Tiempo de actividad del servidor +dashboard.current_goroutine=Gorutinas actuales +dashboard.current_memory_usage=Uso actual de memoria +dashboard.total_memory_allocated=Total de Memoria Reservada +dashboard.memory_obtained=Memoria obtenida +dashboard.pointer_lookup_times=Tiempos de búsqueda de punteros +dashboard.memory_allocate_times=Asignaciones de memoria +dashboard.memory_free_times=Liberaciones de memoria +dashboard.current_heap_usage=Uso actual de Heap +dashboard.heap_memory_obtained=Memoria de Heap obtenida +dashboard.heap_memory_idle=Memoria de Heap inactiva +dashboard.heap_memory_in_use=Memoria de Heap en uso +dashboard.heap_memory_released=Memoria de Heap liberada +dashboard.heap_objects=Objetos en el Heap +dashboard.bootstrap_stack_usage=Uso de la pila de Bootstrap +dashboard.stack_memory_obtained=Memoria de pila obtenida +dashboard.mspan_structures_usage=Uso de estructuras MSpan +dashboard.mspan_structures_obtained=Estructuras MSpan obtenidas +dashboard.mcache_structures_usage=Uso de estructuras MCache +dashboard.mcache_structures_obtained=Estructuras MCache obtenidas +dashboard.profiling_bucket_hash_table_obtained=Profiling bucket hash table obtenido +dashboard.gc_metadata_obtained=Metadatos obtenidos del recolector de basura +dashboard.other_system_allocation_obtained=Otros recursos asignados del sistema +dashboard.next_gc_recycle=Siguiente reciclaje del recolector de basura +dashboard.last_gc_time=Tiempo desde la última recolección de basura +dashboard.total_gc_pause=Pausa total por el recolector de basura +dashboard.last_gc_pause=Última pausa por el recolector de basura +dashboard.gc_times=Ejecuciones del recolector de basura dashboard.delete_old_actions=Eliminar todas las actividades antiguas de la base de datos dashboard.delete_old_actions.started=Eliminar todas las actividades antiguas de la base de datos iniciadas. dashboard.update_checker=Buscador de actualizaciones @@ -2979,6 +3050,18 @@ users.purge_help=Borrar forzosamente el usuario y cualquier repositorio, organiz users.still_own_packages=Este usuario todavía posee uno o más paquetes, elimina estos paquetes primero. users.deletion_success=La cuenta de usuario ha sido eliminada. users.reset_2fa=Reiniciar 2FA +users.list_status_filter.menu_text=Filtro +users.list_status_filter.reset=Reiniciar +users.list_status_filter.is_active=Activo +users.list_status_filter.not_active=Inactivo +users.list_status_filter.is_admin=Administrador +users.list_status_filter.not_admin=No admin +users.list_status_filter.is_restricted=Restringido +users.list_status_filter.not_restricted=No restringido +users.list_status_filter.is_prohibit_login=Prohibido el inicio de sesión +users.list_status_filter.not_prohibit_login=Permitir el inicio de sesión +users.list_status_filter.is_2fa_enabled=2FA habilitado +users.list_status_filter.not_2fa_enabled=2FA deshabilitado users.details=Detalles del usuario emails.email_manage_panel=Administrar direcciones de correo electrónico del usuario @@ -3296,6 +3379,26 @@ monitor.process.cancel_desc=Cancelar un proceso puede ocasionar una pérdida de monitor.process.cancel_notices=Cancelar: %s? monitor.process.children=Hijos +monitor.queues=Colas +monitor.queue=Cola: %s +monitor.queue.name=Nombre +monitor.queue.type=Tipo +monitor.queue.exemplar=Ejemplo +monitor.queue.numberworkers=Número de trabajadores +monitor.queue.activeworkers=Trabajadores activos +monitor.queue.maxnumberworkers=Nº máx de trabajadores +monitor.queue.numberinqueue=Número en cola +monitor.queue.review_add=Revisar / Añadir trabajadores +monitor.queue.settings.title=Ajustes del grupo +monitor.queue.settings.desc=Los grupos de trabajadores crecen dinámicamente en respuesta al bloqueo de cola de sus trabajadores. +monitor.queue.settings.maxnumberworkers=Número máximo de trabajadores +monitor.queue.settings.maxnumberworkers.placeholder=Actualmente %[1]d +monitor.queue.settings.maxnumberworkers.error=El número máximo de trabajadores debe ser un número +monitor.queue.settings.submit=Actualizar ajustes +monitor.queue.settings.changed=Ajustes actualizados +monitor.queue.settings.remove_all_items=Eliminar todo +monitor.queue.settings.remove_all_items_done=Todos los elementos en la cola han sido eliminados. + notices.system_notice_list=Notificaciones del sistema notices.view_detail_header=Detalles de notificación notices.operations=Operaciones @@ -3327,6 +3430,8 @@ monitor.duration = Duración (es) self_check = Autocomprobación config.app_slogan = Eslogan de la instancia dashboard.sync_tag.started = Sincronización de etiquetas iniciada + + dashboard.sync_repo_tags = Sincronizar etiquetas de Git con la base de datos users.block.description = Bloquear a este usuario para que no pueda interactuar a través de su cuenta e impedirle iniciar sesión. users.admin.description = Otorgar a este usuario acceso completo a todas las funciones administrativas disponibles a través de la interfaz de usuario web y en la API. @@ -3341,8 +3446,8 @@ self_check.database_collation_mismatch = Se espera que la base de datos use inte self_check.database_collation_case_insensitive = La base de datos está usando intercalación %s, que es una intercalación insensible. Aunque Forgejo podría funcionar, puede darse algún caso extraño en el que no funcione como se espera. self_check.database_inconsistent_collation_columns = La base de datos está usando intercalación %s, pero estas columnas están usando intercalaciones desparejadas. Esto puede causar algunos problemas inesperados. self_check.database_fix_mysql = Para usuarios de MySQL/MariaDB, podrían usar el comando "forgejo doctor convert" para arreglar los problemas de intercalación, o también podrían arreglarlos utilizando consultas SQL manuales, como "ALTER ... COLLATE ...". -config.cache_test_failed = Incorrecto al probar la caché: %v. +config.cache_test_failed = Incorrecto al probar la caché: %v. [action] create_repo=creó el repositorio %s @@ -3398,10 +3503,35 @@ raw_seconds=segundos raw_minutes=minutos [dropzone] +default_message=Suelte archivos o haga clic aquí para subir. +invalid_input_type=No puede subir archivos de este tipo. +file_too_big=El tamaño del archivo ({{filesize}} MB) excede el tamaño máximo de ({{maxFilesize}} MB). +remove_file=Eliminar archivo [notification] +notifications=Notificaciones +unread=Sin leer +read=Leídas +no_unread=No tiene notificaciones sin leer. +no_read=No hay notificaciones. +pin=Fijar notificación +mark_as_read=Marcar como leído +mark_as_unread=Marcar como no leído +mark_all_as_read=Marcar todo como leído +subscriptions=Suscripciones +watching=Siguiendo +no_subscriptions=Sin suscripciones [gpg] +default_key=Firmado con clave predeterminada +error.extract_sign=Error al extraer la firma +error.generate_hash=Error al generar hash of commit +error.no_committer_account=Ninguna cuenta vinculada a la dirección de correo electrónico del committer +error.no_gpg_keys_found=No se encontró ninguna clave conocida en la base de datos para esta firma +error.not_signed_commit=No es un commit firmado +error.failed_retrieval_gpg_keys=No se pudo recuperar cualquier clave adjunta a la cuenta del committer +error.probable_bad_signature=¡ADVERTENCIA! ¡Hay una clave con este ID en la base de datos, pero esa clave no verifica este commit! Este commit es SOSPECHOSO. +error.probable_bad_default_signature=¡ADVERTENCIA! ¡La clave por defecto tiene este ID pero esa clave no verifica este commit! Este commit es SOSPECHOSO. [units] unit=Unidad @@ -3409,11 +3539,181 @@ error.no_unit_allowed_repo=No tiene permisos para acceder a ninguna sección de error.unit_not_allowed=No tiene permisos para acceder a esta sección del repositorio. [packages] +title=Paquetes desc=Administrar paquetes del repositorio. +empty=Todavía no hay paquetes. +empty.documentation=Para más información sobre el registro de paquetes, consulte la documentación. +empty.repo=¿Has subido un paquete, pero no se muestra aquí? Ve a la configuración del paquete y añade el link a este repositorio. +registry.documentation=Para obtener más información sobre el registro %s, consulte la documentación. +filter.type=Tipo +filter.type.all=Todo +filter.no_result=El filtro no produjo ningún resultado. +filter.container.tagged=Etiquetado +filter.container.untagged=Etiqueta eliminada +published_by=Publicado %[1]s por %[3]s +published_by_in=Publicado %[1]s por %[3]s en %[5]s +installation=Instalación +about=Acerca de este paquete +requirements=Requisitos +dependencies=Dependencias +keywords=Palabras clave +details=Detalles +details.author=Autor +details.project_site=Sitio del proyecto +details.repository_site=Sitio del repositorio +details.documentation_site=Sitio de documentación +details.license=Licencia +assets=Activos +versions=Versiones +versions.view_all=Ver todo +dependency.id=Id. +dependency.version=Versión +alpine.registry=Configura este registro agregando la url en tu archivo ./apk/repositories: +alpine.registry.key=Descargue la clave RSA pública del registro en la carpeta ./apk/keys/ para verificar la firma del índice: +alpine.registry.info=Seleccione $branch y $repository de la siguiente lista. +alpine.install=Para instalar el paquete, ejecute el siguiente comando: +alpine.repository=Información del repositorio +alpine.repository.branches=Ramas +alpine.repository.repositories=Repositorios +alpine.repository.architectures=Arquitecturas +cargo.registry=Configurar este registro en el archivo de configuración de Cargo (por ejemplo ~/.cargo/config.toml): +cargo.install=Para instalar el paquete usando Cargo, ejecute el siguiente comando: +chef.registry=Configura este registro en tu archivo ~/.chef/config.rb: +chef.install=Para instalar el paquete, ejecute el siguiente comando: +composer.registry=Configura este registro en el archivo ~/.composer/config.json: +composer.install=Para instalar el paquete usando Composer, ejecute el siguiente comando: +composer.dependencies=Dependencias +composer.dependencies.development=Dependencias de desarrollo conan.details.repository=Repositorio +conan.registry=Configurar este registro desde la línea de comandos: +conan.install=Para instalar el paquete usando Conan, ejecuta el siguiente comando: +conda.registry=Configura este registro como un repositorio Conda en tu archivo .condarc: +conda.install=Para instalar el paquete usando Conda, ejecute el siguiente comando: +container.details.type=Tipo de imagen +container.details.platform=Plataforma +container.pull=Arrastra la imagen desde la línea de comandos: +container.digest=Resumen +container.multi_arch=SO / Arquitectura +container.layers=Capas de imagen +container.labels=Etiquetas +container.labels.key=Clave +container.labels.value=Valor +cran.registry=Configurar este registro en su archivo Rprofile.site: +cran.install=Para instalar el paquete, ejecute el siguiente comando: +debian.registry=Configurar este registro desde la línea de comandos: +debian.registry.info=Seleccione $distribution y $component de la siguiente lista. +debian.install=Para instalar el paquete, ejecute el siguiente comando: +debian.repository=Información del repositorio +debian.repository.distributions=Distribuciones +debian.repository.components=Componentes +debian.repository.architectures=Arquitecturas +generic.download=Descargar paquete desde la línea de comandos: +go.install=Instalar el paquete desde la línea de comandos: +helm.registry=Configurar este registro desde la línea de comandos: +helm.install=Para instalar el paquete, ejecute el siguiente comando: +maven.registry=Configure este registro en su proyecto pom.xml archivo: +maven.install=Para usar el paquete incluya lo siguiente en el bloque dependencias en el archivo pom.xml: +maven.install2=Ejecutar vía línea de comandos: +maven.download=Para descargar la dependencia, ejecute vía línea de comandos: +nuget.registry=Configurar este registro desde la línea de comandos: +nuget.install=Para instalar el paquete usando NuGet, ejecute el siguiente comando: +nuget.dependency.framework=Marco de destino +npm.registry=Configura este registro en tu proyecto .npmrc archivo: +npm.install=Para instalar el paquete usando npm, ejecute el siguiente comando: +npm.install2=o añádelo al archivo package.json: +npm.dependencies=Dependencias +npm.dependencies.development=Dependencias de desarrollo +npm.dependencies.peer=Dependencias de pares +npm.dependencies.optional=Dependencias opcionales +npm.details.tag=Etiqueta +pub.install=Para instalar el paquete usando Dart, ejecute el siguiente comando: +pypi.requires=Requiere Python +pypi.install=Para instalar el paquete usando pip, ejecute el siguiente comando: +rpm.registry=Configurar este registro desde la línea de comandos: +rpm.distros.redhat=en distribuciones basadas en RedHat +rpm.distros.suse=en distribuciones basadas en SUSE +rpm.install=Para instalar el paquete, ejecute el siguiente comando: +rpm.repository=Información del repositorio +rpm.repository.architectures=Arquitecturas +rubygems.install=Para instalar el paquete usando gem, ejecute el siguiente comando: +rubygems.install2=o añádelo al archivo Gemfile: +rubygems.dependencies.runtime=Dependencias en tiempo de ejecución +rubygems.dependencies.development=Dependencias de desarrollo +rubygems.required.ruby=Requiere versión Ruby +rubygems.required.rubygems=Requiere la versión de RubyGem +swift.registry=Configurar este registro desde la línea de comandos: +swift.install=Añade el paquete en tu archivo Package.swift: +swift.install2=y ejecuta el siguiente comando: +vagrant.install=Para añadir un paquete Vagrant, ejecuta el siguiente comando: +settings.link=Vincular este paquete a un repositorio +settings.link.description=Si enlaza un paquete con un repositorio, el paquete se enumera en la lista de paquetes del repositorio. +settings.link.select=Seleccionar repositorio +settings.link.button=Actualizar enlace de repositorio +settings.link.success=El enlace del repositorio se ha actualizado correctamente. +settings.link.error=Error al actualizar el enlace del repositorio. +settings.delete=Eliminar paquete +settings.delete.description=La eliminación de un paquete es permanente y no se puede deshacer. +settings.delete.notice=Está a punto de eliminar %s (%s). Esta operación es irreversible, ¿está seguro? +settings.delete.success=Se ha eliminado el paquete. +settings.delete.error=No se pudo eliminar el paquete. +owner.settings.cargo.title=Índice del registro Cargo +owner.settings.cargo.initialize=Inicializar índice +owner.settings.cargo.initialize.description=Se necesita un repositorio Git de índice especial para usar el registro de Cargo. Usar esta opción (re)creará el repositorio y lo configurará automáticamente. +owner.settings.cargo.initialize.error=Fallo al inicializar el índice de Cargo: %v +owner.settings.cargo.initialize.success=El índice de Cargo se ha creado correctamente. +owner.settings.cargo.rebuild=Reconstruir índice +owner.settings.cargo.rebuild.description=Reconstruir puede ser útil si el índice no se sincroniza con los paquetes de Cargo almacenados. +owner.settings.cargo.rebuild.error=Fallo al reconstruir el índice de Cargo: %v +owner.settings.cargo.rebuild.success=El índice de Cargo se ha reconstruido correctamente. +owner.settings.cleanuprules.title=Administrar reglas de limpieza +owner.settings.cleanuprules.add=Añadir regla de limpieza +owner.settings.cleanuprules.edit=Editar regla de limpieza +owner.settings.cleanuprules.none=No hay reglas de limpieza disponibles. Por favor, consulte la documentación. +owner.settings.cleanuprules.preview=Vista previa de regla de limpieza +owner.settings.cleanuprules.preview.overview=%d paquetes están programados para ser eliminados. +owner.settings.cleanuprules.preview.none=Regla de limpieza no coincide con ningún paquete. owner.settings.cleanuprules.enabled=Activo +owner.settings.cleanuprules.pattern_full_match=Aplicar patrón al nombre completo del paquete +owner.settings.cleanuprules.keep.title=Las versiones que coinciden con estas reglas son reales, incluso si coinciden con una regla de eliminación a continuación. +owner.settings.cleanuprules.keep.count=Mantener el más reciente owner.settings.cleanuprules.keep.count.1=1 versión por paquete owner.settings.cleanuprules.keep.count.n=%d versiones por paquete +owner.settings.cleanuprules.keep.pattern=Mantener las versiones coincidentes +owner.settings.cleanuprules.keep.pattern.container=La última versión siempre es guardada en los paquetes de contenedor. +owner.settings.cleanuprules.remove.title=Se eliminan las versiones que coinciden con estas reglas, a menos que una regla anterior indique mantenerlas. +owner.settings.cleanuprules.remove.days=Eliminar versiones anteriores a +owner.settings.cleanuprules.remove.pattern=Eliminar versiones coincidentes +owner.settings.cleanuprules.success.update=Regla de limpieza ha sido actualizada. +owner.settings.cleanuprules.success.delete=Regla de limpieza ha sido eliminada. +owner.settings.chef.title=Registro de Chef +owner.settings.chef.keypair=Generar par de claves +owner.settings.chef.keypair.description=Un par de claves es necesario para autenticarse en el registro del Chef. Si ha generado un par de claves antes, generar un nuevo par de claves descartará el par de claves antiguo. +search_in_external_registry = Buscar en %s +arch.pacman.sync = Sincronizar el paquete con pacman: +arch.version.properties = Propiedades de la versión +arch.pacman.repo.multi = %s tiene la misma versión en diferentes distribuciones. +arch.pacman.repo.multi.item = Configuración para %s +arch.pacman.conf = Añadir servidor con distribución y arquitectura relacionadas a /etc/pacman.conf : +arch.version.backup = Copia de seguridad +arch.version.depends = Depende +arch.version.groups = Grupo +rpm.repository.multiple_groups = Este paquete está disponible en múltiples grupos. +arch.version.conflicts = Conflictos +arch.version.replaces = Reemplazos +container.images.title = Imágenes +alt.registry = Configurar este registro desde la línea de comandos: +alt.registry.install = Para instalar el paquete, ejecute el siguiente comando: +alt.install = Instalar paquete +alt.repository = Información del repositorio +alt.repository.architectures = Arquitecturas +alt.repository.multiple_groups = Este paquete está disponible en múltiples grupos. +arch.version.description = Descripción +arch.version.provides = Proveedores +npm.dependencies.bundle = Empaquetar dependencias +arch.version.checkdepends = Comprobar dependencias +arch.version.optdepends = Dependencias opcionales +arch.version.makedepends = Construir dependencias +arch.pacman.helper.gpg = Añade el certificado de confianza para pacman: [secrets] secrets=Secretos @@ -3431,8 +3731,66 @@ deletion.failed=Error al eliminar secreto. management=Gestión de secretos [actions] +actions=Acciones + unit.desc=Gestione procesos CI/CD integrados con Forgejo Actions. +status.unknown=Desconocido +status.waiting=Esperando +status.running=Corriendo +status.success=Éxito +status.failure=Fallo +status.cancelled=Cancelado +status.skipped=Saltado +status.blocked=Bloqueado + +runners=Nodos +runners.runner_manage_panel=Gestión de nodos +runners.new=Crear nuevo nodo +runners.new_notice=Cómo iniciar un nodo +runners.status=Estado +runners.id=Id. +runners.name=Nombre +runners.owner_type=Tipo +runners.description=Descripción +runners.labels=Etiquetas +runners.last_online=Última hora en línea +runners.runner_title=Nodo +runners.task_list=Tareas recientes en este nodo +runners.task_list.no_tasks=Todavía no hay tarea. +runners.task_list.run=Ejecutar +runners.task_list.status=Estado +runners.task_list.repository=Repositorio +runners.task_list.commit=Confirmación +runners.task_list.done_at=Hecho en +runners.edit_runner=Editar nodo +runners.update_runner=Actualizar cambios +runners.update_runner_success=Nodo actualizado correctamente +runners.update_runner_failed=Fallo al actualizar el nodo +runners.delete_runner=Eliminar este nodo +runners.delete_runner_success=Nodo eliminado con éxito +runners.delete_runner_failed=Fallo al eliminar el nodo +runners.delete_runner_header=Confirma para eliminar el nodo +runners.delete_runner_notice=Si una tarea se está ejecutando en este nodo, se terminará y marcará como fallida. Puede romper el flujo de trabajo en curso. +runners.none=No hay nodos disponibles +runners.status.unspecified=Desconocido +runners.status.idle=Inactivo +runners.status.active=Activo +runners.status.offline=Desconectado +runners.version=Versión +runners.reset_registration_token=Restablecer token de registro +runners.reset_registration_token_success=Se ha restablecido correctamente el token de registro del nodo + +runs.all_workflows=Todos los flujos de trabajo +runs.commit=Commit +runs.scheduled=Programado +runs.pushed_by=push enviado por +runs.invalid_workflow_helper=El archivo de configuración del trabajo no es válido. Revisa tu archivo de configuración: %s +runs.actor=Actor +runs.status=Estado +runs.actors_no_select=Todos los actores +runs.status_no_select=Todo el estado +runs.no_results=No hay resultados coincidentes. runs.no_runs=El flujo de trabajo no tiene ejecuciones todavía. workflow.disable=Desactivar flujo de trabajo @@ -3443,16 +3801,34 @@ workflow.disabled=El flujo de trabajo está deshabilitado. need_approval_desc=Necesita aprobación para ejecutar flujos de trabajo para el pull request del fork. +variables=Variables +variables.management=Gestión de variables +variables.creation=Añadir variable +variables.none=Aún no hay variables. +variables.deletion=Eliminar variable +variables.deletion.description=Eliminar una variable es permanente y no se puede deshacer. ¿Continuar? +variables.description=Las variables se pasarán a ciertas acciones y no se podrán leer de otro modo. +variables.edit=Editar variable +variables.deletion.failed=No se pudo eliminar la variable. +variables.deletion.success=La variable ha sido eliminada. +variables.creation.failed=No se pudo agregar la variable. +variables.creation.success=La variable "%s" ha sido añadida. +variables.update.failed=Error al editar la variable. +variables.update.success=La variable ha sido editada. variables.id_not_exist = Variable con id %d no existe. runs.empty_commit_message = (mensaje de commit vacío) runs.expire_log_message = Los registros han sido eliminados porque eran demasiado antiguos. +runs.workflow = Flujo de trabajo workflow.dispatch.run = Correr flujo de trabajo workflow.dispatch.use_from = Usar el flujo de trabajo de workflow.dispatch.invalid_input_type = Tipo de entrada inválida "%s". +runs.no_workflows = Aún no hay flujos de trabajo. workflow.dispatch.success = La ejecución del flujo de trabajo se ha solicitado correctamente. +variables.not_found = No se ha encontrado la variable. workflow.dispatch.input_required = Se requiere valor para la entrada "%s". workflow.dispatch.trigger_found = Este flujo de trabajo tiene un disparador de eventos workflow_dispatch. workflow.dispatch.warn_input_limit = Sólo se muestran las primeras %d entradas. + runs.no_workflows.help_write_access = ¿No sabes cómo empezar con Forgejo Actions? Comprueba la guía de inicio rápido en la documentación de usuario para escribir tu primer workflow, luego configura un runner de Forgejo para ejecutar tus trabajos. runs.no_workflows.help_no_write_access = Para aprender sobre Forgejo Actions, véase la documentación. @@ -3500,6 +3876,9 @@ regexp_tooltip = Interpretar los términos de búsqueda como una expresión regu regexp = Expresión Regular [markup] +filepreview.lines = Líneas %[1]d a %[2]d en %[3]s +filepreview.line = Línea %[1]d en %[2]s +filepreview.truncated = La vista previa se ha truncado [repo.permissions] pulls.read = Lectura: Leer y crear pull requests. @@ -3518,11 +3897,19 @@ ext_wiki = Acceder al enlace de la wiki externa. Los permisos se gestionan de fo code.write = Escritura: Push al repositorio, crear ramas y tags. issues.write = Escritura: Cerrar incidencias y gestion de metadatos como etiquetas, hitos, asignaciones, fechas de vencimiento y dependencias. packages.write = Escritura: Publicar y eliminar paquetes asignados al repositorio. + + actions.read = Leer: visualizar ejecuciones del flujo de trabajo y sus logs. actions.write = Escribir: Ejecutar, reiniciar y cancelar flujos de trabajo. Gestionar la delegación de confianza a los publicadores de pull requests. - [munits.data] +eib = EiB +gib = GiB +pib = PiB +kib = KiB +tib = TiB +mib = MiB +b = B [translation_meta] test = Did you know that the Garoé, the sacred tree of El Hierro in the Canary Islands, was essentially a natural water fountain? :) \ No newline at end of file diff --git a/options/locale/locale_et.ini b/options/locale/locale_et.ini index 6b3374ce4c..f1dfc1be6e 100644 --- a/options/locale/locale_et.ini +++ b/options/locale/locale_et.ini @@ -26,6 +26,8 @@ enable_javascript = See veebileht eeldab JavaScripti kasutamise lubamist. toc = Sisukord licenses = Litsentsid username = Kasutajanimi +webauthn_error_unable_to_process = Server ei saanud sinu päringut töödelda. +webauthn_error_duplicated = Turvavõti ei ole selle päringu puhul lubatud. Palun veendu, et võti ei ole juba registreeritud. return_to_forgejo = Tagasi Forgejo'sse toggle_menu = Lülita menüü sisse/välja more_items = Rohkem objekte @@ -36,6 +38,16 @@ re_type = Kinnita salasõna twofa = Kahefaktoriline autentimine twofa_scratch = Kahefaktoriline kriipsukood passcode = Salakood +webauthn_insert_key = Sisesta oma turvavõti +webauthn_sign_in = Vajuta turvavõtme nuppu. Kui sinu turvavõtmel ei ole nuppu, sisesta see uuesti. +webauthn_press_button = Palun vajuta turvavõtme nuppu… +webauthn_use_twofa = Sisesta oma telefonist kahefaktorilise autentimise kood +webauthn_error = Sinu turvavõtit ei saanud lugeda. +webauthn_unsupported_browser = Sinu veebibrauser ei toeta praegu WebAuthn-liidestust. +webauthn_error_unknown = Tekkis tundmatu viga. Palun proovi uuesti. +webauthn_error_insecure = WebAuthn toetab ainult turvalisi ühendusi. HTTP kaudu testimiseks võid kasutada lähteaadressina „localhost“ või „127.0.0.1“ +webauthn_error_empty = Palun lisa sellele võtmele täisnimi. +webauthn_error_timeout = Päring aegus enne võtme lugemist. Palun laadi see lehekülg uuesti ja proovi uuesti. repository = Hoidla organization = Organisatsioon new_fork = Uus lähekoodihoidla haru @@ -129,6 +141,7 @@ copy_path = Kopeeri asukoht captcha = Robotilõks unpin = Lõpeta esiletõstmine powered_by = Siin on kasutusel %s + collaborative = Koostöö [search] @@ -178,7 +191,7 @@ buttons.heading.tooltip = Lisa pealkiri buttons.italic.tooltip = Lisa kaldkirjas tekst (Ctrl+I / ⌘I) buttons.quote.tooltip = Tsiteeri teksti buttons.code.tooltip = Lisa kood -buttons.link.tooltip = Lisa link (Ctrl+K / ⌘K) +buttons.link.tooltip = Lisa link buttons.list.ordered.tooltip = Lisa nummerdatud nimekiri buttons.list.unordered.tooltip = Lisa nimekiri buttons.list.task.tooltip = Lisa ülesannete nimekiri @@ -322,6 +335,7 @@ sqlite3_not_available = See Forgejo versioon ei toeta SQLite3 andmebaasi. Palun offline_mode.description = Lülitage kolmandate osapoolte sisuedastusvõrgud välja ja jaga kõiki ressursse kohalikust serverist. password_algorithm = Salasõna räsialgoritm invalid_password_algorithm = Vigane salasõna räsialgoritm + invalid_repo_path = Tarkvarahoidla juurkaust on vigane: %v invalid_app_data_path = Rakenduse andmete asukoht on vigane: %v @@ -330,6 +344,7 @@ allow_password_change = Eelda, et kasutajad muudavad oma salasõna (soovitatav) forgot_password_title = Ununenud salasõna must_change_password = Muuda oma salasõna forgot_password = Kas salasõna ununes? + verify = Verifitseeri openid_connect_submit = Ühenda @@ -337,6 +352,7 @@ openid_connect_submit = Ühenda password_change.subject = Sinu salasõna on muutunud password_change.text_1 = Sinu kasutajakonto salasõna on just muutunud. totp_disabled.text_1 = Lisaautentimine ehk ajapõhise salasõna (TOTP) kasutamine on sinu kasutajakontol just välja lülitatud. + release.note = Märkus: release.downloads = Allalaadimised: repo.transfer.to_you = sina @@ -349,6 +365,7 @@ username_password_incorrect = Kassutajanimi või salasõna pole õige. required_prefix = Sisendi alguses peab olema „&s“ To = Alamharu nimi NewBranchName = Alamharu uus nimi + UserName = Kasutajanimi Description = Kirjeldus Pronouns = Asesõnad @@ -356,7 +373,7 @@ Biography = Biograafia Website = Veebisait Location = Asukoht Content = Sisu -target_branch_not_exist = Sihiks võetud alamharu pole olemas. + password_complexity = Salasõna ei vasta keerukuse reeglitele: password_lowercase_one = Peaks olema vähemalt üks väiketäht password_uppercase_one = Peaks olema vähemalt üks suurtäht @@ -364,6 +381,7 @@ password_digit_one = Peaks olema vähemalt üks number password_special_one = Peaks olema vähemalt üks erimärk (kirjavahemärk, sulg, jutumärk, jne) enterred_invalid_password = Sinu sisestatud salasõna pole korrektne. unset_password = Siselogiv kasutaja pole lisanud oma kontole salasõna. +target_branch_not_exist = Sihiks võetud alamharu pole olemas. [settings] retype_new_password = Korda uut salasõna @@ -373,6 +391,7 @@ update_password = Muuda salasõna old_password = Senine salasõna new_password = Uus salasõna comment_type_group_branch = Alamharu + profile = Profiil account = Kasutajakonto appearance = Välimus @@ -417,6 +436,7 @@ permission_read = Lugemine permissions_list = Õigused: save_application = Salvesta oauth2_application_edit = Muuda + change_password = Salasõna muutmine password_change_disabled = Kohalikus serveris mitteleiduvad kasutajad ei saa oma salasõna Forgejo kasutajaliidesest muuta. email_desc = Sinu põhilist e-posti aadressi kasutatakse teavitusteks, salasõna taastamiseks ning kui ta pole peidetud, siis ka veebipõhisteks toiminguteks gitiga. @@ -457,6 +477,8 @@ default_branch_label = vaikimisi code.desc = Ligipääs lähtekoodile, failidele, sissekannetele ja alamharudele. filter_branch_and_tag = Filtreeri alamharu või sildi alusel branches = Alamharud +n_branch_one = %s alamharu +n_branch_few = %s alamharu commit_graph.select = Vali alamharud commit.contained_in_default_branch = See sissekanne on vaikimisi alamharu osa editor.new_branch_name_desc = Alamharu uus nimi… @@ -473,23 +495,28 @@ settings.event_delete_desc = Alamharu või silt on kustutatud. settings.branches = Alamharud settings.protected_branch.save_rule = Salvesta reegel settings.protected_branch.delete_rule = Kustuta reegel + +editor.add_file = Lisa fail editor.or = või editor.cancel_lower = Katkesta editor.add_tmpl.filename = failinimi pulls.editable = Muudetav -editor.add_file = Lisa fail + fork_repo = Tee lähtekoodihoidlast uus koodiharu fork_from = Tee koodiharu allikast already_forked = Sa juba oled siit teinud koodiharu: %s fork_to_different_account = Tee koodihatu teisele kasutajakontole fork_visibility_helper = Lähtekoodihoidla koodiharu nähtavust ei saa muuta. +fork_branch = Alamharu, millest tahad kloonid koodiharu +mirror_denied_combination = Salasõnapõhist ja avaliku võtme põhist autentimist ei saa samal ajal kasutada. forks = Koodharud +tree_path_not_found.branch = Asukohta „%[1]s“ pole olemas alamharus „%[2]s“ +template.git_content = Giti sisu (vaikimisi alamharu) sync_fork.button = Sünkrooni forked_from = koodiharu allikast fork_from_self = Sa ei saa endale kuuluvast lähtekoodi hoidlast koodiharu teha. fork_guest_user = Logi sisse sellest lähtekoodi hoidlast koodiharu tegemiseks. fork = Tee koodiharu -fork_branch = Alamharu, millest tahad kloonid koodiharu commit.load_referencing_branches_and_tags = Laadi alamharud ja sildid, mis viitavad sellele sissekandele editor.must_be_on_a_branch = Selle faili muutmiseks või muudatuste pakkumiseks pead asuma antud alamharus. editor.create_new_branch_np = Loo selle sissekande jaoks uus alamharu. @@ -501,14 +528,14 @@ commit.revert-content = Vali alamharu, kuhu tahad tagasi pöörata: issues.delete_branch_at = `kustutatud alamharu %s %s` pulls.filter_branch = Filtreeri alamharu alusel pulls.nothing_to_compare_have_tag = Valitud alamharud/sildid on võrdsed. -activity.git_stats_push_to_all_branches = kõikidesse alamharudesse. activity.git_stats_push_to_branch = alamharusse %s ja +activity.git_stats_push_to_all_branches = kõikidesse alamharudesse. activity.git_stats_on_default_branch = Alamharus %s, settings.branches.switch_default_branch = Vaheta vaikimisi alamharusse settings.branch_filter = Alamharude filter settings.protected_branch = Alamharu kaitse -settings.protect_new_rule = Lisa uus alamharu kaitsmise reegel settings.branch_protection = %s alamharu kaitsmise reeglid +settings.protect_new_rule = Lisa uus alamharu kaitsmise reegel settings.rename_branch = Muuda alamharu nime branch.name = Alamharu nimi branch.already_exists = Alamharu nimega „%s“ on juba olemas. @@ -538,11 +565,13 @@ branch.rename_branch_to = Alamharu „%s“ nimi on muutmisel. branch.create_branch_operation = Loo alamharu branch.new_branch = Loo uus alamharu branch.new_branch_from = Loo uus alamharu siit: „%s“ -tree_path_not_found.branch = Asukohta „%[1]s“ pole olemas alamharus „%[2]s“ -template.git_content = Giti sisu (vaikimisi alamharu) -mirror_denied_combination = Salasõnapõhist ja avaliku võtme põhist autentimist ei saa samal ajal kasutada. [actions] +variables = Muutujad +variables.deletion = Eemalda muutuja +variables.creation = Lisa muutuja +variables.none = Muutujaid veel pole. +variables.management = Halda muutujaid [admin] users.password_helper = Kui sa ei taha salasõna muuta, siis jäta väli tühjaks. @@ -551,6 +580,7 @@ auths.bind_password = Seo salasõna [explore] users = Kasutajad + repos = Koodihoidlad organizations = Organisatsioonid code = Kood @@ -586,4 +616,5 @@ cancel = Katkesta [action] compare_branch = Võrdle -[packages] \ No newline at end of file +[packages] +alpine.repository.branches = Alamharud \ No newline at end of file diff --git a/options/locale/locale_eu.ini b/options/locale/locale_eu.ini index af0a54e418..cb4fe075f7 100644 --- a/options/locale/locale_eu.ini +++ b/options/locale/locale_eu.ini @@ -33,6 +33,18 @@ captcha = CAPTCHA twofa = Bi faktoreko autentifikazioa twofa_scratch = Bi faktoreko marradura-kodea passcode = Pasakodea +webauthn_insert_key = Sartu zure segurtasun-gakoa +webauthn_sign_in = Sakatu botoia zure segurtasun-gakoan. Zure segurtasun-gakoak ez badu botoirik, sartu berriro. +webauthn_press_button = Mesedez, sakatu botoia zure segurtasun-gakoan… +webauthn_use_twofa = Erabili zure telefonoko bi faktoreko kodea +webauthn_error = Ezin izan da irakurri zure segurtasun-gakoa. +webauthn_unsupported_browser = Zure nabigatzaileak ez du WebAuthn onartzen. +webauthn_error_unknown = Akats ezezagun bat gertatu da. Mesedez, saiatu berriro. +webauthn_error_insecure = WebAuthnek konexio seguruak baino ez ditu onartzen. HTTP bidez probatzeko, "localhost" edo "127.0.0.1" jatorria erabil daiteke +webauthn_error_unable_to_process = Zerbitzariak ezin izan du zure eskaera prozesatu. +webauthn_error_duplicated = Segurtasun-gakorik ez da onartzen eskaera honetarako. Mesedez, ziurtatu giltza ea dagoela erregistratuta. +webauthn_error_empty = Gako honetarako izen bat jarri behar duzu. +webauthn_error_timeout = Zure gakoa irakurri aurretik denbora-muga gainditu da. Kargatu berriro orrialde hau eta saiatu berriro. repository = Biltegia new_fork = Biltegi-adar berria new_project_column = Zutabe berria diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index bf358b8a8d..c0b69c258e 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -98,6 +98,10 @@ name=نام logo = نشان sign_in_with_provider = ورود با %s enable_javascript = این وب‌گاه به جاوا اسکریپت نیاز دارد. +webauthn_press_button = لطفاً دکمۀ روی کلید امنیتی را فشار دهید… +webauthn_unsupported_browser = مرورگر شما در حال حاضر از WebAuthn پشتیبانی نمی‌کند. +webauthn_error_unknown = خطایی ناشناخته رخ داد. لطفاً دوباره سعی کنید. +webauthn_error_unable_to_process = کارساز نتوانست درخواست شما را پردازش کند. new_project_column = ستون جدید retry = سعی دوباره rerun = اجرای دوباره @@ -109,10 +113,17 @@ locked = قفل شده copy_hash = رونوشت هش unknown = نامشخص copy_type_unsupported = این نوع از فایل نمی‌تواند رونوشت شود +webauthn_insert_key = کلید امنیتی خود را وارد کنید +webauthn_sign_in = دکمۀ روی کلید امنیتی را فشار دهید. اگر کلید امنیتی شما دکمه‌ای ندارد، آن را دوباره وارد کنید. +webauthn_use_twofa = از یک کد دومرحله‌ای از تلفنتان استفاده کنید +webauthn_error = کلید امنیتی شما نتوانست خوانده شود. more_items = موارد بیشتر +webauthn_error_duplicated = کلید امنیتی برای این درخواست مجاز نیست. لطفاً مطمئن شوید که این کلید در حال حاضر ثبت نشده است. +webauthn_error_timeout = مهلت زمانی قبل از اینکه کلید شما خوانده شود تمام شد. لطفاً این صفحه را تازه‌سازی کرده و مجدد تلاش کنید. new_org.link = سازمان جدید new_org.title = سازمان جدید new_migrate.link = مهاجرت جدید +webauthn_error_empty = شما باید یک نام برای این کلید انتخاب کنید. new_repo.title = مخزن جدید new_migrate.title = مهاجرت جدید new_repo.link = مخزن جدید @@ -144,6 +155,7 @@ filter.not_archived = بایگانی نشده copy_content = رونوشت درون‌مایه invalid_data = داده نامعتبر: %v copy_generic = رونوشت در بریده‌دان +webauthn_error_insecure = احرازوب فقط از روش‌های ایمن ممکن است. برای آزمودن بر روی اچ‌تی‌تی‌پی می‌توانید از «میزبان‌محلی» یا «۱۲۷.۰.۰.۱» استفاده کنید. show_log_seconds = نمایش ثانیه‌ها confirm_delete_selected = تایید می‌کنید که همه موارد گزینش شده حذف شوند؟ filter.clear = پاک‌کردن پالایه‌ها @@ -871,6 +883,14 @@ migrate_options_lfs=مهاجرت فایلهای LFS migrate_options_lfs_endpoint.label=نشانهای پایانی LFS migrate_options_lfs_endpoint.description=Migration سعی خواهد کرد از کنترل از راه دور Git شما برای تعیین سرور LFS استفاده کند. همچنین اگر داده های LFS مخزن در جای دیگری ذخیره شده باشد، می توانید یک نقطه پایانی سفارشی را مشخص کنید. migrate_options_lfs_endpoint.description.local=مسیر سرور محلی نیز پشتیبانی می شود. +migrate_items=مولفه های مهاجرت +migrate_items_wiki=دانشنامه +migrate_items_milestones=نقاط عطف +migrate_items_labels=برچسب‌ها +migrate_items_issues=مسائل +migrate_items_pullrequests=تقاضاهای واکشی +migrate_items_merge_requests=تقاضاهای واکشی +migrate_items_releases=انتشارها migrate_repo=انتقال مخزن migrate.clone_address=انتقال / همسان‌سازی از نشانی migrate.clone_address_desc=HTTP(S) or Git 'همسان‌سازی' نشانی‌های موجود در این مخزن @@ -886,6 +906,14 @@ migrate.migrate=مهاجرت از %s migrate.migrating=مهاجرت از %s ... migrate.migrating_failed=مهاجرت از %s ناموفق بود. migrate.migrating_failed_no_addr=مهاجرت ناموفق بود. +migrate.migrating_git=انتقال داده های Git +migrate.migrating_topics=موضوعات مهاجرت +migrate.migrating_milestones=نقاط عطف مهاجرت +migrate.migrating_labels=برچسب های مهاجرت +migrate.migrating_releases=انتشارات مهاجرت +migrate.migrating_issues=مشکلات مهاجرت +migrate.migrating_pulls=مهاجرت درخواست های pull + mirror_from=قرینه از forked_from=انشعاب شده از generated_from=ساخته شده از @@ -2088,6 +2116,34 @@ dashboard.resync_all_hooks=همگام سازی مجدد hook های pre-receive dashboard.reinit_missing_repos=تمامی مخازنی که سوابقشان وجود دارند مجدداً گیت آنها مفقود شده است مجدداً مقدمات آنها فراهم شود dashboard.sync_external_users=همگام سازی اطلاعات کاربر خارجی dashboard.cleanup_hook_task_table=جدول hook_task تمیز کردن +dashboard.server_uptime=فعالیت بی‌وقفه سرور +dashboard.current_goroutine=Goroutine های فعلی +dashboard.current_memory_usage=میزان مصرف فعلی از حافظه +dashboard.total_memory_allocated=کل حافظه اختصاص داده شده +dashboard.memory_obtained=حافظه به دست آمده +dashboard.pointer_lookup_times=اشاره‌گر زمان‌های جستجو +dashboard.memory_allocate_times=تخصیص یافتن حافظه +dashboard.memory_free_times=آزادسازی حافظه +dashboard.current_heap_usage=میزان مصرف توده فعلی +dashboard.heap_memory_obtained=حافظه به دست آمده برای توده +dashboard.heap_memory_idle=بیکار بوده حافظه توده +dashboard.heap_memory_in_use=حافظه توده در حال استفاده +dashboard.heap_memory_released=حافظه توده آزاد شد +dashboard.heap_objects=شی توده یا Heap +dashboard.bootstrap_stack_usage=میزان استفاده از پشته توسط راهنداز +dashboard.stack_memory_obtained=حافظه پشته به دست آمده +dashboard.mspan_structures_usage=میزان استفاده‌ی ساختار های MSpan +dashboard.mspan_structures_obtained=ساختار های MSpan به دست آمده +dashboard.mcache_structures_usage=میزان استفاده از ساختار های MCache +dashboard.mcache_structures_obtained=ساختار های MCache به دست آمده +dashboard.profiling_bucket_hash_table_obtained=Profiling Bucket Hash Table به دست آمده +dashboard.gc_metadata_obtained=متادیتاهای بدست امده از GC +dashboard.other_system_allocation_obtained=تخصیص های حافظه در سایر قسمت های سیستم +dashboard.next_gc_recycle=بازیافت GC بعدی +dashboard.last_gc_time=زمان از آخرین GC +dashboard.total_gc_pause=کل زمان مکث GC +dashboard.last_gc_pause=واپسین مکث در GC +dashboard.gc_times=زمان های GC dashboard.delete_old_actions=تمام اقدامات قدیمی را از پایگاه داده حذف کنید dashboard.delete_old_actions.started=حذف تمام اقدامات قدیمی از پایگاه داده شروع شده است. @@ -2127,6 +2183,19 @@ users.still_own_repo=این کاربر هنوز صاحب یک یا چند مخز users.still_has_org=این کاربر عضو یک سازمان است. ابتدا کاربر را از هر سازمان متعلق حذف کنید. users.deletion_success=حساب کاربری حذف شد. users.reset_2fa=2FA را بازنشانی کنید +users.list_status_filter.menu_text=فیلتر +users.list_status_filter.reset=شروع دوباره +users.list_status_filter.is_active=فعال +users.list_status_filter.not_active=غیرفعال +users.list_status_filter.is_admin=ادمین +users.list_status_filter.not_admin=غیرادمین +users.list_status_filter.is_restricted=محصور +users.list_status_filter.not_restricted=محدود نشده است +users.list_status_filter.is_prohibit_login=ورود را ممنوع شود +users.list_status_filter.not_prohibit_login=اجازه ورود +users.list_status_filter.is_2fa_enabled=2FA فعال است +users.list_status_filter.not_2fa_enabled=2FA غیرفعال است + emails.email_manage_panel=مدیریت ایمیل کاربر emails.primary=اولیه emails.activated=فعال شده @@ -2394,6 +2463,20 @@ monitor.process.cancel_desc=لغو کردن یک فرآیند ممکن است ب monitor.process.cancel_notices=لغو: %s؟ monitor.process.children=فرزندان +monitor.queues=صف ها +monitor.queue=صف: %s +monitor.queue.name=نام +monitor.queue.type=نوع +monitor.queue.exemplar=نوع نمونه +monitor.queue.numberworkers=تعداد کارگران +monitor.queue.maxnumberworkers=بیشینه تعداد کارگران +monitor.queue.settings.title=تنظیمات استخر +monitor.queue.settings.maxnumberworkers=بیشینه تعداد کارگران +monitor.queue.settings.maxnumberworkers.placeholder=در حال حاضر %[1]v +monitor.queue.settings.maxnumberworkers.error=حداکثر تعداد کارگران باید یک عدد باشد +monitor.queue.settings.submit=بالا بردن ساماندهی +monitor.queue.settings.changed=تنظیمات تازه شد + notices.system_notice_list=هشدارهای سامانه notices.view_detail_header=مشاهده جزئیات اخطار notices.select_all=انتخاب همه @@ -2465,22 +2548,69 @@ raw_seconds=ثانیه raw_minutes=دقیقه [dropzone] +default_message=فایل را در این محل رها کنید یا دکمه ی آپلود یا بارگزاری را فشار دهید. +invalid_input_type=شما قادر به ارسال فایل های از این نوع نیستید. +file_too_big=حجم فایل ({{filesize}} MB) بیش از حداکثر مجاز است ({{maxFilesize}} مگابایت). +remove_file=حذف پرونده [notification] +notifications=اعلان‌ها +unread=خوانده نشده +read=خواندن +no_unread=اعلان خوانده نشده‌ای موجود نیست. +no_read=اعلان خوانده شده‌ای موجود نیست. +pin=سنجاق کردن اعلان +mark_as_read=علامتگذاری بعنوان خوانده شده +mark_as_unread=علامتگذاری بعنوان خوانده نشده +mark_all_as_read=علامت همه به عنوان خوانده شده [gpg] +default_key=ثبت شده با کلید پیش فرض +error.extract_sign=خطا در استخراج امضا +error.generate_hash=خطا در ساختن هش کامیت +error.no_committer_account=هیچ ایمیلی به حساب کاربری صاحب کامیت پیونده داده نشده است +error.no_gpg_keys_found=هیچ کلید شناخته شده ای برای این امضا در پایگاه داده ها یافت نشد +error.not_signed_commit=هیچ کامیتی تکلیف نشده است +error.failed_retrieval_gpg_keys=بازیابی هر کلیدی که به حساب کاربری کامیت دهنده پیوست شده بود ناموفق بود +error.probable_bad_signature=هشدار! اگرچه اینجا یک کلید با ID در پایگاه داده است این کامیت تایید نشده است! این کامیت مشـــکــــوک است. +error.probable_bad_default_signature=هشدار! اگرچه اینجا یک کلید پیش فرض با ID است این اما کامیت تایید نشده است! این کامیت مشـــکــــوک است. [units] error.no_unit_allowed_repo=شما اجازه دسترسی به هیچ قسمت از این مخزن را ندارید. error.unit_not_allowed=شما اجازه دسترسی به این قسمت مخزن را ندارید. [packages] +filter.type=نوع +alpine.repository.branches=شاخه‎ها +alpine.repository.repositories=مخازن conan.details.repository=مخزن owner.settings.cleanuprules.enabled=فعال شده [secrets] [actions] +runners.name=نام +runners.owner_type=نوع +runners.description=شرح +runners.task_list.run=اجرا +runners.task_list.repository=مخزن +runners.task_list.commit=کامیت +runners.status.active=فعال + +runs.commit=کامیت +variables.edit = تغییر متغیر +variables.deletion.success = متغیر حذف شد. +variables.deletion = حذف متغیر +variables.creation.success = متغیر "%s" ایجاد شد. +variables = متغیرها +variables.management = مدیریت متغیرها +variables.update.failed = عدم موفقیت در تغییر متغیر +variables.update.success = تغییر متغیر با موفقیت انجام شد. +variables.creation = افزودن متغیر +variables.none = هیچ متغیری هنوز وجود ندارد. + + + [projects] type-1.display_name = پروژه ی مستقل diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 5e89ce5b5d..c373c8bbbc 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -33,6 +33,18 @@ twofa=Kaksivaiheinen todennus twofa_scratch=Kaksivaiheinen kertakäyttöinen koodi passcode=Pääsykoodi +webauthn_insert_key=Aseta turva-avaimesi +webauthn_sign_in=Paina turva-avaimesi painiketta. Jos turva-avaimessasi ei ole painiketta, irrota se ja aseta uudelleen. +webauthn_press_button=Paina turva-avaimesi painiketta… +webauthn_use_twofa=Käytä kaksivaihesta todennusta puhelimestasi +webauthn_error=Turva-avainta ei voitu lukea. +webauthn_unsupported_browser=Selaimesi ei tällä hetkellä tue WebAuthnia. +webauthn_error_unknown=Tuntematon virhe. Yritä uudelleen. +webauthn_error_insecure=WebAuthn tukee vain turvallisia yhteyksiä. Testaamista varten HTTP-välityksellä voit käyttää alkuperää "localhost" tai "127.0.0.1" +webauthn_error_unable_to_process=Palvelin ei pystynyt käsittelemään pyyntöä. +webauthn_error_duplicated=Turva-avainta ei ole sallittu tässä pyynnössä. Varmista, ettei avainta ole jo rekisteröity. +webauthn_error_empty=Sinun täytyy asettaa nimi tälle avaimelle. +webauthn_error_timeout=Aikakatkaisu ennen kuin avaintasi voitiin lukea. Lataa tämä sivu uudelleen ja yritä uudelleen. repository=Tietovarasto organization=Organisaatio mirror=Peili @@ -166,7 +178,7 @@ contributions_format = {contributions} {day}. {month} {year} [editor] buttons.code.tooltip = Lisää koodia -buttons.link.tooltip = Lisää linkki (Ctrl+K / ⌘K) +buttons.link.tooltip = Lisää linkki buttons.mention.tooltip = Mainitse käyttäjä tai tiimi buttons.list.task.tooltip = Lisää tehtävälista buttons.disable_monospace_font = Poista tasalevyinen fontti käytöstä @@ -997,13 +1009,16 @@ ssh_principal_deletion_success = Prinsipaali on poistettu. principal_state_desc = Tätä prinsipaalia on käytetty viimeisen seitsemän päivän aikana regenerate_token_success = Poletti on luotu uudelleen. Sovellukset, jotka käyttivät polettia, eivät enää pääse tilillesi. Kyseiset sovellukset tulee päivittää uudella poletilla. quota.sizes.assets.all = Resurssit + can_not_add_email_activations_pending = Aktivointi odottaa. Odota hetki ja yritä uudelleen muutaman minuutin kuluttua, jos haluat lisätä uuden sähköpostiosoitteen. gpg_invalid_token_signature = Annettu GPG-avain, allekirjoitus ja poletti eivät täsmää, tai poletti on vanhentunut. ssh_invalid_token_signature = Annettu SSH-avain, allekirjoitus tai poletti eivät täsmää, tai poletti on vanhentunut. access_token_desc = Valitut poletin käyttöoikeudet rajoittavat valtuuden vain vastaaville API-reiteille. Lue dokumentaatio saadaksesi lisätietoja. + oauth2_application_remove_description = OAuth2-sovelluksen poistaminen estää sitä pääsemästä valtuutettuihin käyttäjätileihin tässä instanssissa. Jatketaanko? oauth2_application_locked = Forgejo esirekisteröi joitain OAuth2-sovelluksia käynnistymisen yhteydessä, jos näin on määritetty kokoonpanon asetuksissa. Odottamattoman toiminnan estämiseksi näitä sovelluksia ei voi muokata tai poistaa. Lisätietoja on saatavilla OAuth2-dokumentaatiossa. quota.rule.exceeded.helper = Objektien yhteiskoko tälle säännölle on ylittänyt kiintiön. + regenerate_scratch_token_desc = Jos kadotit palautusavaimesi tai käytit sen jo sisäänkirjautumiseen, voit nollata palautusavaimen tästä. delete_with_all_comments = Tilisi on nuorempi kuin %s. Aavekommenttien välttämiseksi kaikki ongelma- ja vetopyyntökommentit poistetaan tilin mukana. quota.sizes.git.lfs = Git LFS @@ -1058,6 +1073,13 @@ template.issue_labels=Ongelmanimilaput +migrate_items=Migraation kohteet +migrate_items_wiki=Wiki +migrate_items_milestones=Merkkipaalut +migrate_items_labels=Nimilaput +migrate_items_issues=Ongelmat +migrate_items_pullrequests=Vetopyynnöt +migrate_items_releases=Julkaisut migrate_repo=Suorita tietovaraston migraatio migrate.clone_address=Migraatio/kloonaus URL-osoitteesta migrate.github_token_desc=Voit lisätä tähän yhden tai useamman tunnuksen pilkuilla erotettuna nopeuttaaksesi migraatiota kiertämällä GitHub API:n nopeusrajoituksen. Varoitus: Tämän ominaisuuden väärinkäyttö voi rikkoa palveluntarjoajan käytäntöä ja johtaa tiliesi sulkemiseen. @@ -1066,6 +1088,8 @@ migrate.failed=Migraatio epäonnistui: %v migrate.migrate_items_options=Lisäkohteiden migraatiota varten vaaditaan pääsypoletti migrate.migrating=Suoritetaan migraatio lähteestä %s … migrate.migrating_failed=Migraatio lähteestä %s epäonnistui. +migrate.migrating_git=Suoritetaan Git-datan migraatiota + mirror_from=peili kohteelle forked_from=forkattu tietovarastosta unwatch=Lopeta tarkkailu @@ -1659,6 +1683,7 @@ fork_to_different_account = Forkkaa toiselle tilille release.compare = Vertaa release.ahead.commits = %d kommittia all_branches = Kaikki haarat +n_tag_few = %s tagia settings.event_fork_desc = Tietovarasto forkattu. actions = Toiminnat fork_guest_user = Kirjaudu sisään forkataksesi tämän tietovaraston. @@ -1667,16 +1692,19 @@ visibility_fork_helper = (Tämän muuttaminen vaikuttaa kaikkien forkkien näkyv fork = Forkkaa activity.git_stats_commit_n = %d kommittia commits.search_branch = Tämä haara +n_branch_few = %s haaraa pulls.show_all_commits = Näytä kaikki kommitit commit_graph.select = Valitse haarat activity.navbar.recent_commits = Viimeaikaiset kommitit settings.branches.add_new_rule = Lisää uusi sääntö +n_commit_few = %s kommittia issues.force_push_compare = Vertaa commits.desc = Selaa lähdekoodin muutoshistoriaa. clone_helper = Tarvitseko apua kloonauksen kanssa? Siirry tukisivulle. settings.mirror_settings.push_mirror.copy_public_key = Kopioi julkinen avain object_format = Objektimuoto editor.fail_to_update_file_summary = Virheviesti: +n_branch_one = %s haara issues.content_history.delete_from_history_confirm = Poistetaanko historiasta? editor.new_patch = Uusi paikkaus pulls.merged_success = Vetopyyntö yhdistetty onnistuneesti ja suljettu @@ -1726,6 +1754,8 @@ issues.dependency.add_error_dep_exists = Riippuvuus on jo olemassa. wiki.page_content = Sivun sisältö wiki.page_title = Sivun otsikko activity.navbar.contributors = Avustajat +n_release_few = %s julkaisua +n_release_one = %s julkaisu symbolic_link = Symbolinen linkki mirror_last_synced = Viimeksi synkronoitu find_file.go_to_file = Löydä tiedosto @@ -1848,6 +1878,7 @@ issues.filter_label_select_no_label = Ei nimilappua projects.column.set_default = Aseta oletukseksi projects.edit_success = Projekti "%s" on päivitetty. desc.sha256 = SHA256 +n_commit_one = %s kommitti transfer.accept = Hyväksy siirto transfer.reject = Hylkää siirto default_branch_label = oletus @@ -1904,6 +1935,7 @@ milestones.deletion = Poista merkkipaalu more_operations = Lisää toimintoja settings.branches.switch_default_branch = Vaihda oletushaara tag.confirm_create_tag = Luo tagi +n_tag_one = %s tagi branch.renamed = Haaran %s uudeksi nimeksi asetettiin %s. release.tag_name_protected = Tagin nimi on suojattu. pulls.merge_conflict_summary = Virheviesti @@ -1956,6 +1988,7 @@ milestones.filter_sort.earliest_due_data = Lähin eräpäivä issues.filter_type.reviewed_by_you = Katselmoitu toimestasi settings.units.overview = Yleisnäkymä settings.remove_team_success = Tiimin pääsy tietovarastoon on poistettu. +migrate.cancel_migrating_confirm = Haluatko perua tämän migraation? settings.units.units = Yksiköt settings.update_settings_no_unit = Tietovaraston tulisi sallia edes jonkinlainen vuorovaikutus. settings.units.add_more = Ota lisää käyttöön @@ -2029,6 +2062,7 @@ release.title_empty = Nimi ei voi olla tyhjä. archive.title = Tämä tietovarasto on arkistoitu. Voit tarkastella sen tiedostoja ja kloonata sen, mutta et voi tehdä muutoksia sen tilaan, kuten tehdä työntöjä tai luoda uusia ongelmia, vetopyyntöjä tai kommentteja. reactions_more = ja %d lisää mirror_address = Kloonaa URL-osoitteesta +migrate_items_merge_requests = Yhdistämispyynnöt stars_remove_warning = Tämä poistaa kaikki tähdet tästä tietovarastosta. settings.webhook_deletion_desc = Webkoukun poistaminen poistaa sen asetukset ja toimitushistorian. Jatketaanko? settings.discord_icon_url.exceeds_max_length = Kuvakkeen URL-osoite voi sisältää enintään 2048 merkkiä @@ -2053,7 +2087,7 @@ settings.transfer_desc = Siirrä tämä tietovarasto käyttäjälle tai organisa settings.add_collaborator = Lisää avustaja settings.mirror_settings.push_mirror.none = Työntöpeilejä ei ole määritetty settings.collaborator_deletion_desc = Avustajan poistaminen peruuttaa hänen pääsynsä tähän tietovarastoon. Jatketaanko? -settings.archive.text = Tietovaraston arkistointi asettaa sen pelkkään lukutilaan. Se piilotetaan kojelaudalta. Kukaan ei voi tehdä (et edes sinä) uusia kommitteja, tai avata ongelmia tai vetopyyntöjä. Arkistoinnin syy on suositeltavaa kertoa, sillä kyseinen tieto auttaa tietovaraston haarauttamista harkitsevia kehittäjiä. +settings.archive.text = Tietovaraston arkistointi asettaa sen pelkkään lukutilaan. Se piilotetaan kojelaudalta. Kukaan ei voi tehdä (et edes sinä) uusia kommitteja, tai avata ongelmia tai vetopyyntöjä. settings.mirror_settings.docs = Määritä tietovarastosi synkronoimaan kommitit, tagit ja haarat automaattisesti toisen tietovaraston kanssa. settings.add_collaborator_duplicate = Avustaja on jo lisätty tähän tietovarastoon. settings.add_collaborator_blocked_them = Avustajaa ei voi lisätä, koska hän on estänyt tietovaraston omistajan. @@ -2197,6 +2231,8 @@ issues.dismiss_review_warning = Haluatko hylätä katselmoinnin? commit.operations = Toimenpiteet commits.view_single_diff = Näytä tässä kommitissa tähän tiedostoon kohdistuneet muutokset issues.choose.ignore_invalid_templates = Virheelliset mallipohjat on jätetty huomiotta +migrate.migrating_milestones = Suoritetaan merkkipaalujen migraatiota +migrate.migrating_issues = Suoritetaan ongelmien migraatiota migrate.clone_local_path = tai paikallisen palvelimen polku pulls.filter_changes_by_commit = Suodata kommitin perusteella pulls.show_changes_since_your_last_review = Näytä viimeisimmän katselmointisi jälkeiset muutokset @@ -2211,6 +2247,7 @@ migrate.invalid_lfs_endpoint = LFS-päätepiste ei ole kelvollinen. issues.new.clear_projects = Tyhjennä projektit mirror_denied_combination = Julkiseen avaimeen ja salasanaan pohjautuvaa todennusta ei voi käyttää yhdessä. template.git_content = Git-sisältö (Oletushaara) +migrate.migrating_releases = Suoritetaan julkaisujen migraatiota unit_disabled = Sivuston ylläpitäjä on poistanut käytöstä tämän tietovarasto-osion. issues.filter_sort.relevance = Asiaankuuluvuus pulls.reopen_to_merge = Avaa tämä vetopyyntö uudelleen suorittaaksesi yhdistämisen. @@ -2222,6 +2259,9 @@ issues.dismiss_review = Hylkää katselmointi editor.file_changed_while_editing = Tiedoston sisältö on muuttunut sen avaamisen jälkeen. Napsauta tästä nähdäksesi muutokset tai kommitoi muutokset uudelleen korvataksesi muutokset. sync_fork.button = Synkronoi migrated_from = Suoritettu migraatio lähteestä %[2]s +migrate.migrating_topics = Suoritetaan aiheiden migraatiota +migrate.migrating_pulls = Suoritetaan vetopyyntöjen migraatiota +migrate.cancel_migrating_title = Peruuta migraatio file_follow = Seuraa symbolista linkkiä commit.load_referencing_branches_and_tags = Lataa haarat ja tagit, jotka viittaavat tähän kommittiin editor.commit_id_not_matching = Tiedosto muuttui sillä aikaa, kun muokkasit sitä. Kommitoi uuteen haaraan ja yhdistä sen jälkeen. @@ -2245,6 +2285,7 @@ projects.column.deletion_desc = Projektin sarakkeen poistaminen siirtää kaikki issues.del_time = Poista tämä aikaloki migrated_from_fake = Suoritettu migraatio lähteestä %[1]s migrate.migrate = Tee migraatio lähteestä %s +migrate.migrating_labels = Suoritetaan nimilappujen migraatiota file_view_rendered = Näytä renderöitynä editor.invalid_commit_mail = Virheellinen sähköposti kommitin luomista varten. sync_fork.branch_behind_one = Tämä haara on %[1]d kommitin jäljessä %[2]s @@ -2518,13 +2559,19 @@ commitstatus.success = Onnistui projects.deletion_desc = Projektin poistaminen poistaa sen kaikilta siihen liittyviltä ongelmilta. Jatketaanko? issues.lock.unknown_reason = Ongelmaa ei voi lukita tuntemattomalla syyllä. issues.unlock_error = Ongelmaa, jota ei ole lukittu, ei voi avata lukituksesta. + + + issues.tracking_already_started = `Olet jo aloittanut ajanseurannan toisessa ongelmassa!` issues.cancel_tracking_history = `perui ajanseurannan %s` + summary_card_alt = Tietovaraston %s yhteenvetokortti migrate_options_lfs_endpoint.placeholder = Jos jätetty tyhjäksi, päätepiste johdetaan kloonaus-URL:stä broken_message = Tämän tietovaraston taustalla olevaa Git-dataa ei voi lukea. Ota yhteys tämän instanssin ylläpitoon tai poista tietovarasto. issues.edit.already_changed = Muutosten tallentaminen ongelmaan ei onnistu. Vaikuttaa siltä, että sisältöä on jo muutettu toisen käyttäjän toimesta. Päivitä sivu ja yritä muokata uudelleen välttääksesi muiden tekemien muutosten ylikirjoittamisen issues.choose.invalid_templates = Virheellisiä mallipohjia löytyi %v +issues.filter_assginee_no_assignee = Ei käsittelijää +issues.action_assignee_no_select = Ei käsittelijää issues.ref_issue_from = `viittasi tähän ongelmaan %[3]s %[1]s` issues.due_date_invalid = Eräpäivä on virheellinen tai ajanjakson ulkopuolella. Käytä muotoa "yyyy-mm-dd". issues.dependency.issue_close_blocked = Sinun täytyy sulkea kaikki tämän ongelman estävät ongelmat, ennen kuin voit sulkea tämän ongelman. @@ -2532,29 +2579,27 @@ issues.review.pending.tooltip = Tämä kommentti ei ole näkyvissä tällä hetk milestones.deletion_desc = Merkkipaalun poistaminen poistaa sen kaikista siihen liittyvistä ongelmista. Jatketaanko? activity.title.issues_closed_from = %s sulkenut %s settings.protected_branch_duplicate_rule_name = Tälle joukolle haaroja on jo olemassa sääntö -issues.filter_assginee_no_assignee = Ei käsittelijää -issues.action_assignee_no_select = Ei käsittelijää + +commit.contained_in = Tämä kommitti sisältyy haaraan: issues.ref_closing_from = `viittasi tähän ongelmaan vetopyynnöstä %[3]s, joka sulkee ongelman, %[1]s` issues.ref_reopening_from = `viittasi tähän ongelmaan vetopyynnöstä %[3]s, joka avaa ongelman uudelleen, %[1]s` -release.releases_for = Projektin %s julkaisut -release.tags_for = Projektin %s tagit -settings.wiki_branch_rename_success = Tietovaraston wikin haaranimi on normalisoitu onnistuneesti. -settings.wiki_branch_rename_failure = Tietovaraston wikin haaranimen normalisointi epäonnistui. -pulls.editable = Muokattavissa +issues.lock_no_reason = lukitsi ja rajoitti keskustelun avustajille %s issues.dependency.no_permission_1 = Sinulla ei ole oikeutta lukea %d riippuvuutta issues.dependency.no_permission_n = Sinulla ei ole oikeutta lukea %d riippuvuutta -issues.lock_no_reason = lukitsi ja rajoitti keskustelun avustajille %s -commit.contained_in = Tämä kommitti sisältyy haaraan: -diff.file_suppressed = Tiedoston eroavaisuutta ei näytetä, koska se on liian suuri +pulls.editable = Muokattavissa +settings.wiki_branch_rename_success = Tietovaraston wikin haaranimi on normalisoitu onnistuneesti. +settings.wiki_branch_rename_failure = Tietovaraston wikin haaranimen normalisointi epäonnistui. +release.releases_for = Projektin %s julkaisut +release.tags_for = Projektin %s tagit + size_format = %[1]s: %[2]s, %[3]s: %[4]s transfer.accept_desc = Siirrä taholle "%s" transfer.reject_desc = Peru siirto taholle "%s" +invisible_runes_description = `Tämä tiedosto sisältää näkymättömiä Unicode-merkkejä, joita ihminen ei huomaa, mutta jotka mahdollisesti käsitellään eri tavalla tietokoneen toimesta. Jos olet sitä mieltä, että tämä on tarkoituksellista, voit ohittaa tämän varoituksen. Käytä eskapointipainiketta näyttääksesi ne.` ambiguous_runes_description = `Tämä tiedosto sisältää Unicode-merkkejä, jotka voi olla virheellisesti yhdistettävissä toisiin merkkeihin. Jos olet sitä mieltä, että tämä on tarkoituksellista, voit ohittaa tämän varoituksen. Käytä eskapointipainiketta näyttääksesi ne.` escape_control_characters = Eskapoi unescape_control_characters = Poista eskapointi -invisible_runes_description = `Tämä tiedosto sisältää näkymättömiä Unicode-merkkejä, joita ihminen ei huomaa, mutta jotka mahdollisesti käsitellään eri tavalla tietokoneen toimesta. Jos olet sitä mieltä, että tämä on tarkoituksellista, voit ohittaa tämän varoituksen. Käytä eskapointipainiketta näyttääksesi ne.` - - +diff.file_suppressed = Tiedoston eroavaisuutta ei näytetä, koska se on liian suuri [graphs] component_loading_info = Tämä saattaa kestää hetken… @@ -2679,8 +2724,9 @@ settings.change_orgname_redirect_prompt.with_cooldown.one = Vanha organisaation settings.change_orgname_redirect_prompt.with_cooldown.few = Vanha organisaation nimi on kenen tahansa saatavilla %[1]d päivän suojaamisjakson jälkeen. Voit palauttaa organisaation nimen itsellesi suojaamisjakson aikana. teams.all_repositories_helper = Tiimillä on pääsy kaikkiin tietovarastoihin. Tämän valitseminen lisää kaikki olemassa olevat tietovarastot tiimiin. settings.labels_desc = Lisää nimilappuja, joita voi käyttää tämän organisaation kaikkien tietovarastojen ongelmissa. -teams.invite_team_member = Kutsu tiimiin %s + teams.general_access_helper = Jäsenten oikeudet päätetään alla olevan käyttöoikeustaulun perusteella. +teams.invite_team_member = Kutsu tiimiin %s [admin] dashboard=Kojelauta @@ -2704,6 +2750,33 @@ dashboard.operation_switch=Vaihda dashboard.operation_run=Suorita dashboard.delete_inactive_accounts=Poista kaikki aktivoimattomat tilit dashboard.delete_repo_archives=Poista kaikki tietovarastojen arkistot (ZIP, TAR.GZ, jne.) +dashboard.server_uptime=Palvelimen uptime +dashboard.current_goroutine=Nykyiset goroutinet +dashboard.current_memory_usage=Nykyinen muistinkäyttö +dashboard.total_memory_allocated=Yhteensä muistia varattu +dashboard.memory_obtained=Muistia saatu +dashboard.pointer_lookup_times=Osoittimen hakuajat +dashboard.current_heap_usage=Nykyinen heapin käyttö +dashboard.heap_memory_obtained=Heap-muisti saatu +dashboard.heap_memory_idle=Heap-muisti tyhjäkäynnillä +dashboard.heap_memory_in_use=Heap-muisti käytössä +dashboard.heap_memory_released=Heap-muisti vapautettu +dashboard.heap_objects=Heap-objektit +dashboard.bootstrap_stack_usage=Bootstrap-pinon käyttö +dashboard.stack_memory_obtained=Pinonmuisti saatu +dashboard.mspan_structures_usage=MSpan-rakenteiden käyttö +dashboard.mspan_structures_obtained=MSpan-rakenteet saatu +dashboard.mcache_structures_usage=MCache-rakenteiden käyttö +dashboard.mcache_structures_obtained=MCache-rakenteet saatu +dashboard.profiling_bucket_hash_table_obtained=Profilointiämpäritiivistetaulukko saatu +dashboard.gc_metadata_obtained=Roskienkeruumetatiedot saatu +dashboard.other_system_allocation_obtained=Muu järjestestelmän varaus saatu +dashboard.next_gc_recycle=Seuraava roskienkeruukierrätys +dashboard.last_gc_time=Aika edellisestä roskienkeruusta +dashboard.total_gc_pause=Yhteensä roskienkeruutauko +dashboard.last_gc_pause=Viimeinen roskienkeruutauko +dashboard.gc_times=Roskienkeruuajat + users.user_manage_panel=Käyttäjätilien hallinta users.new_account=Luo käyttäjätili users.name=Käyttäjänimi @@ -2731,6 +2804,19 @@ users.allow_git_hook=Voi luoda Git-koukkuja users.allow_create_organization=Voi luoda organisaatioita users.update_profile=Päivitä käyttäjätili users.delete_account=Poista käyttäjätili +users.list_status_filter.menu_text=Suodata +users.list_status_filter.reset=Tyhjennä +users.list_status_filter.is_active=Aktiivinen +users.list_status_filter.not_active=Ei-aktiivinen +users.list_status_filter.is_admin=Ylläpitäjä +users.list_status_filter.not_admin=Ei ylläpitäjä +users.list_status_filter.is_restricted=Rajoitettu +users.list_status_filter.not_restricted=Ei rajoitettu +users.list_status_filter.is_prohibit_login=Kirjautuminen estetty +users.list_status_filter.not_prohibit_login=Kirjautuminen sallittu +users.list_status_filter.is_2fa_enabled=2FA käytössä +users.list_status_filter.not_2fa_enabled=2FA ei käytössä + emails.email_manage_panel=Käyttäjien sähköpostien hallinta emails.primary=Ensisijainen emails.activated=Aktivoitu @@ -2885,6 +2971,12 @@ monitor.desc=Kuvaus monitor.start=Alkamisaika monitor.execute_time=Suoritusaika +monitor.queues=Jonot +monitor.queue=Jono: %s +monitor.queue.name=Nimi +monitor.queue.type=Tyyppi +monitor.queue.settings.submit=Päivitä asetukset + notices.system_notice_list=Järjestelmän ilmoitukset notices.select_all=Valitse kaikki notices.deselect_all=Poista kaikki valinnat @@ -2914,6 +3006,7 @@ users.new_success = Käyttäjätili "%s" on luotu. config.disable_register = Poista itserekisteröinti käytöstä config.enable_openid_signin = Ota OpenID-kirjautuminen käyttöön config.enable_openid_signup = Ota OpenID-itserekisteröinti käyttöön +monitor.queue.settings.changed = Asetukset päivitetty config.db_schema = Skeema settings = Ylläpitäjän asetukset emails.delete = Poista sähköpostiosoite @@ -2936,6 +3029,7 @@ auths.oauth2_provider = OAuth2-palveluntarjoaja auths.tips.gmail_settings = Gmail-asetukset: config.mailer_sendmail_path = Sendmail-polku config_settings = Asetukset +monitor.queue.settings.remove_all_items = Poista kaikki config.skip_tls_verify = Ohita TLS-vahvistus dashboard.new_version_hint = Forgejo %s on nyt saatavilla. Käytössäsi on %s. Lue lisätietoja blogista. defaulthooks.add_webhook = Lisää oletusarvoinen webkoukku @@ -3037,6 +3131,7 @@ dashboard.update_checker = Päivitysten tarkistaja auths.allowed_domains_helper = Jätä tyhjäksi salliaksesi kaikki verkkotunnukset. Erota useat verkkotunnukset pilkulla (","). auths.activated = Tämä todennuslähde on aktivoitu auths.login_source_of_type_exist = Tätä tyyppiä oleva todennuslähde on jo olemassa. +dashboard.memory_allocate_times = Muistiallokaatiot users.send_register_notify = Ilmoita rekisteröitymisestä sähköpostitse config.offline_mode = Paikallinen tila config.cache_item_ttl = Välimuistitietueen TTL @@ -3067,18 +3162,21 @@ monitor.process.cancel_notices = Perutaanko: %s? config.enable_federated_avatar = Ota federoidut profiilikuvat käyttöön notices.operations = Toimenpiteet config.xorm_log_sql = Lokita SQL +monitor.queue.settings.remove_all_items_done = Kaikki jonossa olleet tietueet on poistettu. config.logger_name_fmt = Lokittaja: %s config.git_max_diff_line_characters = Diff-merkkejä enintään riviä kohden config.git_max_diff_files = Diff-tiedostoja enintään näytettäväksi config.access_log_mode = Pääsylokin tila config.picture_config = Kuvan ja avatarin asetukset notices.delete_success = Järjestelmäilmoitukset on poistettu. +monitor.queue.settings.maxnumberworkers.placeholder = Tällä hetkellä %[1]d auths.force_smtps_helper = SMTPS:ää käytetään aina portissa 465. Aseta tämä pakottaaksesi SMTPS toisiin portteihin. (Muuten STARTTLS:ää käytetään toisiin portteihin, jos palvelin tukee sitä.) dashboard.resync_all_sshprincipals = Päivitä ".ssh/authorized_principals"-tiedosto Forgejon SSH-prinsipaaleilla. + + auths.verify_group_membership = Vahvista ryhmäjäsenyys LDAP:issa (jätä suodatin tyhjäksi ohittaaksesi) auths.oauth2_map_group_to_team_removal = Poista käyttäjät synkronoiduista tiimeistä, jos käyttäjä ei kuulu vastaavaan ryhmään. - [action] create_repo=loi tietovaraston %s rename_repo=asetti tietovaraston %[1]s uudeksi nimeksi %[3]s @@ -3128,10 +3226,35 @@ raw_seconds=sekuntia raw_minutes=minuuttia [dropzone] +default_message=Pudota tiedostot tähän tai napsauta tästä lähettääksesi tiedoston. +invalid_input_type=Tätä tyyppiä olevia tiedostoja ei voi lähettää. +remove_file=Poista tiedosto +file_too_big = Tiedoston koko ({{filesize}} Mt) ylittää enimmäisrajan ({{maxFilesize}} Mt). [notification] +notifications=Ilmoitukset +unread=Lukematon +read=Luettu +no_unread=Ei lukemattomia ilmoituksia. +no_read=Ei luettuja ilmoituksia. +pin=Kiinnitä ilmoitus +mark_as_read=Merkitse luetuksi +mark_as_unread=Merkitse lukemattomaksi +mark_all_as_read=Merkitse kaikki luetuiksi +watching = Tarkkaillaan +no_subscriptions = Ei tilauksia +subscriptions = Tilaukset [gpg] +error.no_committer_account=Kommitin tekijän sähköpostiosoitteeseen ei ole linkitetty tiliä +error.not_signed_commit=Kommitti ei ole allekirjoitettu +error.extract_sign = Allekirjoituksen purkaminen epäonnistui +default_key = Allekirjoitettu oletusavaimella +error.failed_retrieval_gpg_keys = Ei saatu yhtäkään kommitin tekijän tiliin liitettyä avainta +error.generate_hash = Kommitin tiivisteen luominen epäonnistui +error.probable_bad_signature = VAROITUS! Vaikka tietokannassa on avain tällä ID-tunnistella, se ei vahvista tätä kommittia! Tämä kommitti on EPÄILYTTÄVÄ. +error.probable_bad_default_signature = VAROITUS! Vaikka oletusavaimella on tämä ID-tunniste, se ei vahvista tätä kommittia! Tämä kommitti on EPÄILYTTÄVÄ. +error.no_gpg_keys_found = Tälle allekirjoitukselle ei löytynyt tunnettua avainta tietokannasta [units] unit = Yksikkö @@ -3139,11 +3262,163 @@ error.unit_not_allowed = Sinulla ei ole pääsyä tähän tietovaraston osioon. error.no_unit_allowed_repo = Sinulla ei ole pääsyä mihinkään tämän tietovaraston osioon. [packages] +title=Paketit desc=Hallitse tietovaraston paketteja. +empty=Täällä ei vielä ole paketteja. +filter.type=Tyyppi +filter.type.all=Kaikki +filter.no_result=Suodattimesi ei tuottanut tuloksia. +installation=Asennus +details.author=Tekijä +alpine.repository.branches=Haarat +alpine.repository.repositories=Tietovarastot conan.details.repository=Tietovarasto owner.settings.cleanuprules.enabled=Käytössä +details.license = Lisenssi +about = Tietoja tästä paketista +debian.install = Asenna paketti komennolla: +owner.settings.cleanuprules.edit = Muokkaa siivoussääntöä +arch.version.groups = Ryhmä +details.project_site = Projektin verkkosivusto +details.repository_site = Tietovaraston verkkosivusto +container.pull = Vedä levykuva komentoriviltä: +generic.download = Lataa paketti komentoriviltä: +dependency.version = Versio +keywords = Avainsanat +dependencies = Riippuvuudet +container.labels.key = Avain +container.labels.value = Arvo +pypi.install = Asenna paketti pipillä komennolla: +npm.install = Asenna paketti npm:llä komennolla: +npm.install2 = tai lisää se package.json-tiedostoon: +empty.documentation = Lisätietoja pakettirekisteristä on saatavilla dokumentaatiossa. +helm.install = Asenna paketti komennolla: +owner.settings.chef.keypair = Luo avainpari +settings.delete.error = Paketin poistaminen epäonnistui. +requirements = Vaatimukset +published_by_in = Julkaistu %[1]s, julkaisija %[3]s tietovarastossa %[5]s +pypi.requires = Vaatii Pythonin +alpine.install = Asenna paketti seuraavalla komennolla: +debian.repository.components = Komponentit +cran.install = Asenna paketti komennolla: +settings.link.select = Valitse tietovarasto +owner.settings.chef.title = Chef-rekisteri +owner.settings.cleanuprules.add = Lisää siivoussääntö +versions = Versiot +versions.view_all = Näytä kaikki +debian.repository.architectures = Arkkitehtuurit +container.details.type = Levykuvan tyyppi +arch.version.properties = Version ominaisuudet +rpm.install = Asenna paketti komennolla: +owner.settings.cleanuprules.none = Siivoussääntöjä ei vielä ole. +container.details.platform = Alusta +npm.dependencies = Riippuvuudet +owner.settings.cleanuprules.title = Siivoussäännöt +arch.version.depends = Riippuu +settings.delete = Poista paketti +arch.version.description = Kuvaus +settings.delete.success = Paketti on poistettu. +npm.dependencies.optional = Valinnaiset riippuvuudet +debian.repository.distributions = Jakelut +composer.dependencies = Riippuvuudet +chef.install = Asenna paketti komennolla: +details.documentation_site = Dokumentaation verkkosivusto +go.install = Asenna paketti komentoriviltä: +alpine.repository.architectures = Arkkitehtuurit +composer.registry = Määritä tämä rekisteri ~/.composer/config.json-tiedostossa: +debian.registry = Määritä tämä rekisteri komentoriviltä: +rpm.registry = Määritä rekisteri komentoriviltä: +maven.install = Käytä pakettia sisällyttämällä seuraava sisältö dependencies-lohkoon pom.xml-tiedostossa: +npm.registry = Määritä rekisteri projektin .npmrc-tiedostossa: +alpine.repository = Tietovaraston tiedot +cargo.registry = Määritä tämä rekisteri Cargon asetustiedostossa (esimerkiksi ~/.cargo/config.toml): +cargo.install = Asenna paketti Cargolla suorittamalla seuraava komento: +composer.install = Asenna paketti Composerilla suorittamalla komento: +rpm.distros.redhat = RedHatiin pohjautuvilla jakeluilla +rpm.distros.suse = SUSE:en pohjautuvilla jakeluilla +rpm.repository.architectures = Arkkitehtuurit +cran.registry = Määritä rekisteri Rprofile.site-tiedostossa: +swift.install2 = ja suorita komento: +maven.registry = Määritä tämä rekisteri projektin pom.xml-tiedostossa: +maven.install2 = Suorita komentoriviltä: +nuget.registry = Määritä rekisteri komentoriviltä: +nuget.install = Asenna paketti NuGetillä suorittamalla komento: +rubygems.install = Asenna paketti gemillä suorittamalla komento: +rubygems.install2 = tai lisää se Gemfileen: +swift.registry = Määritä rekisteri komentoriviltä: +swift.install = Lisää paketti Package.swift-tiedostoon: owner.settings.cleanuprules.keep.count.1 = 1 versio per paketti owner.settings.cleanuprules.keep.count.n = %d versiota per paketti +conan.install = Asenna paketti Conanilla suorittamalla komento: +chef.registry = Määritä tämä rekisteri ~/.chef/config.rb-tiedostossa: +conan.registry = Määritä tämä rekisteri komentoriviltä: +conda.install = Asenna paketti Condalla suorittamalla komento: +helm.registry = Määritä tämä rekisteri komentoriviltä: +pub.install = Asenna paketti Dartilla suorittamalla komento: +owner.settings.cargo.title = Cargo-rekisterin indeksi +settings.delete.description = Paketin poistaminen on peruuttamaton toimenpide, sitä ei voi perua. +settings.link.success = Tietovaraston linkki päivitettiin onnistuneesti. +settings.link.button = Päivitä tietovaraston linkki +owner.settings.cleanuprules.preview.overview = %d pakettia on ajastettu poistettavaksi. +owner.settings.cargo.initialize.success = Cargo-indeksi luotiin onnistuneesti. +vagrant.install = Lisää Vagrant-boksi suorittamalla komento: +rubygems.dependencies.development = Kehitysriippuvuudet +owner.settings.cleanuprules.preview = Siivoussäännön esikatselu +npm.dependencies.development = Kehitysriippuvuudet +composer.dependencies.development = Kehitysriippuvuudet +owner.settings.cleanuprules.success.update = Siivoussääntö on päivitetty. +owner.settings.cleanuprules.success.delete = Siivoussääntö on poistettu. +settings.link = Linkitä tämä paketti tietovarastoon +maven.download = Lataa riippuvuus suorittamalla komentorivillä: +registry.documentation = Lisätietoja %s-rekisteristä on dokumentaatiossa. +owner.settings.chef.keypair.description = Chef-rekisteriin lähetettävät pyynnöt on allekirjoitettava salauskirjoituksella todennuskeinona. Avainparia luotaessa vain julkinen avain tallennetaan Forgejoon. Yksityinen avain toimitetaan sinulle käytettäväksi knifen kanssa. Uuden avainparin luominen korvaa edellisen. +owner.settings.cleanuprules.keep.pattern = Säilytä kaavaa vastaavat versiot +owner.settings.cleanuprules.pattern_full_match = Toteuta kaava paketin koko nimeen +owner.settings.cleanuprules.keep.title = Näitä sääntöjä vastaavat versiot säilytetään, vaikka ne vastaisivat alla olevaa poistosääntöä. +owner.settings.cleanuprules.keep.count = Säilytä viimeisimmät +owner.settings.cleanuprules.remove.pattern = Poista kaavaa vastaavat versiot +owner.settings.cleanuprules.keep.pattern.container = Viimeisin (latest) versio säilytetään aina Container-paketeista. +owner.settings.cleanuprules.remove.title = Näitä sääntöjä vastaavat versiot poistetaan, ellei sääntö yläpuolella käske säilyttää niitä. +owner.settings.cleanuprules.remove.days = Poista versiot, jotka ovat vanhempia kuin +arch.pacman.helper.gpg = Lisää luottamusvarmenne pacmanille: +arch.pacman.sync = Synkronoi paketti pacmanin kanssa: +debian.registry.info = Valitse $distribution ja $component alla olevasta listasta. +rpm.repository.multiple_groups = Tämä paketti on saatavilla useissa ryhmissä. +owner.settings.cargo.rebuild.success = Cargo-indeksi rakennettiin uudelleen. +owner.settings.cleanuprules.preview.none = Siivoussääntö ei vastaa yhtäkään pakettia. +arch.pacman.conf = Lisää palvelin asiaan liittyvällä jakelulla ja arkkitehtuurilla tiedostoon /etc/pacman.conf : +published_by = Julkaistu %[1]s käyttäjän %[3]s toimesta +alpine.registry.key = Lataa rekisterin julkinen RSA-avain hakemistoon /etc/apk/keys/ vahvistaaksesi indeksin allekirjoituksen: +alpine.registry = Aseta tämä rekisteri lisäämällä URL-osoite tiedostoon /etc/apk/tietovarastot: +rubygems.dependencies.runtime = Ajonaikaiset riippuvuudet +owner.settings.cargo.rebuild.error = Cargo-indeksin rakentaminen uudelleen epäonnistui: %v +owner.settings.cargo.rebuild = Rakenna indeksi uudelleen +empty.repo = Lähetitkö paketin, mutta se ei näy täällä? Siirry paketin asetuksiin ja linkitä se tähän tietovarastoon. +alpine.registry.info = Valitse $branch ja $repository alla olevasta listasta. +container.images.title = Levykuvat +owner.settings.cargo.initialize = Alusta indeksi +owner.settings.cargo.initialize.description = Cargo-rekisterin käyttöä varten tarvitaan erityinen indeksi Git -tietovarasto. Tämän vaihtoehdon käyttäminen luo (uudelleen) tietovaraston ja määrittää sen automaattisesti. +settings.link.error = Tietovaraston linkin päivittäminen epäonnistui. +alt.repository.multiple_groups = Tämä paketti on saatavilla useissa ryhmissä. +alt.repository.architectures = Arkkitehtuurit +alt.install = Asenna paketti +alt.registry.install = Asenna paketti komennolla: +details = Yksityiskohdat +arch.version.provides = Tarjoaa +rpm.repository = Tietovaraston tiedot +rubygems.required.ruby = Vaatii Ruby-version +settings.delete.notice = Olet aikeissa poistaa %s (%s). Tätä toimenpidettä ei voi perua. Haluatko varmasti jatkaa? +owner.settings.cargo.initialize.error = Cargo-indeksin alustaminen epäonnistui: %v +owner.settings.cargo.rebuild.no_index = Ei voi rakentaa uudelleen, indeksiä ei ole alustettu. +rubygems.required.rubygems = Vaatii RubyGem-version +alt.registry = Määritä tämä rekisteri komentoriviltä: +alt.repository = Tietovaraston tiedot +arch.version.replaces = Korvaa +debian.repository = Tietovaraston tiedot +conda.registry = Määritä tämä rekisteri Conda-tietovarastoksi .condarc-tiedostossa: +container.labels = Nimilaput +settings.link.description = Jos linkität paketin tietovarastoon, paketti listataan tietovaraston pakettilistalla. +assets = Resurssit [secrets] creation.failed = Salaisuuden lisääminen epäonnistui. @@ -3161,27 +3436,105 @@ creation.name_placeholder = kirjoinkoolla ei merkitystä, vain aakkosnumeerisia creation.value_placeholder = Syötä mitä tahansa sisältöä. Tyhjätila alussa ja lopussa jätetään huomiotta. [actions] +runners.name=Nimi +runners.owner_type=Tyyppi +runners.description=Kuvaus +runners.task_list.run=Suorita +runners.task_list.repository=Tietovarasto +runners.task_list.commit=Kommitti + +runs.commit=Kommitti +status.success = Onnistunut +status.unknown = Tuntematon +status.waiting = Odotustilassa +status.running = Käynnissä +status.blocked = Estetty +status.failure = Epäonnistunut +status.cancelled = Peruttu +status.skipped = Ohitettu +runners.none = Testinajajia ei saatavilla +runners.status.unspecified = Tuntematon +runners.update_runner = Päivitä muutokset +runners.edit_runner = Muokkaa testinajajaa +runners.update_runner_success = Testinajaja päivitetty onnistuneesti +runners.delete_runner_success = Testinajaja poistettu onnistuneesti +runners.reset_registration_token = Uudelleenaseta rekisteröintipoletti +runs.scheduled = Ajastettu +runs.status = Tila runs.empty_commit_message = (tyhjä kommittiviesti) +variables.deletion = Poista muuttuja +runners.new_notice = Testinajajan aloitusohjeet workflow.dispatch.input_required = Arvo syötteelle "%s" vaadittu. +runners.status.active = Aktiivinen +variables.description = Muuttujat asetetaan tietyille toiminnoille eikä niitä voida lukea muutoin. +runners.labels = Nimilaput +runners.delete_runner_failed = Testinajajan poisto epäonnistui +runners.delete_runner_header = Varmista testinajajan poisto +runners.task_list.status = Tila +runners.reset_registration_token_success = Testiajajan rekisteröintipoletti asetettu uudelleen onnistuneesti +variables.none = Ei muuttujia vielä. +runners.id = Tunniste +runners.status = Tila +runners.task_list = Ajajan viimeisimmät tehtävät +runners.task_list.no_tasks = Tehtäviä ei ole vielä määritelty. +runners.last_online = Viimeisin käynnissäoloajankohta +runners.runner_title = Testinajaja +runners.task_list.done_at = Valmistunut ajankohtana +runs.no_matching_online_runner_helper = Testiajajaa nimilapulla %s ei löytynyt +runs.no_results = Ei tuloksia. +runners.delete_runner = Poista testinajaja +variables.deletion.description = Muuttujan poistaminen on lopullista, eikä sitä voi perua. Jatketaanko? workflow.dispatch.invalid_input_type = Syötetyyppi "%s" ei kelpaa. workflow.dispatch.warn_input_limit = Näytetään vain ensimmäiset %d syötettä. +runners.runner_manage_panel = Hallinnoi testinajajia +variables = Muuttujat +variables.management = Hallitse muuttujia +variables.creation = Lisää muuttuja +runners.new = Luo uusi testinajaja +runners.version = Versio runs.expire_log_message = Lokitiedostot on tyhjätty vanhenemisen vuoksi. +runners.delete_runner_notice = Jos tehtävä on käynnissä tällä testinajajalla, se lopetetaan ja merkitään epäonnistuneeksi. Se voi rikkoa koonnin työnkulun. +runners.update_runner_failed = Testinajajan päivitys epäonnistui +variables.deletion.success = Muuttuja poistettu. +variables.edit = Muokkaa muuttujaa +variables.creation.success = Muuttuja "%s" lisätty. +variables.deletion.failed = Muuttujan poisto epäonnistui. +variables.creation.failed = Muuttujan lisäys epäonnistui. +variables.update.failed = Muuttujan muokkaus epäonnistui. +variables.update.success = Muuttuja muokattu. variables.id_not_exist = Muuttujaa tunnisteella %d ei ole olemassa. +runs.all_workflows = Kaikki työnkulut workflow.dispatch.run = Suorita työnkulku workflow.enable = Ota työnkulku käyttöön +runs.no_workflows = Ei työnkulkuja vielä. +runs.actors_no_select = Kaikki toimijat +runs.workflow = Työnkulku workflow.enable_success = Työnkulku "%s" on otettu käyttöön. workflow.disabled = Työnkulku on poistettu käytöstä. +runs.actor = Toimija workflow.disable = Poista työnkulku käytöstä workflow.disable_success = Työnkulku "%s" on poistettu käytöstä. +runs.no_job = Työnkulun tulee sisältää vähintään yksi työ +runs.invalid_workflow_helper = Työnkulun asetustiedosto on virheellinen. Tarkista asetustiedosto: %s +runners = Ajajat +actions = Actions unit.desc = Hallitse integroituja CI-/CD-putkia Forgejo Actionsia hyödyntäen. +runs.pushed_by = työntänyt runs.no_workflows.help_no_write_access = Lisätietoja Forgejo Actionsista on saatavilla dokumentaatiosta. +runners.status.idle = Jouten +runners.status.offline = Ei-verkkotilassa +runs.no_job_without_needs = Työnkulun tulee sisältää vähintään yksi työ ilman riippuvuuksia. runs.no_runs = Työnkululla ei ole vielä suorituksia. +variables.not_found = Muuttujaa ei löytynyt. runs.no_workflows.help_write_access = Etkö tiedä, miten aloittaa Forgejo Actionsin käyttö? Lue pikaopas kirjoittaaksesi ensimmäisen työnkulun, sen jälkeen määritä Forgejo-ajaja suorittamaan asettamiasi töitä. -workflow.dispatch.success = Työnkulun suoritusta pyydettiin onnistuneesti. + + + + +runs.status_no_select = Kaikki tilat + workflow.dispatch.trigger_found = Tällä työnkululla on workflow_dispatch-tapahtumaliipaisin. - - - +workflow.dispatch.success = Työnkulun suoritusta pyydettiin onnistuneesti. [projects] type-1.display_name = Yksittäinen projekti @@ -3236,6 +3589,7 @@ projects.read = Lue: Pääsy tietovaraston projektitauluille. wiki.write = Kirjoita: Luo, päivitä ja poista integroidun wikin sivuja. [markup] +filepreview.truncated = Esikatselu on typistetty [translation_meta] test = Tämä on testimerkkijono. Sitä ei näytetä Forgejo-käyttöliittymässä, mutta sitä käytetään testaustarkoituksiin. Voit vapaasti kirjoittaa "selvä" säästääksesi aikaa (tai käyttää hauskaa faktaa) ja saavuttaaksesi sen makean 100 %:n valmistumisrajan :) \ No newline at end of file diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 68a6efddee..051399a472 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -43,6 +43,9 @@ sign_up = Magrehistro link_account = Mag-link ng account template = Template tracked_time_summary = Buod ng mga nakasubaybay na oras base sa filter ng listahan ng isyu +webauthn_sign_in = Pindutin ang button ng iyong security key. Kung walang button ang iyong security key, ilagay muli. +webauthn_error_insecure = Sinusuportahan lamang ng WebAuthn ang mga secure na koneksyon. Para sa pagsubok sa HTTP, pwede mo gamitin ang origin na "localhost" o "127.0.0.1" +webauthn_error_timeout = Naabot ang timeout bago mabasa ang iyong key. Mangyaring i-reload ang page na ito at subukan muli. view = Itignan disabled = Naka-disable copy_url = Kopyahin ang URL @@ -61,6 +64,15 @@ re_type = Kumpirmahin ang password captcha = CAPTCHA twofa = Authentikasyong two-factor passcode = Passcode +webauthn_insert_key = Ilagay ang iyong security key +webauthn_press_button = Pindutin ang button ng iyong security key… +webauthn_use_twofa = Gumamit ng two-factor code galing sa iyong telepono +webauthn_error = Hindi mabasa ang iyong security key. +webauthn_unsupported_browser = Kasalukuyang hindi sinusuportahan ng iyong browser ang WebAuthn. +webauthn_error_unknown = May nangyaring hindi alam na error. Magyaring subukan muli. +webauthn_error_unable_to_process = Hindi maproseso ng server ang iyong hiling. +webauthn_error_duplicated = Hindi pinapayagan ang security key na ito para sa hiling na ito. Mangyaring siguraduhin na ang key na ito ay hindi ito nakarehistro na. +webauthn_error_empty = Kailangan mong maglapat ng pangalan para sa key na ito. repository = Repositoryo organization = Organisasyon mirror = Salamin @@ -303,7 +315,7 @@ buttons.bold.tooltip = Magdagdag ng bold na text (Ctrl+B / ⌘B) buttons.italic.tooltip = Magdagdag ng italic text (Ctrl+I / ⌘I) buttons.quote.tooltip = I-quote ang text buttons.code.tooltip = Magdagdag ng code -buttons.link.tooltip = Magdagdag ng link (Ctrl+K / ⌘K) +buttons.link.tooltip = Magdagdag ng link buttons.list.unordered.tooltip = Magdagdag ng bullet na listahan buttons.list.task.tooltip = Magdagdag ng listahan ng mga gawain buttons.mention.tooltip = Magmensyon ng user o koponan @@ -1079,6 +1091,7 @@ delete_preexisting = Burahin ang mga dating umiiral na file delete_preexisting_content = Burahin ang mga file sa %s tree_path_not_found_commit = Hindi umiiral ang path na %[1]s sa commit %[2]s tree_path_not_found_branch = Hindi umiiral ang daanang %[1]s sa branch %[2]s +migrate_items_pullrequests = Mga hiling sa paghila archive.title = Naka-archive ang repositoryong ito. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado ito, tulad ng pagtulak at paggawa ng mga isyu, pull request o mga komento. archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado nito, tulad ng pagtulak o paggawa ng mga bagong isyu, mga pull request, o komento. pulls = Mga hiling sa paghila @@ -1130,7 +1143,13 @@ form.name_pattern_not_allowed = Hindi pinapayagan ang pattern na "%s" sa pangala migrate_options_lfs = I-migrate ang mga LFS file migrate_options_lfs_endpoint.label = Endpoint ng LFS migrate_options_lfs_endpoint.placeholder = Kung iwanang blangko, ang endpoint ay magmumula sa clone URL +migrate_items_milestones = Mga milestone +migrate_items_labels = Mga label +migrate_items_issues = Mga isyu +migrate_items_merge_requests = Mga merge request migrate.clone_address = Magmigrate / Mag-clone mula sa URL +migrate_items = Mga item sa pagmigrate +migrate_items_releases = Mga paglabas migrate_repo = I-migrate ang repositoryo size_format = %[1]s: %[2]s, %[3]s: %[4]s template.git_hooks_tooltip = Kasalukuyang hindi mo mababago o matatanggal ang mga hook ng Git kapag nadagdag. Piliin lang ito kapag pinagkakatiwalaan mo ang template na repositoryo. @@ -1138,6 +1157,7 @@ template.webhooks = Mga webhook template.topics = Mga paksa template.issue_labels = Mga label ng isyu template.one_item = Kailangang pumili ng kahit isang template item +migrate_items_wiki = Wiki migrate.clone_local_path = o isang lokal na path ng server adopt_preexisting_success = Pinagtibay ang mga file at ginawa ang repositoryo mula sa %s delete_preexisting_success = Burahin ang mga hindi pinatibay na file sa %s @@ -1148,6 +1168,12 @@ migrate.invalid_local_path = Hindi wasto ang lokal na path. Hindi ito umiiral o migrate.invalid_lfs_endpoint = Hindi wasto ang LFS endpoint. migrate.migrating_failed = Nabigo ang pag-migrate mula sa %s. migrate.migrating_failed_no_addr = Nabigo ang pagmigrate. +migrate.migrating_topics = Nililipat ang mga paksa +migrate.migrating_milestones = Nililipat ang mga milestone +migrate.migrating_releases = Nililipat ang mga paglabas +migrate.migrating_pulls = Nililipat ang mga pull request +migrate.cancel_migrating_title = Kanselahin ang pag-migrate +migrate.cancel_migrating_confirm = Gusto mo bang kanselahin ang migration na ito? mirror_from = mirror ng forked_from = na-fork mula sa generated_from = na-generate mula sa @@ -1201,6 +1227,7 @@ from_comment = (komento) editor.add_file = Magdagdag ng file editor.upload_file = Mag-upload ng file editor.cannot_edit_lfs_files = Hindi mababago ang mga LFS file sa web interface. +migrate.migrating_issues = Nililipat ang mga isyu fork_from_self = Hindi ka makaka-fork ng repositoryo na minamay-ari mo. broken_message = Ang Git data na pinagbabatayan sa repositoryo na ito ay hindi mabasa. Makipag-ugnayan sa tagapangasiwa ng instansya na ito o burahin ang repositoryo na ito. file_history = Kasaysayan @@ -1208,6 +1235,7 @@ invisible_runes_header = `Nalalaman ng file na ito ng mga hindi nakikitang Unico file_too_large = Masyadong malaki ang file para ipakita. invisible_runes_description = `Ang file na ito ay naglalaman ng mga hindi nakikitang Unicode character na hindi nakikilala ng mga tao ngunit maaaring maproseso ng ibang paraan ng isang computer. Kung sa tingin mo ay sinasadya ito, maaari mong ligtas na hindi pansinin ang babala na ito. Gamitin ang I-escape na button para ipakita sila.` commit.contained_in_default_branch = Ang commit na ito ay bahagi ng default na branch +migrate.migrating_labels = Nililipat ang mga label filter_branch_and_tag = I-filter ang branch o tag clear_ref = `Burahin ang kasalukuyang sanggunian` actions = Mga Aksyon @@ -1220,6 +1248,12 @@ commits = Mga Commit file_permalink = Permalink migrate.migrating_failed.error = Nabigong magmigrate: %s unwatch = I-unwatch +n_commit_one = %s commit +n_commit_few = %s mga commit +n_branch_one = %s branch +n_branch_few = %s mga branch +n_tag_one = %s tag +n_tag_few = %s mga tag editor.cannot_edit_non_text_files = Hindi mababago ang mga binary file sa web interface. migrated_from = Na-migrate mula sa %[2]s migrated_from_fake = Na-migrate mula sa %[1]s @@ -1238,6 +1272,7 @@ unescape_control_characters = I-unescape invisible_runes_line = `Ang linya na ito ay may mga hindi nakikitang Unicode character` ambiguous_runes_line = `Ang linya na ito ay may mga hindi tiyak na Unicode character` tag = Tag +migrate.migrating_git = Nililipat ang Git data vendored = Naka-vendor editor.delete = Burahin ang %s editor.add = Idagdag ang %s @@ -1577,6 +1612,7 @@ issues.new.clear_assignees = I-clear ang mga mangangasiwa issues.new.no_assignees = Walang mga mangangasiwa issues.choose.invalid_config = Ang config ng isyu ay naglalaman ng mga error: issues.label_templates.info = Walang pang mga umiiral na label. Gumawa ng label gamit ang "Bagong label" o gumamit ng label preset: +n_release_one = %s release issues.action_label = Label issues.action_milestone = Milestone issues.action_milestone_no_select = Walang milestone @@ -1610,6 +1646,7 @@ issues.remove_project_at = `tinanggal ito mula sa %s na proyekto %s` issues.filter_label_select_no_label = Walang label issues.filter_sort.fewestforks = Pinakakaunting fork issues.add_assignee_at = `ay itinalaga ni/ng %s %s` +n_release_few = %s mga release issues.remove_ref_at = `tinanggal ang sangguni na %s %s` issues.add_ref_at = `idinagdag ang sangguni na %s %s` issues.filter_milestone = Milestone @@ -2441,7 +2478,7 @@ settings.chat_id = ID ng chat settings.matrix.message_type = Uri ng mensahe settings.tags.protection.allowed.noone = Walang sinuman settings.archive.header = I-archive ang repo na ito -settings.archive.text = Gagawing read-only ang buong repositoryo ang pag-archive. Itatago ito sa dashboard. Hindi magagawa ng anumang tao (kahit ikaw rin!) ang paggawa ng mga bagong commit at makapagbukas ng mga isyu at hiling sa paghila. Inirerekomenda ang pagdokumento ng dahilan ng pagka-archive para bigyan ng tagubilin ang mga developer na nagpaplanong i-fork ang repositoryo sa lalong madaling panahon. +settings.archive.text = Ang pag-archive ng repo ay gagawin itong buo na read-only. Itatago ito sa dashboard. Walang tao (kahit ikaw rin!) ay makakagawa ng bagong commit, o magbukas ng mga isyu o hiling sa paghila. settings.archive.error = May naganap na error habang sinusubukang i-archive ang repo. Tignan ang log para sa mga detalye. settings.archive.error_ismirror = Hindi ka makaka-archive ng naka-mirror na repo. settings.unarchive.button = I-unarchive ang repo @@ -2673,6 +2710,7 @@ emails.updated = Napalitan na ang email emails.not_updated = Nabigong baguhin ang hinihiling na email address: %v monitor.next = Susunod na oras monitor.last_execution_result = Resulta +dashboard.last_gc_time = Oras noong huling GC users.last_login = Huling nag sign-in first_page = Una last_page = Huli @@ -2691,6 +2729,8 @@ config_settings = Mga setting dashboard.statistic = Buod dashboard.task.error = Error sa Utos: %[1]s: %[3]s users.full_name = Buong pangalan +users.list_status_filter.menu_text = Isaasyos +users.list_status_filter.not_2fa_enabled = Naka-disable ang 2FA users.never_login = Hindi nag-sign in kailanman dashboard.system_status = Status ng sistema dashboard.operation_switch = Palitan @@ -2716,6 +2756,20 @@ dashboard.resync_all_hooks = I-resychronize ang mga Git hook ng lahat ng mga rep dashboard.cleanup_hook_task_table = Linisin ang hook_task table dashboard.cleanup_packages = Linisin ang mga na-expire na package dashboard.cleanup_actions = Linisin ang mga nag-expire na log at artifact mula sa mga aksyon +dashboard.server_uptime = Uptime ng server +dashboard.current_goroutine = Mga kasalukuyang goroutine +dashboard.total_memory_allocated = Kabuuan na na-allocate na memory +dashboard.memory_obtained = Mga nakuhang memory +dashboard.pointer_lookup_times = Oras ng pointer lookup +dashboard.memory_free_times = Mga memory free +dashboard.current_heap_usage = Kasalukuyang paggamit ng heap +dashboard.heap_memory_obtained = Nakuhang heap memory +dashboard.heap_memory_idle = Idle ng heap memory +dashboard.heap_objects = Mga heap object +dashboard.bootstrap_stack_usage = Paggamit ng bootstrap stack +dashboard.stack_memory_obtained = Nakuhang stack memory +dashboard.profiling_bucket_hash_table_obtained = Mga nakuhang profiling bucket hash table +dashboard.gc_metadata_obtained = Nakuhang GC metadata dashboard.delete_old_actions = Burahin ang lahat ng mga lumang aktibidad mula sa database dashboard.stop_endless_tasks = Itigil ang mga hindi natatapos na aksyon ng task dashboard.cancel_abandoned_jobs = Kanselahin ang mga naiwang aksyon ng trabaho @@ -2734,7 +2788,16 @@ users.update_profile = I-update ang user account users.delete_account = Burahin ang user account users.cannot_delete_self = Hindi mo maaaring burahin ang sarili mo users.still_own_repo = Ang user na ito ay nagmamay-ari pa ng isa o higit pang mga repositoryo. Burahin o ilipat sila muna. +users.list_status_filter.is_active = Aktibo +users.list_status_filter.not_active = Hindi aktibo +users.list_status_filter.is_admin = Tagapangasiwa +users.list_status_filter.not_admin = Hindi tagapangasiwa +users.list_status_filter.is_restricted = Pinaghihigpitan +users.list_status_filter.is_prohibit_login = Pinagbawalan ang pag-login +users.list_status_filter.not_prohibit_login = Pinapayagan ang pag-login +users.list_status_filter.is_2fa_enabled = Naka-enable ang 2FA users.details = Mga detalye ng user +dashboard.memory_allocate_times = Mga allocation ng memory users.edit = I-edit users = Mga user account organizations = Mga organisasyon @@ -2767,7 +2830,13 @@ dashboard.reinit_missing_repos = Muling pagsasaayos ng lahat ng nawawalang mga r users.allow_git_hook_tooltip = Ang mga Git hook ay tinatakbo bilang OS user na tinatakbo ang Forgejo at may katulad na level ng pag-access ng host. Bilang isang resulta, ang mga gumagamit na may espesyal na pribilehiyo ng Git hook na ito ay maaaring ma-access at baguhin ang lahat ng mga repositori ng Forgejo pati na rin ang database na ginamit ng Forgejo. Dahil dito nakakamit din nila ang mga pribilehiyo ng Forgejo Administrator. dashboard.delete_repo_archives = Burahin ang lahat ng mga archive ng mga repositoryo (ZIP, TAR.GZ, atbp..) dashboard.sync_external_users = I-synchronize ang panlabas na user data +dashboard.heap_memory_released = Mga na-release na heap memory +dashboard.other_system_allocation_obtained = Ibang allocation ng sistema na nakuha users.allow_git_hook = Makakagawa ng mga Git hook +dashboard.current_memory_usage = Kasalukuyang paggamit ng memory +dashboard.gc_times = Mga oras ng GC +users.list_status_filter.reset = I-reset +users.list_status_filter.not_restricted = Hindi pinaghihigpitan config_summary = Buod dashboard.new_version_hint = Available na ang Forgejo %s, tumatakbo ka ng %s. Suriin ang blog para sa karagdagang detalye. dashboard.operations = Mga operasyon ng pagpapanatili @@ -2779,6 +2848,11 @@ dashboard.sync_repo_tags = I-sync ang mga tag mula sa Git data sa database dashboard.update_mirrors = I-update ang mga mirror dashboard.repo_health_check = Suriin ang kalusugan ng lahat ng mga repositoryo dashboard.check_repo_stats = Suriin ang lahat ng istatistika ng repositoryo +dashboard.mspan_structures_usage = Paggamit ng MSpan structure +dashboard.mspan_structures_obtained = Mga nakuhang MSpan structure +dashboard.mcache_structures_usage = Paggamit ng MCache structure +dashboard.next_gc_recycle = Susunod na GC recycle +dashboard.mcache_structures_obtained = Mga nakuhang MCache structure dashboard.delete_old_actions.started = Nasimula na ang burahin ang lahat ng mga lumang aktibidad mula sa database. dashboard.update_checker = Tagasuri ng update dashboard.delete_old_system_notices = Burahin ang lahat ng mga lumang paunawa ng sistema mula sa database @@ -2794,8 +2868,11 @@ users.is_activated = Naka-activate na account users.prohibit_login = Sinuspending account emails.email_manage_panel = Ipamahala ang mga email ng user self_check = Pansariling pagsusuri +dashboard.total_gc_pause = Kabuuang GC pause +dashboard.last_gc_pause = Huling GC pause users.still_has_org = Ang user na ito ay isang miyembro ng isang organisasyon. Tanggalin ang user sa anumang mga organisasyon muna. users.deletion_success = Binura na ang user account. +dashboard.heap_memory_in_use = Ginagamit na heap memory emails.filter_sort.name = Username emails.primary = Pauna emails.filter_sort.email = Email @@ -2965,6 +3042,8 @@ auths.allowed_domains = Mga pinapayagang domain auths.helo_hostname_helper = Hostname na pinapadala sa pamamagitan ng HELO. Iwanang walang laman para ipadala ang kasalukuyang hostname. auths.disable_helo = I-disable ang HELO auths.oauth2_profileURL = URL ng profile +monitor.queue.settings.maxnumberworkers.placeholder = Kasalukuyang %[1]d +monitor.queue.settings.remove_all_items = Tanggalin lahat users.block.description = Harangan ang tagagamit na ito mula sa pag-interact sa serbisyong ito sa pamamagitan ng kanilang mga account at pagbawalan ang pag-sign in. monitor.cron = Mga cron task config.mailer_sendmail_timeout = Timeout ng Sendmail @@ -2983,9 +3062,13 @@ config.provider_config = Config ng provider config.cache_test_slow = Matagumpay ang pagsubok ng cache, ngunit mabagal ang tugon: %s. config.picture_config = Configuration ng larawan at avatar config.disabled_logger = Naka-disable +monitor.queue.settings.submit = I-update ang mga setting auths.tips = Mga tip config.test_mail_sent = Ang isang test email ay ipinadala kay "%s". monitor.process.cancel = Kanselahin ang proseso +monitor.queues = Mga queue +monitor.queue.exemplar = Uri ng Exemplar +monitor.queue.numberworkers = Numero ng mga worker config.enable_openid_signup = I-enable ang OpenID na pansariling pagrehistro config.session_config = Configuration ng session monitor.name = Pangalan @@ -2996,6 +3079,9 @@ config.git_gc_args = Mga argument ng GC config.git_migrate_timeout = Timeout ng paglipat config.cache_config = Configuration ng cache config.git_pull_timeout = Timeout ng operasyon ng paghila +monitor.queue.settings.maxnumberworkers = Pinakamaraming numero ng worker +monitor.queue.settings.title = Mga setting ng pool +monitor.queue.settings.desc = Dinamikang lumalaki ang mga pool tugon sa kanilang worker queue blocking. auths.tip.google_plus = Kumuha ng OAuth2 client credentials mula sa Google API console sa %s config.mail_notify = Paganahin ang mga email notification config.active_code_lives = Oras ng pag-expire ng activation code @@ -3005,6 +3091,7 @@ users.organization_creation.description = Payagan ang paggawa ng mga bagong orga auths.delete_auth_title = Burahin ang source ng authentikasyon auths.delete_auth_desc = Ang pagtanggal ng authentication source ay pumipigil sa mga user na gamitin ito upang mag-sign in. Magpatuloy? auths.login_source_of_type_exist = Umiiral na ang authentication source na may ganitong uri. +monitor.queue.settings.maxnumberworkers.error = Dapat numero ang pinakamaraming numero ng mga worker self_check.no_problem_found = Wala pang mga problema na nahanap. self_check.database_collation_mismatch = Inaasahan ang database na gamitin ang collation: %s auths.oauth2_admin_group = Group claim value para sa mga tagapangasiwa. (Opsyonal - kinakailangan ang claim name sa itaas) @@ -3063,6 +3150,11 @@ monitor.desc = Paglalarawan monitor.start = Oras ng pagsimula monitor.execute_time = Oras ng pag-execute monitor.process.children = Mga anak +monitor.queue = Queue: %s +monitor.queue.type = Uri +monitor.queue.maxnumberworkers = Pinakamaraming numero ng worker +monitor.queue.numberinqueue = Number sa queue +monitor.queue.review_add = Suriin / magdagdag ng mga worker self_check.database_collation_case_insensitive = Gumagamit ang database ng collation %s, na isang insensitive na collation. Bagama't maaaring gumana ang Forgejo dito, maaaring may ilang bihirang kaso na hindi gumagana gaya ng inaasahan. auths.tips.oauth2.general.tip = Kapag nagrerehistro ng bagong OAuth2 na authentication, ang callback/redirect URL ay dapat na: auths.activated = Na-activate na ang source ng authentikasyon @@ -3076,11 +3168,14 @@ config.default_enable_timetracking = I-enable ang pagsubaybay ng oras bilang def auths.deletion_success = Binura na ang authentication source. auths.login_source_exist = Umiiral na ang authentication source na "%s". auths.still_in_used = Ginagamit pa ang authentication source. I-convert o burahin ang mga user na gumagamit ng authentication source na ito muna. +monitor.queue.settings.remove_all_items_done = Tinanggal na ang lahat ng mga item sa queue. +monitor.queue.settings.changed = Na-update ang mga setting auths.oauth2_map_group_to_team_removal = Tanggalin ang user sa mga naka-synchronize na team kung ang user ay hindi kasama sa katumbas na groupo. config.cache_test_succeeded = Matagumpay ang pagsubok ng cache, nakakuha ng tugon sa %s. config.open_with_editor_app_help = Ang mga "Open with" editor para sa clone menu. Kung iniwang walang laman, ang default ang gagamitin. I-expand para makita ang default. config.set_setting_failed = Nabigo ang pagtakda ng setting na %s monitor.download_diagnosis_report = I-download ang ulat ng diagnosis +monitor.queue.activeworkers = Mga aktibong worker self_check.database_inconsistent_collation_columns = Gumagamit ang database ng collation %s, ngunit ang mga column na ito ay gumagamit ng hindi tugmang collations. Maaaring magdulot ito ng ilang hindi inaasahang problema. auths.tip.twitter = Pumunta sa %s, gumawa ng application at siguraduhing naka-enable ang "Payagan ang application na gamitin para Mag-sign in sa Twitter" na opsyon auths.tip.gitea = Magrehistro ng bagong OAuth2 na application. Ang guide ay mahahanap sa %s @@ -3105,6 +3200,7 @@ config.gc_interval_time = Oras ng pagitan ng GC config.cookie_life_time = Lifetime ng cookie config.git_clone_timeout = Timeout ng operasyon na pag-clone monitor.process.cancel_desc = Ang pagkansela ng proseso ay maaaring magdulot ng pagkawalan ng data +monitor.queue.name = Pangalan auths.oauth2_required_claim_value_helper = Itakda ang value na ito upang i-restrict ang pag-login mula sa pinagmulang ito sa mga user na may claim na may ganitong pangalan at value auths.tip.bitbucket = Magrehistro ng bagong OAuth consumer sa %s at idagdag ang pahintulot na "Account" - "Read" config.require_sign_in_view = Kailanganin ang pag-sign in para itignan ang nilalaman @@ -3239,33 +3335,280 @@ settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available a [packages] +alpine.repository.branches = Mga branch +owner.settings.cargo.initialize.success = Matagumpay na nagawa ang Cargo index. +details = Mga detalye +empty = Wala pang anumang mga package. +filter.container.tagged = Naka-tag +filter.container.untagged = Hindi naka-tag +filter.type = Uri +filter.type.all = Lahat +filter.no_result = Walang resulta ang iyong filter. +about = Tungkol sa package na ito +installation = Pag-install +details.repository_site = Website ng repositoryo +details.documentation_site = Website ng dokumentasyon +alpine.repository.repositories = Mga Repositoryo +alpine.repository.architectures = Mga architechture +chef.install = Para i-install ang package na ito, patakbuhin ang sumusunod na command: +composer.registry = I-setup ang registry na ito sa iyong ~/.composee/config.json file: +composer.install = Para i-install ang package gamit ang Composer, patakbuhin ang sumusunod na command: +empty.repo = Nag-upload ka ba ng package, ngunit hindi pinapakita dito? Pumunta sa mga setting ng package at i-link iyan sa repo na ito. +keywords = Mga keyword +versions = Mga bersyon +title = Mga package desc = Ipamahala ang mga package ng repositoryo. +registry.documentation = Para sa higit pang impormasyon tungkol sa %s registry, tignan ang dokumentasyon. +published_by = Na-publish ang %[1]s ni/ng %[3]s +requirements = Mga kinakailangan +dependencies = Mga dependency +details.author = May-akda +details.project_site = Website ng proyekto +details.license = Lisensya +versions.view_all = Tignan lahat +dependency.id = ID +dependency.version = Bersyon +alpine.registry = I-setup ang registry na ito sa pamamagitan ng pagdagdag ng url sa iyong /etc/apk/repositories file: +alpine.registry.info = Pumili ng $branch at $repository mula sa listahan sa ibaba. +alpine.install = Para i-install ang package, patakbuhin ang sumusunod na command: +alpine.repository = Info ng repositoryo +cargo.registry = I-setup ang registry na ito sa Cargo configuration file (halimbawa ~/.cargo/config.toml): +chef.registry = I-setup ang registry na ito sa iyong ~/.chef/config.rb file: +composer.dependencies = Mga dependency +composer.dependencies.development = Mga dependency ng pag-develop conan.details.repository = Repositoryo +conan.registry = I-setup ang registry na ito mula sa command line: +assets = Mga asset +empty.documentation = Para sa higit pang impormasyon sa package registry, tignan ang dokumentasyon. +cargo.install = Para i-install ang package gamit ang Cargo, patakbuhin ang sumusunod na command: +published_by_in = Na-publish ang %[1]s ni %[3]s sa %[5]s +alpine.registry.key = I-download ang registry public RSA key sa /etc/apk/keys folder para i-verify ang index signature: +swift.install2 = at patakbuhin ang sumusunod na utos: +arch.version.description = Paglalarawan +arch.version.properties = Mga katangian ng bersyon +settings.delete.description = Permanente ang pagbura ng package at hindi ito mababawi. +owner.settings.cargo.initialize.description = Ang isang espesyal na index Git repository ay kinakailangan para gamitin ang Cargo registry. Ang paggamit ng opsyon na ito ay (muling) gagawin ang repositoryo at awtomatikong i-configure. +owner.settings.cargo.rebuild.description = Maaaring maging kapaki-pakinabang ang muling pagtatayo kung ang index ay hindi naka-synchronize sa mga nakaimbak na Cargo package. +helm.install = Para i-install ang package na ito, patakbuhin ang sumusunod na command: +helm.registry = I-setup ang registry na ito mula sa command line: +owner.settings.cargo.rebuild.no_index = Hindi makaka-rebuild, walang index na naka-initialize. +pypi.install = Para i-install ang package gamit ang pip, patakbuhin ang sumusunod na command: owner.settings.cleanuprules.keep.count.n = %d mga bersyon kada package +owner.settings.cleanuprules.success.update = Na-update na cleanup rule. +arch.version.backup = Backup +owner.settings.cleanuprules.keep.pattern = Panatilihin ang mga bersyon na tumutugma +conda.install = Para i-install ang package gamit ang Conda, patakbuhin ang sumusunod na command: +container.multi_arch = OS / Arch +debian.repository.components = Mga component +conda.registry = I-set up ang registry na ito bilang Conda repository sa iyong .condarc file: +npm.registry = I-set up ang registry na ito sa .npmrc file ng iyong proyekto: +owner.settings.cargo.rebuild = I-rebuild ang index +cran.registry = I-set up ang registry na ito sa iyong Rprofile.site na file: +arch.version.depends = Dumedepende +arch.version.provides = Nagbibigay +arch.version.groups = Grupo +arch.version.optdepends = Opsyonal na dumepende +pub.install = Para i-install ang package gamit ang Dart, patakbuhin ang sumusunod na command: +pypi.requires = Kinakailangan ang Python +rubygems.dependencies.development = Mga development dependency owner.settings.cleanuprules.keep.count.1 = 1 bersyon kada package +owner.settings.cleanuprules.remove.pattern = Tanggalin ang mga bersyon na tumutugma sa +nuget.install = Para i-install ang package gamit ang NuGet, patakbuhin ang sumusunod na command: +container.labels = Mga Label +rpm.repository.multiple_groups = Available ang package na ito sa iba't ibang grupo. +settings.delete.error = Nabigong burahin ang package. +owner.settings.cargo.title = Index ng Cargo registry +debian.repository = Info ng repositoryo +owner.settings.cleanuprules.remove.title = Ang mga bersyon na tumutugma sa mga rule na ito ay tatanggalin, maliban mung may rule sa itaas ay sasabihin na panatilihin sila. +owner.settings.cleanuprules.remove.days = Tanggalin ang mga bersyon mas luma sa +container.details.platform = Platform +container.digest = Digest +container.details.type = Uri ng image +rubygems.required.ruby = Kinakailangan ang bersyon ng Ruby +npm.dependencies.bundle = Mga kasamang dependency +owner.settings.cleanuprules.success.delete = Nabura na ang cleanup rule. +owner.settings.chef.keypair.description = Ang mga hiling na pinapadala sa Chef registry ay dapat na cryptographically na nakalagda bilang paraan ng authentication. Kapag nag-ge-generate ng isang keypair, ang pampublikong key lamang ang iniimbak ng Forgejo. Ang pribadong key ay binibigay sa iyo na gagamitin sa knife. Ang pag-generate ng bagong keypair i io-overwrite ang luma. +npm.dependencies = Mga dependency +npm.dependencies.peer = Mga peer dependency +rubygems.install = Para i-install ang package sa pamamagitan ng gem, patakbuhin ang sumusunod na command: +settings.link.description = Kung mag-link ka ng package sa repository, ang package ay nakalista sa listahan ng mga package ng repositoryo. +settings.delete.success = Nabura na ang package. +arch.pacman.repo.multi.item = Configuration para sa %s +arch.pacman.conf = Idagdag ang server na may kaugnay na distribution at architechture sa /etc/pacman.conf: +arch.pacman.sync = I-sync ang package sa pamamagitan ng pacman: +arch.version.makedepends = Mga make depend +arch.version.checkdepends = Mga check depend +container.pull = Hilahin ang image mula sa command line: +container.labels.key = Key +cran.install = Para i-install ang package na ito, patakbuhin ang sumusunod na command: +debian.repository.distributions = Mga distribution +generic.download = I-download ang package mula sa command line: +go.install = I-install ang package mula sa command line: +maven.install2 = Patakbuhin sa pamamagitan ng command line: +maven.download = Para i-download ang dependency, patakbuhin sa pamamagitan ng command line: +nuget.registry = I-setup ang registry na ito mula sa command line: +nuget.dependency.framework = Target na Framework +npm.install = Para i-install ang package gamit ng npm, patakbuhin ang sumusunod na command: +npm.install2 = o idagdag ito sa package.json file: +npm.dependencies.development = Mga development dependency +npm.details.tag = Tag +swift.install = Idagdag ang package sa iyong Package.swift na file: +vagrant.install = Para magdagdag ng Vagrant box, patakbuhin ang sumusunod na command: +settings.link = I-link ang package na ito sa repository +settings.link.select = Pumili ng repositoryo +settings.link.button = I-update ang link ng repositoryo +settings.link.error = Nabigong i-update ang link ng repositoryo. +settings.delete = Burahin ang package +owner.settings.cargo.initialize = I-initialize ang index +owner.settings.cleanuprules.add = Magdagdag ng cleanup rule +owner.settings.cleanuprules.preview = Preview ng cleanup rule +owner.settings.cleanuprules.preview.overview = %d mga package ay naka-iskedyul para tanggalin. +owner.settings.cleanuprules.preview.none = Hindi tumutugma sa anumang mga package ang cleanup rule. owner.settings.cleanuprules.enabled = Naka-enable +owner.settings.cleanuprules.pattern_full_match = I-apply ang pattern sa punong pangalan ng package +owner.settings.chef.keypair = Gumawa ng key pair +arch.pacman.helper.gpg = Magdagdag ng trust certificate para sa pacman: +arch.pacman.repo.multi = Ang %s ay may katulad na beryson sa iba't ibang mga distribution. +arch.version.conflicts = Mga salungatan +arch.version.replaces = Pinapalit +npm.dependencies.optional = Mga opsyonal na dependency +rpm.distros.suse = sa mga SUSE based na distribution +rpm.install = Para i-install ang package na ito, patakbuhin ang sumusunod na command: +rpm.repository = Info ng repositoryo +rpm.repository.architectures = Mga architechture +rpm.registry = I-setup ang registry na ito mula sa command line: +rpm.distros.redhat = sa mga RedHat based na distribution +rubygems.dependencies.runtime = Mga runtime dependency +rubygems.install2 = o idagdag ito sa Gemfile: +debian.repository.architectures = Mga architechture +owner.settings.chef.title = Chef registry +rubygems.required.rubygems = Kinakailangan ang bersyon ng RubyGem +owner.settings.cleanuprules.edit = I-edit ang cleanup rule +settings.link.success = Matagumpay na na-update ang link ng repositoryo. +owner.settings.cleanuprules.keep.pattern.container = Ang latest na bersyon ay palaging pinapatilihin para sa mga Container package. +owner.settings.cleanuprules.none = Wala pang mga cleanup rule sa ngayon. +owner.settings.cleanuprules.keep.count = Panatilihin ang pinaka-recent +settings.delete.notice = Buburahin mo ang %s (%s). Hindi mababalikan ang aksyon na ito, sigurado ka ba? +owner.settings.cargo.initialize.error = Nabigong i-initialize ang Cargo index: %v +debian.install = Para i-install ang package na ito, patakbuhin ang sumusunod na command: +owner.settings.cargo.rebuild.error = Nabigong i-rebuild ang cargo index: %v +conan.install = Para i-install ang package gamit ang Conan, patakbuhin ang sumusunod na command: +swift.registry = I-setup ang registry na ito mula sa command line: +maven.registry = I-set up ang registry na ito sa iyong pom.xml ng iyong proyekto: +owner.settings.cleanuprules.keep.title = Ang mga bersyon na tumutugma sa mga rule na ito ay papanatilihin, kahit na tumutugma sila sa removal rule sa ibaba. +maven.install = Para gamitin ang package isama ang sumusunod sa dependencies block sa pom.xml file: +container.labels.value = Value +debian.registry = I-setup ang registry na ito mula sa command line: +debian.registry.info = Pumili ng $distribution at $component sa listahan sa ibaba. +owner.settings.cargo.rebuild.success = Matagumpay na na-rebuild ang Cargo index. +owner.settings.cleanuprules.title = Mga cleanup rule +container.layers = Mga layer ng image +container.images.title = Mga image +search_in_external_registry = Maghanap sa %s +alt.registry = I-setup ang registry na ito mula sa command line: +alt.registry.install = Para i-install ang package na ito, patakbuhin ang sumusunod na command: +alt.install = I-install ang package +alt.setup = Idagdag ang repositoryo sa listahan ng mga nakakonektang repositoryo (piliin ang kinakailangang architechture sa halip ng "_arch_"): +alt.repository = Info ng repositoryo +alt.repository.architectures = Mga architechture +alt.repository.multiple_groups = Available ang package na ito sa iba't ibang grupo. [actions] +runners.last_online = Huling oras na online +status.waiting = Hinihintay +runners.task_list.run = Patakbuhin +runners.description = Paglalarawan +runners.owner_type = Uri +runners.name = Pangalan +status.success = Tagumpay +runs.pushed_by = itinulak ni/ng +runners.status = Katayuan +status.failure = Nabigo +actions = Mga Aksyon +runs.no_job = Ang workflow ay dapat maglaman ng hindi bababa sa isang trabaho +runners = Mga Runner +runs.commit = Commit workflow.dispatch.trigger_found = Mayroong workflow_dispatch na trigger ang workflow na ito. unit.desc = Ipamahala ang mga pinag-sasamang CI/CD pipeline sa pamamagitan ng Forgejo Actions. +runners.edit_runner = Baguhin ang Runner +runners.update_runner = I-update ang mga pagbabago +variables.update.failed = Nabigong baguhin ang variable. +variables.update.success = Nabago na ang variable. +runs.no_results = Walang mga tumugmang resulta. +runners.delete_runner_success = Matagumpay na nabura ang runner +runs.all_workflows = Lahat ng mga workflow +runs.scheduled = Naka-iskedyul +runs.workflow = Workflow +variables.edit = Baguhin ang Variable workflow.enable = I-enable ang workflow workflow.disabled = Naka-disable ang workflow. need_approval_desc = Kailangan ng pag-apruba para tumakbo ng mga workflow para sa fork na hiling sa paghila. +variables = Mga variable +runners.status.active = Aktibo +runners.version = Bersyon +status.unknown = Hindi alam +runs.invalid_workflow_helper = Hindi wasto ang workflow config file. Pakisuri ang iyong config file: %s +runs.actors_no_select = Lahat ng mga actor +runners.runner_title = Runner +runners.task_list = Mga kamakailang trabaho sa runner na ito +runners.task_list.no_tasks = Wala pang mga trabaho sa ngayon. +runners.labels = Mga label +runs.no_matching_online_runner_helper = Walang tumutugmang online runner na may label: %s +runs.status = Status +runs.no_workflows = Wala pang mga workflow sa ngayon. runs.no_runs = Wala pang mga pagtatakbo ang workflow na ito sa ngayon. +variables.creation = Magdagdag ng variable +variables.none = Wala pang mga variable sa ngayon. +variables.deletion = Tanggalin ang variable +variables.deletion.description = Permanente ang pagtanggal ng isang variable at hindi ito mababalik. Magpatuloy? +status.running = Tumatakbo +runners.new_notice = Paano magsimula ng runner +runners.update_runner_success = Matagumpay na na-update ang runner +runners.delete_runner_notice = Kapag may trabaho na tumatakbo sa runner na ito, titigilan ito at mamarkahan bilang nabigo. Maaaring sirain ang building workflow. +runners.none = Walang mga available na runner +runs.status_no_select = Lahat ng status runs.empty_commit_message = (walang laman na mensahe ng commit) workflow.enable_success = Matagumpay na na-enable ang workflow na "%s". workflow.dispatch.run = Patakbuhin ang workflow workflow.dispatch.success = Matagumpay na nahiling ang pagtakbo ng workflow. +variables.management = Ipamahala ang mga variable +variables.deletion.failed = Nabigong tanggalin ang variable. +runners.status.unspecified = Hindi alam +runs.no_job_without_needs = Ang workflow ay dapat maglaman ng hindi bababa sa isang trabaho na walang dependencies. workflow.disable = I-disable ang workflow workflow.disable_success = Matagumpay na na-disable ang workflow na "%s". +runners.task_list.repository = Repositoryo +status.skipped = Nilaktawan +runners.runner_manage_panel = Ipamahala ang mga runner +runners.new = Gumawa ng bagong runner +variables.creation.failed = Nabigong idagdag ang variable. +runners.id = ID +runs.actor = Actor +runners.update_runner_failed = Nabigong i-update ang runner +runners.delete_runner = Burahin ang runner na ito +runners.delete_runner_failed = Nabigong burahin ang runner +runners.delete_runner_header = Kumpirmahin na burahin ang runner +status.blocked = Naharang +status.cancelled = Kinansela +runners.task_list.status = Status +runners.status.idle = Idle workflow.dispatch.use_from = Gamitin ang workflow mula sa +runners.reset_registration_token = I-reset ang token ng pagrehistro +runners.status.offline = Offline workflow.dispatch.invalid_input_type = Hindi wastong input type "%s". +runners.task_list.commit = Commit +runners.task_list.done_at = Natapos sa +runners.reset_registration_token_success = Matagumpay na na-reset ang token ng pagrehistro ng runner workflow.dispatch.input_required = Kumailangan ng value para sa input na "%s". workflow.dispatch.warn_input_limit = Pinapakita lamang ang unang %d na mga input. +variables.description = Ipapasa ang mga variable sa ilang mga aksyon at hindi mababasa kung hindi man. variables.id_not_exist = Hindi umiiral ang variable na may ID na %d. +variables.deletion.success = Tinanggal na ang variable. +variables.creation.success = Nadagdag na ang variable na "%s". runs.expire_log_message = Na-purge ang mga log dahil masyado silang luma. runs.no_workflows.help_write_access = Hindi alam kung paano magsimula sa Forgejo Actions? Tignan ang mabilisang pagsimula sa user documentation para magsimulang magsulat ng unang workflow, at mag-setup ng Forgejo runner para patakbuhin ang mga job. runs.no_workflows.help_no_write_access = Para matuto tungkol sa Forgejo Actions, tignan ang dokumentasyon. +variables.not_found = Nabigong hanapin ang variable. [action] commit_repo = itinulak sa %[3]s sa %[4]s @@ -3321,10 +3664,38 @@ raw_seconds = segundo raw_minutes = minuto [munits.data] +mib = MiB +gib = GiB +b = B +kib = KiB +tib = TiB +pib = PiB +eib = EiB [gpg] +error.not_signed_commit = Hindi isang naka-sign na commit +error.probable_bad_signature = BABALA! Bagaman na may key na may ID na ito sa database hindi nito pinapatunayan ang commit na ito! Ang commit na ito ay KAHINA-HINALA. +error.extract_sign = Nabigong i-extract ang signature +error.no_committer_account = Walang account na naka-link sa email address ng committer +error.no_gpg_keys_found = Walang kilalang key na nahanap para sa signature na ito sa database +default_key = Naka-sign gamit ang default key +error.generate_hash = Nabigong i-generate ang hash ng commit +error.failed_retrieval_gpg_keys = Nabigong kumuha ng anumang key na naka-attach sa account ng committer +error.probable_bad_default_signature = BABALA! Bagaman na ang default key ay may ID na ito hindi nito pinapatunayan ang commit na ito! Ang commit na ito ay KAHINA-HINALA. [notification] +unread = Hindi nabasa +read = Nabasa +no_unread = Walang mga hindi nabasang notification. +notifications = Mga abiso +no_read = Walang mga nabasang notification. +pin = I-pin ang notification +mark_as_read = Markahan bilang nabasa +mark_as_unread = Markahan bilang hindi nabasa +subscriptions = Mga subscription +watching = Pinapanood +no_subscriptions = Walang mga subscription +mark_all_as_read = Markahan lahat bilang nabasa [units] error.no_unit_allowed_repo = Hindi ka pinapayagang ma-access ang anumang seksyon ng repositoryong ito. @@ -3332,6 +3703,10 @@ unit = Yunit error.unit_not_allowed = Hindi ka pinapayagang ma-access ang seksyon ng repositoryong ito. [dropzone] +default_message = I-drop ang mga file o mag-click dito para mag-upload. +invalid_input_type = Hindi ka maaaring mag-upload ng mga file sa uri na ito. +file_too_big = Ang laki ng file ({{filesize}}) MB) ay lumalagpas sa pinakamataas na size na ({{maxFilesize}} MB). +remove_file = Tanggalin ang file [secrets] creation.success = Naidagdag na ang lihim na "%s". @@ -3349,6 +3724,9 @@ management = Ipamahala ang mga secret deletion.description = Ang pagtanggal ng sikreto ay permanente at hindi mababawi. Magpatuloy? [markup] +filepreview.line = Linya %[1]d sa %[2]s +filepreview.truncated = Na-truncate ang preview +filepreview.lines = Mga linya %[1]d hanggang %[2]d sa %[3]s [projects] deleted.display_name = Binurang proyekto diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 2558b99f74..ebcdeb56cc 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -37,6 +37,18 @@ twofa=Authentification à deux facteurs twofa_scratch=Code de secours pour l'authentification à deux facteurs passcode=Code d'accès +webauthn_insert_key=Insérez votre clé de sécurité +webauthn_sign_in=Appuyez sur le bouton de votre clé de sécurité. Si votre clé de sécurité n'a pas de bouton, réinsérez-la. +webauthn_press_button=Veuillez appuyer sur le bouton de votre clé de sécurité… +webauthn_use_twofa=Utilisez l'authentification à deux facteurs avec votre téléphone +webauthn_error=Impossible de lire votre clé de sécurité. +webauthn_unsupported_browser=Votre navigateur ne prend actuellement pas en charge WebAuthn. +webauthn_error_unknown=Une erreur indéterminée s'est produite. Veuillez réessayer. +webauthn_error_insecure=`WebAuthn ne prend en charge que les connexions sécurisées. Pour les tests via HTTP, vous pouvez utiliser l'origine "localhost" ou "127.0.0.1"` +webauthn_error_unable_to_process=Le serveur n'a pas pu traiter votre demande. +webauthn_error_duplicated=La clé de sécurité n'est pas autorisée pour cette demande. Veuillez vous assurer que la clé n'est pas déjà enregistrée. +webauthn_error_empty=Vous devez définir un nom pour cette clé. +webauthn_error_timeout=Le délai d'attente imparti a été atteint avant que votre clé ne puisse être lue. Veuillez recharger la page pour réessayer. repository=Dépôt organization=Organisation mirror=Miroir @@ -169,7 +181,7 @@ buttons.bold.tooltip=Ajouter du texte en gras (Ctrl+B / ⌘B) buttons.italic.tooltip=Ajouter du texte en italique (Ctrl+I / ⌘I) buttons.quote.tooltip=Citer le texte buttons.code.tooltip=Ajouter du code -buttons.link.tooltip=Ajouter un lien (Ctrl+K / ⌘K) +buttons.link.tooltip=Ajouter un lien buttons.list.unordered.tooltip=Ajouter une liste à puces buttons.list.ordered.tooltip=Ajouter une liste numérotée buttons.list.task.tooltip=Ajouter une liste de tâches @@ -1141,6 +1153,14 @@ migrate_options_lfs_endpoint.label=Point d'accès LFS migrate_options_lfs_endpoint.description=La migration va tenter d'utiliser votre dépôt Git distant pour déterminer le serveur LFS. Vous pouvez également spécifier un point d'accès personnalisé si les données LFS du dépôt sont stockées ailleurs. migrate_options_lfs_endpoint.description.local=Un chemin de serveur local est également pris en charge. migrate_options_lfs_endpoint.placeholder=Si laissé vide, le point de terminaison sera dérivé de l'URL du clone +migrate_items=Éléments à migrer +migrate_items_wiki=Wiki +migrate_items_milestones=Jalons +migrate_items_labels=Labels +migrate_items_issues=Tickets +migrate_items_pullrequests=Demandes d'ajout +migrate_items_merge_requests=Demandes de fusion +migrate_items_releases=Publications migrate_repo=Migrer le dépôt migrate.clone_address=Migrer/Cloner depuis une URL migrate.clone_address_desc=L'URL HTTP(S) ou Git "clone" d'un dépôt existant @@ -1159,6 +1179,16 @@ migrate.migrating=Migration de %s … migrate.migrating_failed=La migration de %s a échoué. migrate.migrating_failed.error=Échec de la migration : %s migrate.migrating_failed_no_addr=Échec de la migration. +migrate.migrating_git=Migration des données Git +migrate.migrating_topics=Migration des sujets +migrate.migrating_milestones=Migration des jalons +migrate.migrating_labels=Migration des labels +migrate.migrating_releases=Migration des publications +migrate.migrating_issues=Migration des tickets +migrate.migrating_pulls=Migration des demandes d'ajout +migrate.cancel_migrating_title=Annuler la migration +migrate.cancel_migrating_confirm=Voulez-vous abandonner cette migration ? + mirror_from=miroir de forked_from=bifurqué depuis generated_from=généré depuis @@ -1536,7 +1566,7 @@ issues.ref_reopening_from=`a référencé ce ticket dans une pul issues.ref_from=`de %[1]s` issues.author=Auteur issues.role.owner=Propriétaire -issues.role.owner_helper=Cet utilisateur est propriétaire de ce dépôt. +issues.role.owner_helper=Cet utilisateur est le propriétaire de ce dépôt. issues.role.member=Membre issues.role.member_helper=Cet utilisateur est membre de l’organisation propriétaire de ce dépôt. issues.role.collaborator=Collaborateur @@ -2609,10 +2639,16 @@ file_follow = Suivre le lien symbolique settings.confirmation_string = Chaine de confirmation pulls.agit_explanation = Créé par le workflow AGit. AGit permet aux contributeurs de proposer des modifications en utilisant "git push" sans créer une bifurcation ou une nouvelle branche. stars = Étoiles +n_tag_few = %s étiquettes editor.commit_id_not_matching = Le fichier a été modifié pendant que vous l'éditiez. Appliquez les modifications à une nouvelle branche puis procédez à la fusion. commits.search_branch = Cette branche open_with_editor = Ouvrir avec %s pulls.ready_for_review = Prêt à être évalué ? +n_commit_one = %s commit +n_commit_few = %s commits +n_branch_one = %s branch +n_branch_few = %s branches +n_tag_one = %s étiquettes editor.push_out_of_date = Le push semble obsolète. issues.num_participants_one = %d participant issues.archived_label_description = (Archivé) %s @@ -2643,6 +2679,8 @@ settings.federation_settings = Paramètres de féderation project = Projets subscribe.issue.guest.tooltip = Authentifiez vous pour vous abonner à ce ticket. subscribe.pull.guest.tooltip = Authentifiez vous pour suivre cette demande d'ajout. +n_release_one = %s publication +n_release_few = %s publications issues.author.tooltip.pr = Cet utilisateur est l'auteur de cette pull request. issues.author.tooltip.issue = Cet utilisateur est l'auteur de ce ticket. issues.edit.already_changed = Impossible de sauvegarder les changements du ticket car son contenu a déjà été modifié par un autre utilisateur. Veuillez recharger la page et essayer de l'éditer à nouveau pour éviter d'écraser ses changements @@ -2924,6 +2962,34 @@ dashboard.sync_external_users=Synchroniser les données de l’utilisateur exter dashboard.cleanup_hook_task_table=Nettoyer la table hook_task dashboard.cleanup_packages=Nettoyer des paquets expirés dashboard.cleanup_actions=Nettoyer les journaux et les artefacts des actions obsolètes +dashboard.server_uptime=Uptime du serveur +dashboard.current_goroutine=Goroutines actuelles +dashboard.current_memory_usage=Utilisation Mémoire actuelle +dashboard.total_memory_allocated=Mémoire totale allouée +dashboard.memory_obtained=Mémoire obtenue +dashboard.pointer_lookup_times=Nombre de Consultations Pointeur +dashboard.memory_allocate_times=Allocations de mémoire +dashboard.memory_free_times=Nombre de libérations de mémoire +dashboard.current_heap_usage=Utilisation Tas (Heap) +dashboard.heap_memory_obtained=Mémoire tas (Heap) obtenue +dashboard.heap_memory_idle=Mémoire tas (Heap) au repos +dashboard.heap_memory_in_use=Utilisation mémoire tas (Heap) +dashboard.heap_memory_released=Mémoire tas (Heap) libérée +dashboard.heap_objects=Objets tas (Heap) +dashboard.bootstrap_stack_usage=Utilisation pile bootstrap +dashboard.stack_memory_obtained=Mémoire pile obtenue +dashboard.mspan_structures_usage=Utilisation des structures MSpan +dashboard.mspan_structures_obtained=Structures MSpan obtenues +dashboard.mcache_structures_usage=Utilisation des structures MCache +dashboard.mcache_structures_obtained=Structures MCache obtenues +dashboard.profiling_bucket_hash_table_obtained=Profilage de seau de table de hashage obtenu +dashboard.gc_metadata_obtained=Métadonnées GC obtenues +dashboard.other_system_allocation_obtained=Autres allocation système obtenue +dashboard.next_gc_recycle=Prochain recyclage GC +dashboard.last_gc_time=Temps depuis le dernier GC +dashboard.total_gc_pause=Pause totale GC +dashboard.last_gc_pause=Dernière pause GC +dashboard.gc_times=Nombres de GC dashboard.delete_old_actions=Supprimer toutes les anciennes activités de la base de données dashboard.delete_old_actions.started=Suppression de toutes les anciennes activités de la base de données démarrée. dashboard.update_checker=Vérificateur de mise à jour @@ -2981,6 +3047,18 @@ users.purge_help=Éradique l’utilisateur et tous ses dépôts, organisations e users.still_own_packages=Cet utilisateur possède encore un ou plusieurs paquets. Supprimez d’abord ces paquets. users.deletion_success=Le compte a été supprimé. users.reset_2fa=Réinitialiser l'authentification à deux facteurs +users.list_status_filter.menu_text=Filtrer +users.list_status_filter.reset=Réinitialiser +users.list_status_filter.is_active=Actif +users.list_status_filter.not_active=Inactif +users.list_status_filter.is_admin=Administrateur +users.list_status_filter.not_admin=Non administrateur +users.list_status_filter.is_restricted=Restreint +users.list_status_filter.not_restricted=Non restreint +users.list_status_filter.is_prohibit_login=Interdit de connexion +users.list_status_filter.not_prohibit_login=Autorisé à se connecter +users.list_status_filter.is_2fa_enabled=2FA Activé +users.list_status_filter.not_2fa_enabled=2FA désactivé users.details=Informations de l’utilisateur emails.email_manage_panel=Gestion des courriels des utilisateurs @@ -3300,6 +3378,26 @@ monitor.process.cancel_desc=L'annulation d'un processus peut entraîner une pert monitor.process.cancel_notices=Annuler : %s ? monitor.process.children=Enfant +monitor.queues=Files d'attente +monitor.queue=File d'attente : %s +monitor.queue.name=Nom +monitor.queue.type=Type +monitor.queue.exemplar=Type d'exemple +monitor.queue.numberworkers=Nombre de processus +monitor.queue.activeworkers=Processus actifs +monitor.queue.maxnumberworkers=Nombre maximal de processus +monitor.queue.numberinqueue=Position dans la queue +monitor.queue.review_add=Examiner / ajouter des processus +monitor.queue.settings.title=Paramètres du réservoir +monitor.queue.settings.desc=Les bassins croissent proportionnellement au besoin de leurs exécuteurs. +monitor.queue.settings.maxnumberworkers=Nombre maximale de processus +monitor.queue.settings.maxnumberworkers.placeholder=Actuellement %[1]d +monitor.queue.settings.maxnumberworkers.error=Le nombre de processus doit être un nombre +monitor.queue.settings.submit=Appliquer les paramètres +monitor.queue.settings.changed=Paramètres mis à jour +monitor.queue.settings.remove_all_items=Tout effacer +monitor.queue.settings.remove_all_items_done=Tous les éléments de la file d'attente ont été effacés. + notices.system_notice_list=Notifications systèmes notices.view_detail_header=Voir les détails de la notification notices.operations=Opérations @@ -3396,10 +3494,35 @@ raw_seconds=secondes raw_minutes=minutes [dropzone] +default_message=Déposez les fichiers ou cliquez ici pour téléverser. +invalid_input_type=Vous ne pouvez pas téléverser des fichiers de ce type. +file_too_big=La taille du fichier ({{filesize}} Mo) dépasse la taille maximale ({{maxFilesize}} Mo). +remove_file=Supprimer le fichier [notification] +notifications=Notifications +unread=Non lue(s) +read=Lue(s) +no_unread=Aucune notification non lue. +no_read=Aucune notification lue. +pin=Épingler la notification +mark_as_read=Marquer comme lu +mark_as_unread=Marquer comme non lue +mark_all_as_read=Tout marquer comme lu +subscriptions=Abonnements +watching=Suivi +no_subscriptions=Pas d'abonnements [gpg] +default_key=Signé avec la clé par défaut +error.extract_sign=Impossible d'extraire la signature +error.generate_hash=Impossible de générer la chaine de hachage de la révision +error.no_committer_account=Aucun compte lié à l'adresse e-mail de l'auteur +error.no_gpg_keys_found=Aucune clé n'a été trouvée pour cette signature dans la base de données +error.not_signed_commit=Révision non signée +error.failed_retrieval_gpg_keys=Impossible de récupérer la clé liée au compte de l'auteur +error.probable_bad_signature=AVERTISSEMENT ! Bien qu'il y ait une clé avec cet ID dans la base de données, il ne vérifie pas ce commit ! Ce commit est SUSPECT. +error.probable_bad_default_signature=AVERTISSEMENT ! Bien que la clé par défaut ait cet ID, elle ne vérifie pas ce commit ! Ce commit est SUSPECT. [units] unit=Unité @@ -3407,11 +3530,183 @@ error.no_unit_allowed_repo=Vous n'êtes pas autorisé à accéder à n'importe q error.unit_not_allowed=Vous n'êtes pas autorisé à accéder à cette section du dépôt. [packages] +title=Paquets desc=Gérer les paquets du dépôt. +empty=Il n'y pas de paquet pour le moment. +empty.documentation=Pour plus d'informations sur le registre de paquets, voir la documentation. +empty.repo=Avez-vous téléchargé un paquet, mais il n'est pas affiché ici ? Allez dans les paramètres du paquet et liez le à ce dépôt. +registry.documentation=Pour plus d’informations sur le registre %s, voir la documentation. +filter.type=Type +filter.type.all=Tous +filter.no_result=Votre filtre n'affiche aucun résultat. +filter.container.tagged=Balisé +filter.container.untagged=Débalisé +published_by=%[1]s publié par %[3]s +published_by_in=%[1]s publié par %[3]s en %[5]s +installation=Installation +about=À propos de ce paquet +requirements=Exigences +dependencies=Dépendances +keywords=Mots-clés +details=Détails +details.author=Auteur +details.project_site=Site du projet +details.repository_site=Site du dépôt +details.documentation_site=Site de la documentation +details.license=Licence +assets=Ressources +versions=Versions +versions.view_all=Voir tout +dependency.id=ID +dependency.version=Version +alpine.registry=Configurez ce registre en ajoutant l’URL dans votre fichier /etc/apk/repositories : +alpine.registry.key=Téléchargez la clé RSA publique du registre dans le dossier /etc/apk/keys/ pour vérifier la signature de l'index : +alpine.registry.info=Choisissez $branch et $repository dans la liste ci-dessous. +alpine.install=Pour installer le paquet, exécutez la commande suivante : +alpine.repository=Informations sur le dépôt +alpine.repository.branches=Branches +alpine.repository.repositories=Dépôts +alpine.repository.architectures=Architectures +cargo.registry=Configurez ce registre dans le fichier de configuration Cargo (par exemple ~/.cargo/config.toml) : +cargo.install=Pour installer le paquet en utilisant Cargo, exécutez la commande suivante : +chef.registry=Configurer ce registre dans votre fichier ~/.chef/config.rb : +chef.install=Pour installer le paquet, exécutez la commande suivante : +composer.registry=Configurez ce registre dans votre fichier ~/.composer/config.json : +composer.install=Pour installer le paquet en utilisant Composer, exécutez la commande suivante : +composer.dependencies=Dépendances +composer.dependencies.development=Dépendances de développement conan.details.repository=Dépôt +conan.registry=Configurez ce registre à partir d'un terminal : +conan.install=Pour installer le paquet en utilisant Conan, exécutez la commande suivante : +conda.registry=Configurez ce registre en tant que dépôt Conda dans le fichier .condarc : +conda.install=Pour installer le paquet en utilisant Conda, exécutez la commande suivante : +container.details.type=Type d'image +container.details.platform=Plateforme +container.pull=Tirez l'image depuis un terminal : +container.digest=Empreinte +container.multi_arch=SE / Arch +container.layers=Calques d'image +container.labels=Labels +container.labels.key=Clé +container.labels.value=Valeur +cran.registry=Configurez ce registre dans le fichier Rprofile.site : +cran.install=Pour installer le paquet, exécutez la commande suivante : +debian.registry=Configurez ce registre à partir d'un terminal : +debian.registry.info=Choisissez $distribution et $component dans la liste ci-dessous. +debian.install=Pour installer le paquet, exécutez la commande suivante : +debian.repository=Infos sur le dépôt +debian.repository.distributions=Distributions +debian.repository.components=Composants +debian.repository.architectures=Architectures +generic.download=Télécharger le paquet depuis un terminal : +go.install=Installer le paquet à partir de la ligne de commande : +helm.registry=Configurer ce registre à partir d'un terminal : +helm.install=Pour installer le paquet, exécutez la commande suivante : +maven.registry=Configurez ce registre dans le fichier pom.xml de votre projet : +maven.install=Pour utiliser le paquet, inclure ce qui suit dans le bloc dependencies dans le fichier pom.xml : +maven.install2=Exécuter dans un terminal : +maven.download=Pour télécharger la dépendance, exécutez dans un terminal : +nuget.registry=Configurer ce registre à partir d'un terminal : +nuget.install=Pour installer le paquet en utilisant NuGet, exécutez la commande suivante : +nuget.dependency.framework=Cadriciel cible +npm.registry=Configurez ce registre dans le fichier .npmrc de votre projet : +npm.install=Pour installer le paquet en utilisant npm, exécutez la commande suivante : +npm.install2=ou ajoutez-le au fichier package.json : +npm.dependencies=Dépendances +npm.dependencies.development=Dépendances de développement +npm.dependencies.peer=Dépendances de pairs +npm.dependencies.optional=Dépendances optionnelles +npm.details.tag=Balise +pub.install=Pour installer le paquet en utilisant Dart, exécutez la commande suivante : +pypi.requires=Nécessite Python +pypi.install=Pour installer le paquet en utilisant pip, exécutez la commande suivante : +rpm.registry=Configurez ce registre à partir d'un terminal : +rpm.distros.redhat=sur les distributions basées sur RedHat +rpm.distros.suse=sur les distributions basées sur SUSE +rpm.install=Pour installer le paquet, exécutez la commande suivante : +rpm.repository = Information sur le dépôt +rpm.repository.architectures = Architectures +rpm.repository.multiple_groups = Ce paquet est disponible dans plusieurs groupes. +rubygems.install=Pour installer le paquet en utilisant gem, exécutez la commande suivante : +rubygems.install2=ou ajoutez-le au Gemfile : +rubygems.dependencies.runtime=Dépendances d'exécution +rubygems.dependencies.development=Dépendances de développement +rubygems.required.ruby=Nécessite Ruby en version +rubygems.required.rubygems=Nécessite RubyGem en version +swift.registry=Configurez ce registre à partir d'un terminal : +swift.install=Ajoutez le paquet dans votre fichier Package.swift : +swift.install2=et exécutez la commande suivante : +vagrant.install=Pour ajouter une machine Vagrant, exécutez la commande suivante : +settings.link=Lier ce paquet à un dépôt +settings.link.description=Si vous liez un paquet à dépôt, le paquet sera inclus dans sa liste des paquets. +settings.link.select=Sélectionner un dépôt +settings.link.button=Actualiser le lien du dépôt +settings.link.success=Le lien du dépôt a été mis à jour avec succès. +settings.link.error=Impossible de mettre à jour le lien du dépôt. +settings.delete=Supprimer le paquet +settings.delete.description=Supprimer un paquet est permanent et irréversible. +settings.delete.notice=Vous êtes sur le point de supprimer %s (%s). Cette opération est irréversible, êtes-vous sûr ? +settings.delete.success=Le paquet a été supprimé. +settings.delete.error=Impossible de supprimer le paquet. +owner.settings.cargo.title=Index du registre cargo +owner.settings.cargo.initialize=Initialiser l'index +owner.settings.cargo.initialize.description=Un dépôt Git d’index spécial est nécessaire pour utiliser le registre Cargo. Utiliser cette option va (re)créer le dépôt et le configurer automatiquement. +owner.settings.cargo.initialize.error=Impossible d'initialiser l'index de Cargo : %v +owner.settings.cargo.initialize.success=L'index Cargo a été créé avec succès. +owner.settings.cargo.rebuild=Reconstruire l'index +owner.settings.cargo.rebuild.description=La reconstruction peut être utile si l'index n'est pas synchronisé avec les paquets Cargo stockés. +owner.settings.cargo.rebuild.error=Impossible de reconstruire l'index Cargo : %v +owner.settings.cargo.rebuild.success=L'index Cargo a été reconstruit avec succès. +owner.settings.cleanuprules.title=Règles de nettoyage +owner.settings.cleanuprules.add=Ajouter une règle de nettoyage +owner.settings.cleanuprules.edit=Modifier la règle de nettoyage +owner.settings.cleanuprules.none=Aucune règle de nettoyage disponible. Veuillez consulter la documentation. +owner.settings.cleanuprules.preview=Aperçu des règles de nettoyage +owner.settings.cleanuprules.preview.overview=%d paquets sont programmés pour être supprimés. +owner.settings.cleanuprules.preview.none=La règle de nettoyage ne correspond à aucun paquet. owner.settings.cleanuprules.enabled=Activé +owner.settings.cleanuprules.pattern_full_match=Appliquer le motif au nom complet du paquet +owner.settings.cleanuprules.keep.title=Les versions qui correspondent à ces règles sont conservées, même si elles correspondent à une règle de suppression ci-dessous. +owner.settings.cleanuprules.keep.count=Garder le plus récent owner.settings.cleanuprules.keep.count.1=1 version par paquet owner.settings.cleanuprules.keep.count.n=%d versions par paquet +owner.settings.cleanuprules.keep.pattern=Garder les versions correspondantes +owner.settings.cleanuprules.keep.pattern.container=La version latest est toujours conservée pour les paquets Container. +owner.settings.cleanuprules.remove.title=Les versions qui correspondent à ces règles sont supprimées, sauf si une règle ci-dessus dit de les garder. +owner.settings.cleanuprules.remove.days=Supprimer les versions antérieures à +owner.settings.cleanuprules.remove.pattern=Supprimer les versions correspondantes +owner.settings.cleanuprules.success.update=La règle de nettoyage a été mise à jour. +owner.settings.cleanuprules.success.delete=La règle de nettoyage a été supprimée. +owner.settings.chef.title=Dépôt Chef +owner.settings.chef.keypair=Générer une paire de clés +owner.settings.chef.keypair.description=Les requêtes envoyées au registre Chef doivent être signées cryptographiquement à des fin d'authentification. Lorsqu'une paire de clés est générée, seule la clé publique est conservée dans Forgejo. La clé privée est fournie afin que vous puissiez l'utiliser avec knife. La génération d'une nouvelle clé remplace la précédente. +owner.settings.cargo.rebuild.no_index = Incapable de reconstruire, index non initialisé. +npm.dependencies.bundle = Bundles de dépendances +arch.pacman.helper.gpg = Ajouter un certificat de confiance pour pacman : +arch.pacman.repo.multi = %s a la même version dans différentes distributions. +arch.pacman.repo.multi.item = Configuration pour %s +arch.pacman.conf = Ajouter un serveur associées à la distribution et l'architecture dans /etc/pacman.conf : +arch.pacman.sync = Synchroniser le paquet avec pacman : +arch.version.properties = Propriétés de version +arch.version.description = Description +arch.version.provides = Fournit +arch.version.groups = Groupe +arch.version.depends = Dépend +arch.version.optdepends = Dépendances optionnelles +arch.version.checkdepends = Vérifier les dépendances +arch.version.conflicts = Conflits +arch.version.replaces = Remplace +arch.version.backup = Sauvegarde +arch.version.makedepends = Faire des dépendances +container.images.title = Images +search_in_external_registry = Rechercher dans %s +alt.repository = Informations sur le dépôt +alt.repository.architectures =Architectures +alt.registry = Configurez ce registre à partir d'un terminal : +alt.registry.install = Pour installer le paquet, exécutez la commande suivante : +alt.install = Installer le paquet +alt.repository.multiple_groups = Ce paquet est disponible dans plusieurs groupes. +alt.setup = Ajouter un dépôt à la liste des dépôts connecté (choisissez l'architecture nécessaire à la place de "_arch") : [secrets] secrets=Secrets @@ -3429,8 +3724,68 @@ deletion.failed=Impossible de supprimer le secret. management=Gestion des secrets [actions] +actions=Actions + unit.desc=Gérer l'intégration continue avec Forgejo Actions. +status.unknown=Inconnu +status.waiting=En attente +status.running=En cours d'exécution +status.success=Succès +status.failure=Échec +status.cancelled=Annulé +status.skipped=Ignoré +status.blocked=Bloqué + +runners=Exécuteurs +runners.runner_manage_panel=Gestion des exécuteurs +runners.new=Créer un nouvel exécuteur +runners.new_notice=Comment démarrer un exécuteur +runners.status=Statut +runners.id=ID +runners.name=Nom +runners.owner_type=Type +runners.description=Description +runners.labels=Labels +runners.last_online=Dernière fois en ligne +runners.runner_title=Exécuteur +runners.task_list=Tâches récentes sur cet exécuteur +runners.task_list.no_tasks=Il n'y a pas de tâche ici. +runners.task_list.run=Exécuter +runners.task_list.status=Statut +runners.task_list.repository=Dépôt +runners.task_list.commit=Révision +runners.task_list.done_at=Fait à +runners.edit_runner=Éditer l'Exécuteur +runners.update_runner=Appliquer les modifications +runners.update_runner_success=Exécuteur mis à jour avec succès +runners.update_runner_failed=Impossible d'actualiser l'Exécuteur +runners.delete_runner=Supprimer cet exécuteur +runners.delete_runner_success=Exécuteur supprimé avec succès +runners.delete_runner_failed=Impossible de supprimer l'Exécuteur +runners.delete_runner_header=Confirmer la suppression de cet exécuteur +runners.delete_runner_notice=Si une tâche est en cours sur cet exécuteur, elle sera terminée et marquée comme échouée. Cela risque d’interrompre le flux de travail. +runners.none=Aucun exécuteur disponible +runners.status.unspecified=Inconnu +runners.status.idle=Inactif +runners.status.active=Actif +runners.status.offline=Hors-ligne +runners.version=Version +runners.reset_registration_token=Réinitialiser le jeton d'enregistrement +runners.reset_registration_token_success=Le jeton d’inscription de l’exécuteur a été réinitialisé avec succès + +runs.all_workflows=Tous les workflows +runs.commit=Révision +runs.scheduled=Planifié +runs.pushed_by=soumis par +runs.invalid_workflow_helper=La configuration du flux de travail est invalide. Veuillez vérifier votre fichier %s +runs.no_matching_online_runner_helper=Aucun exécuteur en ligne correspondant au libellé %s +runs.actor=Acteur +runs.status=Statut +runs.actors_no_select=Tous les acteurs +runs.status_no_select=Touts les statuts +runs.no_results=Aucun résultat correspondant. +runs.no_workflows=Il n'y a pas encore de workflows. runs.no_runs=Le flux de travail n'a pas encore d'exécution. runs.empty_commit_message=(message de révision vide) @@ -3442,8 +3797,25 @@ workflow.disabled=Le flux de travail est désactivé. need_approval_desc=Besoin d’approbation pour exécuter des flux de travail pour une demande d’ajout de bifurcation. +variables=Variables +variables.management=Gestion des variables +variables.creation=Ajouter une variable +variables.none=Il n'y a pas encore de variables. +variables.deletion=Retirer la variable +variables.deletion.description=La suppression d’une variable est permanente et ne peut être défaite. Continuer ? +variables.description=Les variables sont passées aux actions et ne peuvent être lues autrement. variables.id_not_exist = La variable numéro %d n’existe pas. +variables.edit=Modifier la variable +variables.deletion.failed=Impossible de retirer la variable. +variables.deletion.success=La variable a bien été retirée. +variables.creation.failed=Impossible d'ajouter la variable. +variables.creation.success=La variable « %s » a été ajoutée. +variables.update.failed=Impossible d’éditer la variable. +variables.update.success=La variable a bien été modifiée. +runs.workflow = Workflow +runs.no_job_without_needs = Le workflow doit contenir au moins une tâche sans dépendances. workflow.dispatch.use_from = Utiliser un workflow depuis +runs.no_job = Le workflow doit au moins contenir une tâche workflow.dispatch.trigger_found = Ce workflow a un déclencheur d'événement workflow_dispatch. workflow.dispatch.run = Exécuter le workflow workflow.dispatch.success = L'exécution du workflow a bien été demandée. @@ -3453,6 +3825,7 @@ workflow.dispatch.warn_input_limit = Affichage des %d premiers champs seulement. runs.expire_log_message = Les journaux ont été purgés car ils étaient trop anciens. runs.no_workflows.help_write_access = Vous ne savez pas par où commencer avec Forgejo Actions ? Regardez la section démarrage rapide dans la documentation utilisateur pour écrire votre premier workflow, puis mettre en place un Forgejo runner pour exécuter vos jobs. runs.no_workflows.help_no_write_access = Pour en savoir plus sur Forgejo Actions, consultez la documentation. +variables.not_found = La variable n'a pas été trouvée. [projects] type-1.display_name=Projet personnel @@ -3499,8 +3872,18 @@ regexp = RegExp [munits.data] +b = o +mib = Mio +kib = Kio +gib = Gio +tib = Tio +pib = Pio +eib = Eio [markup] +filepreview.line = Ligne %[1]d dans %[2]s +filepreview.lines = Lignes %[1]d jusqu'à %[2]d dans %[3]s +filepreview.truncated = L'aperçu a été tronqué [repo.permissions] pulls.write = Écrire : Fermer des demandes de tirage et gérer les métadonnées telles que les étiquettes, les jalons, les assignés, les dates d'échéance et les dépendances. diff --git a/options/locale/locale_fur.ini b/options/locale/locale_fur.ini deleted file mode 100644 index 8b13789179..0000000000 --- a/options/locale/locale_fur.ini +++ /dev/null @@ -1 +0,0 @@ - diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index 76ebcd1b91..7991c98994 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -35,6 +35,18 @@ captcha = CAPTCHA twofa = Fíordheimhniú Dhá-Fhachtóir twofa_scratch = Cód Scratch Dhá-Fhachtóra passcode = Paschód +webauthn_insert_key = Cuir isteach d'eochair slándála +webauthn_sign_in = Brúigh an cnaipe ar d'eochair slándála. Mura bhfuil aon chnaipe ag d'eochair slándála, cuir isteach é arís. +webauthn_press_button = Brúigh an cnaipe ar d'eochair slándála le do thoil… +webauthn_use_twofa = Úsáid cód dhá fhachtóir ó do ghuthán +webauthn_error = Ní fhéadfaí do eochair slándála a léamh. +webauthn_unsupported_browser = Ní thacaíonn do bhrabhsálaí le WebAuthn faoi láthair. +webauthn_error_unknown = Tharla earráid anaithnid. Déan iarracht arís. +webauthn_error_insecure = Ní thacaíonn WebAuthn ach le naisc slán. Le haghaidh tástála thar HTTP, is féidir leat an bunús “localhost” nó "127.0.0.1" a úsáid +webauthn_error_unable_to_process = Ní fhéadfadh an freastalaí d'iarratas a phróiseáil. +webauthn_error_duplicated = Ní cheadaítear an eochair slándála don iarratas seo. Déan cinnte le do thoil nach bhfuil an eochair cláraithe cheana féin. +webauthn_error_empty = Ní mór duit ainm a shocrú don eochair seo. +webauthn_error_timeout = Sroicheadh an teorainn ama sula bhféadfaí d’eochair a léamh. Athlódáil an leathanach seo, le do thoil, agus déan iarracht arís. repository = Stór organization = Eagraíocht mirror = Scáthán @@ -112,6 +124,7 @@ filter.is_archived = Cartlannaithe filter.not_archived = Gan Cartlannaithe filter.public = Poiblí filter.private = Príobháideach + return_to_forgejo = Fill ar ais go Forgejo toggle_menu = Roghchlár scoránaigh new_repo.title = Stórlann nua @@ -152,6 +165,7 @@ no_results = Níl aon torthaí meaitseála le fáil. issue_kind = Saincheisteanna cuardaigh… pull_kind = Cuardaigh iarratais tarraingthe… keyword_search_unavailable = Níl cuardach de réir eochairfhocal ar fáil faoi láthair. Déan teagmháil le riarthóir an láithreáin. + union = Aontas union_tooltip = Cuir torthaí san áireamh a mheaitseálann aon cheann de na heochairfhocail scartha le spás bán regexp = RegExp @@ -161,12 +175,14 @@ regexp_tooltip = Léirmhínigh an téarma cuardaigh mar ghnáthléiriú navbar = Barra Nascleanúint footer = Buntásc footer.links = Naisc + footer.software = Maidir leis an mbogearra seo [heatmap] number_of_contributions_in_the_last_12_months = %s ranníocaíochtaí le 12 mhí anuas less = Níos lú more = Níos mó + contributions_zero = Gan aon ranníocaíochtaí contributions_format = {contributions} ar {month} {day}, {year} contributions_one = ranníocaíocht @@ -178,7 +194,7 @@ buttons.bold.tooltip = Cuir téacs trom leis (Ctrl+B / ⌘B) buttons.italic.tooltip = Cuir téacs iodálach leis (Ctrl+I / ⌘I) buttons.quote.tooltip = Téacs luaigh buttons.code.tooltip = Cuir cód leis -buttons.link.tooltip = Cuir nasc leis (Ctrl+K / ⌘K) +buttons.link.tooltip = Cuir nasc leis buttons.list.unordered.tooltip = Cuir liosta piléar leis buttons.list.ordered.tooltip = Cuir liosta uimhrithe buttons.list.task.tooltip = Cuir liosta tascanna leis @@ -187,6 +203,7 @@ buttons.ref.tooltip = Déan tagairt d'eisiúint nó iarratas tarraingthe buttons.switch_to_legacy.tooltip = Úsáid an eagarthóir oidhreachta ina ionad buttons.enable_monospace_font = Cumasaigh cló monospace buttons.disable_monospace_font = Díchumasaigh cló monospace + buttons.indent.tooltip = Míreanna neadaithe leibhéal amháin buttons.unindent.tooltip = Díneadaigh míreanna leibhéal amháin buttons.new_table.tooltip = Cuir tábla leis @@ -208,6 +225,7 @@ string.desc = Z - A occurred = Tharla earráid not_found = Ní raibh an sprioc in ann a fháil. network_error = Earráid líonra + report_message = Má chreideann tú gur fabht Forgejo atá ann, déan cuardach ar shaincheisteanna ar Codeberg nó oscail saincheist nua más gá. server_internal = Earráid inmheánach freastalaí @@ -218,6 +236,7 @@ install_desc = Níl ort ach Forgejo! Bí linn trí cur leis chun an tionscadal seo a dhéanamh níos fearr fós. Ná bíodh drogall ort a bheith i do rannpháirtí! @@ -297,7 +316,9 @@ password_algorithm_helper = Socraigh an algartam haiseála pasfhocail. Bíonn ri enable_update_checker = Cumasaigh Seiceoir Nuashonraithe env_config_keys = Cumraíocht Comhshaoil env_config_keys_prompt = Cuirfear na hathróga comhshaoil seo a leanas i bhfeidhm ar do chomhad cumraíochta freisin: + docker_helper = Má ritheann tú Forgejo taobh istigh de Docker, léigh an doiciméadú le do thoil sula n-athraíonn tú aon socruithe. + require_db_desc = Éilíonn Forgejo MySQL, PostgreSQL, SQLite3 nó TiDB (prótacal MySQL). sqlite_helper = Cosán comhaid don bhunachar sonraí SQLite3.
Cuir isteach cosán absalóideach má ritheann tú Forgejo mar sheirbhís. reinstall_error = Tá tú ag iarraidh suiteáil i mbunachar sonraí Forgejo atá ann cheana féin @@ -354,6 +375,7 @@ show_both_private_public = Ag taispeáint poiblí agus príobháideach araon show_only_private = Ag taispeáint príobháideach amháin show_only_public = Ag taispeáint poiblí amháin issues.in_your_repos = I do stórais + my_orgs = Eagraíochtaí [explore] @@ -423,6 +445,7 @@ password_pwned = Tá an pasfhocal a roghnaigh tú ar Sínigh isteach anois! hint_register = An bhfuil cuntas uait? Cláraigh anois. sign_up_button = Cláraigh anois. @@ -483,6 +506,7 @@ team_invite.subject = Tá cuireadh tugtha agat ag %[1]s chun dul le heagraíocht team_invite.text_1 = Tá cuireadh tugtha ag %[1]s duit chun dul le foireann %[2]s in eagraíocht %[3]s. team_invite.text_2 = Cliceáil ar an nasc seo a leanas le do thoil chun dul isteach san fhoireann: team_invite.text_3 = Nóta: Bhí an cuireadh seo beartaithe do %[1]s. Mura raibh tú ag súil leis an gcuireadh seo, is féidir leat neamhaird a dhéanamh den ríomhphost seo. + link_not_working_do_paste = Nach bhfuil an nasc ag obair? Bain triail as é a chóipeáil agus a ghreamú i mbarra URL do bhrabhsálaí. admin.new_user.subject = Úsáideoir nua %s díreach cláraithe admin.new_user.user_info = Faisnéis úsáideora @@ -588,6 +612,7 @@ must_use_public_key = Is eochair phríobháideach an eochair a sholáthraíonn t auth_failed = Theip ar fhíordheimhniú:%v target_branch_not_exist = Níl spriocbhrainse ann. admin_cannot_delete_self = Ní féidir leat tú féin a scriosadh nuair is riarachán tú. Bain do phribhléidí riaracháin ar dtús. + FullName = Ainm iomlán Description = Cur síos Pronouns = Forainmneacha @@ -630,6 +655,7 @@ settings = Socruithe Úsáideora disabled_public_activity = Dhíchumasaigh an t-úsáideoir seo infheictheacht phoiblí na gníomhaíochta. form.name_reserved = Tá an t-ainm úsáideora "%s" in áirithe. form.name_pattern_not_allowed = Ní cheadaítear an patrún "%s" in ainm úsáideora. + followers.title.one = Leantóir followers.title.few = Leantóirí following.title.one = Ag leanúint @@ -897,6 +923,7 @@ visibility.public_tooltip = Infheicthe do gach duine visibility.limited = Teoranta visibility.private = Príobháideach visibility.private_tooltip = Ní fheictear ach do bhaill d'eagraíochtaí a chuaigh tú isteach + orgs = Eagraíochtaí blocked_users = Úsáideoirí blocáilte storage_overview = Forbhreathnú ar stóráil @@ -1091,6 +1118,14 @@ migrate_options_lfs_endpoint.label = Críochphointe LFS migrate_options_lfs_endpoint.description = Déanfaidh imirce iarracht do chianda Git a úsáid chun freastalaí LFS a chinneadh. Is féidir leat críochphointe saincheaptha a shonrú freisin má tá na sonraí LFS stórtha stóráilte áit éigin eile. migrate_options_lfs_endpoint.description.local = Tacaítear le cosán freastalaí áitiúil freisin. migrate_options_lfs_endpoint.placeholder = Má fhágtar bán, díorthófar an críochphointe ón URL clóin +migrate_items = Míreanna Imirce +migrate_items_wiki = Wiki +migrate_items_milestones = Clocha míle +migrate_items_labels = Lipéid +migrate_items_issues = Saincheisteanna +migrate_items_pullrequests = Iarrataí Tarraing +migrate_items_merge_requests = Iarrataí Cumaisc +migrate_items_releases = Eisiúintí migrate_repo = Stóras Imirc migrate.clone_address = Aimirce/ Clón Ó URL migrate.github_token_desc = Is féidir leat comhartha amháin nó níos mó a chur anseo scartha le camóga chun an t-aistriú a dhéanamh níos tapúla trí theorainn ráta API GitHub a sheachaint. RABHADH: D’fhéadfadh mí-úsáid a bhaint as an ngné seo sárú a dhéanamh ar pholasaí an tsoláthraí seirbhíse agus d’fhéadfadh sé go gcuirfí bac ar do chuntas(í). @@ -1107,6 +1142,15 @@ migrate.migrating = Ag aistriú ó %s … migrate.migrating_failed = Theip ar aistriú ó %s. migrate.migrating_failed.error = Theip ar aistriú: %s migrate.migrating_failed_no_addr = Theip ar an imirce. +migrate.migrating_git = Sonraí Git a Aimirce +migrate.migrating_topics = Ábhair Imirce +migrate.migrating_milestones = Clocha Míle a Imirce +migrate.migrating_labels = Lipéid Imirce +migrate.migrating_releases = Eisiúintí Imirce +migrate.migrating_issues = Saincheisteanna Imirce +migrate.migrating_pulls = Iarratais Tarraingthe á n-Imirce +migrate.cancel_migrating_title = Cealaigh Imirce +migrate.cancel_migrating_confirm = Ar mhaith leat an imirce seo a chealú? mirror_from = scáthán de forked_from = forcailte ó generated_from = a ghintear ó @@ -1441,7 +1485,7 @@ issues.ref_pull_from = `tagairt don iarratas tarraingthe seo %[3 issues.ref_from = `ó %[1]s` issues.author = Údar issues.role.owner = Úinéir -issues.role.owner_helper = Is úinéir an stórais seo an t-úsáideoir seo. +issues.role.owner_helper = Is é an t-úsáideoir seo úinéir an stór seo. issues.role.member = Comhalta issues.role.member_helper = Is ball den eagraíocht é an t-úsáideoir seo a bhfuil an stór seo ina úinéireacht. issues.role.collaborator = Comhoibritheoir @@ -2134,7 +2178,7 @@ settings.matrix.room_id = ID seomra settings.matrix.message_type = Cineál teachtaireachta settings.archive.button = Cartlann Stóras settings.archive.header = Cartlann an Stóras seo -settings.archive.text = Trí an stór a chartlannú, beidh sé inléite amháin go hiomlán. Beidh sé i bhfolach ón deais. Ní bheidh aon duine (fiú tusa!) in ann gealltanais nua a dhéanamh, ná aon saincheisteanna ná iarratais tarraingthe a oscailt. Moltar an chúis cartlannúcháin a dhoiciméadú chun treoir a thabhairt do fhorbróirí amach anseo a bhfuil sé beartaithe acu an stór a fhorcadh. +settings.archive.text = Má dhéantar an stóras a chartlannú, beidh sé léite go hiomlán amháin. Beidh sé i bhfolach ón bpainéal. Aon duine (ní fiú tú!) beidh siad in ann tiomantas nua a dhéanamh, nó aon saincheisteanna nó iarratais a tharraingt a oscailt. settings.archive.success = Rinneadh an stóras a chartlannú go rathúil. settings.archive.error = Tharla earráid agus tú ag iarraidh an stóras a chartlannú. Féach an logáil le haghaidh tuilleadh sonraí. settings.archive.error_ismirror = Ní féidir leat stóras scátháin a chartlannú. @@ -2311,6 +2355,7 @@ find_file.no_matching = Níl aon chomhad meaitseála le fáil error.csv.too_large = Ní féidir an comhad seo a rinneadh toisc go bhfuil sé ró-mhór. error.csv.unexpected = Ní féidir an comhad seo a rindreáil toisc go bhfuil carachtar ann gan súil leis i líne %d agus i gcolún %d. error.csv.invalid_field_count = Ní féidir an comhad seo a rindreáil toisc go bhfuil líon mícheart réimsí i líne %d. + rss.must_be_on_branch = Ní mór duit a bheith ar bhrainse le go mbeidh fotha RSS agat. admin.manage_flags = Bainistigh bratacha admin.enabled_flags = Bratacha cumasaithe don stórlann: @@ -2766,6 +2811,7 @@ teams.all_repositories_helper = Tá rochtain ag an bhfoireann ar gach stórais. teams.invite.title = Tugadh cuireadh duit dul isteach i bhfoireann %s san eagraíocht %s. teams.invite.by = Ar cuireadh ó %s teams.invite.description = Cliceáil ar an gcnaipe thíos le do thoil chun dul isteach san fhoireann. + open_dashboard = Oscail an painéal rialaithe repo_updated = Nuashonraithe %s follow_blocked_user = Ní féidir leat an eagraíocht seo a leanúint mar tá bac curtha ag an eagraíocht seo ort. @@ -2836,6 +2882,33 @@ dashboard.reinit_missing_repos = Aththosaigh gach stórais Git atá in easnamh a dashboard.sync_external_users = Sioncrónaigh sonraí úsáideoirí seachtracha dashboard.cleanup_hook_task_table = Glan suas an tábla hook_task dashboard.cleanup_packages = Glan suas pacáistí atá imithe in éag +dashboard.server_uptime = Aga fónaimh Freastalaí +dashboard.current_goroutine = Goroutines Reatha +dashboard.current_memory_usage = Úsáid Cuimhne Reatha +dashboard.total_memory_allocated = Cuimhne Iomlán Leithdháilte +dashboard.memory_obtained = Cuimhne Faighte +dashboard.pointer_lookup_times = Amanna Cuardaigh Pointeora +dashboard.memory_allocate_times = Leithdháiltí Cuimhne +dashboard.memory_free_times = Saorálann Cuimhne +dashboard.current_heap_usage = Úsáid Charn Reatha +dashboard.heap_memory_obtained = Cuimhne Charn Faighte +dashboard.heap_memory_idle = Díomhaoin Cuimhne Carn +dashboard.heap_memory_in_use = Cuimhne Carm In Úsáid +dashboard.heap_memory_released = Cuimhne Carn Eisithe +dashboard.heap_objects = Cuspóirí Carn +dashboard.bootstrap_stack_usage = Úsáid Staca Bootstrap +dashboard.stack_memory_obtained = Cuimhne Staca Faighte +dashboard.mspan_structures_usage = Úsáid Struchtúir MSpan +dashboard.mspan_structures_obtained = Struchtúir MSpan a Faightear +dashboard.mcache_structures_usage = Úsáid Struchtúir MCache +dashboard.mcache_structures_obtained = Struchtúir MCache a Faightear +dashboard.profiling_bucket_hash_table_obtained = Tábla Hash Buicéad Próifílithe a Faightear +dashboard.gc_metadata_obtained = Meiteashonraí GC faighte +dashboard.other_system_allocation_obtained = Leithdháileadh Córais Eile a Fuarthas +dashboard.next_gc_recycle = Athchúrsáil GC Eile +dashboard.total_gc_pause = Sos Iomlán GC +dashboard.last_gc_pause = Sos GC Deireanach +dashboard.gc_times = Amanna GC dashboard.delete_old_actions = Scrios gach sean-ghníomhaíocht ón mbunachar dashboard.delete_old_actions.started = Scrios na sean-ghníomhaíocht go léir ón mbunachar sonraí tosaithe. dashboard.update_checker = Seiceoir nuashonraithe @@ -2873,6 +2946,18 @@ users.purge = Úsáideoir a Ghlanadh users.still_own_packages = Tá pacáiste amháin nó níos mó fós ag an úsáideoir seo, scrios na pacáistí seo ar dtús. users.deletion_success = Scriosadh an cuntas úsáideora. users.reset_2fa = Athshocraigh 2FA +users.list_status_filter.menu_text = Scagaire +users.list_status_filter.reset = Athshocraigh +users.list_status_filter.is_active = Gníomhach +users.list_status_filter.not_active = Neamhghníomhach +users.list_status_filter.is_admin = Riarachán +users.list_status_filter.not_admin = Ní Riarachán +users.list_status_filter.is_restricted = Srianta +users.list_status_filter.not_restricted = Gan Srian +users.list_status_filter.is_prohibit_login = Cosc ar Logáil Isteach +users.list_status_filter.not_prohibit_login = Ceadaigh Logáil isteach +users.list_status_filter.is_2fa_enabled = 2FA Cumasaithe +users.list_status_filter.not_2fa_enabled = 2FA faoi mhíchumas users.details = Sonraí Úsáideora emails.primary = Bunscoile emails.activated = Gníomhachtaithe @@ -3136,6 +3221,25 @@ monitor.execute_time = Am Forghníomhaithe monitor.last_execution_result = Toradh monitor.process.cancel = Cealaigh próiseas monitor.process.children = Leanaí +monitor.queues = Scuaineanna +monitor.queue = Scuaine: %s +monitor.queue.name = Ainm +monitor.queue.type = Cineál +monitor.queue.exemplar = Cineál Eiseamláire +monitor.queue.numberworkers = Líon na nOibrithe +monitor.queue.activeworkers = Oibrithe Gníomhacha +monitor.queue.maxnumberworkers = Líon Uasta na nOibrithe +monitor.queue.numberinqueue = Uimhir i scuaine +monitor.queue.review_add = Athbhreithniú / Cuir Oibrithe leis +monitor.queue.settings.title = Socruithe Linn +monitor.queue.settings.desc = Fásann linnte go dinimiciúil mar fhreagra ar a gcuid scuaine oibrithe a bhlocáil. +monitor.queue.settings.maxnumberworkers = Uaslíon na n-oibrithe +monitor.queue.settings.maxnumberworkers.placeholder = Faoi láthair %[1]d +monitor.queue.settings.maxnumberworkers.error = Caithfidh uaslíon na n-oibrithe a bheith ina uimhir +monitor.queue.settings.submit = Nuashonrú Socruithe +monitor.queue.settings.changed = Socruithe Nuashonraithe +monitor.queue.settings.remove_all_items = Bain gach +monitor.queue.settings.remove_all_items_done = Baineadh na míreanna go léir sa scuaine. notices.system_notice_list = Fógraí Córais notices.operations = Oibríochtaí notices.select_all = Roghnaigh Gach @@ -3152,6 +3256,7 @@ notices.delete_success = Scriosadh na fógraí córais. self_check.no_problem_found = Níor aimsíodh aon fhadhb fós. self_check.database_collation_mismatch = Bí ag súil le comhthiomsú a úsáid sa bhunachar sonraí: %s self_check.database_inconsistent_collation_columns = Tá comhthiomsú %s in úsáid ag an mbunachar sonraí, ach tá comhthiomsuithe mímheaitseála á n-úsáid ag na colúin seo. D'fhéadfadh sé a bheith ina chúis le roinnt fadhbanna gan choinne. + dashboard.new_version_hint = Tá Forgejo %s ar fáil anois, tá %s á rith agat. Seiceáil an blag le haghaidh tuilleadh sonraí. dashboard.operations = Oibríochtaí cothabhála dashboard.delete_repo_archives = Scrios cartlanna na stórtha uile (ZIP, TAR.GZ, srl.) @@ -3160,6 +3265,7 @@ dashboard.resync_all_sshkeys = Nuashonraigh an comhad ".ssh/authorized_keys" le dashboard.resync_all_sshprincipals = Nuashonraigh an comhad ".ssh/authorized_principals" le príomhoidí SSH Forgejo. dashboard.resync_all_hooks = Athshioncrónaigh crúcaí Git na stórtha uile (réamhghlacadh, nuashonrú, iarghlacadh, próiseasghlacadh, ...) dashboard.cleanup_actions = Glan suas logaí agus déantáin atá imithe in éag ó ghníomhartha +dashboard.last_gc_time = Am ó GC deireanach dashboard.stop_zombie_tasks = Stop tascanna gníomhartha zombie dashboard.stop_endless_tasks = Stop tascanna gníomhartha gan teorainn dashboard.cancel_abandoned_jobs = Cealaigh poist gníomhartha tréigthe @@ -3288,10 +3394,30 @@ raw_seconds = soicind raw_minutes = nóiméad [dropzone] +default_message = Scaoil comhaid nó cliceáil anseo chun iad a uaslódáil. +invalid_input_type = Ní féidir leat comhaid den chineál seo a uaslódáil. +file_too_big = Sáraíonn méid comhaid ({{filesize}} MB) an t-uasmhéid de ({{maxFilesize}} MB). +remove_file = Bain an comhad [notification] +notifications = Fógraí +unread = Gan léamh +read = Léigh +no_unread = Gan aon fhógraí neamh-léite. +no_read = Gan aon fhógraí léite. +pin = Fógra bioráin +mark_as_read = Marcáil mar léite +mark_as_unread = Marcáil mar neamh-léite +mark_all_as_read = Marcáil gach ceann mar léite +subscriptions = Síntiúis +watching = Ag féachaint +no_subscriptions = Gan síntiúis [gpg] +default_key = Sínithe leis an eochair réamhshocraithe +error.extract_sign = Theip ar an síniú a bhaint +error.generate_hash = Theip ar hash gealltanas a ghiniúint +error.no_committer_account = Níl aon chuntas nasctha le seoladh ríomhphoist an tiomnóra [units] unit = Aonad @@ -3299,11 +3425,153 @@ error.no_unit_allowed_repo = Níl cead agat rochtain a fháil ar aon chuid den t error.unit_not_allowed = Níl cead agat an rannán stóras seo a rochtain. [packages] +title = Pacáistí desc = Bainistigh pacáistí stórais. +empty = Níl aon phacáistí ann fós. +empty.documentation = Le haghaidh tuilleadh eolais ar chlárlann na bpacáistí, féach ar na doiciméid. +empty.repo = An ndearna tú uaslódáil ar phacáiste, ach nach bhfuil sé léirithe anseo? Téigh go socruithe pacáiste agus nasc leis an stóras seo é. +registry.documentation = Le haghaidh tuilleadh eolais ar chlárlann %s, féach ar na doiciméid. +filter.type = Cineál +filter.type.all = Gach +filter.no_result = Níor thug do scagaire aon torthaí. +filter.container.tagged = Clibeáilte +filter.container.untagged = Gan chlib +published_by = Foilsithe %[1]s ag %[3]s +published_by_in = Foilsithe ag %[1]s ag %[3]s in %[5]s +installation = Suiteáil +about = Maidir leis an bpacáiste seo +requirements = Riachtanais +dependencies = Spleithiúlachtaí +keywords = Eochairfhocail +details = Sonraí +details.author = Údar +details.license = Ceadúnas +assets = Sócmhainní +versions = Leaganacha +versions.view_all = Féach ar gach +dependency.id = ID +dependency.version = Leagan +search_in_external_registry = Cuardaigh i %s +alpine.registry = Socraigh an chlár seo tríd an url a chur i do chomhad /etc/apk/repositories: +alpine.registry.key = Íoslódáil eochair RSA poiblí na clárlainne isteach san fhillteán /etc/apk/keys/ chun an síniú innéacs a fhíorú: +alpine.registry.info = Roghnaigh $branch agus $repository ón liosta thíos. +alpine.install = Chun an pacáiste a shuiteáil, rith an t-ordú seo a leanas: +alpine.repository = Eolas Stórais +alpine.repository.branches = Brainsí +alpine.repository.repositories = Stórais +alpine.repository.architectures = Ailtireachtaí +cargo.registry = Socraigh an clárlann seo sa chomhad cumraíochta lasta (mar shampla ~/.cargo/config.toml): +cargo.install = Chun an pacáiste a shuiteáil ag baint úsáide as Cargo, reáchtáil an t-ordú seo a leanas: +chef.registry = Socraigh an clárlann seo i do chomhad ~/.chef/config.rb: +chef.install = Chun an pacáiste a shuiteáil, rith an t-ordú seo a leanas: +composer.registry = Socraigh an chlár seo i do chomhad ~/.composer/config.json: +composer.install = Chun an pacáiste a shuiteáil ag baint úsáide as Cumadóir, reáchtáil an t-ordú seo a leanas: +composer.dependencies = Spleithiúlachtaí +composer.dependencies.development = Spleithiúlachtaí Forbartha conan.details.repository = Stóras +conan.registry = Socraigh an clárlann seo ón líne ordaithe: +conan.install = Chun an pacáiste a shuiteáil ag úsáid Conan, reáchtáil an t-ordú seo a leanas: +conda.registry = Socraigh an chlár seo mar stóras Conda i do chomhad .condarc: +conda.install = Chun an pacáiste a shuiteáil ag úsáid Conda, reáchtáil an t-ordú seo a leanas: +container.details.type = Cineál Íomhá +container.details.platform = Ardán +container.pull = Tarraing an íomhá ón líne ordaithe: +container.digest = Díleáigh +container.multi_arch = Córas Oibriúcháin / Ailtireacht +container.layers = Sraitheanna Íomhá +container.labels = Lipéid +container.labels.key = Eochair +container.labels.value = Luach +cran.registry = Cumraigh an chlárlann seo i do chomhad Rprofile.site: +cran.install = Chun an pacáiste a shuiteáil, rith an t-ordú seo a leanas: +debian.registry = Socraigh an clárlann seo ón líne ordaithe: +debian.registry.info = Roghnaigh $distribution agus $component ón liosta thíos. +debian.install = Chun an pacáiste a shuiteáil, rith an t-ordú seo a leanas: +debian.repository = Eolas Stóras +debian.repository.distributions = Dáiltí +debian.repository.components = Comhpháirteanna +debian.repository.architectures = Ailtireachtaí +generic.download = Íoslódáil pacáiste ón líne ordaithe: +go.install = Suiteáil an pacáiste ón líne ordaithe: +helm.registry = Socraigh an clárlann seo ón líne ordaithe: +helm.install = Chun an pacáiste a shuiteáil, rith an t-ordú seo a leanas: +maven.registry = Socraigh an clárlann seo i do chomhad pom.xml tionscadail: +maven.install = Chun an pacáiste a úsáid cuir na nithe seo a leanas sa bhloc spleáchais sa chomhad pom.xml: +maven.install2 = Rith tríd an líne ordaithe: +maven.download = Chun an spleáchas a íoslódáil, rith tríd an líne ordaithe: +nuget.registry = Socraigh an clárlann seo ón líne ordaithe: +nuget.install = Chun an pacáiste a shuiteáil ag úsáid NuGet, reáchtáil an t-ordú seo a leanas: +nuget.dependency.framework = Spriocchreat +npm.registry = Socraigh an chlárlann seo i do chomhad .npmrc do thionscadail: +npm.install = Chun an pacáiste a shuiteáil ag úsáid npm, reáchtáil an t-ordú seo a leanas: +npm.install2 = nó cuir leis an gcomhad package.json é: +npm.dependencies = Spleithiúlachtaí +npm.dependencies.development = Spleithiúlachtaí Forbartha +npm.dependencies.bundle = Spleáchais Chuachta +npm.dependencies.peer = Spleithiúlachtaí Piaraí +npm.dependencies.optional = Spleáchais Roghnacha +npm.details.tag = Clib +pub.install = Chun an pacáiste a shuiteáil ag úsáid Dart, reáchtáil an t-ordú seo a leanas: +pypi.requires = Teastaíonn Python +pypi.install = Chun an pacáiste a shuiteáil ag úsáid pip, reáchtáil an t-ordú seo a leanas: +rpm.registry = Socraigh an clárlann seo ón líne ordaithe: +rpm.distros.redhat = ar dháileadh bunaithe ar RedHat +rpm.distros.suse = ar dháileadh bunaithe ar SUSE +rpm.install = Chun an pacáiste a shuiteáil, rith an t-ordú seo a leanas: +rpm.repository = Eolas Stóras +rpm.repository.architectures = Ailtireachtaí +rpm.repository.multiple_groups = Tá an pacáiste seo ar fáil i ngrúpaí éagsúla. +rubygems.install = Chun an pacáiste a shuiteáil ag baint úsáide as gem, reáchtáil an t-ordú seo a leanas: +rubygems.install2 = nó cuir leis an Gemfile é: +rubygems.dependencies.runtime = Spleáchais Rith-Ama +rubygems.dependencies.development = Spleáchais Forbartha +rubygems.required.ruby = Éilíonn leagan Ruby +rubygems.required.rubygems = Éilíonn leagan RubyGem +swift.registry = Socraigh an clárlann seo ón líne ordaithe: +swift.install = Cuir an pacáiste i do chomhad Package.swift: +swift.install2 = agus reáchtáil an t-ordú seo a leanas: +vagrant.install = Chun bosca Vagrant a chur leis, reáchtáil an t-ordú seo a leanas: +settings.link = Nasc an pacáiste seo le stóras +settings.link.description = Má nascann tú pacáiste le stóras, liostaítear an pacáiste i liosta pacáistí an stórais. +settings.link.select = Roghnaigh Stóras +settings.link.button = Nuashonraigh Nasc Stórais +settings.link.success = D'éirigh le nasc an stórais a nuashonrú. +settings.link.error = Theip ar an nasc stóras a nuashonrú. +settings.delete = Scrios pacáiste +settings.delete.description = Tá pacáiste a scriosadh buan agus ní féidir é a chur ar ais. +settings.delete.notice = Tá tú ar tí %s (%s) a scriosadh. Tá an oibríocht seo dochúlaithe, an bhfuil tú cinnte? +settings.delete.success = Tá an pacáiste scriosta. +settings.delete.error = Theip ar an pacáiste a scriosadh. +owner.settings.cargo.title = Innéacs Clárlann Lasta +owner.settings.cargo.initialize = Innéacs a chur i dtosach +owner.settings.cargo.initialize.description = Tá gá le stóras innéacs speisialta Git chun an clárlann Cargo a úsáid. Tríd an rogha seo, cruthófar an stóras (nó athchruthófar é) agus cumrófar é go huathoibríoch. +owner.settings.cargo.initialize.error = Níorbh fhéidir an t-innéacs Cargo a thúsú: %v +owner.settings.cargo.initialize.success = Cruthaíodh an t-innéacs Cargo go rathúil. +owner.settings.cargo.rebuild = Innéacs Atógáil +owner.settings.cargo.rebuild.description = Is féidir atógáil a bheith úsáideach mura bhfuil an t-innéacs sioncronaithe leis na pacáistí Cargo stóráilte. +owner.settings.cargo.rebuild.error = Níorbh fhéidir an t-innéacs Cargo a atógáil: %v +owner.settings.cargo.rebuild.success = D'éirigh leis an innéacs Cargo a atógáil. +owner.settings.cleanuprules.add = Cuir Riail Glantacháin leis +owner.settings.cleanuprules.edit = Cuir Riail Glantacháin in eagar +owner.settings.cleanuprules.preview = Réamhamharc Riail Glantacháin +owner.settings.cleanuprules.preview.overview = Tá pacáistí %d beartaithe a bhaint. +owner.settings.cleanuprules.preview.none = Ní hionann riail glantacháin agus pacáistí ar bith. owner.settings.cleanuprules.enabled = Cumasaithe +owner.settings.cleanuprules.pattern_full_match = Cuir patrún i bhfeidhm ar ainm an phacáiste iomlán +owner.settings.cleanuprules.keep.title = Coinnítear leaganacha a mheaitseálann leis na rialacha seo, fiú má mheaitseálann siad riail bhaint thíos. +owner.settings.cleanuprules.keep.count = Coinnigh an ceann is déanaí owner.settings.cleanuprules.keep.count.1 = 1 leagan in aghaidh an phacáiste owner.settings.cleanuprules.keep.count.n = Leaganacha %d in aghaidh an phacáiste +owner.settings.cleanuprules.keep.pattern = Coinnigh leaganacha meaitseála +owner.settings.cleanuprules.keep.pattern.container = Coinnítear an leagan is déanaí le haghaidh pacáistí Coimeádán i gcónaí. +owner.settings.cleanuprules.remove.title = Baintear leaganacha a mheaitseálann leis na rialacha seo, mura deir riail thuas iad a choinneáil. +owner.settings.cleanuprules.remove.days = Bain leaganacha níos sine ná +owner.settings.cleanuprules.remove.pattern = Bain leaganacha meaitseála +owner.settings.cleanuprules.success.update = Nuashonraíodh an riail ghlantacháin. +owner.settings.cleanuprules.success.delete = Scriosadh an riail glantacháin. +owner.settings.chef.title = Clárlann Chef +owner.settings.chef.keypair = Gin péire eochair +owner.settings.chef.keypair.description = Tá eochairphéire riachtanach le fíordheimhniú a dhéanamh ar chlárlann an Chef. Má tá péire eochrach ginte agat roimhe seo, má ghinfidh tú eochairphéire nua, scriosfar an seanphéire eochair. [secrets] secrets = Rúin @@ -3318,9 +3586,60 @@ deletion = Bain rún deletion.description = Is buan rún a bhaint agus ní féidir é a chealú. Lean ort? deletion.success = Tá an rún bainte. deletion.failed = Theip ar rún a bhaint. + management = Bainistigh rúin [actions] +actions = Gníomhartha +runners = Reathaitheoirí +runners.new = Cruthaigh reathaí nua +runners.new_notice = Conas reathaí a thosú +runners.status = Stádas +runners.id = ID +runners.name = Ainm +runners.owner_type = Cineál +runners.description = Cur síos +runners.labels = Lipéid +runners.last_online = Am Ar Líne Deiridh +runners.runner_title = Reathaí +runners.task_list = Tascanna le déanaí ar an reathaí seo +runners.task_list.no_tasks = Níl aon tasc ann fós. +runners.task_list.run = Rith +runners.task_list.status = Stádas +runners.task_list.repository = Stóras +runners.task_list.commit = Tiomantas +runners.task_list.done_at = Déanta ag +runners.edit_runner = Cuir Reathaí in Eagar +runners.update_runner = Nuashonrú Athruithe +runners.update_runner_success = Nuashonraíodh an Reathaí +runners.update_runner_failed = Theip ar an reathaí a nuashonrú +runners.delete_runner = Scrios an reathaí seo +runners.delete_runner_success = Scriosadh an reathaí go rathúil +runners.delete_runner_failed = Theip ar an reathaí a scriosadh +runners.delete_runner_header = Deimhnigh an reathaí seo a scriosadh +runners.delete_runner_notice = Má tá tasc ar siúl ar an reathaí seo, cuirfear deireadh leis agus marcáil mar theip. Féadfaidh sé sreabhadh oibre tógála a bhriseadh. +runners.none = Níl aon reathaí ar fáil +runners.status.unspecified = Anaithnid +runners.status.idle = Díomhaoin +runners.status.active = Gníomhach +runners.status.offline = As líne +runners.version = Leagan +runners.reset_registration_token = Athshocraigh comhartha clár +runners.reset_registration_token_success = D'éirigh le hathshocrú comhartha clárúcháin an dara háit +runs.all_workflows = Gach Sreafaí Oibre +runs.commit = Tiomantas +runs.scheduled = Sceidealaithe +runs.pushed_by = bhrú ag +runs.invalid_workflow_helper = Tá comhad cumraíochta sreabhadh oibre nebhailí. Seiceáil do chomhad cumraithe le do thoil: %s +runs.no_matching_online_runner_helper = Gan aon reathaí ar líne a mheaitseáil le lipéad: %s +runs.no_job_without_needs = Caithfidh post amháin ar a laghad a bheith sa sreabhadh oibre gan spleáchas. +runs.no_job = Caithfidh post amháin ar a laghad a bheith sa sreabhadh oibre +runs.actor = Aisteoir +runs.status = Stádas +runs.actors_no_select = Gach aisteoir +runs.status_no_select = Gach stádas +runs.no_results = Níor mheaitseáil aon torthaí. +runs.no_workflows = Níl aon sreafaí oibre ann fós. runs.no_runs = Níl aon rith ag an sreabhadh oibre fós. runs.empty_commit_message = (teachtaireacht tiomantas folamh) runs.expire_log_message = Glanadh logaí toisc go raibh siad ró-sean. @@ -3328,7 +3647,21 @@ workflow.disable = Díchumasaigh sreabhadh oibre workflow.enable = Cumasaigh sreabhadh oibre workflow.disabled = Tá an sreabhadh oibre díchumasaithe. need_approval_desc = Teastaíonn faomhadh chun sreafaí oibre a rith le haghaidh iarratas tarraingt forc. +variables = Athróga +variables.creation = Cuir Athróg leis +variables.none = Níl aon athróga ann fós. +variables.deletion = Bain athróg +variables.deletion.description = Tá athróg a bhaint buan agus ní féidir é a chur ar ais. Lean ar aghaidh? +variables.description = Cuirfear athróga chuig gníomhartha áirithe agus ní féidir iad a léamh ar mhalairt eile. variables.id_not_exist = Níl athróg le ID %d ann. +variables.edit = Cuir Athróg in Eagar +variables.deletion.failed = Theip ar athróg a bhaint. +variables.deletion.success = Tá an athróg bainte. +variables.creation.failed = Theip ar athróg a chur leis. +variables.creation.success = Tá an athróg "%s" curtha leis. +variables.update.failed = Theip ar athróg a chur in eagar. +variables.update.success = Tá an t-athróg curtha in eagar. + runs.no_workflows.help_write_access = Nach bhfuil a fhios agat conas tosú le Gníomhartha Forgejo? Féach ar an tús tapa sa doiciméadacht úsáideora chun do chéad sreabhadh oibre a scríobh, ansin bunaigh rithire Forgejo chun do phoist a fhorghníomhú. runs.no_workflows.help_no_write_access = Chun tuilleadh eolais a fháil faoi Ghníomhartha Forgejo, féach ar an doiciméadacht. workflow.disable_success = Sreabhadh oibre "%s" díchumasaithe go rathúil. @@ -3340,6 +3673,8 @@ workflow.dispatch.success = Iarradh an sreabhadh oibre go rathúil. workflow.dispatch.input_required = Éilítear luach don ionchur "%s". workflow.dispatch.invalid_input_type = Cineál ionchuir neamhbhailí "%s". workflow.dispatch.warn_input_limit = Ag taispeáint na chéad %d ionchur amháin. +variables.management = Bainistigh athróga +variables.not_found = Theip ar an athróg a aimsiú. [projects] deleted.display_name = Tionscadal scriosta @@ -3355,7 +3690,6 @@ executable_file = Comhad infheidhmithe symbolic_link = Nasc siombalach submodule = Fo-mhodúl - [repo.permissions] code.read = Léigh: Rochtain a fháil ar chód an stórais agus é a chlónáil. code.write = Scríobh: Brúigh chuig an stórlann, cruthaigh brainsí agus clibeanna. @@ -3377,6 +3711,9 @@ ext_issues = Rochtain ar an nasc chuig rianaitheoir saincheisteanna seachtrach. ext_wiki = Rochtain ar an nasc chuig vicí seachtrach. Déantar na ceadanna a bhainistiú go seachtrach. [markup] +filepreview.line = Líne %[1]d i %[2]s +filepreview.lines = Línte %[1]d go %[2]d i %[3]s +filepreview.truncated = Tá an réamhamharc giorraithe [translation_meta] -test = Is teaghrán tástála é seo. Ní thaispeántar é i gcomhéadan úsáideora Forgejo ach úsáidtear é chun críocha tástála. Ná bíodh drogall ort "ceart go leor" a iontráil chun am a shábháil (nó fíric spraíúil de do rogha féin) chun an marc críochnaithe 100% sin a bhaint amach :) \ No newline at end of file +test = Is teaghrán tástála é seo. Ní thaispeántar é i gcomhéadan úsáideora Forgejo ach úsáidtear é chun críocha tástála. Ná bíodh drogall ort "ceart go leor" a iontráil chun am a shábháil (nó fíric spraíúil de do rogha féin) chun an marc críochnaithe 100% sin a bhaint amach :) diff --git a/options/locale/locale_gl.ini b/options/locale/locale_gl.ini index 43257512b9..7ccafc81f4 100644 --- a/options/locale/locale_gl.ini +++ b/options/locale/locale_gl.ini @@ -31,6 +31,15 @@ re_type = Confirme o contrasinal captcha = CAPTCHA twofa = Autenticación de dobre factor passcode = Código de acceso +webauthn_insert_key = Insira a súa chave de seguridade +webauthn_press_button = Prema o botón da súa chave de seguridade… +webauthn_use_twofa = Use o Código de Dous Factores do seu Teléfono +webauthn_error = Non se puido ler a súa clave de seguridade. +webauthn_unsupported_browser = O seu navegador non soporta WebAuthn actualmente. +webauthn_error_unknown = Produciuse un erro descoñecido. Ténteo de novo. +webauthn_error_unable_to_process = O servidor non puido procesar a súa solicitude. +webauthn_error_duplicated = A clave de seguridade non está permitida para esta solicitude. Asegúrese de que a clave non estea xa rexistrada. +webauthn_error_empty = Debe definir un nome para esta clave. repository = Repositorio organization = Organización mirror = Espello @@ -87,10 +96,13 @@ copy_content = Copiar contido language = Linguaxe copy_hash = Copiar hash twofa_scratch = Código Scratch de Dous Factores +webauthn_sign_in = Prema o botón da súa chave de seguridade. Se a súa chave de seguridade non ten ningún botón, volva inserila. issues = Incidencias disabled = Desactivado error404 = A páxina á que estás tentando acceder non existe ou non tes autorización para vela. tracked_time_summary = Resumo do tempo de seguimento baseado nos filtros da lista de incidencias +webauthn_error_insecure = WebAuthn só admite conexións seguras. Para probar a través de HTTP, pode usar a orixe "localhost" ou "127.0.0.1" +webauthn_error_timeout = Alcanzouse o límite de tempo antes de que se puidera ler a súa clave. Volva cargar esta páxina e ténteo de novo. remove = Quitar view = Ver copy_type_unsupported = Este tipo de ficheiro non se pode copiar diff --git a/options/locale/locale_he.ini b/options/locale/locale_he.ini index 6e6252229d..e1d3fc334e 100644 --- a/options/locale/locale_he.ini +++ b/options/locale/locale_he.ini @@ -1,4 +1,5 @@ [common] +webauthn_error_unable_to_process = שרת זה נכשל בעיבוד בקשתך. help = עזרה logo = לוגו sign_in_with_provider = כניסה דרך %s @@ -28,6 +29,9 @@ access_token = קוד גישה captcha = CAPTCHA twofa_scratch = קוד אימות דו־שלבי passcode = קוד כניסה +webauthn_error_unknown = שגיאה לא ידועה, אפשר לנסות שוב. +webauthn_error_empty = שם המפתח הוא שדה חובה. +webauthn_error_timeout = קריאת מפתחך לקחה יותר מדי זמן. אפשר לטעון מחדש את הדף ולנסות שוב. organization = ארגון mirror = מראה new_mirror = מראה חדשה @@ -97,6 +101,8 @@ twofa = אימות דו־שלבי pull_requests = בקשות מיזוג powered_by = רץ על %s copy_generic = העתקה לCtrl + C +webauthn_unsupported_browser = הדפדפן שלך לא תומך בWebAuthn. +webauthn_error_insecure = הפרוטוקול WebAuthn לא תומך בחיבורים לא מאובטחים, למעט דרך "localhost" או "127.0.0.1" settings = הגדרות your_settings = הגדרות new_org.link = ארגון חדש @@ -109,6 +115,9 @@ copy_content = העתקת תוכן copy_type_unsupported = אי אפשר להעתיק קבצים מסוג זה concept_system_global = גלובלי concept_user_individual = אישי +webauthn_insert_key = יש להכניס את מפתח אבטחך +webauthn_press_button = נא ללחוץ על הכפתור שעל מפתח האבטחה… +webauthn_error = קריאת מפתח האבטחה נכשלה. repository = קרפיף new_repo.title = קרפיף חדש new_migrate.title = יבוא קרפיף @@ -119,6 +128,8 @@ disabled = כבוי copy_path = העתקת מיקום קובץ invalid_data = הבנת הקלט נכשלה: %v concept_code_repository = קרפיף +webauthn_sign_in = יש ללחוץ על הכפתור שעל מפתח האבטחה. אם אין כפתור, אפשר להוציא את המפתח ולחבר אותו שוב. +webauthn_error_duplicated = מפתח האבטחה לא יכול לשמש לבקשה זו. נא לוודא שהמפתח לא רשום. dashboard = מבט על remove_label_str = הסרת "%s" explore = קטלוגים @@ -128,6 +139,7 @@ confirm_delete_artifact = למחוק את הארטיפקט "%s"? toggle_menu = הצגת\הסתרת תפריט re_type = סיסמה (שוב) tracked_time_summary = סיכום זמן מעקב בהתבסס על מסננים של רשימת סוגיות +webauthn_use_twofa = השתמש בקוד אימות דו־שלבי מהטלפון שלך error413 = מיצית את ההגבלה שלך. [search] @@ -155,6 +167,7 @@ runner_kind = חיפוש מריצים… keyword_search_unavailable = חיפוש מילות מפתח לא זמין. נא לדווח למנהלי המערכת. code_search_unavailable = חיפוש קוד לא זמין. נא לדווח למנהלי המערכת. pull_kind = חיפוש בקשות מיזוג… + regexp_tooltip = פרש את החיפוש כביטוי רגולרי [heatmap] @@ -200,6 +213,7 @@ table_modal.placeholder.header = כותרת table_modal.placeholder.content = תוכן table_modal.label.rows = שורות table_modal.label.columns = עמודות + buttons.indent.tooltip = קנן פריטים ברמה אחת פנימה buttons.unindent.tooltip = קנן פריטים ברמה אחת החוצה link_modal.header = הוספת קישור @@ -311,6 +325,7 @@ repo_name_been_taken = כבר יש קרפיף בשם זה. repository_files_already_exist = כבר יש קבצים בקרפיף זה. יש לדבר עם מנהל המערכת כדי לתקן את הבעיה. AccessToken = קוד גישה Content = תוכן + email_been_used = כתובת האימייל כבר בשימוש. email_invalid = כתובת האימייל אינה תקינה. username_password_incorrect = שם משתמש או סיסמה לא נכונים. @@ -468,6 +483,7 @@ delete_with_all_comments = חשבונך נוצר לפני פחות מ־%s; כל update_avatar = עדכון תמונת פרופיל delete_current_avatar = מחיקת תמונת הפרופיל הנוכחית uploaded_avatar_not_a_image = הקובץ שהועלה לא תמונה. + ssh_key_name_used = מפתח SSH עם אותו השם כבר קיים בחשבון שלך. gpg_key_id_used = מפתח GPG ציבורי עם אותו מזהה כבר הוסף. gpg_key_matched_identities = זהויות תואמות: @@ -559,6 +575,9 @@ desc.archived = בארכיון archive.pull.noreview = קרפיף זה בארכיון. אי אפשר לבקר בקשות מיזוג. form.reach_limit_of_creation_n = החשבון של הבעלים כבר הגיע לכמות הקרפיפים המקסימלית (%s). form.name_reserved = שם הקרפיף "%s" שמור. +migrate_items_wiki = ויקי +migrate_items_issues = סוגיות +migrate_items_pullrequests = בקשות מיזוג migrate.clone_address = ייבוא \ שכפול מ־URL migrated_from = יובא מ‏־%[2]s migrated_from_fake = יובא מ־%[1]s @@ -567,6 +586,8 @@ migrate.migrating = פורג'ו בתהליכי יבוא מ־%s... migrate.migrating_failed = היבוא מ%s נכשל. migrate.migrating_failed.error = הייבוא נכשל: %s migrate.migrating_failed_no_addr = הייבוא נכשל. +migrate.migrating_topics = ייבוא נושאים +migrate.migrating_milestones = ייבוא אבני דרך editor.delete_this_file = מחיקת קובץ editor.name_your_file = שם הקובץ… editor.or = או @@ -583,6 +604,8 @@ default_branch = ענף ברירת המחדל migrate_options_mirror_helper = קרפיף זה יהיה מראה migrate_options_lfs = ייבוא אחסון קבצים גדולים (LFS) migrate_options = אפשרויות ייבוא +migrate.migrating_labels = ייבוא תוויות +migrate.migrating_releases = ייבוא גרסאות editor.file_delete_success = הקובץ "%s" נמחק. editor.commit_message_desc = תיאור ארוך לא חובה… already_forked = כבר מזלגת את %s @@ -612,6 +635,7 @@ template.topics = נושאים stars_remove_warning = פעולה זו תסיר מהקרפיף את כל הכוכבים. owner_helper = ארגונים שכבר יצרו את כמות הקרפיפים המקסימלית לא מוצגים בתפריט. fork_to_different_account = מזלוג לחשבון אחר +migrate_items_releases = גרסאות migrate.permission_denied = אין לך הרשאה לייבא קרפיפים מהשרת הנוכחי. fork_repo = מזלוג watchers = מנויים @@ -622,11 +646,14 @@ archive.title_date = קרפיך זה בארכיון כבר %s. אפשר לצפו form.name_pattern_not_allowed = התבנית "%s" לא חוקית בתוך שם של קרפיף. mirror_use_ssh.helper = פורג'ו ישקף את הקרפיף דרך התמיכה המובנית של גיט ב־SSH, ויצור בשבילך זוג מפתחות אוטומטית. באחריותך לוודא שלמפתח הציבורי שיווצר יהיה גישה לקרפיף היעד. אי אפשר להשתמש בכניסה על־בסיס סיסמה עם אפשרות זו. mirror_lfs = אחסון קבצים גדולים (LFS) +migrate_items_labels = תוויות migrate_repo = ייבוא קרפיף transfer.no_permission_to_accept = אין לך הרשאה לאשר את העברת הקרפיף הזו. form.string_too_long = הטקסט הנתון ארוך מ־%d תווים. +migrate_items_milestones = אבני דרך migrate.failed = הייבוא נכשל: %v migrate.migrate_items_options = פורג'ו צריך קוד גישה כדי לייבא מידע נוסף +migrate.migrating_git = ייבוא הקרפיף עצמו editor.filename_help = אפשר להפריד בין תיקיות עם סלאשים, ו"למחוק" תיקיות עם backspace, כאילו זוהי תיבת טקסט רגילה. editor.filename_is_a_directory = אי אפשר לקרוא לקובץ "%s"; כבר קיימת תיקייה תחת אותו שם. editor.file_editing_no_longer_exists = הקובץ הנערך שהיה ידוע כ־"%s" לא קיים יותר בקרפיף זה. diff --git a/options/locale/locale_hi.ini b/options/locale/locale_hi.ini index 42d427f071..21ecd34f21 100644 --- a/options/locale/locale_hi.ini +++ b/options/locale/locale_hi.ini @@ -41,6 +41,14 @@ settings = सेटिंग्स your_settings = आपकी सेटिंग्स return_to_forgejo = फ़ोर्जेगो पे वापस जाएं access_token = एक्सेस टोकन +webauthn_insert_key = अपनी सिक्योरिटी की डालें +webauthn_press_button = अपनी सिक्योरिटी की पर बटन दबाएं… +webauthn_use_twofa = अपने फ़ोन से दो फैक्टर कोड लाएं +webauthn_unsupported_browser = आपका ब्राउज़र वेबौथ सपोर्ट नहीं करता। +webauthn_error_unknown = कोई अंजान एरर हुई, फिर से कोशिश करें। +webauthn_error_unable_to_process = सर्वर आपकी रिक्वेस्ट प्रोसेस नहीं कर पाया। +webauthn_error_empty = कृपया इस चाभी का नाम रखें। +webauthn_error_timeout = आपकी की पढ़ने से पहले टाइमआउट हो गया। पृष्ट रीलोड करें और फिर कोशिश करें। new_project = नया प्रोजेक्ट test = टेस्ट locked = लॉक्ड @@ -80,9 +88,13 @@ remove_label_str = हटाएं आइटम “%s” edit = संपादित करना view = देखें disabled = असक्षम किया गया +webauthn_sign_in = सिक्योरिटी की का बटन दबाएं, नहीं है तो फिर से प्लग करें। +webauthn_error_insecure = वेबौथ पर सिर्फ सुरक्षित कनेक्शन हो. HTTP टेस्ट के लिए ओरिजिन “लोकलहोस्ट” या “127.0.0.1” new_repo.title = नई रिपॉजिटरी pull_requests = पुल्ल करें enabled = सक्षम किया गया +webauthn_error = सिक्योरिटी की रीड नहीं हो पा रही। +webauthn_error_duplicated = सिक्योरिटी की इस रिक्वेस्ट के लिए नहीं है। कृपया देखें की पहले से रजिस्टर्ड तो नहीं। new_migrate.title = नया प्रवासन activities = गतिविधियाँ rerun_all = फिर से सारे काम करें @@ -277,6 +289,7 @@ sign_up_button = अभी रजिस्टर करें। sign_up_successful = अकाउंट बन गया है। स्वागत! back_to_sign_in = sign in में फिर से जाएं sign_in_openid = OpenID के साथ आगे बढ़ें + confirmation_mail_sent_prompt = एक नया कन्फर्मेशन ईमेल आपको भेजा गया है, %s। पंजीकरण पुख्ता करने के लिए अपना ईमेल इनबॉक्स चेक करें और वहां ईमेल से आगे बढ़ें %s। अगर ईमेल गलत हो गया तो आप फिर से लॉग इन करके फिरसे सही ईमेल पर पुख्ता कर सकते हैं। must_change_password = अपना पासवर्ड अपडेट करें allow_password_change = ये ज़रूरी है की ईमेल पासवर्ड अपडेट करें diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index df48fcd03b..2bf82ad025 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -92,7 +92,14 @@ filter = Szűrő filter.is_archived = Archivált logo = Logó sign_in_with_provider = Bejelentkezés %s fiókkal +webauthn_insert_key = Helyezze be biztonsági kulcsát +webauthn_press_button = Nyomja meg a biztonsági kulcsán található gombot… access_token = Hozzáférési token +webauthn_error = A biztonsági kulcsának beolvasása sikertelen volt. +webauthn_unsupported_browser = A böngészője jelenleg nem támogatja a WebAuthn protokollt. +webauthn_error_unknown = Ismeretlen hiba történt. Próbálja újra. +webauthn_error_unable_to_process = A kiszolgáló nem tudta feldolgozni a kérését. +webauthn_error_empty = Nevet kell adnia ennek a kulcsnak. new_project_column = Új oszlop never = Soha unknown = Ismeretlen @@ -115,8 +122,12 @@ view = Megtekintés ok = OK copy_generic = Másolás vágólapra copy_url = Webcím másolása +webauthn_error_insecure = A WebAuthn csak biztonságos kapcsolatokat támogat. HTTP-n keresztüli tesztelés esetén használja a „localhost” vagy a „127.0.0.1” forrást. filter.clear = Szűrők törlése enable_javascript = Az oldal működéséhez engedélyezni kell a JavaScriptet. +webauthn_sign_in = Nyomja meg a biztonsági kulcsán található gombot. Ha nincs rajta gomb, próbálja meg újra behelyezni. +webauthn_use_twofa = Kétlépcsős hitelesítési kód használata telefonról +webauthn_error_timeout = Időtúllépés a kulcs beolvasása során. Töltse be újra ezt az oldalt, és próbálkozzon újra. copy_branch = Elágazás nevének másolása test = Tesztelés copy_type_unsupported = Ezt a fájltípust nem lehet másolni @@ -141,6 +152,7 @@ new_migrate.link = Új migráció new_org.title = Új szervezet new_org.link = Új szervezet filter.is_fork = Másolatok +webauthn_error_duplicated = A biztonsági kulcs nem engedélyezett ehhez a kéréshez. Győződjön meg róla, hogy a kulcs nincs-e már regisztrálva. filter.is_mirror = Tükrök [aria] @@ -673,6 +685,12 @@ template.issue_labels=Hibajegy címkék template.one_item=Legalább egy sablonelemet ki kell választani template.invalid=Ki kell választani egy sablon tárolót +migrate_items_wiki=Wiki +migrate_items_milestones=Mérföldkövek +migrate_items_labels=Címkék +migrate_items_issues=Hibajegyek +migrate_items_pullrequests=Pull request-ek +migrate_items_releases=Kiadások migrate_repo=Tároló migrációja migrate.clone_address=Migráció / Másolás URL-ről migrate.clone_address_desc=HTTP(S) vagy Git URL-e egy már létező tárolónak @@ -1286,6 +1304,34 @@ dashboard.clean_unbind_oauth_success=Az összes megszüntetett OAuth kapcsolat t dashboard.delete_generated_repository_avatars=Generált tároló avatarok törlése dashboard.reinit_missing_repos=Az összes Git tároló újra-inicializálása amihez léteznek bejegyzések dashboard.sync_external_users=Külső felhasználói adatok szinkronizálása +dashboard.server_uptime=Kiszolgáló futási ideje +dashboard.current_goroutine=Jelenlegi Goroutinok +dashboard.current_memory_usage=Jelenlegi memória használat +dashboard.total_memory_allocated=Összes lefoglalt memória +dashboard.memory_obtained=Megszerzett Memória +dashboard.pointer_lookup_times=Pointer Lookup Idők +dashboard.memory_allocate_times=Memóriafoglalások +dashboard.current_heap_usage=Aktuális Heap Használat +dashboard.heap_memory_obtained=Heap Memória Megszerezve +dashboard.heap_memory_idle=Tétlen Heap Memória +dashboard.heap_memory_in_use=Használatban lévő Heap Memória +dashboard.heap_memory_released=Elengedett Heap Memória +dashboard.heap_objects=Heap Objektumok +dashboard.bootstrap_stack_usage=Bootstrap Stack Használat +dashboard.stack_memory_obtained=Stack Memória Megszerezve +dashboard.mspan_structures_usage=MSpan Struktúrák Használata +dashboard.mspan_structures_obtained=MSpan Struktúrák Megszerezve +dashboard.mcache_structures_usage=MCache Struktúrák Használata +dashboard.mcache_structures_obtained=MCache Struktúrák Megszerezve +dashboard.profiling_bucket_hash_table_obtained=Profilozó Vödrös Hash Tábla Megszerezve +dashboard.gc_metadata_obtained=GC Metaadat Megszerezve +dashboard.other_system_allocation_obtained=Másik Rendszer Allokáció Megszerezve +dashboard.next_gc_recycle=Következő GC Újrahasznosítás +dashboard.last_gc_time=Utolsó GC óta eltelt idő +dashboard.total_gc_pause=Teljes GC szünet +dashboard.last_gc_pause=Utolsó GC szünet +dashboard.gc_times=GC Idők + users.new_account=Felhasználó létrehozása users.name=Felhasználónév users.full_name=Teljes név @@ -1307,6 +1353,10 @@ users.allow_create_organization=Létrehozhat szervezeteket users.update_profile=Fiók frissítése users.delete_account=Fiók törlése users.still_has_org=Ez a felhasználó tagja egy szervezetnek. Először el kell távolítani a felhasználót az összes szervezetből. +users.list_status_filter.is_active=Aktív +users.list_status_filter.is_admin=Rendszergazda +users.list_status_filter.is_restricted=Korlátozott + emails.primary=Elsődleges emails.activated=Aktivált emails.filter_sort.email=Email @@ -1524,6 +1574,10 @@ monitor.process.cancel=Folyamat megszakítása monitor.process.cancel_desc=Egy folyamat megszakítása adatvesztést okozhat monitor.process.cancel_notices=Megszakítás: %s? +monitor.queue.name=Név +monitor.queue.type=Típus +monitor.queue.settings.submit=Beállítások frissítése + notices.system_notice_list=Rendszer Értesítések notices.view_detail_header=Értesítés Részletei notices.select_all=Összes Kijelölése @@ -1581,20 +1635,50 @@ raw_seconds=másodperc raw_minutes=perc [dropzone] +file_too_big=A fájl mérete ({{filesize}} MB) meghaladja a maximális méretet ({{maxFilesize}} MB). +remove_file=Fájl eltávolítása [notification] +notifications=Értesítések +unread=Olvasatlan +read=Olvasott +no_unread=Nincsenek olvasatlan értesítések. +no_read=Nincsenek olvasott értesítések. +pin=Értesítés kitűzése +mark_as_read=Megjelölés olvasottként +mark_as_unread=Megjelölés olvasatlanként +mark_all_as_read=Összes üzenet megjelölése olvasottként [gpg] +error.extract_sign=Nem sikerült kinyerni az aláírást +error.generate_hash=Nem sikerült létrehozni a commitot azonosító hash-t +error.no_gpg_keys_found=Nem található kulcs ehhez az aláíráshoz az adatbázisban +error.not_signed_commit=Nem aláírt commit [units] [packages] +filter.type=Típus +alpine.repository.branches=Ágak +alpine.repository.repositories=Tárolók conan.details.repository=Tároló owner.settings.cleanuprules.enabled=Engedélyezett [secrets] [actions] +runners.name=Név +runners.owner_type=Típus +runners.description=Leírás +runners.task_list.run=Futtatás +runners.task_list.repository=Tároló +runners.task_list.commit=Commit +runners.status.active=Aktív + +runs.commit=Commit + + + [projects] diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 2379f3edfe..8102e8e5ba 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -80,6 +80,18 @@ concept_code_repository=Repositori name=Nama re_type = Konfirmasi Kata Sandi +webauthn_insert_key = Masukkan kunci keamanan anda +webauthn_sign_in = Tekan tombol pada kunci keamanan Anda. Jika kunci keamanan Anda tidak memiliki tombol, masukkan kembali. +webauthn_press_button = Silakan tekan tombol pada kunci keamanan Anda… +webauthn_use_twofa = Gunakan kode dua faktor dari telepon Anda +webauthn_error = Tidak dapat membaca kunci keamanan Anda. +webauthn_unsupported_browser = Browser Anda saat ini tidak mendukung WebAuthn. +webauthn_error_unknown = Terdapat kesalahan yang tidak diketahui. Mohon coba lagi. +webauthn_error_insecure = `WebAuthn hanya mendukung koneksi aman. Untuk pengujian melalui HTTP, Anda dapat menggunakan "localhost" atau "127.0.0.1"` +webauthn_error_unable_to_process = Server tidak dapat memproses permintaan Anda. +webauthn_error_duplicated = Kunci keamanan tidak diperbolehkan untuk permintaan ini. Pastikan bahwa kunci ini belum terdaftar sebelumnya. +webauthn_error_empty = Anda harus menetapkan nama untuk kunci ini. +webauthn_error_timeout = Waktu habis sebelum kunci Anda dapat dibaca. Mohon muat ulang halaman ini dan coba lagi. new_project = Proyek Baru new_project_column = Kolom Baru ok = Oke @@ -123,6 +135,7 @@ filter.private = Pribadi logo = Logo sign_in_with_provider = Masuk menggunakan %s active_stopwatch = Pelacak Waktu Aktif + tracked_time_summary = Ringkasan waktu terlacak berdasarkan filter daftar isu enable_javascript = Situs web ini memerlukan JavaScript. licenses = Lisensi @@ -154,12 +167,14 @@ filter.not_template = Bukan templat navbar = Bar Navigasi footer = Footer footer.links = Tautan + footer.software = Tentang perangkat lunak ini [heatmap] number_of_contributions_in_the_last_12_months = %s Kontribusi pada 12 bulan terakhir less = Lebih sedikit more = Lebih banyak + contributions_zero = Tidak ada kontribusi contributions_format = {contributions} pada {month} {day}, {year} contributions_one = kontribusi @@ -171,7 +186,7 @@ buttons.bold.tooltip = Tambahkan teks tebal (Ctrl+B / ⌘B) buttons.italic.tooltip = Tambahkan teks miring (Ctrl+I / ⌘I) buttons.quote.tooltip = Kutip teks buttons.code.tooltip = Tambah Kode -buttons.link.tooltip = Tambahkan tautan (Ctrl+K / ⌘K) +buttons.link.tooltip = Tambahkan tautan buttons.list.unordered.tooltip = Tambah daftar titik buttons.list.ordered.tooltip = Tambah daftar angka buttons.list.task.tooltip = Tambahkan daftar tugas @@ -180,6 +195,7 @@ buttons.ref.tooltip = Merujuk pada issue atau permintaan tarik buttons.switch_to_legacy.tooltip = Gunakan editor versi lama buttons.enable_monospace_font = Aktifkan font monospace buttons.disable_monospace_font = Non-Aktifkan font monospace + buttons.indent.tooltip = Indentasi item satu tingkat buttons.unindent.tooltip = Hapus indentasi item satu tingkat buttons.new_table.tooltip = Tambahkan tabel @@ -200,6 +216,7 @@ string.desc = Z - A [error] occurred = Terjadi kesalahan not_found = Target tidak dapat ditemukan. + report_message = Jika Anda yakin ini adalah bug Forgejo, silakan cari issue di Codeberg atau buka issue baru jika diperlukan. network_error = Kesalahan jaringan server_internal = Kesalahan server internal @@ -212,6 +229,7 @@ lightweight=Ringan lightweight_desc=Forgejo hanya membutuhkan persyaratan minimal dan bisa berjalan pada Raspberry Pi yang murah. Bisa menghemat listrik! license=Sumber Terbuka license_desc=Go get (Dapatkan kode sumber dari) Forgejo! Mari bergabung dengan berkontribusi untuk membuat proyek ini lebih baik. Jangan malu untuk menjadi kontributor! + install_desc = Cukup jalankan biner untuk platform Anda, kirimkan dengan Docker, atau dapatkan dalam bentuk paket. platform_desc = Forgejo telah dikonfirmasi berjalan pada sistem operasi bebas seperti Linux dan FreeBSD, serta berbagai arsitektur CPU. Pilih yang Anda sukai! @@ -238,6 +256,7 @@ admin_email=Alamat email email_title = Pengaturan email smtp_from = Kirim Email Sebagai + install = Instalasi docker_helper = Jika Anda menjalankan Forgejo di dalam Docker, harap baca dokumentasi sebelum mengubah pengaturan apa pun. require_db_desc = Forgejo memerlukan MySQL, PostgreSQL, SQLite3, atau TiDB (protokol MySQL). @@ -343,6 +362,7 @@ show_private=Pribadi issues.in_your_repos=Dalam repositori anda show_archived = Diarsipkan + filter = Filter lainnya filter_by_team_repositories = Filter berdasarkan repositori tim feed_of = Umpan dari "%s" @@ -358,6 +378,7 @@ repos=Repositori users=Pengguna organizations=Organisasi code=Kode + go_to = Pergi ke code_last_indexed_at = Terakhir diindeks %s relevant_repositories_tooltip = Repositori yang merupakan garpu atau yang tidak memiliki topik, ikon, dan deskripsi disembunyikan. @@ -409,6 +430,7 @@ authorize_application_created_by=Aplikasi ini dibuat oleh %s. authorize_application_description=Jika Anda memberikan akses, layanan tersebut akan dapat mengakses dan menulis ke semua informasi akun Anda, termasuk repositori privat dan organisasi. authorize_title=Izinkan "%s" untuk mengakses akun Anda? authorization_failed=Otorisasi gagal + manual_activation_only = Hubungi administrator situs Anda untuk menyelesaikan aktivasi. remember_me = Ingat perangkat ini hint_login = Sudah punya akun? Masuk sekarang! @@ -448,6 +470,13 @@ register_notify=Selamat datang di %s reset_password=Pulihkan akun Anda register_success=Pendaftaran berhasil + + + + + + + view_it_on = Lihat di %s reply = atau balas email ini secara langsung link_not_working_do_paste = Tautan tidak berfungsi? Coba salin dan tempel ke bilah URL browser Anda. @@ -511,17 +540,12 @@ team_invite.text_1 = %[1]s mengundang Anda untuk bergabung dengan tim %[2]s di o team_invite.text_2 = Silakan klik tautan berikut untuk bergabung dengan tim: team_invite.text_3 = Catatan: Undangan ini ditujukan untuk %[1]s. Jika Anda tidak mengharapkan undangan ini, Anda dapat mengabaikan email ini. - - - - - - [modal] yes=Ya no=Tidak cancel=Membatalkan modify=Perbarui + confirm = Konfirmasi [form] @@ -582,6 +606,8 @@ auth_failed=Otentikasi gagal: %v target_branch_not_exist=Target cabang tidak ada. + + FullName = Nama lengkap Description = Deskripsi Pronouns = Kata ganti @@ -624,7 +650,6 @@ org_still_own_packages = Organisasi ini masih memiliki satu atau lebih paket, ha admin_cannot_delete_self = Anda tidak dapat menghapus diri sendiri saat Anda adalah admin. Harap hapus hak admin Anda terlebih dahulu. required_prefix = Masukan harus dimulai dengan "%s" - [user] change_avatar=Ganti avatar anda… repositories=Repositori @@ -636,6 +661,8 @@ following_few=%d mengikuti follow=Ikuti unfollow=Berhenti Mengikuti user_bio=Biografi + + joined_on = Bergabung pada %s followers.title.one = Pengikut followers.title.few = Pengikut @@ -667,7 +694,6 @@ form.name_reserved = Nama pengguna "%s" sudah dicadangkan. form.name_pattern_not_allowed = Pola "%s" tidak diperbolehkan dalam nama pengguna. form.name_chars_not_allowed = Nama pengguna "%s" mengandung karakter yang tidak valid. - [settings] profile=Profil account=Akun @@ -843,6 +869,7 @@ email_notifications.submit=Atur preferensi email visibility.private=Pribadi visibility.public = Publik + appearance = Tampilan webauthn = Autentikasi dua faktor (Kunci keamanan) blocked_users = Pengguna yang diblokir @@ -1057,6 +1084,12 @@ template.topics=Topik template.avatar=Avatar template.issue_labels=Label isu +migrate_items=Ihwal Migrasi +migrate_items_wiki=Wiki +migrate_items_milestones=Tonggak +migrate_items_issues=Masalah +migrate_items_pullrequests=Tarik Permintaan +migrate_items_releases=Rilis migrate_repo=Migrasi repositori migrate.permission_denied=Anda tidak diizinkan untuk mengimpor repositori lokal. migrate.failed=Migrasi gagal: %v @@ -1450,6 +1483,7 @@ desc.archived = Diarsipkan commitstatus.error = Gangguan projects.new = Proyek Baru milestones.filter_sort.name = Nama + rss.must_be_on_branch = Anda harus berada di cabang untuk memiliki umpan RSS. admin.manage_flags = Kelola tanda admin.enabled_flags = Tanda yang diaktifkan untuk repositori: @@ -1817,7 +1851,6 @@ issues.ref_from = `dari %[1]s` issues.author = Penulis issues.author.tooltip.issue = Pengguna ini adalah penulis issue ini. issues.author.tooltip.pr = Pengguna ini adalah penulis permintaan tarik ini. -issues.role.owner_helper = Pengguna ini adalah pemilik repositori ini. issues.role.member_helper = Pengguna ini adalah anggota organisasi yang memiliki repositori ini. issues.role.collaborator = Kolaborator issues.role.collaborator_helper = Pengguna ini telah diundang untuk berkolaborasi di repositori. @@ -2544,7 +2577,6 @@ settings.matrix.access_token_helper = Disarankan untuk menyiapkan akun Matrix kh settings.matrix.room_id_helper = ID Ruangan dapat diambil dari klien web Element > Pengaturan Ruangan > Lanjutan > ID ruangan internal. Contoh: %s. settings.archive.button = Arsipkan repositori settings.archive.header = Arsipkan repositori ini -settings.archive.text = Mengarsipkan repositori akan membuatnya sepenuhnya hanya-baca. Repositori akan disembunyikan dari dasbor. Tidak ada seorang pun (bahkan Anda!) yang dapat membuat commit baru, atau membuka issue atau permintaan tarik baru. Mendokumentasikan alasan pengarsipan disarankan untuk memandu pengembang di masa mendatang yang berencana melakukan fork repositori. settings.archive.success = Repositori berhasil diarsipkan. settings.archive.error = Terjadi kesalahan saat mencoba mengarsipkan repositori. Lihat log untuk detail selengkapnya. settings.archive.error_ismirror = Anda tidak dapat mengarsipkan repositori yang dicerminkan. @@ -2775,6 +2807,7 @@ teams.add_team_member=Tambah anggota tim teams.delete_team_success=Tim sudah di hapus. teams.repositories=Tim repositori settings.visibility.public = Publik + org_name_helper = Nama organisasi sebaiknya singkat dan mudah diingat. open_dashboard = Buka dasbor code = Kode @@ -2857,6 +2890,33 @@ dashboard.clean_unbind_oauth=Bersihkan koneksi OAuth yang tidak terikat dashboard.clean_unbind_oauth_success=Semua koneksi OAuth yang tidak terikat telah dihapus. dashboard.reinit_missing_repos=Menginstal kembali semua repositori Git yang hilang dimana ada catatan dashboard.sync_external_users=Sinkronkan data pengguna eksternal +dashboard.server_uptime=Waktu aktif server +dashboard.current_goroutine=Goroutine saat ini +dashboard.current_memory_usage=Penggunaan memori saat ini +dashboard.total_memory_allocated=Total memori yang dialokasikan +dashboard.memory_obtained=Memori yang diperoleh +dashboard.pointer_lookup_times=Jumlah pencarian pointer +dashboard.current_heap_usage=Penggunaan heap saat ini +dashboard.heap_memory_obtained=Memori heap yang diperoleh +dashboard.heap_memory_idle=Memori heap yang menganggur +dashboard.heap_memory_in_use=Memori heap yang digunakan +dashboard.heap_memory_released=Memori heap yang dilepaskan +dashboard.heap_objects=Objek heap +dashboard.bootstrap_stack_usage=Penggunaan stack bootstrap +dashboard.stack_memory_obtained=Memori stack yang diperoleh +dashboard.mspan_structures_usage=Penggunaan struktur MSpan +dashboard.mspan_structures_obtained=Struktur MSpan yang diperoleh +dashboard.mcache_structures_usage=Penggunaan struktur MCache +dashboard.mcache_structures_obtained=Struktur MCache yang diperoleh +dashboard.profiling_bucket_hash_table_obtained=Tabel hash bucket pemrofilan yang diperoleh +dashboard.gc_metadata_obtained=Metadata GC yang diperoleh +dashboard.other_system_allocation_obtained=Alokasi sistem lainnya yang diperoleh +dashboard.next_gc_recycle=Daur ulang GC berikutnya +dashboard.last_gc_time=Waktu sejak GC terakhir +dashboard.total_gc_pause=Total jeda GC +dashboard.last_gc_pause=Jeda GC terakhir +dashboard.gc_times=Jumlah GC + users.full_name=Nama lengkap users.activated=Diaktifkan users.admin=Pengelola @@ -2865,6 +2925,8 @@ users.created=Dibuat users.edit=Edit users.auth_source=Sumber autentikasi users.local=Lokal +users.list_status_filter.is_admin=Pengelola + emails.activated=Diaktifkan orgs.org_manage_panel=Kelola organisasi @@ -3036,6 +3098,19 @@ monitor.execute_time=Waktu pelaksanaan monitor.process.cancel=Batalkan proses monitor.process.cancel_desc=Membatalkan proses dapat menyebabkan kehilangan data +monitor.queues=Antrian +monitor.queue=Antrian: %s +monitor.queue.name=Nama +monitor.queue.type=Tipe +monitor.queue.exemplar=Contoh Tipe +monitor.queue.numberworkers=Jumlah pekerja +monitor.queue.maxnumberworkers=Jumlah maksimum pekerja +monitor.queue.settings.title=Pengaturan pool +monitor.queue.settings.maxnumberworkers=Jumlah Maks. Worker +monitor.queue.settings.maxnumberworkers.error=Jumlah maks. worker haruslah sebuah angka +monitor.queue.settings.submit=Perbarui pengaturan +monitor.queue.settings.changed=Pengaturan diperbarui + notices.system_notice_list=Pemberitahuan sistem notices.view_detail_header=Detail pemberitahuan notices.select_all=Pilih semua @@ -3051,6 +3126,8 @@ notices.delete_success=Laporan sistem telah dihapus. config_settings = Pengaturan +users.list_status_filter.menu_text = Saring + self_check = Pemeriksaan mandiri identity_access = Identitas & akses users = Akun pengguna @@ -3098,6 +3175,8 @@ dashboard.resync_all_hooks = Sinkronkan ulang hook Git dari semua repositori (pr dashboard.cleanup_hook_task_table = Bersihkan tabel hook_task dashboard.cleanup_packages = Bersihkan paket yang kedaluwarsa dashboard.cleanup_actions = Bersihkan log dan artefak yang kedaluwarsa dari aksi +dashboard.memory_allocate_times = Alokasi memori +dashboard.memory_free_times = Pembebasan memori dashboard.delete_old_actions = Hapus semua aktivitas lama dari basis data dashboard.delete_old_actions.started = Hapus semua aktivitas lama dari basis data dimulai. dashboard.update_checker = Pemeriksa pembaruan @@ -3152,6 +3231,16 @@ users.purge_help = Hapus paksa pengguna beserta semua repositori, organisasi, da users.still_own_packages = Pengguna ini masih memiliki satu atau lebih paket, hapus paket tersebut terlebih dahulu. users.deletion_success = Akun pengguna telah dihapus. users.reset_2fa = Atur ulang 2FA +users.list_status_filter.reset = Atur ulang +users.list_status_filter.is_active = Aktif +users.list_status_filter.not_active = Tidak aktif +users.list_status_filter.not_admin = Bukan admin +users.list_status_filter.is_restricted = Dibatasi +users.list_status_filter.not_restricted = Tidak dibatasi +users.list_status_filter.is_prohibit_login = Larang masuk +users.list_status_filter.not_prohibit_login = Izinkan masuk +users.list_status_filter.is_2fa_enabled = 2FA diaktifkan +users.list_status_filter.not_2fa_enabled = 2FA dinonaktifkan users.details = Detail pengguna emails.email_manage_panel = Kelola email pengguna emails.primary = Utama @@ -3310,6 +3399,13 @@ monitor.download_diagnosis_report = Unduh laporan diagnosis monitor.duration = Durasi (dtk) monitor.last_execution_result = Hasil monitor.process.cancel_notices = Batalkan: %s? +monitor.queue.activeworkers = Pekerja aktif +monitor.queue.numberinqueue = Jumlah dalam antrian +monitor.queue.review_add = Tinjau / tambah pekerja +monitor.queue.settings.desc = Pool berkembang secara dinamis sebagai respons terhadap pemblokiran antrian pekerja mereka. +monitor.queue.settings.maxnumberworkers.placeholder = Saat ini %[1]d +monitor.queue.settings.remove_all_items = Hapus semua +monitor.queue.settings.remove_all_items_done = Semua item dalam antrian telah dihapus. notices.operations = Operasi notices.type_2 = Tugas self_check.no_problem_found = Belum ditemukan masalah. @@ -3325,6 +3421,7 @@ transfer_repo=ditransfer repositori %s ke %s delete_tag=tag dihapus %[2]s dari %[3]s delete_branch=cabang dihapus %[2]s dari %[3]s compare_commits=Bandingkan %d melakukan + commit_repo = mendorong ke %s di %s create_issue = `membuka issue %[3]s#%[2]s` close_issue = `menutup issue %[3]s#%[2]s` @@ -3372,19 +3469,39 @@ raw_seconds=detik raw_minutes=menit [dropzone] +default_message=Jatuhkan berkas disini atau klik untuk mengunggah. +invalid_input_type=Anda tidak bisa mengunggah berkas jenis ini. +file_too_big=Ukuran berkas ({{filesize}} MB) melebihi ukuran maksimum ({{maxFilesize}} MB). +remove_file=Hilangkan berkas [notification] +notifications=Notifikasi +unread=Belum dibaca +read=Dibaca +pin=Pemberitahuan Pin +mark_as_read=Tandai sebagai membaca +mark_as_unread=Tandai sebagai belum dibaca +mark_all_as_read=Tandai semua sudah dibaca [gpg] +error.extract_sign=Gagal untuk mengambil tanda tangan +error.generate_hash=Gagal untuk menghasilkan hash komit +error.no_gpg_keys_found=Tidak diketahui kunci yang ditemukan di database signature +error.not_signed_commit=Bukan melakukan yang ditandatangani [units] error.no_unit_allowed_repo=Anda tidak diijinkan untuk melihat semua unit dari repositori ini. error.unit_not_allowed=Anda tidak diizinkan untuk mengunjungi unit repositori ini. + unit = Unit [packages] +filter.type=Jenis +alpine.repository.branches=Cabang +alpine.repository.repositories=Repositori conan.details.repository=Repositori owner.settings.cleanuprules.enabled=Aktif + owner.settings.cleanuprules.keep.count.1 = 1 versi per paket owner.settings.cleanuprules.keep.count.n = %d versi per paket @@ -3402,13 +3519,46 @@ deletion.failed = Gagal menghapus rahasia. management = Kelola rahasia [actions] +runners.name=Nama +runners.owner_type=Jenis +runners.description=Deskripsi +runners.task_list.run=Lari +runners.task_list.repository=Repositori +runners.task_list.commit=Memperbuat + +runs.commit=Memperbuat + + + + +runs.no_matching_online_runner_helper = Tidak ada runner online yang cocok dengan label: %s +runs.actor = Aktor +runs.status = Status +runs.actors_no_select = Semua aktor +runs.status_no_select = Semua status +runs.no_results = Tidak ada hasil yang cocok. +runs.no_workflows = Belum ada alur kerja. runs.no_runs = Alur kerja belum berjalan. runs.empty_commit_message = (pesan commit kosong) workflow.disable = Nonaktifkan Alur Kerja workflow.enable = Aktifkan Alur Kerja workflow.disabled = Alur kerja dinonaktifkan. need_approval_desc = Butuh persetujuan untuk menjalankan alur kerja untuk pull request fork. +variables = Variabel +variables.creation = Tambah Variabel +variables.none = Belum ada variabel. +variables.deletion = Hapus variabel +variables.deletion.description = Menghapus variabel bersifat permanen dan tidak dapat dibatalkan. Lanjutkan? +variables.description = Variabel akan diteruskan ke beberapa tindakan dan tidak dapat dibaca sebaliknya. variables.id_not_exist = Variabel dengan ID %d tidak ada. +variables.edit = Edit Variabel +variables.deletion.failed = Gagal menghapus variabel. +variables.deletion.success = Variabel telah dihapus. +variables.creation.failed = Gagal menambahkan variabel. +variables.creation.success = Variabel "%s" telah ditambahkan. +variables.update.failed = Gagal mengedit variabel. +variables.update.success = Variabel telah diedit. + runs.no_workflows.help_write_access = Tidak tahu cara memulai dengan Forgejo Actions? Lihat panduan memulai cepat di dokumentasi pengguna untuk menulis alur kerja pertama Anda, lalu siapkan runner Forgejo untuk menjalankan pekerjaan Anda. runs.no_workflows.help_no_write_access = Untuk mempelajari Forgejo Actions, lihat dokumentasi. runs.expire_log_message = Log telah dihapus karena sudah terlalu lama. @@ -3421,11 +3571,14 @@ workflow.dispatch.success = Permintaan jalankan alur kerja berhasil dikirim. workflow.dispatch.input_required = Wajib mengisi nilai untuk input "%s". workflow.dispatch.invalid_input_type = Jenis input tidak valid "%s". workflow.dispatch.warn_input_limit = Hanya menampilkan %d input pertama. +variables.management = Kelola variabel +variables.not_found = Gagal menemukan variabel. [projects] type-1.display_name = Proyek Individu type-2.display_name = Proyek Repositori type-3.display_name = Proyek Organisasi + deleted.display_name = Proyek yang dihapus [git.filemode] @@ -3462,7 +3615,6 @@ issue_kind = Cari isu… pull_kind = Cari permintaan tarik… keyword_search_unavailable = Pencarian berdasarkan kata kunci saat ini tidak tersedia. Silakan hubungi administrator situs. - [repo.permissions] code.read = Baca: Akses dan klon kode repositori. code.write = Tulis: Dorong ke repositori, buat cabang dan tag. @@ -3484,6 +3636,9 @@ ext_issues = Akses tautan ke pelacak issue eksternal. Izin dikelola secara ekste ext_wiki = Akses tautan ke wiki eksternal. Izin dikelola secara eksternal. [markup] +filepreview.line = Baris %[1]d di %[2]s +filepreview.lines = Baris %[1]d hingga %[2]d di %[3]s +filepreview.truncated = Pratinjau telah dipotong [translation_meta] -test = Ini adalah string uji. Tidak ditampilkan di antarmuka Forgejo tetapi digunakan untuk keperluan pengujian. Silakan masukkan "ok" untuk menghemat waktu (atau fakta menarik pilihan Anda) demi mencapai angka penyelesaian 100% yang memuaskan :) \ No newline at end of file +test = Ini adalah string uji. Tidak ditampilkan di antarmuka Forgejo tetapi digunakan untuk keperluan pengujian. Silakan masukkan "ok" untuk menghemat waktu (atau fakta menarik pilihan Anda) demi mencapai angka penyelesaian 100% yang memuaskan :) diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index e22dfefcc1..4cee9c0cd3 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -33,6 +33,18 @@ twofa=Tvíþætt Auðkenning twofa_scratch=Tveggja-Þátta Skrapkóði passcode=Aðgangstala +webauthn_insert_key=Settu öryggislykilinn þinn inn +webauthn_sign_in=Ýttu á hnappinn á öryggislyklinum þínum. Ef öryggislykillinn þinn hefur engan hnapp skaltu setja hann aftur inn. +webauthn_press_button=Vinsamlegast ýttu á hnappinn á öryggislyklinum þínum… +webauthn_use_twofa=Notaðu tveggja-þátta kóða úr símanum þínum +webauthn_error=Gat ekki lesið öryggislykilinn þinn. +webauthn_unsupported_browser=Vafrinn þinn styður ekki WebAuthn eins og er. +webauthn_error_unknown=Óþekkt villa kom upp. Vinsamlegast reyndu aftur. +webauthn_error_insecure=WebAuthn styður aðeins öruggar tengingar. Til að prófa yfir HTTP geturðu notað upprunann „localhost“ eða „127.0.0.1“ +webauthn_error_unable_to_process=Netþjónninn gat ekki ráðið við beiðni þína. +webauthn_error_duplicated=Öryggislykillinn er ekki leyfður fyrir þessa beiðni. Gakktu úr skugga um að lykillinn sé ekki þegar skráður. +webauthn_error_empty=Þú verður að setja nafn fyrir þennan lykil. +webauthn_error_timeout=Tímamörk náð áður en hægt var að lesa lykilinn þinn. Vinsamlegast endurhlaðið þessa síðu og reyndu aftur. repository=Hugbúnaðarsafn organization=Stofnun mirror=Speglun @@ -578,8 +590,17 @@ template.issue_labels=Vandamálslýsingar migrate_options_lfs=Flytja LFS skrár migrate_options_lfs_endpoint.label=LFS Endapunktur +migrate_items_wiki=Handbók +migrate_items_milestones=Tímamót +migrate_items_labels=Skýringar +migrate_items_issues=Vandamál +migrate_items_pullrequests=Sameiningarbeiðnir +migrate_items_merge_requests=Sameiningarbeiðnir +migrate_items_releases=Útgáfur migrate_repo=Flytja Hugbúnaðarsafn migrate.migrate=Flytja Frá %s +migrate.migrating_labels=Að færa Lýsingar + mirror_from=speglun af forked_from=tvískipt frá generated_from=myndað frá @@ -1107,6 +1128,9 @@ dashboard.statistic=Yfirlit dashboard.operation_switch=Skipta dashboard.operation_run=Keyra dashboard.update_mirrors=Uppfæra Speglanir +dashboard.server_uptime=Uppitími Netþjóns +dashboard.total_memory_allocated=Heildarminni úthlutað + users.name=Notandanafn users.full_name=Fullt Nafn users.admin=Stjórnandi @@ -1115,6 +1139,14 @@ users.repos=Söfn users.created=Búið til users.edit=Breyta users.local=Staðbundið +users.list_status_filter.menu_text=Sía +users.list_status_filter.reset=Endurstilla +users.list_status_filter.is_active=Virkt +users.list_status_filter.is_admin=Stjórnandi +users.list_status_filter.is_prohibit_login=Stöðva Innskráningu +users.list_status_filter.not_prohibit_login=Leyfa Innskráningu +users.list_status_filter.not_2fa_enabled=Tvíþætt Auðkenning Óvirk + emails.primary=Aðal emails.filter_sort.email=Tölvupóstur emails.filter_sort.name=Notandanafn @@ -1194,6 +1226,12 @@ monitor.name=Heiti monitor.desc=Lýsing monitor.process.children=Börn +monitor.queues=Raðir +monitor.queue.name=Heiti +monitor.queue.type=Tegund +monitor.queue.settings.submit=Uppfæra Stillingar +monitor.queue.settings.changed=Stillingar Uppfærðar + notices.type=Tegund notices.type_1=Hugbúnaðarsafn notices.type_2=Verkefni @@ -1234,17 +1272,55 @@ raw_minutes=mínútur [dropzone] [notification] +notifications=Tilkynningar +unread=Ólesnar +read=Lesnar [gpg] [units] [packages] +title=Pakkar +filter.type=Tegund +filter.type.all=Allir +installation=Uppsetning +keywords=Stikkorð +details=Nánar +details.author=Höfundur +details.license=Hugbúnaðarleyfi +versions=Útgáfur +versions.view_all=Sjá allar +dependency.id=Auðkenni +dependency.version=Útgáfa +alpine.repository.branches=Greinar +alpine.repository.repositories=Hugbúnaðarsöfn conan.details.repository=Hugbúnaðarsafn +container.details.platform=Vettvangur +container.labels=Lýsingar +container.labels.key=Lykill +container.labels.value=Gildi +npm.details.tag=Merki +pypi.requires=Þarfnast Python [secrets] [actions] +runners.id=Auðkenni +runners.name=Heiti +runners.owner_type=Tegund +runners.description=Lýsing +runners.labels=Lýsingar +runners.task_list.run=Keyra +runners.task_list.repository=Hugbúnaðarsafn +runners.task_list.commit=Framlag +runners.status.active=Virkt +runners.version=Útgáfa + +runs.commit=Framlag + + + [projects] diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 3b8475ba67..68aec9eed6 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -34,6 +34,18 @@ twofa=Autenticazione a due fattori twofa_scratch=Codice di recupero per l'autenticazione a due fattori passcode=Codice di sicurezza +webauthn_insert_key=Inserisci la tua chiave di sicurezza +webauthn_sign_in=Premi il pulsante sul tasto di sicurezza. Se il tasto di sicurezza non ha pulsante, reinseriscilo. +webauthn_press_button=Premi il pulsante sulla chiave di sicurezza… +webauthn_use_twofa=Usa un codice a due fattori dal tuo telefono +webauthn_error=Impossibile leggere la tua chiave di sicurezza. +webauthn_unsupported_browser=Il tuo browser al momento non supporta WebAuthn. +webauthn_error_unknown=Si è verificato un errore sconosciuto. Riprova. +webauthn_error_insecure=WebAuthn supporta solo connessioni sicure. Per il test su HTTP, è possibile utilizzare l'origine "localhost" o "127.0.0.1" +webauthn_error_unable_to_process=Il server non può elaborare la richiesta. +webauthn_error_duplicated=La chiave di sicurezza non è consentita per questa richiesta. Assicurati che la chiave non sia già registrata. +webauthn_error_empty=Devi impostare un nome per questa chiave. +webauthn_error_timeout=Timeout raggiunto prima che la tua chiave possa essere letta. Ricarica la pagina e riprova. repository=Repositorio organization=Organizzazione mirror=Mirror @@ -1120,6 +1132,14 @@ migrate_options_lfs=Migra file LFS migrate_options_lfs_endpoint.label=Punto d'accesso LFS migrate_options_lfs_endpoint.description=La migrazione tenterà di utilizzare il tuo Git remote per determinare il server LFS. È inoltre possibile specificare un endpoint personalizzato se il repositorio dati LFS è memorizzato da qualche altra parte. migrate_options_lfs_endpoint.description.local=È supportato anche un percorso server locale. +migrate_items=Elementi di migrazione +migrate_items_wiki=Wiki +migrate_items_milestones=Traguardi +migrate_items_labels=Etichette +migrate_items_issues=Segnalazioni +migrate_items_pullrequests=Richieste di modifica +migrate_items_merge_requests=Richieste di fusione +migrate_items_releases=Rilasci migrate_repo=Migra repositorio migrate.clone_address=Migra / Clona da URL migrate.clone_address_desc=URL HTTP(S) o Git "clone" di un repositorio esistente @@ -1136,6 +1156,14 @@ migrate.migrate=Migra da %s migrate.migrating=Migrazione da %s... migrate.migrating_failed=Migrazione da %s fallita. migrate.migrating_failed_no_addr=Migrazione non riuscita. +migrate.migrating_git=Migrazione dei dati Git +migrate.migrating_topics=Migrazione degli argomenti +migrate.migrating_milestones=Migrazione dei traguardi +migrate.migrating_labels=Migrazione delle etichette +migrate.migrating_releases=Migrazione dei rilasci +migrate.migrating_issues=Migrazione delle segnalazioni +migrate.migrating_pulls=Migrazione delle richieste di modifica + mirror_from=mirror da forked_from=derivato da generated_from=generato da @@ -2288,6 +2316,7 @@ pulls.cmd_instruction_merge_title = Merge pulls.cmd_instruction_checkout_desc = Dalla tua repository del progetto, accedi a un nuovo ramo e prova le modifiche. milestones.new_subheader = I traguardi possono aiutarti ad organizzare le segnalazioni e a tracciarne i progressi. activity.navbar.contributors = Contributori +migrate.cancel_migrating_title = Annulla migrazione more_operations = Ulteriori operazioni actions = Azioni commit.operations = Operazioni @@ -2497,6 +2526,7 @@ tree_path_not_found.tag = Il percorso %[1]s non esiste nel tag %[2]s transfer.no_permission_to_accept = Non hai i permessi per accettare questo trasferimento. transfer.no_permission_to_reject = Non hai i permessi per rifiutare questo trasferimento. migrate_options_lfs_endpoint.placeholder = Se lasciato vuoto il punto di accesso sarà derivato dall'URL usato per clonare +migrate.cancel_migrating_confirm = Vuoi annullare questa migrazione? editor.filename_is_invalid = Nome file invalido: "%s". editor.unable_to_upload_files = Impossibile caricare i file su "%s" con errore: %v projects.create_success = Il progetto "%s" è stato creato. @@ -2615,6 +2645,9 @@ settings.event_pull_request_review_request_desc = Richiesta la revisione della r stars = Stelle issues.num_participants_one = %d partecipante open_with_editor = Apri con %s +n_commit_few = %s commit +n_branch_one = %s ramo +n_tag_few = %s etichette settings.web_hook_name_sourcehut_builds = Build SourceHut settings.sourcehut_builds.manifest_path = Percorso manifest della build settings.sourcehut_builds.visibility = Visibilità attività @@ -2625,10 +2658,13 @@ editor.push_out_of_date = Il push sembra essere obsoleto. issues.archived_label_description = (Archiviato) %s settings.sourcehut_builds.secrets_helper = Fornisci l'accesso ai segreti della build all'incarico (richiede il permesso SECRETS:RO) settings.add_webhook.invalid_path = Il percorso non deve contenere dei componenti quali ".", "..", o una stringa vuota. Non può iniziare o finire con uno slash. +n_commit_one = %s commit settings.enforce_on_admins = Imponi questa regola agli amministratori del repository release.system_generated = Questo allegato è stato generato automaticamente. pulls.ready_for_review = Pronto alla revisione? editor.commit_id_not_matching = L'ID del commit non combacia con quello del commit che stavi modificando. Conferma le tue modifiche su un nuovo ramo, poi fondilo col ramo desiderato. +n_branch_few = %s rami +n_tag_one = %s etichetta commits.search_branch = Questo ramo settings.rename_branch_failed_protected = Non è possibile rinominare il ramo %s perché è un ramo protetto. settings.event_pull_request_enforcement = Imposizione @@ -2642,6 +2678,8 @@ project = Progetti issues.edit.already_changed = Impossibile salvare le modifiche alla segnalazione. Sembra che il contenuto sia già stato modificato da un*altrə utente. Aggiornare la pagina e provare a modificare nuovamente per evitare di sovrascrivere le modifiche subscribe.pull.guest.tooltip = Accedi per iscriverti a questa richiesta di modifica. subscribe.issue.guest.tooltip = Accedere per seguire questa segnalazione. +n_release_one = %s rilascio +n_release_few = %s rilasci issues.author.tooltip.pr = Quest'utente è l'autorə di questa richiesta di modifica. release.hide_archive_links = Nascondi automaticamente gli archivi generati settings.federation_settings = Impostazioni di federazione @@ -2915,6 +2953,34 @@ dashboard.reinit_missing_repos=Reinizializza tutti i repository Git mancanti per dashboard.sync_external_users=Sincronizza dati utente esterno dashboard.cleanup_hook_task_table=Pulisci tabella hook_task dashboard.cleanup_packages=Pulizia pacchetti scaduti +dashboard.server_uptime=Tempo di attività +dashboard.current_goroutine=Goroutine attuali +dashboard.current_memory_usage=Utilizzo di memoria attuale +dashboard.total_memory_allocated=Memoria allocata totale +dashboard.memory_obtained=Memoria ottenuta +dashboard.pointer_lookup_times=Tempi di ricerca del puntatore +dashboard.memory_allocate_times=Allocazioni di memoria +dashboard.memory_free_times=Rilasci di memoria +dashboard.current_heap_usage=Utilizzo heap attuale +dashboard.heap_memory_obtained=Memoria heap ottenuta +dashboard.heap_memory_idle=Memoria heap inattiva +dashboard.heap_memory_in_use=Memoria heap in uso +dashboard.heap_memory_released=Memoria heap rilasciata +dashboard.heap_objects=Oggetti dell'heap +dashboard.bootstrap_stack_usage=Utilizzo pila iniziale +dashboard.stack_memory_obtained=Memoria pila ottenuta +dashboard.mspan_structures_usage=Utilizzo strutture MSpan +dashboard.mspan_structures_obtained=Strutture MSpan ottenute +dashboard.mcache_structures_usage=Utilizzo di strutture MCache +dashboard.mcache_structures_obtained=Strutture MCache ottenute +dashboard.profiling_bucket_hash_table_obtained=Tabelle hash del secchio di profilazione ottenute +dashboard.gc_metadata_obtained=Metadata del GC ottenuto +dashboard.other_system_allocation_obtained=Altre allocazioni di sistema ottenute +dashboard.next_gc_recycle=Prossimo riciclaggio GC +dashboard.last_gc_time=Dall'ultimo GC +dashboard.total_gc_pause=Pausa totale del GC +dashboard.last_gc_pause=Ultima pausa del GC +dashboard.gc_times=Esecuzioni GC dashboard.delete_old_actions=Elimina tutte le vecchie azioni dal database dashboard.delete_old_actions.started=Elimina tutte le vecchie azioni dal database iniziate. dashboard.update_checker=Controllore dell'aggiornamento @@ -2959,6 +3025,19 @@ users.purge=Elimina utente users.purge_help=Eliminare forzatamente l'utente e tutti i progetti, le organizzazioni e i pacchetti di proprietà dell'utente. Anche tutti i commenti e le segnalazioni verranno eliminati. users.deletion_success=L'account utente è stato eliminato. users.reset_2fa=Resetta 2FA +users.list_status_filter.menu_text=Filtro +users.list_status_filter.reset=Ripristina +users.list_status_filter.is_active=Attivo +users.list_status_filter.not_active=Inattivo +users.list_status_filter.is_admin=Amministratore +users.list_status_filter.not_admin=Non amministratore +users.list_status_filter.is_restricted=Limitato +users.list_status_filter.not_restricted=Non limitato +users.list_status_filter.is_prohibit_login=Accesso vietato +users.list_status_filter.not_prohibit_login=Accesso consentito +users.list_status_filter.is_2fa_enabled=2FA abilitato +users.list_status_filter.not_2fa_enabled=2FA disabilitato + emails.email_manage_panel=Gestisci email dell'utente emails.primary=Primario emails.activated=Attivato @@ -3251,6 +3330,21 @@ monitor.process.cancel_desc=Annullare un processo potrebbe causare una perdita d monitor.process.cancel_notices=Annulla: %s? monitor.process.children=Figli +monitor.queues=Code +monitor.queue=Coda: %s +monitor.queue.name=Nome +monitor.queue.type=Tipo +monitor.queue.exemplar=Tipo di esemplare +monitor.queue.numberworkers=Numero di lavoratori +monitor.queue.maxnumberworkers=Massimo numero di lavoratori +monitor.queue.numberinqueue=Numero in coda +monitor.queue.settings.title=Impostazioni piscina +monitor.queue.settings.maxnumberworkers=Massimo numero di workers +monitor.queue.settings.maxnumberworkers.placeholder=Attualmente %[1]d +monitor.queue.settings.maxnumberworkers.error=Il numero massimo di lavoratori deve essere un numero +monitor.queue.settings.submit=Aggiorna impostazioni +monitor.queue.settings.changed=Impostazioni aggiornate + notices.system_notice_list=Avvisi di sistema notices.view_detail_header=Visualizza dettagli dell'avviso notices.select_all=Seleziona tutto @@ -3289,6 +3383,7 @@ auths.tip.gitea = Registra una nuova applicazione OAuth2. La guida può essere t config.test_mail_sent = Una email di prova è stata inviata a "%s". monitor.processes_count = %d processi monitor.download_diagnosis_report = Scarica relazione diagnostica +monitor.queue.activeworkers = Lavoratori attivi config.app_data_path = Percorso dati dell'applicazione packages.cleanup = Pulisci dati scaduti dashboard.cleanup_actions = Pulisci log scaduti e artefatti dalle azioni @@ -3316,8 +3411,12 @@ auths.new_success = L'autenticazione "%s" è stata aggiunta. auths.tips.gmail_settings = Impostazioni Gmail: config.test_mail_failed = Impossibile inviare email di prova a "%s": %v users.details = Dettagli dell'utente +monitor.queue.review_add = Revisiona / aggiungi lavoratori self_check.no_problem_found = Non c'è ancora nessuna segnalazione. self_check.database_inconsistent_collation_columns = La base di dati sta usando la collazione %s ma queste colonne usano una collazione diversa. Potrebbe causare problemi imprevisti. +monitor.queue.settings.remove_all_items = Rimuovi tutto +monitor.queue.settings.desc = Le piscine crescono dinamicamente in risposta al blocco dei lavoratori in coda. +monitor.queue.settings.remove_all_items_done = Tutti gli elementi in coda sono stati rimossi. self_check.database_collation_mismatch = Pretendi che la base di dati usi la collazione: %s self_check.database_fix_mysql = Per utenti MySQL/MariaDB, potresti usare il comando "forgejo doctor convert" per risolvere problemi di collazione, o potresti risolvere il problema manualmente tramite SQL con "ALTER ... COLLATE ...". self_check.database_collation_case_insensitive = La base di dati sta usando la collazione %s, che è una collazione insensibile. Nonostante Forgejo potrebbe lavorarci, ci potrebbero essere rari casi che non vanno come previsto. @@ -3398,10 +3497,35 @@ raw_seconds=secondi raw_minutes=minuti [dropzone] +default_message=Trascina i file o clicca qui per caricare. +invalid_input_type=Non è possibile caricare file di questo tipo. +file_too_big=La dimensione del file ({{filesize}} MB) supera la dimensione massima ({{maxFilesize}} MB). +remove_file=Rimuovi file [notification] +notifications=Notifiche +unread=Non lette +read=Lette +no_unread=Nessuna notifica da leggere. +no_read=Nessuna notifica letta. +pin=Appunta notifica +mark_as_read=Segna come letto +mark_as_unread=Segna come non letto +mark_all_as_read=Segna tutti come letti +subscriptions = Sottoscrizioni +watching = Osservando +no_subscriptions = Nessuna iscrizione [gpg] +default_key=Firmato con la chiave predefinita +error.extract_sign=Impossibile ricavare la firma +error.generate_hash=Impossibile generare hash del commit +error.no_committer_account=Nessun account collegato all'indirizzo email del committer +error.no_gpg_keys_found=Non sono state trovate chiavi note per questa firma nel database +error.not_signed_commit=Commit non firmato +error.failed_retrieval_gpg_keys=Impossibile recuperare le chiavi associate al profilo dell'autore del commit +error.probable_bad_signature=ATTENZIONE! Anche se esiste una chiave con questo ID nel database, essa non verifica questo commit! Questo commit è SOSPETTO. +error.probable_bad_default_signature=ATTENZIONE! Anche se la chiave predefinita ha questo ID essa non verifica questo commit! Questo commit è SOSPETTO. [units] unit=Unità @@ -3409,11 +3533,183 @@ error.no_unit_allowed_repo=Non possiedi il permesso di accedere ad alcuna sezion error.unit_not_allowed=Non possiedi il permesso di accedere a questa sezione di repository. [packages] +title=Pacchetti desc=Gestisci pacchetti repository. +empty=Non ci sono ancora pacchetti. +empty.repo=Hai caricato un pacchetto, ma non è mostrato qui? Vai alle impostazioni del pacchetto e collegalo a questo repo. +filter.type=Tipo +filter.type.all=Tutti +filter.no_result=Il filtro non ha prodotto risultati. +filter.container.tagged=Etichettato +filter.container.untagged=Nont etichettato +published_by=Pubblicato %[1]s di %[3]s +published_by_in=Pubblicato %[1]s di %[3]s in %[5]s +installation=Installazione +about=Informazioni su questo pacchetto +requirements=Requisiti +dependencies=Dipendenze +keywords=Parole Chiave +details=Dettagli +details.author=Autore +details.project_site=Sito del progetto +details.license=Licenza +assets=Asset +versions=Versioni +versions.view_all=Vedi tutti +dependency.id=ID +dependency.version=Versione +alpine.install=Per installare il pacchetto, eseguire il seguente comando: +alpine.repository.branches=Rami +alpine.repository.repositories=Repository +chef.install=Per installare il pacchetto, eseguire il seguente comando: +composer.registry=Imposta questo registro nel tuo file ~/.composer/config.json: +composer.install=Per installare il pacchetto utilizzando Composer, eseguire il seguente comando: +composer.dependencies=Dipendenze +composer.dependencies.development=Dipendenze Di Sviluppo conan.details.repository=Repository +conan.registry=Configura questo registro dalla riga di comando: +conan.install=Per installare il pacchetto usando Conan, eseguire il seguente comando: +container.details.type=Tipo Immagine +container.details.platform=Piattaforma +container.pull=Tirare l'immagine dalla riga di comando: +container.multi_arch=SO / Architettura +container.layers=Livelli Immagine +container.labels=Etichette +container.labels.key=Chiave +container.labels.value=Valore +cran.install=Per installare il pacchetto, eseguire il seguente comando: +debian.registry=Configura questo registro dalla riga di comando: +debian.install=Per installare il pacchetto, eseguire il seguente comando: +generic.download=Scarica il pacchetto dalla riga di comando: +helm.registry=Configura questo registro dalla riga di comando: +helm.install=Per installare il pacchetto, eseguire il seguente comando: +maven.registry=Configura questo registro nel file pom.xml del tuo progetto: +maven.install=Per utilizzare il pacchetto includere i seguenti nel blocco dipendenze nel file pom.xml: +maven.install2=Esegui tramite riga di comando: +maven.download=Per scaricare la dipendenza, eseguire tramite riga di comando: +nuget.registry=Configura questo registro dalla riga di comando: +nuget.install=Per installare il pacchetto utilizzando NuGet, eseguire il seguente comando: +nuget.dependency.framework=Target Framework +npm.registry=Impostare questo registro nel file del progetto .npmrc: +npm.install=Per installare il pacchetto usando npm, eseguire il seguente comando: +npm.install2=o aggiungerlo al file package.json: +npm.dependencies=Dipendenze +npm.dependencies.development=Dipendenze Di Sviluppo +npm.dependencies.peer=Dipendenze Peer +npm.dependencies.optional=Dipendenze Opzionali +npm.details.tag=Tag +pub.install=Per installare il pacchetto utilizzando NuGet, eseguire il seguente comando: +pypi.requires=Richiede Python +pypi.install=Per installare il pacchetto usando pip, eseguire il seguente comando: +rpm.registry=Configura questo registro dalla riga di comando: +rpm.install=Per installare il pacchetto, eseguire il seguente comando: +rubygems.install=Per installare il pacchetto usando gem, eseguire il seguente comando: +rubygems.install2=o aggiungerlo al file Gem: +rubygems.dependencies.runtime=Dipendenze Runtime +rubygems.dependencies.development=Dipendenze Di Sviluppo +rubygems.required.ruby=Richiede la versione di Ruby +rubygems.required.rubygems=Richiede la versione RubyGem +swift.registry=Configura questo registro dalla riga di comando: +settings.link=Collega questo pacchetto a un repository +settings.link.description=Se si collega un pacchetto a un repository, il pacchetto è elencato nell'elenco dei pacchetti del repository. +settings.link.select=Seleziona repositorio +settings.link.button=Aggiorna collegamento repositorio +settings.link.success=Il link del repository è stato aggiornato correttamente. +settings.link.error=Impossibile aggiornare il link del repository. +settings.delete=Elimina pacchetto +settings.delete.description=L'eliminazione di un pacchetto è permanente e non può essere annullata. +settings.delete.notice=Stai per eliminare %s (%s). Questa operazione è irreversibile, sei sicuro? +settings.delete.success=Il pacchetto è stato eliminato. +settings.delete.error=Impossibile eliminare il pacchetto. owner.settings.cleanuprules.enabled=Attivo +debian.repository.architectures = Architetture +rpm.repository.architectures = Architetture +container.digest = Digest: +debian.repository.components = Componenti +alpine.repository.architectures = Architetture +debian.repository.distributions = Distribuzioni +empty.documentation = Per ulteriori informazioni sul registro dei pacchetti vedi la documentazione. +registry.documentation = Per ulteriori informazioni sul registro %s vedi la documentazione. +details.repository_site = Sito del repositorio +details.documentation_site = Sito della documentazione +alpine.registry = Imposta questo registro aggiungendo l'URL nel tuo file /etc/apk/repositories: +alpine.registry.key = Scarica la chiave RSA pubblica del registro nella cartella /etc/apk/keys/ per verificare la firma dell'indice: +alpine.repository = Informazioni sul repositorio +cargo.registry = Imposta il registro nel file di configurazione di Cargo (per esempio ~/.cargo/config.toml): +chef.registry = Imposta questo registro nel tuo file ~/.chef/config.rb: +conda.registry = Imposta questo registro come un repositorio Conda nel tuo file .condarc: +conda.install = Per installare il pacchetto usando conda, esegui il comando seguente: +debian.registry.info = Scegli $distribuzione e $componente dalla lista sotto. +debian.repository = Informazioni sul repositorio +go.install = Installa il pacchetto dalla riga di comando: +rpm.distros.suse = sulle distribuzioni basate su SUSE +rpm.repository = Informazioni sul repositorio +swift.install = Aggiungi il pacchetto nel tuo file Package.swift: +swift.install2 = ed esegui il comando seguente: +vagrant.install = Per aggiungere una scatola Vagrant esegui il comando seguente: +owner.settings.cargo.initialize = Inizializza indice +rpm.distros.redhat = sulle distribuzione basate su RedHat +alpine.registry.info = scegli $ramo e $repositorio dalla lista sotto. +cargo.install = Per installare il pacchetto usando Cargo esegui il comando seguente: +cran.registry = Imposta questo registro nel tuo file Rprofile.site: +rpm.repository.multiple_groups = Questo pacchetto è disponibile in molteplici gruppi. +owner.settings.cargo.title = Indice del registro di Cargo +owner.settings.cargo.initialize.error = Impossibile inizializzare l'indice di cargo: %v +owner.settings.cargo.rebuild = Ricostruendo l'indice +owner.settings.chef.keypair = Genera coppia di chiavi +owner.settings.chef.title = Registro Chef +owner.settings.cleanuprules.success.delete = La regola di pulizia è stata eliminata. +owner.settings.cleanuprules.success.update = La regola di pulizia è stata aggiornata. +owner.settings.cleanuprules.remove.pattern = Rimuovi versioni corrispondenti +owner.settings.cleanuprules.remove.days = Rimuovi versioni più vecchie di +owner.settings.cleanuprules.keep.pattern.container = La versione latest è sempre tenuta per pacchetti container. +owner.settings.cleanuprules.keep.pattern = Mantieni le versioni corrispondenti owner.settings.cleanuprules.keep.count.n = %s versioni per pacchetto owner.settings.cleanuprules.keep.count.1 = 1 versione per pacchetto +owner.settings.cleanuprules.keep.count = Mantieni la più recente +owner.settings.cleanuprules.remove.title = Le versioni che soddisfano queste regole saranno rimosse, eccetto se la regola sopra dice di tenerle. +owner.settings.cleanuprules.pattern_full_match = Applica il modello a tutto il nome del pacchetto +owner.settings.cleanuprules.preview.none = La regola di pulizia non corrisponde ad alcun pacchetto. +owner.settings.cleanuprules.preview.overview = È prevista la rimozione di %d pacchetti. +owner.settings.cleanuprules.preview = Anteprima della regola di pulizia +owner.settings.cleanuprules.none = Non c'è ancora nessuna regola di pulizia. +owner.settings.cleanuprules.edit = Modifica regola di pulizia +owner.settings.cleanuprules.add = Aggiungi regola di pulizia +owner.settings.cleanuprules.title = Gestisci regole di pulizia +owner.settings.cargo.rebuild.success = L'indice di Cargo è stato ricostruito correttamente. +owner.settings.cargo.rebuild.error = Impossibile ricostruire l'indice di Cargo: %v +owner.settings.cleanuprules.keep.title = Le versioni che soddisfano queste regole sono mantenute, anche se soddisfano una regola di rimozione sotto. +owner.settings.cargo.initialize.description = Per utilizzare il registro Cargo è necessario un repositorio Git con un indice speciale. Utilizzando questa opzione, il repositorio verrà (ri)creato e configurato automaticamente. +owner.settings.chef.keypair.description = Per autenticarsi al registro Chef è necessaria una coppia di chiavi. Se hai già generato una coppia di chiavi la generazione di una nuova coppia scarterà la vecchia. +owner.settings.cargo.initialize.success = L'indice di Cargo è stato creato correttamente. +owner.settings.cargo.rebuild.no_index = Impossibile ricostruire, nessun indice è inizializzato. +owner.settings.cargo.rebuild.description = La ricostruzione può essere utile se l'indice non è sincronizzato con i pacchetti Cargo conservati. +npm.dependencies.bundle = Dipendenze raggruppate +arch.version.groups = Gruppo +arch.version.conflicts = Va in conflitto con +arch.version.depends = Dipende da +arch.version.makedepends = Dipendenze di build +arch.version.checkdepends = Dipendenze di controllo +arch.version.replaces = Sostituisce +arch.version.optdepends = Dipende opzionalmente da +arch.version.backup = Backup +search_in_external_registry = Cerca in %s +arch.version.provides = Fornisce +arch.pacman.conf = Aggiungi il server con la relativa distribuzione e architettura a /etc/pacman.conf: +alt.setup = Aggiungi il repositorio alla lista dei repositori in rete (seleziona l'architettura necessaria al posto di "_arch_"): +container.images.title = Immagini +arch.version.properties = Proprietà della versione +alt.registry.install = Per installare il pacchetto, esegui il comando seguente: +alt.install = Installa pacchetto +alt.registry = Configura questo registro dalla riga di comando: +arch.pacman.helper.gpg = Aggiungi il certificato a pacman: +arch.pacman.repo.multi = %s ha la stessa versione in diverse distribuzioni. +arch.pacman.repo.multi.item = Configurazione per %s +arch.pacman.sync = Sincronizza il paccketto con pacman: +arch.version.description = Descrizione +alt.repository = Informazioni del repositorio +alt.repository.architectures = Architetture +alt.repository.multiple_groups = Questo pacchetto è disponibile per più gruppi. [secrets] secrets = Segreti @@ -3431,23 +3727,99 @@ creation.success = Il segreto "%s" è stato aggiungo. deletion.success = Il segreto è stato rimosso. [actions] +runners.id=ID +runners.name=Nome +runners.owner_type=Tipo +runners.description=Descrizione +runners.labels=Etichette +runners.task_list.run=Esegui +runners.task_list.repository=Repository +runners.task_list.commit=Commit +runners.status.active=Attivo +runners.version=Versione + +runs.commit=Commit +status.unknown = Sconosciuto +status.waiting = In attesa +status.success = Successo +runners.task_list.status = Stato +variables = Variabili +runs.workflow = Workflow +runs.actor = Attore +runs.status = Stato +runners.status.idle = Inattivo +status.cancelled = Annullato +status.skipped = Saltato +status.blocked = Bloccato +runners = Esecutori +runners.status.unspecified = Sconosciuto +runners.status.offline = Offline +runs.scheduled = Pianificato unit.desc = Gestisci azioni +runners.runner_manage_panel = Gestisci esecutori +actions = Azioni +status.running = In corso +status.failure = Errore +runners.status = Stato +runners.runner_title = Esecutore +runners.delete_runner_success = Esecutore eliminato correttamente +runs.no_results = Nessun risultato trovato. workflow.enable = Abilita flusso di lavoro +runs.no_workflows = Non ci sono ancora flussi di lavoro. +runs.status_no_select = Tutti gli stati +runs.actors_no_select = Tutti gli agenti +runs.no_matching_online_runner_helper = Nessun esecutore online corrispondente con etichetta: %s +runs.invalid_workflow_helper = Il file di configurazione del flusso di lavoro è invalido. Controlla il tuo file di configurazione: %s +runs.pushed_by = immesso da +runs.all_workflows = Tutti i flussi di lavoro +runners.reset_registration_token_success = Reimpostazione del token di registrazione dell'esecutore fallita +runners.reset_registration_token = Reimposta token di registrazione +runners.none = Nessun esecutore disponibile +runners.delete_runner_header = Conferma l'eliminazione di questo esecutore +runners.delete_runner_failed = Impossibile eliminare l'esecutore +runners.delete_runner = Elimina questo esecutore +runners.update_runner_failed = Impossibile aggiornare l'esecutore +runners.update_runner_success = Esecutore aggiornato correttamente +runners.update_runner = Aggiorna cambiamenti +runners.edit_runner = Modifica esecutore +runners.task_list.done_at = Eseguito +runners.task_list.no_tasks = Non c'è ancora nessuna attività. +runners.last_online = Ultima volta online +runners.new_notice = Come avviare un esecutore +runners.new = Crea un nuovo esecutore +variables.update.success = La variabile è stata modificata. +variables.update.failed = Impossibile modificare la variabile. +variables.creation.failed = Errore nell'aggiunta della variabile. +variables.deletion.success = La variabile è stata rimossa. +variables.deletion.failed = Impossibile rimuovere la variabile. +variables.edit = Modifica variabile variables.id_not_exist = La variabile con ID %s non esiste. +variables.deletion.description = La rimozione di una variabile è permanente e non può essere annullata. Continuare? +variables.deletion = Rimuovi variabile +variables.none = Non ci sono ancora variabili. +variables.creation = Aggiungi variabile +variables.management = Gestisci variabili workflow.disabled = Il flusso di lavoro è disabilitato. workflow.enable_success = Il flusso di lavoro "%s" è stato abilitato correttamente. workflow.disable_success = Il flusso di lavoro "%s" è stato disabilitato con successo. workflow.disable = Disabilita flusso di lavoro runs.empty_commit_message = (messaggio di commit vuoto) runs.no_runs = Il flusso di lavoro non è stato ancora eseguito. +variables.creation.success = La variabile "%s" è stata aggiunta. +variables.description = Le variabili saranno passate a determinate azioni e non possono essere lette altrimenti. need_approval_desc = È necessaria l'approvazione per eseguire flussi di lavoro per richieste di modifica da fork. +runners.delete_runner_notice = Se un'attività è in esecuzione su questo esecutore sarà terminata ed etichettata fallito. Potrebbe rompere flussi di lavoro di costruzione. +runners.task_list = Attività recenti su questo esecutore +runs.no_job_without_needs = Il flusso di lavoro deve contenere almeno un incarico senza dipendenze. workflow.dispatch.trigger_found = Questo flusso di lavoro ha un rilevatore di eventi workflow_dispatch. workflow.dispatch.run = Esegui flusso di lavoro workflow.dispatch.success = L'esecuzione del flusso di lavoro è stata richiesta con successo. workflow.dispatch.input_required = Richiedi valore per l'ingresso "%s". workflow.dispatch.invalid_input_type = Tipo ingresso "%s" non valido. workflow.dispatch.warn_input_limit = Visualizzati solo i primi %d ingressi. +runs.no_job = Il flusso di lavoro deve contenere almeno un incarico workflow.dispatch.use_from = Usa flusso di lavoro da +variables.not_found = Non è stato possibile trovare la variabile. runs.expire_log_message = I log sono stati eliminati in quanto troppo vecchi. runs.no_workflows.help_no_write_access = Per saperne di più su Forgejo Actions, consulta la documentazione. runs.no_workflows.help_write_access = Non sai come iniziare con Forgejo Actions? Dai un'occhiata alla guida rapida nella documentazione utente per scrivere il tuo primo flusso di lavoro, poi configura un esecutore Forgejo per eseguire i tuoi incarichi. @@ -3499,8 +3871,19 @@ union_tooltip = Include i risultati che combaciano con una qualsiasi delle parol union = Parole chiavi [munits.data] +gib = GiB +tib = TiB +pib = PiB +kib = KiB +mib = MiB +eib = EiB +b = B [markup] +filepreview.lines = Linee da %[1]d a %[2]d in %[3]s +filepreview.truncated = L'anteprima è stata troncata +filepreview.line = Linea %[1]d in %[2]s + [repo.permissions] issues.write = Scrittura: Chiudere segnalazioni e gestire metadati come etichette, traguardi, assegnatarɜ, scadenze e dipendenze. diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index f4cbb22f49..92844d5dce 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -37,6 +37,18 @@ twofa=2要素認証 twofa_scratch=2要素認証スクラッチコード passcode=パスコード +webauthn_insert_key=セキュリティキーを挿入 +webauthn_sign_in=セキュリティキーのボタンを押してください。セキュリティキーにボタンが無い場合は、挿入しなおしてください。 +webauthn_press_button=セキュリティキーのボタンを押してください… +webauthn_use_twofa=携帯電話から2要素認証コードを使用する +webauthn_error=セキュリティキーを読み取ることができません。 +webauthn_unsupported_browser=お使いのブラウザは現在 WebAuthn をサポートしていません。 +webauthn_error_unknown=不明なエラーが発生しました。 もう一度やり直してください。 +webauthn_error_insecure=WebAuthn はセキュアな接続のみをサポートしています。HTTP 経由でテストする場合は、"localhost" または "127.0.0.1" のオリジンが使用できます +webauthn_error_unable_to_process=サーバーがリクエストを処理できませんでした。 +webauthn_error_duplicated=このリクエストに対しては、許可されていないセキュリティキーです。 キーが未登録であることを確認してください。 +webauthn_error_empty=このキーに名前を設定する必要があります。 +webauthn_error_timeout=キーを読み取る前にタイムアウトになりました。 このページをリロードしてもう一度やり直してください。 repository=リポジトリ organization=組織 mirror=ミラー @@ -182,6 +194,7 @@ buttons.enable_monospace_font=等幅フォントを有効にする buttons.disable_monospace_font=等幅フォントを無効にする buttons.unindent.tooltip = アイテムを1つずつネストの解除をする buttons.indent.tooltip = アイテムを1つずつネストする + buttons.new_table.tooltip = テーブル追加 table_modal.header = テーブル追加 table_modal.placeholder.header = ヘッダー @@ -212,6 +225,7 @@ lightweight=軽量 lightweight_desc=Forgejo の最小動作要件は小さくて、安価な Raspberry Pi でも動きます。エネルギー消費を節約しましょう! license=オープンソース license_desc=Go get Forgejo! 私たちと一緒にこのプロジェクトをより良くしていくために、何か貢献してみませんか。 些細なことでも大丈夫! 積極的にお願いします! + platform_desc = ForgejoはLinuxやFreeBSDなどのオープンソースOS、さまざまなCPUアーキテクチャで動作することが確認されています。お気に入りの環境を選んでください! [install] @@ -437,6 +451,7 @@ unauthorized_credentials = 資格情報が正しくないか、期限が切れ sign_up_button = 今すぐ登録して下さい。 hint_login = すでにアカウントをお持ちですか? 今すぐサインイン! hint_register = アカウントが必要ですか? 今すぐ登録してください。 + use_onetime_code = ワンタイムコードを使用する [mail] @@ -666,6 +681,7 @@ following.title.one = フォロー中 following.title.few = フォロー中 followers.title.one = フォロワー followers.title.few = フォロワー + public_activity.visibility_hint.self_private_profile = あなたのプロフィールは非公開のため、あなたのアクティビティはあなたとインスタンス管理者にのみ表示されます。設定。 [settings] @@ -980,8 +996,11 @@ keep_activity_private.description = 公開アクティビティ language.description = この言語はアカウントに保存され、ログイン後にデフォルトとして使用されます。 language.localization_project = Forgejo をあなたの言語に翻訳するのを手伝ってください。詳細はこちら。 quota = クオータ + +storage_overview = ストレージ設定 keep_pronouns_private = 認証されたユーザーにのみ代名詞を表示する keep_pronouns_private.description = これにより、ログインしていないユーザーに対して代名詞が表示されなくなります。 +ssh_token_help_ssh_agent = または、SSH エージェント (SSH_AUTH_SOCK 変数が設定されている) を使用している場合: regenerate_token = 再生成 access_token_regeneration = アクセストークンを再生成する access_token_regeneration_desc = トークンを再生成すると、そのトークンを使用しているアプリケーションからアカウントへのアクセスが取り消されます。この操作は元に戻せません。続行しますか? @@ -992,18 +1011,16 @@ quota.applies_to_org = この組織には以下の制限が適用されます quota.rule.exceeded = 超過 quota.rule.exceeded.helper = このルールのオブジェクトの合計サイズが制限を超えました。 quota.rule.no_limit = 無制限 -quota.sizes.repos.all = リポジトリ -quota.sizes.repos.private = プライベートリポジトリ -quota.sizes.repos.public = 公開リポジトリ -ssh_token_help_ssh_agent = または、SSH エージェント (SSH_AUTH_SOCK 変数が設定されている) を使用している場合: quota.sizes.all = 全体 +quota.sizes.repos.all = リポジトリ +quota.sizes.repos.public = 公開リポジトリ +quota.sizes.repos.private = プライベートリポジトリ 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 = Wiki -quota.sizes.assets.artifacts = アーティファクト -storage_overview = ストレージ設定 [repo] new_repo_helper=リポジトリには、プロジェクトのすべてのファイルとリビジョン履歴が入ります。 すでにほかの場所でホストしていますか? リポジトリを移行 もどうぞ。 @@ -1137,6 +1154,14 @@ migrate_options_lfs_endpoint.label=LFS エンドポイント migrate_options_lfs_endpoint.description=マイグレーションでは、リモート側のGitをもとにLFSサーバーを決定しようとします。 リポジトリのLFSデータがほかの場所に保存されている場合は、独自のエンドポイントを指定することができます。 migrate_options_lfs_endpoint.description.local=ローカルサーバーのパスもサポートされています。 migrate_options_lfs_endpoint.placeholder=空にするとエンドポイントはクローン URL から決定されます +migrate_items=移行する項目 +migrate_items_wiki=Wiki +migrate_items_milestones=マイルストーン +migrate_items_labels=ラベル +migrate_items_issues=イシュー +migrate_items_pullrequests=プルリクエスト +migrate_items_merge_requests=マージリクエスト +migrate_items_releases=リリース migrate_repo=リポジトリを移行 migrate.clone_address=移行 / クローンするURL migrate.clone_address_desc=既存リポジトリの、HTTP(S)またはGit形式のクローンURL @@ -1155,6 +1180,16 @@ migrate.migrating=%s から移行しています … migrate.migrating_failed=%s からの移行が失敗しました。 migrate.migrating_failed.error=移行に失敗しました: %s migrate.migrating_failed_no_addr=移行に失敗しました。 +migrate.migrating_git=Gitデータ移行中 +migrate.migrating_topics=トピック移行中 +migrate.migrating_milestones=マイルストーン移行中 +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=generated from @@ -1448,7 +1483,7 @@ issues.remove_ref_at=`が参照 %s を削除 %s` issues.add_ref_at=`が参照 %s を追加 %s` issues.delete_branch_at=`がブランチ %[1]s を削除 %[2]s` issues.filter_label=ラベル -issues.filter_label_exclude=ラベルを除外するには、Alt + クリック を使用します +issues.filter_label_exclude=`ラベルで除外するには alt + click/enter` issues.filter_label_no_select=すべてのラベル issues.filter_label_select_no_label=ラベルなし issues.filter_milestone=マイルストーン @@ -2594,7 +2629,9 @@ settings.wiki_globally_editable = 誰でもWikiを編集できる様にする settings.confirmation_string = 確認 settings.wiki_rename_branch_main_notices_1 = この操作は 取り消しできません 。 stars = スター +n_tag_few = %s のタグ settings.graphql_url = GraphQL URL +n_branch_one = %s のブランチ settings.units.units = 機能設定 settings.wiki_rename_branch_main_notices_2 = これにより、%s のリポジトリ wiki の内部ブランチの名前が永久に変更されます。既存のチェックアウトを更新する必要があります。 settings.sourcehut_builds.access_token_helper = JOBS:RW 権限を持つアクセス トークン。meta.sr.ht で builds.sr.ht トークン または シークレット アクセスを持つ builds.sr.ht トークン を生成します。 @@ -2606,6 +2643,9 @@ open_with_editor = %s で開く release.system_generated = この添付ファイルは自動的に生成されます。 settings.archive.mirrors_unavailable = リポジトリがアーカイブされている場合、ミラーは利用できません。 settings.rename_branch_failed_protected = 保護されたブランチのため、ブランチ %s の名前を変更できません。 +n_tag_one = %s のタグ +n_branch_few = %s のブランチ +n_commit_few = %s のコミット settings.confirm_wiki_branch_rename = Wikiブランチの名前を変更する settings.add_collaborator_blocked_our = リポジトリ所有者が共同作業者をブロックしているため、共同作業者を追加できません。 settings.sourcehut_builds.visibility = ジョブの可視性 @@ -2613,6 +2653,7 @@ settings.sourcehut_builds.secrets = シークレット settings.ignore_stale_approvals_desc = 古いコミット (古いレビュー) に対して行われた承認を、PR の承認数にカウントしないでください。古いレビューがすでに却下されている場合は関係ありません。 settings.enforce_on_admins_desc = リポジトリ管理者はこのルールを回避できません。 release.hide_archive_links = 自動生成されたアーカイブを非表示にする +n_commit_one = %s のコミット settings.wiki_branch_rename_success = リポジトリ wiki のブランチ名が正常に正規化されました。 settings.add_collaborator_blocked_them = リポジトリ所有者がブロックされているため、共同作業者を追加できません。 settings.add_webhook.invalid_path = パスには「.」や「..」や空の文字列を含めることはできません。また、スラッシュで開始または終了することはできません。 @@ -2647,6 +2688,8 @@ issues.edit.already_changed = イシューの変更を保存できません。 no_eol.text = EOLなし pulls.edit.already_changed = プルリクエストの変更を保存できません。コンテンツは既に別のユーザーによって変更されているようです。変更が上書きされないように、ページを更新して再度編集してください pulls.cmd_instruction_merge_warning = 警告: このリポジトリでは「手動マージの自動検出」設定が有効になっていません。後でこのプル リクエストを手動でマージ済みとしてマークする必要があります。 +n_release_one = %s リリース +n_release_few = %s リリース milestones.filter_sort.name = 名前 mirror_use_ssh.not_available = SSH認証は利用できません。 mirror_denied_combination = 公開鍵とパスワードベースの認証を組み合わせて使用することはできません。 @@ -2671,6 +2714,7 @@ issues.all_title = 全て settings.protect_new_rule = 新しいブランチ保護ルールを作成する settings.discord_icon_url.exceeds_max_length = アイコンのURLは 2048 文字以下にする必要があります issues.new.assign_to_me = 自分に割り当て + new_from_template = テンプレートを使用する new_from_template_description = このインスタンスで既存のリポジトリ テンプレートを選択して、その設定を適用できます。 new_advanced = 詳細設定 @@ -2681,10 +2725,10 @@ archive.pull.noreview = このリポジトリはアーカイブされていま sync_fork.branch_behind_one = このブランチは %[2]s より %[1]d コミット遅れています sync_fork.branch_behind_few = このブランチは %[2]s より %[1]d コミット遅れています sync_fork.button = 同期 +editor.commit_email = コミットメールアドレス issues.filter_no_results = 結果なし issues.filter_no_results_placeholder = 検索フィルターを調整してみてください。 issues.filter_type.all_pull_requests = すべてのプルリクエスト -editor.commit_email = コミットメールアドレス issues.filter_sort.relevance = 関連度 issues.num_reviews_one = %d件のレビュー issues.num_reviews_few = %d件のレビュー @@ -2889,6 +2933,34 @@ dashboard.sync_external_users=外部ユーザーデータの同期 dashboard.cleanup_hook_task_table=hook_taskテーブルのクリーンアップ dashboard.cleanup_packages=期限切れパッケージのクリーンアップ dashboard.cleanup_actions=Actionsから期限切れのログとアーティファクトのクリーンアップする +dashboard.server_uptime=サーバーの稼働時間 +dashboard.current_goroutine=現在のGoroutine数 +dashboard.current_memory_usage=現在のメモリ使用量 +dashboard.total_memory_allocated=メモリ割当量の累計 +dashboard.memory_obtained=メモリ取得量 +dashboard.pointer_lookup_times=ポインタ参照回数 +dashboard.memory_allocate_times=メモリ割当回数 +dashboard.memory_free_times=メモリ解放回数 +dashboard.current_heap_usage=現在のヒープ使用量 +dashboard.heap_memory_obtained=ヒープ用メモリ取得量 +dashboard.heap_memory_idle=未使用のヒープ容量 +dashboard.heap_memory_in_use=使用中のヒープ容量 +dashboard.heap_memory_released=解放済みヒープ容量 +dashboard.heap_objects=ヒープオブジェクト数 +dashboard.bootstrap_stack_usage=スタック使用量 +dashboard.stack_memory_obtained=スタック用メモリ取得量 +dashboard.mspan_structures_usage=MSpan構造体の使用量 +dashboard.mspan_structures_obtained=MSpan構造体用取得量 +dashboard.mcache_structures_usage=MCache構造体の使用量 +dashboard.mcache_structures_obtained=MCache構造体用取得量 +dashboard.profiling_bucket_hash_table_obtained=バケットハッシュテーブルのプロファイリング割当量 +dashboard.gc_metadata_obtained=GCメタデータ用取得量 +dashboard.other_system_allocation_obtained=その他システム割当用取得量 +dashboard.next_gc_recycle=次回のGCリサイクル +dashboard.last_gc_time=前回GCからの時間 +dashboard.total_gc_pause=GC停止時間の合計 +dashboard.last_gc_pause=前回のGC停止時間 +dashboard.gc_times=GC実行回数 dashboard.delete_old_actions=データベースから古い操作履歴をすべて削除 dashboard.delete_old_actions.started=データベースからの古い操作履歴の削除を開始しました。 dashboard.update_checker=更新チェック @@ -2945,6 +3017,18 @@ users.purge_help=強制的にユーザーとそのユーザーが所有してい users.still_own_packages=このユーザーはまだ1つ以上のパッケージを所有しています。先にそれらのパッケージを削除してください。 users.deletion_success=ユーザーアカウントを削除しました。 users.reset_2fa=2要素認証をリセット +users.list_status_filter.menu_text=フィルター +users.list_status_filter.reset=リセット +users.list_status_filter.is_active=有効 +users.list_status_filter.not_active=無効 +users.list_status_filter.is_admin=管理者 +users.list_status_filter.not_admin=非管理者 +users.list_status_filter.is_restricted=制限あり +users.list_status_filter.not_restricted=制限なし +users.list_status_filter.is_prohibit_login=ログインを禁止 +users.list_status_filter.not_prohibit_login=ログインを許可 +users.list_status_filter.is_2fa_enabled=2要素認証有効 +users.list_status_filter.not_2fa_enabled=2要素認証無効 users.details=ユーザーの詳細 emails.email_manage_panel=ユーザーのメールアドレスを管理 @@ -3264,6 +3348,26 @@ monitor.process.cancel_desc=処理をキャンセルするとデータが失わ monitor.process.cancel_notices=キャンセル: %s? monitor.process.children=子プロセス +monitor.queues=キュー +monitor.queue=キュー: %s +monitor.queue.name=キュー名 +monitor.queue.type=種類 +monitor.queue.exemplar=要素の型 +monitor.queue.numberworkers=ワーカー数 +monitor.queue.activeworkers=使用ワーカー数 +monitor.queue.maxnumberworkers=ワーカー数上限 +monitor.queue.numberinqueue=キュー内の数 +monitor.queue.review_add=ワーカーの確認 / 追加 +monitor.queue.settings.title=プール設定 +monitor.queue.settings.desc=プールはワーカーキューの待機状態に応じて動的に大きくなります。 +monitor.queue.settings.maxnumberworkers=ワーカー数上限 +monitor.queue.settings.maxnumberworkers.placeholder=現在の設定 %[1]d +monitor.queue.settings.maxnumberworkers.error=ワーカー数上限は数値にしてください +monitor.queue.settings.submit=設定を更新 +monitor.queue.settings.changed=設定を更新しました +monitor.queue.settings.remove_all_items=すべて削除 +monitor.queue.settings.remove_all_items_done=キュー内のすべての項目を削除しました。 + notices.system_notice_list=システム通知 notices.view_detail_header=通知の詳細 notices.operations=操作 @@ -3363,10 +3467,35 @@ raw_seconds=秒 raw_minutes=分 [dropzone] +default_message=ここにファイルをドロップまたはクリックしてアップロードします。 +invalid_input_type=この種類のファイルはアップロードできません。 +file_too_big=アップロードされたファイルのサイズ ({{filesize}} MB) が最大サイズ ({{maxFilesize}} MB) を超えています。 +remove_file=ファイル削除 [notification] +notifications=通知 +unread=未読 +read=既読 +no_unread=未読の通知はありません。 +no_read=既読の通知はありません。 +pin=通知をピン留め +mark_as_read=既読にする +mark_as_unread=未読にする +mark_all_as_read=すべて既読にする +subscriptions=購読 +watching=ウォッチ中 +no_subscriptions=購読しているものはありません [gpg] +default_key=デフォルト鍵で署名 +error.extract_sign=署名の抽出に失敗しました +error.generate_hash=コミットのハッシュ生成に失敗しました +error.no_committer_account=コミッターのメールアドレスに対応するアカウントが存在しません +error.no_gpg_keys_found=この署名に対応する既知のキーがデータベースに存在しません +error.not_signed_commit=署名されたコミットではありません +error.failed_retrieval_gpg_keys=コミッターのアカウントに登録されたキーを取得できませんでした +error.probable_bad_signature=警告! このIDの鍵はデータベースに登録されていますが、その鍵でコミットの検証が通りません! これは疑わしいコミットです。 +error.probable_bad_default_signature=警告! これはデフォルト鍵のIDですが、デフォルト鍵ではコミットの検証が通りません! これは疑わしいコミットです。 [units] unit=ユニット @@ -3374,11 +3503,160 @@ error.no_unit_allowed_repo=このリポジトリのどのセクションにも error.unit_not_allowed=このセクションへのアクセスが許可されていません。 [packages] +title=パッケージ desc=リポジトリ パッケージを管理します。 +empty=パッケージはまだありません。 +empty.documentation=パッケージレジストリの詳細については、 ドキュメント を参照してください。 +empty.repo=パッケージはアップロードしたけども、ここに表示されない? パッケージ設定を開いて、パッケージをこのリポジトリにリンクしてください。 +registry.documentation=%sレジストリの詳細については、 ドキュメント を参照してください。 +filter.type=タイプ +filter.type.all=すべて +filter.no_result=フィルタの結果、空になりました。 +filter.container.tagged=タグあり +filter.container.untagged=タグなし +published_by=%[1]sに%[3]sが配布 +published_by_in=%[1]sに%[3]s%[5]sで配布 +installation=インストール方法 +about=このパッケージについて +requirements=要求事項 +dependencies=依存関係 +keywords=キーワード +details=詳細 +details.author=著作者 +details.project_site=プロジェクトサイト +details.repository_site=リポジトリサイト +details.documentation_site=ドキュメンテーションサイト +details.license=ライセンス +assets=アセット +versions=バージョン +versions.view_all=すべて表示 +dependency.id=ID +dependency.version=バージョン +alpine.registry=あなたの /etc/apk/repositories ファイルにURLを追加して、このレジストリをセットアップします: +alpine.registry.key=インデックス署名の検証のため、レジストリのRSA公開鍵を /etc/apk/keys/ フォルダにダウンロードします: +alpine.registry.info=$branch と $repository は下にあるリストから選んでください。 +alpine.install=パッケージをインストールするには、次のコマンドを実行します: +alpine.repository=リポジトリ情報 +alpine.repository.branches=Branches +alpine.repository.repositories=Repositories +alpine.repository.architectures=Architectures +cargo.registry=Cargo 設定ファイルでこのレジストリをセットアップします。(例 ~/.cargo/config.toml): +cargo.install=Cargo を使用してパッケージをインストールするには、次のコマンドを実行します: +chef.registry=あなたの ~/.chef/config.rb ファイルに、このレジストリをセットアップします: +chef.install=パッケージをインストールするには、次のコマンドを実行します: +composer.registry=あなたの ~/.composer/config.json ファイルに、このレジストリをセットアップします: +composer.install=Composer を使用してパッケージをインストールするには、次のコマンドを実行します: +composer.dependencies=依存関係 +composer.dependencies.development=開発用依存関係 conan.details.repository=リポジトリ +conan.registry=このレジストリをコマンドラインからセットアップします: +conan.install=Conan を使用してパッケージをインストールするには、次のコマンドを実行します: +conda.registry=あなたの .condarc ファイルに、このレジストリを Conda リポジトリとしてセットアップします: +conda.install=Conda を使用してパッケージをインストールするには、次のコマンドを実行します: +container.details.type=イメージタイプ +container.details.platform=プラットフォーム +container.pull=コマンドラインでイメージを取得します: +container.digest=ダイジェスト: +container.multi_arch=OS / アーキテクチャ +container.layers=イメージレイヤー +container.labels=ラベル +container.labels.key=キー +container.labels.value=値 +cran.registry=あなたの Rprofile.site ファイルに、このレジストリをセットアップします: +cran.install=パッケージをインストールするには、次のコマンドを実行します: +debian.registry=このレジストリをコマンドラインからセットアップします: +debian.registry.info=$distribution と $component は下にあるリストから選んでください。 +debian.install=パッケージをインストールするには、次のコマンドを実行します: +debian.repository=リポジトリ情報 +debian.repository.distributions=Distributions +debian.repository.components=Components +debian.repository.architectures=Architectures +generic.download=コマンドラインでパッケージをダウンロードします: +go.install=コマンドラインでパッケージをインストール: +helm.registry=このレジストリをコマンドラインからセットアップします: +helm.install=パッケージをインストールするには、次のコマンドを実行します: +maven.registry=あなたのプロジェクトの pom.xml ファイルに、このレジストリをセットアップします: +maven.install=パッケージを使用するため pom.xml ファイル内の dependencies ブロックに以下を含めます: +maven.install2=コマンドラインで実行します: +maven.download=依存関係をダウンロードするには、コマンドラインでこれを実行します: +nuget.registry=このレジストリをコマンドラインからセットアップします: +nuget.install=NuGet を使用してパッケージをインストールするには、次のコマンドを実行します: +nuget.dependency.framework=ターゲットフレームワーク +npm.registry=あなたのプロジェクトの .npmrc ファイルに、このレジストリをセットアップします: +npm.install=npm を使用してパッケージをインストールするには、次のコマンドを実行します: +npm.install2=または package.json ファイルに追加します: +npm.dependencies=依存関係 +npm.dependencies.development=開発用依存関係 +npm.dependencies.peer=Peer依存関係 +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=Architectures +rubygems.install=gem を使用してパッケージをインストールするには、次のコマンドを実行します: +rubygems.install2=または Gemfile に追加します: +rubygems.dependencies.runtime=実行用依存関係 +rubygems.dependencies.development=開発用依存関係 +rubygems.required.ruby=必要なRubyバージョン +rubygems.required.rubygems=必要なRubyGemバージョン +swift.registry=このレジストリをコマンドラインからセットアップします: +swift.install=あなたの Package.swift ファイルにパッケージを追加します: +swift.install2=そして次のコマンドを実行します: +vagrant.install=Vagrant ボックスを追加するには、次のコマンドを実行します。 +settings.link=このパッケージをリポジトリにリンク +settings.link.description=パッケージをリポジトリにリンクすると、リポジトリのパッケージリストに表示されるようになります。 +settings.link.select=リポジトリを選択 +settings.link.button=リポジトリのリンクを更新 +settings.link.success=リポジトリのリンクが正常に更新されました。 +settings.link.error=リポジトリのリンクの更新に失敗しました。 +settings.delete=パッケージ削除 +settings.delete.description=パッケージの削除は恒久的で元に戻すことはできません。 +settings.delete.notice=%s (%s) を削除しようとしています。この操作は元に戻せません。よろしいですか? +settings.delete.success=パッケージを削除しました。 +settings.delete.error=パッケージの削除に失敗しました。 +owner.settings.cargo.title=Cargoレジストリ インデックス +owner.settings.cargo.initialize=インデックスを初期化 +owner.settings.cargo.initialize.description=Cargoレジストリを使用するには、インデックス用の特別なgitリポジトリが必要です。 このオプションを使用するとそのリポジトリを(再)作成し、自動的に構成します。 +owner.settings.cargo.initialize.error=Cargoインデックスの初期化に失敗しました: %v +owner.settings.cargo.initialize.success=Cargoインデックスは正常に作成されました。 +owner.settings.cargo.rebuild=インデックスを再構築 +owner.settings.cargo.rebuild.description=インデックスが格納されているCargoパッケージと同期していない場合は、再構築すると良いでしょう。 +owner.settings.cargo.rebuild.error=Cargoインデックスの再構築に失敗しました: %v +owner.settings.cargo.rebuild.success=Cargoインデックスは正常に再構築されました。 +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 パッケージにつき 1 バージョン owner.settings.cleanuprules.keep.count.n=1 パッケージにつき %d バージョン +owner.settings.cleanuprules.keep.pattern=マッチするバージョンを残す +owner.settings.cleanuprules.keep.pattern.container=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レジストリの認証にはキーペアが必要です。 すでにキーペアを生成していた場合、新しいキーペアを生成すると古いキーペアは破棄されます。 +rpm.repository.multiple_groups = このパッケージは複数のグループで利用できます。 +owner.settings.cargo.rebuild.no_index = 再構築できません、インデックスが初期化されていません。 +npm.dependencies.bundle = バンドルされた依存関係 + +search_in_external_registry = %s で検索 [secrets] secrets=シークレット @@ -3396,8 +3674,68 @@ deletion.failed=シークレットの削除に失敗しました。 management=シークレット管理 [actions] +actions=Actions + unit.desc=Actionsの管理 +status.unknown=不明 +status.waiting=待機中 +status.running=実行中 +status.success=成功 +status.failure=失敗 +status.cancelled=キャンセル +status.skipped=スキップ +status.blocked=ブロックされた + +runners=ランナー +runners.runner_manage_panel=ランナーの管理 +runners.new=新しいランナーを作成 +runners.new_notice=ランナーの開始方法 +runners.status=ステータス +runners.id=ID +runners.name=名称 +runners.owner_type=タイプ +runners.description=説明 +runners.labels=ラベル +runners.last_online=最終オンライン時刻 +runners.runner_title=ランナー +runners.task_list=このランナーの最近のタスク +runners.task_list.no_tasks=タスクはまだありません。 +runners.task_list.run=実行 +runners.task_list.status=ステータス +runners.task_list.repository=リポジトリ +runners.task_list.commit=コミット +runners.task_list.done_at=終了時刻 +runners.edit_runner=ランナーの編集 +runners.update_runner=変更を保存 +runners.update_runner_success=ランナーを更新しました +runners.update_runner_failed=ランナーの更新に失敗しました +runners.delete_runner=このランナーを削除 +runners.delete_runner_success=ランナーを削除しました +runners.delete_runner_failed=ランナーの削除に失敗しました +runners.delete_runner_header=ランナー削除の確認 +runners.delete_runner_notice=このランナーでタスクが実行されている場合、タスクは停止され失敗扱いとなります。 それによりビルドワークフローが途中で終了することになるかもしれません。 +runners.none=利用可能なランナーはありません +runners.status.unspecified=不明 +runners.status.idle=アイドル +runners.status.active=稼働中 +runners.status.offline=オフライン +runners.version=バージョン +runners.reset_registration_token=登録トークンをリセット +runners.reset_registration_token_success=ランナー登録トークンをリセットしました + +runs.all_workflows=すべてのワークフロー +runs.commit=コミット +runs.scheduled=スケジュール済み +runs.pushed_by=pushed by +runs.invalid_workflow_helper=ワークフロー設定ファイルは無効です。あなたの設定ファイルを確認してください: %s +runs.no_matching_online_runner_helper=ラベルに一致するオンラインのランナーが見つかりません: %s +runs.actor=アクター +runs.status=ステータス +runs.actors_no_select=すべてのアクター +runs.status_no_select=すべてのステータス +runs.no_results=一致する結果はありません。 +runs.no_workflows=ワークフローはまだありません。 runs.no_runs=ワークフローはまだ実行されていません。 runs.empty_commit_message=(空のコミットメッセージ) @@ -3409,7 +3747,23 @@ workflow.disabled=ワークフローは無効です。 need_approval_desc=フォークプルリクエストのワークフローを実行するには承認が必要です。 +variables=変数 +variables.management=変数の管理 +variables.creation=変数の追加 +variables.none=変数はまだありません。 +variables.deletion=変数を削除 +variables.deletion.description=変数の削除は恒久的で元に戻すことはできません。 続行しますか? +variables.description=変数は特定のActionsに渡されます。 それ以外で読み出されることはありません。 +variables.edit=変数の編集 +variables.deletion.failed=変数を削除できませんでした。 +variables.deletion.success=変数を削除しました。 +variables.creation.failed=変数を追加できませんでした。 +variables.creation.success=変数 "%s" を追加しました。 +variables.update.failed=変数を更新できませんでした。 +variables.update.success=変数を更新しました。 variables.id_not_exist = idが%dの変数は存在しません。 +runs.workflow = ワークフロー +runs.no_job_without_needs = ワークフローには、依存関係のないジョブが少なくとも 1 つ含まれている必要があります。 workflow.dispatch.run = ワークフローを実行 workflow.dispatch.success = ワークフローの実行が正常にリクエストされました。 workflow.dispatch.trigger_found = このワークフローには workflow_dispatch イベントトリガーがあります。 @@ -3417,9 +3771,13 @@ workflow.dispatch.use_from = ワークフローを使用する workflow.dispatch.input_required = 入力 "%s" に値が必要です。 workflow.dispatch.invalid_input_type = 入力タイプ「%s」が無効です。 workflow.dispatch.warn_input_limit = 最初の %d 個の入力のみを表示します。 +runs.no_job = ワークフローには少なくとも1つのジョブが含まれている必要があります + runs.expire_log_message = ログは古すぎるため消去されています。 -runs.no_workflows.help_no_write_access = Forgejoアクションの詳細については、ドキュメントを参照してください。 + runs.no_workflows.help_write_access = Forgejo アクションの始め方がわからない場合は、ユーザードキュメントのクイック スタートを参照して最初のワークフローを作成し、次に Forgejo ランナーをセットアップしてジョブを実行して下さい。 +runs.no_workflows.help_no_write_access = Forgejoアクションの詳細については、ドキュメントを参照してください。 +variables.not_found = 変数が見つかりませんでした。 [projects] type-1.display_name=個人プロジェクト @@ -3467,8 +3825,18 @@ union = フレーズ一致 [munits.data] +pib = PiB +tib = TiB +eib = EiB +kib = KiB +mib = MiB +gib = GiB +b = B [markup] +filepreview.lines = %[3]s の %[1]d 行目から %[2]d 行目 +filepreview.line = %[2]s の %[1]d 行目 +filepreview.truncated = プレビューは途中から省略されています [repo.permissions] actions.write = 書き込み: 保留中の CI/CD パイプラインを手動でトリガー、再起動、キャンセル、または承認します。 diff --git a/options/locale/locale_ka.ini b/options/locale/locale_ka.ini index d99ab6a667..eb60c243b2 100644 --- a/options/locale/locale_ka.ini +++ b/options/locale/locale_ka.ini @@ -285,6 +285,10 @@ template.webhooks = ვებჰუკები template.topics = თემები template.avatar = ავატარი need_auth = ავტორიზაცია +migrate_items_wiki = ვიკი +migrate_items_labels = ჭდეები +migrate_items_issues = პრობლემები +migrate_items_releases = რელიზები star = ვარსკვლავი unstar = ვარსკვლავის მოხსნა fork = ფორკი @@ -575,6 +579,7 @@ diff.vendored = გარედან შემოტანილია release.edit = ჩასწორება release.type_attachment = მიმაგრებული ფაილი topic.done = დასრულება +migrate_items_milestones = მისაღწევი გეგმები issues.subscribe = გამოწერა issues.review.outdated = ვადაგადაცილებული activity.merged_prs_label = შერწყმულია @@ -641,6 +646,10 @@ users.repos = რეპოები users.created = შეიქმნა users.edit = ჩასწორება users.local = ლოკალური +users.list_status_filter.reset = ჩამოყრა +users.list_status_filter.is_active = აქტიურია +users.list_status_filter.is_admin = ადმინი +users.list_status_filter.is_restricted = შეზღუდული emails.primary = ძირითადი emails.activated = გააქტიურებულია emails.filter_sort.email = ელფოსტა @@ -691,12 +700,17 @@ monitor.stacktrace = სტეკის დატრეისება monitor.desc = აღწერა monitor.last_execution_result = შედეგი monitor.process.children = შვილები +monitor.queues = რიგები +monitor.queue.name = სახელი +monitor.queue.type = ტიპი notices.operations = ოპერაციები notices.type = ტიპი notices.type_1 = რეპოზიტორია notices.type_2 = ამოცანა notices.desc = აღწერა notices.op = ოპ. +users.list_status_filter.menu_text = ფილტრი +users.list_status_filter.not_active = არააქტიურია config.send_test_mail_submit = გაგზავნა packages.creator = შემქმნელი dashboard.operation_switch = გადართვა @@ -715,20 +729,104 @@ raw_minutes = წუთი future = მომავალში [munits.data] +b = ბ +kib = კიბ +mib = მიბ +gib = გიბ +tib = ტიბ +pib = პიბ +eib = ეიბ [notification] +notifications = გაფრთხილებები +read = წაკითხვა +subscriptions = გამოწერები +watching = უყურებთ +unread = წაკითხულობის გაუქმება [units] unit = ერთეული [packages] +title = პაკეტები +filter.type = ტიპი +filter.type.all = ყველა +filter.container.tagged = ჭდით +filter.container.untagged = ჭდემოხსნილი +installation = დაყენება +requirements = მოთხოვნები +dependencies = დამოკიდებულებები +keywords = საკვანძო სიტყვები +details = დეტალები +details.author = ავტორი +details.license = ლიცენზია +assets = ობიექტები +versions = ვერსიები +dependency.id = ID +dependency.version = ვერსია +alpine.repository.branches = ბრენჩები +alpine.repository.repositories = რეპოზიტორიები +alpine.repository.architectures = არქიტექტურები +arch.version.provides = მოგაწვდით +arch.version.depends = დამოკიდებულია +arch.version.conflicts = კონფლიქტშია +arch.version.replaces = ანაცვლებს +arch.version.backup = მარქაფი conan.details.repository = რეპოზიტორია +container.images.title = ასლის ფაილები +container.details.platform = პლატფორმა +container.digest = დაიჯესტი +container.labels = ჭდეები +container.labels.key = გასაღები +debian.repository.distributions = დისტრიბუტივები +debian.repository.components = კომპონენტები +debian.repository.architectures = არქიტექტურები +npm.dependencies = დამოკიდებულებები +npm.details.tag = ჭდე +rpm.repository.architectures = არქიტექტურები +alt.repository.architectures = არქიტექტურები owner.settings.cleanuprules.enabled = ჩართულია +arch.version.description = აღწერა +arch.version.groups = ჯგუფი +composer.dependencies = დამოკიდებულებები +container.labels.value = მნიშვნელობა [secrets] secrets = საიდუმლოები [actions] +actions = ქმედებები +status.unknown = უცნობი +status.waiting = ველოდები +status.running = მიმდინარეობს შესრულება +status.success = წარმატება +status.failure = ჩავარდნა +status.cancelled = გაუქმებულია +status.skipped = გამოტოვებულია +status.blocked = დაბლოკილია +runners = გამსვებები +runners.status = სტატუსი +runners.id = ID +runners.owner_type = ტიპი +runners.description = აღწერა +runners.labels = ჭდეები +runners.runner_title = გამშვები +runners.task_list.run = გაშვება +runners.task_list.status = სტატუსი +runners.task_list.repository = რეპოზიტორია +runners.task_list.commit = კომიტი +runners.status.unspecified = უცნობი +runners.status.idle = უქმე +runners.status.active = აქტიურია +runners.status.offline = ქსელგარეშე +runners.version = ვერსია +runs.commit = კომიტი +runs.scheduled = დაგეგმილია +runs.workflow = შრომის პროცესი +runs.actor = ავტორი +runs.status = სტატუსი +variables = ცვლადები +runners.name = სახელი [git.filemode] directory = საქაღალდე diff --git a/options/locale/locale_kab.ini b/options/locale/locale_kab.ini index 71d7d508a1..75500736d1 100644 --- a/options/locale/locale_kab.ini +++ b/options/locale/locale_kab.ini @@ -63,19 +63,22 @@ sign_out = Senser tuqqna link_account = Qqen amiḍan email = Tansa imayl access_token = Ajuṭu n unekcum -copy_url = Nɣel tansa URL -copy_path = Nɣel abrid -copy_content = Nɣel agbur -go_back = Uɣal ɣee deffir -powered_by = S lmendad n %s + sign_in_with_provider = Qqen s %s +powered_by = S lmendad n %s user_profile_and_more = Amaɣnu akken iɣewwaren… signed_in_as = D uqqin amzun d enable_javascript = Asmel-a Web yesra JavaScript. return_to_forgejo = Tuɣalin ɣer Forgejo twofa = Asesteb s snat n tarrayin -filter.not_template = Mačči d timudmiwin +copy_url = Nɣel tansa URL +copy_path = Nɣel abrid +copy_content = Nɣel agbur +go_back = Uɣal ɣee deffir + admin_panel = Tadbelt n wesmel +filter.not_template = Mačči d timudmiwin + add_all = Rnu-ten akk remove_all = Kkes-iten akk @@ -92,10 +95,12 @@ following.title.one = Yeṭṭafaṛ following.title.few = Yeṭṭafaṛ unfollow = Ur ṭṭafar ara overview = Tamuɣli s umata + settings = Iɣewwaren n useqdac + change_avatar = Beddel avaṭar-ik… -block_user = Sewḥel aseqdac activity = Armud azayez +block_user = Sewḥel aseqdac [org] code = Tanglat @@ -106,21 +111,24 @@ settings.website = Asmel web settings.options = Tuddsa members.remove = Kkes teams.settings = Iɣewwaren + +create_new_team = Agraw amaynut +create_team = Snulfu-d yiwen n ugraw +team_name = Isem n ugraw settings.visibility.public = Azayaz settings.visibility.private_shortname = Uslig members.private = Yettwaffer members.owner = Bab-is members.member = Aɛeggal -create_new_team = Agraw amaynut -create_team = Snulfu-d yiwen n ugraw -team_name = Isem n ugraw + settings = Iɣewwaren -members.private_helper = Err-it ad yettban + teams = Tirebbuyaɛ lower_members = Iɛeggalen team_permission_desc = Tasiregt -teams.delete_team = Kkes tarbaɛt +members.private_helper = Err-it ad yettban teams.update_settings = Leqqem iɣewwaren +teams.delete_team = Kkes tarbaɛt teams.delete_team_title = Kkes tarbaɛt [repo] @@ -147,6 +155,8 @@ desc.sha256 = SHA256 template.topics = Isental template.avatar = Avaṭar sync_fork.button = Mtawi +migrate_items_wiki = Wiki +migrate_items_labels = Tibzimin tags = Tibzimin project = Isenfaren packages = Ikemmusen @@ -174,6 +184,7 @@ projects.title = Azwel projects.type.none = Ula yiwen projects.template.desc = Tamudemt mirror_sync = yemtawa +migrate_items_issues = Uguren star = Rnu-yas itri branches = Tiṣeḍwa issues = Uguren @@ -291,17 +302,12 @@ activity.period.monthly = 1 n wayyur activity.period.quarterly = 3 n wayyuren activity.period.semiyearly = 6 n wayyuren activity.period.yearly = 1 n useggas -settings.collaboration.admin = Anedbal -settings.collaboration.write = Tira -settings.collaboration.read = Taɣuṛi -settings.collaboration.owner = Bab-is -settings.event_create = Snulfu-d -settings.event_delete = Tukksa -release.draft = Arewway + +new_advanced = Iɣewwaren leqqayen +template_select = Fren yiwet n tmudemt download_zip = Sader-d ZIP download_tar = Sader-d TAR.GZ download_bundle = Sader-d BUNDLE -new_advanced = Iɣewwaren leqqayen editor.add_tmpl = Rnu "<%s>" editor.add = Rnu %s projects.edit = Ẓreg asenfar @@ -317,33 +323,31 @@ activity.title.user_1 = %d n useqdac activity.title.user_n = %d n iseqdacen activity.git_stats_file_1 = %d n ufaylu activity.git_stats_file_n = %d n ifuyla +settings.collaboration.admin = Anedbal +settings.collaboration.write = Tira +settings.collaboration.read = Taɣuṛi +settings.collaboration.owner = Bab-is +settings.sync_mirror = Mtawi tura settings.update_settings = Sekles iɣewwaren settings.advanced_settings = Iɣewwaren leqqayen -settings.sync_mirror = Mtawi tura settings.admin_settings = Iɣewwaren n unedbal settings.add_team = Rnu yiwen n ugraw settings.slack_icon_url = URL n tignit settings.discord_icon_url = URL n tignit +settings.event_create = Snulfu-d +settings.event_delete = Tukksa settings.graphql_url = URL n GraphQL settings.web_hook_name_msteams = Microsoft Teams settings.web_hook_name_larksuite_only = Lark Suite settings.packagist_username = Isem n useqdac n Packagist -template_select = Fren yiwet n tmudemt -issues.open_title = Yeldin -issues.closed_title = Yemedlen -settings.slack_token = Tiddest -settings.slack_domain = Taɣult -settings.slack_channel = Abadu -settings.lfs = LFS -diff.git-notes = Tizmilin -diff.comment.reply = Efk-d tiririt -diff.review.comment = Awennit +release.draft = Arewway + new_from_template = Seqdec tamudemt -template.items = Iferdisen n tmudemt -use_template = Fren tamudemt-a size_format = %[1]s: %[2]s, %[3]s: %[4]s +use_template = Fren tamudemt-a open_with_editor = Ldi s %s license_helper = Fren afaylu n turagt +template.items = Iferdisen n tmudemt download_file = Sader-d afaylu editor.name_your_file = Efk-as isem i ufaylu-k… editor.delete = Kkes %s @@ -356,35 +360,45 @@ projects.new = Asenfar amaynut projects.deletion = Kkes asenfar projects.column.set_default = Sbadu-t d amezwer projects.card_type.text_only = Aḍris kan -issues.new.open_projects = Isenfaren yeldin issues.filter_no_results = Ulac igmaḍ +issues.new.open_projects = Isenfaren yeldin +issues.open_title = Yeldin +issues.closed_title = Yemedlen issues.num_comments_1 = %d n uwennit issues.num_comments = %d n iwenniten issues.context.copy_link = Nɣel aseɣwen -issues.label_deletion = Kkes tabzimt -issues.label_modify = Ẓreg tabzimt issues.label_count = %d n tebzimin -pulls.status_checks_details = Talqayt +issues.label_modify = Ẓreg tabzimt +issues.label_deletion = Kkes tabzimt pulls.merge_conflict_summary = Izen n tuccḍa +pulls.status_checks_details = Talqayt wiki.welcome = Ansuf ar uwiki. activity.closed_issue_label = Yemedlen activity.unresolved_conv_label = Ldi settings = Iɣewwaren -settings.federation_settings = Iɣewwaren n tfidiralit settings.basic_settings = Iɣewwaren n taffa -settings.tracker_issue_style.alphanumeric = Agmumḍin +settings.federation_settings = Iɣewwaren n tfidiralit settings.tracker_issue_style.numeric = Umḍin +settings.tracker_issue_style.alphanumeric = Agmumḍin settings.teams = Igrawen +settings.slack_token = Tiddest +settings.slack_domain = Taɣult +settings.slack_channel = Abadu +settings.lfs = LFS +diff.git-notes = Tizmilin +diff.comment.reply = Efk-d tiririt +diff.review.comment = Awennit + visibility_helper = Err akufi d uslig issues.filter_poster_no_select = Akk imeskaren activity.git_stats_author_1 = %d n umeskar activity.git_stats_author_n = %d n imeskaren settings.packagist_api_token = Tiddest API +diff.git-notes.add = Rnu tazmilt +diff.git-notes.remove-header = Kkes tazmilt diff.view_file = Wali afaylu diff.show_more = Wali ugar diff.comment.add_review_comment = Rnu awennit -diff.git-notes.add = Rnu tazmilt -diff.git-notes.remove-header = Kkes tazmilt [install] admin_password = Awal n uɛeddi @@ -396,12 +410,14 @@ db_schema = Azenziɣ ssl_mode = SSL path = Abrid db_name = Isem n taffa n yisefka + general_title = Iɣewwaren Imuta app_name = Azwel n tummant -admin_email = Tansa imayl -install_btn_confirm = Sebded Forgejo admin_name = Isem n useqdac n unedbal confirm_password = Serggeg awal n uɛeddi +admin_email = Tansa imayl +install_btn_confirm = Sebded Forgejo + domain = Taɣult n aqeddac app_url = URL n taffa email_title = Iɣewwaren n imayl @@ -424,6 +440,7 @@ auths.name = Isem config.db_name = Isem config.mailer_name = Isem monitor.name = Isem +monitor.queue.name = Isem repositories = Ikufan dashboard = Tafelwit n usenqed organizations = Tuddsiwin @@ -433,6 +450,7 @@ config_settings = Iɣewwaren dashboard.statistic = Agzul users.2fa = 2FA config.db_ssl_mode = SSL + first_page = Amezwaru last_page = Aneggaru users.edit = Ẓreg @@ -441,51 +459,58 @@ orgs.teams = Igrawen orgs.members = Imttekkiyen repos.owner = Bab-is config.mailer_user = Aseqdac + +users.full_name = Isem ummid users.admin = Anedbal +users.last_login = Tuqqna taneggarut +users.auth_source = Tiɣbula n usesteb +users.is_activated = Amiḍan-a d urmid +users.list_status_filter.is_admin = Anedbal repos.size = Tiddi +repos.lfs_size = Tiddi LFS packages.owner = Bab-is packages.creator = Asaraw packages.version = Lqem packages.type = Tawsit packages.size = Tiddi -repos.lfs_size = Tiddi LFS auths.search_page_size = Tiddi n usebter -users.full_name = Isem ummid -users.last_login = Tuqqna taneggarut -users.auth_source = Tiɣbula n usesteb -users.is_activated = Amiḍan-a d urmid auths.tips.gmail_settings = Iɣewwaren n Gmail: -config.ssh_config = Tawila n SSH -config.domain = Taɣult n uqeddac config.server_config = Tawila n uqeddac config.app_name = Azwel n wesmel config.app_ver = Lqem n Forgejo config.app_url = URL n taffa n Forgejo +config.domain = Taɣult n uqeddac +config.ssh_config = Tawila n SSH config.mailer_smtp_port = Tawwurt n SMTP config.mailer_use_sendmail = Seqdec Sendmail config.https_only = HTTPS kan config.git_config = Tawila n Git -auths.oauth2_profileURL = URL n umaɣnu +monitor.queue.settings.remove_all_items = Kkes-iten akk + users.update_profile = Leqqem amiḍan n useqdac +users.list_status_filter.is_active = D urmid +users.list_status_filter.not_active = D arurmid auths.type = Anaw auths.enabled = D urmid auths.host = Asenneftaɣ auths.port = Tawwurt +auths.oauth2_profileURL = URL n umaɣnu config.ssh_port = Tawwurt config.lfs_enabled = D urmid config.db_type = Anaw config.db_host = Asenneftaɣ config.db_schema = Azenziɣ config.db_path = Abrid +config.enable_captcha = Sermed CAPTCHA config.mailer_enabled = D urmid config.mailer_protocol = Aneggaf monitor.stats = Tiddadanin -config.enable_captcha = Sermed CAPTCHA notices.select_all = Fren-iten akk [search] code_kind = Nadi tangalt… search = Nadi… + repo_kind = Nadi deg ikufiyen… user_kind = Nadi iseqdacen… org_kind = Nadi tirmisin… @@ -493,6 +518,7 @@ team_kind = Nadi igrawen… package_kind = Nadi ikemmusen… project_kind = Nadi isenfaren… branch_kind = Nadi tiseḍwa… + type_tooltip = Tawsit n unadi union = Tadukli @@ -502,10 +528,12 @@ release.download.zip = Tangalt taɣbalut (ZIP) release.note = Tazmilt: release.downloads = Isidar: repo.transfer.to_you = kečč·kemm + hi_user_x = Azul a %s, admin.new_user.user_info = Talɣut ɣef useqdac -release.title = Azwel: %s register_notify = Ansuf ar %s +release.title = Azwel: %s + view_it_on = Wali-t ɣef %s activate_account = Ttxil, rmed amiḍan-ik issue.in_tree_path = Deg %s : @@ -520,7 +548,9 @@ table_modal.placeholder.header = Aqeṛṛu table_modal.label.rows = Izirigen buttons.new_table.tooltip = Rnu tafelwit table_modal.header = Rnu tafelwit + buttons.link.tooltip = Rnu yiwen n useɣwen + link_modal.header = Rnu yiwen n useɣwen [explore] @@ -528,6 +558,7 @@ code = Tanglat repos = Ikufan users = Iseqdacen organizations = Tuddsiwin + go_to = Ddu ɣer [form] @@ -542,7 +573,9 @@ RepoName = Isem n ukufi TeamName = Isem n terbaɛt Email = Tansa imayl Retype = Sentem awal n uɛeddi + FullName = Isem ummid + AdminEmail = Imayl n unedbal To = Isem n tseṭṭa AccessToken = Tiddest n wadduf @@ -561,28 +594,32 @@ more = Ugar [startpage] lightweight = D afessas license = Aɣbalu yeldin + install = Fessus i usebded [home] my_repos = Ikufan show_private = Uslig my_orgs = Tuddsiwin + uname_holder = Isem n useqdac neɣ tansa imayl [auth] openid_connect_submit = Tuqqna verify = Senqed login_userpass = Qqen + create_new_account = Snulfu-d amiḍan forgot_password_title = Tettuḍ awal n uɛeddi forgot_password = Tettuḍ awal n uɛeddi? sign_up_button = Asnulfu n umiḍan. reset_password = Tiririt n umiḍan -openid_register_title = Snulfu-d amiḍan amaynut -sign_in_openid = Kemmel s OpenID + active_your_account = Sermed amiḍan-ik account_activated = Amiḍan-a yettwarmed +openid_register_title = Snulfu-d amiḍan amaynut back_to_sign_in = Tuɣalin ar tuqqna +sign_in_openid = Kemmel s OpenID [modal] yes = Ih @@ -640,6 +677,7 @@ quota.sizes.assets.all = Igburen delete_email = Kkes update_language = Beddel tutlayt language.title = Tutlayt tamezwarut + blocked_users = Imiḍanen yettusḥebsen old_password = Awal n uɛeddi amiran new_password = Awal n uɛeddi amaynut @@ -647,13 +685,7 @@ manage_themes = Asentel amezwer manage_openid = Tansiwin OpenID add_key = Rnu yiwet n tsarut quota.sizes.git.lfs = Git LFS -add_new_email = Rnu tansa imayl -add_email = Rnu tansa imayl -manage_ssh_keys = Sefrek tisura SSH -email_deletion = Kkes tansa imayl -openid_deletion = Kkes tansa OpenID -webauthn_register_key = Rnu yiwet n tsarut n tɣellist -webauthn_delete_key = Kkes tasarut-nni n tɣellist + ssh_gpg_keys = Tisura SSH / GPG profile_desc = Fell-ak·am full_name = Isem ummid @@ -668,14 +700,19 @@ update_password = Leqqem awal n uɛeddi retype_new_password = Serggeg tikkelt-nniḍen awal n uɛeddi amaynut password_incorrect = Awal-a n uɛeddi amiran d arameɣtu. manage_emails = Sefrek tansiwin n imayl +activated = D urmid primary_email = Err-itt d tansa-k tamezwarut +email_deletion = Kkes tansa imayl theme_update_success = Yettuleqqem usentel-ik. theme_update_error = Asentel yettufernen ulac-it. -activated = D urmid +openid_deletion = Kkes tansa OpenID +add_new_email = Rnu tansa imayl +add_email = Rnu tansa imayl +manage_ssh_keys = Sefrek tisura SSH manage_gpg_keys = Sefrek tisura GPG add_new_principal = Rnu agejdan -key_name = Isem n tsarut key_id = Asulay ID n tsarut +key_name = Isem n tsarut ssh_key_deletion = Kkes tasarut SSH gpg_key_deletion = Kkes tasarut GPG generate_new_token = Sarew-d tiddest tamaynut @@ -683,45 +720,71 @@ token_name = Isem n tiddest generate_token = Sarew-d tiddest permissions_public_only = Azayez kan oauth2_application_name = Isem n usnas +webauthn_register_key = Rnu yiwet n tsarut n tɣellist +webauthn_delete_key = Kkes tasarut-nni n tɣellist delete_account = Kkes amiḍan-ik quota.sizes.git.all = Agbur deg Git + public_profile = Amaɣnu azayaz -show_openid = Sken-it-id ɣef umaɣnu -hide_openid = Ffer-it s ufella umaɣnu keep_activity_private = Ffer armud s ufella usebter n umaɣnu keep_email_private = Ffer tansa n yimayl -quota.sizes.repos.private = Ikufan usligen +show_openid = Sken-it-id ɣef umaɣnu +hide_openid = Ffer-it s ufella umaɣnu create_oauth2_application_button = Snulfu-d asnas oauth2_client_id = Asulay ID n wemsaɣ +quota.sizes.repos.private = Ikufan usligen [packages] +arch.version.description = Aglam +alpine.repository.branches = Tiṣeḍwa +alpine.repository.repositories = Ikufan +settings.link.select = Fren akufi +alpine.repository.architectures = Tisegda +debian.repository.architectures = Tisegda +rpm.repository.architectures = Tisegda +alt.repository.architectures = Tisegda +container.images.title = Tugniwin +container.details.platform = Tiɣerɣert + +installation = Asebded +details.project_site = Asmel Web n usenfar +details.repository_site = Asmel Web n ukufi +details.documentation_site = Asmel Web n tsemlit +versions.view_all = Wali-ten akk [actions] +runners.description = Aglam +runners.name = Isem +runners.task_list.repository = Akufi [projects] type-2.display_name = Asenfar n ukufi [error] network_error = Tuccḍa deg uẓeṭṭa + occurred = Tella-d tuccḍa -[tool] -days = %d n wussan -weeks = %d n imalasen -months = %d n wayyuren -years = %d n iseggasen -1mon = 1 n wayyur -1y = 1 n useggas -1d = 1 n wass -now = tura -raw_seconds = tasinin -raw_minutes = tesdatin - -[dropzone] - [filter] string.asc = A - Z string.desc = Z - A +[tool] +1mon = 1 n wayyur +1y = 1 n useggas +days = %d n wussan +weeks = %d n imalasen +months = %d n wayyuren +years = %d n iseggasen + +1d = 1 n wass + +now = tura +raw_seconds = tasinin +raw_minutes = tesdatin + +[dropzone] +remove_file = Kkes afaylu + [git.filemode] directory = Akaram \ No newline at end of file diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index 7d8ddcf5b6..b19e823525 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -88,19 +88,31 @@ toc = 목차 licenses = 라이센스 return_to_forgejo = Forgejo로 돌아가기 access_token = 액세스 토큰 +webauthn_error_unable_to_process = 서버가 귀하의 요청을 처리할 수 없습니다. +webauthn_error_duplicated = 이 요청에는 보안 키가 허용되지 않습니다. 키가 이미 등록되어 있는지 확인하세요. remove_label_str = "%s" 항목 제거 disabled = 비활성화됨 locked = 잠김 filter = 필터 filter.not_fork = 포크가 아님 filter.is_mirror = 미러 +webauthn_press_button = 보안 키의 버튼을 누르세요… toggle_menu = 토글 메뉴 +webauthn_error = 보안 키를 읽을 수 없습니다. +webauthn_unsupported_browser = 현재 귀하의 브라우저는 웹 인증을 지원하지 않습니다. +webauthn_insert_key = 보안 키를 입력하세요 +webauthn_sign_in = 보안 키에 있는 버튼을 누르세요. 만약 보안 키에 버튼이 없다면 다시 입력하세요. +webauthn_use_twofa = 휴대전화 2단계 코드 사용 retry = 다시 시도하기 rerun = 다시 실행하기 rerun_all = 모든 작업을 다시 실행하기 copy = 복사 new_project_column = 새 열 more_items = 더 보기 +webauthn_error_unknown = 알 수 없는 오류가 발생했습니다. 다시 시도해주세요. +webauthn_error_insecure = 웹 인증은 안전한 연결만 지원합니다. HTTP에서 테스트할 경우, "localhost" 또는 "127.0.0.1" 오리진을 사용할 수 있습니다. +webauthn_error_empty = 키의 이름을 설정해야 합니다. +webauthn_error_timeout = 키를 읽기 전에 시간이 초과되었습니다. 페이지를 새로 고치고 다시 시도해주세요. copy_generic = 클립보드에 복사 copy_url = URL 복사 copy_hash = 해시 복사 @@ -170,7 +182,7 @@ buttons.italic.tooltip = 기울어진 텍스트 추가 (Ctrl+I / ⌘I) buttons.heading.tooltip = 헤딩 추가 buttons.bold.tooltip = 두꺼운 텍스트 추가 (Ctrl+B / ⌘B) buttons.code.tooltip = 코드 추가 -buttons.link.tooltip = 링크 추가 (Ctrl+K / ⌘K) +buttons.link.tooltip = 링크 추가 buttons.quote.tooltip = 인용구 추가 buttons.list.unordered.tooltip = 불릿 리스트 추가 buttons.ref.tooltip = 이슈 또는 풀 리퀘스트 참조 @@ -186,6 +198,7 @@ table_modal.placeholder.header = 헤더 table_modal.placeholder.content = 내용 table_modal.label.rows = 행 table_modal.label.columns = 열 + buttons.indent.tooltip = 항목들을 한 단계 들여쓰기 buttons.unindent.tooltip = 항목들을 한 단계 내어쓰기 link_modal.header = 링크 추가 @@ -324,6 +337,7 @@ secret_key_failed = 비밀 키 생성 실패: %v env_config_keys = 환경 설정 invalid_password_algorithm = 올바르지 않은 암호 해시 알고리즘 invalid_db_table = 데이터베이스 테이블 "%s"이(가) 올바르지 않습니다: %v + domain_helper = 서버의 도메인 혹은 호스트 주소. require_sign_in_view.description = 콘텐츠 접근을 로그인한 사용자에게만 허용합니다. 손님은 인증 페이지를 통해서만 방문할 수 있습니다. config_location_hint = 이 구성 옵션은 다음 위치에 저장됩니다: @@ -344,6 +358,7 @@ show_private=비공개 issues.in_your_repos=당신의 저장소에 feed_of = "%s"의 피드 filter = 다른 필터 + filter_by_team_repositories = 팀 저장소로 필터링 show_archived = 보관됨 show_both_archived_unarchived = 보관된 항목과 보관되지 않은 항목 모두 표시 @@ -358,7 +373,9 @@ repos=저장소 users=사용자 organizations=조직 code=코드 + go_to = 이동 + code_last_indexed_at = 마지막으로 %s에 인덱싱 됨 relevant_repositories_tooltip = 포크된 저장소이거나 주제, 아이콘, 설명이 없는 저장소는 숨겨집니다. relevant_repositories = 관련 저장소만 표시됩니다, 필터링되지 않은 결과 보기. @@ -407,6 +424,7 @@ authorize_application=애플리케이션 승인 authorize_redirect_notice=애플리케이션을 승인하면 %s (으)로 리다이렉트됩니다. authorize_application_created_by=이 애플리케이션은 %s (이)가 만들었습니다. authorization_failed=인증 실패 + manual_activation_only = 활성화를 완료하려면 사이트 관리자에게 문의하십시오. remember_me = 이 장치 기억하기 hint_login = 이미 계정이 있으신가요? 지금 로그인하세요! @@ -740,6 +758,11 @@ template.avatar=아바타 +migrate_items_wiki=위키 +migrate_items_milestones=마일스톤 +migrate_items_issues=이슈 +migrate_items_pullrequests=풀 리퀘스트 +migrate_items_releases=릴리즈 migrate_repo=저장소 마이그레이션 migrate.clone_address=URL로 부터 마이그레이트 / 클론 migrate.clone_local_path=또는 로컬 서버의 경로 @@ -1459,6 +1482,35 @@ dashboard.operation_switch=스위치 dashboard.operation_run=실행 dashboard.git_gc_repos=모든 저장소 가비지 콜렉트 dashboard.sync_external_users=외부 사용자 데이터 동기화 +dashboard.server_uptime=서버를 켠 시간 +dashboard.current_goroutine=현재 Go루틴 +dashboard.current_memory_usage=현재 메모리 사용율 +dashboard.total_memory_allocated=전체 할당 메모리 +dashboard.memory_obtained=메모리 확보 +dashboard.pointer_lookup_times=포인터 조회 시간 +dashboard.memory_allocate_times=사용 메모리 +dashboard.memory_free_times=가용 메모리 +dashboard.current_heap_usage=현재 힙 사용현황 +dashboard.heap_memory_obtained=힙 메모리 확보 +dashboard.heap_memory_idle=힙 메모리 유휴 +dashboard.heap_memory_in_use=힙 메모리 사용중 +dashboard.heap_memory_released=힙 메모리 풀림 +dashboard.heap_objects=힙 객체 +dashboard.bootstrap_stack_usage=부트스트랩 스택 사용현황 +dashboard.stack_memory_obtained=스택 메모리 확보 +dashboard.mspan_structures_usage=MSpan 구조 사용현황 +dashboard.mspan_structures_obtained=MSpan 구조 확보 +dashboard.mcache_structures_usage=MCache 구조 사용현황 +dashboard.mcache_structures_obtained=MCache 구조 확보 +dashboard.profiling_bucket_hash_table_obtained=버켓 해시 테이블 확보 프로파일링 +dashboard.gc_metadata_obtained=가비지 콜렉션 메타 데이터 확보 +dashboard.other_system_allocation_obtained=기타 시스템 할당 확보 +dashboard.next_gc_recycle=다음 가비지 콜렉션 순환 +dashboard.last_gc_time=마지막 가비지 콜렉션 시간 +dashboard.total_gc_pause=모든 가비지 콜렉션 중지 +dashboard.last_gc_pause=마지막 가비지 콜렉션 중지 +dashboard.gc_times=가비지 콜렉션 시간 + users.user_manage_panel=사용자 계정 관리 users.new_account=사용자 계정 생성 users.name=사용자명 @@ -1487,6 +1539,9 @@ users.allow_create_organization=조직 생성 허용 users.update_profile=사용자 계정 갱신 users.delete_account=사용자 계정 삭제 users.deletion_success=사용자 계정이 삭제되었습니다. +users.list_status_filter.is_active=사용 +users.list_status_filter.is_admin=관리자 + emails.activated=활성화됨 orgs.org_manage_panel=조직 관리 @@ -1681,6 +1736,10 @@ monitor.desc=설명 monitor.start=시작 시간 monitor.execute_time=실행 시간 +monitor.queue.name=이름 +monitor.queue.type=유형 +monitor.queue.settings.submit=설정 업데이트 + notices.system_notice_list=시스템 공지 notices.view_detail_header=알림 세부정보 보기 notices.select_all=모두 선택 @@ -1734,22 +1793,60 @@ raw_seconds=초 raw_minutes=분 [dropzone] +invalid_input_type=이 형식의 파일을 업로드할 수 없습니다. +file_too_big=파일 크기({{filesize}} MB) 가 최대 크기({{maxFilesize}} MB) 를 초과합니다. +remove_file=파일 제거 [notification] +notifications=알림 +unread=읽지 않음 +read=읽음 +no_unread=읽지 않은 알림이 없습니다. +no_read=알림이 없습니다. +pin=알림 고정 +mark_as_read=읽음으로 표시 +mark_as_unread=읽지 않음으로 표시 +mark_all_as_read=모두 읽음으로 표시 +subscriptions = 구독한 알림 +no_subscriptions = 알림이 없음 +watching = 주시중 [gpg] +error.extract_sign=서명 추출에 실패 +error.generate_hash=커밋의 해시 생성에 실패 +error.not_signed_commit=서명되지 않은 커밋 [units] error.no_unit_allowed_repo=이 저장소의 어떤 섹션에도 접근할 수 없습니다. error.unit_not_allowed=이 저장소 섹션에 접근할 수 없습니다. [packages] +filter.type=유형 +alpine.repository.branches=브랜치 +alpine.repository.repositories=저장소 conan.details.repository=저장소 owner.settings.cleanuprules.enabled=활성화됨 +nuget.dependency.framework = 타겟 프레임워크 +maven.download = 종속성을 다운로드하려면 명령줄을 통해 실행하세요: +dependency.id = ID +dependency.version = 버전 +details.author = 작성자 [secrets] [actions] +runners.name=이름 +runners.owner_type=유형 +runners.description=설명 +runners.task_list.run=실행 +runners.task_list.repository=저장소 +runners.task_list.commit=커밋 +runners.status.active=사용 + +runs.commit=커밋 + + + [projects] diff --git a/options/locale/locale_kw.ini b/options/locale/locale_kw.ini deleted file mode 100644 index ac8dd8a41a..0000000000 --- a/options/locale/locale_kw.ini +++ /dev/null @@ -1,980 +0,0 @@ -[common] -home = Tre -dashboard = Skostel -explore = Trovya -help = Gweres -logo = Logo -sign_in = Omgelmi -sign_in_with_provider = Omgelmi gans %s -sign_in_or = po -sign_out = Omdhigelmi -sign_up = Kovskrifa -link_account = Keskelmi akont -register = Kovskrifa -version = Versyon -powered_by = Gallosegys gans %s -page = Folen -template = Ensampel -notifications = Gwarnyansow -create_new = Gwruthyl… -user_profile_and_more = Profil ha settyansow… -signed_in_as = Omgelmys avel -enable_javascript = An wiasva ma a rekwir JavaScript. -toc = Mosen a Synsas -licenses = Leshyansow -return_to_forgejo = Dehweles dhe Forgejo -toggle_menu = Dewis rol -more_items = Moy taklow -username = Hanow Devnydhyer -email = Trigva ebost -password = Ger-tremena -access_token = Tokyn hedhas -re_type = Afydhya ger-tremena -captcha = CAPTCHA -twofa = Gwirheans dewkamm -twofa_scratch = Kod kravas dewkamm -passcode = Kod-tremena -repository = Gwithva -new_fork = Forgh gwithva nowydh -new_project_column = Koloven nowydh -admin_panel = Menystrans gwiasva -settings = Settyansow -your_profile = Profil -your_starred = Drudh -your_settings = Settyansow -new_repo.title = Gwithva nowydh -new_migrate.title = Divroans nowydh -new_org.title = Kowethyans nowydh -new_repo.link = Gwithva nowydh -new_migrate.link = Divroans nowydh -new_org.link = Kowethyans nowydh -all = Oll -sources = Pennfentynnyow -mirrors = Gwedrow-mires -collaborative = Kesoberus -forks = Fergh -activities = Aktivitys -pull_requests = Profyansow -issues = Kudynnow -milestones = Meyn mildir -ok = Da Lowr -cancel = Hedhi -retry = Assaya Arta -rerun = Daseksekutya -rerun_all = Daseksekutya oll oberennow -save = Sawya -add = Keworra -add_all = Keworra oll -remove = Remova -remove_all = Remova oll -remove_label_str = Remova tra "%s" -edit = Golegi -view = Gweles -test = Prov -enabled = Byw -disabled = Marow -locked = Alhwedhys -copy = Kopia -copy_url = Kopia Gorgevren -copy_hash = Kopia hash -copy_path = Kopia tyller -copy_content = Kopia synsas -copy_branch = Kopia hanow skorr -copy_success = Kopys! -copy_error = Ny yllir kopia -copy_type_unsupported = Ny yllir an eghen restren ma bos kopys -write = Skrifa -preview = Ragweles -loading = Ow Karga… -error = Error -error404 = An folen assayowgh os rag hedhes po nyns yw eksistya, re bos dileys po nyns os reythhes rag gweles. -error413 = Hwi re spenys agas finweth. -go_back = Dehweles -invalid_data = Data anewn: %v -never = Bythkweth -unknown = Ankoth -rss_feed = Strem RSS -pin = Pynna -unpin = Dispynna -artifacts = Eskorransow -confirm_delete_artifact = Esowgh hwi sur ty a vynn dilea an eskorrans "%s" ? -archived = Gwithys yn Kovskrif -concept_system_global = Ollvysel -concept_user_individual = Unnik -concept_code_repository = Gwithva -concept_user_organization = Kowethyans -show_timestamps = Diskwedhes termynyow -show_log_seconds = Diskwedhes eylennow -show_full_screen = Diskwedhes leun-skrin -download_logs = Iskarga kovskrifow -confirm_delete_selected = Afydhyewgh rag dilea oll taklow dewisys? -name = Hanow -value = Synsas -filter = Sidhla -filter.clear = Dilea sidhlow -filter.is_archived = Gwithys yn kovskrif -filter.not_archived = Na gwithys yn kovskrif -filter.is_fork = Fergh -filter.not_fork = Na fergh -filter.is_mirror = Gwedrow-mires -filter.not_mirror = Na gwedrow-mires -filter.is_template = Ensamplys -filter.not_template = Na ensamplys -filter.public = Poblek -filter.private = Privedh -active_stopwatch = Hedheuryer byw -tracked_time_summary = Berrskrif a dermyn helerghys selys war sidhlow a rol kudynnow - -[search] -search = Hwilas… -type_tooltip = Eghen hwithrans -repo_kind = Hwilas gwithvaow… -user_kind = Hwilas devnydhyoryon… -org_kind = Hwilas kowethyansow… -team_kind = Hwilas bagasow… -code_kind = Hwilas kod… -union = Kesunyans -union_tooltip = Synsi sewyansow a omdhesedh neb a'n eryow diberthys gans spas -exact = Kewar -exact_tooltip = Synsi sewyansow a omdhesedh an term hwithrans kewar -regexp = RegExp -regexp_tooltip = Styrya an term hwithrans avel menegyn rewlys -code_search_unavailable = Hwithrans kod nyns yw kavadow a-lemmyn. Kestavewgh an menystrer gwiasva mar pleg. -package_kind = Hwilas fardellow… -project_kind = Hwilas ragdresow… -branch_kind = Hwilas skorrow… -commit_kind = Hwilas gwriansow… -runner_kind = Hwilas eksekutyoryon… -no_results = Nyns eus sewyansow a omdhesedh. -issue_kind = Hwilas kudynnow… -pull_kind = Hwilas profyansow… -keyword_search_unavailable = Ow hwilas gans ger alhwedh nyns yw kavadow a-lemmyn. Kestavewgh an menystrer gwiasva mar pleg. - -[aria] -navbar = Barr navigacyon -footer = Ben folen -footer.software = A-dro dhe'n medhelweyth ma -footer.links = Gorgevrennow - -[heatmap] -number_of_contributions_in_the_last_12_months = %s kevrohow y'n 12 misyow yw passys -contributions_zero = Kevrohow vyth -contributions_format = {contributions} {day} {month} {year} -contributions_one = kevro -contributions_few = kevrohow -less = Le -more = Moy - -[editor] -buttons.heading.tooltip = Keworra pennlinen -buttons.bold.tooltip = Keworra tekst poos (Ctrl+B / ⌘B) -buttons.italic.tooltip = Keworra tekst italek (Ctrl+I / ⌘I) -buttons.quote.tooltip = Devyn tekst -buttons.code.tooltip = Keworra kod -buttons.link.tooltip = Keworra gorgevren (Ctrl+K / ⌘K) -buttons.list.unordered.tooltip = Keworra rol pellen -buttons.list.ordered.tooltip = Keworra rol niverys -buttons.list.task.tooltip = Keworra rol a rannow ober -buttons.mention.tooltip = Kampolla devnydhyer po bagas -buttons.ref.tooltip = Kampolla kudyn po profyans -buttons.switch_to_legacy.tooltip = Devnydhya an janjyell koth yn le -buttons.enable_monospace_font = Bywhe font unspas -buttons.disable_monospace_font = Marowhe font unspas -buttons.indent.tooltip = Neytha taklow gans unn nivel -buttons.unindent.tooltip = Dineytha taklow gans unn nivel -buttons.new_table.tooltip = Keworra mosen -table_modal.header = Keworra mosen -table_modal.placeholder.header = Pennlinen -table_modal.placeholder.content = Synsas -table_modal.label.rows = Rewyow -table_modal.label.columns = Kolovennow -link_modal.header = Keworra gorgevren -link_modal.url = Gorgevren -link_modal.description = Deskrifans -link_modal.paste_reminder = Hynt: Gans URL yn agas astel glypp, hwi a yll dasskrifa y'n janjyell distowgh rag gwruthyl gorgevren. - -[filter] -string.asc = A - Z -string.desc = Z - A - -[error] -occurred = Yth esa error -report_message = Mar krysowgh kudyn Forgejo yw hemma, hwilasewgh mar pleg rag kudynnow war Codeberg po ygeri kudyn nowydh mars yma res. -not_found = Ny yllir kavos an gosten. -network_error = Error rosweyth -server_internal = Error a-bervedh servell - -[startpage] -app_desc = Gonis Git dibayn omostys -install = Es rag lea -install_desc = Yn sempel eksekutya an dewek rag agas bynk, danvon gans Docker, po kavos fardellys. -platform = Treus-bynk -platform_desc = Forgejo yw afydhys rag eksekutya war systemow oberyans rydh kepar ha Linux ha FreeBSD, ha pennsernethow CPU dyffrans. Dewisewgh an onan kyrrowgh! -lightweight = Skav -lightweight_desc = Y’s teves Forgejo edhommow ispoyntel isel hag y hyllir eksekutya war Raspberry Pi a bris isel. Difres tredan! -license = Pennfenten Ygor -license_desc = Kavewgh Forgejo! Omjunyewgh ni gans ow kevri rag gwellhe an ragdres ma moy bryntin hogen. Na berthewgh meth rag bos kevriyas! - -[install] -install = Leyans -title = Selyans kynsa -docker_helper = Mars eksekutyowgh Forgejo a-berth yn Docker, redyewgh mar pleg an kowethlyver kyns ow chanjya neb settyansow. -require_db_desc = Forgejo a vynn MySQL, PostgreSQL, SQLite3 po TiDB (protokol MySQL). -db_title = Settyansow sel dherivadow -db_type = Eghen sel dherivadow -host = Ost -user = Hanow Devnydhyer -password = Ger-tremena -db_name = Hanow sel dherivadow -db_schema = Skema -db_schema_helper = Gwitha gwag rag defowt sel dherivadow ("poblek"). -ssl_mode = SSL -path = Tyller -sqlite_helper = Tyller rag an sel dherivadow SQLite3.
Ynworra tyller absolut mars eksekutyowgh Forgejo avel gonis. -reinstall_error = Esowgh owth assaya rag lea yn sel dherivadow Forgejo seulabrys -reinstall_confirm_message = Ow taslea gans sel dherivadow Forgejo a yll kawsya meur a gudynnow. Yn kasys moyha, y kodh dhywgh devnydhya agas "app.ini" seulabrys rag eksekutya Forgejo. Mar esowgh konnyk, afydhyewgh an pyth a sew: -reinstall_confirm_check_1 = An data argelys gans an SECRET_KEY yn app.ini a yll bos kellys: martesen ny vydh devnydhyoryon omgelmi gans 2FA/OPT & gwedrow-mires na oberi yn ta. Gans ow checkya an gist ma afydhyowgh an restren app.ini synsi an SECRET_KEY ewn. -reinstall_confirm_check_2 = Martesen y fydh edhom dhir daskettermynyegi an gwithvaow ha settyansow. Gans ow checkya an gist ma afydhyowgh hwi a wra daskettermynyegi an higennow rag an gwithvaow ha restren authorized_keys dre dhorn. Afydhyowgh hwi a wra surhe settyansow gwithva ha gweder-mires yw ewn. -reinstall_confirm_check_3 = Afydhyowgh pur sur owgh hwi an Forgejo ma yw owth eksekutya gans an tyller ewn app.ini ha sur owgh hwi yma edhom dhywgh daslea. Afydhyowgh mayth aswonowgh an peryllow ma. -err_empty_db_path = Ny yll an tyller sel dherivadow SQLite3 bos gwag. -no_admin_and_disable_registration = Ny yllowgh marowhe omgovskrifans devnydhyer heb ow kwruthyl akont menystrer. -err_empty_admin_password = Ny yll an ger-tremena menystrer bos gwag. -err_empty_admin_email = Ny yll an drigva ebost menystrer bos gwag. -err_admin_name_is_reserved = Anewn yw an hanow devnydhyer menystrer, ragerghys yw an hanow devnydhyer ma -err_admin_name_pattern_not_allowed = Anewn yw an hanow devnydhyer menystrer, an hanow devnydhyer ma a omdhesedh patron ragerghys -err_admin_name_is_invalid = Anewn yw an hanow devnydhyer -general_title = Settyansow ollgemmyn -app_name = Titel gweyth -app_name_helper = Ynworrewgh agas hanow gweyth omma. An hanow na a vydh bos diskwedhys war bub folen. -app_slogan = Lavar gweyth -app_slogan_helper = Ynworrewgh agas lavar gweyth omma. Gwitha gwag rag marowhe. -repo_path = Tyller gwreydhek gwithva -repo_path_helper = Gwithvaow Git pell a vydh bos sawys dhe'n restrenva ma. -lfs_path = Tyller gwreydhek Git LFS -lfs_path_helper = Restrennow helerghys gans Git LFS a vydh bos gwithys y'n restrenva ma. Gwitha gwag rag marowhe. -run_user = Devnydhyer rag eksekutya avel -run_user_helper = An hanow devnydher rag an system oberyans mayth eksekutir Forgejo. Notyewgh: res yw dhodho kavos hedhas dhe'n tyller gwreydhek gwithva. -domain = Arlotteth servell -domain_helper = Arlotteth po trigva ost rag an servell. -ssh_port = Porth servell SSH -ssh_port_helper = Niver porth rag devnydh gans an servell SSH. Gwitha gwag rag marowhe servell SSH. -http_port = Porth goslowes HTTP -http_port_helper = Niver porth rag devnydh gans an servell gwi Forgejo. -app_url = Gorgevren Sel -app_url_helper = Trigva sel rag gorgevrennow HTTP(S) ha gwarnyansow ebost. -log_root_path = Tyller kovadh -log_root_path_helper = Restrennow kovadh a vydh bos skrifys war'n restrenva ma. -optional_title = Settyansow dre dhewis -email_title = Settyansow ebost -smtp_addr = Ost SMTP -smtp_port = Porth SMTP -smtp_from = Danvon ebost avel -smtp_from_invalid = Anewn yw an drigva "Danvon ebost avel" -smtp_from_helper = An drigva ebost ma a vydh bos devnydhys gans Forgejo. Ynworrewgh trigva ebost sempel po devnydhyewgh an furvas "Hanow" . -mailer_user = Hanow devnydhyer SMTP -mailer_password = Ger-tremena SMTP -register_confirm = Rekwirya afydhyans ebost rag kovskrifa -mail_notify = Bywhe gwarnyansow ebost -server_service_title = Settyansow servell ha gonis parti-tressa -offline_mode = Bywhe fordh leel -offline_mode.description = Marowhe rosweythyow synsas parti-tressa ha dyghtya oll asnodhow yn leel. -disable_gravatar = Marowhe Gravatar -disable_gravatar.description = Marowhe denvydh a Ravatar op pennfentynnyow imach parti-tressa aral. Imajys defowt a vydh bos devnydhys rag imajys devnydhyer marna ughkarg aga honan dhe'n weyth. -federated_avatar_lookup = Bywhe imajys keffrysek -federated_avatar_lookup.description = Arhwilas imajys ow tevnydhya Libravatar. -disable_registration = Marowhe omgovskrifans -disable_registration.description = Menystroryon gweyth yn unnik a yll gwruthyl akontys devnydhyer nowydh. Pur gomendys yw rag gwitha kovskrifans marowhes marnas mynnowgh ostya gweyth poblek rag pubonan ha parys owgh hwi rag ardhyghtya mynsow bras a akontys atal. -allow_only_external_registration = Gasa kovskrifans dre wonisyow a-ves yn unnik -allow_only_external_registration.description = Devnydhyoryon a yll gwruthyl akontys nowydh ow tevnydhya gonisyow a-ves selys. -openid_signin = Bywhe omgelmyans OpenID -openid_signin.description = Gasa devnydhyoryon rag omgelmi der OpenID. -openid_signup = Bywhe omgovskrifans OpenID -openid_signup.description = Gasa devnydhyoryon rag gwruthyl akontys der OpenID mar bywhes yw omgovskrifans. -enable_captcha = Bywhe CAPTCHA kovskrifans -enable_captcha.description = Rekwirya devnydhyoryon tremena CAPTCHA rag gwruthyl akontys. -require_sign_in_view = Rekwirya omgelmyans rag gweles synsas gweyth -require_sign_in_view.description = Finwetha hedhas synsas dhe dhevnydhyoryon omgelmys. Ostysi a yll vysytya an folennow omgelmyans yn unnik. -default_keep_email_private = Kudha trigvaow ebost yn defowt -default_keep_email_private.description = Bywhe ow kudha trigvaow ebost rag devnydhyoryon nowydh yn defowt rag may nyns eus an kedhlow ma dyllys wosa kovskrifans. -default_allow_create_organization = Gasa ow kwruthyl kowethyansow yn defowt -default_allow_create_organization.description = Gasa devnydhyoryon nowydh gwruthyl kowethyansow yn defowt. Mar byw yw an dewis ma, res yw dhodho menystrer grontya kummyas rag gwruthyl kowethyansow dhe dhevnydhyoryon nowydh. -default_enable_timetracking = Bywhe hedheuryer yn defowt -default_enable_timetracking.description = Gasa devnydh a'n nas hedheuryer rag gwithvaow nowydh yn defowt. -admin_title = Settyansow akont menystrer -admin_setting.description = Dre dhewis yw ow kwruthyl akont menystrer. An devnydhyer kovskrifys kynsa a wra bos menystrer yn awtomatek. -admin_name = Hanow devnydhyer menystrer -admin_password = Ger-tremena -confirm_password = Afydhya ger-tremena -admin_email = Trigva ebost -config_location_hint = An dhewisyow selyans ma a vydh bos sawys yn: -install_btn_confirm = Lea ForgeJo -test_git_failed = Ny yllir previ arghadow "git": %v -sqlite3_not_available = Ny skoodhya SQLite3 an dyllans Forgejo ma. Iskargewgh mar pleg an dyllans dewek sodhogel dhyworth %s (nyns yw an dyllans "gobuild"). -invalid_db_setting = Anewn yw an settyansow sel dherivadow: %v -invalid_db_table = Anewn yw an vosen sel dherivadow "%s": %v -invalid_repo_path = Anewn yw an tyller gwreydhek gwithva: %v -invalid_app_data_path = Anewn yw an tyller data app: %v -run_user_not_match = Nyns yw an hanow devnydhyer "devnydhyer rag eksekutya avel" an hanow devnydhyer a-lemmyn: %s -> %s -internal_token_failed = Ny yllir dinythi tokyn a-bervedh: %v -save_config_failed = Ny yllir sawya selyans: %v -enable_update_checker_helper_forgejo = Checkya a wra rag dyllansow Forgejo nowydh a dermyn dhe dermyn gans ow checkya kovadh DNS TXT yn release.forgejo.org. -invalid_admin_setting = Anewn yw settyans akont menystrer: %v -invalid_log_root_path = Anewn yw an tyller kovadh: %v -no_reply_address = Arlotteth ebost kudh -no_reply_address_helper = Hanow arlotteth rag devnydhyoryon gans trigva ebost kudh. Rag ensampel, an hanow devnydhyer "jago" a vydh bos kovadhys yn Git avel "jago@noreply.ensampel.org mars an arlotteth ebost kudh yw "noreply.ensampel.org". -password_algorithm = Method hash ger-tremena -invalid_password_algorithm = Method hash ger-tremena anewn -password_algorithm_helper = Settyewgh an method hash ger-tremena. Yma edhommow ha sleyneth dyffrans. An method argon2 yw saw mes a wra devnydhya meur a gov hag a yll bos anwiw rag systemow byghan. -enable_update_checker = Bwyhe checkyell nowedhyans -env_config_keys = Selyans Kerghynnedh -env_config_keys_prompt = An nowejynnow kerghynnedh a sew a vydh bos gweythys dh'gas restren selyans: - -[home] -uname_holder = Hanow devnydhyer po trigva ebost -switch_dashboard_context = Skwychella testen skostel -my_repos = Gwithvaow -my_orgs = Kowethyansow -view_home = Gweles %s -filter = Sidhlow aral -filter_by_team_repositories = Sidhla gans gwithvaow bagas -feed_of = Strem a "%s" -show_archived = Gwithys yn kovskrif -show_both_archived_unarchived = Ow tiskwedhes an dhew yn kovskrif ha na yn kovskrif -show_only_archived = Ow tiskwedhes yn kovskrif yn unnik -show_only_unarchived = Ow tiskwedhes na yn kovskrif yn unnik -show_private = Privedh -show_both_private_public = Ow tiskwedhes an dhew poblek ha privedh -show_only_private = Ow tiskwedhes privedh yn unnik -show_only_public = Ow tiskwedhes poblek yn unnik -issues.in_your_repos = Yn agas gwithvaow - -[explore] -repos = Gwithvaow -users = Devnydhyoryon -organizations = Kowethyansow -go_to = Mos dhe -code = Kod -code_last_indexed_at = Arhwilys kyns %s -relevant_repositories_tooltip = Kudh yw gwithvaow fergh po kavos testen vyth, arwodhik vyth, ha deskrifans vyth. -relevant_repositories = Gwithvaow a vri yw diskwedhys yn unnik, diskwedhes sewyansow ansidhlys. - -[auth] -create_new_account = Kovskrifa akont -disable_register_prompt = Kovskrifans yw marow. Kestavewgh agas menystrer gwiasva mar pleg. -disable_register_mail = Afydhyans ebost rag kovskrifans yw marow. -manual_activation_only = Kestavewgh agas menystrer gwiasva rag kowlwul bywheans. -remember_me = Kofhe an devis ma -forgot_password_title = Ger-tremena ankevys -forgot_password = Ger-tremena ankevys? -hint_login = Eus akont dhywgh seulabrys? Omgelmewgh lemmyn! -hint_register = Eus edhom dhywgh a akont? Kovskrifewgh lemmyn. -sign_up_button = Kovskrifewgh lemmyn. -sign_up_successful = Akont o gwrys yn sewen. Dynnargh! -confirmation_mail_sent_prompt = Yma ebost afydhyans nowydh danvenys dhe %s. Rag kowlwul an argerdh kovskrifans, checkyewgh agas yngist mar pleg ha holyewgh an gorgevren provys a-ji dhe'n nessa %s. Mars an ebost yw anewn, hwi a yll omgelmi, ha govyn ebost afydhyans rag bos danvenys dhe drigva ebost aral. -must_change_password = Nowedhi agas ger-tremena -allow_password_change = Erghi devnydhyer rag chanjya ger-tremena (komendys) -reset_password_mail_sent_prompt = Yma ebost afydhyans danvenys dhe %s. Rag kowlwul an argerdh yaghheans akont, checkyewgh agas yngist mar pleg ha holyewgh an gorgevren provys a-ji dhe'n nessa %s. -active_your_account = Bywhe agas akont -account_activated = Akont yw bywhes -prohibit_login = Akont yw astelys -prohibit_login_desc = Agas akont re beu astelys rag ow tevnydhya gans an weyth. Kestavewgh an menystrer weyth rag daskemeres hedhas. -resent_limit_prompt = Hwi re govynys ebost bywheans seulabrys a-dhiwedhes. Gortewgh 3 mynysennow mar pleg hag ena assayewgh arta. -has_unconfirmed_mail = Yow %s, yma trigva ebost anafydhys dhywgh (%s). Mar nyns eus ebost afydhyans yn agas yngist po yma edhom dhywgh danvon onan nowydh, klyckyewgh war'n boton a-woles mar pleg. -change_unconfirmed_email_summary = Chanjya an drigva ebost devnydhys rag danvon an ebost bywheans. -change_unconfirmed_email = Mar proviowgh an drigva ebost anewn dres kovskrifans, hwi a yll chanjya a-woles, hag yma afydhyans nowydh danvenys dhe'n drigva nowydh yn le. -change_unconfirmed_email_error = Ny yllir chanjya an drigva ebost: %v -resend_mail = Klyckyewgh omma rag dastanvon agas ebost bywheans -send_reset_mail = Danvon ebost yaghheans -reset_password = Yaghheans akont -invalid_code = Agas kod afydhyans yw anewn po re diwedhys. -invalid_code_forgot_password = Agas kod afydhyans yw anewn po re diwedhys. Klyckyewgh omma rag dalleth esedhek nowydh. -invalid_password = An ger-tremena ma ny omdhesedha an ger-tremena devnydhys rag gwruthyl an akont. -reset_password_helper = Daskavos Akont -reset_password_wrong_user = Omgelmys owgh hwi avel %s, mes an gorgevren yaghheans akont yw rag %s -password_too_short = An ger-tremena na yllir bos le hir es %d lytherennow. -non_local_account = Devnydhyoryon anleel na yllir nowedhi aga ger-tremena dres an ynterfas gwi Forgejo. -verify = Gwirhe -unauthorized_credentials = Manylyon devnydhyer yw anewn po re diwedhys. Assayewgh arta agas arghadow po gweles %s rag moy kedhlow -scratch_code = Kod kravas -use_scratch_code = Devnydhya kod kravas -use_onetime_code = Devnydhya kod unweyth -twofa_scratch_used = Hwi re devnydhys agas kod kravas. Hwi re daskevarwodhys dhe'n folen settyansow dewkamm rag may hwi a yll remova agas kovskrifans devis po dinythi kod kravas nowydh. -twofa_passcode_incorrect = Agas kod-tremena yw anewn. Mar kellowgh agas devis, devnydhya agas kod kravas rag omgelmi. -twofa_scratch_token_incorrect = Agas kod kravas yw anewn. -login_userpass = Omgelmi -oauth_signup_tab = Kovskrifa akont nowydh -oauth_signup_title = Kowlwul akont nowydh -oauth_signup_submit = Kowlwul akont -oauth_signin_tab = Keskelmi dhe akont seulabrys -oauth_signin_title = Omgelmi rag reythhe akont keskelmys -oauth_signin_submit = Keskelmi akont -oauth.signin.error = Yth esa error owth argerdhes an govyn reythheans. Mar durya an error ma, kestavewgh an menystrer gwiasva mar pleg. -oauth.signin.error.access_denied = An govyn reythheans o naghys. -oauth.signin.error.temporarily_unavailable = Ny yllir reythhe drefen an ankavadewder a'n servell. Anbarghus yw hemma; assayewgh arta diwettha mar pleg. -openid_connect_submit = Junya -openid_connect_title = Junya dhe akont seulabrys -openid_connect_desc = An URI OpenID dewisys yw ankoth. Kowethya gans akont nowydh omma. -openid_register_title = Gwruthyl akont nowydh -openid_register_desc = An URI OpenID dewisys yw ankoth. Kowethya gans akont nowydh omma. -openid_signin_desc = Ynworrewgh agas URI OpenID. Rag ensampel: alice.openid.example.org po https://openid.example.org/alice. -disable_forgot_password_mail = Yaghheans akont yw marow drefen nyns eus trigva ebost selys. Kestavewgh agas menystrer gwiasva mar pleg. -disable_forgot_password_mail_admin = Yaghheans akont yw kavadow mar selys yw ebost. Selyewgh ebost rag bywhe yaghheans akont mar pleg. -email_domain_blacklisted = Ny yllowgh kovskrifa gans agas trigva ebost. -authorize_application = Reythhe App -authorize_redirect_notice = Hwi a vydh daskevarwodhys dhe %s mar reythhowgh an app ma. -authorize_application_created_by = An app ma o gwrys gans %s. -authorize_application_description = Mar grontyowgh hedhes, y hyll drehedhes ha skrifa dhe oll agas kedhlow akont, ow synsi gwithvaow ha kowethyansow privedh. -authorize_title = Reythhe "%s" rag drehedhes agas akont? -authorization_failed = Reythheans fyllis -authorization_failed_desc = An reythheans fyllis drefen helerghyn ni govyn anewn. Kestavewgh an mentenour a'n app hwi re assayys rag reythhe mar pleg. -password_pwned = An ger-tremena hwi re dewisys yw war rol a eryow ledrys diskudhys kyns lemmyn yn torrvaow data poblek. Assayewgh arta gans ger-tremena aral mar pleg ha prederewgh ow chanjya an ger-tremena ma yn leow aral ynwedh. -password_pwned_err = Ny yllir kowlwul govyn dhe HaveIBeenPwned -last_admin = Ny yllowgh remova an menystrer finek. Res yw bos unn menystrer dhe'n lyha. -back_to_sign_in = Dehweles dhe Omgelmyans -sign_in_openid = Procedya gans OpenID - -[mail] -view_it_on = Gweles war %s -reply = po gorthebi dhe'n ebost ma distowgh -link_not_working_do_paste = Yw an gorgevren terrys? Assayewgh ow kopia hag ow tasskrifa y'n barr a'gas peurell. -hi_user_x = Yow %s, -activate_account = Bywhewgh agas akont mar pleg -activate_account.text_1 = Yow %[1]s, meur ras rag ow kovskrifa yn %[2]s! -activate_account.text_2 = Klyckyewgh war'n gorgevren a sew mar pleg rag bywhe agas akont a-ji dhe %s: -activate_email = Gwirhe agas trigva ebost -activate_email.text = Klyckyewgh an gorgevren a sew mar pleg rag gwirhe agas trigva ebost a-ji dhe %s: -admin.new_user.subject = Devnydhyer nowydh %s kovskrifys namnygen -admin.new_user.user_info = Kedhlow devnydhyer -admin.new_user.text = Klyckyewgh omma mar pleg rag dyghtya an devnydhyer ma y'n panel menystrer. -register_notify = %s a'gas dynnargh -register_notify.text_1 = agas ebost afydhyans kovskrifans yw hemma rag %s! -register_notify.text_2 = Hwi a yll omgelmi yn agas akont ow tevnydhya dha hanow devnydhyer: %s -register_notify.text_3 = Mars an akont ma o gwrys rag hwi gans nebonan aral, yma edhom dhywgh settya agas ger-tremena kyns. -reset_password = Daskavos agas akont -reset_password.text = Mar hwi o hemma, klyckyewgh an gorgevren a sew mar pleg rag daskavos agas akont a-ji dhe %s: -password_change.subject = Agas ger-tremena re beu chanjys -password_change.text_1 = An ger-tremena rag agas akont o chanjys namnygen. -primary_mail_change.subject = Agas penntrigva ebost re beu chanjys -primary_mail_change.text_1 = An penntrigva ebost a'gas akont o chanjys namnygen dhe %[1]s. Hemm a styr an drigva ebost ny vydh degemeres gwarnyans ebost rag agas akont na fella. -totp_disabled.subject = TOTP re beu marowhes -totp_disabled.text_1 = Ger-tremena termyn-selys unweyth (TOTP) war agas akont o marowhes namnygen. -totp_disabled.no_2fa = Nyns eus methodys 2FA selys na fella, ha nyns yw res rag omgelmi yn agas akont gans 2FA. -removed_security_key.subject = Alhwedh sawder re beu dileys -removed_security_key.text_1 = Alhwedh sawder "%[1]s" re beu dileys rag agas akont namnygen. -removed_security_key.no_2fa = Nyns eus methodys 2FA selys na fella, ha nyns yw res rag omgelmi yn agas akont gans 2FA. -account_security_caution.text_1 = Mar hwi o hemma, hwi a yll skonya aswon an ebost ma. -account_security_caution.text_2 = Mar nyns yw hwi, agas akont yw diantel. Kestavewgh an venystroryon a'n wiasva ma mar pleg. -totp_enrolled.subject = Hwi re bywhes TOTP avel method 2FA -totp_enrolled.text_1.no_webauthn = Hwi re bywhes TOTP rag agas akont namnygen. Hemm a styr rag oll omgelmyansow dh'gas akont, res yw dhywgh devnydhya TOTP avel method 2FA. -totp_enrolled.text_1.has_webauthn = Hwi re bywhes TOTP rag agas akont namnygen. Hemm a styr rag oll omgelmyansow dh'gas akont, hwi a yll devnydhya TOTP avel method 2FA po devnydhya neb a'gas alhwedhow sawder. -issue_assigned.pull = Yth ewgh hwi charjys gans @%[1]s dhe brofyans %[2]s yn gwithva %[3]s. -issue_assigned.issue = Yth ewgh hwi charjys gans @%[1]s dhe gudyn %[2]s yn gwithva %[3]s. -issue.x_mentioned_you = Yth ewgh hwi kampollys gans @%s: -issue.action.force_push = %[1]s herdhys gans nerth an %[2]s dhyworth %[3]s dhe %[4]s. -issue.action.push_1 = @%[1]s herdhys %[3]d gwrians dhe %[2]s -issue.action.push_n = @%[1]s herdhys %[3]d gwriansow dhe %[2]s -issue.action.close = @%[1]s deges #%[2]d. -issue.action.reopen = @%[1]s dasygerys #%[2]d. -issue.action.merge = @%[1]s kesunys #%[2]d yn %[3]s. -issue.action.approve = @%[1]s komendys an profyans ma. -issue.action.reject = @%[1]s govynnys chanjyow orth an profyans ma. -issue.action.review = @%[1]s kampollys war'n profyans ma. -issue.action.review_dismissed = @%[1]s naghys breusyans finek dhhyworth %[2]s rag an profyans ma. -issue.action.ready_for_review = @%[1]s merkys an profyans ma avel parys rag breusyans. -issue.action.new = @%[1]s gwrys #%[2]d. -issue.in_tree_path = Yn %s: -release.new.subject = %s yn %s dyllys -release.new.text = @%[1]s dyllys %[2]s yn %[3]s -release.title = Titel: %s -release.note = Noten: -release.downloads = Iskargow: -release.download.zip = Kod Pennfenten (ZIP) -release.download.targz = Kod Pennfenten (TAR.GZ) -repo.transfer.subject_to = %s a vynn treusworra gwithva "%s" dhe %s -repo.transfer.subject_to_you = %s a vynn treusworra gwithva "%s" dhe hwi -repo.transfer.to_you = hwi -repo.transfer.body = Rag degemeres po nagha vysytya %s po skonya aswon. -repo.collaborator.added.subject = %s addys hwi dhe %s avel kesoberer -repo.collaborator.added.text = Hwi re beu addys avel kesoberer dhe withva: -team_invite.subject = %[1]s re welwys hwi rag junya an kowethyans %[2]s -team_invite.text_1 = %[1]s re welwys hwi rag junya bagas %[2]s yn kowethyans %[3]s. -team_invite.text_2 = Klyckyewgh an gorgevren a sew mar pleg rag junya an bagas: -team_invite.text_3 = Noten: An galow ma o mynnys rag %[1]s. Mar na gwaytyowgh an galow ma, hwi a yll skonya aswon an ebost ma. - -[modal] -yes = Ya -no = Na -confirm = Afydhya -cancel = Hedhi - -[form] -UserName = Hanow devnydhyer -FullName = Hanow leun -Description = Deskrifans -Pronouns = Rakhenwyn -Biography = Bewskrif -Website = Gwiasva -Location = Tyller -RepoName = Hanow gwithva -Email = Trigva ebost -Password = Ger-tremena -Retype = Afydhya ger-tremena -PayloadUrl = Gorgevren karg -TeamName = Hanow bagas -AuthName = Hanow reythheans -AdminEmail = Trigva ebost menystrer -To = Hanow skorr -AccessToken = Tokyn hedhas -NewBranchName = Hanow skorr nowydh -CommitSummary = Berrskrif gwrians -CommitMessage = Messach gwrians -CommitChoice = Dewis gwrians -TreeName = Tyller restren -Content = Synsas -require_error = ` ny yllir bos gwag.` -alpha_dash_error = ` y kodh synsi lytherennow y'n abecedari, niveryow, linen ("-") hag islinen ("_").` -alpha_dash_dot_error = ` y kodh synsi lytherennow y'n abecedari, niveryow, linen ("-"), islinen ("_"), ha dyjyn (".").` -git_ref_name_error = ` res yw bos hanow kampol Git gwrys yn ta.` -size_error = ` res yw bos braster %s.` -min_size_error = ` res yw synsi %s lytherennow dhe'n lyha.` -max_size_error = ` res yw synsi %s lytherennow dhe'n moyha.` -email_error = ` nyns yw trigva ebost ewn.` -url_error = `"%s" nyns yw gorgevren ewn.` -include_error = ` res yw synsi istekst "%s".` -glob_pattern_error = ` patron glob yw anewn: %s.` -regex_pattern_error = ` patron menegyn rewlys yw anewn: %s.` -username_error = ` a yll synsi lytherennow ("0-9","a-z","A-Z"), linen ("-"), islinen ("_") ha dyjyn (".") yn unnik. Ny yllir dalleth po finsya gans lytherennow na'n par na, ha lytherennow na'n par na yw difennys ynwedh.` -username_error_no_dots = ` a yll synsi lytherennow ("0-9","a-z","A-Z"), linen ("-"), hag islinen ("_") yn unnik. Ny yllir dalleth po finsya gans lytherennow na'n par na, ha lytherennow na'n par na yw difennys ynwedh.` -invalid_group_team_map_error = ` mappyans yw anewn: %s` -unknown_error = Error ankoth: -captcha_incorrect = Anewn yw an kod CAPTCHA. -password_not_match = Na omdhesedha an eryow-tremena. -lang_select_error = Dewis yeth dhyworth an rol. -username_been_taken = An hanow devnydhyer yw kemerys seulabrys. -username_change_not_local_user = Nyns yw devnydhyoryon anleel gesys rag chanjya aga hanow devnydhyer. -username_claiming_cooldown = Nyllir perghenegi an hanow devnydhyer drefen nyns yw deu y dermyn-mygla. Y hyll bos perghenegys %[1]s. -repo_name_been_taken = An hanow gwithva yw devnydhys seulabrys. -repository_force_private = Ynia Privedh yw byw: ny yllir dylla yn poblek gwithvaow privedh. -repository_files_already_exist = Yma restrennow rag an gwithva ma seulabrys. Kestavewgh an menystrer system. -repository_files_already_exist.adopt = Yma restrennow rag an gwithva ma seulabrys hag yllons bos Asvebys hepken. -repository_files_already_exist.delete = Yma restrennow rag an gwithva ma seulabrys. Res yw dhywgh dilea. -repository_files_already_exist.adopt_or_delete = Yma restrennow rag an gwithva ma seulabrys. Res yw dhywgh dilea po asvaba. -visit_rate_limit = Vysytyans pell a ewnhe finweth. -2fa_auth_required = Vysytyans pell rekwirys gwirheans dewkamm. -org_name_been_taken = An hanow kowethyans yw kemerys seulabrys. -team_name_been_taken = An hanow bagas yw kemerys seulabrys. -team_no_units_error = Gasa hedhas dhe unn dregh gwithva dhe'n lyha. -email_been_used = An drigva ebost yw devnydhys seulabrys. -email_invalid = Anewn yw an drigva ebost. -email_domain_is_not_allowed = Yma kas ynter an arlotteth a'n drigva ebost devnydhyer %s ha EMAIL_DOMAIN_ALLOWLIST po EMAIL_DOMAIN_BLOCKLIST. Surhewgh hwi re settys an drigva ebost yn ta. -openid_been_used = An drigva OpenID "%s" yw devnydhys seulabrys. -username_password_incorrect = Hanow devnydhyer po ger-tremena yw anewn. -password_complexity = Ger-tremena ny hedhes edhommow komplegeth: -password_lowercase_one = Unn lytheren byghan dhe'n lyha -password_uppercase_one = Unn lytheren bras dhe'n lyha -password_digit_one = Unn niver dhe'n lyha -password_special_one = Unn lytheren arbennik (poyntyans, krommvaghow, devynys, h.e.) dhe'n lyha -enterred_invalid_repo_name = Anewn yw an hanow gwithva ynworrys. -enterred_invalid_org_name = Anewn yw an hanow kowethyans ynworrys. -enterred_invalid_owner_name = Nyns yw ewn an hanow perghen nowydh. -enterred_invalid_password = Anewn yw an ger-tremena ynworrys. -unset_password = Ny veu an devnydhyer omgelmyans an ger-tremena settys. -unsupported_login_type = Nyns yw skoodhys an eghen omgelmyans rag dilea akont. -user_not_exist = Nyns eus an devnydhyer. -team_not_exist = Nyns eus an bagas. -last_org_owner = Ny yllowgh remova an devnydhyer diwettha rag an bagas "perghennow". Res yw bos unn perghen dhe'n lyha rag kowethyans. -cannot_add_org_to_team = Ny yllir keworra Kowethyans avel esel bagas. -duplicate_invite_to_team = An devnydhyer yw gelwys avel esel bagas seulabrys. -organization_leave_success = Hwi re gesys an kowethyans %s yn sewen. -invalid_ssh_key = Ny yllir gwirhe agas alhwedh SSH: %s -invalid_gpg_key = Ny yllir gwirhe agas alhwedh GPG: %s -must_use_public_key = An alhwedh provys yw alhwedh privedh. Na ughkargewgh agas alhwedh privedh yn neb le. Devnydhyewgh agas alhwedh poblek yn le. -unable_verify_ssh_key = Ny yllir gwirhe an alhwedh SSH, checkyewgh rag kammgemeryansow. -auth_failed = Ny yllir gwirhe: %v -still_own_repo = Yma dh'gas akont unn po moy gwithvaow, dileewgh po treusperthi kyns. -still_has_org = Agas akont yw esel a unn po moy kowethyansow, gasewgh kyns. -still_own_packages = Yma dh'gas akont unn po moy fardellow, dileewgh kyns. -org_still_own_repo = Yma dhe'n kowethyans ma unn po moy gwithvaow, dileewgh po treusperthewgh kyns. -org_still_own_packages = Yma dhe'n kowethyans ma unn po moy fardellow, dileewgh kyns. -target_branch_not_exist = Nyns eus an skorr kostennys. -admin_cannot_delete_self = Ny yllowgh dilea agas honan mar menystrer owgh hwi. Removewgh agas pryvylejys menystrer kyns mar pleg. -required_prefix = Res yw dhywgh dalleth ynworrans gans "%s" - -[user] -change_avatar = Chanjya agas imach… -joined_on = Omjunys %s -repositories = Gwithvaow -activity = Aktivita poblek -followers.title.one = Holyer -followers.title.few = Holyoryon -following.title.one = Ow Holya -following.title.few = Ow Holya -followers_one = %d holyer -followers_few = %d holyoryon -following_one = %d ow holya -following_few = %d ow holya -follow = Holya -unfollow = Diholya -block_user = Lettya devnydhyer -block_user.detail = Notyewgh yma effeythyow aral a ow lettya devnydhyer mar pleg, kepar ha: -block_user.detail_1 = Hwi a vydh astel ow holya an eyl y gila ha ny yllir holya an eyl y gila. -block_user.detail_2 = An devnydhyer ma ny yllir ynterweytha gans agas gwithvaow, po an gudynnow ha kampollansow hwi re gwrys. -block_user.detail_3 = Ny yllowgh keworra an eyl y gila avel kesoberoryon gwithva. -follow_blocked_user = Ny yllowgh holya an devnydhyer ma drefen hwi re lettys an devnydhyer ma po an devnydhyer ma re hwi lettys. -starred = Gwithvaow drudh -watched = Gwithvaow mirys -code = Kod -projects = Ragdresow -overview = Gorwolok -block = Lettya -unblock = Dilettya -user_bio = Bewskrif -email_visibility.limited = Agas trigva ebost yw gweladow rag oll devnydhyoryon omgelmys -show_on_map = Diskwedhes an tyller ma war vappa -settings = Settyansow devnydhyer -disabled_public_activity = An devnydhyer ma re marowhes an gweladewder poblek a'n aktivita. -public_activity.visibility_hint.self_public = Agas aktivita yw gweladow rag pubonan, marnas keskowsow yn spassow privedh. Chanjya. -public_activity.visibility_hint.admin_public = Agas aktivita yw gweladow rag pubonan mes, drefen menystrer owgh hwi, hwi a yll gweles keskowsow yn spassow privedh. -public_activity.visibility_hint.self_private = Agas aktivita yw gweladow rag hwi ha'n venystroryon gweyth yn unnik. Chanjya. -public_activity.visibility_hint.admin_private = An aktivita ma yw gweladow rag hwi drefen menystrer owgh hwi, mes an devnydhya mynnes gwitha privedh. -public_activity.visibility_hint.self_private_profile = Agas aktivita yw gweladow rag hwi ha'n venystroryon gweyth yn unnik drefen agas profil yw privedh. Chanjya. -form.name_reserved = An hanow devndyhyer "%s" yw ragerghys. -form.name_pattern_not_allowed = An patron "%s" nyns yw gesys yn hanow devnydhyer. -form.name_chars_not_allowed = Yma lytherennow anewn y'n hanow devnydhyer "%s". - -[settings] -profile = Profil -account = Akont -appearance = Gis -security = Sawder -avatar = Imach -ssh_gpg_keys = Alhwedhow SSH / GPG -applications = Appys -orgs = Kowethyansow -repos = Gwithvaow -twofa = Reythheans dewkamm (TOTP) -organization = Kowethyansow -webauthn = Reythheans dewkamm (Alhwedhow sawder) -blocked_users = Devnydhyoryon lettys -storage_overview = Gorwolok gwithans -quota = Finweth -public_profile = Profil poblek -biography_placeholder = Kedhlewgh re erel nebes a-dro dhywgh! (Markdown yw skoodhys) -location_placeholder = Kevrenna agas tyller nesogas gans re erel -profile_desc = A-dro dhywgh -password_username_disabled = Devnydhyoryon anleel nyns yw gesys chanjya aga hanow devnydhyer. Kestavewgh agas menystrer gwiasva rag moy manylyon mar pleg. -full_name = Hanow leun -website = Gwiasva -location = Tyller -pronouns = Rakhenwyn -pronouns_unspecified = Anragnotys -update_theme = Chanjya thema -update_profile = Nowedhi profil -update_language = Chanjya yeth -update_language_not_found = Yeth "%s" nyns yw kavadow. -update_language_success = Yeth re beu nowedhys. -update_profile_success = Agas profil re beu nowedhys. -change_username_prompt = Noten: Ow chanjya agas hanow devnydhyer a wra chanjya an URL a'gas akont. -change_username_redirect_prompt = An hanow devnydhyer koth a wra daskevarwodha ernag yma nebonan ow perghenegi. -change_username_redirect_prompt.with_cooldown.one = An hanow devnydhyer koth a wra bos kavadow rag pubonan wosa termyn-mygla a %[1]d dydh. Hwi a yll dasberghenegi an hanow devnydhyer koth dres an termyn-mygla. -change_username_redirect_prompt.with_cooldown.few = An hanow devnydhyer koth a wra bos kavadow rag pubonan wosa termyn-mygla a %[1]d dedhyow. Hwi a yll dasberghenegi an hanow devnydhyer koth dres an termyn-mygla. -language = Yeth -language.title = Yeth defowt -language.description = An yeth ma y fydh sawys dh'gas akont ha devnydhys avel an defowt wosa omgelmowgh. -language.localization_project = Gweresewgh ni treylya Forgejo yn agas yeth! Dyski moy. -ui = Thema -hints = Hyntys -additional_repo_units_hint = Profyewgh bywhe unses gwithva keworransel -additional_repo_units_hint_description = Diskwedhes hynt "Bywhe moy" rag gwithvaow le may nyns eus oll unses kavadow bywhes. -update_hints = Nowedhi hyntys -update_hints_success = Hyntys re beu nowedhys. -hidden_comment_types = Eghennow kampol kudh -hidden_comment_types_description = Eghennow kampol checkys omma ny vydh diskwedhys yn folennow kudyn. Ow checkya "Label" rag ensampel a wra remova oll kampollys " keworrys/removys
{{end}} - {{if .EnableFederation}} -
- {{ctx.Locale.Tr "admin.federation.federation"}} - -
- {{end}}
{{ctx.Locale.Tr "admin.config"}}
- {{ctx.Locale.Tr "packages.settings.delete.notice" (``|TrustHTML) (``|TrustHTML)}} + {{ctx.Locale.Tr "packages.settings.delete.notice" (``|SafeHTML) (``|SafeHTML)}}
{{template "base/modal_actions_confirm" .}} diff --git a/templates/admin/queue_manage.tmpl b/templates/admin/queue_manage.tmpl index 7686755d6d..a793fe1350 100644 --- a/templates/admin/queue_manage.tmpl +++ b/templates/admin/queue_manage.tmpl @@ -44,7 +44,7 @@ {{ctx.Locale.Tr "admin.monitor.queue.settings.title"}}
-

{{ctx.Locale.Tr "admin.monitor.queue.settings.description"}}

+

{{ctx.Locale.Tr "admin.monitor.queue.settings.desc"}}

diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index 465c51ddcf..8f3f41cea9 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -103,7 +103,7 @@

{{ctx.Locale.Tr "repo.settings.delete_desc"}}

- {{ctx.Locale.Tr "repo.settings.delete_notices_2" (``|TrustHTML)}}
+ {{ctx.Locale.Tr "repo.settings.delete_notices_2" (``|SafeHTML)}}
{{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}}
{{template "base/modal_actions_confirm" .}} diff --git a/templates/admin/repo/unadopted.tmpl b/templates/admin/repo/unadopted.tmpl index bc74f24a58..d7e57c7c14 100644 --- a/templates/admin/repo/unadopted.tmpl +++ b/templates/admin/repo/unadopted.tmpl @@ -24,37 +24,37 @@ {{svg "octicon-file-directory-fill"}} {{$dir}}
- -
-
{{ctx.Locale.Tr "repo.adopt_preexisting"}}
-
-

{{ctx.Locale.Tr "repo.adopt_preexisting_content" $dir}}

-
- - - - - - {{template "base/modal_actions_confirm"}} - -
-
+ - -
-
{{ctx.Locale.Tr "repo.delete_preexisting"}}
-
-

{{ctx.Locale.Tr "repo.delete_preexisting_content" $dir}}

-
-
- - - - - {{template "base/modal_actions_confirm"}} -
-
-
+
{{end}} diff --git a/templates/admin/runners/create.tmpl b/templates/admin/runners/create.tmpl deleted file mode 100644 index 5d3bc739ac..0000000000 --- a/templates/admin/runners/create.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin runners")}} -
- {{template "shared/actions/runner_create" .}} -
-{{template "admin/layout_footer" .}} diff --git a/templates/admin/runners/details.tmpl b/templates/admin/runners/details.tmpl deleted file mode 100644 index 47eb552952..0000000000 --- a/templates/admin/runners/details.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin runners")}} -
- {{template "shared/actions/runner_details" .}} -
-{{template "admin/layout_footer" .}} diff --git a/templates/admin/runners/edit.tmpl b/templates/admin/runners/edit.tmpl index 6a4b696696..1165c84b79 100644 --- a/templates/admin/runners/edit.tmpl +++ b/templates/admin/runners/edit.tmpl @@ -1,5 +1,5 @@ {{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin runners")}} -
- {{template "shared/actions/runner_edit" .}} -
+
+ {{template "shared/actions/runner_edit" .}} +
{{template "admin/layout_footer" .}} diff --git a/templates/admin/runners/setup.tmpl b/templates/admin/runners/setup.tmpl deleted file mode 100644 index 6fa9f69a96..0000000000 --- a/templates/admin/runners/setup.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin runners")}} -
- {{template "shared/actions/runner_setup" .}} -
-{{template "admin/layout_footer" .}} diff --git a/templates/admin/stacktrace.tmpl b/templates/admin/stacktrace.tmpl index b89140483c..57c0c210cc 100644 --- a/templates/admin/stacktrace.tmpl +++ b/templates/admin/stacktrace.tmpl @@ -40,7 +40,7 @@ {{ctx.Locale.Tr "admin.monitor.process.cancel"}}
-

{{ctx.Locale.Tr "admin.monitor.process.cancel_notices" (``|TrustHTML)}}

+

{{ctx.Locale.Tr "admin.monitor.process.cancel_notices" (``|SafeHTML)}}

{{ctx.Locale.Tr "admin.monitor.process.cancel_desc"}}

{{template "base/modal_actions_confirm" .}} diff --git a/templates/admin/system_status.tmpl b/templates/admin/system_status.tmpl index c1dd838290..d9856ccd0b 100644 --- a/templates/admin/system_status.tmpl +++ b/templates/admin/system_status.tmpl @@ -1,62 +1,62 @@
-
{{ctx.Locale.Tr "admin.system_status.server_uptime"}}
+
{{ctx.Locale.Tr "admin.dashboard.server_uptime"}}
{{.SysStatus.StartTime}}
-
{{ctx.Locale.Tr "admin.system_status.current_goroutine"}}
+
{{ctx.Locale.Tr "admin.dashboard.current_goroutine"}}
{{.SysStatus.NumGoroutine}}
-
{{ctx.Locale.Tr "admin.system_status.current_memory_usage"}}
+
{{ctx.Locale.Tr "admin.dashboard.current_memory_usage"}}
{{ctx.Locale.TrSize .SysStatus.MemAllocated}}
-
{{ctx.Locale.Tr "admin.system_status.total_memory_allocated"}}
+
{{ctx.Locale.Tr "admin.dashboard.total_memory_allocated"}}
{{ctx.Locale.TrSize .SysStatus.MemTotal}}
-
{{ctx.Locale.Tr "admin.system_status.memory_obtained"}}
+
{{ctx.Locale.Tr "admin.dashboard.memory_obtained"}}
{{ctx.Locale.TrSize .SysStatus.MemSys}}
-
{{ctx.Locale.Tr "admin.system_status.pointer_lookup_times"}}
+
{{ctx.Locale.Tr "admin.dashboard.pointer_lookup_times"}}
{{.SysStatus.Lookups}}
-
{{ctx.Locale.Tr "admin.system_status.memory_allocate_times"}}
+
{{ctx.Locale.Tr "admin.dashboard.memory_allocate_times"}}
{{.SysStatus.MemMallocs}}
-
{{ctx.Locale.Tr "admin.system_status.memory_free_times"}}
+
{{ctx.Locale.Tr "admin.dashboard.memory_free_times"}}
{{.SysStatus.MemFrees}}
-
{{ctx.Locale.Tr "admin.system_status.current_heap_usage"}}
+
{{ctx.Locale.Tr "admin.dashboard.current_heap_usage"}}
{{ctx.Locale.TrSize .SysStatus.HeapAlloc}}
-
{{ctx.Locale.Tr "admin.system_status.heap_memory_obtained"}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_memory_obtained"}}
{{ctx.Locale.TrSize .SysStatus.HeapSys}}
-
{{ctx.Locale.Tr "admin.system_status.heap_memory_idle"}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_memory_idle"}}
{{ctx.Locale.TrSize .SysStatus.HeapIdle}}
-
{{ctx.Locale.Tr "admin.system_status.heap_memory_in_use"}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_memory_in_use"}}
{{ctx.Locale.TrSize .SysStatus.HeapInuse}}
-
{{ctx.Locale.Tr "admin.system_status.heap_memory_released"}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_memory_released"}}
{{ctx.Locale.TrSize .SysStatus.HeapReleased}}
-
{{ctx.Locale.Tr "admin.system_status.heap_objects"}}
+
{{ctx.Locale.Tr "admin.dashboard.heap_objects"}}
{{.SysStatus.HeapObjects}}
-
{{ctx.Locale.Tr "admin.system_status.bootstrap_stack_usage"}}
+
{{ctx.Locale.Tr "admin.dashboard.bootstrap_stack_usage"}}
{{ctx.Locale.TrSize .SysStatus.StackInuse}}
-
{{ctx.Locale.Tr "admin.system_status.stack_memory_obtained"}}
+
{{ctx.Locale.Tr "admin.dashboard.stack_memory_obtained"}}
{{ctx.Locale.TrSize .SysStatus.StackSys}}
-
{{ctx.Locale.Tr "admin.system_status.mspan_structures_usage"}}
+
{{ctx.Locale.Tr "admin.dashboard.mspan_structures_usage"}}
{{ctx.Locale.TrSize .SysStatus.MSpanInuse}}
-
{{ctx.Locale.Tr "admin.system_status.mspan_structures_obtained"}}
+
{{ctx.Locale.Tr "admin.dashboard.mspan_structures_obtained"}}
{{ctx.Locale.TrSize .SysStatus.MSpanSys}}
-
{{ctx.Locale.Tr "admin.system_status.mcache_structures_usage"}}
+
{{ctx.Locale.Tr "admin.dashboard.mcache_structures_usage"}}
{{ctx.Locale.TrSize .SysStatus.MCacheInuse}}
-
{{ctx.Locale.Tr "admin.system_status.mcache_structures_obtained"}}
+
{{ctx.Locale.Tr "admin.dashboard.mcache_structures_obtained"}}
{{ctx.Locale.TrSize .SysStatus.MCacheSys}}
-
{{ctx.Locale.Tr "admin.system_status.profiling_bucket_hash_table_obtained"}}
+
{{ctx.Locale.Tr "admin.dashboard.profiling_bucket_hash_table_obtained"}}
{{ctx.Locale.TrSize .SysStatus.BuckHashSys}}
-
{{ctx.Locale.Tr "admin.system_status.gc_metadata_obtained"}}
+
{{ctx.Locale.Tr "admin.dashboard.gc_metadata_obtained"}}
{{ctx.Locale.TrSize .SysStatus.GCSys}}
-
{{ctx.Locale.Tr "admin.system_status.other_system_allocation_obtained"}}
+
{{ctx.Locale.Tr "admin.dashboard.other_system_allocation_obtained"}}
{{ctx.Locale.TrSize .SysStatus.OtherSys}}
-
{{ctx.Locale.Tr "admin.system_status.next_gc_recycle"}}
+
{{ctx.Locale.Tr "admin.dashboard.next_gc_recycle"}}
{{ctx.Locale.TrSize .SysStatus.NextGC}}
-
{{ctx.Locale.Tr "admin.system_status.last_gc_time"}}
+
{{ctx.Locale.Tr "admin.dashboard.last_gc_time"}}
{{.SysStatus.LastGCTime}}
-
{{ctx.Locale.Tr "admin.system_status.total_gc_pause"}}
+
{{ctx.Locale.Tr "admin.dashboard.total_gc_pause"}}
{{.SysStatus.PauseTotalNs}}
-
{{ctx.Locale.Tr "admin.system_status.last_gc_pause"}}
+
{{ctx.Locale.Tr "admin.dashboard.last_gc_pause"}}
{{.SysStatus.PauseNs}}
-
{{ctx.Locale.Tr "admin.system_status.gc_times"}}
+
{{ctx.Locale.Tr "admin.dashboard.gc_times"}}
{{.SysStatus.NumGC}}
diff --git a/templates/admin/user/edit.tmpl b/templates/admin/user/edit.tmpl index f18317e694..1d29a991cd 100644 --- a/templates/admin/user/edit.tmpl +++ b/templates/admin/user/edit.tmpl @@ -224,23 +224,24 @@ - -
-
{{svg "octicon-trash"}} {{ctx.Locale.Tr "settings.delete_account_title"}}
-
-
-

{{ctx.Locale.Tr "settings.delete_account_desc"}}

-
-
- - -
-

{{ctx.Locale.Tr "admin.users.purge_help"}}

+
-
+ + {{template "base/modal_actions_confirm" .}} + + {{template "admin/layout_footer" .}} diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl index 8ce4fd21eb..133ebac33a 100644 --- a/templates/base/footer_content.tmpl +++ b/templates/base/footer_content.tmpl @@ -6,7 +6,7 @@ {{if (or .ShowFooterVersion .PageIsAdmin)}} {{ctx.Locale.Tr "version"}}: {{if .IsAdmin}} - {{AppVerNoMetadata}} + {{AppVer}} {{else}} {{AppVerNoMetadata}} {{end}} diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 8b4f661b57..6027c13f12 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -4,7 +4,7 @@ {{/* Display `- .Repository.FullName` only if `.Title` does not already start with that. */}} {{if .Title}}{{.Title}} - {{end}}{{if and (.Repository.Name) (not (StringUtils.HasPrefix .Title .Repository.FullName))}}{{.Repository.FullName}} - {{end}}{{AppDisplayName}} - + {{if .ManifestData}}{{end}} @@ -19,9 +19,6 @@ {{end}} -{{if and FederationEnabled .PageIsUserProfile .ContextUser .ContextUser.IsIndividual}} - -{{end}} {{template "base/head_script" .}} {{template "shared/user/mention_highlight" .}} {{template "base/head_opengraph" .}} diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index f7405a743e..e5d67556c2 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -14,7 +14,7 @@
{{else if .IsSigned}} {{if EnableTimetracking}} - -
+ +
{{svg "octicon-stopwatch"}}
@@ -113,7 +113,7 @@ {{end}}
-
+
{{svg "octicon-bell"}} {{$notificationUnreadCount}}
@@ -122,7 +122,7 @@
diff --git a/templates/shared/repo_search.tmpl b/templates/shared/repo_search.tmpl index 8ceadc95da..d08d6017ed 100644 --- a/templates/shared/repo_search.tmpl +++ b/templates/shared/repo_search.tmpl @@ -20,19 +20,19 @@ diff --git a/templates/shared/secrets/add_list.tmpl b/templates/shared/secrets/add_list.tmpl index d2eb74b406..0cd276f64c 100644 --- a/templates/shared/secrets/add_list.tmpl +++ b/templates/shared/secrets/add_list.tmpl @@ -30,18 +30,8 @@ {{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}} - - - - {{if eq (len .Repos) 0}} - {{ctx.Locale.Tr "settings.access_token.no_repositories_found"}} - {{else}} -
- {{range .Repos}} - {{template "user/settings/repo_icon" .}} -
- {{.FullName}} -
- - {{end}} -
- {{end}} - - {{/* Can't use base/paginate template include here because all the pagination links in - that template are simple
links. Here, we need to turn them into form - buttons so that we can submit the current form. If a user just changed a value (eg. set - the token name, changed a selected permission) and then clicked a pagination button, the - new value that they changed needs to be submitted. base/paginate would allow preserving - old values from before the change, but not new updates. Implementing here also allows - the use of smaller styling. */}} - {{with .Page.Paginater}} - -
- -
- {{end}} - - - -
-
{{ctx.Locale.TrPluralString (len .SelectedRepos) "settings.access_token.selected_repositories" (len .SelectedRepos)}}
- {{if eq (len .SelectedRepos) 0}} -
- {{ctx.Locale.Tr "settings.access_token.no_repositories_selected"}} -
- {{else}} -
- {{range .SelectedRepos}} - - {{template "user/settings/repo_icon" .}} -
- {{.FullName}} -
- - {{end}} -
- {{end}} -
- - - -
- -

-

{{ctx.Locale.Tr "settings.access_token_desc" (printf "%s/api/swagger" AppSubUrl) "https://forgejo.org/docs/latest/user/token-scope/"}}

-

- - {{range .Categories}} -
- -
- -
-
- {{end}} -
- - - - - -{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl index 6f81b255f1..35f1d07b60 100644 --- a/templates/user/settings/account.tmpl +++ b/templates/user/settings/account.tmpl @@ -4,7 +4,7 @@ {{ctx.Locale.Tr "settings.change_password"}}
- {{if and (not ($.UserDisabledFeatures.Contains "manage_password")) (or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2))}} + {{if or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2)}}
{{template "base/disable_form_autofill"}} {{if .SignedUser.IsPasswordSet}} diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index eaf18f6de1..b77bcf067b 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -2,43 +2,33 @@

{{ctx.Locale.Tr "settings.manage_access_token"}} -

{{ctx.Locale.Tr "settings.tokens_desc"}}
- {{range .TokensWithResources}} + {{range .Tokens}}
- + {{svg "fontawesome-send" 32}}
- {{.Token.Name}} - {{if .Token.ResourceAllRepos}} -

{{ctx.Locale.Tr "settings.repo_and_org_access"}}:

- {{if .Token.DisplayPublicOnly}} + {{.Name}} +

+ {{ctx.Locale.Tr "settings.repo_and_org_access"}}: + {{if .DisplayPublicOnly}} {{ctx.Locale.Tr "settings.permissions_public_only"}} {{else}} {{ctx.Locale.Tr "settings.permissions_access_all"}} {{end}} - {{else}} -

{{ctx.Locale.Tr "settings.specific_repo_access"}}:

- - {{end}} +

{{ctx.Locale.Tr "settings.permissions_list"}}

    - {{range .Token.Scope.StringSlice}} + {{range .Scope.StringSlice}} {{if (ne . $.AccessTokenScopePublicOnly)}}
  • {{.}}
  • {{end}} @@ -46,15 +36,15 @@
-

{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .Token.CreatedUnix)}} — {{svg "octicon-info"}} {{if .Token.HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateUtils.AbsoluteShort .Token.UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}

+

{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateUtils.AbsoluteShort .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}

- - @@ -63,6 +53,48 @@ {{end}}
+
+
+ {{ctx.Locale.Tr "settings.generate_new_token"}} +
+ +
+ + +
+
+ + + +
+
+ + {{ctx.Locale.Tr "settings.select_permissions"}} + +

+

{{ctx.Locale.Tr "settings.access_token_desc" (printf "%s/api/swagger" AppSubUrl) "https://forgejo.org/docs/latest/user/token-scope/"}}

+

+
+
+ + {{/* Fomantic ".ui.form .warning.message" is hidden by default, so put the warning message out of the form*/}} +
+ {{ctx.Locale.Tr "settings.at_least_one_permission"}} +
+
{{if .EnableOAuth2}} {{template "user/settings/grants_oauth2" .}} diff --git a/templates/user/settings/authorized_integrations.tmpl b/templates/user/settings/authorized_integrations.tmpl deleted file mode 100644 index 868077bbfa..0000000000 --- a/templates/user/settings/authorized_integrations.tmpl +++ /dev/null @@ -1,51 +0,0 @@ -{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings applications")}} - -
-

- {{ctx.Locale.Tr "settings.manage_authorized_integrations"}} -

-
-
-
- {{ctx.Locale.Tr "settings.authorized_integration.desc"}} -
- {{range .AuthorizedIntegrations}} -
-
- {{if eq .UI "forgejo-actions-local"}} - - {{svg "gitea-forgejo" 32}} - - {{else}} - - {{svg "octicon-cloud" 32}} - - {{end}} -
-
- {{.Name}} -
-

{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasBeenUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateUtils.AbsoluteShort .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}

-
-
- -
- {{else}} -
-
-
-

{{ctx.Locale.Tr "settings.authorized_integration.none"}}

-
-
-
- {{end}} -
-
-
- -{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/authorized_integrations/generic/view.tmpl b/templates/user/settings/authorized_integrations/generic/view.tmpl deleted file mode 100644 index 070114fe28..0000000000 --- a/templates/user/settings/authorized_integrations/generic/view.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -{{template "user/settings/authorized_integrations/view_head" .}} - -

- {{ctx.Locale.Tr "settings.authorized_integration.claims.generic"}} -

-
-
- - -
- -
- - - {{template "shared/codemirror_container" .}} -
- -
- -{{template "user/settings/authorized_integrations/view_footer" .}} diff --git a/templates/user/settings/authorized_integrations/view_footer.tmpl b/templates/user/settings/authorized_integrations/view_footer.tmpl deleted file mode 100644 index 23bd1cb0b7..0000000000 --- a/templates/user/settings/authorized_integrations/view_footer.tmpl +++ /dev/null @@ -1,76 +0,0 @@ - -

- {{ctx.Locale.Tr "settings.authorized_integration.perms.title"}} -

-
- -
-
- -
-
- - -

{{ctx.Locale.Tr "settings.access_token.resource_all_help"}}

-
-
-
-
- - -

- {{ctx.Locale.Tr "settings.access_token.resource_public_only_help"}} - {{if $.IsAdmin}}{{ctx.Locale.Tr "settings.access_token.admin_disabled"}}{{end}} -

-
-
-
-
- - -

- {{ctx.Locale.Tr "settings.access_token.resource_specific_repo_help"}} - {{if $.IsAdmin}}{{ctx.Locale.Tr "settings.access_token.admin_disabled"}}{{end}} -

-
-
-
-
- - -
- -

-

{{ctx.Locale.Tr "settings.access_token_desc" (printf "%s/api/swagger" AppSubUrl) "https://forgejo.org/docs/latest/user/token-scope/"}}

-

- - {{range .Categories}} -
- -
- -
-
- {{end}} -
- -
- - -
- -{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/authorized_integrations/view_head.tmpl b/templates/user/settings/authorized_integrations/view_head.tmpl deleted file mode 100644 index a148008cd1..0000000000 --- a/templates/user/settings/authorized_integrations/view_head.tmpl +++ /dev/null @@ -1,27 +0,0 @@ -{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings applications authorized-integrations")}} - -
-
- -

- {{ctx.Locale.Tr "settings.authorized_integration.view_page_title" .Form.Name}} -

-
-
- - -
- -
- - -
- -
- -
- - -
-
-
diff --git a/templates/user/settings/navbar.tmpl b/templates/user/settings/navbar.tmpl index c6012ba437..325fdada7d 100644 --- a/templates/user/settings/navbar.tmpl +++ b/templates/user/settings/navbar.tmpl @@ -17,9 +17,6 @@ {{ctx.Locale.Tr "settings.applications"}} - - {{ctx.Locale.Tr "settings.authorized_integrations"}} - {{ctx.Locale.Tr "settings.ssh_gpg_keys"}} diff --git a/templates/user/settings/organization.tmpl b/templates/user/settings/organization.tmpl index 13749806c6..e3a8ec1471 100644 --- a/templates/user/settings/organization.tmpl +++ b/templates/user/settings/organization.tmpl @@ -46,7 +46,7 @@ {{ctx.Locale.Tr "org.members.leave"}}
-

{{ctx.Locale.Tr "org.members.leave.detail" (``|TrustHTML)}}

+

{{ctx.Locale.Tr "org.members.leave.detail" (``|SafeHTML)}}

{{template "base/modal_actions_confirm" .}}
diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl index 49a349a4f1..d43a4d4c6e 100644 --- a/templates/user/settings/profile.tmpl +++ b/templates/user/settings/profile.tmpl @@ -9,8 +9,8 @@ {{ctx.Locale.Tr "settings.profile_desc"}}
-
+
-
+
-