diff --git a/.deadcode-out b/.deadcode-out index cf5f7c479b..dc4aa0e498 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -84,7 +84,6 @@ package "code.gitea.io/gitea/models/repo" func (*releaseSorter).Swap func SortReleases func FindReposMapByIDs - func RepositoryListOfMap func (SearchOrderBy).String func IsErrTopicNotExist func (ErrTopicNotExist).Error @@ -178,6 +177,7 @@ package "code.gitea.io/gitea/modules/git" func (ErrExecTimeout).Error func (ErrUnsupportedVersion).Error func SetUpdateHook + func GetObjectFormatOfRepo func openRepositoryWithDefaultContext func IsTagExist func ToEntryMode @@ -189,6 +189,7 @@ package "code.gitea.io/gitea/modules/gitgraph" package "code.gitea.io/gitea/modules/gitrepo" func GetBranchCommitID + func GetWikiDefaultBranch package "code.gitea.io/gitea/modules/graceful" func (*Manager).TerminateContext @@ -221,7 +222,6 @@ package "code.gitea.io/gitea/modules/markup/markdown" func IsSummary func IsTaskCheckBoxListItem func IsIcon - func IsColorPreview func RenderRawString package "code.gitea.io/gitea/modules/markup/markdown/math" @@ -291,9 +291,6 @@ package "code.gitea.io/gitea/modules/translation" func (MockLocale).TrN func (MockLocale).PrettyNumber -package "code.gitea.io/gitea/modules/util" - func UnsafeStringToBytes - package "code.gitea.io/gitea/modules/util/filebuffer" func CreateFromReader diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9e290fb6a5..8563aafd04 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,9 @@ }, "ghcr.io/devcontainers/features/git-lfs:1.1.0": {}, "ghcr.io/devcontainers-contrib/features/poetry:2": {}, - "ghcr.io/devcontainers/features/python:1": {} + "ghcr.io/devcontainers/features/python:1": { + "version": "3.12" + } }, "customizations": { "vscode": { diff --git a/.eslintrc.yaml b/.eslintrc.yaml index e9991c02ba..b62b13cefe 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -283,7 +283,7 @@ rules: i/unambiguous: [0] init-declarations: [0] jquery/no-ajax-events: [2] - jquery/no-ajax: [0] + jquery/no-ajax: [2] jquery/no-animate: [2] jquery/no-attr: [0] jquery/no-bind: [2] @@ -315,7 +315,7 @@ rules: jquery/no-parent: [0] jquery/no-parents: [0] jquery/no-parse-html: [2] - jquery/no-prop: [0] + jquery/no-prop: [2] jquery/no-proxy: [2] jquery/no-ready: [2] jquery/no-serialize: [2] @@ -396,11 +396,11 @@ rules: no-irregular-whitespace: [2] no-iterator: [2] no-jquery/no-ajax-events: [2] - no-jquery/no-ajax: [0] + no-jquery/no-ajax: [2] no-jquery/no-and-self: [2] no-jquery/no-animate-toggle: [2] no-jquery/no-animate: [2] - no-jquery/no-append-html: [0] + no-jquery/no-append-html: [2] no-jquery/no-attr: [0] no-jquery/no-bind: [2] no-jquery/no-box-model: [2] @@ -466,7 +466,7 @@ rules: no-jquery/no-parse-html: [2] no-jquery/no-parse-json: [2] no-jquery/no-parse-xml: [2] - no-jquery/no-prop: [0] + no-jquery/no-prop: [2] no-jquery/no-proxy: [2] no-jquery/no-ready-shorthand: [2] no-jquery/no-ready: [2] @@ -487,7 +487,7 @@ rules: no-jquery/no-visibility: [2] no-jquery/no-when: [2] no-jquery/no-wrap: [2] - no-jquery/variable-pattern: [0] + no-jquery/variable-pattern: [2] no-label-var: [2] no-labels: [0] # handled by no-restricted-syntax no-lone-blocks: [2] diff --git a/.forgejo/cascading-pr-end-to-end b/.forgejo/cascading-pr-end-to-end index 2350394f2c..d7a6b46b48 100755 --- a/.forgejo/cascading-pr-end-to-end +++ b/.forgejo/cascading-pr-end-to-end @@ -9,9 +9,15 @@ forgejo_pr_or_ref=$4 cd $forgejo full_version=$(make show-version-full) -major_version=$(make show-version-major) +minor_version=$(make show-version-minor) cd $end_to_end + +if ! test -f forgejo/sources/$minor_version ; then + echo "FAIL: forgejo/sources/$minor_version does not exist in the end-to-end repository" + false +fi + date > last-upgrade if test -f "$forgejo_pr_or_ref" ; then @@ -20,11 +26,8 @@ if test -f "$forgejo_pr_or_ref" ; then test "$head_url" != null branch=$(jq --raw-output .head.ref < $forgejo_pr) test "$branch" != null - echo $head_url $branch $full_version > forgejo/sources/$major_version + echo $head_url $branch $full_version > forgejo/sources/$minor_version else forgejo_ref=$forgejo_pr_or_ref - echo $GITHUB_SERVER_URL/$GITHUB_REPOSITORY ${forgejo_ref#refs/heads/} $full_version > forgejo/sources/$major_version + echo $GITHUB_SERVER_URL/$GITHUB_REPOSITORY ${forgejo_ref#refs/heads/} $full_version > forgejo/sources/$minor_version fi - -test "$GITHUB_RUN_NUMBER" -echo $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_NUMBER/artifacts/forgejo > forgejo/binary-url diff --git a/.forgejo/cascading-release-end-to-end b/.forgejo/cascading-release-end-to-end index 7b43c89ed8..08ad8a4431 100755 --- a/.forgejo/cascading-release-end-to-end +++ b/.forgejo/cascading-release-end-to-end @@ -9,4 +9,14 @@ forgejo_ref=$4 cd $end_to_end date > last-upgrade -echo $FORGEJO_BINARY > forgejo/binary-url +organizations=lib/ORGANIZATIONS +if ! test -f $organizations ; then + echo "$organizations file not found" + false +fi +# +# do not include forgejo-experimental so that 7.0-test is found +# in forgejo-integration where it was just built instead of +# forgejo-experimental which was published by the previous build +# +echo forgejo forgejo-integration > $organizations diff --git a/.forgejo/workflows/backport.yml b/.forgejo/workflows/backport.yml new file mode 100644 index 0000000000..59d953ebeb --- /dev/null +++ b/.forgejo/workflows/backport.yml @@ -0,0 +1,81 @@ +# Copyright 2024 The Forgejo Authors +# SPDX-License-Identifier: MIT +# +# To modify this workflow: +# +# - change pull_request_target: to pull_request: +# so that it runs from a pull request instead of the default branch +# +# - push it to the wip-ci-backport branch on the forgejo repository +# otherwise it will not have access to the secrets required to push +# the PR +# +# - open a pull request targetting wip-ci-backport that includes a change +# that can be backported without conflict in v1.21 and set the +# `backport/v1.21` label. +# +# - once it works, open a pull request for the sake of keeping track +# of the change even if the PR won't run it because it will use +# whatever is in the default branch instead +# +# - after it is merged, double check it works by setting a +# `backport/v1.21` label on a merged pull request that can be backported +# without conflict. +# +on: + pull_request_target: + types: + - closed + - labeled + +jobs: + backporting: + if: > + !startsWith(vars.ROLE, 'forgejo-') && ( + github.event.pull_request.merged + && ( + github.event.action == 'closed' + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport/') + ) + ) + ) + runs-on: docker + container: + image: 'docker.io/node:20-bookworm' + steps: + - name: Fetch labels + id: fetch-labels + shell: bash + run: | + set -x + echo "Labels retrieved below" + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get -q install -qq -y jq + filtered_labels=$(echo "$LABELS" | jq -c 'map(select(.name | startswith("backport/")))') + echo "FILTERED_LABELS=${filtered_labels}" >> $GITHUB_ENV + env: + LABELS: ${{ toJSON(github.event.pull_request.labels) }} + - name: Extract targets + id: extract-targets + shell: bash + run: | + set -x + targets="$(echo $FILTERED_LABELS | jq -c '[.[] | .name | sub("backport/"; "")]')" + echo "targets=$(echo $targets)" >> $GITHUB_OUTPUT + + - name: Printing info + shell: bash + run: | + echo "targets: ${{ steps.extract-targets.outputs.targets }}" + echo "target-branch: ${{ fromJSON(steps.extract-targets.outputs.targets)[0] }}" + echo "pull-request: ${{ github.event.pull_request.url }}" + + - uses: https://code.forgejo.org/actions/git-backporting@v4.5.2 + with: + target-branch: ${{ fromJSON(steps.extract-targets.outputs.targets)[0] }}/forgejo + no-squash: true + auth: ${{ secrets.BACKPORT_TOKEN }} + pull-request: ${{ github.event.pull_request.url }} diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index c012991b3a..19423205af 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -203,11 +203,9 @@ jobs: destination-url: https://code.forgejo.org destination-fork-repo: ${{ vars.CASCADE_DESTINATION_DOER }}/end-to-end destination-repo: forgejo/end-to-end - destination-branch: forgejo-pr + destination-branch: main destination-token: ${{ secrets.CASCADE_DESTINATION_TOKEN }} update: .forgejo/cascading-release-end-to-end - env: - FORGEJO_BINARY: "${{ env.GITHUB_SERVER_URL }}/${{ github.repository }}/releases/download/v${{ steps.release-info.outputs.version }}/forgejo-${{ steps.release-info.outputs.version }}-linux-amd64" - name: copy to experimental if: vars.ROLE == 'forgejo-integration' && secrets.TOKEN != '' diff --git a/.forgejo/workflows/cascade-setup-end-to-end.yml b/.forgejo/workflows/cascade-setup-end-to-end.yml index 85871ec31d..dcca2404d9 100644 --- a/.forgejo/workflows/cascade-setup-end-to-end.yml +++ b/.forgejo/workflows/cascade-setup-end-to-end.yml @@ -42,45 +42,6 @@ jobs: ${{ toJSON(github.event) }} EOF - build: - if: > - !startsWith(vars.ROLE, 'forgejo-') && ( - github.event_name == 'push' || - ( - github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'run-end-to-end-tests') - ) - ) - runs-on: docker - container: - image: 'docker.io/node:20-bookworm' - steps: - - uses: https://code.forgejo.org/actions/checkout@v3 - with: - fetch-depth: '0' - show-progress: 'false' - - name: adduser forgejo - run: | - git config --add safe.directory '*' - git config user.email "you@example.com" - git config user.name "Your Name" - adduser --quiet --comment forgejo --disabled-password forgejo - chown -R forgejo:forgejo . - - uses: https://code.forgejo.org/actions/setup-go@v4 - with: - go-version: "1.21" - - name: make deps-backend - run: | - su forgejo -c 'make deps-backend' - - name: make forgejo - run: | - su forgejo -c 'make generate-backend static-executable && ln gitea forgejo' - env: - TAGS: bindata sqlite sqlite_unlock_notify - - uses: actions/upload-artifact@v3 - with: - name: forgejo - path: forgejo - cascade: if: > !startsWith(vars.ROLE, 'forgejo-') && ( @@ -89,7 +50,6 @@ jobs: github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'run-end-to-end-tests') ) ) - needs: [build] runs-on: docker container: image: node:20-bookworm @@ -108,7 +68,7 @@ jobs: destination-url: https://code.forgejo.org destination-fork-repo: cascading-pr/end-to-end destination-repo: forgejo/end-to-end - destination-branch: forgejo-pr + destination-branch: main destination-token: ${{ secrets.END_TO_END_CASCADING_PR_DESTINATION }} close-merge: true update: .forgejo/cascading-pr-end-to-end diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index 80fd87152e..85d7691cd1 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -21,8 +21,6 @@ jobs: check-latest: true - run: make deps-backend deps-tools - run: make --always-make -j$(nproc) lint-backend checks-backend # ensure the "go-licenses" make target runs - env: - TAGS: bindata sqlite sqlite_unlock_notify frontend-checks: if: ${{ !startsWith(vars.ROLE, 'forgejo-') }} runs-on: docker @@ -43,8 +41,11 @@ jobs: image: 'docker.io/node:20-bookworm' services: minio: - image: 'docker.io/bitnami/minio:2023.8.31' + image: bitnami/minio:2024.2.26 + options: >- + --hostname gitea.minio env: + MINIO_DOMAIN: minio MINIO_ROOT_USER: 123456 MINIO_ROOT_PASSWORD: 12345678 steps: @@ -130,10 +131,10 @@ jobs: image: 'docker.io/node:20-bookworm' services: minio: - image: bitnami/minio:2021.3.17 + image: bitnami/minio:2024.2.26 env: - MINIO_ACCESS_KEY: 123456 - MINIO_SECRET_KEY: 12345678 + MINIO_ROOT_USER: 123456 + MINIO_ROOT_PASSWORD: 12345678 pgsql: image: 'docker.io/postgres:15' env: diff --git a/Makefile b/Makefile index bc3837f065..6735ffd1a5 100644 --- a/Makefile +++ b/Makefile @@ -91,9 +91,11 @@ STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null) ifneq ($(STORED_VERSION),) FORGEJO_VERSION ?= $(STORED_VERSION) else - FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//')+${GITEA_COMPATIBILITY} + # drop the "g" prefix prepended by git describe to the commit hash + FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//' | sed 's/\-g/-/')+${GITEA_COMPATIBILITY} endif FORGEJO_VERSION_MAJOR=$(shell echo $(FORGEJO_VERSION) | sed -e 's/\..*//') +FORGEJO_VERSION_MINOR=$(shell echo $(FORGEJO_VERSION) | sed -E -e 's/^([0-9]+\.[0-9]+).*/\1/') show-version-full: @echo ${FORGEJO_VERSION} @@ -101,6 +103,9 @@ show-version-full: show-version-major: @echo ${FORGEJO_VERSION_MAJOR} +show-version-minor: + @echo ${FORGEJO_VERSION_MINOR} + RELEASE_VERSION ?= ${FORGEJO_VERSION} VERSION ?= ${RELEASE_VERSION} @@ -313,12 +318,8 @@ fmt: .PHONY: fmt-check fmt-check: fmt - @diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \ - if [ -n "$$diff" ]; then \ - echo "Please run 'make fmt' and commit the result:"; \ - echo "$${diff}"; \ - exit 1; \ - fi + @git diff --exit-code --color=always $(GO_SOURCES) templates $(WEB_DIRS) \ + || (code=$$?; echo "Please run 'make fmt' and commit the result"; exit $${code}) .PHONY: $(TAGS_EVIDENCE) $(TAGS_EVIDENCE): @@ -339,12 +340,8 @@ generate-forgejo-api: $(FORGEJO_API_SPEC) .PHONY: forgejo-api-check forgejo-api-check: generate-forgejo-api - @diff=$$(git diff $(FORGEJO_API_SERVER) ; \ - if [ -n "$$diff" ]; then \ - echo "Please run 'make generate-forgejo-api' and commit the result:"; \ - echo "$${diff}"; \ - exit 1; \ - fi + @git diff --exit-code --color=always $(FORGEJO_API_SERVER) \ + || (code=$$?; echo "Please run 'make generate-forgejo-api' and commit the result"; exit $${code}) .PHONY: forgejo-api-validate forgejo-api-validate: @@ -361,12 +358,8 @@ $(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA) .PHONY: swagger-check swagger-check: generate-swagger - @diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \ - if [ -n "$$diff" ]; then \ - echo "Please run 'make generate-swagger' and commit the result:"; \ - echo "$${diff}"; \ - exit 1; \ - fi + @git diff --exit-code --color=always '$(SWAGGER_SPEC)' \ + || (code=$$?; echo "Please run 'make generate-swagger' and commit the result"; exit $${code}) .PHONY: swagger-validate swagger-validate: @@ -437,11 +430,8 @@ lint-spell-fix: lint-go: $(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GOLANGCI_LINT_ARGS) $(GO) run $(DEADCODE_PACKAGE) -generated=false -test code.gitea.io/gitea > .cur-deadcode-out - @$(DIFF) .deadcode-out .cur-deadcode-out; \ - if [ $$? -eq 1 ]; then \ - echo "Please run 'make lint-go-fix' and commit the result"; \ - exit 1; \ - fi + @$(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: @@ -541,12 +531,8 @@ vendor: go.mod go.sum .PHONY: tidy-check tidy-check: tidy - @diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \ - if [ -n "$$diff" ]; then \ - echo "Please run 'make tidy' and commit the result:"; \ - echo "$${diff}"; \ - exit 1; \ - fi + @git diff --exit-code --color=always go.mod go.sum $(GO_LICENSE_FILE) \ + || (code=$$?; echo "Please run 'make tidy' and commit the result"; exit $${code}) .PHONY: go-licenses go-licenses: $(GO_LICENSE_FILE) @@ -899,10 +885,6 @@ release-sources: | $(DIST_DIRS) release-docs: | $(DIST_DIRS) docs tar -czf $(DIST)/release/gitea-docs-$(VERSION).tar.gz -C ./docs . -.PHONY: docs -docs: - cd docs; bash scripts/trans-copy.sh; - .PHONY: deps deps: deps-frontend deps-backend deps-tools deps-py @@ -961,6 +943,7 @@ fomantic: cd $(FOMANTIC_WORK_DIR) && npm install --no-save cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/ + $(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build # fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event $(SED_INPLACE) -e 's/clickEvent[ \t]*=/clickEvent = "click", unstableClickEvent =/g' $(FOMANTIC_WORK_DIR)/build/semantic.js @@ -984,23 +967,14 @@ svg: node-check | node_modules .PHONY: svg-check svg-check: svg @git add $(SVG_DEST_DIR) - @diff=$$(git diff --color=always --cached $(SVG_DEST_DIR)); \ - if [ -n "$$diff" ]; then \ - echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \ - echo "$${diff}"; \ - exit 1; \ - fi + @git diff --exit-code --color=always --cached $(SVG_DEST_DIR) \ + || (code=$$?; echo "Please run 'make svg' and commit the result"; exit $${code}) .PHONY: lockfile-check lockfile-check: npm install --package-lock-only - @diff=$$(git diff --color=always package-lock.json); \ - if [ -n "$$diff" ]; then \ - echo "package-lock.json is inconsistent with package.json"; \ - echo "Please run 'npm install --package-lock-only' and commit the result:"; \ - echo "$${diff}"; \ - exit 1; \ - fi + @git diff --exit-code --color=always package-lock.json \ + || (code=$$?; echo "Please run 'npm install --package-lock-only' and commit the result"; exit $${code}) .PHONY: update-translations update-translations: diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 4e87078811..228511989a 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,6 +4,40 @@ A Forgejo release is published shortly after a Gitea release is published and th The Forgejo admin should carefully read the required manual actions before upgrading. A point release (e.g. v1.21.1-0 or v1.21.2-0) does not require manual actions but others might (e.g. v1.20, v1.21). +## 1.21.8-0 + +The [complete list of commits](https://codeberg.org/forgejo/forgejo/commits/branch/v1.21/forgejo) included in the `Forgejo v1.21.8-0` release can be reviewed from the command line with: + +```shell +$ git clone https://codeberg.org/forgejo/forgejo/ +$ git -C forgejo log --oneline --no-merges v1.21.7-0..v1.21.8-0 +``` + +This stable release contains bug fixes. + +* Recommended Action + + We recommend that all Forgejo installations are [upgraded](https://forgejo.org/docs/v1.21/admin/upgrade/) to the latest version. + +* [Forgejo Semantic Version](https://forgejo.org/docs/v1.21/user/semver/) + + The semantic version was updated to `6.0.8+0-gitea-1.21.8` + +* Bug fixes + + The most prominent ones are described here, others can be found in the list of commits included in the release as described above. + + * [Fix `/api/v1/{owner}/{repo}/issue_templates`](https://codeberg.org/forgejo/forgejo/commit/969d3f44101402afd9dd848e79dd5823d547a00d) which was always failing with a 500 error. + * [Prevent error 500 on /user/settings/security when SignedUser has a linked account from a deactivated authentication source](https://codeberg.org/forgejo/forgejo/commit/d9418651af8c8a3276ebc40a516109c0f33139b0). + * [Fix error 500 when pushing release to an empty repo](https://codeberg.org/forgejo/forgejo/commit/b76f370a3f8993638cad91547491b46631776f59). + * [Fix incorrect rendering csv file when file size is larger than UI.CSV.MaxFileSize](https://codeberg.org/forgejo/forgejo/commit/e151e0467341bd25a2465ca15e81ebc0c8fa3fc4). + * [Fix error 500 when deleting account with incorrect password or unsupported login type](https://codeberg.org/forgejo/forgejo/commit/66061d28286ee3adf69747c284d40121e2e4b280). + * [handle user-defined `name` anchors like `[Link](#link)` linking to `Link`](https://codeberg.org/forgejo/forgejo/commit/03caefbb02854d5c98a26d889e0e30c910651395). + * [Use correct head commit for CODEOWNER](https://codeberg.org/forgejo/forgejo/commit/d1cebb0e884a88941e27e2280e117cc04d2665fe). + * [Fix manual merge button](https://codeberg.org/forgejo/forgejo/commit/c70719c59c410e5378c04926c66461d7b9f72884). + * [Make meilisearch do exact search for issues](https://codeberg.org/forgejo/forgejo/commit/a876ac2c7934fa0f8a66424e178b501e4a341e0f). + * [Fix PR creation via api between branches of same repo with head field namespaced](https://codeberg.org/forgejo/forgejo/commit/120a173e24fd359b0b57f2bd1021168645fab5a8). + ## 1.21.7-0 The [complete list of commits](https://codeberg.org/forgejo/forgejo/commits/branch/v1.21/forgejo) included in the `Forgejo v1.21.7-0` release can be reviewed from the command line with: diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 2aab21595b..ad3ab6c4c1 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -34,6 +34,11 @@ "path": "dario.cat/mergo/LICENSE", "licenseText": "Copyright (c) 2013 Dario Castañé. All rights reserved.\nCopyright (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": "filippo.io/edwards25519", + "path": "filippo.io/edwards25519/LICENSE", + "licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + }, { "name": "git.sr.ht/~mariusor/go-xsd-duration", "path": "git.sr.ht/~mariusor/go-xsd-duration/LICENSE", diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index a257ce21c8..10965c7e8f 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -47,7 +47,8 @@ var microcmdUserCreate = &cli.Command{ }, &cli.BoolFlag{ Name: "must-change-password", - Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)", + Usage: "Set this option to false to prevent forcing the user to change their password after initial login", + Value: true, }, &cli.IntFlag{ Name: "random-password-length", @@ -110,8 +111,7 @@ func runCreateUser(c *cli.Context) error { return errors.New("must set either password or random-password flag") } - // always default to true - changePassword := true + changePassword := c.Bool("must-change-password") // If this is the first user being created. // Take it as the admin and don't force a password update. @@ -119,10 +119,6 @@ func runCreateUser(c *cli.Context) error { changePassword = false } - if c.IsSet("must-change-password") { - changePassword = c.Bool("must-change-password") - } - restricted := optional.None[bool]() if c.IsSet("restricted") { diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 4d077643f5..9853a8e050 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1496,10 +1496,11 @@ LEVEL = Info ;; ;; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled ;DEFAULT_EMAIL_NOTIFICATIONS = enabled -;; Disabled features for users, could be "deletion","manage_gpg_keys" more features can be disabled in future +;; 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", 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 ;USER_DISABLED_FEATURES = @@ -2622,7 +2623,7 @@ LEVEL = Info ;ENDLESS_TASK_TIMEOUT = 3h ;; Timeout to cancel the jobs which have waiting status, but haven't been picked by a runner for a long time ;ABANDONED_JOB_TIMEOUT = 24h -;; Strings committers can place inside a commit message to skip executing the corresponding actions workflow +;; 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] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index ea6e1eb1a4..8ca1dd3b2b 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -518,9 +518,10 @@ And the following unique queues: - `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**: Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled - `DISABLE_REGULAR_ORG_CREATION`: **false**: Disallow regular (non-admin) users from creating organizations. -- `USER_DISABLED_FEATURES`: **_empty_** Disabled features for users, could be `deletion`, `manage_gpg_keys` and more features can be added in future. +- `USER_DISABLED_FEATURES`: **_empty_** Disabled features for users, could be `deletion`, `manage_ssh_keys`, `manage_gpg_keys` and more features can be added in future. - `deletion`: User cannot delete their own account. - - `manage_gpg_keys`: User cannot configure gpg keys + - `manage_ssh_keys`: User cannot configure ssh keys. + - `manage_gpg_keys`: User cannot configure gpg keys. ## Security (`security`) @@ -831,7 +832,7 @@ Default templates for project boards: ## Issue and pull request attachments (`attachment`) - `ENABLED`: **true**: Whether issue and pull request attachments are enabled. -- `ALLOWED_TYPES`: **.csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types. +- `ALLOWED_TYPES`: **.cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types. - `MAX_SIZE`: **2048**: Maximum size (MB). - `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once. - `STORAGE_TYPE`: **local**: Storage type for attachments, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]` @@ -841,6 +842,10 @@ Default templates for project boards: - `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when STORAGE_TYPE is `minio` - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio` - `MINIO_BUCKET`: **gitea**: Minio bucket to store the attachments only available when STORAGE_TYPE is `minio` +- `MINIO_BUCKET_LOOKUP`: **auto**: Minio bucket lookup type only available when `STORAGE_TYPE` is `minio` + - `auto` Auto detect + - `dns` Virtual Host Style bucket lookup + - `path` Path style bucket lookup - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when STORAGE_TYPE is `minio` - `MINIO_BASE_PATH`: **attachments/**: Minio base path on the bucket only available when STORAGE_TYPE is `minio` - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when STORAGE_TYPE is `minio` @@ -1271,6 +1276,10 @@ is `data/lfs` and the default of `MINIO_BASE_PATH` is `lfs/`. - `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio` - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio` - `MINIO_BUCKET`: **gitea**: Minio bucket to store the lfs only available when `STORAGE_TYPE` is `minio` +- `MINIO_BUCKET_LOOKUP`: **auto**: Minio bucket lookup type only available when `STORAGE_TYPE` is `minio` + - `auto` Auto detect + - `dns` Virtual Host Style bucket lookup + - `path` Path style bucket lookup - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio` - `MINIO_BASE_PATH`: **lfs/**: Minio base path on the bucket only available when `STORAGE_TYPE` is `minio` - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio` @@ -1286,6 +1295,10 @@ Default storage configuration for attachments, lfs, avatars, repo-avatars, repo- - `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio` - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio` - `MINIO_BUCKET`: **gitea**: Minio bucket to store the data only available when `STORAGE_TYPE` is `minio` +- `MINIO_BUCKET_LOOKUP`: **auto**: Minio bucket lookup type only available when `STORAGE_TYPE` is `minio` + - `auto` Auto detect + - `dns` Virtual Host Style bucket lookup + - `path` Path style bucket lookup - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio` - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio` - `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio` @@ -1371,6 +1384,10 @@ is `data/repo-archive` and the default of `MINIO_BASE_PATH` is `repo-archive/`. - `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio` - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey to connect only available when `STORAGE_TYPE is` `minio` - `MINIO_BUCKET`: **gitea**: Minio bucket to store the lfs only available when `STORAGE_TYPE` is `minio` +- `MINIO_BUCKET_LOOKUP`: **auto**: Minio bucket lookup type only available when `STORAGE_TYPE` is `minio` + - `auto` Auto detect + - `dns` Virtual Host Style bucket lookup + - `path` Path style bucket lookup - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket only available when `STORAGE_TYPE` is `minio` - `MINIO_BASE_PATH`: **repo-archive/**: Minio base path on the bucket only available when `STORAGE_TYPE` is `minio` - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio` @@ -1405,7 +1422,7 @@ PROXY_HOSTS = *.github.com - `ZOMBIE_TASK_TIMEOUT`: **10m**: Timeout to stop the task which have running status, but haven't been updated for a long time - `ENDLESS_TASK_TIMEOUT`: **3h**: Timeout to stop the tasks which have running status and continuous updates, but don't end for a long time - `ABANDONED_JOB_TIMEOUT`: **24h**: Timeout to cancel the jobs which have waiting status, but haven't been picked by a runner for a long time -- `SKIP_WORKFLOW_STRINGS`: **[skip ci],[ci skip],[no ci],[skip actions],[actions skip]**: Strings committers can place inside a commit message to skip executing the corresponding actions workflow +- `SKIP_WORKFLOW_STRINGS`: **[skip ci],[ci skip],[no ci],[skip actions],[actions skip]**: Strings committers can place inside a commit message or PR title to skip executing the corresponding actions workflow `DEFAULT_ACTIONS_URL` indicates where the Gitea Actions runners should find the actions with relative path. For example, `uses: actions/checkout@v4` means `https://github.com/actions/checkout@v4` since the value of `DEFAULT_ACTIONS_URL` is `github`. diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md index 5cc5734359..f636927da6 100644 --- a/docs/content/administration/config-cheat-sheet.zh-cn.md +++ b/docs/content/administration/config-cheat-sheet.zh-cn.md @@ -497,9 +497,10 @@ Gitea 创建以下非唯一队列: - `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**:用户电子邮件通知的默认配置(用户可配置)。选项:enabled、onmention、disabled - `DISABLE_REGULAR_ORG_CREATION`: **false**:禁止普通(非管理员)用户创建组织。 -- `USER_DISABLED_FEATURES`:**_empty_** 禁用的用户特性,当前允许为空或者 `deletion`,`manage_gpg_keys` 未来可以增加更多设置。 +- `USER_DISABLED_FEATURES`:**_empty_** 禁用的用户特性,当前允许为空或者 `deletion`,`manage_ssh_keys`, `manage_gpg_keys` 未来可以增加更多设置。 - `deletion`: 用户不能通过界面或者API删除他自己。 - - `manage_gpg_keys`: 用户不能配置 GPG 密钥 + - `manage_ssh_keys`: 用户不能通过界面或者API配置SSH Keys。 + - `manage_gpg_keys`: 用户不能配置 GPG 密钥。 ## 安全性 (`security`) @@ -781,7 +782,7 @@ Gitea 创建以下非唯一队列: ## 工单和合并请求的附件 (`attachment`) - `ENABLED`: **true**: 是否允许用户上传附件。 -- `ALLOWED_TYPES`: **.csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: 允许的文件扩展名(`.zip`)、mime 类型(`text/plain`)或通配符类型(`image/*`、`audio/*`、`video/*`)的逗号分隔列表。空值或 `*/*` 允许所有类型。 +- `ALLOWED_TYPES`: **.cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: 允许的文件扩展名(`.zip`)、mime 类型(`text/plain`)或通配符类型(`image/*`、`audio/*`、`video/*`)的逗号分隔列表。空值或 `*/*` 允许所有类型。 - `MAX_SIZE`: **2048**: 附件的最大限制(MB)。 - `MAX_FILES`: **5**: 一次最多上传的附件数量。 - `STORAGE_TYPE`: **local**: 附件的存储类型,`local` 表示本地磁盘,`minio` 表示兼容 S3 的对象存储服务,如果未设置将使用默认值 `local` 或其他在 `[storage.xxx]` 中定义的名称。 @@ -791,6 +792,10 @@ Gitea 创建以下非唯一队列: - `MINIO_ACCESS_KEY_ID`: Minio accessKeyID 以连接,仅当 STORAGE_TYPE 为 `minio` 时可用。 - `MINIO_SECRET_ACCESS_KEY`: Minio secretAccessKey 以连接,仅当 STORAGE_TYPE 为 `minio` 时可用。 - `MINIO_BUCKET`: **gitea**: Minio 存储附件的存储桶,仅当 STORAGE_TYPE 为 `minio` 时可用。 +- `MINIO_BUCKET_LOOKUP`: **auto**: Minio 存储桶寻址方式, 仅当 `STORAGE_TYPE` 为 `minio` 时可用。 + - `auto` 自动检测 + - `dns` 子域名寻址 + - `path` 路径寻址 - `MINIO_LOCATION`: **us-east-1**: Minio 存储桶的位置以创建,仅当 STORAGE_TYPE 为 `minio` 时可用。 - `MINIO_BASE_PATH`: **attachments/**: Minio 存储桶上的基本路径,仅当 STORAGE_TYPE 为 `minio` 时可用。 - `MINIO_USE_SSL`: **false**: Minio 启用 SSL,仅当 STORAGE_TYPE 为 `minio` 时可用。 @@ -1206,6 +1211,10 @@ ALLOW_DATA_URI_IMAGES = true - `MINIO_ACCESS_KEY_ID`:Minio 的 accessKeyID,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 - `MINIO_SECRET_ACCESS_KEY`:Minio 的 secretAccessKey,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 - `MINIO_BUCKET`:**gitea**:用于存储 lfs 的 Minio 桶,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 +- `MINIO_BUCKET_LOOKUP`: **auto**: Minio 存储桶寻址方式,可选值为 `auto`, `dns`, `path` 仅当 `STORAGE_TYPE` 为 `minio` 时可用。 + - `auto` 自动检测 + - `dns` 子域名寻址 + - `path` 路径寻址 - `MINIO_LOCATION`:**us-east-1**:创建桶的 Minio 位置,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 - `MINIO_BASE_PATH`:**lfs/**:桶上的 Minio 基本路径,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 - `MINIO_USE_SSL`:**false**:Minio 启用 ssl,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 @@ -1221,6 +1230,10 @@ ALLOW_DATA_URI_IMAGES = true - `MINIO_ACCESS_KEY_ID`:Minio 的 accessKeyID,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 - `MINIO_SECRET_ACCESS_KEY`:Minio 的 secretAccessKey,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 - `MINIO_BUCKET`:**gitea**:用于存储数据的 Minio 桶,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 +- `MINIO_BUCKET_LOOKUP`: **auto**: Minio 存储桶寻址方式,可选值为 `auto`, `dns`, `path` 仅当 `STORAGE_TYPE` 为 `minio` 时可用。 + - `auto` 自动检测 + - `dns` 子域名寻址 + - `path` 路径寻址 - `MINIO_LOCATION`:**us-east-1**:创建桶的 Minio 位置,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 - `MINIO_USE_SSL`:**false**:Minio 启用 ssl,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 - `MINIO_INSECURE_SKIP_VERIFY`:**false**:Minio 跳过 SSL 验证,仅在 `STORAGE_TYPE` 为 `minio` 时可用。 @@ -1304,6 +1317,10 @@ MINIO_INSECURE_SKIP_VERIFY = false - `MINIO_ACCESS_KEY_ID`: Minio的accessKeyID,仅在`STORAGE_TYPE`为`minio`时可用。 - `MINIO_SECRET_ACCESS_KEY`: Minio的secretAccessKey,仅在`STORAGE_TYPE`为`minio`时可用。 - `MINIO_BUCKET`: **gitea**:用于存储归档的Minio存储桶,仅在`STORAGE_TYPE`为`minio`时可用。 +- `MINIO_BUCKET_LOOKUP`: **auto**: Minio 存储桶寻址方式,可选值为 `auto`, `dns`, `path` 仅当 `STORAGE_TYPE` 为 `minio` 时可用。 + - `auto` 自动检测 + - `dns` 子域名寻址 + - `path` 路径寻址 - `MINIO_LOCATION`: **us-east-1**:用于创建存储桶的Minio位置,仅在`STORAGE_TYPE`为`minio`时可用。 - `MINIO_BASE_PATH`: **repo-archive/**:存储桶上的Minio基本路径,仅在`STORAGE_TYPE`为`minio`时可用。 - `MINIO_USE_SSL`: **false**:启用Minio的SSL,仅在`STORAGE_TYPE`为`minio`时可用。 diff --git a/docs/content/administration/mail-templates.en-us.md b/docs/content/administration/mail-templates.en-us.md index 0154fe55d0..8e4e416e8d 100644 --- a/docs/content/administration/mail-templates.en-us.md +++ b/docs/content/administration/mail-templates.en-us.md @@ -163,7 +163,7 @@ clients don't even support HTML, so they show the text version included in the g If the template fails to render, it will be noticed only at the moment the mail is sent. A default subject is used if the subject template fails, and whatever was rendered successfully -from the the _mail body_ is used, disregarding the rest. +from the _mail body_ is used, disregarding the rest. Please check [Gitea's logs](administration/logging-config.md) for error messages in case of trouble. @@ -224,7 +224,7 @@ Please check [Gitea's logs](administration/logging-config.md) for error messages {{if not (eq .Body "")}}

Message content


- {{.Body | SanitizeHTML}} + {{.Body}} {{end}}


diff --git a/docs/content/administration/mail-templates.zh-cn.md b/docs/content/administration/mail-templates.zh-cn.md index e8c2817336..3c7c2a9397 100644 --- a/docs/content/administration/mail-templates.zh-cn.md +++ b/docs/content/administration/mail-templates.zh-cn.md @@ -207,7 +207,7 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/ {{if not (eq .Body "")}}

消息内容:


- {{.Body | SanitizeHTML}} + {{.Body}} {{end}}


diff --git a/docs/content/contributing/guidelines-frontend.en-us.md b/docs/content/contributing/guidelines-frontend.en-us.md index edd89e1231..2637780718 100644 --- a/docs/content/contributing/guidelines-frontend.en-us.md +++ b/docs/content/contributing/guidelines-frontend.en-us.md @@ -47,7 +47,7 @@ We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/h 9. Avoid unnecessary `!important` in CSS, add comments to explain why it's necessary if it can't be avoided. 10. Avoid mixing different events in one event listener, prefer to use individual event listeners for every event. 11. Custom event names are recommended to use `ce-` prefix. -12. Gitea's tailwind-style CSS classes use `gt-` prefix (`gt-relative`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`). +12. Prefer using Tailwind CSS which is available via `tw-` prefix, e.g. `tw-relative`. Gitea's helper CSS classes use `gt-` prefix (`gt-df`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`). 13. Avoid inline scripts & styles as much as possible, it's recommended to put JS code into JS files and use CSS classes. If inline scripts & styles are unavoidable, explain the reason why it can't be avoided. ### Accessibility / ARIA diff --git a/docs/content/contributing/guidelines-frontend.zh-cn.md b/docs/content/contributing/guidelines-frontend.zh-cn.md index 66a4d4b4d6..ace0d97f49 100644 --- a/docs/content/contributing/guidelines-frontend.zh-cn.md +++ b/docs/content/contributing/guidelines-frontend.zh-cn.md @@ -34,7 +34,7 @@ HTML 页面由[Go HTML Template](https://pkg.go.dev/html/template)渲染。 我们推荐使用[Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html)和[Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)。 -## Gitea 特定准则: +## Gitea 特定准则 1. 每个功能(Fomantic-UI/jQuery 模块)应放在单独的文件/目录中。 2. HTML 的 id 和 class 应使用 kebab-case,最好包含2-3个与功能相关的关键词。 @@ -47,7 +47,8 @@ HTML 页面由[Go HTML Template](https://pkg.go.dev/html/template)渲染。 9. 避免在 CSS 中使用不必要的`!important`,如果无法避免,添加注释解释为什么需要它。 10. 避免在一个事件监听器中混合不同的事件,优先为每个事件使用独立的事件监听器。 11. 推荐使用自定义事件名称前缀`ce-`。 -12. Gitea 的 tailwind-style CSS 类使用`gt-`前缀(`gt-relative`),而 Gitea 自身的私有框架级 CSS 类使用`g-`前缀(`g-modal-confirm`)。 +12. 建议使用 Tailwind CSS,它可以通过 `tw-` 前缀获得,例如 `tw-relative`. Gitea 自身的助手类 CSS 使用 `gt-` 前缀(`gt-df`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。 +13. 尽量避免内联脚本和样式,建议将JS代码放入JS文件中并使用CSS类。如果内联脚本和样式不可避免,请解释无法避免的原因。 ### 可访问性 / ARIA @@ -64,18 +65,21 @@ Gitea使用一些补丁使Fomantic UI更具可访问性(参见`aria.js`和`ari * Vue + Vanilla JS * Fomantic-UI(jQuery) +* htmx (部分页面重新加载其他静态组件) * Vanilla JS 不推荐的实现方式: * Vue + Fomantic-UI(jQuery) * jQuery + Vanilla JS +* htmx + 任何其他需要大量 JavaScript 代码或不必要的功能,如 htmx 脚本 (`hx-on`) 为了保持界面一致,Vue 组件可以使用 Fomantic-UI 的 CSS 类。 尽管不建议混合使用不同的框架, +我们使用 htmx 进行简单的交互。您可以在此 [PR](https://github.com/go-gitea/gitea/pull/28908) 中查看一个简单交互的示例,其中应使用 htmx。如果您需要更高级的反应性,请不要使用 htmx,请使用其他框架(Vue/Vanilla JS)。 但如果混合使用是必要的,并且代码设计良好且易于维护,也可以工作。 -### async 函数 +### `async` 函数 只有当函数内部存在`await`调用或返回`Promise`时,才将函数标记为`async`。 @@ -91,6 +95,12 @@ Gitea使用一些补丁使Fomantic UI更具可访问性(参见`aria.js`和`ari 这是有意为之的,我们想调用异步函数并忽略Promise。 一些 lint 规则和 IDE 也会在未处理返回的 Promise 时发出警告。 +### 获取数据 + +要获取数据,请使用`modules/fetch.js`中的包装函数`GET`、`POST`等。他们 +接受内容的`data`选项,将自动设置 CSRF 令牌并返回 +[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response)。 + ### HTML 属性和 dataset 禁止使用`dataset`,它的驼峰命名行为使得搜索属性变得困难。 @@ -132,3 +142,7 @@ Gitea使用一些补丁使Fomantic UI更具可访问性(参见`aria.js`和`ari ### Vue3 和 JSX Gitea 现在正在使用 Vue3。我们决定不引入 JSX,以保持 HTML 代码和 JavaScript 代码分离。 + +### UI示例 + +Gitea 使用一些自制的 UI 元素并自定义其他元素,以将它们更好地集成到通用 UI 方法中。当在开发模式(`RUN_MODE=dev`)下运行 Gitea 时,在 `http(s)://your-gitea-url:port/devtest` 下会提供一个包含一些标准化 UI 示例的页面。 diff --git a/docs/content/development/hacking-on-gitea.en-us.md b/docs/content/development/hacking-on-gitea.en-us.md index df8a9047d6..982dbcf6ea 100644 --- a/docs/content/development/hacking-on-gitea.en-us.md +++ b/docs/content/development/hacking-on-gitea.en-us.md @@ -333,14 +333,9 @@ Documentation for the website is found in `docs/`. If you change this you can test your changes to ensure that they pass continuous integration using: ```bash -# from the docs directory within Gitea -make trans-copy clean build +make lint-md ``` -You will require a copy of [Hugo](https://gohugo.io/) to run this task. Please -note: this may generate a number of untracked Git objects, which will need to -be cleaned up. - ## Visual Studio Code A `launch.json` and `tasks.json` are provided within `contrib/ide/vscode` for diff --git a/docs/content/development/hacking-on-gitea.zh-cn.md b/docs/content/development/hacking-on-gitea.zh-cn.md index 2dba3c92b6..a31e1dc511 100644 --- a/docs/content/development/hacking-on-gitea.zh-cn.md +++ b/docs/content/development/hacking-on-gitea.zh-cn.md @@ -307,13 +307,9 @@ TAGS="bindata sqlite sqlite_unlock_notify" make build test-sqlite 该网站的文档位于 `docs/` 中。如果你改变了文档内容,你可以使用以下测试方法进行持续集成: ```bash -# 来自 Gitea 中的 docs 目录 -make trans-copy clean build +make lint-md ``` -运行此任务依赖于 [Hugo](https://gohugo.io/)。请注意:这可能会生成一些未跟踪的 Git 对象, -需要被清理干净。 - ## Visual Studio Code `contrib/ide/vscode` 中为 Visual Studio Code 提供了 `launch.json` 和 `tasks.json`。查看 diff --git a/docs/scripts/trans-copy.sh b/docs/scripts/trans-copy.sh deleted file mode 100755 index 7374ab9e73..0000000000 --- a/docs/scripts/trans-copy.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -e - -# -# This script is used to copy the en-US content to our available locales as a -# fallback to always show all pages when displaying a specific locale that is -# missing some documents to be translated. -# -# Just execute the script without any argument and you will get the missing -# files copied into the content folder. We are calling this script within the CI -# server simply by `make trans-copy`. -# - -declare -a LOCALES=( - "fr-fr" - "nl-nl" - "pt-br" - "zh-cn" - "zh-tw" -) - -ROOT=$(realpath $(dirname $0)/..) - -for SOURCE in $(find ${ROOT}/content -type f -iname *.en-us.md); do - for LOCALE in "${LOCALES[@]}"; do - DEST="${SOURCE%.en-us.md}.${LOCALE}.md" - - if [[ ! -f ${DEST} ]]; then - cp ${SOURCE} ${DEST} - sed -i.bak "s/en\-us/${LOCALE}/g" ${DEST} - rm ${DEST}.bak - fi - done -done diff --git a/go.mod b/go.mod index 788bb9f392..cc62681c37 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/PuerkitoBio/goquery v1.8.1 - github.com/alecthomas/chroma/v2 v2.12.0 + github.com/alecthomas/chroma/v2 v2.13.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.3.10 github.com/buildkite/terminal-to-html/v3 v3.10.1 @@ -45,9 +45,9 @@ require ( github.com/go-git/go-billy/v5 v5.5.0 github.com/go-git/go-git/v5 v5.11.0 github.com/go-ldap/ldap/v3 v3.4.6 - github.com/go-sql-driver/mysql v1.7.1 + github.com/go-sql-driver/mysql v1.8.0 github.com/go-swagger/go-swagger v0.30.5 - github.com/go-testfixtures/testfixtures/v3 v3.9.0 + github.com/go-testfixtures/testfixtures/v3 v3.10.0 github.com/go-webauthn/webauthn v0.10.0 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f @@ -55,7 +55,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.0 github.com/google/go-github/v57 v57.0.0 github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 - github.com/google/uuid v1.5.0 + github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.1.2 github.com/gorilla/sessions v1.2.2 github.com/hashicorp/go-version v1.6.0 @@ -71,7 +71,7 @@ require ( github.com/lib/pq v1.10.9 github.com/markbates/goth v1.78.0 github.com/mattn/go-isatty v0.0.20 - github.com/mattn/go-sqlite3 v1.14.19 + github.com/mattn/go-sqlite3 v1.14.22 github.com/meilisearch/meilisearch-go v0.26.1 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.26 @@ -123,9 +123,10 @@ require ( cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect dario.cat/mergo v1.0.0 // indirect + filippo.io/edwards25519 v1.1.0 // indirect git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect github.com/ClickHouse/ch-go v0.61.1 // indirect - github.com/ClickHouse/clickhouse-go/v2 v2.17.1 // indirect + github.com/ClickHouse/clickhouse-go/v2 v2.18.0 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect @@ -168,7 +169,7 @@ require ( 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.10.0 // indirect + github.com/dlclark/regexp2 v1.11.0 // indirect github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -238,7 +239,7 @@ require ( github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/paulmach/orb v0.11.0 // indirect + github.com/paulmach/orb v0.11.1 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect @@ -298,7 +299,7 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 -replace github.com/nektos/act => gitea.com/gitea/act v0.2.51 +replace github.com/nektos/act => gitea.com/gitea/act v0.259.1 replace github.com/gorilla/feeds => github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5 diff --git a/go.sum b/go.sum index 18e0aadd87..7d68d4a798 100644 --- a/go.sum +++ b/go.sum @@ -48,10 +48,12 @@ connectrpc.com/connect v1.15.0/go.mod h1:bQmjpDY8xItMnttnurVgOkHUBMRT9cpsNi2O4Aj dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= -gitea.com/gitea/act v0.2.51 h1:gXc/B4OlTciTTzAx9cmNyw04n2SDO7exPjAsR5Idu+c= -gitea.com/gitea/act v0.2.51/go.mod h1:CoaX2053jqBlD6JMgu4d4UgFL/rp2I14Kt5mMqcs0Z0= +gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw= +gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8= gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 h1:RUBX+MK/TsDxpHmymaOaydfigEbbzqUnG1OTZU/HAeo= gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc= gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0= @@ -80,8 +82,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/ch-go v0.61.1 h1:j5rx3qnvcnYjhnP1IdXE/vdIRQiqgwAzyqOaasA6QCw= github.com/ClickHouse/ch-go v0.61.1/go.mod h1:myxt/JZgy2BYHFGQqzmaIpbfr5CMbs3YHVULaWQj5YU= -github.com/ClickHouse/clickhouse-go/v2 v2.17.1 h1:ZCmAYWpu75IyEi7+Yrs/uaAjiCGY5wfW5kXo64exkX4= -github.com/ClickHouse/clickhouse-go/v2 v2.17.1/go.mod h1:rkGTvFDTLqLIm0ma+13xmcCfr/08Gvs7KmFt1tgiWHQ= +github.com/ClickHouse/clickhouse-go/v2 v2.18.0 h1:O1LicIeg2JS2V29fKRH4+yT3f6jvvcJBm506dpVQ4mQ= +github.com/ClickHouse/clickhouse-go/v2 v2.18.0/go.mod h1:ztQvX6wm7kAbhJslS87EXEhOVNY/TObXwyURnGju5FQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= @@ -101,14 +103,14 @@ github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAc github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/RoaringBitmap/roaring v1.7.0 h1:OZF303tJCER1Tj3x+aArx/S5X7hrT186ri6JjrGvG68= github.com/RoaringBitmap/roaring v1.7.0/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= -github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= -github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= +github.com/alecthomas/assert/v2 v2.6.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.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw= -github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw= +github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI= +github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= -github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= -github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= @@ -233,8 +235,8 @@ github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmW github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= -github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= @@ -342,8 +344,8 @@ github.com/go-openapi/validate v0.22.6/go.mod h1:eaddXSqKeTg5XpSmj1dYyFTK/95n/XH github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4= +github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-swagger/go-swagger v0.30.5 h1:SQ2+xSonWjjoEMOV5tcOnZJVlfyUfCBhGQGArS1b9+U= github.com/go-swagger/go-swagger v0.30.5/go.mod h1:cWUhSyCNqV7J1wkkxfr5QmbcnCewetCdvEXqgPvbc/Q= github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l9rI6sNaZgNC0LnF3MiE+qTmyBA/tZAg1rtyrGbUMK0= @@ -351,8 +353,8 @@ github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.m 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.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-testfixtures/testfixtures/v3 v3.9.0 h1:938g5V+GWLVejm3Hc+nWCuEXRlcglZDDlN/t1gWzcSY= -github.com/go-testfixtures/testfixtures/v3 v3.9.0/go.mod h1:cdsKD2ApFBjdog9jRsz6EJqF+LClq/hrwE9K/1Dzo4s= +github.com/go-testfixtures/testfixtures/v3 v3.10.0 h1:BrBwN7AuC+74g5qtk9D59TLGOaEa8Bw1WmIsf+SyzWc= +github.com/go-testfixtures/testfixtures/v3 v3.10.0/go.mod h1:z8RoleoNtibi6Ar8ziCW7e6PQ+jWiqbUWvuv8AMe4lo= github.com/go-webauthn/webauthn v0.10.0 h1:yuW2e1tXnRAwAvKrR4q4LQmc6XtCMH639/ypZGhZCwk= github.com/go-webauthn/webauthn v0.10.0/go.mod h1:l0NiauXhL6usIKqNLCUM3Qir43GK7ORg8ggold0Uv/Y= github.com/go-webauthn/x v0.1.6 h1:QNAX+AWeqRt9loE8mULeWJCqhVG5D/jvdmJ47fIWCkQ= @@ -455,8 +457,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -607,8 +609,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= -github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/meilisearch/meilisearch-go v0.26.1 h1:3bmo2uLijX7kvBmiZ9LupVfC95TFcRJDgrRTzbOoE4A= github.com/meilisearch/meilisearch-go v0.26.1/go.mod h1:SxuSqDcPBIykjWz1PX+KzsYzArNLSCadQodWs8extS0= github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= @@ -679,8 +681,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU= github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/paulmach/orb v0.11.0 h1:JfVXJUBeH9ifc/OrhBY0lL16QsmPgpCHMlqSSYhcgAA= -github.com/paulmach/orb v0.11.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= +github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= diff --git a/models/actions/variable.go b/models/actions/variable.go index 12717e0ae4..14ded60fac 100644 --- a/models/actions/variable.go +++ b/models/actions/variable.go @@ -10,6 +10,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -82,3 +83,35 @@ func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) }) return count != 0, err } + +func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) { + variables := map[string]string{} + + // Global + globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{}) + if err != nil { + log.Error("find global variables: %v", err) + return nil, err + } + + // Org / User level + ownerVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{OwnerID: run.Repo.OwnerID}) + if err != nil { + log.Error("find variables of org: %d, error: %v", run.Repo.OwnerID, err) + return nil, err + } + + // Repo level + repoVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{RepoID: run.RepoID}) + if err != nil { + log.Error("find variables of repo: %d, error: %v", run.RepoID, err) + return nil, err + } + + // Level precedence: Repo > Org / User > Global + for _, v := range append(globalVariables, append(ownerVariables, repoVariables...)...) { + variables[v.Name] = v.Data + } + + return variables, nil +} diff --git a/models/activities/action.go b/models/activities/action.go index 6df113138b..f85a493e22 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -150,6 +150,7 @@ type Action struct { Repo *repo_model.Repository `xorm:"-"` CommentID int64 `xorm:"INDEX"` Comment *issues_model.Comment `xorm:"-"` + Issue *issues_model.Issue `xorm:"-"` // get the issue id from content IsDeleted bool `xorm:"NOT NULL DEFAULT false"` RefName string IsPrivate bool `xorm:"NOT NULL DEFAULT false"` @@ -292,11 +293,6 @@ func (a *Action) GetRepoAbsoluteLink(ctx context.Context) string { return setting.AppURL + url.PathEscape(a.GetRepoUserName(ctx)) + "/" + url.PathEscape(a.GetRepoName(ctx)) } -// GetCommentHTMLURL returns link to action comment. -func (a *Action) GetCommentHTMLURL(ctx context.Context) string { - return a.getCommentHTMLURL(ctx) -} - func (a *Action) loadComment(ctx context.Context) (err error) { if a.CommentID == 0 || a.Comment != nil { return nil @@ -305,7 +301,8 @@ func (a *Action) loadComment(ctx context.Context) (err error) { return err } -func (a *Action) getCommentHTMLURL(ctx context.Context) string { +// GetCommentHTMLURL returns link to action comment. +func (a *Action) GetCommentHTMLURL(ctx context.Context) string { if a == nil { return "#" } @@ -313,34 +310,19 @@ func (a *Action) getCommentHTMLURL(ctx context.Context) string { if a.Comment != nil { return a.Comment.HTMLURL(ctx) } - if len(a.GetIssueInfos()) == 0 { + + if err := a.LoadIssue(ctx); err != nil || a.Issue == nil { return "#" } - // Return link to issue - issueIDString := a.GetIssueInfos()[0] - issueID, err := strconv.ParseInt(issueIDString, 10, 64) - if err != nil { + if err := a.Issue.LoadRepo(ctx); err != nil { return "#" } - issue, err := issues_model.GetIssueByID(ctx, issueID) - if err != nil { - return "#" - } - - if err = issue.LoadRepo(ctx); err != nil { - return "#" - } - - return issue.HTMLURL() + return a.Issue.HTMLURL() } // GetCommentLink returns link to action comment. func (a *Action) GetCommentLink(ctx context.Context) string { - return a.getCommentLink(ctx) -} - -func (a *Action) getCommentLink(ctx context.Context) string { if a == nil { return "#" } @@ -348,26 +330,15 @@ func (a *Action) getCommentLink(ctx context.Context) string { if a.Comment != nil { return a.Comment.Link(ctx) } - if len(a.GetIssueInfos()) == 0 { + + if err := a.LoadIssue(ctx); err != nil || a.Issue == nil { return "#" } - // Return link to issue - issueIDString := a.GetIssueInfos()[0] - issueID, err := strconv.ParseInt(issueIDString, 10, 64) - if err != nil { + if err := a.Issue.LoadRepo(ctx); err != nil { return "#" } - issue, err := issues_model.GetIssueByID(ctx, issueID) - if err != nil { - return "#" - } - - if err = issue.LoadRepo(ctx); err != nil { - return "#" - } - - return issue.Link() + return a.Issue.Link() } // GetBranch returns the action's repository branch. @@ -395,33 +366,66 @@ func (a *Action) GetCreate() time.Time { return a.CreatedUnix.AsTime() } -// GetIssueInfos returns a list of issues associated with -// the action. +func (a *Action) IsIssueEvent() bool { + return a.OpType.InActions("comment_issue", "approve_pull_request", "reject_pull_request", "comment_pull", "merge_pull_request") +} + +// GetIssueInfos returns a list of associated information with the action. func (a *Action) GetIssueInfos() []string { - return strings.SplitN(a.Content, "|", 3) + // make sure it always returns 3 elements, because there are some access to the a[1] and a[2] without checking the length + ret := strings.SplitN(a.Content, "|", 3) + for len(ret) < 3 { + ret = append(ret, "") + } + return ret +} + +func (a *Action) getIssueIndex() int64 { + infos := a.GetIssueInfos() + if len(infos) == 0 { + return 0 + } + index, _ := strconv.ParseInt(infos[0], 10, 64) + return index +} + +func (a *Action) LoadIssue(ctx context.Context) error { + if a.Issue != nil { + return nil + } + if index := a.getIssueIndex(); index > 0 { + issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) + if err != nil { + return err + } + a.Issue = issue + a.Issue.Repo = a.Repo + } + return nil } // GetIssueTitle returns the title of first issue associated with the action. func (a *Action) GetIssueTitle(ctx context.Context) string { - index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) - issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) - if err != nil { - log.Error("GetIssueByIndex: %v", err) - return "500 when get issue" + if err := a.LoadIssue(ctx); err != nil { + log.Error("LoadIssue: %v", err) + return "<500 when get issue>" } - return issue.Title + if a.Issue == nil { + return "" + } + return a.Issue.Title } -// GetIssueContent returns the content of first issue associated with -// this action. +// GetIssueContent returns the content of first issue associated with this action. func (a *Action) GetIssueContent(ctx context.Context) string { - index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) - issue, err := issues_model.GetIssueByIndex(ctx, a.RepoID, index) - if err != nil { - log.Error("GetIssueByIndex: %v", err) - return "500 when get issue" + if err := a.LoadIssue(ctx); err != nil { + log.Error("LoadIssue: %v", err) + return "<500 when get issue>" } - return issue.Content + if a.Issue == nil { + return "" + } + return a.Issue.Content } // GetFeedsOptions options for retrieving feeds @@ -461,7 +465,7 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err return nil, 0, fmt.Errorf("FindAndCount: %w", err) } - if err := ActionList(actions).loadAttributes(ctx); err != nil { + if err := ActionList(actions).LoadAttributes(ctx); err != nil { return nil, 0, fmt.Errorf("LoadAttributes: %w", err) } diff --git a/models/activities/action_list.go b/models/activities/action_list.go index 3d74397c69..fdf0f35d4f 100644 --- a/models/activities/action_list.go +++ b/models/activities/action_list.go @@ -6,11 +6,16 @@ package activities import ( "context" "fmt" + "strconv" "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" ) // ActionList defines a list of actions @@ -24,7 +29,7 @@ func (actions ActionList) getUserIDs() []int64 { return userIDs.Values() } -func (actions ActionList) loadUsers(ctx context.Context) (map[int64]*user_model.User, error) { +func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) { if len(actions) == 0 { return nil, nil } @@ -52,7 +57,7 @@ func (actions ActionList) getRepoIDs() []int64 { return repoIDs.Values() } -func (actions ActionList) loadRepositories(ctx context.Context) error { +func (actions ActionList) LoadRepositories(ctx context.Context) error { if len(actions) == 0 { return nil } @@ -63,11 +68,11 @@ func (actions ActionList) loadRepositories(ctx context.Context) error { if err != nil { return fmt.Errorf("find repository: %w", err) } - for _, action := range actions { action.Repo = repoMaps[action.RepoID] } - return nil + repos := repo_model.RepositoryList(util.ValuesOfMap(repoMaps)) + return repos.LoadUnits(ctx) } func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]*user_model.User) (err error) { @@ -75,37 +80,124 @@ func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]* userMap = make(map[int64]*user_model.User) } + userSet := make(container.Set[int64], len(actions)) for _, action := range actions { if action.Repo == nil { continue } - repoOwner, ok := userMap[action.Repo.OwnerID] - if !ok { - repoOwner, err = user_model.GetUserByID(ctx, action.Repo.OwnerID) - if err != nil { - if user_model.IsErrUserNotExist(err) { - continue - } - return err - } - userMap[repoOwner.ID] = repoOwner + if _, ok := userMap[action.Repo.OwnerID]; !ok { + userSet.Add(action.Repo.OwnerID) + } + } + + if err := db.GetEngine(ctx). + In("id", userSet.Values()). + Find(&userMap); err != nil { + return fmt.Errorf("find user: %w", err) + } + + for _, action := range actions { + if action.Repo != nil { + action.Repo.Owner = userMap[action.Repo.OwnerID] } - action.Repo.Owner = repoOwner } return nil } -// loadAttributes loads all attributes -func (actions ActionList) loadAttributes(ctx context.Context) error { - userMap, err := actions.loadUsers(ctx) +// LoadAttributes loads all attributes +func (actions ActionList) LoadAttributes(ctx context.Context) error { + // the load sequence cannot be changed because of the dependencies + userMap, err := actions.LoadActUsers(ctx) if err != nil { return err } - - if err := actions.loadRepositories(ctx); err != nil { + if err := actions.LoadRepositories(ctx); err != nil { return err } - - return actions.loadRepoOwner(ctx, userMap) + if err := actions.loadRepoOwner(ctx, userMap); err != nil { + return err + } + if err := actions.LoadIssues(ctx); err != nil { + return err + } + return actions.LoadComments(ctx) +} + +func (actions ActionList) LoadComments(ctx context.Context) error { + if len(actions) == 0 { + return nil + } + + commentIDs := make([]int64, 0, len(actions)) + for _, action := range actions { + if action.CommentID > 0 { + commentIDs = append(commentIDs, action.CommentID) + } + } + + commentsMap := make(map[int64]*issues_model.Comment, len(commentIDs)) + if err := db.GetEngine(ctx).In("id", commentIDs).Find(&commentsMap); err != nil { + return fmt.Errorf("find comment: %w", err) + } + + for _, action := range actions { + if action.CommentID > 0 { + action.Comment = commentsMap[action.CommentID] + if action.Comment != nil { + action.Comment.Issue = action.Issue + } + } + } + return nil +} + +func (actions ActionList) LoadIssues(ctx context.Context) error { + if len(actions) == 0 { + return nil + } + + conditions := builder.NewCond() + issueNum := 0 + for _, action := range actions { + if action.IsIssueEvent() { + infos := action.GetIssueInfos() + if len(infos) == 0 { + continue + } + index, _ := strconv.ParseInt(infos[0], 10, 64) + if index > 0 { + conditions = conditions.Or(builder.Eq{ + "repo_id": action.RepoID, + "`index`": index, + }) + issueNum++ + } + } + } + if !conditions.IsValid() { + return nil + } + + issuesMap := make(map[string]*issues_model.Issue, issueNum) + issues := make([]*issues_model.Issue, 0, issueNum) + if err := db.GetEngine(ctx).Where(conditions).Find(&issues); err != nil { + return fmt.Errorf("find issue: %w", err) + } + for _, issue := range issues { + issuesMap[fmt.Sprintf("%d-%d", issue.RepoID, issue.Index)] = issue + } + + for _, action := range actions { + if !action.IsIssueEvent() { + continue + } + if index := action.getIssueIndex(); index > 0 { + if issue, ok := issuesMap[fmt.Sprintf("%d-%d", action.RepoID, index)]; ok { + action.Issue = issue + action.Issue.Repo = action.Repo + } + } + } + return nil } diff --git a/models/activities/statistic.go b/models/activities/statistic.go index fe5f7d0872..d1a459d1b2 100644 --- a/models/activities/statistic.go +++ b/models/activities/statistic.go @@ -9,6 +9,7 @@ import ( asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" access_model "code.gitea.io/gitea/models/perm/access" @@ -29,7 +30,8 @@ type Statistic struct { Mirror, Release, AuthSource, Webhook, Milestone, Label, HookTask, Team, UpdateTask, Project, - ProjectBoard, Attachment int64 + ProjectBoard, Attachment, + Branches, Tags, CommitStatus int64 IssueByLabel []IssueByLabelCount IssueByRepository []IssueByRepositoryCount } @@ -58,6 +60,9 @@ func GetStatistic(ctx context.Context) (stats Statistic) { stats.Counter.Watch, _ = e.Count(new(repo_model.Watch)) stats.Counter.Star, _ = e.Count(new(repo_model.Star)) stats.Counter.Access, _ = e.Count(new(access_model.Access)) + stats.Counter.Branches, _ = e.Count(new(git_model.Branch)) + stats.Counter.Tags, _ = e.Where("is_draft=?", false).Count(new(repo_model.Release)) + stats.Counter.CommitStatus, _ = e.Count(new(git_model.CommitStatus)) type IssueCount struct { Count int64 diff --git a/models/db/collation.go b/models/db/collation.go index 2f5ff2bf05..c128cf5029 100644 --- a/models/db/collation.go +++ b/models/db/collation.go @@ -166,8 +166,7 @@ func preprocessDatabaseCollation(x *xorm.Engine) { // try to alter database collation to expected if the database is empty, it might fail in some cases (and it isn't necessary to succeed) // at the moment, there is no "altering" solution for MSSQL, site admin should manually change the database collation - // and there is a bug https://github.com/go-testfixtures/testfixtures/pull/182 mssql: Invalid object name 'information_schema.tables'. - if !r.CollationEquals(r.DatabaseCollation, r.ExpectedCollation) && r.ExistingTableNumber == 0 && x.Dialect().URI().DBType == schemas.MYSQL { + if !r.CollationEquals(r.DatabaseCollation, r.ExpectedCollation) && r.ExistingTableNumber == 0 { if err = alterDatabaseCollation(x, r.ExpectedCollation); err != nil { log.Error("Failed to change database collation to %q: %v", r.ExpectedCollation, err) } else { diff --git a/models/fixtures/hook_task.yml b/models/fixtures/hook_task.yml index 6dbb10151a..d573406b36 100644 --- a/models/fixtures/hook_task.yml +++ b/models/fixtures/hook_task.yml @@ -3,3 +3,35 @@ hook_id: 1 uuid: uuid1 is_delivered: true + is_succeed: false + request_content: > + { + "url": "/matrix-delivered", + "http_method":"PUT", + "headers": { + "X-Head": "42" + }, + "body": "{}" + } + +- + id: 2 + hook_id: 1 + uuid: uuid2 + is_delivered: false + +- + id: 3 + hook_id: 1 + uuid: uuid3 + is_delivered: true + is_succeed: true + payload_content: '{"key":"value"}' # legacy task, payload saved in payload_content (and not in request_content) + request_content: > + { + "url": "/matrix-success", + "http_method":"PUT", + "headers": { + "X-Head": "42" + } + } diff --git a/models/git/branch.go b/models/git/branch.go index 6baad65ab4..a5ee2bde66 100644 --- a/models/git/branch.go +++ b/models/git/branch.go @@ -162,6 +162,11 @@ func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, e return &branch, nil } +func GetBranches(ctx context.Context, repoID int64, branchNames []string) ([]*Branch, error) { + branches := make([]*Branch, 0, len(branchNames)) + return branches, db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames).Find(&branches) +} + func AddBranches(ctx context.Context, branches []*Branch) error { for _, branch := range branches { if _, err := db.GetEngine(ctx).Insert(branch); err != nil { diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index a932ac2554..0fb8447ff7 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -476,6 +476,16 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) { } trackedTimes := make(map[int64]int64, len(issues)) + reposMap := make(map[int64]*repo_model.Repository, len(issues)) + for _, issue := range issues { + reposMap[issue.RepoID] = issue.Repo + } + repos := repo_model.RepositoryListOfMap(reposMap) + + if err := repos.LoadUnits(ctx); err != nil { + return err + } + ids := make([]int64, 0, len(issues)) for _, issue := range issues { if issue.Repo.IsTimetrackerEnabled(ctx) { diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index c5c9cecdb9..4e1bd9e87e 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -393,7 +393,7 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64) func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session { // Query for pull requests where you are a reviewer or commenter, excluding - // any pull requests already returned by the the review requested filter. + // any pull requests already returned by the review requested filter. notPoster := builder.Neq{"issue.poster_id": reviewedID} reviewed := builder.In("issue.id", builder. Select("issue_id"). diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index a0cf92c3ad..f20d552a1b 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -173,7 +173,7 @@ func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User, return fmt.Errorf("createComment: %w", err) } if err = issue.AddCrossReferences(ctx, doer, true); err != nil { - return err + return fmt.Errorf("addCrossReferences: %w", err) } return committer.Commit() diff --git a/models/issues/pull.go b/models/issues/pull.go index 98d1617380..f1baa9b5e5 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -891,6 +891,14 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque return nil } + if err := pull.LoadRepo(ctx); err != nil { + return err + } + + if pull.Repo.IsFork { + return nil + } + if err := pr.LoadBaseRepo(ctx); err != nil { return err } @@ -901,12 +909,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque } defer repo.Close() - branch, err := repo.GetDefaultBranch() - if err != nil { - return err - } - - commit, err := repo.GetBranchCommit(branch) + commit, err := repo.GetBranchCommit(pr.BaseRepo.DefaultBranch) if err != nil { return err } @@ -929,7 +932,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque } // Use the merge base as the base instead of the main branch to avoid problems // if the pull request is out of date with the base branch. - changedFiles, err := repo.GetFilesChangedBetween(prInfo.MergeBase, pr.HeadCommitID) + changedFiles, err := repo.GetFilesChangedBetween(prInfo.MergeBase, prInfo.HeadCommitID) if err != nil { return err } diff --git a/models/migrations/base/db_test.go b/models/migrations/base/db_test.go index 4d61b758cf..80bf00b22a 100644 --- a/models/migrations/base/db_test.go +++ b/models/migrations/base/db_test.go @@ -36,12 +36,14 @@ func Test_DropTableColumns(t *testing.T) { "updated_unix", } + x.SetMapper(names.GonicMapper{}) + for i := range columns { - x.SetMapper(names.GonicMapper{}) if err := x.Sync(new(DropTest)); err != nil { t.Errorf("unable to create DropTest table: %v", err) return } + sess := x.NewSession() if err := sess.Begin(); err != nil { sess.Close() @@ -64,7 +66,6 @@ func Test_DropTableColumns(t *testing.T) { return } for j := range columns[i+1:] { - x.SetMapper(names.GonicMapper{}) if err := x.Sync(new(DropTest)); err != nil { t.Errorf("unable to create DropTest table: %v", err) return diff --git a/models/migrations/fixtures/Test_AddIssueResourceIndexTable/issue.yml b/models/migrations/fixtures/Test_AddIssueResourceIndexTable/issue.yml new file mode 100644 index 0000000000..f95d47916b --- /dev/null +++ b/models/migrations/fixtures/Test_AddIssueResourceIndexTable/issue.yml @@ -0,0 +1,4 @@ +- + id: 1 + repo_id: 1 + index: 1 diff --git a/models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task.yml b/models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task.yml new file mode 100644 index 0000000000..716a2a017d --- /dev/null +++ b/models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task.yml @@ -0,0 +1,16 @@ +- id: 11 + uuid: uuid11 + hook_id: 1 + payload_content: > + {"data":"payload"} + event_type: create + delivered: 1706106005 + +- id: 101 + uuid: uuid101 + hook_id: 1 + payload_content: > + {"data":"payload"} + event_type: create + delivered: 1706106006 + is_delivered: true diff --git a/models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task_migrated.yml b/models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task_migrated.yml new file mode 100644 index 0000000000..913d927d91 --- /dev/null +++ b/models/migrations/fixtures/Test_AddPayloadVersionToHookTaskTable/hook_task_migrated.yml @@ -0,0 +1,18 @@ +- id: 11 + uuid: uuid11 + hook_id: 1 + payload_content: > + {"data":"payload"} + event_type: create + delivered: 1706106005 + payload_version: 1 + +- id: 101 + uuid: uuid101 + hook_id: 1 + payload_content: > + {"data":"payload"} + event_type: create + delivered: 1706106006 + is_delivered: true + payload_version: 1 diff --git a/models/migrations/fixtures/Test_AddRepoIDForAttachment/attachment.yml b/models/migrations/fixtures/Test_AddRepoIDForAttachment/attachment.yml new file mode 100644 index 0000000000..056236ba9e --- /dev/null +++ b/models/migrations/fixtures/Test_AddRepoIDForAttachment/attachment.yml @@ -0,0 +1,11 @@ +- + id: 1 + uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 + issue_id: 1 + release_id: 0 + +- + id: 2 + uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12 + issue_id: 0 + release_id: 1 diff --git a/models/migrations/fixtures/Test_AddRepoIDForAttachment/issue.yml b/models/migrations/fixtures/Test_AddRepoIDForAttachment/issue.yml new file mode 100644 index 0000000000..7f3255096d --- /dev/null +++ b/models/migrations/fixtures/Test_AddRepoIDForAttachment/issue.yml @@ -0,0 +1,3 @@ +- + id: 1 + repo_id: 1 diff --git a/models/migrations/fixtures/Test_AddRepoIDForAttachment/release.yml b/models/migrations/fixtures/Test_AddRepoIDForAttachment/release.yml new file mode 100644 index 0000000000..7f3255096d --- /dev/null +++ b/models/migrations/fixtures/Test_AddRepoIDForAttachment/release.yml @@ -0,0 +1,3 @@ +- + id: 1 + repo_id: 1 diff --git a/models/migrations/fixtures/Test_RepositoryFormat/comment.yml b/models/migrations/fixtures/Test_RepositoryFormat/comment.yml new file mode 100644 index 0000000000..1197b086e3 --- /dev/null +++ b/models/migrations/fixtures/Test_RepositoryFormat/comment.yml @@ -0,0 +1,3 @@ +- + id: 1 + commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d diff --git a/models/migrations/fixtures/Test_RepositoryFormat/commit_status.yml b/models/migrations/fixtures/Test_RepositoryFormat/commit_status.yml new file mode 100644 index 0000000000..ca0aaec4cc --- /dev/null +++ b/models/migrations/fixtures/Test_RepositoryFormat/commit_status.yml @@ -0,0 +1,3 @@ +- + id: 1 + context_hash: 19fe5caf872476db265596eaac1dc35ad1c6422d diff --git a/models/migrations/fixtures/Test_RepositoryFormat/pull_request.yml b/models/migrations/fixtures/Test_RepositoryFormat/pull_request.yml new file mode 100644 index 0000000000..380cc079ee --- /dev/null +++ b/models/migrations/fixtures/Test_RepositoryFormat/pull_request.yml @@ -0,0 +1,5 @@ +- + id: 1 + commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d + merge_base: 19fe5caf872476db265596eaac1dc35ad1c6422d + merged_commit_id: 19fe5caf872476db265596eaac1dc35ad1c6422d diff --git a/models/migrations/fixtures/Test_RepositoryFormat/release.yml b/models/migrations/fixtures/Test_RepositoryFormat/release.yml new file mode 100644 index 0000000000..ffabe4ab9e --- /dev/null +++ b/models/migrations/fixtures/Test_RepositoryFormat/release.yml @@ -0,0 +1,3 @@ +- + id: 1 + sha1: 19fe5caf872476db265596eaac1dc35ad1c6422d diff --git a/models/migrations/fixtures/Test_RepositoryFormat/repo_archiver.yml b/models/migrations/fixtures/Test_RepositoryFormat/repo_archiver.yml new file mode 100644 index 0000000000..f04cb3b340 --- /dev/null +++ b/models/migrations/fixtures/Test_RepositoryFormat/repo_archiver.yml @@ -0,0 +1,3 @@ +- + id: 1 + commit_id: 19fe5caf872476db265596eaac1dc35ad1c6422d diff --git a/models/migrations/fixtures/Test_RepositoryFormat/repo_indexer_status.yml b/models/migrations/fixtures/Test_RepositoryFormat/repo_indexer_status.yml new file mode 100644 index 0000000000..1197b086e3 --- /dev/null +++ b/models/migrations/fixtures/Test_RepositoryFormat/repo_indexer_status.yml @@ -0,0 +1,3 @@ +- + id: 1 + commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d diff --git a/models/migrations/fixtures/Test_RepositoryFormat/review_state.yml b/models/migrations/fixtures/Test_RepositoryFormat/review_state.yml new file mode 100644 index 0000000000..1197b086e3 --- /dev/null +++ b/models/migrations/fixtures/Test_RepositoryFormat/review_state.yml @@ -0,0 +1,3 @@ +- + id: 1 + commit_sha: 19fe5caf872476db265596eaac1dc35ad1c6422d diff --git a/models/migrations/fixtures/Test_UpdateBadgeColName/badge.yml b/models/migrations/fixtures/Test_UpdateBadgeColName/badge.yml new file mode 100644 index 0000000000..7025144106 --- /dev/null +++ b/models/migrations/fixtures/Test_UpdateBadgeColName/badge.yml @@ -0,0 +1,4 @@ +- + id: 1 + description: the badge + image_url: https://gitea.com/myimage.png diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 1c8563cebe..173d37234a 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -560,6 +560,14 @@ var migrations = []Migration{ NewMigration("Add PreviousDuration to ActionRun", v1_22.AddPreviousDurationToActionRun), // v286 -> v287 NewMigration("Add support for SHA256 git repositories", v1_22.AdjustDBForSha256), + // v287 -> v288 + NewMigration("Use Slug instead of ID for Badges", v1_22.UseSlugInsteadOfIDForBadges), + // v288 -> v289 + NewMigration("Add user_blocking table", v1_22.AddUserBlockingTable), + // v289 -> v290 + NewMigration("Add default_wiki_branch to repository table", v1_22.AddDefaultWikiBranch), + // v290 -> v291 + NewMigration("Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go index 17669a012e..d99bbc2962 100644 --- a/models/migrations/v1_16/v193_test.go +++ b/models/migrations/v1_16/v193_test.go @@ -15,7 +15,6 @@ func Test_AddRepoIDForAttachment(t *testing.T) { type Attachment struct { ID int64 `xorm:"pk autoincr"` UUID string `xorm:"uuid UNIQUE"` - RepoID int64 `xorm:"INDEX"` // this should not be zero IssueID int64 `xorm:"INDEX"` // maybe zero when creating ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating UploaderID int64 `xorm:"INDEX DEFAULT 0"` @@ -44,12 +43,21 @@ func Test_AddRepoIDForAttachment(t *testing.T) { return } - var issueAttachments []*Attachment - err := x.Where("issue_id > 0").Find(&issueAttachments) + type NewAttachment struct { + ID int64 `xorm:"pk autoincr"` + UUID string `xorm:"uuid UNIQUE"` + RepoID int64 `xorm:"INDEX"` // this should not be zero + IssueID int64 `xorm:"INDEX"` // maybe zero when creating + ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating + UploaderID int64 `xorm:"INDEX DEFAULT 0"` + } + + var issueAttachments []*NewAttachment + err := x.Table("attachment").Where("issue_id > 0").Find(&issueAttachments) assert.NoError(t, err) for _, attach := range issueAttachments { - assert.Greater(t, attach.RepoID, 0) - assert.Greater(t, attach.IssueID, 0) + assert.Greater(t, attach.RepoID, int64(0)) + assert.Greater(t, attach.IssueID, int64(0)) var issue Issue has, err := x.ID(attach.IssueID).Get(&issue) assert.NoError(t, err) @@ -57,12 +65,12 @@ func Test_AddRepoIDForAttachment(t *testing.T) { assert.EqualValues(t, attach.RepoID, issue.RepoID) } - var releaseAttachments []*Attachment - err = x.Where("release_id > 0").Find(&releaseAttachments) + var releaseAttachments []*NewAttachment + err = x.Table("attachment").Where("release_id > 0").Find(&releaseAttachments) assert.NoError(t, err) for _, attach := range releaseAttachments { - assert.Greater(t, attach.RepoID, 0) - assert.Greater(t, attach.IssueID, 0) + assert.Greater(t, attach.RepoID, int64(0)) + assert.Greater(t, attach.ReleaseID, int64(0)) var release Release has, err := x.ID(attach.ReleaseID).Get(&release) assert.NoError(t, err) diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go index 97b22f72a0..0a45c51245 100644 --- a/models/migrations/v1_22/v283.go +++ b/models/migrations/v1_22/v283.go @@ -4,10 +4,40 @@ package v1_22 //nolint import ( + "fmt" + "xorm.io/xorm" + "xorm.io/xorm/schemas" ) func AddCombinedIndexToIssueUser(x *xorm.Engine) error { + type OldIssueUser struct { + IssueID int64 + UID int64 + Cnt int64 + } + + var duplicatedIssueUsers []OldIssueUser + if err := x.SQL("select * from (select issue_id, uid, count(1) as cnt from issue_user group by issue_id, uid) a where a.cnt > 1"). + Find(&duplicatedIssueUsers); err != nil { + return err + } + for _, issueUser := range duplicatedIssueUsers { + if x.Dialect().URI().DBType == schemas.MSSQL { + if _, err := x.Exec(fmt.Sprintf("delete from issue_user where id in (SELECT top %d id FROM issue_user WHERE issue_id = ? and uid = ?)", issueUser.Cnt-1), issueUser.IssueID, issueUser.UID); err != nil { + return err + } + } else { + var ids []int64 + if err := x.SQL("SELECT id FROM issue_user WHERE issue_id = ? and uid = ? limit ?", issueUser.IssueID, issueUser.UID, issueUser.Cnt-1).Find(&ids); err != nil { + return err + } + if _, err := x.Table("issue_user").In("id", ids).Delete(); err != nil { + return err + } + } + } + type IssueUser struct { UID int64 `xorm:"INDEX unique(uid_to_issue)"` // User ID. IssueID int64 `xorm:"INDEX unique(uid_to_issue)"` diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go index ef19f64221..fbbd87344f 100644 --- a/models/migrations/v1_22/v286.go +++ b/models/migrations/v1_22/v286.go @@ -36,9 +36,9 @@ func expandHashReferencesToSha256(x *xorm.Engine) error { if setting.Database.Type.IsMSSQL() { // drop indexes that need to be re-created afterwards droppedIndexes := []string{ - "DROP INDEX commit_status.IDX_commit_status_context_hash", - "DROP INDEX review_state.UQE_review_state_pull_commit_user", - "DROP INDEX repo_archiver.UQE_repo_archiver_s", + "DROP INDEX IF EXISTS [IDX_commit_status_context_hash] ON [commit_status]", + "DROP INDEX IF EXISTS [UQE_review_state_pull_commit_user] ON [review_state]", + "DROP INDEX IF EXISTS [UQE_repo_archiver_s] ON [repo_archiver]", } for _, s := range droppedIndexes { _, err := db.Exec(s) @@ -53,7 +53,7 @@ func expandHashReferencesToSha256(x *xorm.Engine) error { if setting.Database.Type.IsMySQL() { _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1])) } else if setting.Database.Type.IsMSSQL() { - _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` VARCHAR(64)", alts[0], alts[1])) + _, err = db.Exec(fmt.Sprintf("ALTER TABLE [%s] ALTER COLUMN [%s] VARCHAR(64)", alts[0], alts[1])) } else { _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1])) } diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go index 6493bfba2f..7c353747e3 100644 --- a/models/migrations/v1_22/v286_test.go +++ b/models/migrations/v1_22/v286_test.go @@ -14,59 +14,75 @@ import ( func PrepareOldRepository(t *testing.T) (*xorm.Engine, func()) { type Repository struct { // old struct - ID int64 `xorm:"pk autoincr"` - ObjectFormatName string `xorm:"VARCHAR(6) NOT NULL DEFAULT 'sha1'"` + ID int64 `xorm:"pk autoincr"` } - type CommitStatus struct { // old struct - ID int64 `xorm:"pk autoincr"` - ContextHash string `xorm:"char(40)"` + type CommitStatus struct { + ID int64 + ContextHash string } - type Comment struct { // old struct - ID int64 `xorm:"pk autoincr"` - CommitSHA string `xorm:"VARCHAR(40)"` + type RepoArchiver struct { + ID int64 + RepoID int64 + Type int + CommitID string } - type PullRequest struct { // old struct - ID int64 `xorm:"pk autoincr"` - MergeBase string `xorm:"VARCHAR(40)"` - MergedCommitID string `xorm:"VARCHAR(40)"` + type ReviewState struct { + ID int64 + CommitSHA string + UserID int64 + PullID int64 } - type Review struct { // old struct - ID int64 `xorm:"pk autoincr"` - CommitID string `xorm:"VARCHAR(40)"` + type Comment struct { + ID int64 + CommitSHA string } - type ReviewState struct { // old struct - ID int64 `xorm:"pk autoincr"` - CommitSHA string `xorm:"VARCHAR(40)"` + type PullRequest struct { + ID int64 + CommitSHA string + MergeBase string + MergedCommitID string } - type RepoArchiver struct { // old struct - ID int64 `xorm:"pk autoincr"` - CommitID string `xorm:"VARCHAR(40)"` + type Release struct { + ID int64 + Sha1 string } - type Release struct { // old struct - ID int64 `xorm:"pk autoincr"` - Sha1 string `xorm:"VARCHAR(40)"` + type RepoIndexerStatus struct { + ID int64 + CommitSHA string } - type RepoIndexerStatus struct { // old struct - ID int64 `xorm:"pk autoincr"` - CommitSha string `xorm:"VARCHAR(40)"` + type Review struct { + ID int64 + CommitID string } // Prepare and load the testing database - return base.PrepareTestEnv(t, 0, new(Repository), new(CommitStatus), new(Comment), new(PullRequest), new(Review), new(ReviewState), new(RepoArchiver), new(Release), new(RepoIndexerStatus)) + return base.PrepareTestEnv(t, 0, + new(Repository), + new(CommitStatus), + new(RepoArchiver), + new(ReviewState), + new(Review), + new(Comment), + new(PullRequest), + new(Release), + new(RepoIndexerStatus), + ) } func Test_RepositoryFormat(t *testing.T) { x, deferable := PrepareOldRepository(t) defer deferable() + assert.NoError(t, AdjustDBForSha256(x)) + type Repository struct { ID int64 `xorm:"pk autoincr"` ObjectFormatName string `xorg:"not null default('sha1')"` @@ -79,12 +95,10 @@ func Test_RepositoryFormat(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 4, count) - assert.NoError(t, AdjustDBForSha256(x)) - - repo.ID = 20 repo.ObjectFormatName = "sha256" _, err = x.Insert(repo) assert.NoError(t, err) + id := repo.ID count, err = x.Count(new(Repository)) assert.NoError(t, err) @@ -97,7 +111,7 @@ func Test_RepositoryFormat(t *testing.T) { assert.EqualValues(t, "sha1", repo.ObjectFormatName) repo = new(Repository) - ok, err = x.ID(20).Get(repo) + ok, err = x.ID(id).Get(repo) assert.NoError(t, err) assert.EqualValues(t, true, ok) assert.EqualValues(t, "sha256", repo.ObjectFormatName) diff --git a/models/migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go new file mode 100644 index 0000000000..c8b1593286 --- /dev/null +++ b/models/migrations/v1_22/v287.go @@ -0,0 +1,46 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "xorm.io/xorm" +) + +type BadgeUnique struct { + ID int64 `xorm:"pk autoincr"` + Slug string `xorm:"UNIQUE"` +} + +func (BadgeUnique) TableName() string { + return "badge" +} + +func UseSlugInsteadOfIDForBadges(x *xorm.Engine) error { + type Badge struct { + Slug string + } + + err := x.Sync(new(Badge)) + if err != nil { + return err + } + + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + _, err = sess.Exec("UPDATE `badge` SET `slug` = `id` Where `slug` IS NULL") + if err != nil { + return err + } + + err = sess.Sync(new(BadgeUnique)) + if err != nil { + return err + } + + return sess.Commit() +} diff --git a/models/migrations/v1_22/v288.go b/models/migrations/v1_22/v288.go new file mode 100644 index 0000000000..7c93bfcc66 --- /dev/null +++ b/models/migrations/v1_22/v288.go @@ -0,0 +1,26 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +type Blocking struct { + ID int64 `xorm:"pk autoincr"` + BlockerID int64 `xorm:"UNIQUE(block)"` + BlockeeID int64 `xorm:"UNIQUE(block)"` + Note string + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` +} + +func (*Blocking) TableName() string { + return "user_blocking" +} + +func AddUserBlockingTable(x *xorm.Engine) error { + return x.Sync(&Blocking{}) +} diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go new file mode 100644 index 0000000000..e2dfc48715 --- /dev/null +++ b/models/migrations/v1_22/v289.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func AddDefaultWikiBranch(x *xorm.Engine) error { + type Repository struct { + ID int64 + DefaultWikiBranch string + } + if err := x.Sync(&Repository{}); err != nil { + return err + } + _, err := x.Exec("UPDATE `repository` SET default_wiki_branch = 'master' WHERE (default_wiki_branch IS NULL) OR (default_wiki_branch = '')") + return err +} diff --git a/models/migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go new file mode 100644 index 0000000000..e9c471b3dd --- /dev/null +++ b/models/migrations/v1_22/v290.go @@ -0,0 +1,39 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "code.gitea.io/gitea/modules/timeutil" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "xorm.io/xorm" +) + +// HookTask represents a hook task. +// exact copy of models/webhook/hooktask.go when this migration was created +// - xorm:"-" fields deleted +type HookTask struct { + ID int64 `xorm:"pk autoincr"` + HookID int64 `xorm:"index"` + UUID string `xorm:"unique"` + PayloadContent string `xorm:"LONGTEXT"` + EventType webhook_module.HookEventType + IsDelivered bool + Delivered timeutil.TimeStampNano + + // History info. + IsSucceed bool + RequestContent string `xorm:"LONGTEXT"` + ResponseContent string `xorm:"LONGTEXT"` + + // Version number to allow for smooth version upgrades: + // - Version 1: PayloadContent contains the JSON as send to the URL + // - Version 2: PayloadContent contains the original event + PayloadVersion int `xorm:"DEFAULT 1"` +} + +func AddPayloadVersionToHookTaskTable(x *xorm.Engine) error { + // create missing column + return x.Sync(new(HookTask)) +} diff --git a/models/migrations/v1_22/v290_test.go b/models/migrations/v1_22/v290_test.go new file mode 100644 index 0000000000..24a1c0b0a5 --- /dev/null +++ b/models/migrations/v1_22/v290_test.go @@ -0,0 +1,58 @@ +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "strconv" + "testing" + + "code.gitea.io/gitea/models/migrations/base" + "code.gitea.io/gitea/modules/timeutil" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/stretchr/testify/assert" +) + +func Test_AddPayloadVersionToHookTaskTable(t *testing.T) { + type HookTaskMigrated HookTask + + // HookTask represents a hook task, as of before the migration + type HookTask struct { + ID int64 `xorm:"pk autoincr"` + HookID int64 `xorm:"index"` + UUID string `xorm:"unique"` + PayloadContent string `xorm:"LONGTEXT"` + EventType webhook_module.HookEventType + IsDelivered bool + Delivered timeutil.TimeStampNano + + // History info. + IsSucceed bool + RequestContent string `xorm:"LONGTEXT"` + ResponseContent string `xorm:"LONGTEXT"` + } + + // Prepare and load the testing database + x, deferable := base.PrepareTestEnv(t, 0, new(HookTask), new(HookTaskMigrated)) + defer deferable() + if x == nil || t.Failed() { + return + } + + assert.NoError(t, AddPayloadVersionToHookTaskTable(x)) + + expected := []HookTaskMigrated{} + assert.NoError(t, x.Table("hook_task_migrated").Asc("id").Find(&expected)) + assert.Len(t, expected, 2) + + got := []HookTaskMigrated{} + assert.NoError(t, x.Table("hook_task").Asc("id").Find(&got)) + + for i, expected := range expected { + expected, got := expected, got[i] + t.Run(strconv.FormatInt(expected.ID, 10), func(t *testing.T) { + assert.Equal(t, expected.PayloadVersion, got.PayloadVersion) + }) + } +} diff --git a/models/project/board.go b/models/project/board.go index 3e2d8e0472..c0e6529880 100644 --- a/models/project/board.go +++ b/models/project/board.go @@ -232,7 +232,7 @@ func UpdateBoard(ctx context.Context, board *Board) error { func (p *Project) GetBoards(ctx context.Context) (BoardList, error) { boards := make([]*Board, 0, 5) - if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("Sorting").Find(&boards); err != nil { + if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("sorting").Find(&boards); err != nil { return nil, err } diff --git a/models/repo/repo.go b/models/repo/repo.go index 81ed6efa24..4e1b6fcbcf 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -553,6 +553,9 @@ func (repo *Repository) GetBaseRepo(ctx context.Context) (err error) { return nil } + if repo.BaseRepo != nil { + return nil + } repo.BaseRepo, err = GetRepositoryByID(ctx, repo.ForkID) return err } diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 6b452291ea..cb7cd47a8d 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -63,6 +63,41 @@ func RepositoryListOfMap(repoMap map[int64]*Repository) RepositoryList { return RepositoryList(ValuesRepository(repoMap)) } +func (repos RepositoryList) LoadUnits(ctx context.Context) error { + if len(repos) == 0 { + return nil + } + + // Load units. + units := make([]*RepoUnit, 0, len(repos)*6) + if err := db.GetEngine(ctx). + In("repo_id", repos.IDs()). + Find(&units); err != nil { + return fmt.Errorf("find units: %w", err) + } + + unitsMap := make(map[int64][]*RepoUnit, len(repos)) + for _, unit := range units { + if !unit.Type.UnitGlobalDisabled() { + unitsMap[unit.RepoID] = append(unitsMap[unit.RepoID], unit) + } + } + + for _, repo := range repos { + repo.Units = unitsMap[repo.ID] + } + + return nil +} + +func (repos RepositoryList) IDs() []int64 { + repoIDs := make([]int64, len(repos)) + for i := range repos { + repoIDs[i] = repos[i].ID + } + return repoIDs +} + // LoadAttributes loads the attributes for the given RepositoryList func (repos RepositoryList) LoadAttributes(ctx context.Context) error { if len(repos) == 0 { diff --git a/models/secret/secret.go b/models/secret/secret.go index 41e860d7f6..35bed500b9 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -9,7 +9,10 @@ import ( "fmt" "strings" + actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" + actions_module "code.gitea.io/gitea/modules/actions" + "code.gitea.io/gitea/modules/log" secret_module "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -112,3 +115,39 @@ func UpdateSecret(ctx context.Context, secretID int64, data string) error { } return err } + +func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) { + secrets := map[string]string{} + + secrets["GITHUB_TOKEN"] = task.Token + secrets["GITEA_TOKEN"] = task.Token + + if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget { + // ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_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", task.Job.Run.Repo.OwnerID, err) + return nil, err + } + repoSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{RepoID: task.Job.Run.RepoID}) + if err != nil { + log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) + return nil, err + } + + for _, secret := range append(ownerSecrets, repoSecrets...) { + v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data) + if err != nil { + log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err) + return nil, err + } + secrets[secret.Name] = v + } + + return secrets, nil +} diff --git a/models/unittest/mock_http.go b/models/unittest/mock_http.go index afdc5bed21..e2c181408b 100644 --- a/models/unittest/mock_http.go +++ b/models/unittest/mock_http.go @@ -34,7 +34,7 @@ func NewMockWebServer(t *testing.T, liveServerBaseURL, testDataDir string, liveM path := NormalizedFullPath(r.URL) log.Info("Mock HTTP Server: got request for path %s", r.URL.Path) // TODO check request method (support POST?) - fixturePath := fmt.Sprintf("%s/%s", testDataDir, strings.NewReplacer("/", "_", "?", "!").Replace(path)) + fixturePath := fmt.Sprintf("%s/%s_%s", testDataDir, r.Method, url.PathEscape(path)) if liveMode { liveURL := fmt.Sprintf("%s%s", liveServerBaseURL, path) @@ -51,6 +51,7 @@ func NewMockWebServer(t *testing.T, liveServerBaseURL, testDataDir string, liveM response, err := http.DefaultClient.Do(request) assert.NoError(t, err, "HTTP request to %s failed: %s", liveURL) + assert.Less(t, response.StatusCode, 400, "unexpected status code for %s", liveURL) fixture, err := os.Create(fixturePath) assert.NoError(t, err, "failed to open the fixture file %s for writing", fixturePath) diff --git a/models/user/email_address.go b/models/user/email_address.go index 6f3d5b1dde..f2ee5e61b2 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -157,37 +157,18 @@ func UpdateEmailAddress(ctx context.Context, email *EmailAddress) error { var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") -// ValidateEmail check if email is a allowed address +// ValidateEmail check if email is a valid & allowed address func ValidateEmail(email string) error { - if len(email) == 0 { - return ErrEmailInvalid{email} + if err := validateEmailBasic(email); err != nil { + return err } + return validateEmailDomain(email) +} - if !emailRegexp.MatchString(email) { - return ErrEmailCharIsNotSupported{email} - } - - if email[0] == '-' { - return ErrEmailInvalid{email} - } - - if _, err := mail.ParseAddress(email); err != nil { - return ErrEmailInvalid{email} - } - - // if there is no allow list, then check email against block list - if len(setting.Service.EmailDomainAllowList) == 0 && - validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, email) { - return ErrEmailInvalid{email} - } - - // if there is an allow list, then check email against allow list - if len(setting.Service.EmailDomainAllowList) > 0 && - !validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email) { - return ErrEmailInvalid{email} - } - - return nil +// ValidateEmailForAdmin check if email is a valid address when admins manually add or edit users +func ValidateEmailForAdmin(email string) error { + return validateEmailBasic(email) + // In this case we do not need to check the email domain } func GetEmailAddressByEmail(ctx context.Context, email string) (*EmailAddress, error) { @@ -543,3 +524,41 @@ func ActivateUserEmail(ctx context.Context, userID int64, email string, activate return committer.Commit() } + +// validateEmailBasic checks whether the email complies with the rules +func validateEmailBasic(email string) error { + if len(email) == 0 { + return ErrEmailInvalid{email} + } + + if !emailRegexp.MatchString(email) { + return ErrEmailCharIsNotSupported{email} + } + + if email[0] == '-' { + return ErrEmailInvalid{email} + } + + if _, err := mail.ParseAddress(email); err != nil { + return ErrEmailInvalid{email} + } + + return nil +} + +// validateEmailDomain checks whether the email domain is allowed or blocked +func validateEmailDomain(email string) error { + if !IsEmailDomainAllowed(email) { + return ErrEmailInvalid{email} + } + + return nil +} + +func IsEmailDomainAllowed(email string) bool { + if len(setting.Service.EmailDomainAllowList) == 0 { + return !validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, email) + } + + return validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email) +} diff --git a/models/user/user.go b/models/user/user.go index 710fab9f4e..d37463895d 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -436,7 +436,7 @@ func (u *User) GetDisplayName() string { return u.Name } -// GetCompleteName returns the the full name and username in the form of +// GetCompleteName returns the full name and username in the form of // "Full Name (username)" if full name is not empty, otherwise it returns // "username". func (u *User) GetCompleteName() string { @@ -598,6 +598,16 @@ type CreateUserOverwriteOptions struct { // CreateUser creates record of a new user. func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) { + return createUser(ctx, u, false, overwriteDefault...) +} + +// AdminCreateUser is used by admins to manually create users +func AdminCreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) { + return createUser(ctx, u, true, overwriteDefault...) +} + +// createUser creates record of a new user. +func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefault ...*CreateUserOverwriteOptions) (err error) { if err = IsUsableUsername(u.Name); err != nil { return err } @@ -651,8 +661,14 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve return err } - if err := ValidateEmail(u.Email); err != nil { - return err + if createdByAdmin { + if err := ValidateEmailForAdmin(u.Email); err != nil { + return err + } + } else { + if err := ValidateEmail(u.Email); err != nil { + return err + } } ctx, committer, err := db.TxContext(ctx) diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go index 2fb655ebca..ff3fdbadb2 100644 --- a/models/webhook/hooktask.go +++ b/models/webhook/hooktask.go @@ -5,13 +5,13 @@ package webhook import ( "context" + "errors" "time" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -31,6 +31,7 @@ type HookRequest struct { URL string `json:"url"` HTTPMethod string `json:"http_method"` Headers map[string]string `json:"headers"` + Body string `json:"body"` } // HookResponse represents hook task response information. @@ -45,11 +46,15 @@ type HookTask struct { ID int64 `xorm:"pk autoincr"` HookID int64 `xorm:"index"` UUID string `xorm:"unique"` - api.Payloader `xorm:"-"` PayloadContent string `xorm:"LONGTEXT"` - EventType webhook_module.HookEventType - IsDelivered bool - Delivered timeutil.TimeStampNano + // PayloadVersion number to allow for smooth version upgrades: + // - PayloadVersion 1: PayloadContent contains the JSON as sent to the URL + // - PayloadVersion 2: PayloadContent contains the original event + PayloadVersion int `xorm:"DEFAULT 1"` + + EventType webhook_module.HookEventType + IsDelivered bool + Delivered timeutil.TimeStampNano // History info. IsSucceed bool @@ -115,16 +120,12 @@ func HookTasks(ctx context.Context, hookID int64, page int) ([]*HookTask, error) // it handles conversion from Payload to PayloadContent. func CreateHookTask(ctx context.Context, t *HookTask) (*HookTask, error) { t.UUID = gouuid.New().String() - if t.Payloader != nil { - data, err := t.Payloader.JSONPayload() - if err != nil { - return nil, err - } - t.PayloadContent = string(data) - } if t.Delivered == 0 { t.Delivered = timeutil.TimeStampNanoNow() } + if t.PayloadVersion == 0 { + return nil, errors.New("missing HookTask.PayloadVersion") + } return t, db.Insert(ctx, t) } @@ -165,6 +166,7 @@ func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask, HookID: task.HookID, PayloadContent: task.PayloadContent, EventType: task.EventType, + PayloadVersion: task.PayloadVersion, }) } diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index c70c8e99fc..f4403776ce 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/optional" - api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -35,8 +34,10 @@ func TestWebhook_History(t *testing.T) { webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1}) tasks, err := webhook.History(db.DefaultContext, 0) assert.NoError(t, err) - if assert.Len(t, tasks, 1) { - assert.Equal(t, int64(1), tasks[0].ID) + if assert.Len(t, tasks, 3) { + assert.Equal(t, int64(3), tasks[0].ID) + assert.Equal(t, int64(2), tasks[1].ID) + assert.Equal(t, int64(1), tasks[2].ID) } webhook = unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2}) @@ -197,8 +198,10 @@ func TestHookTasks(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) hookTasks, err := HookTasks(db.DefaultContext, 1, 1) assert.NoError(t, err) - if assert.Len(t, hookTasks, 1) { - assert.Equal(t, int64(1), hookTasks[0].ID) + if assert.Len(t, hookTasks, 3) { + assert.Equal(t, int64(3), hookTasks[0].ID) + assert.Equal(t, int64(2), hookTasks[1].ID) + assert.Equal(t, int64(1), hookTasks[2].ID) } hookTasks, err = HookTasks(db.DefaultContext, unittest.NonexistentID, 1) @@ -209,8 +212,8 @@ func TestHookTasks(t *testing.T) { func TestCreateHookTask(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) hookTask := &HookTask{ - HookID: 3, - Payloader: &api.PushPayload{}, + HookID: 3, + PayloadVersion: 2, } unittest.AssertNotExistsBean(t, hookTask) _, err := CreateHookTask(db.DefaultContext, hookTask) @@ -232,10 +235,10 @@ func TestUpdateHookTask(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) hookTask := &HookTask{ - HookID: 3, - Payloader: &api.PushPayload{}, - IsDelivered: true, - Delivered: timeutil.TimeStampNanoNow(), + HookID: 3, + IsDelivered: true, + Delivered: timeutil.TimeStampNanoNow(), + PayloadVersion: 2, } unittest.AssertNotExistsBean(t, hookTask) _, err := CreateHookTask(db.DefaultContext, hookTask) @@ -249,9 +252,9 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) hookTask := &HookTask{ - HookID: 4, - Payloader: &api.PushPayload{}, - IsDelivered: false, + HookID: 4, + IsDelivered: false, + PayloadVersion: 2, } unittest.AssertNotExistsBean(t, hookTask) _, err := CreateHookTask(db.DefaultContext, hookTask) @@ -265,10 +268,10 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) hookTask := &HookTask{ - HookID: 4, - Payloader: &api.PushPayload{}, - IsDelivered: true, - Delivered: timeutil.TimeStampNanoNow(), + HookID: 4, + IsDelivered: true, + Delivered: timeutil.TimeStampNanoNow(), + PayloadVersion: 2, } unittest.AssertNotExistsBean(t, hookTask) _, err := CreateHookTask(db.DefaultContext, hookTask) @@ -282,10 +285,10 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) hookTask := &HookTask{ - HookID: 3, - Payloader: &api.PushPayload{}, - IsDelivered: true, - Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -8).UnixNano()), + HookID: 3, + IsDelivered: true, + Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -8).UnixNano()), + PayloadVersion: 2, } unittest.AssertNotExistsBean(t, hookTask) _, err := CreateHookTask(db.DefaultContext, hookTask) @@ -299,9 +302,9 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) hookTask := &HookTask{ - HookID: 4, - Payloader: &api.PushPayload{}, - IsDelivered: false, + HookID: 4, + IsDelivered: false, + PayloadVersion: 2, } unittest.AssertNotExistsBean(t, hookTask) _, err := CreateHookTask(db.DefaultContext, hookTask) @@ -315,10 +318,10 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) hookTask := &HookTask{ - HookID: 4, - Payloader: &api.PushPayload{}, - IsDelivered: true, - Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -6).UnixNano()), + HookID: 4, + IsDelivered: true, + Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -6).UnixNano()), + PayloadVersion: 2, } unittest.AssertNotExistsBean(t, hookTask) _, err := CreateHookTask(db.DefaultContext, hookTask) diff --git a/modules/git/blame_sha256_test.go b/modules/git/blame_sha256_test.go index 92f8b5b02a..fcb00e2a38 100644 --- a/modules/git/blame_sha256_test.go +++ b/modules/git/blame_sha256_test.go @@ -120,11 +120,12 @@ func TestReadingBlameOutputSha256(t *testing.T) { }, } + objectFormat, err := repo.GetObjectFormat() + assert.NoError(t, err) for _, c := range cases { commit, err := repo.GetCommit(c.CommitID) assert.NoError(t, err) - - blameReader, err := CreateBlameReader(ctx, repo.objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass) + blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass) assert.NoError(t, err) assert.NotNil(t, blameReader) defer blameReader.Close() diff --git a/modules/git/blame_test.go b/modules/git/blame_test.go index 327edab767..4220c85600 100644 --- a/modules/git/blame_test.go +++ b/modules/git/blame_test.go @@ -118,11 +118,13 @@ func TestReadingBlameOutput(t *testing.T) { }, } + objectFormat, err := repo.GetObjectFormat() + assert.NoError(t, err) for _, c := range cases { commit, err := repo.GetCommit(c.CommitID) assert.NoError(t, err) - blameReader, err := CreateBlameReader(ctx, repo.objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass) + blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass) assert.NoError(t, err) assert.NotNil(t, blameReader) defer blameReader.Close() diff --git a/modules/git/commit.go b/modules/git/commit.go index 012ba975e8..3140d1f302 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -311,7 +311,7 @@ func (c *Commit) GetFilesChangedSinceCommit(pastCommit string) ([]string, error) return c.repo.GetFilesChangedBetween(pastCommit, c.ID.String()) } -// FileChangedSinceCommit Returns true if the file given has changed since the the past commit +// FileChangedSinceCommit Returns true if the file given has changed since the past commit // YOU MUST ENSURE THAT pastCommit is a valid commit ID. func (c *Commit) FileChangedSinceCommit(filename, pastCommit string) (bool, error) { return c.repo.FileChangedBetweenCommits(filename, pastCommit, c.ID.String()) diff --git a/modules/git/commit_sha256_test.go b/modules/git/commit_sha256_test.go index 916169f4e2..a4309519cf 100644 --- a/modules/git/commit_sha256_test.go +++ b/modules/git/commit_sha256_test.go @@ -152,10 +152,13 @@ func TestHasPreviousCommitSha256(t *testing.T) { commit, err := repo.GetCommit("f004f41359117d319dedd0eaab8c5259ee2263da839dcba33637997458627fdc") assert.NoError(t, err) + objectFormat, err := repo.GetObjectFormat() + assert.NoError(t, err) + parentSHA := MustIDFromString("b0ec7af4547047f12d5093e37ef8f1b3b5415ed8ee17894d43a34d7d34212e9c") notParentSHA := MustIDFromString("42e334efd04cd36eea6da0599913333c26116e1a537ca76e5b6e4af4dda00236") - assert.Equal(t, repo.objectFormat, parentSHA.Type()) - assert.Equal(t, repo.objectFormat.Name(), "sha256") + assert.Equal(t, objectFormat, parentSHA.Type()) + assert.Equal(t, objectFormat.Name(), "sha256") haz, err := commit.HasPreviousCommit(parentSHA) assert.NoError(t, err) diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go index 86b6a93567..50a0a975b8 100644 --- a/modules/git/repo_base_nogogit.go +++ b/modules/git/repo_base_nogogit.go @@ -71,11 +71,6 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) { repo.batchWriter, repo.batchReader, repo.batchCancel = CatFileBatch(ctx, repoPath) repo.checkWriter, repo.checkReader, repo.checkCancel = CatFileBatchCheck(ctx, repoPath) - repo.objectFormat, err = repo.GetObjectFormat() - if err != nil { - return nil, err - } - return repo, nil } diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index 979c5dec91..552ae2bb8c 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -55,15 +55,8 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) { }, nil } -// SetDefaultBranch sets default branch of repository. -func (repo *Repository) SetDefaultBranch(name string) error { - _, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").AddDynamicArguments(BranchPrefix + name).RunStdString(&RunOpts{Dir: repo.Path}) - return err -} - -// GetDefaultBranch gets default branch of repository. -func (repo *Repository) GetDefaultBranch() (string, error) { - stdout, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repo.Path}) +func GetDefaultBranch(ctx context.Context, repoPath string) (string, error) { + stdout, _, err := NewCommand(ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repoPath}) if err != nil { return "", err } diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 9c9ee7768f..44273d2253 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -246,7 +246,12 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) } }() - len := repo.objectFormat.FullLength() + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + + len := objectFormat.FullLength() commits := []*Commit{} shaline := make([]byte, len+1) for { diff --git a/modules/git/repo_commit_gogit.go b/modules/git/repo_commit_gogit.go index 4cab957564..84580be9a5 100644 --- a/modules/git/repo_commit_gogit.go +++ b/modules/git/repo_commit_gogit.go @@ -41,7 +41,10 @@ func (repo *Repository) RemoveReference(name string) error { // ConvertToHash returns a Hash object from a potential ID string func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { - objectFormat := repo.objectFormat + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } if len(commitID) == hash.HexSize && objectFormat.IsValid(commitID) { ID, err := NewIDFromString(commitID) if err == nil { diff --git a/modules/git/repo_commit_nogogit.go b/modules/git/repo_commit_nogogit.go index a7031184e2..ae4c21aaa3 100644 --- a/modules/git/repo_commit_nogogit.go +++ b/modules/git/repo_commit_nogogit.go @@ -132,8 +132,11 @@ func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id ObjectID) // ConvertToGitID returns a GitHash object from a potential ID string func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { - IDType := repo.objectFormat - if len(commitID) == IDType.FullLength() && IDType.IsValid(commitID) { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + if len(commitID) == objectFormat.FullLength() && objectFormat.IsValid(commitID) { ID, err := NewIDFromString(commitID) if err == nil { return ID, nil @@ -142,7 +145,7 @@ func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx) defer cancel() - _, err := wr.Write([]byte(commitID + "\n")) + _, err = wr.Write([]byte(commitID + "\n")) if err != nil { return nil, err } diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go index 0e9a0c70d7..b6e9d2b44a 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -283,8 +283,12 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error { // If base is undefined empty SHA (zeros), it only returns the files changed in the head commit // If base is the SHA of an empty tree (EmptyTreeSHA), it returns the files changes from the initial commit to the head commit func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } cmd := NewCommand(repo.Ctx, "diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z") - if base == repo.objectFormat.EmptyObjectID().String() { + if base == objectFormat.EmptyObjectID().String() { cmd.AddDynamicArguments(head) } else { cmd.AddDynamicArguments(base, head) diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go index 526b213550..9983873186 100644 --- a/modules/git/repo_compare_test.go +++ b/modules/git/repo_compare_test.go @@ -126,17 +126,20 @@ func TestGetCommitFilesChanged(t *testing.T) { assert.NoError(t, err) defer repo.Close() + objectFormat, err := repo.GetObjectFormat() + assert.NoError(t, err) + testCases := []struct { base, head string files []string }{ { - repo.objectFormat.EmptyObjectID().String(), + objectFormat.EmptyObjectID().String(), "95bb4d39648ee7e325106df01a621c530863a653", []string{"file1.txt"}, }, { - repo.objectFormat.EmptyObjectID().String(), + objectFormat.EmptyObjectID().String(), "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2", []string{"file2.txt"}, }, @@ -146,7 +149,7 @@ func TestGetCommitFilesChanged(t *testing.T) { []string{"file2.txt"}, }, { - repo.objectFormat.EmptyTree().String(), + objectFormat.EmptyTree().String(), "8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2", []string{"file1.txt", "file2.txt"}, }, diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go index 47705a92af..6aaab242c1 100644 --- a/modules/git/repo_index.go +++ b/modules/git/repo_index.go @@ -94,6 +94,10 @@ func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { // RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present. func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return err + } cmd := NewCommand(repo.Ctx, "update-index", "--remove", "-z", "--index-info") stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) @@ -101,7 +105,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { for _, file := range filenames { if file != "" { buffer.WriteString("0 ") - buffer.WriteString(repo.objectFormat.EmptyObjectID().String()) + buffer.WriteString(objectFormat.EmptyObjectID().String()) buffer.WriteByte('\t') buffer.WriteString(file) buffer.WriteByte('\000') diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index ae5dbd171f..e8c5ce6fb8 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -141,7 +141,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) { break } - tag, err := parseTagRef(repo.objectFormat, ref) + tag, err := parseTagRef(ref) if err != nil { return nil, 0, fmt.Errorf("GetTagInfos: parse tag: %w", err) } @@ -161,7 +161,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) { } // parseTagRef parses a tag from a 'git for-each-ref'-produced reference. -func parseTagRef(objectFormat ObjectFormat, ref map[string]string) (tag *Tag, err error) { +func parseTagRef(ref map[string]string) (tag *Tag, err error) { tag = &Tag{ Type: ref["objecttype"], Name: ref["refname:lstrip=2"], diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go index 9816e311a8..785c3442a7 100644 --- a/modules/git/repo_tag_test.go +++ b/modules/git/repo_tag_test.go @@ -194,7 +194,6 @@ func TestRepository_GetAnnotatedTag(t *testing.T) { } func TestRepository_parseTagRef(t *testing.T) { - sha1 := Sha1ObjectFormat tests := []struct { name string @@ -351,7 +350,7 @@ Add changelog of v1.9.1 (#7859) for _, test := range tests { tc := test // don't close over loop variable t.Run(tc.name, func(t *testing.T) { - got, err := parseTagRef(sha1, tc.givenRef) + got, err := parseTagRef(tc.givenRef) if tc.wantErr { require.Error(t, err) diff --git a/modules/git/repo_tree_gogit.go b/modules/git/repo_tree_gogit.go index 6391959e6a..dc97ce1344 100644 --- a/modules/git/repo_tree_gogit.go +++ b/modules/git/repo_tree_gogit.go @@ -21,7 +21,12 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) { // GetTree find the tree object in the repository. func (repo *Repository) GetTree(idStr string) (*Tree, error) { - if len(idStr) != repo.objectFormat.FullLength() { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + + if len(idStr) != objectFormat.FullLength() { res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(&RunOpts{Dir: repo.Path}) if err != nil { return nil, err diff --git a/modules/git/repo_tree_nogogit.go b/modules/git/repo_tree_nogogit.go index 582247b4a4..e82012de6f 100644 --- a/modules/git/repo_tree_nogogit.go +++ b/modules/git/repo_tree_nogogit.go @@ -51,7 +51,11 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) { case "tree": tree := NewTree(repo, id) tree.ResolvedID = id - tree.entries, err = catBatchParseTreeEntries(repo.objectFormat, tree, rd, size) + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + tree.entries, err = catBatchParseTreeEntries(objectFormat, tree, rd, size) if err != nil { return nil, err } @@ -69,7 +73,11 @@ func (repo *Repository) getTree(id ObjectID) (*Tree, error) { // GetTree find the tree object in the repository. func (repo *Repository) GetTree(idStr string) (*Tree, error) { - if len(idStr) != repo.objectFormat.FullLength() { + objectFormat, err := repo.GetObjectFormat() + if err != nil { + return nil, err + } + if len(idStr) != objectFormat.FullLength() { res, err := repo.GetRefCommitID(idStr) if err != nil { return nil, err diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go index 28d02c7e81..a591485082 100644 --- a/modules/git/tree_nogogit.go +++ b/modules/git/tree_nogogit.go @@ -77,8 +77,11 @@ func (t *Tree) ListEntries() (Entries, error) { return nil, runErr } - var err error - t.entries, err = parseTreeEntries(t.repo.objectFormat, stdout, t) + objectFormat, err := t.repo.GetObjectFormat() + if err != nil { + return nil, err + } + t.entries, err = parseTreeEntries(objectFormat, stdout, t) if err == nil { t.entriesParsed = true } @@ -101,8 +104,11 @@ func (t *Tree) listEntriesRecursive(extraArgs TrustedCmdArgs) (Entries, error) { return nil, runErr } - var err error - t.entriesRecursive, err = parseTreeEntries(t.repo.objectFormat, stdout, t) + objectFormat, err := t.repo.GetObjectFormat() + if err != nil { + return nil, err + } + t.entriesRecursive, err = parseTreeEntries(objectFormat, stdout, t) if err == nil { t.entriesRecursiveParsed = true } diff --git a/modules/gitrepo/branch.go b/modules/gitrepo/branch.go index dcaf92668d..e13a4c82e1 100644 --- a/modules/gitrepo/branch.go +++ b/modules/gitrepo/branch.go @@ -30,3 +30,20 @@ func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (str return gitRepo.GetBranchCommitID(branch) } + +// SetDefaultBranch sets default branch of repository. +func SetDefaultBranch(ctx context.Context, repo Repository, name string) error { + _, _, err := git.NewCommand(ctx, "symbolic-ref", "HEAD"). + AddDynamicArguments(git.BranchPrefix + name). + RunStdString(&git.RunOpts{Dir: repoPath(repo)}) + return err +} + +// GetDefaultBranch gets default branch of repository. +func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) { + return git.GetDefaultBranch(ctx, repoPath(repo)) +} + +func GetWikiDefaultBranch(ctx context.Context, repo Repository) (string, error) { + return git.GetDefaultBranch(ctx, wikiPath(repo)) +} diff --git a/modules/graceful/manager.go b/modules/graceful/manager.go index f3f412863a..3f1115066a 100644 --- a/modules/graceful/manager.go +++ b/modules/graceful/manager.go @@ -233,7 +233,10 @@ func (g *Manager) setStateTransition(old, new state) bool { // At the moment the total number of servers (numberOfServersToCreate) are pre-defined as a const before global init, // so this function MUST be called if a server is not used. func (g *Manager) InformCleanup() { - g.createServerWaitGroup.Done() + g.createServerCond.L.Lock() + defer g.createServerCond.L.Unlock() + g.createdServer++ + g.createServerCond.Signal() } // Done allows the manager to be viewed as a context.Context, it returns a channel that is closed when the server is finished terminating diff --git a/modules/graceful/manager_common.go b/modules/graceful/manager_common.go index 27196e1531..f6dbcc748d 100644 --- a/modules/graceful/manager_common.go +++ b/modules/graceful/manager_common.go @@ -42,8 +42,9 @@ type Manager struct { terminateCtxCancel context.CancelFunc managerCtxCancel context.CancelFunc runningServerWaitGroup sync.WaitGroup - createServerWaitGroup sync.WaitGroup terminateWaitGroup sync.WaitGroup + createServerCond sync.Cond + createdServer int shutdownRequested chan struct{} toRunAtShutdown []func() @@ -52,7 +53,7 @@ type Manager struct { func newGracefulManager(ctx context.Context) *Manager { manager := &Manager{ctx: ctx, shutdownRequested: make(chan struct{})} - manager.createServerWaitGroup.Add(numberOfServersToCreate) + manager.createServerCond.L = &sync.Mutex{} manager.prepare(ctx) manager.start() return manager diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go index f4af4993d9..f49c42650c 100644 --- a/modules/graceful/manager_unix.go +++ b/modules/graceful/manager_unix.go @@ -57,12 +57,27 @@ func (g *Manager) start() { // Handle clean up of unused provided listeners and delayed start-up startupDone := make(chan struct{}) go func() { - defer close(startupDone) - // Wait till we're done getting all the listeners and then close the unused ones - g.createServerWaitGroup.Wait() - // Ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function - _ = CloseProvidedListeners() - g.notify(readyMsg) + defer func() { + close(startupDone) + // Close the unused listeners and ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function + _ = CloseProvidedListeners() + }() + // Wait for all servers to be created + g.createServerCond.L.Lock() + for { + if g.createdServer >= numberOfServersToCreate { + g.createServerCond.L.Unlock() + g.notify(readyMsg) + return + } + select { + case <-g.IsShutdown(): + g.createServerCond.L.Unlock() + return + default: + } + g.createServerCond.Wait() + } }() if setting.StartupTimeout > 0 { go func() { @@ -70,16 +85,7 @@ func (g *Manager) start() { case <-startupDone: return case <-g.IsShutdown(): - func() { - // When WaitGroup counter goes negative it will panic - we don't care about this so we can just ignore it. - defer func() { - _ = recover() - }() - // Ensure that the createServerWaitGroup stops waiting - for { - g.createServerWaitGroup.Done() - } - }() + g.createServerCond.Signal() return case <-time.After(setting.StartupTimeout): log.Error("Startup took too long! Shutting down") diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go index 0248dcb24d..d776e0e9f9 100644 --- a/modules/graceful/manager_windows.go +++ b/modules/graceful/manager_windows.go @@ -149,25 +149,35 @@ hammerLoop: func (g *Manager) awaitServer(limit time.Duration) bool { c := make(chan struct{}) go func() { - defer close(c) - g.createServerWaitGroup.Wait() + g.createServerCond.L.Lock() + for { + if g.createdServer >= numberOfServersToCreate { + g.createServerCond.L.Unlock() + close(c) + return + } + select { + case <-g.IsShutdown(): + g.createServerCond.L.Unlock() + return + default: + } + g.createServerCond.Wait() + } }() + + var tc <-chan time.Time if limit > 0 { - select { - case <-c: - return true // completed normally - case <-time.After(limit): - return false // timed out - case <-g.IsShutdown(): - return false - } - } else { - select { - case <-c: - return true // completed normally - case <-g.IsShutdown(): - return false - } + tc = time.After(limit) + } + select { + case <-c: + return true // completed normally + case <-tc: + return false // timed out + case <-g.IsShutdown(): + g.createServerCond.Signal() + return false } } diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index 8ba50ed77c..d7f735e957 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -142,7 +142,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro return err } if size, err = strconv.ParseInt(strings.TrimSpace(stdout), 10, 64); err != nil { - return fmt.Errorf("Misformatted git cat-file output: %w", err) + return fmt.Errorf("misformatted git cat-file output: %w", err) } } @@ -233,26 +233,26 @@ func (b *Indexer) Delete(_ context.Context, repoID int64) error { // Search searches for files in the specified repo. // Returns the matching file-paths -func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) { +func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) { var ( indexerQuery query.Query keywordQuery query.Query ) - if isMatch { - prefixQuery := bleve.NewPrefixQuery(keyword) - prefixQuery.FieldVal = "Content" - keywordQuery = prefixQuery - } else { - phraseQuery := bleve.NewMatchPhraseQuery(keyword) + if opts.IsKeywordFuzzy { + phraseQuery := bleve.NewMatchPhraseQuery(opts.Keyword) phraseQuery.FieldVal = "Content" phraseQuery.Analyzer = repoIndexerAnalyzer keywordQuery = phraseQuery + } else { + prefixQuery := bleve.NewPrefixQuery(opts.Keyword) + prefixQuery.FieldVal = "Content" + keywordQuery = prefixQuery } - if len(repoIDs) > 0 { - repoQueries := make([]query.Query, 0, len(repoIDs)) - for _, repoID := range repoIDs { + if len(opts.RepoIDs) > 0 { + repoQueries := make([]query.Query, 0, len(opts.RepoIDs)) + for _, repoID := range opts.RepoIDs { repoQueries = append(repoQueries, inner_bleve.NumericEqualityQuery(repoID, "RepoID")) } @@ -266,8 +266,8 @@ func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword // Save for reuse without language filter facetQuery := indexerQuery - if len(language) > 0 { - languageQuery := bleve.NewMatchQuery(language) + if len(opts.Language) > 0 { + languageQuery := bleve.NewMatchQuery(opts.Language) languageQuery.FieldVal = "Language" languageQuery.Analyzer = analyzer_keyword.Name @@ -277,12 +277,12 @@ func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword ) } - from := (page - 1) * pageSize + from, pageSize := opts.GetSkipTake() searchRequest := bleve.NewSearchRequestOptions(indexerQuery, pageSize, from, false) searchRequest.Fields = []string{"Content", "RepoID", "Language", "CommitID", "UpdatedAt"} searchRequest.IncludeLocations = true - if len(language) == 0 { + if len(opts.Language) == 0 { searchRequest.AddFacet("languages", bleve.NewFacetRequest("Language", 10)) } @@ -326,7 +326,7 @@ func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword } searchResultLanguages := make([]*internal.SearchResultLanguages, 0, 10) - if len(language) > 0 { + if len(opts.Language) > 0 { // Use separate query to go get all language counts facetRequest := bleve.NewSearchRequestOptions(facetQuery, 1, 0, false) facetRequest.Fields = []string{"Content", "RepoID", "Language", "CommitID", "UpdatedAt"} diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go index 0f70f13485..e4622fd66e 100644 --- a/modules/indexer/code/elasticsearch/elasticsearch.go +++ b/modules/indexer/code/elasticsearch/elasticsearch.go @@ -281,18 +281,18 @@ func extractAggs(searchResult *elastic.SearchResult) []*internal.SearchResultLan } // Search searches for codes and language stats by given conditions. -func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) { - searchType := esMultiMatchTypeBestFields - if isMatch { - searchType = esMultiMatchTypePhrasePrefix +func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) { + searchType := esMultiMatchTypePhrasePrefix + if opts.IsKeywordFuzzy { + searchType = esMultiMatchTypeBestFields } - kwQuery := elastic.NewMultiMatchQuery(keyword, "content").Type(searchType) + kwQuery := elastic.NewMultiMatchQuery(opts.Keyword, "content").Type(searchType) query := elastic.NewBoolQuery() query = query.Must(kwQuery) - if len(repoIDs) > 0 { - repoStrs := make([]any, 0, len(repoIDs)) - for _, repoID := range repoIDs { + if len(opts.RepoIDs) > 0 { + repoStrs := make([]any, 0, len(opts.RepoIDs)) + for _, repoID := range opts.RepoIDs { repoStrs = append(repoStrs, repoID) } repoQuery := elastic.NewTermsQuery("repo_id", repoStrs...) @@ -300,16 +300,12 @@ func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword } var ( - start int - kw = "" + keyword + "" - aggregation = elastic.NewTermsAggregation().Field("language").Size(10).OrderByCountDesc() + start, pageSize = opts.GetSkipTake() + kw = "" + opts.Keyword + "" + aggregation = elastic.NewTermsAggregation().Field("language").Size(10).OrderByCountDesc() ) - if page > 0 { - start = (page - 1) * pageSize - } - - if len(language) == 0 { + if len(opts.Language) == 0 { searchResult, err := b.inner.Client.Search(). Index(b.inner.VersionedIndexName()). Aggregation("language", aggregation). @@ -330,7 +326,7 @@ func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword return convertResult(searchResult, kw, pageSize) } - langQuery := elastic.NewMatchQuery("language", language) + langQuery := elastic.NewMatchQuery("language", opts.Language) countResult, err := b.inner.Client.Search(). Index(b.inner.VersionedIndexName()). Aggregation("language", aggregation). diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go index 76cd78e11e..2905a540e5 100644 --- a/modules/indexer/code/git.go +++ b/modules/indexer/code/git.go @@ -32,7 +32,7 @@ func getRepoChanges(ctx context.Context, repo *repo_model.Repository, revision s needGenesis := len(status.CommitSha) == 0 if !needGenesis { - hasAncestorCmd := git.NewCommand(ctx, "merge-base").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision) + hasAncestorCmd := git.NewCommand(ctx, "merge-base").AddDynamicArguments(status.CommitSha, revision) stdout, _, _ := hasAncestorCmd.RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) needGenesis = len(stdout) == 0 } @@ -91,11 +91,9 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s return nil, runErr } + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) + var err error - objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath()) - if err != nil { - return nil, err - } changes.Updates, err = parseGitLsTreeOutput(objectFormat, stdout) return &changes, err } @@ -174,10 +172,8 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio return nil, err } - objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath()) - if err != nil { - return nil, err - } + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) + changes.Updates, err = parseGitLsTreeOutput(objectFormat, lsTreeStdout) return &changes, err } diff --git a/modules/indexer/code/indexer_test.go b/modules/indexer/code/indexer_test.go index 5eb8e61e3d..8975c5ce40 100644 --- a/modules/indexer/code/indexer_test.go +++ b/modules/indexer/code/indexer_test.go @@ -8,6 +8,7 @@ import ( "os" "testing" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/indexer/code/bleve" @@ -70,7 +71,15 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) { for _, kw := range keywords { t.Run(kw.Keyword, func(t *testing.T) { - total, res, langs, err := indexer.Search(context.TODO(), kw.RepoIDs, "", kw.Keyword, 1, 10, false) + total, res, langs, err := indexer.Search(context.TODO(), &internal.SearchOptions{ + RepoIDs: kw.RepoIDs, + Keyword: kw.Keyword, + Paginator: &db.ListOptions{ + Page: 1, + PageSize: 10, + }, + IsKeywordFuzzy: true, + }) assert.NoError(t, err) assert.Len(t, kw.IDs, int(total)) assert.Len(t, langs, kw.Langs) diff --git a/modules/indexer/code/internal/indexer.go b/modules/indexer/code/internal/indexer.go index da3ac3623c..c259fcd26e 100644 --- a/modules/indexer/code/internal/indexer.go +++ b/modules/indexer/code/internal/indexer.go @@ -7,6 +7,7 @@ import ( "context" "fmt" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/indexer/internal" ) @@ -16,7 +17,17 @@ type Indexer interface { internal.Indexer Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *RepoChanges) error Delete(ctx context.Context, repoID int64) error - Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*SearchResult, []*SearchResultLanguages, error) + Search(ctx context.Context, opts *SearchOptions) (int64, []*SearchResult, []*SearchResultLanguages, error) +} + +type SearchOptions struct { + RepoIDs []int64 + Keyword string + Language string + + IsKeywordFuzzy bool + + db.Paginator } // NewDummyIndexer returns a dummy indexer @@ -38,6 +49,6 @@ func (d *dummyIndexer) Delete(ctx context.Context, repoID int64) error { return fmt.Errorf("indexer is not ready") } -func (d *dummyIndexer) Search(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int64, []*SearchResult, []*SearchResultLanguages, error) { +func (d *dummyIndexer) Search(ctx context.Context, opts *SearchOptions) (int64, []*SearchResult, []*SearchResultLanguages, error) { return 0, nil, nil, fmt.Errorf("indexer is not ready") } diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go index e19e22eea0..51c7595cf8 100644 --- a/modules/indexer/code/search.go +++ b/modules/indexer/code/search.go @@ -16,18 +16,24 @@ import ( // Result a search result to display type Result struct { - RepoID int64 - Filename string - CommitID string - UpdatedUnix timeutil.TimeStamp - Language string - Color string - LineNumbers []int - FormattedLines template.HTML + RepoID int64 + Filename string + CommitID string + UpdatedUnix timeutil.TimeStamp + Language string + Color string + Lines []ResultLine +} + +type ResultLine struct { + Num int + FormattedContent template.HTML } type SearchResultLanguages = internal.SearchResultLanguages +type SearchOptions = internal.SearchOptions + func indices(content string, selectionStartIndex, selectionEndIndex int) (int, int) { startIndex := selectionStartIndex numLinesBefore := 0 @@ -70,7 +76,7 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res var formattedLinesBuffer bytes.Buffer contentLines := strings.SplitAfter(result.Content[startIndex:endIndex], "\n") - lineNumbers := make([]int, len(contentLines)) + lines := make([]ResultLine, 0, len(contentLines)) index := startIndex for i, line := range contentLines { var err error @@ -93,31 +99,40 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res return nil, err } - lineNumbers[i] = startLineNum + i + lines = append(lines, ResultLine{Num: startLineNum + i}) index += len(line) } - highlighted, _ := highlight.Code(result.Filename, "", formattedLinesBuffer.String()) + // we should highlight the whole code block first, otherwise it doesn't work well with multiple line highlighting + hl, _ := highlight.Code(result.Filename, "", formattedLinesBuffer.String()) + highlightedLines := strings.Split(string(hl), "\n") + + // The lines outputted by highlight.Code might not match the original lines, because "highlight" removes the last `\n` + lines = lines[:min(len(highlightedLines), len(lines))] + highlightedLines = highlightedLines[:len(lines)] + for i := 0; i < len(lines); i++ { + lines[i].FormattedContent = template.HTML(highlightedLines[i]) + } return &Result{ - RepoID: result.RepoID, - Filename: result.Filename, - CommitID: result.CommitID, - UpdatedUnix: result.UpdatedUnix, - Language: result.Language, - Color: result.Color, - LineNumbers: lineNumbers, - FormattedLines: highlighted, + RepoID: result.RepoID, + Filename: result.Filename, + CommitID: result.CommitID, + UpdatedUnix: result.UpdatedUnix, + Language: result.Language, + Color: result.Color, + Lines: lines, }, nil } // PerformSearch perform a search on a repository -func PerformSearch(ctx context.Context, repoIDs []int64, language, keyword string, page, pageSize int, isMatch bool) (int, []*Result, []*internal.SearchResultLanguages, error) { - if len(keyword) == 0 { +// if isFuzzy is true set the Damerau-Levenshtein distance from 0 to 2 +func PerformSearch(ctx context.Context, opts *SearchOptions) (int, []*Result, []*SearchResultLanguages, error) { + if opts == nil || len(opts.Keyword) == 0 { return 0, nil, nil, nil } - total, results, resultLanguages, err := (*globalIndexer.Load()).Search(ctx, repoIDs, language, keyword, page, pageSize, isMatch) + total, results, resultLanguages, err := (*globalIndexer.Load()).Search(ctx, opts) if err != nil { return 0, nil, nil, err } diff --git a/modules/indexer/internal/bleve/query.go b/modules/indexer/internal/bleve/query.go index c7d66538c1..b96875343e 100644 --- a/modules/indexer/internal/bleve/query.go +++ b/modules/indexer/internal/bleve/query.go @@ -4,6 +4,8 @@ package bleve import ( + "code.gitea.io/gitea/modules/optional" + "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/search/query" ) @@ -25,6 +27,13 @@ func MatchPhraseQuery(matchPhrase, field, analyzer string) *query.MatchPhraseQue return q } +// PrefixQuery generates a match prefix query for the given prefix and field +func PrefixQuery(matchPrefix, field string) *query.PrefixQuery { + q := bleve.NewPrefixQuery(matchPrefix) + q.FieldVal = field + return q +} + // BoolFieldQuery generates a bool field query for the given value and field func BoolFieldQuery(value bool, field string) *query.BoolFieldQuery { q := bleve.NewBoolFieldQuery(value) @@ -32,18 +41,18 @@ func BoolFieldQuery(value bool, field string) *query.BoolFieldQuery { return q } -func NumericRangeInclusiveQuery(min, max *int64, field string) *query.NumericRangeQuery { +func NumericRangeInclusiveQuery(min, max optional.Option[int64], field string) *query.NumericRangeQuery { var minF, maxF *float64 var minI, maxI *bool - if min != nil { + if min.Has() { minF = new(float64) - *minF = float64(*min) + *minF = float64(min.Value()) minI = new(bool) *minI = true } - if max != nil { + if max.Has() { maxF = new(float64) - *maxF = float64(*max) + *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 6a5d65cb66..927ad58cd4 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -156,12 +156,19 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( var queries []query.Query if options.Keyword != "" { - keywordQueries := []query.Query{ - inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer), - inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer), - inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer), + if options.IsFuzzyKeyword { + queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{ + inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer), + inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer), + inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer), + }...)) + } else { + queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{ + inner_bleve.PrefixQuery(options.Keyword, "title"), + inner_bleve.PrefixQuery(options.Keyword, "content"), + inner_bleve.PrefixQuery(options.Keyword, "comments"), + }...)) } - queries = append(queries, bleve.NewDisjunctionQuery(keywordQueries...)) } if len(options.RepoIDs) > 0 || options.AllPublic { @@ -217,38 +224,41 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( queries = append(queries, bleve.NewDisjunctionQuery(milestoneQueries...)) } - if options.ProjectID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ProjectID, "project_id")) + if options.ProjectID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectID.Value(), "project_id")) } - if options.ProjectBoardID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ProjectBoardID, "project_board_id")) + if options.ProjectBoardID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectBoardID.Value(), "project_board_id")) } - if options.PosterID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.PosterID, "poster_id")) + if options.PosterID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.PosterID.Value(), "poster_id")) } - if options.AssigneeID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.AssigneeID, "assignee_id")) + if options.AssigneeID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.AssigneeID.Value(), "assignee_id")) } - if options.MentionID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.MentionID, "mention_ids")) + if options.MentionID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.MentionID.Value(), "mention_ids")) } - if options.ReviewedID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ReviewedID, "reviewed_ids")) + if options.ReviewedID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ReviewedID.Value(), "reviewed_ids")) } - if options.ReviewRequestedID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.ReviewRequestedID, "review_requested_ids")) + if options.ReviewRequestedID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.ReviewRequestedID.Value(), "review_requested_ids")) } - if options.SubscriberID != nil { - queries = append(queries, inner_bleve.NumericEqualityQuery(*options.SubscriberID, "subscriber_ids")) + if options.SubscriberID.Has() { + queries = append(queries, inner_bleve.NumericEqualityQuery(options.SubscriberID.Value(), "subscriber_ids")) } - if options.UpdatedAfterUnix != nil || options.UpdatedBeforeUnix != nil { - queries = append(queries, inner_bleve.NumericRangeInclusiveQuery(options.UpdatedAfterUnix, options.UpdatedBeforeUnix, "updated_unix")) + if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() { + queries = append(queries, inner_bleve.NumericRangeInclusiveQuery( + options.UpdatedAfterUnix, + options.UpdatedBeforeUnix, + "updated_unix")) } var indexerQuery query.Query = bleve.NewConjunctionQuery(queries...) diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index 69146573a8..eeaf1696ad 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -15,22 +15,6 @@ import ( ) func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_model.IssuesOptions, error) { - // See the comment of issues_model.SearchOptions for the reason why we need to convert - convertID := func(id *int64) int64 { - if id == nil { - return 0 - } - if *id == 0 { - return db.NoConditionID - } - return *id - } - convertInt64 := func(i *int64) int64 { - if i == nil { - return 0 - } - return *i - } var sortType string switch options.SortBy { case internal.SortByCreatedAsc: @@ -53,6 +37,18 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m sortType = "newest" } + // See the comment of issues_model.SearchOptions for the reason why we need to convert + convertID := func(id optional.Option[int64]) int64 { + if !id.Has() { + return 0 + } + value := id.Value() + if value == 0 { + return db.NoConditionID + } + return value + } + opts := &issue_model.IssuesOptions{ Paginator: options.Paginator, RepoIDs: options.RepoIDs, @@ -73,8 +69,8 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m IncludeMilestones: nil, SortType: sortType, IssueIDs: nil, - UpdatedAfterUnix: convertInt64(options.UpdatedAfterUnix), - UpdatedBeforeUnix: convertInt64(options.UpdatedBeforeUnix), + UpdatedAfterUnix: options.UpdatedAfterUnix.Value(), + UpdatedBeforeUnix: options.UpdatedBeforeUnix.Value(), PriorityRepoID: 0, IsArchived: optional.None[bool](), Org: nil, diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go index 80e233e29a..4a98b4588a 100644 --- a/modules/indexer/issues/dboptions.go +++ b/modules/indexer/issues/dboptions.go @@ -6,6 +6,7 @@ package issues import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/optional" ) func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOptions { @@ -38,13 +39,12 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp } // See the comment of issues_model.SearchOptions for the reason why we need to convert - convertID := func(id int64) *int64 { + convertID := func(id int64) optional.Option[int64] { if id > 0 { - return &id + return optional.Some(id) } if id == db.NoConditionID { - var zero int64 - return &zero + return optional.None[int64]() } return nil } @@ -59,10 +59,10 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp searchOpt.SubscriberID = convertID(opts.SubscriberID) if opts.UpdatedAfterUnix > 0 { - searchOpt.UpdatedAfterUnix = &opts.UpdatedAfterUnix + searchOpt.UpdatedAfterUnix = optional.Some(opts.UpdatedAfterUnix) } if opts.UpdatedBeforeUnix > 0 { - searchOpt.UpdatedBeforeUnix = &opts.UpdatedBeforeUnix + searchOpt.UpdatedBeforeUnix = optional.Some(opts.UpdatedBeforeUnix) } searchOpt.Paginator = opts.Paginator diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index 3acd3ade71..53b383c8d5 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -19,6 +19,10 @@ import ( const ( issueIndexerLatestVersion = 1 + // 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" + esMultiMatchTypePhrasePrefix = "phrase_prefix" ) var _ internal.Indexer = &Indexer{} @@ -141,7 +145,13 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query := elastic.NewBoolQuery() if options.Keyword != "" { - query.Must(elastic.NewMultiMatchQuery(options.Keyword, "title", "content", "comments")) + + searchType := esMultiMatchTypePhrasePrefix + if options.IsFuzzyKeyword { + searchType = esMultiMatchTypeBestFields + } + + query.Must(elastic.NewMultiMatchQuery(options.Keyword, "title", "content", "comments").Type(searchType)) } if len(options.RepoIDs) > 0 { @@ -185,43 +195,43 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query.Must(elastic.NewTermsQuery("milestone_id", toAnySlice(options.MilestoneIDs)...)) } - if options.ProjectID != nil { - query.Must(elastic.NewTermQuery("project_id", *options.ProjectID)) + if options.ProjectID.Has() { + query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value())) } - if options.ProjectBoardID != nil { - query.Must(elastic.NewTermQuery("project_board_id", *options.ProjectBoardID)) + if options.ProjectBoardID.Has() { + query.Must(elastic.NewTermQuery("project_board_id", options.ProjectBoardID.Value())) } - if options.PosterID != nil { - query.Must(elastic.NewTermQuery("poster_id", *options.PosterID)) + if options.PosterID.Has() { + query.Must(elastic.NewTermQuery("poster_id", options.PosterID.Value())) } - if options.AssigneeID != nil { - query.Must(elastic.NewTermQuery("assignee_id", *options.AssigneeID)) + if options.AssigneeID.Has() { + query.Must(elastic.NewTermQuery("assignee_id", options.AssigneeID.Value())) } - if options.MentionID != nil { - query.Must(elastic.NewTermQuery("mention_ids", *options.MentionID)) + if options.MentionID.Has() { + query.Must(elastic.NewTermQuery("mention_ids", options.MentionID.Value())) } - if options.ReviewedID != nil { - query.Must(elastic.NewTermQuery("reviewed_ids", *options.ReviewedID)) + if options.ReviewedID.Has() { + query.Must(elastic.NewTermQuery("reviewed_ids", options.ReviewedID.Value())) } - if options.ReviewRequestedID != nil { - query.Must(elastic.NewTermQuery("review_requested_ids", *options.ReviewRequestedID)) + if options.ReviewRequestedID.Has() { + query.Must(elastic.NewTermQuery("review_requested_ids", options.ReviewRequestedID.Value())) } - if options.SubscriberID != nil { - query.Must(elastic.NewTermQuery("subscriber_ids", *options.SubscriberID)) + if options.SubscriberID.Has() { + query.Must(elastic.NewTermQuery("subscriber_ids", options.SubscriberID.Value())) } - if options.UpdatedAfterUnix != nil || options.UpdatedBeforeUnix != nil { + if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() { q := elastic.NewRangeQuery("updated_unix") - if options.UpdatedAfterUnix != nil { - q.Gte(*options.UpdatedAfterUnix) + if options.UpdatedAfterUnix.Has() { + q.Gte(options.UpdatedAfterUnix.Value()) } - if options.UpdatedBeforeUnix != nil { - q.Lte(*options.UpdatedBeforeUnix) + if options.UpdatedBeforeUnix.Has() { + q.Lte(options.UpdatedBeforeUnix.Value()) } query.Must(q) } diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index 10ffa7cbe6..0d0cfc8516 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -134,63 +134,60 @@ func searchIssueInRepo(t *testing.T) { } func searchIssueByID(t *testing.T) { - int64Pointer := func(x int64) *int64 { - return &x - } tests := []struct { opts SearchOptions expectedIDs []int64 }{ { - SearchOptions{ - PosterID: int64Pointer(1), + opts: SearchOptions{ + PosterID: optional.Some(int64(1)), }, - []int64{11, 6, 3, 2, 1}, + expectedIDs: []int64{11, 6, 3, 2, 1}, }, { - SearchOptions{ - AssigneeID: int64Pointer(1), + opts: SearchOptions{ + AssigneeID: optional.Some(int64(1)), }, - []int64{6, 1}, + expectedIDs: []int64{6, 1}, }, { - SearchOptions{ - MentionID: int64Pointer(4), + opts: SearchOptions{ + MentionID: optional.Some(int64(4)), }, - []int64{1}, + expectedIDs: []int64{1}, }, { - SearchOptions{ - ReviewedID: int64Pointer(1), + opts: SearchOptions{ + ReviewedID: optional.Some(int64(1)), }, - []int64{}, + expectedIDs: []int64{}, }, { - SearchOptions{ - ReviewRequestedID: int64Pointer(1), + opts: SearchOptions{ + ReviewRequestedID: optional.Some(int64(1)), }, - []int64{12}, + expectedIDs: []int64{12}, }, { - SearchOptions{ - SubscriberID: int64Pointer(1), + opts: SearchOptions{ + SubscriberID: optional.Some(int64(1)), }, - []int64{11, 6, 5, 3, 2, 1}, + expectedIDs: []int64{11, 6, 5, 3, 2, 1}, }, { // issue 20 request user 15 and team 5 which user 15 belongs to // the review request number of issue 20 should be 1 - SearchOptions{ - ReviewRequestedID: int64Pointer(15), + opts: SearchOptions{ + ReviewRequestedID: optional.Some(int64(15)), }, - []int64{12, 20}, + expectedIDs: []int64{12, 20}, }, { // user 20 approved the issue 20, so return nothing - SearchOptions{ - ReviewRequestedID: int64Pointer(20), + opts: SearchOptions{ + ReviewRequestedID: optional.Some(int64(20)), }, - []int64{}, + expectedIDs: []int64{}, }, } @@ -318,16 +315,13 @@ func searchIssueByLabelID(t *testing.T) { } func searchIssueByTime(t *testing.T) { - int64Pointer := func(i int64) *int64 { - return &i - } tests := []struct { opts SearchOptions expectedIDs []int64 }{ { SearchOptions{ - UpdatedAfterUnix: int64Pointer(0), + UpdatedAfterUnix: optional.Some(int64(0)), }, []int64{22, 21, 17, 16, 15, 14, 13, 12, 11, 20, 6, 5, 19, 18, 10, 7, 4, 9, 8, 3, 2, 1}, }, @@ -363,28 +357,25 @@ func searchIssueWithOrder(t *testing.T) { } func searchIssueInProject(t *testing.T) { - int64Pointer := func(i int64) *int64 { - return &i - } tests := []struct { opts SearchOptions expectedIDs []int64 }{ { SearchOptions{ - ProjectID: int64Pointer(1), + ProjectID: optional.Some(int64(1)), }, []int64{5, 3, 2, 1}, }, { SearchOptions{ - ProjectBoardID: int64Pointer(1), + ProjectBoardID: optional.Some(int64(1)), }, []int64{1}, }, { SearchOptions{ - ProjectBoardID: int64Pointer(0), // issue with in default board + ProjectBoardID: optional.Some(int64(0)), // issue with in default board }, []int64{2}, }, diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index 947335d8ce..b7102c35af 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -74,6 +74,8 @@ type SearchResult struct { type SearchOptions struct { Keyword string // keyword to search + IsFuzzyKeyword bool // if false the levenshtein distance is 0 + RepoIDs []int64 // repository IDs which the issues belong to AllPublic bool // if include all public repositories @@ -87,22 +89,22 @@ type SearchOptions struct { MilestoneIDs []int64 // milestones the issues have - ProjectID *int64 // project the issues belong to - ProjectBoardID *int64 // project board the issues belong to + ProjectID optional.Option[int64] // project the issues belong to + ProjectBoardID optional.Option[int64] // project board the issues belong to - PosterID *int64 // poster of the issues + PosterID optional.Option[int64] // poster of the issues - AssigneeID *int64 // assignee of the issues, zero means no assignee + AssigneeID optional.Option[int64] // assignee of the issues, zero means no assignee - MentionID *int64 // mentioned user of the issues + MentionID optional.Option[int64] // mentioned user of the issues - ReviewedID *int64 // reviewer of the issues - ReviewRequestedID *int64 // requested reviewer of the issues + ReviewedID optional.Option[int64] // reviewer of the issues + ReviewRequestedID optional.Option[int64] // requested reviewer of the issues - SubscriberID *int64 // subscriber of the issues + SubscriberID optional.Option[int64] // subscriber of the issues - UpdatedAfterUnix *int64 - UpdatedBeforeUnix *int64 + UpdatedAfterUnix optional.Option[int64] + UpdatedBeforeUnix optional.Option[int64] db.Paginator diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 6724471539..91aafd589c 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -300,10 +300,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectID: func() *int64 { - id := int64(1) - return &id - }(), + ProjectID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -321,10 +318,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectID: func() *int64 { - id := int64(0) - return &id - }(), + ProjectID: optional.Some(int64(0)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -342,10 +336,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectBoardID: func() *int64 { - id := int64(1) - return &id - }(), + ProjectBoardID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -363,10 +354,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ProjectBoardID: func() *int64 { - id := int64(0) - return &id - }(), + ProjectBoardID: optional.Some(int64(0)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -384,10 +372,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - PosterID: func() *int64 { - id := int64(1) - return &id - }(), + PosterID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -405,10 +390,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - AssigneeID: func() *int64 { - id := int64(1) - return &id - }(), + AssigneeID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -426,10 +408,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - AssigneeID: func() *int64 { - id := int64(0) - return &id - }(), + AssigneeID: optional.Some(int64(0)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -447,10 +426,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - MentionID: func() *int64 { - id := int64(1) - return &id - }(), + MentionID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -468,10 +444,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ReviewedID: func() *int64 { - id := int64(1) - return &id - }(), + ReviewedID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -489,10 +462,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - ReviewRequestedID: func() *int64 { - id := int64(1) - return &id - }(), + ReviewRequestedID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -510,10 +480,7 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - SubscriberID: func() *int64 { - id := int64(1) - return &id - }(), + SubscriberID: optional.Some(int64(1)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) @@ -531,14 +498,8 @@ var cases = []*testIndexerCase{ Paginator: &db.ListOptions{ PageSize: 5, }, - UpdatedAfterUnix: func() *int64 { - var t int64 = 20 - return &t - }(), - UpdatedBeforeUnix: func() *int64 { - var t int64 = 30 - return &t - }(), + UpdatedAfterUnix: optional.Some(int64(20)), + UpdatedBeforeUnix: optional.Some(int64(30)), }, Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { assert.Equal(t, 5, len(result.Hits)) diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index 325883196b..b735c26968 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -5,6 +5,8 @@ package meilisearch import ( "context" + "errors" + "fmt" "strconv" "strings" @@ -16,12 +18,15 @@ import ( ) const ( - issueIndexerLatestVersion = 2 + issueIndexerLatestVersion = 3 // TODO: make this configurable if necessary maxTotalHits = 10000 ) +// ErrMalformedResponse is never expected as we initialize the indexer ourself and so define the types. +var ErrMalformedResponse = errors.New("meilisearch returned unexpected malformed content") + var _ internal.Indexer = &Indexer{} // Indexer implements Indexer interface @@ -47,6 +52,9 @@ func NewIndexer(url, apiKey, indexerName string) *Indexer { }, DisplayedAttributes: []string{ "id", + "title", + "content", + "comments", }, FilterableAttributes: []string{ "repo_id", @@ -163,41 +171,41 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( query.And(inner_meilisearch.NewFilterIn("milestone_id", options.MilestoneIDs...)) } - if options.ProjectID != nil { - query.And(inner_meilisearch.NewFilterEq("project_id", *options.ProjectID)) + if options.ProjectID.Has() { + query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value())) } - if options.ProjectBoardID != nil { - query.And(inner_meilisearch.NewFilterEq("project_board_id", *options.ProjectBoardID)) + if options.ProjectBoardID.Has() { + query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectBoardID.Value())) } - if options.PosterID != nil { - query.And(inner_meilisearch.NewFilterEq("poster_id", *options.PosterID)) + if options.PosterID.Has() { + query.And(inner_meilisearch.NewFilterEq("poster_id", options.PosterID.Value())) } - if options.AssigneeID != nil { - query.And(inner_meilisearch.NewFilterEq("assignee_id", *options.AssigneeID)) + if options.AssigneeID.Has() { + query.And(inner_meilisearch.NewFilterEq("assignee_id", options.AssigneeID.Value())) } - if options.MentionID != nil { - query.And(inner_meilisearch.NewFilterEq("mention_ids", *options.MentionID)) + if options.MentionID.Has() { + query.And(inner_meilisearch.NewFilterEq("mention_ids", options.MentionID.Value())) } - if options.ReviewedID != nil { - query.And(inner_meilisearch.NewFilterEq("reviewed_ids", *options.ReviewedID)) + if options.ReviewedID.Has() { + query.And(inner_meilisearch.NewFilterEq("reviewed_ids", options.ReviewedID.Value())) } - if options.ReviewRequestedID != nil { - query.And(inner_meilisearch.NewFilterEq("review_requested_ids", *options.ReviewRequestedID)) + if options.ReviewRequestedID.Has() { + query.And(inner_meilisearch.NewFilterEq("review_requested_ids", options.ReviewRequestedID.Value())) } - if options.SubscriberID != nil { - query.And(inner_meilisearch.NewFilterEq("subscriber_ids", *options.SubscriberID)) + if options.SubscriberID.Has() { + query.And(inner_meilisearch.NewFilterEq("subscriber_ids", options.SubscriberID.Value())) } - if options.UpdatedAfterUnix != nil { - query.And(inner_meilisearch.NewFilterGte("updated_unix", *options.UpdatedAfterUnix)) + if options.UpdatedAfterUnix.Has() { + query.And(inner_meilisearch.NewFilterGte("updated_unix", options.UpdatedAfterUnix.Value())) } - if options.UpdatedBeforeUnix != nil { - query.And(inner_meilisearch.NewFilterLte("updated_unix", *options.UpdatedBeforeUnix)) + if options.UpdatedBeforeUnix.Has() { + query.And(inner_meilisearch.NewFilterLte("updated_unix", options.UpdatedBeforeUnix.Value())) } if options.SortBy == "" { @@ -210,7 +218,14 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( skip, limit := indexer_internal.ParsePaginator(options.Paginator, maxTotalHits) - searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).Search(options.Keyword, &meilisearch.SearchRequest{ + keyword := options.Keyword + if !options.IsFuzzyKeyword { + // to make it non fuzzy ("typo tolerance" in meilisearch terms), we have to quote the keyword(s) + // https://www.meilisearch.com/docs/reference/api/search#phrase-search + keyword = doubleQuoteKeyword(keyword) + } + + searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).Search(keyword, &meilisearch.SearchRequest{ Filter: query.Statement(), Limit: int64(limit), Offset: int64(skip), @@ -221,11 +236,9 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( return nil, err } - hits := make([]internal.Match, 0, len(searchRes.Hits)) - for _, hit := range searchRes.Hits { - hits = append(hits, internal.Match{ - ID: int64(hit.(map[string]any)["id"].(float64)), - }) + hits, err := convertHits(searchRes) + if err != nil { + return nil, err } return &internal.SearchResult{ @@ -241,3 +254,36 @@ func parseSortBy(sortBy internal.SortBy) string { } return field + ":asc" } + +func doubleQuoteKeyword(k string) string { + kp := strings.Split(k, " ") + parts := 0 + for i := range kp { + part := strings.Trim(kp[i], "\"") + if part != "" { + kp[parts] = fmt.Sprintf(`"%s"`, part) + parts++ + } + } + return strings.Join(kp[:parts], " ") +} + +func convertHits(searchRes *meilisearch.SearchResponse) ([]internal.Match, error) { + hits := make([]internal.Match, 0, len(searchRes.Hits)) + for _, hit := range searchRes.Hits { + hit, ok := hit.(map[string]any) + if !ok { + return nil, ErrMalformedResponse + } + + issueID, ok := hit["id"].(float64) + if !ok { + return nil, ErrMalformedResponse + } + + hits = append(hits, internal.Match{ + ID: int64(issueID), + }) + } + return hits, nil +} diff --git a/modules/indexer/issues/meilisearch/meilisearch_test.go b/modules/indexer/issues/meilisearch/meilisearch_test.go index 8a6b0a61d3..81b825ae69 100644 --- a/modules/indexer/issues/meilisearch/meilisearch_test.go +++ b/modules/indexer/issues/meilisearch/meilisearch_test.go @@ -10,7 +10,11 @@ import ( "testing" "time" + "code.gitea.io/gitea/modules/indexer/issues/internal" "code.gitea.io/gitea/modules/indexer/issues/internal/tests" + + "github.com/meilisearch/meilisearch-go" + "github.com/stretchr/testify/assert" ) func TestMeilisearchIndexer(t *testing.T) { @@ -49,3 +53,44 @@ func TestMeilisearchIndexer(t *testing.T) { tests.TestIndexer(t, indexer) } + +func TestConvertHits(t *testing.T) { + _, err := convertHits(&meilisearch.SearchResponse{ + Hits: []any{"aa", "bb", "cc", "dd"}, + }) + assert.ErrorIs(t, err, ErrMalformedResponse) + + validResponse := &meilisearch.SearchResponse{ + Hits: []any{ + map[string]any{ + "id": float64(11), + "title": "a title", + "content": "issue body with no match", + "comments": []any{"hey whats up?", "I'm currently bowling", "nice"}, + }, + map[string]any{ + "id": float64(22), + "title": "Bowling as title", + "content": "", + "comments": []any{}, + }, + map[string]any{ + "id": float64(33), + "title": "Bowl-ing as fuzzy match", + "content": "", + "comments": []any{}, + }, + }, + } + hits, err := convertHits(validResponse) + assert.NoError(t, err) + assert.EqualValues(t, []internal.Match{{ID: 11}, {ID: 22}, {ID: 33}}, hits) +} + +func TestDoubleQuoteKeyword(t *testing.T) { + assert.EqualValues(t, "", doubleQuoteKeyword("")) + assert.EqualValues(t, `"a" "b" "c"`, doubleQuoteKeyword("a b c")) + assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g")) + assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword("a d g")) + assert.EqualValues(t, `"a" "d" "g"`, doubleQuoteKeyword(`a "" "d" """g`)) +} diff --git a/modules/markup/csv/csv.go b/modules/markup/csv/csv.go index 7af34a6cbc..570c4f4704 100644 --- a/modules/markup/csv/csv.go +++ b/modules/markup/csv/csv.go @@ -77,27 +77,62 @@ func writeField(w io.Writer, element, class, field string) error { } // Render implements markup.Renderer -func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { +func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { tmpBlock := bufio.NewWriter(output) + maxSize := setting.UI.CSV.MaxFileSize - // FIXME: don't read all to memory - rawBytes, err := io.ReadAll(input) + if maxSize == 0 { + return r.tableRender(ctx, input, tmpBlock) + } + + rawBytes, err := io.ReadAll(io.LimitReader(input, maxSize+1)) if err != nil { return err } - if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < int64(len(rawBytes)) { - if _, err := tmpBlock.WriteString("
"); err != nil {
-			return err
-		}
-		if _, err := tmpBlock.WriteString(html.EscapeString(string(rawBytes))); err != nil {
-			return err
-		}
-		_, err = tmpBlock.WriteString("
") + if int64(len(rawBytes)) <= maxSize { + return r.tableRender(ctx, bytes.NewReader(rawBytes), tmpBlock) + } + return r.fallbackRender(io.MultiReader(bytes.NewReader(rawBytes), input), tmpBlock) +} + +func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error { + _, err := tmpBlock.WriteString("
")
+	if err != nil {
 		return err
 	}
 
-	rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, bytes.NewReader(rawBytes))
+	scan := bufio.NewScanner(input)
+	scan.Split(bufio.ScanRunes)
+	for scan.Scan() {
+		switch scan.Text() {
+		case `&`:
+			_, err = tmpBlock.WriteString("&")
+		case `'`:
+			_, err = tmpBlock.WriteString("'") // "'" is shorter than "'" and apos was not in HTML until HTML5.
+		case `<`:
+			_, err = tmpBlock.WriteString("<")
+		case `>`:
+			_, err = tmpBlock.WriteString(">")
+		case `"`:
+			_, err = tmpBlock.WriteString(""") // """ is shorter than """.
+		default:
+			_, err = tmpBlock.Write(scan.Bytes())
+		}
+		if err != nil {
+			return err
+		}
+	}
+
+	_, err = tmpBlock.WriteString("
") + if err != nil { + return err + } + return tmpBlock.Flush() +} + +func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock *bufio.Writer) error { + rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input) if err != nil { return err } diff --git a/modules/markup/csv/csv_test.go b/modules/markup/csv/csv_test.go index 8c07184b21..3d12be477c 100644 --- a/modules/markup/csv/csv_test.go +++ b/modules/markup/csv/csv_test.go @@ -4,6 +4,8 @@ package markup import ( + "bufio" + "bytes" "strings" "testing" @@ -29,4 +31,12 @@ func TestRenderCSV(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, v, buf.String()) } + + t.Run("fallbackRender", func(t *testing.T) { + var buf bytes.Buffer + err := render.fallbackRender(strings.NewReader("1,\n2,"), bufio.NewWriter(&buf)) + assert.NoError(t, err) + want := "
1,<a>\n2,<b>
" + assert.Equal(t, want, buf.String()) + }) } diff --git a/modules/markup/html.go b/modules/markup/html.go index b7291823b5..0cfd8be590 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -609,7 +609,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) { if ok && strings.Contains(mention, "/") { mentionOrgAndTeam := strings.Split(mention, "/") if mentionOrgAndTeam[0][1:] == ctx.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") { - replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention")) + replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention")) node = node.NextSibling.NextSibling start = 0 continue @@ -620,7 +620,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) { mentionedUsername := mention[1:] if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) { - replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mentionedUsername), mention, "mention")) + replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention")) node = node.NextSibling.NextSibling } else { node = node.NextSibling @@ -898,9 +898,9 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { path = "pulls" } if ref.Owner == "" { - link = createLink(util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue") + link = createLink(util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue") } else { - link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue") + link = createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue") } } @@ -939,7 +939,7 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) { } reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha) - link := createLink(util.URLJoin(setting.AppSubURL, ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit") + link := createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit") replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link) node = node.NextSibling.NextSibling @@ -1166,7 +1166,7 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) { continue } - link := util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], "commit", hash) + link := util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], "commit", hash) replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit")) start = 0 node = node.NextSibling.NextSibling diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 93ba9d7667..e313be7040 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -287,6 +287,7 @@ func TestRender_IssueIndexPattern_Document(t *testing.T) { } func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) { + ctx.Links.AbsolutePrefix = true if ctx.Links.Base == "" { ctx.Links.Base = TestRepoURL } diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 132955c019..1db6952bed 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -43,7 +43,8 @@ func TestRender_Commits(t *testing.T) { Ctx: git.DefaultContext, RelativePath: ".md", Links: markup.Links{ - Base: markup.TestRepoURL, + AbsolutePrefix: true, + Base: markup.TestRepoURL, }, Metas: localMetas, }, input) @@ -96,7 +97,8 @@ func TestRender_CrossReferences(t *testing.T) { Ctx: git.DefaultContext, RelativePath: "a.md", Links: markup.Links{ - Base: setting.AppSubURL, + AbsolutePrefix: true, + Base: setting.AppSubURL, }, Metas: localMetas, }, input) @@ -579,7 +581,8 @@ func TestPostProcess_RenderDocument(t *testing.T) { err := markup.PostProcess(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: "https://example.com", + AbsolutePrefix: true, + Base: "https://example.com", }, Metas: localMetas, }, strings.NewReader(input), &res) diff --git a/modules/markup/markdown/ast.go b/modules/markup/markdown/ast.go index 72d32600f5..7f0ac6a92c 100644 --- a/modules/markup/markdown/ast.go +++ b/modules/markup/markdown/ast.go @@ -174,10 +174,3 @@ func NewColorPreview(color []byte) *ColorPreview { Color: color, } } - -// IsColorPreview returns true if the given node implements the ColorPreview interface, -// otherwise false. -func IsColorPreview(node ast.Node) bool { - _, ok := node.(*ColorPreview) - return ok -} diff --git a/modules/markup/markdown/callout/github.go b/modules/markup/markdown/callout/github.go index 58f1fc960f..78f1db7e96 100644 --- a/modules/markup/markdown/callout/github.go +++ b/modules/markup/markdown/callout/github.go @@ -71,6 +71,7 @@ func (g *GitHubCalloutTransformer) Transform(node *ast.Document, reader text.Rea v.SetAttributeString("class", []byte("gt-py-3 attention attention-"+attentionType)) // create an emphasis to make it bold + attentionParagraph := ast.NewParagraph() emphasis := ast.NewEmphasis(2) emphasis.SetAttributeString("class", []byte("attention-"+attentionType)) firstParagraph.InsertBefore(firstParagraph, firstTextNode, emphasis) @@ -78,14 +79,11 @@ func (g *GitHubCalloutTransformer) Transform(node *ast.Document, reader text.Rea // capitalize first letter attentionText := ast.NewString([]byte(strings.ToUpper(string(attentionType[0])) + attentionType[1:])) - // replace the ![TYPE] with icon+Type + // replace the ![TYPE] with a dedicated paragraph of icon+Type emphasis.AppendChild(emphasis, attentionText) - for i := 0; i < 2; i++ { - lineBreak := ast.NewText() - lineBreak.SetSoftLineBreak(true) - firstParagraph.InsertAfter(firstParagraph, emphasis, lineBreak) - } - firstParagraph.InsertBefore(firstParagraph, emphasis, NewAttention(attentionType)) + attentionParagraph.AppendChild(attentionParagraph, NewAttention(attentionType)) + attentionParagraph.AppendChild(attentionParagraph, emphasis) + firstParagraph.Parent().InsertBefore(firstParagraph.Parent(), firstParagraph, attentionParagraph) firstParagraph.RemoveChild(firstParagraph, firstTextNode) firstParagraph.RemoveChild(firstParagraph, secondTextNode) firstParagraph.RemoveChild(firstParagraph, thirdTextNode) diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index 7750279ef2..77c876dfff 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -105,7 +105,8 @@ func SpecializedMarkdown() goldmark.Markdown { } // include language-x class as part of commonmark spec - _, err = w.WriteString(``) + // the "display" class is used by "js/markup/math.js" to render the code element as a block + _, err = w.WriteString(``) if err != nil { return } diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index e7c496a913..ddb6814cd9 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -23,12 +23,11 @@ import ( ) const ( - AppURL = "http://localhost:3000/" - Repo = "gogits/gogs" - AppSubURL = AppURL + Repo + "/" + AppURL = "http://localhost:3000/" + FullURL = AppURL + "gogits/gogs/" ) -// these values should match the Repo const above +// these values should match the const above var localMetas = map[string]string{ "user": "gogits", "repo": "gogs", @@ -50,13 +49,12 @@ func TestMain(m *testing.M) { func TestRender_StandardLinks(t *testing.T) { setting.AppURL = AppURL - setting.AppSubURL = AppSubURL test := func(input, expected, expectedWiki string) { buffer, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: setting.AppSubURL, + Base: FullURL, }, }, input) assert.NoError(t, err) @@ -65,7 +63,7 @@ func TestRender_StandardLinks(t *testing.T) { buffer, err = markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: setting.AppSubURL, + Base: FullURL, }, IsWiki: true, }, input) @@ -76,8 +74,8 @@ func TestRender_StandardLinks(t *testing.T) { googleRendered := `

https://google.com/

` test("", googleRendered, googleRendered) - lnk := util.URLJoin(AppSubURL, "WikiPage") - lnkWiki := util.URLJoin(AppSubURL, "wiki", "WikiPage") + lnk := util.URLJoin(FullURL, "WikiPage") + lnkWiki := util.URLJoin(FullURL, "wiki", "WikiPage") test("[WikiPage](WikiPage)", `

WikiPage

`, `

WikiPage

`) @@ -85,13 +83,12 @@ func TestRender_StandardLinks(t *testing.T) { func TestRender_Images(t *testing.T) { setting.AppURL = AppURL - setting.AppSubURL = AppSubURL test := func(input, expected string) { buffer, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: setting.AppSubURL, + Base: FullURL, }, }, input) assert.NoError(t, err) @@ -101,7 +98,7 @@ func TestRender_Images(t *testing.T) { url := "../../.images/src/02/train.jpg" title := "Train" href := "https://gitea.io" - result := util.URLJoin(AppSubURL, url) + result := util.URLJoin(FullURL, url) // hint: With Markdown v2.5.2, there is a new syntax: [link](URL){:target="_blank"} , but we do not support it now test( @@ -134,11 +131,11 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
  • Links, Language bindings, Engine bindings
  • Tips
  • -

    See commit 65f1bf27bc

    +

    See commit 65f1bf27bc

    Ideas and codes

      -
    • Bezier widget (by @r-lyeh) ocornut/imgui#786
    • -
    • Bezier widget (by @r-lyeh) #786
    • +
    • Bezier widget (by @r-lyeh) ocornut/imgui#786
    • +
    • Bezier widget (by @r-lyeh) #786
    • Node graph editors https://github.com/ocornut/imgui/issues/306
    • Memory Editor
    • Plot var helper
    • @@ -291,15 +288,14 @@ This PR has been generated by [Renovate Bot](https://github.com/renovatebot/reno func TestTotal_RenderWiki(t *testing.T) { setting.AppURL = AppURL - setting.AppSubURL = AppSubURL - answers := testAnswers(util.URLJoin(AppSubURL, "wiki"), util.URLJoin(AppSubURL, "wiki", "raw")) + answers := testAnswers(util.URLJoin(FullURL, "wiki"), util.URLJoin(FullURL, "wiki", "raw")) for i := 0; i < len(sameCases); i++ { line, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: setting.AppSubURL, + Base: FullURL, }, Metas: localMetas, IsWiki: true, @@ -312,12 +308,12 @@ func TestTotal_RenderWiki(t *testing.T) { // Guard wiki sidebar: special syntax `[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`, // rendered - `

      Guardfile-DSL / Configuring-Guard

      + `

      Guardfile-DSL / Configuring-Guard

      `, // special syntax `[[Name|Link]]`, // rendered - `

      Name

      + `

      Name

      `, } @@ -325,7 +321,7 @@ func TestTotal_RenderWiki(t *testing.T) { line, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: setting.AppSubURL, + Base: FullURL, }, IsWiki: true, }, testCases[i]) @@ -336,15 +332,14 @@ func TestTotal_RenderWiki(t *testing.T) { func TestTotal_RenderString(t *testing.T) { setting.AppURL = AppURL - setting.AppSubURL = AppSubURL - answers := testAnswers(util.URLJoin(AppSubURL, "src", "master"), util.URLJoin(AppSubURL, "media", "master")) + answers := testAnswers(util.URLJoin(FullURL, "src", "master"), util.URLJoin(FullURL, "media", "master")) for i := 0; i < len(sameCases); i++ { line, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: AppSubURL, + Base: FullURL, BranchPath: "master", }, Metas: localMetas, @@ -359,7 +354,7 @@ func TestTotal_RenderString(t *testing.T) { line, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: AppSubURL, + Base: FullURL, }, }, testCases[i]) assert.NoError(t, err) @@ -1176,15 +1171,13 @@ space

      func TestCustomMarkdownURL(t *testing.T) { defer test.MockVariableValue(&setting.Markdown.CustomURLSchemes, []string{"abp"})() - setting.AppURL = AppURL - setting.AppSubURL = AppSubURL test := func(input, expected string) { buffer, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ - Base: setting.AppSubURL, + Base: FullURL, BranchPath: "branch/main", }, }, input) diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 5a7adcc553..0f0bf55740 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -82,9 +82,17 @@ type RenderContext struct { } type Links struct { - Base string - BranchPath string - TreePath string + AbsolutePrefix bool + Base string + BranchPath string + TreePath string +} + +func (l *Links) Prefix() string { + if l.AbsolutePrefix { + return setting.AppURL + } + return setting.AppSubURL } func (l *Links) HasBranchInfo() bool { diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index ffc33c3b8e..b942406901 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -105,18 +105,12 @@ func createDefaultPolicy() *bluemonday.Policy { // Allow icons policy.AllowAttrs("class").Matching(regexp.MustCompile(`^icon(\s+[\p{L}\p{N}_-]+)+$`)).OnElements("i") - // Allow unlabelled labels - policy.AllowNoAttrs().OnElements("label") - // Allow classes for emojis policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img") // Allow icons, emojis, chroma syntax and keyword markup on span policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span") - // Allow 'style' attribute on text elements. - policy.AllowAttrs("style").OnElements("span", "p") - // Allow 'color' and 'background-color' properties for the style attribute on text elements. policy.AllowStyles("color", "background-color").OnElements("span", "p") @@ -144,7 +138,7 @@ func createDefaultPolicy() *bluemonday.Policy { generalSafeElements := []string{ "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "br", "b", "i", "strong", "em", "a", "pre", "code", "img", "tt", - "div", "ins", "del", "sup", "sub", "p", "ol", "ul", "table", "thead", "tbody", "tfoot", "blockquote", + "div", "ins", "del", "sup", "sub", "p", "ol", "ul", "table", "thead", "tbody", "tfoot", "blockquote", "label", "dl", "dt", "dd", "kbd", "q", "samp", "var", "hr", "ruby", "rt", "rp", "li", "tr", "td", "th", "s", "strike", "summary", "details", "caption", "figure", "figcaption", "abbr", "bdo", "cite", "dfn", "mark", "small", "span", "time", "video", "wbr", diff --git a/modules/queue/workergroup.go b/modules/queue/workergroup.go index e3801ef2b2..8c9dd274f0 100644 --- a/modules/queue/workergroup.go +++ b/modules/queue/workergroup.go @@ -146,8 +146,6 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) { log.Debug("Queue %q starts new worker", q.GetName()) defer log.Debug("Queue %q stops idle worker", q.GetName()) - atomic.AddInt32(&q.workerStartedCounter, 1) // Only increase counter, used for debugging - t := time.NewTicker(workerIdleDuration) defer t.Stop() diff --git a/modules/queue/workerqueue.go b/modules/queue/workerqueue.go index 4160622d81..b28fd88027 100644 --- a/modules/queue/workerqueue.go +++ b/modules/queue/workerqueue.go @@ -40,8 +40,6 @@ type WorkerPoolQueue[T any] struct { workerMaxNum int workerActiveNum int workerNumMu sync.Mutex - - workerStartedCounter int32 } type flushType chan struct{} diff --git a/modules/queue/workerqueue_test.go b/modules/queue/workerqueue_test.go index e09669c542..9898ceb873 100644 --- a/modules/queue/workerqueue_test.go +++ b/modules/queue/workerqueue_test.go @@ -4,7 +4,9 @@ package queue import ( + "bytes" "context" + "runtime" "strconv" "sync" "testing" @@ -14,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func runWorkerPoolQueue[T any](q *WorkerPoolQueue[T]) func() { @@ -249,23 +252,40 @@ func TestWorkerPoolQueueShutdown(t *testing.T) { } func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) { - defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)() + defer test.MockVariableValue(&workerIdleDuration, 1*time.Millisecond)() + chGoroutineIDs := make(chan string) handler := func(items ...int) (unhandled []int) { - time.Sleep(50 * time.Millisecond) + time.Sleep(10 * workerIdleDuration) + chGoroutineIDs <- goroutineID() // hacky way to identify a worker return nil } q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false) stop := runWorkerPoolQueue(q) - for i := 0; i < 20; i++ { + + const workloadSize = 12 + for i := 0; i < workloadSize; i++ { assert.NoError(t, q.Push(i)) } - time.Sleep(500 * time.Millisecond) - assert.EqualValues(t, 2, q.GetWorkerNumber()) - assert.EqualValues(t, 2, q.GetWorkerActiveNumber()) - // when the queue never becomes empty, the existing workers should keep working - assert.EqualValues(t, 2, q.workerStartedCounter) + workerIDs := make(map[string]struct{}) + for i := 0; i < workloadSize; i++ { + c := <-chGoroutineIDs + workerIDs[c] = struct{}{} + t.Logf("%d workers: overall=%d current=%d", i, len(workerIDs), q.GetWorkerNumber()) + + // ensure that no more than qs.MaxWorkers workers are created over the whole lifetime of the queue + // (otherwise it would mean that some workers got shut down while the queue was full) + require.LessOrEqual(t, len(workerIDs), q.GetWorkerMaxNumber()) + } + close(chGoroutineIDs) + stop() } + +func goroutineID() string { + var buffer [31]byte + _ = runtime.Stack(buffer[:], false) + return string(bytes.Fields(buffer[10:])[0]) +} diff --git a/modules/setting/admin.go b/modules/setting/admin.go index c292db9c8f..35ffa9efbf 100644 --- a/modules/setting/admin.go +++ b/modules/setting/admin.go @@ -22,5 +22,6 @@ func loadAdminFrom(rootCfg ConfigProvider) { const ( UserFeatureDeletion = "deletion" + UserFeatureManageSSHKeys = "manage_ssh_keys" UserFeatureManageGPGKeys = "manage_gpg_keys" ) diff --git a/modules/setting/attachment.go b/modules/setting/attachment.go index 934d4d7f46..0fdabb5032 100644 --- a/modules/setting/attachment.go +++ b/modules/setting/attachment.go @@ -12,7 +12,7 @@ var Attachment = struct { Enabled bool }{ Storage: &Storage{}, - AllowedTypes: ".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip", + AllowedTypes: ".cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip", MaxSize: 2048, MaxFiles: 5, Enabled: true, @@ -25,7 +25,7 @@ func loadAttachmentFrom(rootCfg ConfigProvider) (err error) { return err } - Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip") + Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip") Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(2048) Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5) Attachment.Enabled = sec.Key("ENABLED").MustBool(true) diff --git a/modules/setting/config.go b/modules/setting/config.go index db189f44ac..03558574c2 100644 --- a/modules/setting/config.go +++ b/modules/setting/config.go @@ -15,8 +15,45 @@ type PictureStruct struct { EnableFederatedAvatar *config.Value[bool] } +type OpenWithEditorApp struct { + DisplayName string + OpenURL string +} + +type OpenWithEditorAppsType []OpenWithEditorApp + +func (t OpenWithEditorAppsType) ToTextareaString() string { + ret := "" + for _, app := range t { + ret += app.DisplayName + " = " + app.OpenURL + "\n" + } + return ret +} + +func DefaultOpenWithEditorApps() OpenWithEditorAppsType { + return OpenWithEditorAppsType{ + { + DisplayName: "VS Code", + OpenURL: "vscode://vscode.git/clone?url={url}", + }, + { + DisplayName: "VSCodium", + OpenURL: "vscodium://vscode.git/clone?url={url}", + }, + { + DisplayName: "Intellij IDEA", + OpenURL: "jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo={url}", + }, + } +} + +type RepositoryStruct struct { + OpenWithEditorApps *config.Value[OpenWithEditorAppsType] +} + type ConfigStruct struct { - Picture *PictureStruct + Picture *PictureStruct + Repository *RepositoryStruct } var ( @@ -28,8 +65,11 @@ func initDefaultConfig() { config.SetCfgSecKeyGetter(&cfgSecKeyGetter{}) defaultConfig = &ConfigStruct{ Picture: &PictureStruct{ - DisableGravatar: config.Bool(false, config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}, "picture.disable_gravatar"), - EnableFederatedAvatar: config.Bool(false, config.CfgSecKey{Sec: "picture", Key: "ENABLE_FEDERATED_AVATAR"}, "picture.enable_federated_avatar"), + DisableGravatar: config.ValueJSON[bool]("picture.disable_gravatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}), + EnableFederatedAvatar: config.ValueJSON[bool]("picture.enable_federated_avatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "ENABLE_FEDERATED_AVATAR"}), + }, + Repository: &RepositoryStruct{ + OpenWithEditorApps: config.ValueJSON[OpenWithEditorAppsType]("repository.open-with.editor-apps"), }, } } @@ -42,6 +82,9 @@ func Config() *ConfigStruct { type cfgSecKeyGetter struct{} func (c cfgSecKeyGetter) GetValue(sec, key string) (v string, has bool) { + if key == "" { + return "", false + } cfgSec, err := CfgProvider.GetSection(sec) if err != nil { log.Error("Unable to get config section: %q", sec) diff --git a/modules/setting/config/value.go b/modules/setting/config/value.go index 817fcdb786..f0ec120544 100644 --- a/modules/setting/config/value.go +++ b/modules/setting/config/value.go @@ -5,8 +5,11 @@ package config import ( "context" - "strconv" "sync" + + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" ) type CfgSecKey struct { @@ -23,14 +26,14 @@ type Value[T any] struct { revision int } -func (value *Value[T]) parse(s string) (v T) { - switch any(v).(type) { - case bool: - b, _ := strconv.ParseBool(s) - return any(b).(T) - default: - panic("unsupported config type, please complete the code") +func (value *Value[T]) parse(key, valStr string) (v T) { + v = value.def + if valStr != "" { + if err := json.Unmarshal(util.UnsafeStringToBytes(valStr), &v); err != nil { + log.Error("Unable to unmarshal json config for key %q, err: %v", key, err) + } } + return v } func (value *Value[T]) Value(ctx context.Context) (v T) { @@ -62,7 +65,7 @@ func (value *Value[T]) Value(ctx context.Context) (v T) { if valStr == nil { v = value.def } else { - v = value.parse(*valStr) + v = value.parse(value.dynKey, *valStr) } value.mu.Lock() @@ -76,6 +79,16 @@ func (value *Value[T]) DynKey() string { return value.dynKey } -func Bool(def bool, cfgSecKey CfgSecKey, dynKey string) *Value[bool] { - return &Value[bool]{def: def, cfgSecKey: cfgSecKey, dynKey: dynKey} +func (value *Value[T]) WithDefault(def T) *Value[T] { + value.def = def + return value +} + +func (value *Value[T]) WithFileConfig(cfgSecKey CfgSecKey) *Value[T] { + value.cfgSecKey = cfgSecKey + return value +} + +func ValueJSON[T any](dynKey string) *Value[T] { + return &Value[T]{dynKey: dynKey} } diff --git a/modules/setting/i18n.go b/modules/setting/i18n.go index c3076c0ab7..1639f3ae5b 100644 --- a/modules/setting/i18n.go +++ b/modules/setting/i18n.go @@ -20,11 +20,14 @@ var defaultI18nLangNames = []string{ "pt-BR", "Português do Brasil", "pt-PT", "Português de Portugal", "pl-PL", "Polski", - "bg-BG", "Български", + "bg", "Български", "it-IT", "Italiano", "fi-FI", "Suomi", + "fil", "Filipino", + "eo", "Esperanto", "tr-TR", "Türkçe", "cs-CZ", "Čeština", + "sl", "Slovenščina", "sv-SE", "Svenska", "ko-KR", "한국어", "el-GR", "Ελληνικά", diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 7a07fec85c..50f0fd704c 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -7,7 +7,6 @@ import ( "os/exec" "path" "path/filepath" - "slices" "strings" "code.gitea.io/gitea/modules/log" @@ -20,10 +19,12 @@ const ( RepoCreatingPublic = "public" ) -var RecognisedRepositoryDownloadOrCloneMethods = []string{"download-zip", "download-targz", "download-bundle", "vscode-clone", "vscodium-clone", "cite"} +// MaxUserCardsPerPage sets maximum amount of watchers and stargazers shown per page +// those pages use 2 or 3 column layout, so the value should be divisible by 2 and 3 +var MaxUserCardsPerPage = 36 -// ItemsPerPage maximum items per page in forks, watchers and stars of a repo -const ItemsPerPage = 40 +// MaxForksPerPage sets maximum amount of forks shown per page +var MaxForksPerPage = 40 // Repository settings var ( @@ -46,7 +47,6 @@ var ( DisabledRepoUnits []string DefaultRepoUnits []string DefaultForkRepoUnits []string - DownloadOrCloneMethods []string PrefixArchiveFiles bool DisableMigrations bool DisableStars bool @@ -169,7 +169,6 @@ var ( DisabledRepoUnits: []string{}, DefaultRepoUnits: []string{}, DefaultForkRepoUnits: []string{}, - DownloadOrCloneMethods: []string{"download-zip", "download-targz", "download-bundle", "vscode-clone"}, PrefixArchiveFiles: true, DisableMigrations: false, DisableStars: false, @@ -373,12 +372,5 @@ func loadRepositoryFrom(rootCfg ConfigProvider) { if err := loadRepoArchiveFrom(rootCfg); err != nil { log.Fatal("loadRepoArchiveFrom: %v", err) } - - for _, method := range Repository.DownloadOrCloneMethods { - if !slices.Contains(RecognisedRepositoryDownloadOrCloneMethods, method) { - log.Error("Unrecognised repository download or clone method: %s", method) - } - } - Repository.EnableFlags = sec.Key("ENABLE_FLAGS").MustBool() } diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 87e7b08c6e..781e794d5d 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/user" + "code.gitea.io/gitea/modules/util" ) var ForgejoVersion = "1.0.0" @@ -161,9 +162,11 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error { func loadRunModeFrom(rootCfg ConfigProvider) { rootSec := rootCfg.Section("") RunUser = rootSec.Key("RUN_USER").MustString(user.CurrentUsername()) + // The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches. // Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly. unsafeAllowRunAsRoot := ConfigSectionKeyBool(rootSec, "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT") + unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || util.OptionalBoolParse(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value() RunMode = os.Getenv("GITEA_RUN_MODE") if RunMode == "" { RunMode = rootSec.Key("RUN_MODE").MustString("prod") diff --git a/modules/setting/storage.go b/modules/setting/storage.go index f937c7cff3..1e2d28a88b 100644 --- a/modules/setting/storage.go +++ b/modules/setting/storage.go @@ -41,6 +41,7 @@ type MinioStorageConfig struct { AccessKeyID string `ini:"MINIO_ACCESS_KEY_ID" json:",omitempty"` SecretAccessKey string `ini:"MINIO_SECRET_ACCESS_KEY" json:",omitempty"` Bucket string `ini:"MINIO_BUCKET" json:",omitempty"` + BucketLookup string `ini:"MINIO_BUCKET_LOOKUP" json:",omitempty"` Location string `ini:"MINIO_LOCATION" json:",omitempty"` BasePath string `ini:"MINIO_BASE_PATH" json:",omitempty"` UseSSL bool `ini:"MINIO_USE_SSL"` @@ -78,6 +79,7 @@ func getDefaultStorageSection(rootCfg ConfigProvider) ConfigSection { storageSec.Key("MINIO_ACCESS_KEY_ID").MustString("") storageSec.Key("MINIO_SECRET_ACCESS_KEY").MustString("") storageSec.Key("MINIO_BUCKET").MustString("gitea") + storageSec.Key("MINIO_BUCKET_LOOKUP").MustString("auto") storageSec.Key("MINIO_LOCATION").MustString("us-east-1") storageSec.Key("MINIO_USE_SSL").MustBool(false) storageSec.Key("MINIO_INSECURE_SKIP_VERIFY").MustBool(false) diff --git a/modules/storage/minio.go b/modules/storage/minio.go index b58ab67dc7..0b65577cb5 100644 --- a/modules/storage/minio.go +++ b/modules/storage/minio.go @@ -82,14 +82,26 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, if config.ChecksumAlgorithm != "" && config.ChecksumAlgorithm != "default" && config.ChecksumAlgorithm != "md5" { return nil, fmt.Errorf("invalid minio checksum algorithm: %s", config.ChecksumAlgorithm) } + var lookup minio.BucketLookupType + switch config.BucketLookup { + case "auto", "": + lookup = minio.BucketLookupAuto + case "dns": + lookup = minio.BucketLookupDNS + case "path": + lookup = minio.BucketLookupPath + default: + return nil, fmt.Errorf("invalid minio bucket lookup type %s", config.BucketLookup) + } log.Info("Creating Minio storage at %s:%s with base path %s", config.Endpoint, config.Bucket, config.BasePath) minioClient, err := minio.New(config.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(config.AccessKeyID, config.SecretAccessKey, ""), - Secure: config.UseSSL, - Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}}, - Region: config.Location, + Creds: credentials.NewStaticV4(config.AccessKeyID, config.SecretAccessKey, ""), + Secure: config.UseSSL, + Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}}, + Region: config.Location, + BucketLookup: lookup, }) if err != nil { return nil, convertMinioErr(err) diff --git a/modules/storage/minio_test.go b/modules/storage/minio_test.go index 2364ced0ef..2e1a3028c7 100644 --- a/modules/storage/minio_test.go +++ b/modules/storage/minio_test.go @@ -31,6 +31,23 @@ func TestMinioStorageIterator(t *testing.T) { }) } +func TestVirtualHostMinioStorage(t *testing.T) { + if os.Getenv("CI") == "" { + t.Skip("minioStorage not present outside of CI") + return + } + testStorageIterator(t, setting.MinioStorageType, &setting.Storage{ + MinioConfig: setting.MinioStorageConfig{ + Endpoint: "minio:9000", + AccessKeyID: "123456", + SecretAccessKey: "12345678", + Bucket: "gitea", + Location: "us-east-1", + BucketLookup: "dns", + }, + }) +} + func TestMinioStoragePath(t *testing.T) { m := &MinioStorage{basePath: ""} assert.Equal(t, "", m.buildMinioPath("/")) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 9afcb96fc9..18a2993fb8 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -38,7 +38,7 @@ func NewFuncMap() template.FuncMap { "SafeHTML": SafeHTML, "HTMLFormat": HTMLFormat, "HTMLEscape": HTMLEscape, - "QueryEscape": url.QueryEscape, + "QueryEscape": QueryEscape, "JSEscape": JSEscapeSafe, "SanitizeHTML": SanitizeHTML, "URLJoin": util.URLJoin, @@ -211,14 +211,8 @@ func SafeHTML(s any) template.HTML { } // SanitizeHTML sanitizes the input by pre-defined markdown rules -func SanitizeHTML(s any) template.HTML { - switch v := s.(type) { - case string: - return template.HTML(markup.Sanitize(v)) - case template.HTML: - return template.HTML(markup.Sanitize(string(v))) - } - panic(fmt.Sprintf("unexpected type %T", s)) +func SanitizeHTML(s string) template.HTML { + return template.HTML(markup.Sanitize(s)) } func HTMLEscape(s any) template.HTML { @@ -235,6 +229,10 @@ func JSEscapeSafe(s string) template.HTML { return template.HTML(template.JSEscapeString(s)) } +func QueryEscape(s string) template.URL { + return template.URL(url.QueryEscape(s)) +} + // DotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent autolinkers from detecting these as urls func DotEscape(raw string) string { return strings.ReplaceAll(raw, ".", "\u200d.\u200d") diff --git a/modules/templates/helper_test.go b/modules/templates/helper_test.go index 3365278ac2..64f29d033e 100644 --- a/modules/templates/helper_test.go +++ b/modules/templates/helper_test.go @@ -64,5 +64,4 @@ func TestHTMLFormat(t *testing.T) { func TestSanitizeHTML(t *testing.T) { assert.Equal(t, template.HTML(`link xss
      inline
      `), SanitizeHTML(`link xss
      inline
      `)) - assert.Equal(t, template.HTML(`link xss
      inline
      `), SanitizeHTML(template.HTML(`link xss
      inline
      `))) } diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 465640e08d..5fea4d9a16 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -61,7 +61,7 @@ func TestMain(m *testing.M) { func TestApostrophesInMentions(t *testing.T) { rendered := RenderMarkdownToHtml(context.Background(), "@mention-user's comment") - assert.EqualValues(t, "

      @mention-user's comment

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

      @mention-user's comment

      \n"), rendered) } func TestRenderCommitBody(t *testing.T) { @@ -122,21 +122,21 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a582 com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit 👍 mail@domain.com -@mention-user test -#123 +@mention-user test +#123 space` assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas)) } func TestRenderCommitMessage(t *testing.T) { - expected := `space @mention-user ` + expected := `space @mention-user ` assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas)) } func TestRenderCommitMessageLinkSubject(t *testing.T) { - expected := `space @mention-user` + expected := `space @mention-user` assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas)) } @@ -160,14 +160,14 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit 👍 mail@domain.com @mention-user test -#123 +#123 space ` assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas)) } func TestRenderMarkdownToHtml(t *testing.T) { - expected := `

      space @mention-user
      + expected := `

      space @mention-user
      /just/a/path.bin https://example.com/file.bin local link @@ -184,7 +184,7 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a582 com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit 👍 mail@domain.com -@mention-user test +@mention-user test #123 space

      ` diff --git a/modules/timeutil/datetime.go b/modules/timeutil/datetime.go index 62b94f7cf4..c089173560 100644 --- a/modules/timeutil/datetime.go +++ b/modules/timeutil/datetime.go @@ -13,6 +13,8 @@ import ( // DateTime renders an absolute time HTML element by datetime. func DateTime(format string, datetime any, extraAttrs ...string) template.HTML { + // TODO: remove the extraAttrs argument, it's not used in any call to DateTime + if p, ok := datetime.(*time.Time); ok { datetime = *p } @@ -51,18 +53,16 @@ func DateTime(format string, datetime any, extraAttrs ...string) template.HTML { attrs := make([]string, 0, 10+len(extraAttrs)) attrs = append(attrs, extraAttrs...) - attrs = append(attrs, `data-tooltip-content`, `data-tooltip-interactive="true"`) - attrs = append(attrs, `format="datetime"`, `weekday=""`, `year="numeric"`) + attrs = append(attrs, `weekday=""`, `year="numeric"`) switch format { - case "short": - attrs = append(attrs, `month="short"`, `day="numeric"`) - case "long": - attrs = append(attrs, `month="long"`, `day="numeric"`) - case "full": - attrs = append(attrs, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`) + case "short", "long": // date only + attrs = append(attrs, `month="`+format+`"`, `day="numeric"`) + return template.HTML(fmt.Sprintf(`%s`, strings.Join(attrs, " "), datetimeEscaped, textEscaped)) + case "full": // full date including time + attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`) + return template.HTML(fmt.Sprintf(`%s`, strings.Join(attrs, " "), datetimeEscaped, textEscaped)) default: panic(fmt.Sprintf("Unsupported format %s", format)) } - return template.HTML(fmt.Sprintf(`%s`, strings.Join(attrs, " "), datetimeEscaped, textEscaped)) } diff --git a/modules/timeutil/datetime_test.go b/modules/timeutil/datetime_test.go index 26494b8475..ac2ce35ba2 100644 --- a/modules/timeutil/datetime_test.go +++ b/modules/timeutil/datetime_test.go @@ -18,6 +18,7 @@ func TestDateTime(t *testing.T) { defer test.MockVariableValue(&setting.DefaultUILocation, testTz)() refTimeStr := "2018-01-01T00:00:00Z" + refDateStr := "2018-01-01" refTime, _ := time.Parse(time.RFC3339, refTimeStr) refTimeStamp := TimeStamp(refTime.Unix()) @@ -27,17 +28,20 @@ func TestDateTime(t *testing.T) { assert.EqualValues(t, "-", DateTime("short", TimeStamp(0))) actual := DateTime("short", "invalid") - assert.EqualValues(t, `invalid`, actual) + assert.EqualValues(t, `invalid`, actual) actual = DateTime("short", refTimeStr) - assert.EqualValues(t, `2018-01-01T00:00:00Z`, actual) + assert.EqualValues(t, `2018-01-01T00:00:00Z`, actual) actual = DateTime("short", refTime) - assert.EqualValues(t, `2018-01-01`, actual) + assert.EqualValues(t, `2018-01-01`, actual) + + actual = DateTime("short", refDateStr) + assert.EqualValues(t, `2018-01-01`, actual) actual = DateTime("short", refTimeStamp) - assert.EqualValues(t, `2017-12-31`, actual) + assert.EqualValues(t, `2017-12-31`, actual) actual = DateTime("full", refTimeStamp) - assert.EqualValues(t, `2017-12-31 19:00:00 -05:00`, actual) + assert.EqualValues(t, `2017-12-31 19:00:00 -05:00`, actual) } diff --git a/modules/timeutil/since.go b/modules/timeutil/since.go index dfaa0e3e3a..dba42c793a 100644 --- a/modules/timeutil/since.go +++ b/modules/timeutil/since.go @@ -126,7 +126,7 @@ func timeSinceUnix(then, now time.Time, _ translation.Locale) template.HTML { } // declare data-tooltip-content attribute to switch from "title" tooltip to "tippy" tooltip - htm := fmt.Sprintf(`%s`, + htm := fmt.Sprintf(`%s`, attrs, then.Format(time.RFC3339), friendlyText) return template.HTML(htm) } @@ -134,7 +134,7 @@ func timeSinceUnix(then, now time.Time, _ translation.Locale) template.HTML { // TimeSince renders relative time HTML given a time.Time func TimeSince(then time.Time, lang translation.Locale) template.HTML { if setting.UI.PreferredTimestampTense == "absolute" { - return DateTime("full", then, `class="time-since"`) + return DateTime("full", then) } return timeSinceUnix(then, time.Now(), lang) } diff --git a/modules/util/slice.go b/modules/util/slice.go index a7073fedee..f00e84bf06 100644 --- a/modules/util/slice.go +++ b/modules/util/slice.go @@ -53,3 +53,12 @@ func Sorted[S ~[]E, E cmp.Ordered](values S) S { slices.Sort(values) return values } + +// TODO: Replace with "maps.Values" once available +func ValuesOfMap[K comparable, V any](m map[K]V) []V { + values := make([]V, 0, len(m)) + for _, v := range m { + values = append(values, v) + } + return values +} diff --git a/modules/web/middleware/data.go b/modules/web/middleware/data.go index c1d1af8528..08d83f94be 100644 --- a/modules/web/middleware/data.go +++ b/modules/web/middleware/data.go @@ -53,7 +53,6 @@ func CommonTemplateContextData() ContextData { "ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage, "ShowFooterVersion": setting.Other.ShowFooterVersion, "DisableDownloadSourceArchives": setting.Repository.DisableDownloadSourceArchives, - "DownloadOrCloneMethods": setting.Repository.DownloadOrCloneMethods, "EnableSwagger": setting.API.EnableSwagger, "EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn, diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index c60ed5c42c..0e27147e10 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -1448,7 +1448,7 @@ oauth_signin_tab = أربط بحساب موجود invalid_password = كلمة المرور الخاصة بك لا تطابق كلمة المرور التي استخدمت لتسجيل الحساب. oauth_signin_title = سجّل الدخول لتأذن للحساب المربوط reset_password_helper = إعادة الحساب -login_openid = تسجيل دخول بـOpenID +tab_openid = تسجيل دخول بـOpenID openid_connect_submit = اتصل oauth_signup_tab = سجل حساب جديد oauth.signin.error.temporarily_unavailable = فشل طلب الإذن لأن خادم التوثيق غير متاح مؤقتا. حاول مرة أخرى لاحقاً. @@ -1535,7 +1535,7 @@ filter.container.tagged = موسوم [heatmap] less = أقل number_of_contributions_in_the_last_12_months = %s مساهم في آخر 12 شهر -no_contributions = بلا مساهمات +contributions_zero = بلا مساهمات more = أكثر [admin] diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 3eca60f551..b6ad948b46 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -1270,7 +1270,7 @@ approve_pull_request = `одобри %[3]s#%[2]s` reject_pull_request = `предложи промени за %[3]s#%[2]s` [auth] -login_openid = OpenID +tab_openid = OpenID openid_connect_submit = Свързване sign_up_successful = Акаунтът е създаден успешно. Добре дошли! login_userpass = Влизане @@ -1357,7 +1357,7 @@ runners.description = Описание [heatmap] less = По-малко number_of_contributions_in_the_last_12_months = %s приноса през последните 12 месеца -no_contributions = Няма приноси +contributions_zero = Няма приноси more = Повече [git.filemode] @@ -1382,4 +1382,4 @@ recent_commits.what = скорошни подавания component_loading = Зареждане на %s... [projects] -type-1.display_name = Индивидуален проект \ No newline at end of file +type-1.display_name = Индивидуален проект diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 409b014fab..cb984aad07 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -57,7 +57,7 @@ mirror=Zrcadlo new_repo=Nový repozitář new_migrate=Nová migrace new_mirror=Nové zrcadlo -new_fork=Nové rozštěpení repozitáře +new_fork=Nový fork repozitáře new_org=Nová organizace new_project=Nový projekt new_project_column=Nový sloupec @@ -76,7 +76,7 @@ collaborative=Spolupráce forks=Rozštěpení activities=Aktivity -pull_requests=Požadavky na natažení +pull_requests=Požadavky na sloučení issues=Úkoly milestones=Milníky @@ -145,6 +145,18 @@ value=Hodnota sign_in_with_provider = Přihlásit se přes %s confirm_delete_artifact = Opravdu chcete odstranit artefakt „%s“? toggle_menu = Přepnout nabídku +filter = Filtr +filter.is_fork = Forknuto +filter.not_fork = Není forkuto +filter.is_mirror = Zrcadleno +filter.is_template = Šablona +filter.not_template = Není šablona +filter.public = Veřejné +filter.private = Soukromé +filter.is_archived = Archivováno +filter.not_mirror = Není zrcadleno +filter.not_archived = Není archivováno +filter.clear = Vymazat filtr [aria] navbar=Navigační lišta @@ -154,9 +166,12 @@ footer.links=Odkazy [heatmap] number_of_contributions_in_the_last_12_months=%s příspěvků za posledních 12 měsíců -no_contributions=Žádné příspěvky +contributions_zero=Žádné příspěvky less=Méně more=Více +contributions_format = {contributions} dne {day}. {month} {year} +contributions_one = příspěvek +contributions_few = příspěvků [editor] buttons.heading.tooltip=Přidat nadpis @@ -200,7 +215,7 @@ license_desc=Vše je na dokumentaci, než budete měnit jakákoliv nastavení. require_db_desc=Forgejo requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). db_title=Nastavení databáze @@ -228,7 +243,7 @@ err_admin_name_pattern_not_allowed=Uživatelské jméno administrátora je nepla err_admin_name_is_invalid=Uživatelské jméno administrátora není platné general_title=Obecná nastavení -app_name=Název stránky +app_name=Název instance app_name_helper=Zde můžete zadat název vaší společnosti. repo_path=Kořenový adresář repozitářů repo_path_helper=Všechny vzdálené repozitáře Gitu budou uloženy do tohoto adresáře. @@ -239,47 +254,47 @@ run_user_helper=Zadejte uživatelské jméno, pod kterým Gitea běží v opera domain=Doména serveru domain_helper=Adresa domény, nebo hostitele serveru. ssh_port=Port SSH serveru -ssh_port_helper=Číslo portu, na kterém SSH server naslouchá. Když ponecháte prázdné, SSH server zakážete. -http_port=Port, na kterém Forgejo naslouchá HTTP protokolu -http_port_helper=Číslo portu, na kterém bude naslouchat webový server Forgejo. -app_url=Základní URL Forgejo +ssh_port_helper=Číslo portu, který bude použit pro SSH server. Nechte prázdné pro zakázání SSH serveru. +http_port=Port pro naslouchání HTTP +http_port_helper=Číslo portu, který bude použit webovým serverem Forgejo. +app_url=Základní URL app_url_helper=Základní adresa pro HTTP(S) URL adresy pro klonování a e-mailová oznámení. -log_root_path=Adresář logů +log_root_path=Adresář protokolů log_root_path_helper=Soubory protokolu budou zapsány do tohoto adresáře. -optional_title=Dodatečná nastavení +optional_title=Volitelná nastavení email_title=Nastavení e-mailu -smtp_addr=Server SMTP +smtp_addr=Hostitel SMTP smtp_port=Port SMTP smtp_from=Odeslat e-mail jako smtp_from_helper=E-mailová adresa, kterou bude Forgejo používat. Zadejte běžnou e-mailovou adresu, nebo použijte formát "Jméno". mailer_user=Uživatelské jméno SMTP -mailer_password=Heslo pro SMTP +mailer_password=Heslo SMTP register_confirm=Pro registraci vyžadovat potvrzení e-mailu mail_notify=Povolit e-mailová oznámení -server_service_title=Nastavení serveru a dalších služeb +server_service_title=Nastavení serveru a služeb třetích stran offline_mode=Povolit místní režim offline_mode_popup=Zakázat sítě pro doručování obsahu a poskytovat veškerý obsah lokálně. disable_gravatar=Zakázat Gravatar disable_gravatar_popup=Zakážete Gravatar a jiné cizí zdroje avatarů. Pokud uživatel nenahraje avatar, bude použit výchozí. -federated_avatar_lookup=Povolit avatary z veřejných zdrojů +federated_avatar_lookup=Povolit federované avatary federated_avatar_lookup_popup=Povolte vyhledání avatarů z veřejných zdrojů pro využití služeb založených na libravatar. -disable_registration=Vypnout možnost uživatelské registrace +disable_registration=Zakázat uživatelské registrace disable_registration_popup=Vypnout možnost registrace. Pouze správci budou moci vytvářet účty. allow_only_external_registration_popup=Povolit registraci pouze prostřednictvím externích služeb openid_signin=Povolit přihlášení pomocí OpenID openid_signin_popup=Umožňuje uživateli přihlásit se pomocí OpenID. -openid_signup=Povolit automatickou registraci pomocí OpenID +openid_signup=Povolit uživatelskou registraci pomocí OpenID openid_signup_popup=Umožňuje uživateli automaticky se registrovat pomocí OpenID. enable_captcha=Povolit CAPTCHA při registraci enable_captcha_popup=Vyžadovat správně zadaný text CAPTCHA při registraci. -require_sign_in_view=Vyžadovat přihlášení k zobrazení stránek +require_sign_in_view=Vyžadovat přihlášení pro zobrazení obsahu instance require_sign_in_view_popup=Povolí přístup ke stránkám jen přihlášeným uživatelům. Návštěvníci uvidí jen přihlašovací a registrační stránky. admin_setting_desc=Vytvoření účtu správce je nepovinné. První registrovaný uživatel se automaticky stane správcem. admin_title=Nastavení účtu správce admin_name=Uživatelské jméno správce admin_password=Heslo -confirm_password=Potvrdit heslo +confirm_password=Potvrzení hesla admin_email=E-mailová adresa install_btn_confirm=Nainstalovat Forgejo test_git_failed=Chyba při testu příkazu „git“: %v @@ -288,30 +303,31 @@ invalid_db_setting=Nastavení databáze je neplatné: %v invalid_db_table=Databázová tabulka „%s“ je neplatná: %v invalid_repo_path=Kořenový adresář repozitářů není správný: %v invalid_app_data_path=Cesta k datům aplikace je neplatná: %v -run_user_not_match=Uživatelské jméno v poli „Spustit jako“ není aktuální uživatelské jméno: %s -> %s +run_user_not_match=Uživatelské jméno v poli „spustit jako uživatel“ není aktuální uživatelské jméno: %s -> %s internal_token_failed=Nepodařilo se vytvořit interní token: %v secret_key_failed=Nepodařilo se vytvořit tajný klíč: %v save_config_failed=Uložení konfigurace se nezdařilo: %v invalid_admin_setting=Nastavení účtu správce není správné: %v invalid_log_root_path=Kořenový adresář logů není správný: %v -default_keep_email_private=Jako počáteční nastavení skrýt e-mailové adresy +default_keep_email_private=Ve výchozím nastavení skrýt e-mailové adresy default_keep_email_private_popup=Nastaví e-mailové adresy novým uživatelským účtům jako skryté. -default_allow_create_organization=Dovolí novým uživatelům zakládat organizace +default_allow_create_organization=Povolit novým uživatelům zakládat organizace default_allow_create_organization_popup=Povolit novým uživatelským účtům vytvářet organizace. -default_enable_timetracking=Povolit sledování času ve výchozím nastavení +default_enable_timetracking=Povolit ve výchozím nastavení sledování času default_enable_timetracking_popup=Povolí sledování času pro nové repozitáře. no_reply_address=Skrytá e-mailová doména no_reply_address_helper=Název domény pro uživatele se skrytou e-mailovou adresou. Příklad: pokud je název skryté e-mailové domény nastaven na „noreply.example.org“, uživatelské jméno „joe“ bude zaznamenáno v Gitu jako „joe@noreply.example.org“. -password_algorithm=Hash algoritmus hesla +password_algorithm=Hashovací algoritmus hesla invalid_password_algorithm=Neplatný algoritmus hash hesla password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a sílu. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy. enable_update_checker=Povolit kontrolu aktualizací enable_update_checker_helper=Kontroluje vydání nových verzí pravidelně připojením ke gitea.io. env_config_keys=Konfigurace prostředí env_config_keys_prompt=Následující proměnné prostředí budou také použity pro váš konfigurační soubor: -enable_update_checker_helper_forgejo = Pravidelně kontroluje nové verze Forgejo kontrolou DNS TXT záznamu na adrese release.forgejo.org. +enable_update_checker_helper_forgejo = Bude pravidelně kontrolovat nové verze Forgejo kontrolou TXT DNS záznamu na adrese release.forgejo.org. allow_dots_in_usernames = Povolit uživatelům používat tečky ve svých uživatelských jménech. Neovlivní stávající účty. smtp_from_invalid = Adresa v poli „Poslat e-mail jako“ je neplatná +config_location_hint = Tyto možnosti konfigurace budou uloženy do: [home] uname_holder=Uživatelské jméno nebo e-mailová adresa @@ -320,7 +336,7 @@ switch_dashboard_context=Přepnout kontext přehledu my_repos=Repozitáře show_more_repos=Zobrazit více repozitářů… collaborative_repos=Společné repozitáře -my_orgs=Mé organizace +my_orgs=Organizace my_mirrors=Má zrcadla view_home=Zobrazit %s search_repos=Nalézt repozitář… @@ -361,6 +377,10 @@ code_search_results=Výsledky hledání pro „%s“ code_last_indexed_at=Naposledy indexováno %s relevant_repositories_tooltip=Repozitáře, které jsou rozštěpení nebo nemají žádné téma, ikonu a žádný popis jsou skryty. relevant_repositories=Zobrazují se pouze relevantní repositáře, zobrazit nefiltrované výsledky. +forks_one = %d fork +forks_few = %d forků +stars_one = %d hvězda +stars_few = %d hvězd [auth] create_new_account=Registrovat účet @@ -381,7 +401,7 @@ allow_password_change=Vyžádat od uživatele změnu hesla (doporučeno) reset_password_mail_sent_prompt=Na adresu %s byl zaslán potvrzovací e-mail. Zkontrolujte prosím vaši doručenou poštu během následujících %s, abyste dokončili proces obnovení účtu. active_your_account=Aktivujte si váš účet account_activated=Účet byl aktivován -prohibit_login=Přihlášení zakázáno +prohibit_login=Přihlašování je zakázáno prohibit_login_desc=Vašemu účtu je zakázáno se přihlásit, kontaktujte prosím správce webu. resent_limit_prompt=Omlouváme se, ale před chvílí jste požádal o zaslání aktivačního e-mailu. Počkejte prosím 3 minuty a pak to zkuste znovu. has_unconfirmed_mail=Zdravím, %s, máte nepotvrzenou e-mailovou adresu (%s). Pokud jste nedostali e-mail pro potvrzení nebo potřebujete zaslat nový, klikněte prosím na tlačítku níže. @@ -403,7 +423,7 @@ twofa_scratch_used=Použili jste váš pomocný kód. Byli jste přesměrování twofa_passcode_incorrect=Vaše heslo je neplatné. Pokud jste ztratili vaše zařízení, použijte pomocný kód k přihlášení. twofa_scratch_token_incorrect=Váš pomocný kód není správný. login_userpass=Přihlásit se -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Zaregistrovat nový účet oauth_signup_title=Dokončit nový účet oauth_signup_submit=Dokončit účet @@ -436,6 +456,8 @@ change_unconfirmed_email = Pokud jste při registraci zadali nesprávnou e-mailo change_unconfirmed_email_error = Nepodařilo se změnit e-mailovou adresu: %v change_unconfirmed_email_summary = Změna e-mailové adresy, na kterou bude odeslán aktivační e-mail. last_admin=Nelze odstranit posledního správce. Musí existovat alespoň jeden správce. +tab_signup = Registrace +tab_signin = Přihlášení [mail] view_it_on=Zobrazit na %s @@ -490,13 +512,13 @@ release.downloads=Soubory ke stažení: release.download.zip=Zdrojový kód (ZIP) release.download.targz=Zdrojový kód (TAR.GZ) -repo.transfer.subject_to=%s by chtěl převést „%s“ pro %s -repo.transfer.subject_to_you=%s by Vám chtěl převést „%s“ +repo.transfer.subject_to=%s chce převést repozitář „%s“ k uživateli %s +repo.transfer.subject_to_you=%s by k vám chce převést repozitář „%s“ repo.transfer.to_you=vám repo.transfer.body=Chcete-li ji přijmout nebo odmítnout, navštivte %s nebo ji prostě ignorujte. -repo.collaborator.added.subject=%s vás přidal do %s -repo.collaborator.added.text=Byl jste přidán jako spolupracovník repozitáře: +repo.collaborator.added.subject=%s vás přidal do %s jako spolupracovníka +repo.collaborator.added.text=Byli jste přidáni jako spolupracovník repozitáře: team_invite.subject=%[1]s vás pozval/a, abyste se připojili k organizaci %[2]s team_invite.text_1=%[1]s vás pozval/a do týmu %[2]s v organizaci %[3]s. @@ -518,7 +540,7 @@ UserName=Uživatelské jméno RepoName=Název repozitáře Email=E-mailová adresa Password=Heslo -Retype=Potvrdit heslo +Retype=Potvrzení hesla SSHTitle=Název klíče SSH HttpsUrl=HTTPS URL PayloadUrl=URL nákladu @@ -607,6 +629,8 @@ admin_cannot_delete_self = Nemůžete odstranit sami sebe, když jste administr username_error_no_dots = ` může obsahovat pouze alfanumerické znaky („0-9“, „a-z“, „A-Z“), pomlčky („-“) a podtržítka („_“). Nemůže začínat nebo končit nealfanumerickými znaky. Jsou také zakázány po sobě jdoucí nealfanumerické znaky.` admin_cannot_delete_self=Nemůžete se smazat, dokud jste správce. Nejdříve prosím odeberte svá administrátorská oprávnění. +unset_password = Tento uživatel nemá nastavené heslo. +unsupported_login_type = U tohoto typu účtu není funkce odstranění účtu podporována. [user] change_avatar=Změnit váš avatar… @@ -648,17 +672,17 @@ appearance=Vzhled password=Heslo security=Zabezpečení avatar=Avatar -ssh_gpg_keys=SSH / GPG klíče +ssh_gpg_keys=Klíče SSH / GPG social=Účty sociálních sítí applications=Aplikace orgs=Spravovat organizace repos=Repozitáře delete=Smazat účet -twofa=Dvoufaktorové ověřování +twofa=Dvoufaktorové ověřování (TOTP) account_link=Propojené účty organization=Organizace uid=UID -webauthn=Bezpečnostní klíče +webauthn=Dvoufaktorové ověřování (bezpečnostní klíče) public_profile=Veřejný profil biography_placeholder=Řekněte nám něco o sobě! (Můžete použít Markdown) @@ -668,9 +692,9 @@ password_username_disabled=Externí uživatelé nemohou měnit svoje uživatelsk full_name=Celé jméno website=Web location=Místo -update_theme=Aktualizovat motiv vzhledu -update_profile=Aktualizovat profil -update_language=Aktualizovat jazyk +update_theme=Změnit motiv +update_profile=Upravit profil +update_language=Změnit jazyk update_language_not_found=Jazyk „%s“ není k dispozici. update_language_success=Jazyk byl aktualizován. update_profile_success=Váš profil byl aktualizován. @@ -702,20 +726,20 @@ comment_type_group_issue_ref=Referenční číslo úkolu saved_successfully=Vaše nastavení bylo úspěšně uloženo. privacy=Soukromí keep_activity_private=Skrýt aktivitu z profilové stránky -keep_activity_private_popup=Učinit aktivitu viditelnou pouze pro vás a administrátory +keep_activity_private_popup=Vaše aktivita bude viditelná pouze pro vás a správce instance lookup_avatar_by_mail=Vyhledat avatar pomocí e-mailové adresy -federated_avatar_lookup=Vyhledání avatarů ve veřejných zdrojích +federated_avatar_lookup=Federované vyhledávání avatarů enable_custom_avatar=Použít vlastní avatar choose_new_avatar=Vybrat nový avatar -update_avatar=Aktualizovat avatar -delete_current_avatar=Smazat aktuální avatar +update_avatar=Upravit avatar +delete_current_avatar=Odstranit aktuální avatar uploaded_avatar_not_a_image=Nahraný soubor není obrázek. uploaded_avatar_is_too_big=Nahraný soubor (%d KiB) přesahuje maximální velikost (%d KiB). update_avatar_success=Vaše avatar byl aktualizován. update_user_avatar_success=Uživatelův avatar byl aktualizován. -update_password=Aktualizovat heslo +update_password=Upravit heslo old_password=Stávající heslo new_password=Nové heslo retype_new_password=Potvrzení nového hesla @@ -723,10 +747,10 @@ password_incorrect=Zadané heslo není správné. change_password_success=Vaše heslo bylo aktualizováno. Od teď se přihlašujte novým heslem. password_change_disabled=Externě ověřovaní uživatelé nemohou aktualizovat své heslo prostřednictvím webového rozhraní Forgejo. -emails=E-mailová adresa +emails=E-mailové adresy manage_emails=Správa e-mailových adres manage_themes=Vyberte výchozí motiv vzhledu -manage_openid=Správa OpenID adres +manage_openid=Správa adres OpenID email_desc=Vaše hlavní e-mailová adresa bude použita pro oznámení, obnovení hesla, a pokud není skrytá, pro operace Gitu. theme_desc=Toto bude váš výchozí motiv vzhledu napříč stránkou. primary=Hlavní @@ -767,7 +791,7 @@ gpg_desc=Tyto veřejné klíče GPG jsou propojeny s vaším účtem a používa ssh_helper=Potřebujete pomoct? Podívejte se do příručky GitHubu na to vytvoření vlastních klíčů SSH nebo vyřešte běžné problémy, se kterými se můžete potkat při použití SSH. gpg_helper=Potřebujete pomoct? Podívejte se do příručky GitHubu o GPG. add_new_key=Přidat klíč SSH -add_new_gpg_key=Přidat GPG klíč +add_new_gpg_key=Přidat klíč GPG key_content_ssh_placeholder=Začíná s „ssh-ed25519“, „ssh-rsa“, „ecdsa-sha2-nistp256“, „ecdsa-sha2-nistp384“, „ecdsa-sha2-nistp521“, „sk-ecdsa-sha2-nistp256@openssh.com“ nebo „sk-ssh-ed25519@openssh.com“ key_content_gpg_placeholder=Začíná s „-----BEGIN PGP PUBLIC KEY BLOCK-----“ add_new_principal=Přidat SSH Principal certifikát @@ -808,8 +832,8 @@ add_key_success=SSH klíč „%s“ byl přidán. add_gpg_key_success=GPG klíč „%s“ byl přidán. add_principal_success=Byl přidán SSH Principal certifikát „%s“. delete_key=Odstranit -ssh_key_deletion=Odstraňte SSH klíč -gpg_key_deletion=Odstraňte GPG klíč +ssh_key_deletion=Odebrat klíč SSH +gpg_key_deletion=Odebrat klíč GPG ssh_principal_deletion=Odstranit SSH Principal certifikát ssh_key_deletion_desc=Odstranění SSH klíče zruší jeho přístup k vašemu účtu. Pokračovat? gpg_key_deletion_desc=Odstranění GPG klíče zneplatníte ověření commitů, které jsou jím podepsány. Pokračovat? @@ -871,7 +895,7 @@ create_oauth2_application_button=Vytvořit aplikaci create_oauth2_application_success=Úspěšně jste vytvořili novou OAuth2 aplikaci. update_oauth2_application_success=Úspěšně jste aktualizovali OAuth2 aplikaci. oauth2_application_name=Název aplikace -oauth2_confidential_client=Důvěrný klient. Vyberte aplikace, které zachovávají důvěrnosti v utajení, jako jsou webové aplikace. Nevybírejte pro nativní aplikace včetně stolních a mobilních aplikací. +oauth2_confidential_client=Důvěrný klient. Zvolte jej pro aplikace, které ukládají soubor secret, například webové aplikace. Nevybírejte jej pro nativní aplikace včetně aplikací pro počítače a mobilní zařízení. oauth2_redirect_uris=Přesměrování URI. Použijte nový řádek pro každou URI. save_application=Uložit oauth2_client_id=ID klienta @@ -896,7 +920,7 @@ twofa_recovery_tip=Pokud ztratíte své zařízení, budete moci použít jednor twofa_is_enrolled=Váš účet aktuálně používá dvoufaktorové ověřování. twofa_not_enrolled=Váš účet aktuálně nepoužívá dvoufaktorové ověřování. twofa_disable=Zakázat dvoufaktorové ověřování -twofa_scratch_token_regenerate=Obnovit pomocný token +twofa_scratch_token_regenerate=Znovu vygenerovat jednorázový klíč pro obnovení twofa_scratch_token_regenerated=Váš jednorázový obnovovací klíč je nyní %s. Uložte jej na bezpečném místě, protože se znovu nezobrazí. twofa_enroll=Povolit dvoufaktorové ověřování twofa_disable_note=Dvoufaktorové ověřování můžete zakázat, když bude potřeba. @@ -913,7 +937,7 @@ twofa_failed_get_secret=Nepodařilo se získat tajemství. webauthn_desc=Bezpečnostní klíče jsou hardwarová zařízení obsahující kryptografické klíče. Mohou být použity pro dvoufaktorové ověřování. Bezpečnostní klíče musí podporovat WebAuthn Authenticator standard. webauthn_register_key=Přidat bezpečnostní klíč webauthn_nickname=Přezdívka -webauthn_delete_key=Odstranit bezpečnostní klíč +webauthn_delete_key=Odebrat bezpečnostní klíč webauthn_delete_key_desc=Pokud odstraníte bezpečnostní klíč, již se s ním nebudete moci přihlásit. Pokračovat? webauthn_key_loss_warning=Pokud ztratíte své bezpečnostní klíče, ztratíte přístup k vašemu účtu. webauthn_alternative_tip=Možná budete chtít nakonfigurovat další metodu ověřování. @@ -931,18 +955,18 @@ hooks.desc=Přidat webhooky, které budou spouštěny pro všechny repoz orgs_none=Nejste členem žádné organizace. repos_none=Nevlastníte žádné repozitáře. -delete_account=Smazat váš účet +delete_account=Odstranit svůj účet delete_prompt=Tato operace natrvalo odstraní váš uživatelský účet. NELZE ji vrátit zpět. delete_with_all_comments=Váš účet je mladší než %s. Aby se zabránilo fantomovým komentářům, všechny komentáře k úkolům/požadavkům na natažení budou smazány. -confirm_delete_account=Potvrdit smazání -delete_account_title=Smazat uživatelský účet +confirm_delete_account=Potvrdit odstranění +delete_account_title=Odstranit uživatelský účet delete_account_desc=Jste si jisti, že chcete trvale smazat tento účet? email_notifications.enable=Povolit e-mailová oznámení email_notifications.onmention=E-mail pouze při zmínce email_notifications.disable=Zakázat e-mailová oznámení email_notifications.submit=Nastavit předvolby e-mailu -email_notifications.andyourown=A Vaše vlastní upozornění +email_notifications.andyourown=A vaše vlastní upozornění visibility=Viditelnost uživatele visibility.public=Veřejný @@ -974,10 +998,10 @@ visibility=Viditelnost visibility_description=Pouze majitelé nebo členové organizace to budou moci vidět, pokud mají práva. visibility_helper=Nastavit repozitář jako soukromý visibility_helper_forced=Váš administrátor vynutil, že nové repozitáře budou soukromé. -visibility_fork_helper=(Změna tohoto ovlivní všechny rozštěpení repozitáře.) +visibility_fork_helper=(Změna této možnosti ovlivní viditelnost všech forků repozitáře.) clone_helper=Potřebujete pomoci s klonováním? Navštivte nápovědu. -fork_repo=Rozštěpení repozitáře -fork_from=Rozštěpit z +fork_repo=Fork repozitáře +fork_from=Fork z already_forked=Již jsi rozštěpil %s fork_to_different_account=Rozštěpit na jiný účet fork_visibility_helper=Viditelnost rozštěpeného repozitáře nemůže být změněna. @@ -996,7 +1020,7 @@ repo_desc_helper=Zadejte krátký popis (volitelné) repo_lang=Jazyk repo_gitignore_helper=Vyberte šablony .gitignore. repo_gitignore_helper_desc=Vyberte soubory, které nechcete sledovat ze seznamu šablon pro běžné jazyky. Typické artefakty generované nástroji pro sestavení každého jazyka jsou ve výchozím stavu součástí .gitignore. -issue_labels=Štítky úkolů +issue_labels=Štítky problémů issue_labels_helper=Vyberte sadu štítků úkolů. license=Licence license_helper=Vyberte licenční soubor. @@ -1006,7 +1030,7 @@ object_format_helper=Objektový formát repozitáře. Nelze později změnit. SH readme=README readme_helper=Vyberte šablonu souboru README. readme_helper_desc=Toto je místo, kde můžete napsat úplný popis vašeho projektu. -auto_init=Inicializovat repozitář (Přidá .gitignore, License a README) +auto_init=Inicializovat repozitář (přidá soubory .gitignore, License a README) trust_model_helper=Vyberte model důvěry pro ověření podpisu. Možnosti jsou: trust_model_helper_collaborator=Spolupracovník: Důvěřovat podpisům spolupracovníků trust_model_helper_committer=Přispěvatel: Důvěřovat podpisům, které se shodují s přispěvateli @@ -1018,7 +1042,7 @@ default_branch_label=výchozí default_branch_helper=Výchozí větev je základní větev pro požadavky na natažení a commity kódu. mirror_prune=Vyčistit mirror_prune_desc=Odstranit zastaralé reference na vzdálené sledování -mirror_interval=Interval zrcadlení (platné časové jednotky jsou „h“, „m“ a „s“). 0 zakáže periodickou synchronizaci. (Minimální interval: %s) +mirror_interval=Interval zrcadlení (platné časové jednotky jsou „h“, „m“ a „s“). Nastavením na 0 zakážete periodickou synchronizaci. (Minimální interval: %s) mirror_interval_invalid=Interval zrcadlení není platný. mirror_sync_on_commit=Synchronizovat při nahrávání revizí mirror_address=Klonovat z URL @@ -1027,7 +1051,7 @@ mirror_address_url_invalid=Poskytnutá URL je neplatná. Všechny části musít mirror_address_protocol_invalid=Zadaná URL je neplatná. Mohou být zrcadleny pouze umístění http(s):// nebo git://. mirror_lfs=Úložiště velkých souborů (LFS) mirror_lfs_desc=Aktivovat zrcadlení dat LFS. -mirror_lfs_endpoint=Koncový bod LFS +mirror_lfs_endpoint=Endpoint LFS mirror_lfs_endpoint_desc=Synchronizace se pokusí použít URL pro klonování k určení LFS serveru. Můžete také zadat vlastní koncový bod, pokud jsou data LFS repozitáře uložena někde jinde. mirror_last_synced=Poslední synchronizace mirror_password_placeholder=(Nezměněno) @@ -1057,9 +1081,9 @@ tree_path_not_found_commit=Cesta %[1]s v commitu %[2]s neexistuje tree_path_not_found_branch=Cesta %[1]s ve větvi %[2]s neexistuje tree_path_not_found_tag=Cesta %[1]s ve značce %[2]s neexistuje -transfer.accept=Přijmout převod +transfer.accept=Přijmout přesun transfer.accept_desc=Převést do „%s“ -transfer.reject=Odmítnout převod +transfer.reject=Odmítnout přesun transfer.reject_desc=Zrušit převod do „%s“ transfer.no_permission_to_accept=Nemáte oprávnění k přijetí tohoto převodu. transfer.no_permission_to_reject=Nemáte oprávnění k odmítnutí tohoto převodu. @@ -1072,13 +1096,13 @@ desc.archived=Archivováno desc.sha256=SHA256 template.items=Položky šablony -template.git_content=Obsah gitu (výchozí větev) -template.git_hooks=Háčky Gitu -template.git_hooks_tooltip=Momentálně nemůžete po přidání upravovat nebo odebrat háčky gitu. Vyberte pouze v případě, že důvěřujete šabloně repozitáře. +template.git_content=Obsah Gitu (výchozí větev) +template.git_hooks=Git hooks +template.git_hooks_tooltip=Momentálně nemůžete po přidání upravovat nebo odebírat Git hooky. Vyberte pouze v případě, že důvěřujete šabloně repozitáře. template.webhooks=Webové háčky template.topics=Témata template.avatar=Avatar -template.issue_labels=Štítky úkolů +template.issue_labels=Štítky problémů template.one_item=Musíte vybrat alespoň jednu položku šablony template.invalid=Musíte vybrat repositář šablony @@ -1097,7 +1121,7 @@ migrate_options=Možnosti migrace migrate_service=Migrační služba migrate_options_mirror_helper=Tento repozitář bude zrcadlem migrate_options_lfs=Migrovat LFS soubory -migrate_options_lfs_endpoint.label=Koncový bod LFS +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 @@ -1106,8 +1130,8 @@ migrate_items_wiki=Wiki migrate_items_milestones=Milníky migrate_items_labels=Štítky migrate_items_issues=Úkoly -migrate_items_pullrequests=Požadavky na natažení -migrate_items_merge_requests=Sloučit požadavky +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 @@ -1127,7 +1151,7 @@ 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.github.description=Migrovat data z github.com nebo jiných GitHub instancí. +migrate.github.description=Migrovat data z github.com nebo serveru GitHub Enterprise. migrate.git.description=Migrovat pouze repozitář z libovolné služby Git. migrate.gitlab.description=Migrovat data z gitlab.com nebo jiných GitLab instancí. migrate.gitea.description=Migrovat data z gitea.com nebo jiných Gitea/Forgejo instancí. @@ -1135,13 +1159,13 @@ migrate.gogs.description=Migrovat data z notabug.com nebo jiných Gogs instancí migrate.onedev.description=Migrovat data z code.onedev.io nebo jiných OneDev instancí. migrate.codebase.description=Migrovat data z codebasehq.com. migrate.gitbucket.description=Migrovat data z GitBucket instancí. -migrate.migrating_git=Migrování data gitu -migrate.migrating_topics=Migrování témat -migrate.migrating_milestones=Migrování milnků -migrate.migrating_labels=Migrování štítků -migrate.migrating_releases=Migrování vydání -migrate.migrating_issues=Migrování úkolů -migrate.migrating_pulls=Migrování požadavků na natažení +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? @@ -1179,7 +1203,7 @@ find_tag=Najít značku branches=Větve tags=Značky issues=Úkoly -pulls=Požadavky na natažení +pulls=Žádosti o sloučení project_board=Projekty packages=Balíčky actions=Akce @@ -1214,7 +1238,7 @@ ambiguous_character=`%[1]c [U+%04[1]X] je zaměnitelný s %[2]c [U+%04[2]X]` escape_control_characters=Escape sekvence unescape_control_characters=Bez escape sekvencí file_copy_permalink=Kopírovat trvalý odkaz -view_git_blame=Zobrazit Git Blame +view_git_blame=Zobrazit git blame video_not_supported_in_browser=Váš prohlížeč nepodporuje značku HTML5 „video“. audio_not_supported_in_browser=Váš prohlížeč nepodporuje značku HTML5 „audio“. stored_lfs=Uloženo pomocí Git LFS @@ -1224,7 +1248,7 @@ vendored=Vendorováno generated=Generováno commit_graph=Graf commitů commit_graph.select=Vybrat větve -commit_graph.hide_pr_refs=Skrýt požadavky na natažení +commit_graph.hide_pr_refs=Skrýt žádosti o sloučení commit_graph.monochrome=Černobílé commit_graph.color=Barva commit.contained_in=Tento commit je obsažen v: @@ -1248,7 +1272,7 @@ editor.edit_this_file=Upravit soubor editor.this_file_locked=Soubor je uzamčen editor.must_be_on_a_branch=Musíte mít zvolenu větev pro úpravu či návrh změn tohoto souboru. editor.fork_before_edit=Musíte rozštěpit tento repozitář pro vytvoření nebo navržení změny tohoto souboru. -editor.delete_this_file=Smazat soubor +editor.delete_this_file=Odstranit soubor editor.must_have_write_access=Musíte mít přístup pro zápis pro dělání či navrhování změn tohoto souboru. editor.file_delete_success=Soubor „%s“ byl odstraněn. editor.name_your_file=Pojmenujte váš soubor… @@ -1290,8 +1314,8 @@ editor.commit_empty_file_text=Soubor, který se chystáte odevzdat, je prázdný editor.no_changes_to_show=Žádné změny k zobrazení. editor.fail_to_update_file=Nepodařilo se aktualizovat/vytvořit soubor „%s“. editor.fail_to_update_file_summary=Chybové hlášení: -editor.push_rejected_no_message=Změna byla serverem zamítnuta bez zprávy. Prosím, zkontrolujte háčky Gitu. -editor.push_rejected=Změna byla serverem zamítnuta. Prosím, zkontrolujte háčky Gitu. +editor.push_rejected_no_message=Změna byla serverem zamítnuta bez zprávy. Zkontrolujte prosím Git hooks. +editor.push_rejected=Změna byla serverem zamítnuta. Zkontrolujte prosím Git hooks. editor.push_rejected_summary=Úplná zpráva o odmítnutí: editor.add_subdir=Přidat adresář… editor.unable_to_upload_files=Nepodařilo se nahrát soubory do „%s“. Chyba: %v @@ -1320,7 +1344,7 @@ commits.newer=Novější commits.signed_by=Podepsáno commits.signed_by_untrusted_user=Podepsáno nedůvěryhodným uživatelem commits.signed_by_untrusted_user_unmatched=Podepsáno nedůvěryhodným uživatelem, který nesouhlasí s přispěvatelem -commits.gpg_key_id=ID GPG klíče +commits.gpg_key_id=ID klíče GPG commits.ssh_key_fingerprint=Otisk klíče SSH commits.view_path=Zobrazit v tomto bodě v historii @@ -1337,7 +1361,7 @@ commitstatus.failure=Chyba commitstatus.pending=Čekající commitstatus.success=Úspěch -ext_issues=Přístup k externím úkolům +ext_issues=Přístup k externím problémům ext_issues.desc=Odkaz na externí systém úkolů. projects=Projekty @@ -1352,9 +1376,9 @@ projects.create_success=Projekt „%s“ byl vytvořen. projects.deletion=Odstranit projekt projects.deletion_desc=Odstranění projektu jej odstraní ze všech souvisejících úkolů. Pokračovat? projects.deletion_success=Projekt byl odstraněn. -projects.edit=Upravit projekty +projects.edit=Upravit projekt projects.edit_subheader=Projekty organizují úkoly a sledují pokrok. -projects.modify=Aktualizovat projekt +projects.modify=Upravit projekt projects.edit_success=Projekt „%s“ byl aktualizován. projects.type.none=Žádný projects.type.basic_kanban=Základní Kanban @@ -1387,7 +1411,7 @@ issues.filter_milestones=Filtrovat milník issues.filter_projects=Filtrovat projekt issues.filter_labels=Filtrovat štítky issues.filter_reviewers=Filtrovat posuzovatele -issues.new=Nový úkol +issues.new=Nový problém issues.new.title_empty=Název nesmí být prázdný issues.new.labels=Štítky issues.new.no_label=Bez štítku @@ -1395,17 +1419,17 @@ issues.new.clear_labels=Zrušit štítky issues.new.projects=Projekty issues.new.clear_projects=Vymazat projekty issues.new.no_projects=Žádný projekt -issues.new.open_projects=Otevřít projekty +issues.new.open_projects=Otevřené projekty issues.new.closed_projects=Uzavřené projekty issues.new.no_items=Žádné položky issues.new.milestone=Milník -issues.new.no_milestone=Bez milníku +issues.new.no_milestone=Žádný milník issues.new.clear_milestone=Smazat milník -issues.new.open_milestone=Otevřít milník +issues.new.open_milestone=Otevřené milníky issues.new.closed_milestone=Zavřené milníky issues.new.assignees=Zpracovatelé issues.new.clear_assignees=Smazat zpracovatele -issues.new.no_assignees=Bez zpracovatelů +issues.new.no_assignees=Žádní přiřazení uživatelé issues.new.no_reviewers=Žádní posuzovatelé issues.choose.get_started=Začínáme issues.choose.open_external_link=Otevřít @@ -1415,15 +1439,15 @@ issues.choose.ignore_invalid_templates=Neplatné šablony byly ignorovány issues.choose.invalid_templates=%v nalezených neplatných šablon issues.choose.invalid_config=Nastavení problému obsahuje chyby: issues.no_ref=Není určena žádná větev/značka -issues.create=Vytvořit úkol +issues.create=Vytvořit problém issues.new_label=Nový štítek issues.new_label_placeholder=Název štítku issues.new_label_desc_placeholder=Popis issues.create_label=Vytvořit štítek -issues.label_templates.title=Nahrát předdefinovanou sadu značek -issues.label_templates.info=Zatím nebyly vytvořeny žádné štítky. Vytvořte štítek kliknutím na „Nový štítek“ nebo použijte přednastavenou sadu štítků: -issues.label_templates.helper=Vyberte sadu značek -issues.label_templates.use=Použít sadu štítků +issues.label_templates.title=Nahrát přednastavené štítky +issues.label_templates.info=Zatím nebyly vytvořeny žádné štítky. Vytvořte štítek kliknutím na „Nový štítek“ nebo použijte přednastavené štítky: +issues.label_templates.helper=Vyberte přednastavené značky +issues.label_templates.use=Použít přednastavené štítky issues.label_templates.fail_to_load_file=Nepodařilo se načíst soubor šablony popisku „%s“: %v issues.add_label=přidal/a %s štítek %s issues.add_labels=přidal/a %s štítky %s @@ -1511,7 +1535,7 @@ issues.commented_at=`okomentoval %s` issues.delete_comment_confirm=Jste si jist, že chcete smazat tento komentář? issues.context.copy_link=Kopírovat odkaz issues.context.quote_reply=Citovat odpověď -issues.context.reference_issue=Odkázat v novém úkolu +issues.context.reference_issue=Odkázat v novém problému issues.context.edit=Upravit issues.context.delete=Smazat issues.no_content=K dispozici není žádný popis. @@ -1520,7 +1544,7 @@ issues.comment_pull_merged_at=sloučený commit %[1]s do %[2]s %[3]s issues.comment_manually_pull_merged_at=ručně sloučený commit %[1]s do %[2]s %[3]s issues.close_comment_issue=Okomentovat a zavřít issues.reopen_issue=Znovuotevřít -issues.reopen_comment_issue=Okomentovat a znovuotevřít +issues.reopen_comment_issue=Okomentovat a znovu otevřít issues.create_comment=Okomentovat issues.closed_at=`uzavřel/a tento úkol %[2]s` issues.reopened_at=`znovuotevřel/a tento úkol %[2]s` @@ -1567,7 +1591,7 @@ issues.label_open_issues=%d otevřených úkolů issues.label_edit=Upravit issues.label_delete=Smazat issues.label_modify=Upravit štítek -issues.label_deletion=Smazat štítek +issues.label_deletion=Odstranit štítek issues.label_deletion_desc=Odstranění štítku jej smaže ze všech úkolů. Pokračovat? issues.label_deletion_success=Štítek byl odstraněn. issues.label.filter_sort.alphabetically=Od začátku abecedy @@ -1669,7 +1693,7 @@ issues.dependency.blocked_by_short=Závisí na issues.dependency.remove_header=Odstranit závislost issues.dependency.issue_remove_text=Tímto krokem odeberete závislost z úkolu. Pokračovat? issues.dependency.pr_remove_text=Tímto krokem odeberete závislost z požadavku na natažení. Pokračovat? -issues.dependency.setting=Povolit závislosti pro úkoly a požadavky na natažení +issues.dependency.setting=Povolit závislosti pro problémy a žádosti o sloučení issues.dependency.add_error_same_issue=Úkol nemůže záviset sám na sobě. issues.dependency.add_error_dep_issue_not_exist=Související úkol neexistuje. issues.dependency.add_error_dep_not_exist=Závislost neexistuje. @@ -1718,9 +1742,9 @@ compare.compare_base=základní compare.compare_head=porovnat pulls.desc=Povolit požadavky na natažení a posuzování kódu. -pulls.new=Nový požadavek na natažení -pulls.view=Zobrazit požadavek na natažení -pulls.compare_changes=Nový požadavek na natažení +pulls.new=Nová žádost o sloučení +pulls.view=Zobrazit žádost o sloučení +pulls.compare_changes=Nová žádost o sloučení pulls.allow_edits_from_maintainers=Povolit úpravy od správců pulls.allow_edits_from_maintainers_desc=Uživatelé s přístupem k zápisu do základní větve mohou také nahrávat do této větve pulls.allow_edits_from_maintainers_err=Aktualizace se nezdařila @@ -1747,9 +1771,9 @@ pulls.nothing_to_compare=Tyto větve jsou stejné. Není potřeba vytvářet po pulls.nothing_to_compare_have_tag=Vybraná větev/značka je stejná. pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tento požadavek na natažení bude prázdný. pulls.has_pull_request=`Požadavek na natažení mezi těmito větvemi již existuje: %[2]s#%[3]d` -pulls.create=Vytvořit požadavek na natažení -pulls.title_desc=chce sloučit %[1]d commity z větve %[2]s do %[3]s -pulls.merged_title_desc=sloučil %[1]d commity z větve %[2]s do větve %[3]s před %[4]s +pulls.create=Vytvořit žádost o sloučení +pulls.title_desc_few=chce sloučit %[1]d commity z větve %[2]s do %[3]s +pulls.merged_title_desc_few=sloučil %[1]d commity z větve %[2]s do větve %[3]s před %[4]s pulls.change_target_branch_at=`změnil/a cílovou větev z %s na %s %s` pulls.tab_conversation=Konverzace pulls.tab_commits=Commity @@ -1816,9 +1840,9 @@ pulls.unrelated_histories=Sloučení selhalo: Hlavní a základní revize nesdí pulls.merge_out_of_date=Sloučení selhalo: Základ byl aktualizován při generování sloučení. Tip: Zkuste to znovu. pulls.head_out_of_date=Sloučení selhalo: Hlavní revize byla aktualizován při generování sloučení. Tip: Zkuste to znovu. pulls.has_merged=Chyba: Požadavek na natažení byl sloučen, nelze znovu sloučit nebo změnit cílovou větev. -pulls.push_rejected=Sloučení selhalo: Nahrání bylo zamítnuto. Zkontrolujte háčky Gitu pro tento repozitář. +pulls.push_rejected=Push selhal: nahrání bylo zamítnuto. Zkontrolujte Git hooky pro tento repozitář. pulls.push_rejected_summary=Úplná zpráva o odmítnutí -pulls.push_rejected_no_message=Sloučení se nezdařilo: Nahrání bylo odmítnuto, ale nebyla nalezena žádná vzdálená zpráva.
      Zkontrolujte háčky gitu pro tento repozitář +pulls.push_rejected_no_message=Push selhal: nahrání bylo odmítnuto, ale nebyla nalezena žádná vzdálená zpráva. Zkontrolujte Git hooky pro tento repozitář pulls.open_unmerged_pull_exists=`Nemůžete provést operaci znovuotevření protože je tu čekající požadavek na natažení (#%d) s identickými vlastnostmi.` pulls.status_checking=Některé kontroly jsou nedořešeny pulls.status_checks_success=Všechny kontroly byly úspěšné @@ -1834,7 +1858,7 @@ pulls.update_branch_rebase=Aktualizovat větev pomocí rebase pulls.update_branch_success=Aktualizace větve byla úspěšná pulls.update_not_allowed=Nemáte oprávnění aktualizovat větev pulls.outdated_with_base_branch=Tato větev je zastaralá oproti základní větvi -pulls.close=Zavřít požadavek na natažení +pulls.close=Zavřít žádost o sloučení pulls.closed_at=`uzavřel/a tento požadavek na natažení %[2]s` pulls.reopened_at=`znovuotevřel/a tento požadavek na natažení %[2]s` pulls.cmd_instruction_hint=`Zobrazit instrukce příkazové řádky.` @@ -1879,13 +1903,13 @@ milestones.create_success=Milník „%s“ byl vytvořen. milestones.edit=Upravit milník milestones.edit_subheader=Milník organizuje úkoly a sledují pokrok. milestones.cancel=Zrušit -milestones.modify=Aktualizovat milník +milestones.modify=Upravit milník milestones.edit_success=Milník „%s“ byl aktualizován. -milestones.deletion=Smazat milník +milestones.deletion=Odstranit milník milestones.deletion_desc=Odstranění milníku jej smaže ze všech souvisejících úkolů. Pokračovat? milestones.deletion_success=Milník byl odstraněn. -milestones.filter_sort.earliest_due_data=Nejstarší datum dokončení -milestones.filter_sort.latest_due_date=Nejnovější datum dokončení +milestones.filter_sort.earliest_due_data=Nejbližší termín dokončení +milestones.filter_sort.latest_due_date=Nejzazší termín dokončení milestones.filter_sort.least_complete=Nejméně dokončené milestones.filter_sort.most_complete=Nejvíce dokončené milestones.filter_sort.most_issues=Nejvíce úkolů @@ -1945,38 +1969,38 @@ activity.period.quarterly=3 měsíce activity.period.semiyearly=6 měsíců activity.period.yearly=1 rok activity.overview=Přehled -activity.active_prs_count_1=%d aktivní požadavek na natažení -activity.active_prs_count_n=%d aktivní požadavky na natažení -activity.merged_prs_count_1=Sloučený požadavek na natažení -activity.merged_prs_count_n=Sloučené požadavky na natažení -activity.opened_prs_count_1=Navrhovaný požadavek na natažení -activity.opened_prs_count_n=Navrhované požadavky na natažení +activity.active_prs_count_1=%d aktivní žádost o sloučení +activity.active_prs_count_n=%d aktivních žádostí o sloučení +activity.merged_prs_count_1=Sloučená žádost +activity.merged_prs_count_n=Sloučené žádosti +activity.opened_prs_count_1=Navrhovaná žádost o sloučení +activity.opened_prs_count_n=Navrhované žádosti o sloučení activity.title.user_1=%d uživatel activity.title.user_n=%d uživatelů -activity.title.prs_1=%d Požadavek na natažení -activity.title.prs_n=%d Požadavků na natažení +activity.title.prs_1=%d žádost o sloučení +activity.title.prs_n=%d žádostí o sloučení activity.title.prs_merged_by=%s sloučil %s activity.title.prs_opened_by=%s navrhl %s activity.merged_prs_label=Sloučený activity.opened_prs_label=Navrhovaný -activity.active_issues_count_1=%d aktivní úkol -activity.active_issues_count_n=%d aktivní úkoly -activity.closed_issues_count_1=Uzavřený úkol -activity.closed_issues_count_n=Uzavřené úkoly -activity.title.issues_1=%d úkol -activity.title.issues_n=%d úkolů +activity.active_issues_count_1=%d aktivní problém +activity.active_issues_count_n=%d aktivních problémů +activity.closed_issues_count_1=Uzavřený problém +activity.closed_issues_count_n=Uzavřené problémy +activity.title.issues_1=%d problémy +activity.title.issues_n=%d problémů activity.title.issues_closed_from=%s uzavřel z %s activity.title.issues_created_by=%s vytvořil %s activity.closed_issue_label=Uzavřený -activity.new_issues_count_1=Nový úkol -activity.new_issues_count_n=Nové úkoly +activity.new_issues_count_1=Nový problém +activity.new_issues_count_n=Nové problémy activity.new_issue_label=Otevřený activity.title.unresolved_conv_1=%d nevyřešená konverzace activity.title.unresolved_conv_n=%d nevyřešených konverzací activity.unresolved_conv_desc=Tyto nedávno změněné úkolu a požadavky na natažení ještě nebyly vyřešeny. activity.unresolved_conv_label=Otevřít -activity.title.releases_1=%d Vydání -activity.title.releases_n=%d Vydání +activity.title.releases_1=%d vydání +activity.title.releases_n=%d vydání activity.title.releases_published_by=%s publikoval %s activity.published_release_label=Publikováno activity.no_git_activity=V tomto období nebyla žádná aktivita při odevzdání. @@ -2025,7 +2049,7 @@ settings.collaboration.read=Čtení settings.collaboration.owner=Vlastník settings.collaboration.undefined=Neurčeno settings.hooks=Webové háčky -settings.githooks=Háčky Gitu +settings.githooks=Git hooky settings.basic_settings=Základní nastavení settings.mirror_settings=Nastavení zrcadla settings.mirror_settings.docs=Nastavte repozitář pro automatickou synchronizaci commitů, značek a větví s jiným repozitářem. @@ -2042,7 +2066,7 @@ settings.mirror_settings.direction.pull=Natáhnout settings.mirror_settings.direction.push=Nahrát settings.mirror_settings.last_update=Poslední aktualizace settings.mirror_settings.push_mirror.none=Nenastavena žádná zrcadla pro nahrání -settings.mirror_settings.push_mirror.remote_url=URL vzdáleného Git repozitáře +settings.mirror_settings.push_mirror.remote_url=Adresa URL vzdáleného Git repozitáře settings.mirror_settings.push_mirror.add=Přidat zrcadlo pro nahrání settings.mirror_settings.push_mirror.edit_sync_time=Upravit interval synchronizace zrcadla @@ -2054,21 +2078,21 @@ settings.branches.switch_default_branch=Přepnout výchozí větev settings.branches.update_default_branch=Aktualizovat výchozí větev settings.branches.add_new_rule=Přidat nové pravidlo settings.advanced_settings=Pokročilá nastavení -settings.wiki_desc=Povolit Wiki repozitáře -settings.use_internal_wiki=Používat vestavěnou Wiki -settings.use_external_wiki=Používat externí Wiki -settings.external_wiki_url=URL externí Wiki +settings.wiki_desc=Povolit wiki repozitáře +settings.use_internal_wiki=Používat vestavěnou wiki +settings.use_external_wiki=Použít externí wiki +settings.external_wiki_url=Adresa URL externí wiki settings.external_wiki_url_error=URL externí wiki platné URL. settings.external_wiki_url_desc=Když návštěvníci kliknou na záložku Wiki, jsou přesměrování na URL externí Wiki. -settings.issues_desc=Povolit systém úkolů repozitáře -settings.use_internal_issue_tracker=Použít vestavěný systém úkolů -settings.use_external_issue_tracker=Použít externí systém úkolů -settings.external_tracker_url=URL externího systému úkolů +settings.issues_desc=Povolit systém problémů repozitáře +settings.use_internal_issue_tracker=Použít vestavěný systém problémů +settings.use_external_issue_tracker=Použít externí systém problémů +settings.external_tracker_url=Adresa URL externího systému problémů settings.external_tracker_url_error=URL externího systému úkolu není platné URL. settings.external_tracker_url_desc=Když návštěvníci kliknou na záložku úkolů, jsou přesměrování na externí systém úkolů. -settings.tracker_url_format=Formát URL externího systému úkolů +settings.tracker_url_format=Formát adresy URL externího systému problémů settings.tracker_url_format_error=Formát URL externího systému úkolu není platné URL. -settings.tracker_issue_style=Formát čísel externího systému úkolů +settings.tracker_issue_style=Formát čísel externího systému problémů settings.tracker_issue_style.numeric=Číselný settings.tracker_issue_style.alphanumeric=Alfanumerický settings.tracker_issue_style.regexp=Regulární výraz @@ -2077,7 +2101,7 @@ settings.tracker_issue_style.regexp_pattern_desc=První zachycená skupina bude settings.tracker_url_format_desc=Použijte zástupné symboly {user}, {repo} a {index} pro uživatelské jméno, jméno repozitáře a číslo úkolu. settings.enable_timetracker=Povolit sledování času settings.allow_only_contributors_to_track_time=Povolit sledování času pouze přispěvatelům -settings.pulls_desc=Povolit požadavky na natažení +settings.pulls_desc=Povolit žádosti o sloučení settings.pulls.ignore_whitespace=Ignorovat bílé znaky při konfliktech settings.pulls.enable_autodetect_manual_merge=Povolit autodetekci ručních sloučení (Poznámka: V některých zvláštních případech může dojít k nesprávnému rozhodnutí) settings.pulls.allow_rebase_update=Povolit aktualizaci větve požadavku na natažení pomocí rebase @@ -2087,7 +2111,7 @@ settings.releases_desc=Povolit vydání v repozitáři settings.packages_desc=Povolit registr balíčků repozitáře settings.projects_desc=Povolit projekty v repozitáři settings.actions_desc=Povolit akce repozitáře -settings.admin_settings=Nastavení správce +settings.admin_settings=Administrátorská nastavení settings.admin_enable_health_check=Povolit kontrolu stavu repozitáře (git fsck) settings.admin_code_indexer=Indexování kódu settings.admin_stats_indexer=Index statistiky kódu @@ -2121,7 +2145,7 @@ settings.transfer_notices_1=- Ztratíte přístup k repozitáři, pokud jej pře settings.transfer_notices_2=- Zůstane vám přístup k repozitáři, pokud jej převedete na organizaci kterou (spolu)vlastníte. settings.transfer_notices_3=- Pokud je repozitář soukromý a je předán jednotlivému uživateli, tato akce se ujistí, že uživatel má alespoň oprávnění ke čtení (a v případě potřeby změní oprávnění). settings.transfer_owner=Nový vlastník -settings.transfer_perform=Provést převod +settings.transfer_perform=Provést přesun settings.transfer_started=Tento repozitář byl označen pro převod a čeká na potvrzení od „%s“ settings.transfer_succeed=Repozitář byl předán. settings.signing_settings=Nastavení ověřování podpisu @@ -2137,12 +2161,12 @@ settings.trust_model.committer.desc=Platné podpisy budou označeny pouze jako settings.trust_model.collaboratorcommitter=Spolupracovník+Přispěvatel settings.trust_model.collaboratorcommitter.long=Spolupracovník+Přispěvatel: Důvěřovat podpisům od spolupracovníků, které odpovídají tvůrci revize settings.trust_model.collaboratorcommitter.desc=Platné podpisy spolupracovníků tohoto repozitáře budou označeny jako „důvěryhodné“, pokud se shodují s přispěvatelem. V opačném případě budou platné podpisy označeny jako "nedůvěryhodné", pokud se podpis shoduje s přispěvatelem a „neodpovídajícím“ v opačném případě. To přinutí Giteu, aby byla označena jako přispěvatel podepsaných commitů se skutečným přispěvatelem označeným jako Co-Authored-By: a Co-Committed-By: na konci commitu. Výchozí klíč Forgejo musí odpovídat uživateli v databázi. -settings.wiki_delete=Odstranit data Wiki +settings.wiki_delete=Odstranit data wiki settings.wiki_delete_desc=Smazání Wiki dat repozitáře je trvalé a nemůže být vráceno zpět. settings.wiki_delete_notices_1=- Natrvalo odstraní a zakáže wiki repozitáře pro %s. -settings.confirm_wiki_delete=Odstranit data Wiki +settings.confirm_wiki_delete=Odstranit data wiki settings.wiki_deletion_success=Wiki data repozitáře byla odstraněna. -settings.delete=Smazat tento repozitář +settings.delete=Odstranit tento repozitář settings.delete_desc=Smazání repozitáře je trvalé a nemůže být vráceno zpět. settings.delete_notices_1=- Tuto operaci nelze zvrátit. settings.delete_notices_2=- Tato operace trvale smaže repozitář %s včetně kódu, úkolů, komentářů, Wiki dat a nastavení spolupracovníků. @@ -2150,7 +2174,7 @@ settings.delete_notices_fork_1=- Rozštěpení repozitáře bude nezávislé po settings.deletion_success=Repozitář byl odstraněn. settings.update_settings_success=Nastavení repozitáře bylo aktualizováno. settings.update_settings_no_unit=Repozitář by měl povolit alespoň určitý druh interakce. -settings.confirm_delete=Smazat repozitář +settings.confirm_delete=Odstranit repozitář settings.add_collaborator=Přidat spolupracovníka settings.add_collaborator_success=Spolupracovník byl přidán. settings.add_collaborator_inactive_user=Nelze přidat neaktivního uživatele jako spolupracovníka. @@ -2172,10 +2196,10 @@ settings.search_team=Vyhledat tým… settings.change_team_permission_tip=Oprávnění týmu je nastaveno na stránce nastavení týmu a nelze je změnit pro každý repozitář settings.delete_team_tip=Tento tým má přístup ke všem repositářům a nemůže být odstraněn settings.remove_team_success=Přístup týmu k repozitáři byl odstraněn. -settings.add_webhook=Přidat webový háček +settings.add_webhook=Přidat webhook settings.add_webhook.invalid_channel_name=Kanál webového háčku nemůže být prázdný a nemůže obsahovat pouze znak #. settings.hooks_desc=Webové háčky automaticky vytvářejí dotazy HTTP POST na server, když nastane určitá událost v Forgejo. Čtěte více v příručce webových háčků. -settings.webhook_deletion=Odstranit webový háček +settings.webhook_deletion=Odstranit webhook settings.webhook_deletion_desc=Odstranění webového háčku smaže jeho nastavení a historii doručení. Pokračovat? settings.webhook_deletion_success=Webový háček byl smazán. settings.webhook.test_delivery=Test doručitelnosti @@ -2189,23 +2213,23 @@ settings.webhook.body=Tělo zprávy settings.webhook.replay.description=Zopakovat tento webový háček. settings.webhook.replay.description_disabled=Chcete-li znovu spustit tento webový háček, aktivujte jej. settings.webhook.delivery.success=Událost byla přidána do fronty doručení. Může to trvat několik sekund, než se zobrazí v historii doručení. -settings.githooks_desc=Jelikož Git háčky jsou spravovány Gitem samotným, můžete upravit soubory háčků níže, k provádění libovolných operací. +settings.githooks_desc=Git hooks jsou spravovány samotným Gitem. Níže můžete upravit soubory hooků pro nastavení vlastních operací. settings.githook_edit_desc=Je-li háček neaktivní, bude zobrazen vzorový obsah. Nebude-li zadán žádný obsah, háček bude vypnut. -settings.githook_name=Název háčku -settings.githook_content=Obsah háčku -settings.update_githook=Aktualizovat háček -settings.add_webhook_desc=Forgejo odešle dotaz POST s nastaveným Content Type na cílovou URL. Čtěte více v průvodci webovými háčky. +settings.githook_name=Název hooku +settings.githook_content=Obsah hooku +settings.update_githook=Upravit hook +settings.add_webhook_desc=Forgejo odešle na cílovou adresu URL dotaz POST s nastaveným Content-Type. Více informací v průvodci webhooky. settings.payload_url=Cílové URL settings.http_method=HTTP metoda -settings.content_type=POST Content Type +settings.content_type=Typ obsahu POST settings.secret=Tajný klíč settings.slack_username=Uživatelské jméno settings.slack_icon_url=URL ikony uživatele settings.slack_color=Barva settings.discord_username=Uživatelské jméno settings.discord_icon_url=URL ikony -settings.event_desc=Spuštěno na: -settings.event_push_only=Události nahrání +settings.event_desc=Spustit při: +settings.event_push_only=Události nahrání (push) settings.event_send_everything=Všechny události settings.event_choose=Vlastní události… settings.event_header_repository=Události repozitáře @@ -2223,33 +2247,33 @@ settings.event_push=Nahrát settings.event_push_desc=Nahrání pomocí Gitu do repozitáře. settings.event_repository=Repozitář settings.event_repository_desc=Repozitář vytvořen nebo smazán. -settings.event_header_issue=Události úkolů +settings.event_header_issue=Události problémů settings.event_issues=Úkoly settings.event_issues_desc=Úkol otevřen, uzavřen, znovu otevřen nebo upraven. -settings.event_issue_assign=Úkol přiřazen +settings.event_issue_assign=Problém přiřazen settings.event_issue_assign_desc=Úkol přiřazen nebo nepřiřazen. -settings.event_issue_label=Úkol oštítkován +settings.event_issue_label=Problém označen settings.event_issue_label_desc=Štítky úkolu aktualizovány nebo vymazány. -settings.event_issue_milestone=Úkolu přidán milník +settings.event_issue_milestone=K problému přidán milník settings.event_issue_milestone_desc=Úkolu přidán nebo odebrán milník. -settings.event_issue_comment=Komentář k úkolu +settings.event_issue_comment=Komentář k problému settings.event_issue_comment_desc=Komentář úkolu přidán, upraven nebo smazán. -settings.event_header_pull_request=Události požadavku na natažení -settings.event_pull_request=Požadavek na stažení +settings.event_header_pull_request=Události žádosti o sloučení +settings.event_pull_request=Žádost o sloučení settings.event_pull_request_desc=Požadavek na natažení otevřen, uzavřen, znovu otevřen nebo upraven. -settings.event_pull_request_assign=Požadavek na natažení přiřazen +settings.event_pull_request_assign=Žádost o sloučení přiřazena settings.event_pull_request_assign_desc=Požadavek na natažení přiřazen nebo nepřiřazen. -settings.event_pull_request_label=Požadavek na natažení oštítkován +settings.event_pull_request_label=Žádost o sloučení označena settings.event_pull_request_label_desc=Štítky požadavku na natažení aktualizovány nebo vymazány. -settings.event_pull_request_milestone=Požadavku na natažení přidán milník +settings.event_pull_request_milestone=K žádosti o sloučení přidán milník settings.event_pull_request_milestone_desc=Požadavku na natažení přidán nebo odebrán milník. -settings.event_pull_request_comment=Požadavek na natažení okomentován +settings.event_pull_request_comment=Žádost o sloučení okomentována settings.event_pull_request_comment_desc=Komentář požadavku na natažení vytvořen, upraven nebo odstraněn. -settings.event_pull_request_review=Požadavek na natažení přezkoumán +settings.event_pull_request_review=Žádost o sloučení zkontrolována settings.event_pull_request_review_desc=Požadavek na natažení schválen, odmítnut nebo zkontrolován. -settings.event_pull_request_sync=Požadavek na natažení synchronizován +settings.event_pull_request_sync=Žádost o sloučení synchronizována settings.event_pull_request_sync_desc=Požadavek na natažení synchronizován. -settings.event_pull_request_review_request=Vyžádán požadavek na natažení +settings.event_pull_request_review_request=Vyžádána kontrola žádosti o sloučení settings.event_package=Balíček settings.event_package_desc=Balíček vytvořen nebo odstraněn v repozitáři. settings.branch_filter=Filtr větví @@ -2259,11 +2283,11 @@ settings.authorization_header_desc=Pokud vyplněno, bude připojeno k požadavk settings.active=Aktivní settings.active_helper=Informace o spuštěných událostech budou odeslány na URL webového háčku. settings.add_hook_success=Webový háček byl přidán. -settings.update_webhook=Aktualizovat webový háček +settings.update_webhook=Upravit webhook settings.update_hook_success=Webový háček byl aktualizován. -settings.delete_webhook=Odstranit webový háček -settings.recent_deliveries=Nedávné dodávky -settings.hook_type=Typ háčku +settings.delete_webhook=Odstranit webhook +settings.recent_deliveries=Nedávná doručení +settings.hook_type=Typ hooku settings.slack_token=Token settings.slack_domain=Doména settings.slack_channel=Kanál @@ -2300,28 +2324,28 @@ settings.deploy_key_deletion=Odstranit klíč pro nasazení settings.deploy_key_deletion_desc=Odstranění klíče pro nasazení zruší jeho přístup k repozitáři. Pokračovat? settings.deploy_key_deletion_success=Klíč pro nasazení byl odstraněn. settings.branches=Větve -settings.protected_branch=Ochrana větví +settings.protected_branch=Ochrana větve settings.protected_branch.save_rule=Uložit pravidlo settings.protected_branch.delete_rule=Odstranit pravidlo settings.protected_branch_can_push=Povolit nahrání? settings.protected_branch_can_push_yes=Můžete nahrávat settings.protected_branch_can_push_no=Nemůžete nahrávat -settings.branch_protection=Pravidla ochrany větví pro větev „%s“ -settings.protect_this_branch=Povolit ochranu větví +settings.branch_protection=Pravidla ochrany větve pro větev „%s“ +settings.protect_this_branch=Povolit ochranu větve settings.protect_this_branch_desc=Zabraňuje smazání a omezuje gitu nahrávání a slučování do větve. settings.protect_disable_push=Zakázat nahrávání settings.protect_disable_push_desc=Žádné nahrávání do této větve nebude povoleno. settings.protect_enable_push=Povolit nahrávání settings.protect_enable_push_desc=Každý, kdo má přístup k zápisu, bude moci nahrávat do této větve (ale ne vynucená nahrávání). settings.protect_enable_merge=Povolit sloučení -settings.protect_whitelist_committers=Povolit nahrání jen vyjmenovaným +settings.protect_whitelist_committers=Povolit omezené nahrání settings.protect_whitelist_committers_desc=Pouze povolení uživatelé budou moci nahrávat do této větve (ale ne vynucení nahrávání). settings.protect_whitelist_deploy_keys=Povolit nahrání klíčům pro nasazení s přístupem pro zápis. settings.protect_whitelist_users=Povolení uživatelé pro nahrávání: settings.protect_whitelist_search_users=Hledat uživatele… settings.protect_whitelist_teams=Povolené týmy pro nahrávání: settings.protect_whitelist_search_teams=Vyhledat týmy… -settings.protect_merge_whitelist_committers=Povolit vyjmenovaným slučování +settings.protect_merge_whitelist_committers=Povolit whitelist pro slučování settings.protect_merge_whitelist_committers_desc=Povolit pouze vyjmenovaným uživatelům nebo týmům slučovat požadavky na natažení do této větve. settings.protect_merge_whitelist_users=Povolení uživatelé pro slučování: settings.protect_merge_whitelist_teams=Povolené týmy pro slučování: @@ -2340,9 +2364,9 @@ settings.protect_approvals_whitelist_users=Povolení posuzovatelé: settings.protect_approvals_whitelist_teams=Povolené týmy pro posuzování: settings.dismiss_stale_approvals=Odmítnout nekvalitní schválení settings.dismiss_stale_approvals_desc=Pokud budou do větve nahrány nové revize, které mění obsah tohoto požadavku na natažení, všechna stará schválení budou zamítnuta. -settings.require_signed_commits=Vyžadovat podepsané revize +settings.require_signed_commits=Vyžadovat podepsané commity settings.require_signed_commits_desc=Odmítnout nahrání do této větve pokud nejsou podepsaná nebo jsou neověřitelná. -settings.protect_branch_name_pattern=Vzor jména chráněných větví +settings.protect_branch_name_pattern=Vzor jména chráněné větve settings.protect_branch_name_pattern_desc=Vzory názvů chráněných větví. Pro vzorovou syntaxi viz dokumentace. Příklady: main, release/** settings.protect_patterns=Vzory settings.protect_protected_file_patterns=Vzory chráněných souborů (oddělené středníkem „;“): @@ -2354,7 +2378,7 @@ settings.delete_protected_branch=Vypnout ochranu settings.update_protect_branch_success=Ochrana větví pro větev „%s“ byla aktualizována. settings.remove_protected_branch_success=Ochrana větví pro větev „%s“ byla zakázána. settings.remove_protected_branch_failed=Odstranění ochranného pravidla větve „%s“ se nezdařilo. -settings.protected_branch_deletion=Zakázat ochranu větví +settings.protected_branch_deletion=Zakázat ochranu větve settings.protected_branch_deletion_desc=Zakázání ochrany větví umožní uživatelům s právem zápisu nahrávat do této větve. Pokračovat? settings.block_rejected_reviews=Blokovat sloučení při zamítavých posouzeních settings.block_rejected_reviews_desc=Slučování nebude možné, pokud o změny požádají oficiální posuzovatelé, i když je k dispozici dostatek schválení. @@ -2364,7 +2388,7 @@ settings.block_outdated_branch=Blokovat sloučení, pokud je požadavek na nata settings.block_outdated_branch_desc=Slučování nebude možné, pokud je hlavní větev za základní větví. settings.default_branch_desc=Vybrat výchozí větev repozitáře pro požadavky na natažení a revize kódu: settings.merge_style_desc=Sloučit styly -settings.default_merge_style_desc=Výchozí styl sloučení pro požadavky na natažení: +settings.default_merge_style_desc=Výchozí styl sloučení settings.choose_branch=Vyberte větev… settings.no_protected_branch=Nejsou tu žádné chráněné větve. settings.edit_protected_branch=Upravit @@ -2378,10 +2402,10 @@ settings.tags.protection.allowed=Povoleno settings.tags.protection.allowed.users=Povolení uživatelé settings.tags.protection.allowed.teams=Povolené týmy settings.tags.protection.allowed.noone=Nikdo -settings.tags.protection.create=Chránit značku +settings.tags.protection.create=Přidat pravidlo settings.tags.protection.none=Neexistují žádné chráněné značky. settings.tags.protection.pattern.description=Můžete použít jediné jméno nebo vzor glob nebo regulární výraz, který bude odpovídat více značek. Přečtěte si více v průvodci chráněnými značkami. -settings.bot_token=Token pro robota +settings.bot_token=Token bota settings.chat_id=ID chatu settings.thread_id=ID vlákna settings.matrix.homeserver_url=URL adresa Homeserveru @@ -2395,7 +2419,7 @@ settings.archive.error=Nastala chyba při archivování repozitáře. Prohlédn settings.archive.error_ismirror=Nemůžete archivovat zrcadlený repozitář. settings.archive.branchsettings_unavailable=Nastavení větví není dostupné, pokud je repozitář archivovaný. settings.archive.tagsettings_unavailable=Nastavení značek není k dispozici, pokud je repozitář archivován. -settings.unarchive.button=Obnovit repozitář +settings.unarchive.button=Zrušit archivaci repozitáře settings.unarchive.header=Obnovit tento repozitář settings.unarchive.text=Obnovení repozitáře vrátí možnost přijímání commitů a nahrávání. Stejně tak se obnoví i možnost zadávání nových úkolů a požadavků na natažení. settings.unarchive.success=Repozitář byl úspěšně obnoven. @@ -2433,15 +2457,15 @@ settings.rename_branch_from=starý název větve settings.rename_branch_to=nový název větve settings.rename_branch=Přejmenovat větev -diff.browse_source=Procházet zdrojové kódy +diff.browse_source=Procházet zdroj diff.parent=rodič diff.commit=revize diff.git-notes=Poznámky diff.data_not_available=Rozdílový obsah není dostupný -diff.options_button=Možnosti rozdílového porovnání +diff.options_button=Možnosti porovnání diff.show_diff_stats=Zobrazit statistiky -diff.download_patch=Stáhněte soubor záplaty -diff.download_diff=Stáhněte rozdílový soubor +diff.download_patch=Stáhnout soubor patche +diff.download_diff=Stáhnout rozdílový soubor diff.show_split_view=Rozdělené zobrazení diff.show_unified_view=Jednotný pohled diff.whitespace_button=Bílý znak @@ -2463,7 +2487,7 @@ diff.file_suppressed=Rozdílový obsah nebyl zobrazen, protože je příliš vel diff.file_suppressed_line_too_long=Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá diff.too_many_files=Některé soubory nejsou zobrazny, neboť je v této revizi změněno mnoho souborů diff.show_more=Zobrazit více -diff.load=Načíst rozdílové porovnání +diff.load=Načíst porovnání diff.generated=vygenerováno diff.vendored=vendorováno diff.comment.add_line_comment=Přidat jednořádkový komentář @@ -2538,11 +2562,11 @@ release.add_tag=Vytvořit pouze značku release.releases_for=Vydání pro %s release.tags_for=Značky pro %s -branch.name=Jméno větve +branch.name=Název větve branch.already_exists=Větev pojmenovaná „%s“ již existuje. branch.delete_head=Smazat -branch.delete=Smazat větev „%s“ -branch.delete_html=Smazat větev +branch.delete=Odstranit větev „%s“ +branch.delete_html=Odstranit větev branch.delete_desc=Smazání větve je trvalé. Přestože zrušená větev může existovat i po krátkou dobu, než bude skutečně odstraněna, NELZE ji většinou vrátit. Pokračovat? branch.deletion_success=Větev „%s“ byla smazána. branch.deletion_failed=Nepodařilo se odstranit větev „%s“. @@ -2656,9 +2680,9 @@ settings.mirror_settings.docs.doc_link_pull_section = sekci „Pulling from a re settings.units.overview = Přehled settings.units.add_more = Přidat další... settings.push_mirror_sync_in_progress = Probíhá odesílání změn na vzdálený %s. -settings.wiki_globally_editable = Umožnit komukoli editovat Wiki +settings.wiki_globally_editable = Umožnit komukoli editovat wiki settings.confirmation_string = Potvrzovací řetězec -settings.wiki_rename_branch_main_notices_2 = Touto akcí trvale přejmenujete interní větev Wiki repozitáře %s. Existující kontroly budou muset být aktualizovány. +settings.wiki_rename_branch_main_notices_2 = Touto akcí trvale přejmenujete interní větev wiki repozitáře %s. Existující kontroly budou muset být aktualizovány. settings.wiki_branch_rename_failure = Nepodařilo se normalizovat název větve Wiki repozitáře. settings.add_collaborator_blocked_them = Nepodařilo se přidat spolupracovníka, jelikož má zablokovaného majitele repozitáře. settings.ignore_stale_approvals = Ignorovat zastaralá schválení @@ -2671,6 +2695,7 @@ settings.archive.mirrors_unavailable = Zrcadla nejsou dostupná, když je repozi settings.protect_enable_merge_desc = Kdokoli s přístupem k zápisu bude moci slučovat žádosti o sloučení do této větve. 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é commity ani otevírat problémy a žádosti o sloučení. settings.event_pull_request_review_request_desc = Bylo požádáno o posouzení žádosti o sloučení nebo bylo toto požádání odstraněno. +error.broken_git_hook = Zdá se, že u tohoto repozitáře jsou rozbité Git hooks. Pro jejich opravení se prosím řiďte pokyny v dokumentaci a poté odešlete několik commitů pro obnovení stavu. [graphs] component_loading_info = Tohle může chvíli trvat… @@ -2718,7 +2743,7 @@ settings.permission=Oprávnění settings.repoadminchangeteam=Správce úložišť může týmům přidávat a odebírat přístup settings.visibility=Viditelnost settings.visibility.public=Veřejná -settings.visibility.limited=Omezeno (Viditelné pouze pro ověřené uživatele) +settings.visibility.limited=Omezená (viditelné pouze pro ověřené uživatele) settings.visibility.limited_shortname=Omezený settings.visibility.private=Soukromá (viditelné jen členům organizace) settings.visibility.private_shortname=Soukromý @@ -2728,11 +2753,11 @@ settings.update_setting_success=Nastavení organizace bylo upraveno. settings.change_orgname_prompt=Poznámka: Změna názvu organizace také změní adresu URL vaší organizace a uvolní staré jméno této organizace. settings.change_orgname_redirect_prompt=Staré jméno bude přesměrovávat, dokud nebude znovu obsazeno. settings.update_avatar_success=Avatar organizace byl aktualizován. -settings.delete=Smazat organizaci -settings.delete_account=Smazat tuto organizaci +settings.delete=Odstranit organizaci +settings.delete_account=Odstranit tuto organizaci settings.delete_prompt=Organizace bude trvale odstraněna. Tato změna NEMŮŽE být vrácena! -settings.confirm_delete_account=Potvrdit smazání -settings.delete_org_title=Smazat organizaci +settings.confirm_delete_account=Potvrdit odstranění +settings.delete_org_title=Odstranit organizaci settings.delete_org_desc=Tato organizace bude trvale smazána. Pokračovat? settings.hooks_desc=Přidat webové háčky, které budou spouštěny pro všechny repozitáře v této organizaci. @@ -2773,11 +2798,11 @@ teams.settings=Nastavení teams.owners_permission_desc=Vlastníci mají plný přístup do všech repozitářů a mají správcovský přístup do této organizace. teams.members=Členové týmu teams.update_settings=Upravit nastavení -teams.delete_team=Smazat tým +teams.delete_team=Odstranit tým teams.add_team_member=Přidat člena týmu teams.invite_team_member=Pozvat do %s teams.invite_team_member.list=Čekající pozvánky -teams.delete_team_title=Smazat tým +teams.delete_team_title=Odstranit tým teams.delete_team_desc=Smazání týmu zruší přístup jeho členům. Pokračovat? teams.delete_team_success=Tým byl odstraněn. teams.read_permission_desc=Členství v tom týmu poskytuje právo čtení: členové mohou číst z a vytvářet klony repozitářů týmu. @@ -2827,7 +2852,7 @@ settings=Nastavení správce dashboard.new_version_hint=Gitea %s je nyní k dispozici, právě u vás běži %s. Podívej se na blogu pro více informací. dashboard.statistic=Souhrn dashboard.operations=Operace údržby -dashboard.system_status=Status systému +dashboard.system_status=Stav systému dashboard.operation_name=Název operace dashboard.operation_switch=Přepnout dashboard.operation_run=Spustit @@ -2852,7 +2877,7 @@ dashboard.delete_missing_repos=Smazat všechny repozitáře, které nemají Git dashboard.delete_missing_repos.started=Spuštěna úloha mazání všech repozitářů, které nemají Git soubory. dashboard.delete_generated_repository_avatars=Odstranit vygenerované avatary repozitářů dashboard.sync_repo_tags=Synchronizovat značky z git dat do databáze -dashboard.update_mirrors=Aktualizovat zrcadla +dashboard.update_mirrors=Upravit zrcadla dashboard.repo_health_check=Kontrola stavu všech repozitářů dashboard.check_repo_stats=Zkontrolovat všechny statistiky repositáře dashboard.archive_cleanup=Smazat staré archivy repozitářů @@ -2867,30 +2892,30 @@ 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_goroutine=Aktuální goroutines dashboard.current_memory_usage=Aktuální využití paměti -dashboard.total_memory_allocated=Přidělené paměti celkem -dashboard.memory_obtained=Celkem získané 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žívána 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=Celkem získané paměti zásobníku +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=Užití struktur MCache +dashboard.mcache_structures_usage=Využití struktur MCache dashboard.mcache_structures_obtained=Získané struktury MCache -dashboard.profiling_bucket_hash_table_obtained=Získaná analytická tabulka -dashboard.gc_metadata_obtained=Získané metadata GC +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í recyklace GC +dashboard.last_gc_time=Doba od posledního GC dashboard.total_gc_time=Celková pauza GC dashboard.total_gc_pause=Celková pauza GC dashboard.last_gc_pause=Poslední pauza GC @@ -2928,7 +2953,7 @@ users.new_success=Uživatelský účet „%s“ byl vytvořen. users.edit=Upravit users.auth_source=Zdroj ověřování users.local=Místní -users.auth_login_name=Přihlašovací jméno způsobu ověřování +users.auth_login_name=Název ověření přihlášení users.password_helper=Ponechte heslo prázdné, aby se nezměnilo. users.update_profile_success=Uživatelský účet byl aktualizován. users.edit_account=Upravit uživatelský účet @@ -2938,12 +2963,12 @@ users.is_activated=Uživatelský účet je aktivován users.prohibit_login=Zakázat přihlášení users.is_admin=Je správce users.is_restricted=Je omezený -users.allow_git_hook=Může vytvářet háčky Gitu -users.allow_git_hook_tooltip=Háčky Gitu se spustí pod uživatelem operačního systému, jako běží Forgejo a budou mít stejnou úroveň přístupu k hostiteli. Díky tomu mohou uživatelé s tímto zvláštním oprávněním k háčkům Gitu přistupovat a upravovat všechny Forgejo repozitáře a také databázi používanou Giteou. V důsledku toho mohou také získat oprávnění administrátora Gitey. +users.allow_git_hook=Může vytvářet Git hooks +users.allow_git_hook_tooltip=Git hooks jsou spouštěny jako uživatel operačního systému, pod kterým je spuštěno Forgejo, a mají stejnou úroveň přístupu k hostiteli. Výsledkem je, že uživatelé s tímto speciálním oprávněním Git hooks mohou přistupovat ke všem repozitářům Forgejo a upravovat je, stejně jako databázi používanou systémem Forgejo. V důsledku toho mohou také získat oprávnění správce systému Forgejo. users.allow_import_local=Může importovat lokální repozitáře users.allow_create_organization=Může vytvářet organizace -users.update_profile=Aktualizovat uživatelský účet -users.delete_account=Smazat uživatelský účet +users.update_profile=Upravit uživatelský účet +users.delete_account=Odstranit uživatelský účet users.cannot_delete_self=Nemůžete odstranit sami sebe users.still_own_repo=Tento uživatel stále vlastní jeden nebo více repozitářů. Tyto repozitáře nejprve smažte nebo je převeďte. users.still_has_org=Uživatel je člen organizace. Nejprve odstraňte uživatele ze všech organizací. @@ -2966,7 +2991,7 @@ users.list_status_filter.is_2fa_enabled=2FA povoleno users.list_status_filter.not_2fa_enabled=2FA zakázáno users.details=Detaily uživatele -emails.email_manage_panel=Správa e-mailů uživatele +emails.email_manage_panel=Správa uživatelských e-mailů emails.primary=Hlavní emails.activated=Aktivován emails.filter_sort.email=E-mail @@ -2985,7 +3010,7 @@ orgs.teams=Týmy orgs.members=Členové orgs.new_orga=Nová organizace -repos.repo_manage_panel=Správa repozitáře +repos.repo_manage_panel=Správa repozitářů repos.unadopted=Nepřijaté repozitáře repos.unadopted.no_more=Nebyly nalezeny žádné další nepřijaté repositáře repos.owner=Vlastník @@ -3011,15 +3036,15 @@ packages.repository=Repozitář packages.size=Velikost packages.published=Publikováno -defaulthooks=Výchozí webové háčky +defaulthooks=Výchozí webhooky defaulthooks.add_webhook=Přidat výchozí webový háček defaulthooks.update_webhook=Aktualizovat výchozí webový háček -systemhooks=Systémové webové háčky +systemhooks=Systémové webhooky systemhooks.add_webhook=Přidat systémový webový háček systemhooks.update_webhook=Aktualizovat systémový webový háček -auths.auth_manage_panel=Správa zdroje ověřování +auths.auth_manage_panel=Správa zdrojů ověřování auths.new=Přidat zdroj ověřování auths.name=Název auths.type=Typ @@ -3041,11 +3066,11 @@ auths.attribute_username_placeholder=Nechte prázdně a použije se uživatelsk auths.attribute_name=Atribut křestního jména auths.attribute_surname=Atribut příjmení auths.attribute_mail=Atribut e-mailové adresy -auths.attribute_ssh_public_key=Atribut veřejného SSH klíče -auths.attribute_avatar=Atributy avataru -auths.attributes_in_bind=Získat atributy v kontextu Bind DN +auths.attribute_ssh_public_key=Atribut veřejného klíče SSH +auths.attribute_avatar=Atribut avataru +auths.attributes_in_bind=Načíst atributy v kontextu bind DN auths.allow_deactivate_all=Povolit prázdný výsledek hledání pro deaktivaci všech uživatelů -auths.use_paged_search=Použijte vyhledávání ve stránce +auths.use_paged_search=Použít stránkované vyhledávání auths.search_page_size=Velikost stránky auths.filter=Uživatelský filtr auths.admin_filter=Správcovský filtr @@ -3060,18 +3085,18 @@ auths.map_group_to_team_removal=Odebrat uživatele z synchronizovaných týmů, auths.enable_ldap_groups=Povolit LDAP skupiny auths.ms_ad_sa=Atributy vyhledávání MS AD auths.smtp_auth=Typ ověření SMTP -auths.smtphost=Server SMTP +auths.smtphost=Hostitel SMTP auths.smtpport=Port SMTP auths.allowed_domains=Povolené domény auths.allowed_domains_helper=Nechte prázdné k povolení všech domén. Více domén oddělte čárkou („,“). auths.skip_tls_verify=Přeskočit ověření TLS auths.force_smtps=Vynutit SMTPS auths.force_smtps_helper=SMTPS se vždy používá na portu 465. Nastavením této hodnoty vynutíte použití SMTPS na jiných portech. (V opačném případě se na ostatních portech použije STARTTLS, pokud je podporován hostiteslkým serverem.) -auths.helo_hostname=HELO Hostname +auths.helo_hostname=Hostname HELO auths.helo_hostname_helper=Název hostitele odeslaný s HELO. Chcete-li odeslat aktuální název hostitele, ponechte prázdné. auths.disable_helo=Zakázat HELO auths.pam_service_name=Název služby PAM -auths.pam_email_domain=PAM e-mailová doména (volitelné) +auths.pam_email_domain=E-mailová doména PAM (volitelné) auths.oauth2_provider=Poskytovatel OAuth2 auths.oauth2_icon_url=URL ikony auths.oauth2_clientID=Klientské ID (klíč) @@ -3086,15 +3111,15 @@ auths.skip_local_two_fa=Přeskočit lokální 2FA auths.skip_local_two_fa_helper=Ponechání nenastavené hodnoty znamená, že místní uživatelé s nastavenou funkcí 2FA budou muset při přihlašování stále projít funkcí 2FA auths.oauth2_tenant=Nájemník auths.oauth2_scopes=Další rozsahy -auths.oauth2_required_claim_name=Požadovaný název tvrzení +auths.oauth2_required_claim_name=Požadovaný název claimu auths.oauth2_required_claim_name_helper=Nastavte toto jméno pro omezení přihlášení z tohoto zdroje pro uživatele s tvrzením s tímto jménem -auths.oauth2_required_claim_value=Požadovaná hodnota tvrzení +auths.oauth2_required_claim_value=Požadovaná hodnota claimu auths.oauth2_required_claim_value_helper=Nastavte tuto hodnotu pro omezení přihlášení z tohoto zdroje pro uživatele s tvrzením s tímto jménem a hodnotou auths.oauth2_group_claim_name=Název tvrzení poskytující názvy skupin pro tento zdroj. (nepovinné) -auths.oauth2_admin_group=Hodnota tvrzení pro skupinu uživatelů administrátorů. (Volitelné - vyžaduje název tvrzení výše) -auths.oauth2_restricted_group=Hodnota tvrzení pro skupinu omezených uživatelů. (Volitelné - vyžaduje název tvrzení výše) +auths.oauth2_admin_group=Hodnota claimu pro skupinu uživatelů administrátorů. (Volitelné - vyžaduje název claimu výše) +auths.oauth2_restricted_group=Hodnota claimu pro skupinu omezených uživatelů. (Volitelné - vyžaduje název claimu výše) auths.oauth2_map_group_to_team_removal=Odebrat uživatele z synchronizovaných týmů, pokud uživatel nepatří do odpovídající skupiny. -auths.enable_auto_register=Povolit zaregistrování se +auths.enable_auto_register=Povolit automatické registrace auths.sspi_auto_create_users=Automaticky vytvářet uživatele auths.sspi_auto_create_users_helper=Povolit SSPI autentizační metodě automaticky vytvářet nové účty pro uživatele, kteří se poprvé přihlásili auths.sspi_auto_activate_users=Automaticky aktivovat uživatele @@ -3126,9 +3151,9 @@ auths.edit=Upravit zdroj ověřování auths.activated=Tento zdroj ověřování je aktivován auths.new_success=Zdroj ověřování „%s“ byl přidán. auths.update_success=Zdroj ověřování byl aktualizován. -auths.update=Aktualizovat zdroj ověřování -auths.delete=Smazat zdroj ověřování -auths.delete_auth_title=Smazat zdroj ověřování +auths.update=Upravit zdroj ověřování +auths.delete=Odstranit zdroj ověřování +auths.delete_auth_title=Odstranit zdroj ověřování auths.delete_auth_desc=Zamezíte přihlášení uživatelům pomocí tohoto zdroje ověřování, pokud ho smažete. Pokračovat? auths.still_in_used=Zdroj ověřování je stále používán. Nejprve převeďte nebo smažte všechny uživatele, kteří používají tento způsob ověřování. auths.deletion_success=Zdroj ověřování byl smazán. @@ -3138,23 +3163,23 @@ auths.unable_to_initialize_openid=Nelze inicializovat poskytovatele OpenID Conne auths.invalid_openIdConnectAutoDiscoveryURL=Neplatná URL adresa pro automatické vyhledání (musí být platná adresa URL začínající http:// nebo https://) config.server_config=Nastavení serveru -config.app_name=Název stránky +config.app_name=Název instance config.app_ver=Verze Forgejo -config.app_url=Základní URL Forgejo +config.app_url=Základní URL config.custom_conf=Cesta ke konfiguračnímu souboru config.custom_file_root_path=Vlastní kořenový adresář souborů config.domain=Doména serveru config.offline_mode=Lokální režim -config.disable_router_log=Vypnout log směrovače +config.disable_router_log=Vypnout protokol směrovače config.run_user=Spustit jako uživatel config.run_mode=Režim spouštění config.git_version=Verze Gitu config.app_data_path=Cesta k datům aplikace config.repo_root_path=Kořenový adresář repozitářů config.lfs_root_path=Kořenový adresář LFS -config.log_file_root_path=Adresář logů +config.log_file_root_path=Adresář protokolů config.script_type=Typ skriptu -config.reverse_auth_user=Uživatel obráceného ověření +config.reverse_auth_user=Obrátit uživatele ověření config.ssh_config=Nastavení SSH config.ssh_enabled=Zapnutý @@ -3171,7 +3196,7 @@ config.ssh_minimum_key_sizes=Minimální velikost klíčů config.lfs_config=Nastavení LFS config.lfs_enabled=Povoleno config.lfs_content_path=Cesta k obsahu LFS -config.lfs_http_auth_expiry=Vypršení autorizace LFS HTTPS +config.lfs_http_auth_expiry=Čas vypršení autorizace LFS HTTP config.db_config=Nastavení databáze config.db_type=Typ @@ -3187,55 +3212,55 @@ config.register_email_confirm=Pro registraci vyžadovat potvrzení e-mailu config.disable_register=Vypnout možnost uživatelské registrace config.allow_only_internal_registration=Povolit registraci pouze prostřednictvím Forgejo config.allow_only_external_registration=Povolit registraci pouze prostřednictvím externích služeb -config.enable_openid_signup=Povolit automatickou registraci pomocí OpenID +config.enable_openid_signup=Povolit uživatelskou registraci pomocí OpenID config.enable_openid_signin=Povolit přihlášení pomocí OpenID -config.show_registration_button=Ukázat tlačítko registrace -config.require_sign_in_view=Vyžadovat přihlášení k zobrazení stránek +config.show_registration_button=Zobrazit tlačítko registrace +config.require_sign_in_view=Vyžadovat přihlášení pro zobrazení obsahu config.mail_notify=Povolit e-mailová oznámení config.enable_captcha=Povolit CAPTCHA -config.active_code_lives=Doba života aktivního kódu -config.reset_password_code_lives=Čas vypršení platnosti kódu pro obnovení účtu -config.default_keep_email_private=Jako počáteční nastavení skrýt e-mailové adresy -config.default_allow_create_organization=Dovolí novým uživatelům zakládat organizace +config.active_code_lives=Doba expirace aktivačního kódu +config.reset_password_code_lives=Čas vypršení platnosti obnovovacího kódu +config.default_keep_email_private=Ve výchozím nastavení skrýt e-mailové adresy +config.default_allow_create_organization=Povolit ve výchozím nastavení vytvářet organizace config.enable_timetracking=Povolit sledování času -config.default_enable_timetracking=Povolit sledování času ve výchozím nastavení +config.default_enable_timetracking=Povolit ve výchozím nastavení sledování času config.default_allow_only_contributors_to_track_time=Povolit sledování času pouze přispěvatelům config.no_reply_address=Skrytá e-mailová doména -config.default_visibility_organization=Výchozí viditelnost pro nové organizace -config.default_enable_dependencies=Povolit závislosti úkolů ve výchozím stavu +config.default_visibility_organization=Výchozí viditelnost nových organizací +config.default_enable_dependencies=Povolit ve výchozím nastavení závislosti úkolů -config.webhook_config=Nastavení webových háčků +config.webhook_config=Nastavení webhooků config.queue_length=Délka fronty config.deliver_timeout=Časový limit doručení -config.skip_tls_verify=Přeskočit verifikaci TLS +config.skip_tls_verify=Přeskočit ověření TLS config.mailer_config=Nastavení odesílání e-mailů config.mailer_enabled=Zapnutý config.mailer_enable_helo=Povolit HELO config.mailer_name=Název config.mailer_protocol=Protokol -config.mailer_smtp_addr=Adresa SMTP +config.mailer_smtp_addr=Hostitel SMTP config.mailer_smtp_port=Port SMTP config.mailer_user=Uživatel config.mailer_use_sendmail=Použít Sendmail config.mailer_sendmail_path=Cesta k Sendmail config.mailer_sendmail_args=Dodatečné argumenty pro Sendmail -config.mailer_sendmail_timeout=Časový limit Sandmail +config.mailer_sendmail_timeout=Časový limit Sendmail config.mailer_use_dummy=Fiktivní config.test_email_placeholder=E-mail (např.: test@example.com) config.send_test_mail=Odeslat zkušební e-mail config.send_test_mail_submit=Odeslat -config.test_mail_failed=Odeslání testovacího e-mailu na „%s“ selhalo: %v -config.test_mail_sent=Zkušební e-mail byl odeslán na „%s“. +config.test_mail_failed=Nepodařilo se odeslat zkušební e-mail na adresu „%s“: %v +config.test_mail_sent=Zkušební e-mail byl odeslán na adresu „%s“. -config.oauth_config=Nastavení ověření OAuth +config.oauth_config=Nastavení OAuth config.oauth_enabled=Zapnutý config.cache_config=Nastavení mezipaměti config.cache_adapter=Adaptér mezipaměti config.cache_interval=Interval mezipaměti config.cache_conn=Připojení mezipaměti -config.cache_item_ttl=Čas vypršení položky v mezipaměti +config.cache_item_ttl=TTL položky v mezipaměti config.session_config=Nastavení relace config.session_provider=Poskytovatel relace @@ -3243,37 +3268,37 @@ config.provider_config=Nastavení poskytovatele config.cookie_name=Název souboru cookie config.gc_interval_time=Čas intervalu GC config.session_life_time=Doba trvání relace -config.https_only=Pouze protokol HTTPS +config.https_only=Pouze HTTPS config.cookie_life_time=Doba života souboru cookie -config.picture_config=Nastavení obrázku a avataru -config.picture_service=Služba ikon uživatelů +config.picture_config=Nastavení obrázků a avatarů +config.picture_service=Služba obrázků config.disable_gravatar=Zakázat službu Gravatar -config.enable_federated_avatar=Povolit avatary z veřejných zdrojů +config.enable_federated_avatar=Povolit federované avatary config.git_config=Konfigurace Gitu -config.git_disable_diff_highlight=Zakázat zvýraznění syntaxe v rozdílovém zobrazení -config.git_max_diff_lines=Maximální počet rozdílových řádků jednoho souboru -config.git_max_diff_line_characters=Maximální počet zobrazených rozdílových znaků -config.git_max_diff_files=Maximální počet zobrazených rozdílových souborů -config.git_gc_args=Parametry GC +config.git_disable_diff_highlight=Zakázat zvýraznění syntaxe v zobrazení rozdílů +config.git_max_diff_lines=Maximální počet rozdílových řádků na soubor +config.git_max_diff_line_characters=Maximální počet zobrazených rozdílných znaků +config.git_max_diff_files=Maximální počet zobrazených rozdílných souborů +config.git_gc_args=Argumenty GC config.git_migrate_timeout=Časový limit migrace config.git_mirror_timeout=Časový limit aktualizace zrcadla -config.git_clone_timeout=Časový limit operace naklonování -config.git_pull_timeout=Časový limit operace stažení +config.git_clone_timeout=Časový limit operace klonování +config.git_pull_timeout=Časový limit operace Pull config.git_gc_timeout=Časový limit operace GC -config.log_config=Nastavení logů +config.log_config=Nastavení protokolů config.disabled_logger=Zakázané -config.access_log_mode=Režim logování přístupu -config.access_log_template=Šablona záznamu přístupu +config.access_log_mode=Režim protokolování přístupu +config.access_log_template=Šablona protokolu přístupu config.xorm_log_sql=Logovat SQL config.set_setting_failed=Nastavení %s se nezdařilo monitor.stats=Statistiky -monitor.cron=Naplánované úlohy +monitor.cron=Úlohy Cron monitor.name=Název monitor.schedule=Rozvrh monitor.next=Příští čas spuštění @@ -3300,31 +3325,31 @@ 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í fondu +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=Aktualizovat nastavení -monitor.queue.settings.changed=Nastavení aktualizováno +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=Zobrazit detaily oznámení +notices.view_detail_header=Podrobnosti oznámení notices.operations=Operace notices.select_all=Vybrat vše notices.deselect_all=Zrušit výběr všech -notices.inverse_selection=Inverzní výběr -notices.delete_selected=Smazat vybrané -notices.delete_all=Smazat všechna oznámení +notices.inverse_selection=Invertovat výběr +notices.delete_selected=Odstranit vybrané +notices.delete_all=Odstranit všechna oznámení notices.type=Typ notices.type_1=Repozitář notices.type_2=Úloha notices.desc=Popis notices.op=Akce notices.delete_success=Systémové upozornění bylo smazáno. -dashboard.sync_repo_branches = Synchronizovat zmeškané větve z dat gitu do databáze -dashboard.sync_repo_tags = Synchronizovat značky z dat gitu do databáze +dashboard.sync_repo_branches = Synchronizovat vynechané větve z dat Gitu do databáze +dashboard.sync_repo_tags = Synchronizovat značky z dat Gitu do databáze dashboard.gc_lfs = Sbírat garbage z LFS meta objektů 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ů. @@ -3343,7 +3368,7 @@ dashboard.sync_tag.started = Synchronizace značek spuštěna dashboard.rebuild_issue_indexer = Přestavit indexer vydání self_check.database_collation_case_insensitive = Databáze používá collation %s. Jedná se o intenzivní collation. Ačkoli s ní Forgejo nejspíše bude pracovat, mohou nastat určité vzácné případy, kdy nebude pracovat tak, jak má. self_check.database_fix_mssql = Uživatelé MSSQL mohou tento problém vyřešit pouze ručními SQL příkazy „ALTER ... COLLATE ...“. -auths.oauth2_map_group_to_team = Zmapovat zabrané skupiny u týmů organizací (volitelné - vyžaduje název zabrání výše) +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ů. self_check.no_problem_found=Zatím nebyl nalezen žádný problém. @@ -3352,6 +3377,7 @@ self_check.database_collation_case_insensitive=Databáze používá collation %s self_check.database_inconsistent_collation_columns=Databáze používá collation %s, ale tyto sloupce používají chybné collation. To může způsobit neočekávané problémy. self_check.database_fix_mysql=Pro uživatele MySQL/MariaDB můžete použít příkaz "gitea doctor convert", který opraví problémy s collation, nebo můžete také problém vyřešit příkazem "ALTER ... COLLATE ..." SQL ručně. self_check.database_fix_mssql=Uživatelé MSSQL mohou problém vyřešit pouze pomocí příkazu "ALTER ... COLLATE ..." SQL ručně. +auths.tips.gmail_settings = Nastavení služby Gmail: [action] create_repo=vytvořil/a repozitář %s @@ -3463,9 +3489,9 @@ dependencies=Závislosti keywords=Klíčová slova details=Podrobnosti details.author=Autor -details.project_site=Stránka projektu -details.repository_site=Stránka repositáře -details.documentation_site=Stránka dokumentace +details.project_site=Web projektu +details.repository_site=Web repositáře +details.documentation_site=Web dokumentace details.license=Licence assets=Prostředky versions=Verze @@ -3566,7 +3592,7 @@ owner.settings.cargo.initialize=Inicializovat index owner.settings.cargo.initialize.description=Pro použití Cargo registru 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=Znovu vytvořit index owner.settings.cargo.rebuild.error=Obnovení Cargo indexu se nezdařilo: %v owner.settings.cargo.rebuild.success=Cargo Index byl úspěšně obnoven. owner.settings.cleanuprules.title=Správa pravidel čištění @@ -3594,6 +3620,7 @@ owner.settings.chef.keypair=Generovat pár klíčů owner.settings.chef.keypair.description=Pro autentizaci do registru Chef je zapotřebí pár klíčů. Pokud jste předtím vytvořili pár klíčů, nově vygenerovaný pár klíčů vyřadí starý pár klíčů. rpm.repository.multiple_groups = Tento balíček je dostupný v několika skupinách. 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. [secrets] secrets=Tajné klíče @@ -3632,7 +3659,7 @@ runners.name=Název runners.owner_type=Typ runners.description=Popis runners.labels=Štítky -runners.last_online=Poslední čas online +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. @@ -3658,7 +3685,7 @@ 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 pracovní postupy +runs.all_workflows=Všechny workflowy runs.commit=Commit runs.scheduled=Naplánováno runs.invalid_workflow_helper=Konfigurační soubor pracovního postupu je neplatný. Zkontrolujte prosím konfigurační soubor: %s @@ -3674,9 +3701,9 @@ runs.no_workflows.documentation=Další informace o Gitea Actions naleznete v Codeberg nach Issues oder erstelle gegebenenfalls ein neues Issue. -missing_csrf=Fehlerhafte Anfrage: Kein CSRF Token verfügbar -invalid_csrf=Fehlerhafte Anfrage: Ungültiger CSRF Token +missing_csrf=Fehlerhafte Anfrage: Kein CSRF-Token verfügbar +invalid_csrf=Fehlerhafte Anfrage: Ungültiger CSRF-Token not_found=Das Ziel konnte nicht gefunden werden. network_error=Netzwerkfehler server_internal = Interner Serverfehler @@ -226,7 +241,7 @@ err_admin_name_pattern_not_allowed=Administrator-Benutzername ist ungültig, der err_admin_name_is_invalid=Administratornutzername ist ungültig general_title=Allgemeine Einstellungen -app_name=Seitentitel +app_name=Instanztitel app_name_helper=Du kannst hier den Namen deines Unternehmens eingeben. repo_path=Repository-Verzeichnis repo_path_helper=Remote-Git-Repositorys werden in diesem Verzeichnis gespeichert. @@ -238,9 +253,9 @@ domain=Server-Domain domain_helper=Domain oder Host-Adresse für den Server. ssh_port=SSH-Server-Port ssh_port_helper=Der Port deines SSH-Servers. Leer lassen, um SSH zu deaktivieren. -http_port=Forgejo-HTTP-Listen-Port +http_port=HTTP-Listen-Port http_port_helper=Port, unter dem der Forgejo-Webserver laufen soll. -app_url=Forgejo-Basis-URL +app_url=Basis-URL app_url_helper=Adresse für HTTP(S)-Klon-URLs und E-Mail-Benachrichtigungen. log_root_path=Logdateipfad log_root_path_helper=Log-Dateien werden in diesem Verzeichnis gespeichert. @@ -257,7 +272,7 @@ register_confirm=E-Mail-Bestätigung benötigt zum Registrieren mail_notify=E-Mail-Benachrichtigungen aktivieren server_service_title=Sonstige Server- und Drittserviceeinstellungen offline_mode=Offline-Modus aktivieren -offline_mode_popup=Drittanbieter-CDNs deaktivieren und alle Ressourcen lokal zur Verfügung stellen. +offline_mode_popup=Drittanbieter-CDNs deaktivieren und alle Ressourcen lokal zustellen. disable_gravatar=Gravatar deaktivieren disable_gravatar_popup=Gravatar und Drittanbieter-Avatar-Quellen deaktivieren. Ein Standardavatar wird verwendet, bis der Nutzer einen eigenen Avatar hochlädt. federated_avatar_lookup=Föderierte Profilbilder einschalten @@ -310,6 +325,7 @@ env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Kon allow_dots_in_usernames = Erlaubt Benutzern die Verwendung von Punkten in ihren Benutzernamen. Hat keine Auswirkungen auf bestehende Konten. enable_update_checker_helper_forgejo = Prüft regelmäßig auf neue Forgejo-Versionen, indem ein DNS-TXT-Eintrag unter release.forgejo.org überprüft wird. smtp_from_invalid = Die „Sende E-Mail Als“-Adresse ist ungültig +config_location_hint = Diese Konfigurationsoptionen werden gespeichert in: [home] uname_holder=E-Mail-Adresse oder Benutzername @@ -318,7 +334,7 @@ switch_dashboard_context=Kontext der Übersichtsseite wechseln my_repos=Repositorys show_more_repos=Zeige mehr Repositorys … collaborative_repos=Gemeinschaftliche Repositorys -my_orgs=Meine Organisationen +my_orgs=Organisationen my_mirrors=Meine Spiegel view_home=%s ansehen search_repos=Finde ein Repository … @@ -356,9 +372,13 @@ user_no_results=Keine passenden Benutzer gefunden. org_no_results=Keine passenden Organisationen gefunden. code_no_results=Es konnte kein passender Code für deinen Suchbegriff gefunden werden. code_search_results=Suchergebnisse für „%s“ -code_last_indexed_at=Zuletzt indexiert %s +code_last_indexed_at=Zuletzt indiziert %s relevant_repositories_tooltip=Repositorys, die Forks sind oder die kein Thema, kein Symbol und keine Beschreibung haben, werden ausgeblendet. relevant_repositories=Es werden nur relevante Repositorys angezeigt, ungefilterte Ergebnisse anzeigen. +stars_one = %d Favorisierung +stars_few = %d Favorisierungen +forks_one = %d Fork +forks_few = %d Forks [auth] create_new_account=Konto anlegen @@ -400,7 +420,7 @@ twofa_scratch_used=Du hast dein Einmalpasswort verwendet. Du wurdest zu den Eins twofa_passcode_incorrect=Ungültige PIN. Wenn du dein Gerät verloren hast, verwende dein Einmalpasswort. twofa_scratch_token_incorrect=Das Einmalpasswort ist falsch. login_userpass=Anmelden -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Neues Konto registrieren oauth_signup_title=Neuen Account fertigstellen oauth_signup_submit=Konto vervollständigen @@ -434,6 +454,8 @@ change_unconfirmed_email_error = Ändern der E-Mail-Adresse fehlgeschlagen: %v last_admin = Du kannst den letzten Administrator nicht entfernen. Es muss mindestens einen Administrator geben. change_unconfirmed_email = Wenn Sie bei der Registrierung eine falsche E-Mail-Adresse angegeben haben, können Sie diese unten ändern, woraufhin eine Bestätigung an die neue Adresse geschickt wird. remember_me.compromised = Der Anmeldetoken ist nicht mehr gültig, dies könnte auf ein kompromittiertes Konto hindeuten. Bitte prüfe dein Konto auf ungewöhnliche Aktivitäten. +tab_signin = Anmelden +tab_signup = Registrieren [mail] view_it_on=Auf %s ansehen @@ -603,6 +625,8 @@ org_still_own_packages=Diese Organisation besitzt noch ein oder mehrere Pakete, target_branch_not_exist=Der Ziel-Branch existiert nicht. username_error_no_dots = ` darf nur alphanumerische Zeichen („0-9“, „a-z“, „A-Z“), Bindestriche („-“), Unterstriche („_“) enthalten. Es kann nicht mit nicht-alphanumerischen Zeichen beginnen oder enden und aufeinanderfolgende nicht-alphanumerische Zeichen sind ebenfalls verboten.` admin_cannot_delete_self = Du kannst dich nicht selbst löschen, wenn du ein Admin bist. Bitte entferne zuerst deine Adminrechte. +unset_password = Der Anmeldebenutzer hat das Passwort nicht gesetzt. +unsupported_login_type = Dieser Login-Typ unterstützt keine Accountlöschung. [user] @@ -651,7 +675,7 @@ applications=Anwendungen orgs=Organisationen verwalten repos=Repositorys delete=Konto löschen -twofa=Zwei-Faktor-Authentifizierung +twofa=Zwei-Faktor-Authentifizierung (TOTP) account_link=Verknüpfte Benutzerkonten organization=Organisationen uid=UID @@ -667,7 +691,7 @@ website=Webseite location=Standort update_theme=Theme ändern update_profile=Profil aktualisieren -update_language=Sprache aktualisieren +update_language=Sprache ändern update_language_not_found=Sprache „%s“ ist nicht verfügbar. update_language_success=Sprache wurde aktualisiert. update_profile_success=Dein Profil wurde aktualisiert. @@ -699,7 +723,7 @@ comment_type_group_issue_ref=Issue-Referenz saved_successfully=Die Einstellungen wurden erfolgreich gespeichert. privacy=Datenschutz keep_activity_private=Aktivität auf der Profilseite ausblenden -keep_activity_private_popup=Macht die Aktivität nur für dich und die Administratoren sichtbar +keep_activity_private_popup=Deine Aktivität wird nur für dich und die Instanzadministratoren sichtbar sein lookup_avatar_by_mail=Profilbild anhand der E-Mail-Addresse suchen federated_avatar_lookup=Suche nach föderierten Profilbildern @@ -778,12 +802,12 @@ gpg_key_matched_identities_long=Die eingebetteten Identitäten in diesem Schlüs gpg_key_verified=Verifizierter Schlüssel gpg_key_verified_long=Der Schlüssel wurde mit einem Token verifiziert. Er kann verwendet werden, um Commits zu verifizieren, die mit irgendeiner für diesen Nutzer aktivierten E-Mail-Adresse und irgendeiner Identität dieses Schlüssels übereinstimmen. gpg_key_verify=Verifizieren -gpg_invalid_token_signature=Der GPG Key, die Signatur, und das Token stimmen nicht überein, oder das Token ist veraltet. +gpg_invalid_token_signature=Der GPG-Key, die Signatur, und das Token stimmen nicht überein, oder das Token ist veraltet. gpg_token_required=Du musst eine Signatur für das folgende Token angeben gpg_token=Token gpg_token_help=Du kannst eine Signatur wie folgt generieren: gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig -gpg_token_signature=GPG Textsignatur (armored signature) +gpg_token_signature=GPG-Textsignatur (armored signature) key_signature_gpg_placeholder=Beginnt mit „-----BEGIN PGP SIGNATURE-----“ verify_gpg_key_success=GPG-Schlüssel „%s“ wurde verifiziert. ssh_key_verified=Verifizierter Schlüssel @@ -793,7 +817,7 @@ ssh_invalid_token_signature=Der gegebene SSH-Schlüssel, Signatur oder Token sti ssh_token_required=Du musst eine Signatur für den Token unten angeben ssh_token=Token ssh_token_help=Du kannst eine Signatur wie folgt generieren: -ssh_token_signature=SSH Textsignatur (armored signature) +ssh_token_signature=SSH-Textsignatur (armored signature) key_signature_ssh_placeholder=Beginnt mit „-----BEGIN SSH SIGNATURE-----“ verify_ssh_key_success=SSH-Key „%s“ wurde verifiziert. subkeys=Unterschlüssel @@ -839,7 +863,7 @@ generate_new_token=Neuen Token erzeugen tokens_desc=Diese Tokens gewähren vollen Zugriff auf dein Konto via die Forgejo-API. token_name=Token-Name generate_token=Token generieren -generate_token_success=Ein neuer Token wurde generiert. Kopiere diesen, da er nicht erneut angezeigt wird. +generate_token_success=Ein neuer Token wurde generiert. Kopiere diesen jetzt, da er nicht erneut angezeigt wird. generate_token_name_duplicate=%s wurde bereits als Anwendungsname verwendet. Bitte wähle einen neuen Namen. delete_token=Löschen access_token_deletion=Zugriffstoken löschen @@ -894,17 +918,17 @@ twofa_is_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung twofa_not_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung momentan nicht eingeschaltet. twofa_disable=Zwei-Faktor-Authentifizierung deaktivieren twofa_scratch_token_regenerate=Neues Einmalpasswort erstellen -twofa_scratch_token_regenerated=Dein temporärer Token ist jetzt %s. Speichere ihn an einem sicheren Ort, er wird nie wieder angezeigt. +twofa_scratch_token_regenerated=Dein einmalig verwendbarer Wiederherstellungsschlüssel ist jetzt %s. Speichere ihn an einem sicheren Ort, denn er wird nie wieder angezeigt. twofa_enroll=Zwei-Faktor-Authentifizierung aktivieren twofa_disable_note=Du kannst die Zwei-Faktor-Authentifizierung auch wieder deaktivieren. twofa_disable_desc=Wenn du die Zwei-Faktor-Authentifizierung deaktivierst, wird die Sicherheit deines Kontos verringert. Fortfahren? -regenerate_scratch_token_desc=Wenn du dein Einmalpasswort verlegt oder es bereits benutzt hast, kannst du es hier zurücksetzen. +regenerate_scratch_token_desc=Wenn du deinen Wiederherstellungsschlüssel verlegst oder es bereits benutzt hast, kannst du es hier zurücksetzen. twofa_disabled=Zwei-Faktor-Authentifizierung wurde deaktiviert. scan_this_image=Scanne diese Grafik mit deiner Authentifizierungs-App: or_enter_secret=Oder gib das Secret ein: %s then_enter_passcode=Und gib dann die angezeigte PIN der Anwendung ein: passcode_invalid=Die PIN ist falsch. Probiere es erneut. -twofa_enrolled=Die Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Bewahre dein Einmalpasswort (%s) an einem sicheren Ort auf, da es nicht wieder angezeigt werden wird. +twofa_enrolled=Die Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Bewahre deinen einmalig verwendbaren Wiederherstellungsschlüssel (%s) an einem sicheren Ort auf, da er nicht wieder angezeigt werden wird. twofa_failed_get_secret=Fehler beim Abrufen des Secrets. webauthn_desc=Sicherheitsschlüssel sind Geräte, die kryptografische Schlüssel beeinhalten. Diese können für die Zwei-Faktor-Authentifizierung verwendet werden. Der Sicherheitsschlüssel muss den Standard „WebAuthn“ unterstützen. @@ -947,7 +971,7 @@ visibility.limited_tooltip=Nur für authentifizierte Benutzer sichtbar visibility.private=Privat visibility.private_tooltip=Sichtbar nur für Mitglieder von Organisationen, denen du beigetreten bist user_block_success = Dieser Benutzer wurde erfolgreich blockiert. -twofa_recovery_tip = Falls du dein Gerät verlierst, wirst du in der Lage sein, einen einmalig verwendbaren Key zu benutzen, um den auf dein Konto wiederherzustellen. +twofa_recovery_tip = Falls du dein Gerät verlierst, wirst du in der Lage sein, einen einmalig verwendbaren Wiederherstellungsschlüssel zu benutzen, um den auf dein Konto wiederherzustellen. webauthn_alternative_tip = Du möchtest vielleicht eine zusätzliche Authentifizierungsmethode einrichten. blocked_users_none = Keine Benutzer blockiert. webauthn_key_loss_warning = Falls du deine Security-Keys verlierst, wirst du Zugang zu deinem Konto verlieren. @@ -970,7 +994,7 @@ visibility=Sichtbarkeit visibility_description=Nur der Besitzer oder Organisationsmitglieder mit entsprechender Berechtigung werden in der Lage sein, es zu sehen. visibility_helper=In privates Repository umwandeln visibility_helper_forced=Auf dieser Forgejo-Instanz können nur private Repositorys angelegt werden. -visibility_fork_helper=(Eine Änderung dieses Wertes wirkt sich auf alle Forks aus) +visibility_fork_helper=(Eine Änderung dieses Wertes wirkt sich auf die Sichtbarkeit aller Forks aus) clone_helper=Benötigst du Hilfe beim Klonen? Öffne die Hilfe. fork_repo=Repository forken fork_from=Fork von @@ -1121,7 +1145,7 @@ 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.github.description=Daten von github.com oder anderen GitHub-Instanzen migrieren. +migrate.github.description=Daten von github.com oder GitHub Enterprise Server migrieren. migrate.git.description=Ein Repository von einem beliebigen Git Service klonen. migrate.gitlab.description=Daten von gitlab.com oder anderen GitLab-Instanzen migrieren. migrate.gitea.description=Daten von gitea.com oder anderen Gitea-/Forgejo-Instanzen migrieren. @@ -1395,7 +1419,7 @@ issues.new.no_milestone=Kein Meilenstein issues.new.clear_milestone=Meilenstein entfernen issues.new.open_milestone=Offene Meilensteine issues.new.closed_milestone=Geschlossene Meilensteine -issues.new.assignees=Zuständig +issues.new.assignees=Zuständige issues.new.clear_assignees=Zuständige entfernen issues.new.no_assignees=Niemand zuständig issues.new.no_reviewers=Keine Reviewer @@ -1412,7 +1436,7 @@ issues.new_label=Neues Label issues.new_label_placeholder=Labelname issues.new_label_desc_placeholder=Beschreibung issues.create_label=Label erstellen -issues.label_templates.title=Eine vordefinierte Label-Sammung laden +issues.label_templates.title=Eine Label-Sammlung laden issues.label_templates.info=Es existieren noch keine Labels. Erstelle ein neues Label („Neues Label“) oder verwende die Standard-Label-Sammlung: issues.label_templates.helper=Wähle eine Label-Sammlung issues.label_templates.use=Label-Sammlung verwenden @@ -1422,18 +1446,18 @@ issues.add_labels=hat die Labels %s %s hinzugefügt issues.remove_label=hat das Label %s %s entfernt issues.remove_labels=hat die Labels %s %s entfernt issues.add_remove_labels=hat %s hinzugefügt, und %s %s entfernt -issues.add_milestone_at=`hat diesen Issue %[2]s zum %[1]s Meilenstein hinzugefügt` -issues.add_project_at=`hat dieses zum %s projekt %s hinzugefügt` +issues.add_milestone_at=`hat dieses Issue %[2]s zum Meilenstein %[1]s hinzugefügt` +issues.add_project_at=`hat dies zum Projekt %s %s hinzugefügt` issues.change_milestone_at=`hat den Meilenstein %[3]s von %[1]s zu %[2]s geändert` issues.change_project_at=`hat das Projekt %[3]s von %[1]s zu %[2]s geändert` issues.remove_milestone_at=`hat dieses Issue %[2]s vom %[1]s Meilenstein entfernt` -issues.remove_project_at=`hat dieses vom %s Projekt %s entfernt` +issues.remove_project_at=`hat dies vom Projekt %s %s entfernt` issues.deleted_milestone=`(gelöscht)` issues.deleted_project=`(gelöscht)` issues.self_assign_at=`hat sich das Issue %s selbst zugewiesen` issues.add_assignee_at=`wurde von %s %s zugewiesen` -issues.remove_assignee_at=`wurde von %s %s nicht zugewiesen` -issues.remove_self_assignment=`hat seine Zuweisung %s entfernt` +issues.remove_assignee_at=`wurde von %s von der Zuweisung %s befreit` +issues.remove_self_assignment=`hat die Selbstzuweisung %s entfernt` issues.change_title_at=`hat den Titel von %s zu %s %s geändert` issues.change_ref_at=`hat die Referenz von %s zu %s %s geändert` issues.remove_ref_at=`hat die Referenz %s entfernt %s` @@ -1462,7 +1486,7 @@ issues.filter_type.assigned_to_you=Dir zugewiesen issues.filter_type.created_by_you=Von dir erstellt issues.filter_type.mentioning_you=Hat dich erwähnt issues.filter_type.review_requested=Review angefordert -issues.filter_type.reviewed_by_you=Bewertet von dir +issues.filter_type.reviewed_by_you=Von dir gereviewt issues.filter_sort=Sortieren issues.filter_sort.latest=Neueste issues.filter_sort.oldest=Älteste @@ -1508,14 +1532,14 @@ issues.context.edit=Bearbeiten issues.context.delete=Löschen issues.no_content=Keine Beschreibung angegeben. issues.close=Issue schließen -issues.comment_pull_merged_at=hat Commit %[1]s in %[2]s %[3]s gemerged +issues.comment_pull_merged_at=hat Commit %[1]s in %[2]s %[3]s gemergt issues.comment_manually_pull_merged_at=hat Commit %[1]s in %[2]s %[3]s manuell gemergt issues.close_comment_issue=Kommentieren und schließen issues.reopen_issue=Wieder öffnen issues.reopen_comment_issue=Kommentieren und wieder öffnen issues.create_comment=Kommentieren issues.closed_at=`hat diesen Issue %[2]s geschlossen` -issues.reopened_at=`hat diesen Issue %[2]s wieder geöffnet` +issues.reopened_at=`hat dieses Issue %[2]s wieder geöffnet` issues.commit_ref_at=`hat dieses Issue %[2]s aus einem Commit referenziert` issues.ref_issue_from=`hat %[2]s auf dieses Issue verwiesen %[4]s` issues.ref_pull_from=`hat %[2]s auf diesen Pull-Request verwiesen %[4]s` @@ -1574,15 +1598,15 @@ issues.subscribe=Abonnieren issues.unsubscribe=Abbestellen issues.unpin_issue=Issue abheften issues.max_pinned=Du kannst keine weiteren Issues anheften -issues.pin_comment=hat das %s angeheftet +issues.pin_comment=hat dies %s angeheftet issues.unpin_comment=hat das %s abgeheftet issues.lock=Diskussion sperren issues.unlock=Diskussion entsperren issues.lock.unknown_reason=Es ist nicht möglich, einen Issue mit unbekanntem Grund zu sperren. issues.lock_duplicate=Eine Diskussion kann nicht mehrfach gesperrt werden. issues.unlock_error=Es ist nicht möglich, einen nicht gesperrten Issue zu entsperren. -issues.lock_with_reason=gesperrt als %s und Diskussion auf Mitarbeiter beschränkt %s -issues.lock_no_reason=gesperrt und Diskussion auf Mitarbeiter beschränkt %s +issues.lock_with_reason=sperrte dies %s mit der Begründung „%s“ und schränkte die Diskussion auf Mitarbeiter ein +issues.lock_no_reason=sperrte dies %s und schränkte die Diskussion auf Mitarbeiter ein issues.unlock_comment=hat diese Diskussion %s entsperrt issues.lock_confirm=Sperren issues.unlock_confirm=Entsperren @@ -1612,13 +1636,13 @@ issues.add_time=Zeit manuell hinzufügen issues.del_time=Diese Zeiterfassung löschen issues.add_time_short=Zeit hinzufügen issues.add_time_cancel=Abbrechen -issues.add_time_history=`hat %s gearbeitete Zeit hinzugefügt` -issues.del_time_history=`hat %s gearbeitete Zeit gelöscht` +issues.add_time_history=`hat %s den Zeitaufwand hinzugefügt` +issues.del_time_history=`hat %s den Zeitaufwand gelöscht` issues.add_time_hours=Stunden issues.add_time_minutes=Minuten issues.add_time_sum_to_small=Es wurde keine Zeit eingegeben. issues.time_spent_total=Zeitaufwand insgesamt -issues.time_spent_from_all_authors=`Aufgewendete Zeit: %s` +issues.time_spent_from_all_authors=`Gesamtzeitaufwand: %s` issues.due_date=Fällig am issues.invalid_due_date_format=Das Fälligkeitsdatum muss das Format „JJJJ-MM-TT“ haben. issues.error_modifying_due_date=Fehler beim Ändern des Fälligkeitsdatums. @@ -1634,20 +1658,20 @@ issues.due_date_form_remove=Entfernen issues.due_date_not_writer=Du musst Schreibrechte für dieses Repository haben, um das Fälligkeitsdatum zu ändern. issues.due_date_not_set=Kein Fälligkeitsdatum gesetzt. issues.due_date_added=hat %[2]s das Fälligkeitsdatum %[1]s hinzugefügt -issues.due_date_modified=ändert das Abgabedatum von %[2]s auf %[1]s %[3]s s +issues.due_date_modified=hat das Fälligkeitsdatum von %[2]s auf %[1]s %[3]s geändert issues.due_date_remove=hat %[2]s das Fälligkeitsdatum %[1]s entfernt issues.due_date_overdue=Überfällig issues.due_date_invalid=Das Fälligkeitsdatum ist ungültig oder außerhalb des zulässigen Bereichs. Bitte verwende das Format „jjjj-mm-tt“. issues.dependency.title=Abhängigkeiten issues.dependency.issue_no_dependencies=Keine Abhängigkeiten gesetzt. issues.dependency.pr_no_dependencies=Keine Abhängigkeiten gesetzt. -issues.dependency.no_permission_1=Du bist nicht berechtigt %d Abhängigkeit zu lesen -issues.dependency.no_permission_n=Du bist nicht berechtigt %d Abhängigkeiten zu lesen -issues.dependency.no_permission.can_remove=Du hast keine Berechtigung diese Abhängigkeit zu lesen, kannst diese Abhängigkeit aber entfernen +issues.dependency.no_permission_1=Du bist nicht berechtigt, %d Abhängigkeit zu lesen +issues.dependency.no_permission_n=Du bist nicht berechtigt, %d Abhängigkeiten zu lesen +issues.dependency.no_permission.can_remove=Du hast keine Berechtigung, diese Abhängigkeit zu lesen, kannst diese Abhängigkeit aber entfernen issues.dependency.add=Abhängigkeit hinzufügen … issues.dependency.cancel=Abbrechen issues.dependency.remove=Entfernen -issues.dependency.remove_info=Abhängigkeit löschen +issues.dependency.remove_info=Diese Abhängigkeit löschen issues.dependency.added_dependency=`hat eine neue Abhängigkeit %s hinzugefügt` issues.dependency.removed_dependency=`hat eine Abhängigkeit %s entfernt` issues.dependency.pr_closing_blockedby=Das Schließen dieses Pull-Requests wird von den folgenden Issues blockiert @@ -1672,7 +1696,7 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen. issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen. issues.review.approve=hat die Änderungen %s genehmigt -issues.review.comment=hat %s überprüft +issues.review.comment=hat %s gereviewt issues.review.dismissed=verwarf %ss Review %s issues.review.dismissed_label=Verworfen issues.review.left_comment=hat einen Kommentar hinterlassen @@ -1692,12 +1716,12 @@ issues.review.option.show_outdated_comments=Veraltete Kommentare anzeigen issues.review.option.hide_outdated_comments=Veraltete Kommentare ausblenden issues.review.show_outdated=Veraltete anzeigen issues.review.hide_outdated=Veraltete ausblenden -issues.review.show_resolved=Gelöste anzeigen -issues.review.hide_resolved=Gelöste ausblenden +issues.review.show_resolved=Erledigte anzeigen +issues.review.hide_resolved=Erledigte ausblenden issues.review.resolve_conversation=Diskussion als „erledigt“ markieren issues.review.un_resolve_conversation=Diskussion als „nicht erledigt“ markieren issues.review.resolved_by=markierte diese Unterhaltung als gelöst -issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden. +issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Zuständigen hinzugefügt werden. issues.reference_issue.body=Beschreibung issues.content_history.deleted=gelöscht issues.content_history.edited=bearbeitet @@ -1740,8 +1764,8 @@ pulls.nothing_to_compare=Diese Branches sind identisch. Es muss kein Pull-Reques pulls.nothing_to_compare_and_allow_empty_pr=Diese Branches sind gleich. Der Pull-Request wird leer sein. pulls.has_pull_request=`Es existiert bereits ein Pull-Request zwischen diesen beiden Branches: %[2]s#%[3]d` pulls.create=Pull-Request erstellen -pulls.title_desc=möchte %[1]d Commits von %[2]s nach %[3]s mergen -pulls.merged_title_desc=hat %[1]d Commits von %[2]s nach %[3]s %[4]s zusammengeführt +pulls.title_desc_few=möchte %[1]d Commits von %[2]s nach %[3]s mergen +pulls.merged_title_desc_few=hat %[1]d Commits von %[2]s nach %[3]s %[4]s zusammengeführt pulls.change_target_branch_at=`hat den Zielbranch von %s nach %s %s geändert` pulls.tab_conversation=Diskussion pulls.tab_commits=Commits @@ -1778,8 +1802,8 @@ pulls.cannot_auto_merge_desc=Dieser Pull-Request kann nicht automatisch gemergt pulls.cannot_auto_merge_helper=Bitte manuell mergen, um die Konflikte zu beheben. pulls.num_conflicting_files_1=%d Datei mit Konflikten pulls.num_conflicting_files_n=%d Dateien mit Konflikten -pulls.approve_count_1=%d Zustimmung -pulls.approve_count_n=%d Zustimmungen +pulls.approve_count_1=%d Genehmigung +pulls.approve_count_n=%d Genehmigungen pulls.reject_count_1=%d Änderungsanfrage pulls.reject_count_n=%d Änderungsanfragen pulls.waiting_count_1=%d wartendes Review @@ -1808,9 +1832,9 @@ pulls.unrelated_histories=Merge fehlgeschlagen: Der Head des Merges und die Basi pulls.merge_out_of_date=Merge fehlgeschlagen: Während des Mergens wurde die Basis aktualisiert. Hinweis: Versuche es erneut. pulls.head_out_of_date=Mergen fehlgeschlagen: Der Head wurde aktualisiert während der Merge erstellt wurde. Tipp: Versuche es erneut. pulls.has_merged=Fehler: Der Pull-Request wurde gemergt, du kannst den Zielbranch nicht wieder mergen oder ändern. -pulls.push_rejected=Mergen fehlgeschlagen: Der Push wurde abgelehnt. Überprüfe die Git Hooks für dieses Repository. +pulls.push_rejected=Pushen fehlgeschlagen: Der Push wurde abgelehnt. Überprüfe die Git-Hooks für dieses Repository. pulls.push_rejected_summary=Vollständige Ablehnungsmeldung -pulls.push_rejected_no_message=Mergen fehlgeschlagen: Der Push wurde abgelehnt, aber es gab keine Fehlermeldung.
      Überprüfe die Git Hooks für dieses Repository +pulls.push_rejected_no_message=Pushen fehlgeschlagen: Der Push wurde abgelehnt, aber es gab keine Fehlermeldung. Überprüfe die Git-Hooks für dieses Repository pulls.open_unmerged_pull_exists=`Du kannst diesen Pull-Request nicht erneut öffnen, da noch ein anderer (#%d) mit identischen Eigenschaften offen ist.` pulls.status_checking=Einige Prüfungen sind noch ausstehend pulls.status_checks_success=Alle Prüfungen waren erfolgreich @@ -2084,10 +2108,10 @@ settings.admin_settings=Administratoreinstellungen settings.admin_enable_health_check=Repository-Health-Checks aktivieren (git fsck) settings.admin_code_indexer=Code-Indexer settings.admin_stats_indexer=Code-Statistik-Indexer -settings.admin_indexer_commit_sha=Zuletzt indexierter SHA +settings.admin_indexer_commit_sha=Zuletzt indizierter SHA settings.admin_indexer_unindexed=Unindiziert -settings.reindex_button=Zur Warteschlange für erneutes Indexieren hinzufügen -settings.reindex_requested=Erneutes Indexieren angefordert +settings.reindex_button=Zur Warteschlange für erneutes Indizieren hinzufügen +settings.reindex_requested=Erneutes Indizieren angefordert settings.admin_enable_close_issues_via_commit_in_any_branch=Einen Issue mit einem Commit auf einem Nicht-Standard-Branch schließen settings.danger_zone=Gefahrenzone settings.new_owner_has_same_repo=Der neue Besitzer hat bereits ein Repository mit dem gleichen Namen. Bitte wähle einen anderen Namen. @@ -2171,7 +2195,7 @@ settings.hooks_desc=Webhooks senden bei bestimmten Forgejo-Events automatisch settings.webhook_deletion=Webhook löschen settings.webhook_deletion_desc=Das Entfernen eines Webhooks löscht seine Einstellungen und Zustellungsverlauf. Fortfahren? settings.webhook_deletion_success=Webhook wurde entfernt. -settings.webhook.test_delivery=Senden testen +settings.webhook.test_delivery=Zustellung testen settings.webhook.test_delivery_desc=Teste diesen Webhook mit einem Fake-Event. settings.webhook.test_delivery_desc_disabled=Um diesen Webhook mit einem Fake-Event zu testen, aktiviere ihn. settings.webhook.request=Anfrage @@ -2181,7 +2205,7 @@ settings.webhook.payload=Inhalt settings.webhook.body=Inhalt settings.webhook.replay.description=Diesen Webhook wiederholen. settings.webhook.replay.description_disabled=Um diesen Webhook wiederzugeben, aktiviere ihn. -settings.webhook.delivery.success=Ein Event wurde zur Sendungs-Warteschlange hinzugefügt. Es kann ein paar Sekunden dauern, bevor es im Verlauf erscheint. +settings.webhook.delivery.success=Ein Event wurde zur Zustellungswarteschlange hinzugefügt. Es kann ein paar Sekunden dauern, bevor es im Zustellungsverlauf erscheint. settings.githooks_desc=Git-Hooks werden von Git selbst bereitgestellt. Du kannst die Dateien der unterstützten Hooks in der Liste unten bearbeiten, um eigene Operationen einzubinden. settings.githook_edit_desc=Wenn ein Hook nicht aktiv ist, wird der Standardinhalt benutzt. Lasse den Inhalt leer, um den Hook zu deaktivieren. settings.githook_name=Hook-Name @@ -2234,8 +2258,8 @@ settings.event_pull_request_assign=Pull-Request zugewiesen settings.event_pull_request_assign_desc=Pull-Request zugewiesen oder Zuweisung entfernt. settings.event_pull_request_label=Pull-Request mit Label versehen settings.event_pull_request_label_desc=Pull-Request-Labels aktualisiert oder geleert. -settings.event_pull_request_milestone=Pull-Request zu Milestone hinzugefügt -settings.event_pull_request_milestone_desc=Pull-Request zu Milestone hinzugefügt oder entfernt. +settings.event_pull_request_milestone=Pull-Request zum Meilenstein hinzugefügt +settings.event_pull_request_milestone_desc=Pull-Request zum Meilenstein hinzugefügt oder entfernt. settings.event_pull_request_comment=Pull-Request-Kommentar settings.event_pull_request_comment_desc=Pull-Request-Kommentar angelegt, geändert oder gelöscht. settings.event_pull_request_review=Pull-Request überprüft @@ -2244,7 +2268,7 @@ settings.event_pull_request_sync=Pull-Request synchronisiert settings.event_pull_request_sync_desc=Pull-Request synchronisiert. settings.event_pull_request_review_request=Überprüfung des Pull-Requests angefragt settings.event_pull_request_review_request_desc=Überprüfung des Pull-Requests angefragt oder die Anfrage entfernt. -settings.event_pull_request_approvals=Zustimmungen zum Pull-Request +settings.event_pull_request_approvals=Genehmigungen zum Pull-Request settings.event_pull_request_merge=Pull-Request-Merge settings.event_package=Paket settings.event_package_desc=Paket wurde in einem Repository erstellt oder gelöscht. @@ -2282,7 +2306,7 @@ settings.packagist_username=Benutzername für Packagist settings.packagist_api_token=API-Token settings.packagist_package_url=Paket-URL settings.deploy_keys=Deploy-Keys -settings.add_deploy_key=Deploy-Schlüssel hinzufügen +settings.add_deploy_key=Deploy-Key hinzufügen settings.deploy_key_desc=Deploy-Keys haben nur Lesezugriff auf das Repository. settings.is_writable=Erlaube Schreibzugriff settings.is_writable_info=Erlaube diesem Deploy-Key auf das Repository zu pushen. @@ -2311,9 +2335,9 @@ settings.protect_enable_push=Push aktivieren settings.protect_enable_push_desc=Jeder, der Schreibzugriff hat, darf in diesen Branch pushen (aber kein Force-Push). settings.protect_enable_merge=Merge aktivieren settings.protect_enable_merge_desc=Jeder mit Schreibzugriff darf die Pull-Requests in diesen Branch mergen. -settings.protect_whitelist_committers=Schütze gewhitelistete Commiter +settings.protect_whitelist_committers=Whitelist-eingeschränkter Push settings.protect_whitelist_committers_desc=Jeder, der auf der Whitelist steht, darf in diesen Branch pushen (aber kein Force-Push). -settings.protect_whitelist_deploy_keys=Deploy-Schlüssel mit Schreibzugriff zum Pushen whitelisten. +settings.protect_whitelist_deploy_keys=Deploy-Key mit Schreibzugriff zum Pushen whitelisten. settings.protect_whitelist_users=Nutzer, die pushen dürfen: settings.protect_whitelist_search_users=Benutzer suchen … settings.protect_whitelist_teams=Teams, die pushen dürfen: @@ -2322,7 +2346,7 @@ settings.protect_merge_whitelist_committers=Merge-Whitelist aktivieren settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist, Pull-Requests in diesen Branch zu mergen. settings.protect_merge_whitelist_users=Nutzer, die mergen dürfen: settings.protect_merge_whitelist_teams=Teams, die mergen dürfen: -settings.protect_check_status_contexts=Statusprüfungen aktivieren +settings.protect_check_status_contexts=Statusprüfung aktivieren settings.protect_status_check_patterns=Statuscheck-Muster: settings.protect_status_check_patterns_desc=Gib Muster ein, um festzulegen, welche Statusüberprüfungen durchgeführt werden müssen, bevor Branches in einen Branch, der dieser Regel entspricht, gemergt werden können. Jede Zeile gibt ein Muster an. Muster dürfen nicht leer sein. settings.protect_check_status_contexts_desc=Vor dem Mergen müssen Statusprüfungen bestanden werden. Wähle aus, welche Statusprüfungen erfolgreich durchgeführt werden müssen, bevor Branches in einen anderen gemergt werden können, der dieser Regel entspricht. Wenn aktiviert, müssen Commits zuerst auf einen anderen Branch gepusht werden, dann nach bestandener Statusprüfung gemergt oder direkt auf einen Branch gepusht werden, der dieser Regel entspricht. Wenn kein Kontext ausgewählt ist, muss der letzte Commit unabhängig vom Kontext erfolgreich sein. @@ -2330,12 +2354,12 @@ settings.protect_check_status_contexts_list=Statusprüfungen, die in der letzten settings.protect_status_check_matched=Übereinstimmung settings.protect_invalid_status_check_pattern=Ungültiges Statusprüfungspattern: „%s“. settings.protect_no_valid_status_check_patterns=Keine gültigen Statuscheck-Muster. -settings.protect_required_approvals=Erforderliche Zustimmungen: +settings.protect_required_approvals=Erforderliche Genehmigungen: settings.protect_required_approvals_desc=Erlaube das Mergen des Pull-Requests nur mit genügend positiven Reviews. -settings.protect_approvals_whitelist_enabled=Freigaben auf Benutzer oder Teams auf der Whitelist beschränken -settings.protect_approvals_whitelist_enabled_desc=Nur Bewertungen von Benutzern auf der Whitelist oder Teams zählen zu den erforderlichen Genehmigungen. Gibt es keine Whitelist, so zählen Reviews von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. -settings.protect_approvals_whitelist_users=Freigeschaltete Reviewer: -settings.protect_approvals_whitelist_teams=Freigeschaltete Teams: +settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Whitelist beschränken +settings.protect_approvals_whitelist_enabled_desc=Nur Reviews von Benutzern auf der Whitelist oder Teams zählen zu den erforderlichen Genehmigungen. Gibt es keine Whitelist, so zählen Reviews von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. +settings.protect_approvals_whitelist_users=Reviewer auf der Whitelist: +settings.protect_approvals_whitelist_teams=Für Reviews gewhitelistete Teams: settings.dismiss_stale_approvals=Entferne alte Genehmigungen settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt. settings.require_signed_commits=Signierte Commits erforderlich @@ -2351,12 +2375,12 @@ settings.delete_protected_branch=Schutz deaktivieren settings.update_protect_branch_success=Branchschutzregel „%s“ wurde aktualisiert. settings.remove_protected_branch_success=Branchschutzregel „%s“ wurde entfernt. settings.remove_protected_branch_failed=Entfernen der Branchschutzregel „%s“ fehlgeschlagen. -settings.protected_branch_deletion=Branch-Schutz deaktivieren +settings.protected_branch_deletion=Branch-Schutz löschen settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren? settings.block_rejected_reviews=Merge bei abgelehnten Reviews blockieren -settings.block_rejected_reviews_desc=Mergen ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn es genügend Zustimmungen gibt. +settings.block_rejected_reviews_desc=Mergen ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn es genügend Genehmigungen gibt. settings.block_on_official_review_requests=Mergen bei offiziellen Review-Anfragen blockieren -settings.block_on_official_review_requests_desc=Mergen ist nicht möglich wenn offizielle Review-Anfrangen vorliegen, selbst wenn es genügend Zustimmungen gibt. +settings.block_on_official_review_requests_desc=Mergen ist nicht möglich, wenn offizielle Review-Anfrangen vorliegen, selbst wenn es genügend Genehmigungen gibt. settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist settings.block_outdated_branch_desc=Mergen ist nicht möglich, wenn der Head-Branch hinter dem Basis-Branch ist. settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits: @@ -2367,7 +2391,7 @@ settings.no_protected_branch=Es gibt keine geschützten Branches. settings.edit_protected_branch=Bearbeiten settings.protected_branch_required_rule_name=Regelname erforderlich settings.protected_branch_duplicate_rule_name=Es existiert bereits eine Regel für dieses Branch-Set -settings.protected_branch_required_approvals_min=Die Anzahl der erforderlichen Zustimmungen darf nicht negativ sein. +settings.protected_branch_required_approvals_min=Die Anzahl der erforderlichen Genehmigungen darf nicht negativ sein. settings.tags=Tags settings.tags.protection=Tag-Schutz settings.tags.protection.pattern=Tag-Muster @@ -2375,7 +2399,7 @@ settings.tags.protection.allowed=Erlaubt settings.tags.protection.allowed.users=Erlaubte Benutzer settings.tags.protection.allowed.teams=Erlaubte Teams settings.tags.protection.allowed.noone=Niemand -settings.tags.protection.create=Tag schützen +settings.tags.protection.create=Regel hinzufügen settings.tags.protection.none=Es gibt keine geschützten Tags. settings.tags.protection.pattern.description=Du kannst einen einzigen Namen oder ein globales Schema oder einen regulären Ausdruck verwenden, um mehrere Tags zu schützen. Mehr dazu im Guide für geschützte Tags (Englisch). settings.bot_token=Bot-Token @@ -2386,7 +2410,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 vom Dashboard versteckt. Niemand (nicht einmal du!) wird in der Lage sein, neue Commits zu erstellen oder Issues oder Pull-Requests zu öffnen. +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. @@ -2434,11 +2458,11 @@ diff.browse_source=Quellcode durchsuchen diff.parent=Ursprung diff.commit=Commit diff.git-notes=Hinweise -diff.data_not_available=Keine Diff-Daten verfügbar +diff.data_not_available=Kein Diff-Inhalt verfügbar diff.options_button=Diff-Optionen diff.show_diff_stats=Statistiken anzeigen diff.download_patch=Patch-Datei herunterladen -diff.download_diff=Vergleichs-Datei herunterladen +diff.download_diff=Diff-Datei herunterladen diff.show_split_view=Geteilte Ansicht diff.show_unified_view=Gesamtansicht diff.whitespace_button=Leerzeichen @@ -2462,7 +2486,7 @@ diff.too_many_files=Einige Dateien werden nicht angezeigt, da zu viele Dateien i diff.show_more=Mehr anzeigen diff.load=Diff laden diff.generated=generiert -diff.vendored=vendored +diff.vendored=gevendort diff.comment.add_line_comment=Einzelnen Kommentar hinzufügen diff.comment.placeholder=Kommentieren diff.comment.markdown_info=Styling mit Markdown wird unterstützt. @@ -2470,13 +2494,13 @@ diff.comment.add_single_comment=Einzelnen Kommentar hinzufügen diff.comment.add_review_comment=Kommentar hinzufügen diff.comment.start_review=Review starten diff.comment.reply=Antworten -diff.review=Überprüfen +diff.review=Reviewen diff.review.header=Review einreichen diff.review.placeholder=Kommentar zum Review diff.review.comment=Kommentieren diff.review.approve=Genehmigen diff.review.self_reject=Pull-Request-Autoren können keine Änderungen an ihren eigenen Pull-Request anfordern -diff.review.reject=Änderung anfragen +diff.review.reject=Änderungen anfragen diff.review.self_approve=Pull-Request-Autoren können ihren eigenen Pull-Request nicht genehmigen diff.committed_by=committet von diff.protected=Geschützt @@ -2655,6 +2679,7 @@ pulls.agit_explanation = Mittels AGit-Workflow erstellt. AGit erlaubt Mitwirkend activity.navbar.recent_commits = Neueste Commits activity.navbar.code_frequency = Code-Frequenz file_follow = Symlink folgen +error.broken_git_hook = Die Git-Hooks des Repositorys scheinen kaputt zu sein. Bitte folge der Dokumentation um sie zu reparieren, dann pushe einige Commits um den Status zu aktualisieren. [graphs] @@ -2700,7 +2725,7 @@ settings.visibility.limited_shortname=Begrenzt settings.visibility.private=Privat (nur für Organisationsmitglieder sichtbar) settings.visibility.private_shortname=Privat -settings.update_settings=Einstellungen speichern +settings.update_settings=Einstellungen aktualisieren settings.update_setting_success=Organisationseinstellungen wurden aktualisiert. settings.change_orgname_prompt=Hinweis: Das Ändern des Organisationsnamens wird auch die URL deiner Organisation ändern und den alten Namen freigeben. settings.change_orgname_redirect_prompt=Der alte Name wird weiterleiten, bis er wieder beansprucht wird. @@ -2784,8 +2809,8 @@ teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Tea follow_blocked_user = Du kannst dieser Organisation nicht folgen, weil diese Organisation dich blockiert hat. [admin] -dashboard=Dashboard -identity_access=Identität & Zugriff +dashboard=Übersicht +identity_access=Identität u. Zugriff users=Benutzerkonten organizations=Organisationen assets=Code-Assets @@ -2873,7 +2898,7 @@ dashboard.last_gc_time=Seit letztem GC-Zyklus dashboard.total_gc_time=Gesamte GC-Pause dashboard.total_gc_pause=Gesamte GC-Pause dashboard.last_gc_pause=Letzte GC-Pause -dashboard.gc_times=Anzahl GC +dashboard.gc_times=GC-Zeiten dashboard.delete_old_actions=Alle alten Aktionen aus der Datenbank löschen dashboard.delete_old_actions.started=Löschen aller alten Aktionen in der Datenbank gestartet. dashboard.update_checker=Update-Checker @@ -2886,7 +2911,7 @@ dashboard.start_schedule_tasks=Terminierte Aufgaben starten dashboard.sync_branch.started=Synchronisierung der Branches gestartet dashboard.rebuild_issue_indexer=Issue-Indexer neu bauen -users.user_manage_panel=Benutzerkontenverwaltung +users.user_manage_panel=Benutzerkonten verwalten users.new_account=Benutzerkonto erstellen users.name=Benutzername users.full_name=Vollständiger Name @@ -2916,10 +2941,10 @@ users.is_activated=Account ist aktiviert users.prohibit_login=Anmelden deaktivieren users.is_admin=Ist Administrator users.is_restricted=Ist eingeschränkt -users.allow_git_hook=Darf „Git Hooks“ erstellen +users.allow_git_hook=Kann Git-Hooks erstellen users.allow_git_hook_tooltip=Git-Hooks werden mit denselben Benutzer-Rechten ausgeführt, mit denen Forgejo läuft, und haben die gleiche Ebene von Host-Zugriff. Dadurch können Benutzer mit diesen speziellen Git-Hook-Rechten auf alle Forgejo-Repositorys sowie auf die von Forgejo verwendete Datenbank zugreifen und diese ändern. Auch das Erhalten von Administratorrechten für Forgejo ist möglich. -users.allow_import_local=Darf lokale Repositorys importieren -users.allow_create_organization=Darf Organisationen erstellen +users.allow_import_local=Kann lokale Repositorys importieren +users.allow_create_organization=Kann Organisationen erstellen users.update_profile=Benutzerkonto aktualisieren users.delete_account=Benutzerkonto löschen users.cannot_delete_self=Du kannst dich nicht selbst löschen @@ -2944,7 +2969,7 @@ 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-Mail-Verwaltung +emails.email_manage_panel=Benutzer-E-Mails verwalten emails.primary=Primär emails.activated=Aktiviert emails.filter_sort.email=E-Mail @@ -2956,13 +2981,13 @@ emails.not_updated=Fehler beim Aktualisieren der angeforderten E-Mail-Adresse: % emails.duplicate_active=Diese E-Mail-Adresse wird bereits von einem Nutzer verwendet. emails.change_email_header=E-Mail-Eigenschaften aktualisieren -orgs.org_manage_panel=Organisationsverwaltung +orgs.org_manage_panel=Organisationen verwalten orgs.name=Name orgs.teams=Teams orgs.members=Mitglieder orgs.new_orga=Neue Organisation -repos.repo_manage_panel=Repositoryverwaltung +repos.repo_manage_panel=Repositorys verwalten repos.unadopted=Nicht übernommene Repositorys repos.unadopted.no_more=Keine weiteren nicht übernommenen Repositorys gefunden repos.owner=Besitzer @@ -2975,7 +3000,7 @@ repos.issues=Issues repos.size=Größe repos.lfs_size=LFS-Größe -packages.package_manage_panel=Paketverwaltung +packages.package_manage_panel=Pakete verwalten packages.total_size=Gesamtgröße: %s packages.unreferenced_size=Nicht referenzierte Größe: %s packages.cleanup=Veraltete Daten löschen @@ -3022,7 +3047,7 @@ auths.attribute_surname=Nachnamensattribut auths.attribute_mail=E-Mail-Attribut auths.attribute_ssh_public_key=Öffentlicher-SSH-Schlüssel-Attribut auths.attribute_avatar=Avatar-Attribut -auths.attributes_in_bind=Hole Attribute im Bind-Kontext +auths.attributes_in_bind=Hole Attribute im Nind-DN-Kontext auths.allow_deactivate_all=Erlaube ein leeres Suchergebnis, um alle Benutzer zu deaktivieren auths.use_paged_search=Seitensuche verwenden auths.search_page_size=Seitengröße @@ -3032,7 +3057,7 @@ auths.restricted_filter=Eingeschränkte Filter auths.restricted_filter_helper=Leer lassen, um keine Benutzer als eingeschränkt festzulegen. Verwende einen Asterisk („*“), um alle Benutzer, die nicht dem Admin-Filter entsprechen, als eingeschränkt zu setzen. auths.verify_group_membership=Gruppenmitgliedschaft in LDAP verifizieren (zum Überspringen leer lassen) auths.group_search_base=Gruppensuche Basisdomainname -auths.group_attribute_list_users=Gruppenattribut, welches die Benutzerliste enthält +auths.group_attribute_list_users=Gruppenattribut, welches Benutzerliste enthält auths.user_attribute_in_group=Benutzerattribut in der Gruppenliste auths.map_group_to_team=Ordne LDAP-Gruppen Organisationsteams zu (zum Überspringen leer lassen) auths.map_group_to_team_removal=Benutzer aus synchronisierten Teams entfernen, wenn der Benutzer nicht zur entsprechenden LDAP-Gruppe gehört @@ -3050,7 +3075,7 @@ auths.helo_hostname=HELO-Hostname auths.helo_hostname_helper=Mit HELO gesendeter Hostname. Leer lassen, um den aktuellen Hostnamen zu senden. auths.disable_helo=HELO deaktivieren auths.pam_service_name=PAM-Dienstname -auths.pam_email_domain=PAM E-Mail-Domain (optional) +auths.pam_email_domain=PAM-E-Mail-Domain (optional) auths.oauth2_provider=OAuth2-Anbieter auths.oauth2_icon_url=Symbol-URL auths.oauth2_clientID=Client-ID (Schlüssel) @@ -3118,10 +3143,10 @@ auths.unable_to_initialize_openid=OpenID Connect Provider konnte nicht initialis auths.invalid_openIdConnectAutoDiscoveryURL=Ungültige Auto-Discovery-URL (dies muss eine gültige URL sein, die mit http:// oder https:// beginnt) config.server_config=Serverkonfiguration -config.app_name=Seitentitel +config.app_name=Instanztitel config.app_ver=Forgejo-Version -config.app_url=Forgejo-Basis-URL -config.custom_conf=Konfigurations-Datei-Pfad +config.app_url=Basis-URL +config.custom_conf=Konfigurationsdatei-Pfad config.custom_file_root_path=Benutzerdefinierter Root-Pfad config.domain=Server-Domain config.offline_mode=Lokaler Modus @@ -3150,8 +3175,8 @@ config.ssh_minimum_key_sizes=Mindestschlüssellängen config.lfs_config=LFS-Konfiguration config.lfs_enabled=Aktiviert -config.lfs_content_path=LFS Content-Pfad -config.lfs_http_auth_expiry=Ablauf der LFS HTTP Authentifizierung +config.lfs_content_path=LFS-Content-Pfad +config.lfs_http_auth_expiry=Ablauf der LFS-HTTP-Authentifizierung config.db_config=Datenbankkonfiguration config.db_type=Typ @@ -3174,7 +3199,7 @@ config.require_sign_in_view=Seiten nur für angemeldete Benutzer zugänglich config.mail_notify=E-Mail-Benachrichtigungen aktivieren config.enable_captcha=CAPTCHA aktivieren config.active_code_lives=Aktivierungscode-Lebensdauer -config.reset_password_code_lives=Kontowiederherstellungs-Code Ablaufzeit +config.reset_password_code_lives=Kontowiederherstellungscode-Ablaufzeit config.default_keep_email_private=E-Mail-Adressen standardmäßig verbergen config.default_allow_create_organization=Erstellen von Organisationen standardmäßig erlauben config.enable_timetracking=Zeiterfassung aktivieren @@ -3194,7 +3219,7 @@ config.mailer_enabled=Aktiviert config.mailer_enable_helo=HELO aktivieren config.mailer_name=Name config.mailer_protocol=Protokoll -config.mailer_smtp_addr=SMTP-Adresse +config.mailer_smtp_addr=SMTP-Host config.mailer_smtp_port=SMTP-Port config.mailer_user=Benutzer config.mailer_use_sendmail=Sendmail benutzen @@ -3215,7 +3240,7 @@ config.cache_config=Cache-Konfiguration config.cache_adapter=Cache-Adapter config.cache_interval=Cache-Intervall config.cache_conn=Cache-Anbindung -config.cache_item_ttl=Cache Item-TTL +config.cache_item_ttl=Cache-Item-TTL config.session_config=Session-Konfiguration config.session_provider=Session-Provider @@ -3239,14 +3264,14 @@ config.git_max_diff_files=Max. Diff-Dateien (Angezeigte) config.git_gc_args=GC-Argumente config.git_migrate_timeout=Zeitlimit für Migration config.git_mirror_timeout=Zeitlimit für Spiegel-Aktualisierung -config.git_clone_timeout=Zeitlimit für Clone +config.git_clone_timeout=Zeitlimit für Klon config.git_pull_timeout=Zeitlimit für Pull config.git_gc_timeout=Zeitlimit für GC config.log_config=Konfiguration des Loggings config.logger_name_fmt=Logger: %s config.disabled_logger=Deaktiviert -config.access_log_mode=Access-Log-Modus +config.access_log_mode=Zugriffslog-Modus config.access_log_template=Zugriffslog-Vorlage config.xorm_log_sql=SQL protokollieren @@ -3318,6 +3343,7 @@ self_check.database_fix_mssql = Für MSSQL-Benutzer: Du kannst das Problem im Mo self_check.no_problem_found = Noch kein Problem gefunden. self_check.database_inconsistent_collation_columns = Datenbank benutzt Collation %s, aber diese Spalten benutzen Collations, die nicht zusammenpassen. Das könnte ein paar unerwartete Probleme verursachen. self_check.database_collation_mismatch = Erwarte von Datenbank, folgende Collation zu verwenden: %s +auths.tips.gmail_settings = Gmail-Einstellungen: [action] @@ -3430,9 +3456,9 @@ dependencies=Abhängigkeiten keywords=Schlüsselwörter details=Details details.author=Autor -details.project_site=Projektseite -details.repository_site=Repository-Seite -details.documentation_site=Dokumentationsseite +details.project_site=Projektwebseite +details.repository_site=Repository-Webseite +details.documentation_site=Dokumentationswebseite details.license=Lizenz assets=Dateien versions=Versionen @@ -3483,9 +3509,9 @@ 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=Nimm Folgendes in den dependencies deiner pom.xml auf, um das Paket zu installieren: +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 Dependency herunterzuladen: +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 @@ -3494,7 +3520,7 @@ npm.install=Um das Paket mit npm zu installieren, führe den folgenden Befehl au 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.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: @@ -3562,6 +3588,7 @@ owner.settings.chef.keypair.description=Ein Schlüsselpaar ist notwendig, um mit rpm.repository = Repository-Info rpm.repository.multiple_groups = Dieses Paket ist in mehreren Gruppen verfügbar. rpm.repository.architectures = Architekturen +owner.settings.cargo.rebuild.no_index = Kann nicht erneut erzeugen, es wurde kein Index initialisiert. [secrets] secrets=Secrets @@ -3576,7 +3603,7 @@ deletion=Secret entfernen deletion.description=Das Entfernen eines Secrets kann nicht rückgängig gemacht werden. Fortfahren? deletion.success=Das Secret wurde entfernt. deletion.failed=Secret konnte nicht entfernt werden. -management=Secret-Verwaltung +management=Secrets verwalten [actions] actions=Actions @@ -3593,7 +3620,7 @@ status.skipped=Übersprungen status.blocked=Blockiert runners=Runner -runners.runner_manage_panel=Runner-Verwaltung +runners.runner_manage_panel=Runner verwalten runners.new=Neuen Runner erstellen runners.new_notice=Wie man einen Runner startet runners.status=Status @@ -3650,7 +3677,7 @@ 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=Variablenverwaltung +variables.management=Variablen verwalten variables.creation=Variable hinzufügen variables.none=Es gibt noch keine Variablen. variables.deletion=Variable entfernen diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 129ade8946..7d43c83cc3 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -17,34 +17,34 @@ page=Σελίδα template=Πρότυπο language=Γλώσσα notifications=Ειδοποιήσεις -active_stopwatch=Ενεργή Καταγραφή Χρόνου -tracked_time_summary=Περίληψη του χρόνου παρακολούθησης με βάση τα φίλτρα της λίστας ζητημάτων +active_stopwatch=Ενεργή καταγραφή χρόνου +tracked_time_summary=Περίληψη του χρόνου παρακολούθησης βάσει των φίλτρων της λίστας ζητημάτων create_new=Δημιουργία… user_profile_and_more=Προφίλ και ρυθμίσεις… signed_in_as=Συνδεδεμένος ως enable_javascript=Απαιτείται JavaScript για να εμφανιστεί αυτή η ιστοσελίδα. toc=Πίνακας Περιεχομένων -licenses=Άδειες +licenses=Άδειες Χρήσης return_to_gitea=Επιστροφή στο Forgejo -username=Όνομα Χρήστη -email=Διεύθυνση ηλεκτρονικού ταχυδρομείου (email) +username=Όνομα χρήστη +email=Διεύθυνση email password=Κωδικός πρόσβασης -access_token=Διακριτικό Πρόσβασης -re_type=Επιβεβαίωση Κωδικού Πρόσβασης +access_token=Διακριτικό πρόσβασης +re_type=Επιβεβαίωση κωδικού captcha=CAPTCHA -twofa=Έλεγχος Ταυτότητας Δύο Παραγόντων -twofa_scratch=Κωδικός Μίας Χρήσης Δύο Παραγόντων +twofa=Πιστοποίηση Δύο Παραγόντων +twofa_scratch=Κωδικός μίας χρήσης (για πιστοποίηση δύο παραγόντων / 2FA) passcode=Κωδικός webauthn_insert_key=Εισάγετε το κλειδί ασφαλείας σας -webauthn_sign_in=Πατήστε το κουμπί πάνω στο κλειδί ασφαλείας. Αν το κλειδί ασφαλείας σας δεν έχει κουμπί, αποσυνδέστε το και συνδέστε το ξανά. +webauthn_sign_in=Πατήστε το κουμπί στο κλειδί ασφαλείας σας. Αν το κλειδί ασφαλείας σας δεν έχει κουμπί, αποσυνδέστε το και συνδέστε το ξανά. webauthn_press_button=Παρακαλώ πατήστε το κουμπί στο κλειδί ασφαλείας… webauthn_use_twofa=Χρησιμοποιήστε έναν κωδικό δύο παραγόντων από το τηλέφωνό σας -webauthn_error=Αδύνατη η ανάγνωση του κλειδιού ασφαλείας. +webauthn_error=Δεν ήταν δυνατή η επικοινωνία με το κλειδί ασφαλείας σας. webauthn_unsupported_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει επί του παρόντος WebAuthn. webauthn_error_unknown=Παρουσιάστηκε ένα άγνωστο σφάλμα. Παρακαλώ προσπαθήστε ξανά. -webauthn_error_insecure=`Το WebAuthn υποστηρίζει μόνο ασφαλείς συνδέσεις. Για δοκιμές πάνω από HTTP, μπορείτε να χρησιμοποιήσετε την προέλευση "localhost" ή "127.0.0.1"` +webauthn_error_insecure=Το WebAuthn υποστηρίζει μόνο ασφαλές συνδέσεις. Για την διεξαγωγή δοκιμών μέσω HTTP, μπορείτε να χρησιμοποιήσετε την προέλευση «localhost» ή «127.0.0.1» webauthn_error_unable_to_process=Ο διακομιστής δεν μπόρεσε να επεξεργαστεί το αίτημά σας. webauthn_error_duplicated=Το κλειδί ασφαλείας δεν επιτρέπεται για αυτό το αίτημα. Βεβαιωθείτε ότι το κλειδί δεν έχει ήδη καταχωρηθεί. webauthn_error_empty=Πρέπει να ορίσετε ένα όνομα για αυτό το κλειδί. @@ -66,7 +66,7 @@ admin_panel=Διαχείριση account_settings=Ρυθμίσεις Λογαριασμού settings=Ρυθμίσεις your_profile=Προφίλ -your_starred=Με αστέρι +your_starred=Αγαπημένα your_settings=Ρυθμίσεις all=Όλα @@ -80,17 +80,17 @@ pull_requests=Pull Requests issues=Ζητήματα milestones=Ορόσημα -ok=OK +ok=Εντάξει cancel=Ακύρωση -retry=Επανάληψη +retry=Επαναπροσπάθεια rerun=Επανεκτέλεση rerun_all=Επανεκτέλεση όλων save=Αποθήκευση add=Προσθήκη add_all=Προσθήκη Όλων remove=Αφαίρεση -remove_all=Αφαίρεση Όλων -remove_label_str=`Αφαίρεση του αντικειμένου "%s"` +remove_all=Αφαίρεση όλων +remove_label_str=Αφαίρεση αντικειμένου «%s» edit=Επεξεργασία view=Προβολή @@ -135,12 +135,25 @@ concept_user_organization=Οργανισμός show_timestamps=Εμφάνιση χρονοσημάνσεων show_log_seconds=Εμφάνιση δευτερολέπτων show_full_screen=Εμφάνιση πλήρους οθόνης -download_logs=Λήψη καταγραφών +download_logs=Λήψη αρχείων καταγραφής -confirm_delete_selected=Επιβεβαιώνετε τη διαγραφή όλων των επιλεγμένων στοιχείων; +confirm_delete_selected=Είστε βέβαιοι πως θέλετε να διαγράψετε όλα τα επιλεγμένα στοιχεία; name=Όνομα value=Τιμή +toggle_menu = Μενού +confirm_delete_artifact = Είστε βέβαιοι πως θέλετε να διαγράψετε το προϊόν «%s»; +filter = Φίλτρο +filter.is_archived = Αρχειοθετημένο +filter.clear = Απενεργοποίηση φίλτρου +filter.not_archived = Μη αρχειοθετημένο +filter.is_template = Πρότυπο +filter.public = Δημόσιο +filter.private = Ιδιωτικό +filter.not_fork = Εξαίρεση Fork +filter.is_mirror = Είδωλα +filter.not_mirror = Εξαίρεση Ειδώλων +filter.not_template = Εξαίρεση Προτύπων [aria] navbar=Γραμμή Πλοήγησης @@ -150,7 +163,7 @@ footer.links=Συνδέσεις [heatmap] number_of_contributions_in_the_last_12_months=%s συνεισφορές τους τελευταίους 12 μήνες -no_contributions=Χωρίς συνεισφορές +contributions_zero=Χωρίς συνεισφορές less=Λιγότερα more=Περισσότερα @@ -176,34 +189,35 @@ string.desc=Z - A [error] occurred=Παρουσιάστηκε ένα σφάλμα -report_message=Αν πιστεύετε ότι αυτό είναι ένα πρόβλημα στο Gitea, παρακαλούμε αναζητήστε ζητήματα στο GitHub ή ανοίξτε ένα νέο ζήτημα εάν είναι απαραίτητο. +report_message=Αν πιστεύετε ότι αυτό προέκυψε λόγω κάποιου σφάλματος στο Forgejo, σας παρακαλούμε να αναζητήσετε τα ζητήματα στο Codeberg ή να ανοίξετε ένα νέο ζήτημα εάν είναι απαραίτητο. missing_csrf=Bad Request: δεν υπάρχει διακριτικό CSRF invalid_csrf=Λάθος Αίτημα: μη έγκυρο διακριτικό CSRF not_found=Ο προορισμός δεν βρέθηκε. network_error=Σφάλμα δικτύου +server_internal = Σφάλμα Διακομιστή [startpage] app_desc=Μια ανώδυνη, αυτο-φιλοξενούμενη υπηρεσία Git -install=Εύκολο στην εγκατάσταση -install_desc=Απλά εκτελέστε το αρχείο προγράμματος για την πλατφόρμα σας, χρήσιμοποιήστε το με το Docker, ή εγκαταστήστε το πακέτο. -platform=Πολυπλατφορμικό +install=Εύκολη εγκατάσταση +install_desc=Απλά τρέξε το αρχείο που αντιστοιχεί στην πλατφόρμα σου, εγκατέστησε το με το Docker ή χρησιμοποίησε ένα πακέτο λογισμικού. +platform=Τρέχει παντού platform_desc=Το Forgejo τρέχει σε όλα τα συστήματα που υποστηρίζει η γλώσσα Go, όπως: Windows, macOS, Linux, ARM, κλπ. Διάλεξε αυτό που αγαπάς! lightweight=Ελαφρύ -lightweight_desc=Forgejo έχει χαμηλές ελάχιστες απαιτήσεις και μπορεί να τρέξει σε ένα οικονομικό Raspberry Pi. Εξοικονομήστε ενέργεια! +lightweight_desc=Το Forgejo έχει ελάχιστες απαιτήσεις, μπορείς και να το τρέξεις σε ένα φτηνό Raspberry Pi. Εξοικονόμησε ενέργεια! license=Ανοικτού κώδικα -license_desc=Κατεβάστε το Forgejo! Ελάτε μαζί μας και συνεισφέρετε για να κάνετε αυτό το έργο ακόμα καλύτερο. Δεν είναι ντροπή να συνεισφέρετε! +license_desc=Κατέβασε το Forgejo! Επίσης, μπορείς να μας βοηθήσεις να το βελτιώσουμε με τις συνεισφορές σου. Χωρίς ντροπή! [install] install=Εγκατάσταση -title=Αρχικές Ρυθμίσεις -docker_helper=Αν εκτελέσετε το Forgejo μέσα στο Docker, παρακαλώ διαβάστε την τεκμηρίωση πριν αλλάξετε τις ρυθμίσεις. +title=Αρχικές ρυθμίσεις +docker_helper=Αν εκτελέσετε το Forgejo μέσα στο Docker, παρακαλώ διαβάστε το εγχειρίδιο πριν αλλάξετε τις ρυθμίσεις. require_db_desc=Το Forgejo απαιτεί MySQL, PostgreSQL, MSSQL, SQLite3 ή TiDB (με πρωτόκολλο MySQL). -db_title=Ρυθμίσεις Βάσης Δεδομένων -db_type=Τύπος της Βάσης Δεδομένων +db_title=Ρυθμίσεις βάσης δεδομένων +db_type=Είδος βάσης δεδομένων host=Διακομιστής user=Όνομα Χρήστη password=Συνθηματικό -db_name=Όνομα Βάσης Δεδομένων +db_name=Όνομα βάσης Δδδομένων db_schema=Σχήμα db_schema_helper=Αφήστε κενό για την προεπιλογή της βάσης δεδομένων ("public"). ssl_mode=SSL @@ -222,91 +236,95 @@ err_admin_name_is_reserved=Το Όνομα χρήστη του Διαχειρι err_admin_name_pattern_not_allowed=Το Όνομα χρήστη του Διαχειριστή δεν είναι έγκυρο, ταιριάζει σε μια δεσμευμένη μορφή err_admin_name_is_invalid=Το Όνομα Χρήστη του Διαχειριστή δεν είναι έγκυρο -general_title=Γενικές Ρυθμίσεις -app_name=Τίτλος Ιστοτόπου +general_title=Γενικές ρυθμίσεις +app_name=Όνομα διακομιστή app_name_helper=Μπορείτε να εισάγετε το όνομα της εταιρείας σας εδώ. -repo_path=Ριζική Διαδρομή Αποθετηρίου +repo_path=Τοποθεσία αρχείων αποθετηρίων repo_path_helper=Τα απομακρυσμένα αποθετήρια Git θα αποθηκεύονται σε αυτόν τον κατάλογο. -lfs_path=Ριζική Διαδρομή Git LFS +lfs_path=Τοποθεσία αρχείων Git LFS lfs_path_helper=Τα αρχεία που παρακολουθούνται από το Git LFS θα αποθηκεύονται σε αυτόν τον φάκελο. Αφήστε κενό για να το απενεργοποιήσετε. -run_user=Εκτέλεση Σαν Χρήστη -run_user_helper=Το όνομα του χρήστη του λειτουργικού συστήματος ο οποίος εκτελεί το Gitea. Επισημαίνεται ότι αυτός ο χρήστης πρέπει να έχει πρόσβαση στο ριζικό φάκελο του αποθετηρίου. -domain=Domain Διακομιστή +run_user=Εκτέλεση ως +run_user_helper=Το όνομα του χρήστη στο λειτουργικό σας σύστημα που εκτελεί το Forgejo. Επισημαίνεται ότι αυτός ο χρήστης πρέπει να έχει πρόσβαση στον φάκελο που περιέχει όλα τα αποθετηρία. +domain=Domain διακομιστή domain_helper=Όνομα domain διακομιστή ή η διεύθυνση του. -ssh_port=Θύρα της υπηρεσίας SSH -ssh_port_helper=Αριθμός θύρας που ακούει η υπηρεσία SSH. Αφήστε κενό για να το απενεργοποιήσετε. -http_port=Η HTTP θύρα που ακούει το Forgejo -http_port_helper=Αριθμός θύρας που θα ακούει η υπηρεσία web του Forgejo. -app_url=Βασικό URL του Forgejo +ssh_port=Θύρα υπηρεσίας SSH +ssh_port_helper=Αριθμός θύρας στην οποία θα «ακούει» η υπηρεσία SSH. Αφήστε το πεδίο κενό για να την απενεργοποιήσετε. +http_port=Θύρα υπηρεσίας HTTP +http_port_helper=Αριθμός θύρας στην οποία θα «ακούει» η web υπηρεσία του Forgejo. Αφήστε το πεδίο κενό για να την απενεργοποιήσετε. +app_url=Βασικό URL app_url_helper=Βασική Διεύθυνση για τα URL κλωνοποίησης μέσω HTTP(S) και για τις ειδοποιήσεις μέσω email. -log_root_path=Διαδρομή Αρχείων Καταγραφής +log_root_path=Τοποθεσία αρχείων καταγραφής log_root_path_helper=Τα αρχεία καταγραφής θα γράφονται σε αυτόν τον κατάλογο. -optional_title=Προαιρετικές Ρυθμίσεις -email_title=Ρυθμίσεις Email +optional_title=Προαιρετικές ρυθμίσεις +email_title=Ρυθμίσεις email smtp_addr=Διακομιστής SMTP smtp_port=Θύρα SMTP -smtp_from=Αποστολή Email Ως +smtp_from=Αποστολή email ως smtp_from_helper=Η διεύθυνση email που θα χρησιμοποιεί το Forgejo. Εισάγετε μια απλή διεύθυνση ηλεκτρονικού ταχυδρομείου ή χρησιμοποιήστε τη μορφή "Όνομα" . -mailer_user=Όνομα Χρήστη SMTP +mailer_user=Όνομα χρήστη SMTP mailer_password=Κωδικός SMTP -register_confirm=Απαιτείται Επιβεβαίωση της Διεύθυνσης Εmail για Εγγραφή -mail_notify=Ενεργοποίηση Ειδοποιήσεων με Email -server_service_title=Ρυθμίσεις Διακομιστή και Υπηρεσιών Τρίτων -offline_mode=Ενεργοποίηση Τοπικής Λειτουργίας +register_confirm=Να απαιτείται η επιβεβαίωση της διεύθυνσης email για την δημιουργία λογαριασμού +mail_notify=Ενεργοποίηση ειδοποιήσεων email +server_service_title=Ρυθμίσεις διακομιστή και υπηρεσιών τρίτων +offline_mode=Ενεργοποίηση τοπικής λειτουργίας offline_mode_popup=Απενεργοποιήση των δικτύων διανομής περιεχομένου τρίτων και σερβίρετε όλων των πόρων τοπικά. disable_gravatar=Απενεργοποίηση Gravatar disable_gravatar_popup=Απενεργοποιήση του Gravatar και των εξωτερικών πηγών avatar. Θα χρησιμοποιηθεί ένα προεπιλεγμένο avatar εκτός αν ένας χρήστης ανεβάσει τοπικά ένα avatar. -federated_avatar_lookup=Ενεργοποίηση Ομόσπονδων Avatars +federated_avatar_lookup=Ενεργοποίηση αποκεντρωμένων avatars federated_avatar_lookup_popup=Ενεργοποίηση ομόσπονδης αναζήτησης avatar χρησιμοποιώντας το Libravatar. -disable_registration=Απενεργοποίηση Αυτοεγγραφής +disable_registration=Απενεργοποίηση αυτοεγγραφής disable_registration_popup=Απενεργοποίηση αυτοεγγραφής χρήστη. Μόνο οι διαχειριστές θα μπορούν να δημιουργήσουν νέους λογαριασμούς χρηστών. -allow_only_external_registration_popup=Να Επιτρέπεται Η Εγγραφή Μόνο Μέσω Εξωτερικών Υπηρεσιών -openid_signin=Ενεργοποίηση Σύνδεσης μέσω OpenID +allow_only_external_registration_popup=Να επιτρέπεται η εγγραφή μόνο μέσω εξωτερικών υπηρεσιών +openid_signin=Ενεργοποίηση σύνδεσης μέσω OpenID openid_signin_popup=Ενεργοποίηση σύνδεσης χρήστη μέσω OpenID. -openid_signup=Ενεργοποίηση Ιδιοεγγραφής μέσω OpenID +openid_signup=Ενεργοποίηση εγγραφών μέσω OpenID openid_signup_popup=Ενεργοποίηση ιδιοεγγραφής χρηστών με βάση το OpenID. enable_captcha=Ενεργοποίηση CAPTCHA στην εγγραφή -enable_captcha_popup=Απαιτείται ένα CAPTCHA για τη ιδιοεγγραφή του χρήστη. -require_sign_in_view=Απαιτείται Είσοδος για τη Προβολή Σελίδων +enable_captcha_popup=Να απαιτείται CAPTCHA για την δημιουργία λογαριασμού. +require_sign_in_view=Να απαιτείται είσοδος για την προβολή περιεχομένων require_sign_in_view_popup=Περιορισμός της πρόσβασης σε συνδεδεμένους χρήστες. Οι επισκέπτες θα μπορούν μόνο να δουν τις σελίδες εισόδου και εγγραφής. admin_setting_desc=Η δημιουργία ενός λογαριασμού διαχειριστή είναι προαιρετική. Ο πρώτος εγγεγραμμένος χρήστης θα γίνει αυτόματα διαχειριστής. -admin_title=Ρυθμίσεις Λογαριασμού Διαχειριστή -admin_name=Όνομα Χρήστη Διαχειριστή +admin_title=Ρυθμίσεις λογαριασμού διαχειριστή +admin_name=Όνομα χρήστη διαχειριστή admin_password=Κωδικός Πρόσβασης -confirm_password=Επιβεβαίωση Κωδικού Πρόσβασης -admin_email=Διεύθυνση Email +confirm_password=Επιβεβαίωση κωδικού +admin_email=Διεύθυνση email install_btn_confirm=Εγκατάσταση Forgejo -test_git_failed=Αδυναμία δοκιμής της εντολής 'git': %v -sqlite3_not_available=Αυτή η έκδοση Forgejo δεν υποστηρίζει την SQLite3. Παρακαλώ κατεβάστε την επίσημη δυαδική έκδοση από το %s (όχι την έκδοση 'gobuild'). +test_git_failed=Αδυναμία δοκιμής της εντολής «git»: %v +sqlite3_not_available=Αυτή η έκδοση του Forgejo δεν υποστηρίζει την SQLite3. Παρακαλώ κατεβάστε την επίσημη έκδοση από το %s (όχι την έκδοση «gobuild»). invalid_db_setting=Οι ρυθμίσεις της βάσης δεδομένων δεν είναι έγκυρες: %v -invalid_db_table=Ο πίνακας βάσης δεδομένων "%s" δεν είναι έγκυρος: %v +invalid_db_table=Ο πίνακας βάσης δεδομένων «%s» δεν είναι έγκυρος: %v invalid_repo_path=Η αρχική διαδρομή των αποθετηρίων δεν είναι έγκυρη: %v invalid_app_data_path=Η διαδρομή δεδομένων εφαρμογής (app data) δεν είναι έγκυρη: %v -run_user_not_match=Το όνομα χρήστη 'εκτέλεση ως' δεν είναι το τρέχον όνομα χρήστη: %s -> %s +run_user_not_match=Το όνομα χρήστη «Εκτέλεση ως» δεν είναι το τρέχον όνομα χρήστη: %s -> %s internal_token_failed=Αποτυχία δημιουργίας εσωτερικού διακριτικού: %v secret_key_failed=Αποτυχία δημιουργίας μυστικού κλειδιού: %v save_config_failed=Αποτυχία αποθήκευσης ρυθμίσεων: %v invalid_admin_setting=Η ρύθμιση λογαριασμού διαχειριστή δεν είναι έγκυρη: %v -invalid_log_root_path=Η διαδρομή της καταγραφής δεν είναι έγκυρη: %v +invalid_log_root_path=Η τοποθεσία αρχείων καταγραφής δεν είναι έγκυρη: %v default_keep_email_private=Απόκρυψη διευθύνσεων email από προεπιλογή default_keep_email_private_popup=Απόκρυψη διευθύνσεων email των νέων λογαριασμών χρήστη σαν προεπιλογή. -default_allow_create_organization=Να επιτρέπεται η δημιουργία οργανισμών σαν προεπιλογή +default_allow_create_organization=Να επιτρέπεται η δημιουργία οργανισμών από προεπιλογή default_allow_create_organization_popup=Επιτρέψτε σε νέους λογαριασμούς χρηστών να δημιουργούν οργανισμούς σαν προεπιλογή. -default_enable_timetracking=Ενεργοποίηση Καταγραφής Χρόνου σαν Προεπιλογή +default_enable_timetracking=Ενεργοποίηση καταγραφής χρόνου από προεπιλογή default_enable_timetracking_popup=Ενεργοποίηση καταγραφής χρόνου για νέα αποθετήρια σαν προεπιλογή. -no_reply_address=Κρυφό Όνομα Τομέα Email -no_reply_address_helper=Όνομα τομέα για χρήστες με μια κρυφή διεύθυνση email. Για παράδειγμα, το όνομα χρήστη 'nikos' θα συνδεθεί στο Git ως 'nikos@noreply.example.org' αν ο κρυφός τομέας email έχει οριστεί ως 'noreply.example.org'. -password_algorithm=Αλγόριθμος Hash Κωδικού Πρόσβασης +no_reply_address=Domain κρυφών email +no_reply_address_helper=Όνομα τομέα (domain) για χρήστες με μια κρυφή διεύθυνση email. Για παράδειγμα, το όνομα χρήστη 'nikos' θα συνδεθεί στο Git ως 'nikos@noreply.example.org' αν ο κρυφός τομέας email έχει οριστεί ως 'noreply.example.org'. +password_algorithm=Αλγόριθμος hash για κωδικούς invalid_password_algorithm=Μη έγκυρος αλγόριθμος κωδικού πρόσβασης password_algorithm_helper=Ορίστε τον αλγόριθμο κατακερματισμού για το κωδικό πρόσβασης. Οι αλγόριθμοι διαφέρουν σε απαιτήσεις και αντοχή. Ο αλγόριθμος argon2 είναι αρκετά ασφαλής, αλλά χρησιμοποιεί πολλή μνήμη και μπορεί να είναι ακατάλληλος για μικρά συστήματα. -enable_update_checker=Ενεργοποίηση Ελεγκτή Ενημερώσεων +enable_update_checker=Ενεργοποίηση ελέγχου ενημερώσεων enable_update_checker_helper=Ελέγχει περιοδικά για νέες εκδόσεις κάνοντας σύνδεση στο gitea.io. env_config_keys=Ρυθμίσεις Περιβάλλοντος env_config_keys_prompt=Οι ακόλουθες μεταβλητές περιβάλλοντος θα εφαρμοστούν επίσης στο αρχείο ρυθμίσεων σας: +allow_dots_in_usernames = Επιτρέπει την χρήση τελείων σε ονόματα χρηστών. Δεν θα επηρεαστούν λογαριασμοί που ήδη υπάρχουν. +enable_update_checker_helper_forgejo = Θα γίνεται τακτικά έλεγχος για νέες εκδόσεις του Forgejo ελέγχοντας μία εγγραφή DNS TXT στο release.forgejo.org. +smtp_from_invalid = Η διεύθυνση του πεδίου «Αποστολή email ως» δεν είναι έγκυρη +config_location_hint = Αυτές οι ρυθμίσεις θα αποθηκευτούν στην ακόλουθη τοποθεσία: [home] -uname_holder=Όνομα Χρήστη ή Διεύθυνση Email +uname_holder=Όνομα χρήστη ή διεύθυνση email password_holder=Κωδικός Πρόσβασης switch_dashboard_context=Εναλλαγή Περιεχομένων Αρχικού Πίνακα my_repos=Αποθετήρια @@ -318,7 +336,7 @@ view_home=Προβολή %s search_repos=Βρείτε ένα αποθετήριο… filter=Άλλα Φίλτρα filter_by_team_repositories=Φιλτράρισμα ανά αποθετήρια ομάδας -feed_of=`Τροφοδοσία του "%s"` +feed_of=Ροή (feed) του «%s» show_archived=Αρχειοθετήθηκε show_both_archived_unarchived=Εμφάνιση και αρχειοθετημένων και μη αρχειοθετημένων @@ -340,63 +358,63 @@ search=Αναζήτηση go_to=Μετάβαση σε code=Κώδικας search.type.tooltip=Τύπος αναζήτησης -search.fuzzy=Fuzzy +search.fuzzy=Όμοιο search.fuzzy.tooltip=Συμπερίληψη και των αποτελεσμάτων που είναι πλησιέστερα με τον όρο αναζήτησης search.match=Ταίριασμα search.match.tooltip=Συμπερίληψη μόνο των αποτελεσμάτων που ταιριάζουν ακριβώς με τον όρο αναζήτησης code_search_unavailable=Η αναζήτηση κώδικα δεν είναι διαθέσιμη αυτή τη στιγμή. Παρακαλώ επικοινωνήστε με το διαχειριστή. -repo_no_results=Δεν βρέθηκαν αποθετήρια που να ταιρίαζουν με τα κριτήρια. +repo_no_results=Δεν βρέθηκαν σχετικά αποθετήρια. user_no_results=Δεν βρέθηκαν χρήστες που να ταιριάζουν με τα κριτήρια. org_no_results=Δεν βρέθηκαν οργανισμοί που να ταιριάζουν με τα κριτήρια. code_no_results=Δεν βρέθηκε πηγαίος κώδικας που να ταιριάζει με τον όρο αναζήτησης. -code_search_results=`Αποτελέσματα αναζήτησης για "%s"` -code_last_indexed_at=Τελευταίο δημιουργία ευρετηρίου στις %s +code_search_results=`Αποτελέσματα για «%s»` +code_last_indexed_at=Τελευταία δημιουργία ευρετηρίου: %s relevant_repositories_tooltip=Τα αποθετήρια που είναι forks ή που δεν έχουν θέμα, εικονίδιο και περιγραφή είναι κρυμμένα. -relevant_repositories=Εμφανίζονται μόνο τα σχετικά αποθετήρια, εμφάνιση χωρίς φίλτρο. +relevant_repositories=Εμφανίζονται μόνο αποθετήρια που θεωρούμε «σχετικά» για εσάς. Εμφάνιση αποτελεσμάτων χωρίς φίλτρο. [auth] -create_new_account=Εγγραφή Λογαριασμού +create_new_account=Δημιουργία Λογαριασμού register_helper_msg=Έχετε ήδη λογαριασμό; Συνδεθείτε τώρα! social_register_helper_msg=Έχετε ήδη λογαριασμό; Συνδέστε το τώρα! -disable_register_prompt=Η εγγραφή είναι απενεργοποιημένη. Παρακαλούμε επικοινωνήστε με το διαχειριστή του ιστοτόπου. -disable_register_mail=Η Επιβεβαίωση email για την εγγραφή είναι απενεργοποιημένη. +disable_register_prompt=Οι εγγραφές είναι απενεργοποιημένες. Παρακαλούμε να επικοινωνήσετε με το διαχειριστή του ιστοτόπου. +disable_register_mail=Η επιβεβαίωση email κατά την εγγραφή είναι απενεργοποιημένη. manual_activation_only=Επικοινωνήστε με το διαχειριστή της υπηρεσίας για να ολοκληρώσετε την ενεργοποίηση. -remember_me=Απομνημόνευση αυτής της συσκευής -remember_me.compromised=Το διακριτικό σύνδεσης δεν είναι πλέον έγκυρο, αυτό ίσως υποδεικνύει έναν κλεμμένο λογαριασμό. Παρακαλώ ελέγξτε το λογαριασμό σας για ασυνήθιστες δραστηριότητες. -forgot_password_title=Ξέχασα Τον Κωδικό Πρόσβασης -forgot_password=Ξεχάσατε τον κωδικό πρόσβασης; +remember_me=Θέλω να παραμείνω συνδεδεμένος +remember_me.compromised=Το διακριτικό σύνδεσης δεν είναι πλέον έγκυρο, αυτό ίσως υποδεικνύει έναν κλεμμένο λογαριασμό. Παρακαλώ ελέγξτε τον λογαριασμό σας για ασυνήθιστες δραστηριότητες. +forgot_password_title=Ξέχασα τον κωδικό μου +forgot_password=Ξεχάσατε τον κωδικό σας; sign_up_now=Χρειάζεστε λογαριασμό; Εγγραφείτε τώρα. sign_up_successful=Ο λογαριασμός δημιουργήθηκε επιτυχώς. Καλώς ορίσατε! -confirmation_mail_sent_prompt=Ένα νέο email επιβεβαίωσης έχει σταλεί στο %s. Παρακαλώ ελέγξτε τα εισερχόμενα σας μέσα στις επόμενες %s για να ολοκληρώσετε τη διαδικασία εγγραφής. +confirmation_mail_sent_prompt=Ένα νέο email επιβεβαίωσης έχει σταλεί στην διεύθυνση %s. Για την ολοκλήρωση της εγγραφής σας, παρακαλώ ελέγξτε τα εισερχόμενα σας μέσα στις επόμενες %s. must_change_password=Ενημερώστε τον κωδικό πρόσβασης σας allow_password_change=Απαιτείται από το χρήστη να αλλάξει τον κωδικό πρόσβασης (συνιστόμενο) -reset_password_mail_sent_prompt=Ένα email επιβεβαίωσης έχει σταλεί στο %s. Παρακαλώ ελέγξτε τα εισερχόμενα σας στις επόμενες %s για να ολοκληρώσετε τη διαδικασία ανάκτησης λογαριασμού. +reset_password_mail_sent_prompt=Ένα email επιβεβαίωσης έχει σταλεί στο %s. Για την ολοκλήρωση της διαδικασίας ανάκτησης του λογαριασμού σας, παρακαλώ ελέγξτε τα εισερχόμενα σας στις επόμενες %s. active_your_account=Ενεργοποιήστε Το Λογαριασμό Σας account_activated=Ο λογαριασμός έχει ενεργοποιηθεί prohibit_login=Απαγορεύεται η Σύνδεση -prohibit_login_desc=Ο λογαριασμός σας δεν επιτρέπεται να συνδεθεί, παρακαλούμε επικοινωνήστε με το διαχειριστή σας. +prohibit_login_desc=Δεν επιτρέπεται η σύνδεση στον λογαριασμό σας, παρακαλούμε επικοινωνήστε με το διαχειριστή σας. resent_limit_prompt=Έχετε ήδη ζητήσει ένα email ενεργοποίησης πρόσφατα. Παρακαλώ περιμένετε 3 λεπτά και προσπαθήστε ξανά. -has_unconfirmed_mail=Γεια σας %s, έχετε μια ανεπιβεβαίωτη διεύθυνση ηλεκτρονικού ταχυδρομείου (%s). Εάν δεν έχετε λάβει email επιβεβαίωσης ή χρειάζεται να αποστείλετε εκ νέου ένα νέο, παρακαλώ κάντε κλικ στο παρακάτω κουμπί. +has_unconfirmed_mail=Γειά %s, η διεύθυνση ηλεκτρονικού ταχυδρομείου σας (%s) δεν έχει επιβεβαιωθεί ακόμα. Εάν δεν έχετε λάβει κάποιο email επιβεβαίωσης ή αν χρειάζεστε ένα νέο email επιβεβαίωσης, παρακαλώ πατήστε το παρακάτω κουμπί. resend_mail=Κάντε κλικ εδώ για να στείλετε ξανά το email ενεργοποίησης email_not_associate=Η διεύθυνση ηλεκτρονικού ταχυδρομείου δεν είναι συσχετισμένη με κάποιο λογαριασμό. send_reset_mail=Αποστολή Email Ανάκτησης Λογαριασμού reset_password=Ανάκτηση Λογαριασμού invalid_code=Ο κωδικός επιβεβαίωσης δεν είναι έγκυρος ή έχει λήξει. -invalid_code_forgot_password=Ο κωδικός επιβεβαίωσης δεν είναι έγκυρος ή έληξε. Πατήστε εδώ για να ξεκινήσετε νέα συνεδρία. +invalid_code_forgot_password=Ο κωδικός επιβεβαίωσης έχει λήξει ή δεν είναι έγκυρος. Πατήστε εδώ για να ξαναξεκινήσετε την διαδικασία. invalid_password=Ο κωδικός πρόσβασης σας δεν ταιριάζει με τον κωδικό που χρησιμοποιήθηκε για τη δημιουργία του λογαριασμού. reset_password_helper=Ανάκτηση Λογαριασμού -reset_password_wrong_user=Έχετε συνδεθεί ως %s, αλλά ο σύνδεσμος ανάκτησης λογαριασμού προορίζεται για το %s +reset_password_wrong_user=Έχετε συνδεθεί με τον λογαριασμό %s, αλλά ο σύνδεσμος ανάκτησης λογαριασμού προορίζεται για τον λογαριασμό %s password_too_short=Το μήκος του κωδικού πρόσβασης δεν μπορεί να είναι μικρότερο από %d χαρακτήρες. non_local_account=Οι μη τοπικοί χρήστες δεν μπορούν να ενημερώσουν τον κωδικό πρόσβασής τους μέσω του διεπαφής web του Forgejo. verify=Επαλήθευση scratch_code=Κωδικός μιας χρήσης use_scratch_code=Χρήση κωδικού μιας χρήσης -twofa_scratch_used=Έχετε χρησιμοποιήσει τον κωδικό μιας χρήσης. Έχετε ανακατευθυνθεί στη σελίδα ρυθμίσεων δύο παραγόντων ώστε να μπορείτε να αφαιρέσετε την εγγραφή της συσκευής σας ή να δημιουργήσετε ένα νέο κωδικό μιας χρήσης. +twofa_scratch_used=Χρησιμοποιήσατε τον κωδικό μιας χρήσης σας. Ανακατευθυνθήκατε στις ρυθμίσεις δύο παραγόντων για να αφαιρέσετε την συσκευή με την οποία ταυτοποιήστε κανονικά ή για να δημιουργήσετε έναν νέο κωδικό μιας χρήσης. twofa_passcode_incorrect=Ο κωδικός σας είναι εσφαλμένος. Αν χάσατε τη συσκευή σας, χρησιμοποιήστε τον κωδικό μιας χρήσης για να συνδεθείτε. twofa_scratch_token_incorrect=Ο κωδικός μιας χρήσης είναι εσφαλμένος. login_userpass=Είσοδος -login_openid=OpenID -oauth_signup_tab=Εγγραφή Νέου Λογαριασμού +tab_openid=OpenID +oauth_signup_tab=Δημιουργία νέου λογαριασμού oauth_signup_title=Ολοκλήρωση Νέου Λογαριασμού oauth_signup_submit=Ολοκληρωμένος Λογαριασμός oauth_signin_tab=Σύνδεση με υπάρχων λογαριασμό @@ -410,86 +428,93 @@ openid_connect_title=Σύνδεση σε υπάρχων λογαριασμό openid_connect_desc=Το επιλεγμένο OpenID URI είναι άγνωστο. Συνδέστε το με ένα νέο λογαριασμό εδώ. openid_register_title=Δημιουργία νέου λογαριασμού openid_register_desc=Το επιλεγμένο OpenID URI είναι άγνωστο. Συνδέστε το με ένα νέο λογαριασμό εδώ. -openid_signin_desc=Εισάγετε το OpenID URI σας. Για παράδειγμα: alice.openid.example.org ή https://openid.example.org/alice. -disable_forgot_password_mail=Η ανάκτηση λογαριασμού είναι απενεργοποιημένη επειδή δεν έχει οριστεί email. Παρακαλούμε επικοινωνήστε με το διαχειριστή. -disable_forgot_password_mail_admin=Η ανάκτηση λογαριασμού είναι διαθέσιμη μόνο όταν έχει οριστεί το email. Παρακαλούμε ορίστει το email σας για να ενεργοποιήσετε την ανάκτηση λογαριασμού. +openid_signin_desc=Εισάγετε το OpenID URI σας. Για παράδειγμα: ioanna.openid.example.org ή https://openid.example.org/grigoris. +disable_forgot_password_mail=Ο λογαριασμός δεν μπορεί να ανακτηθεί επειδή δεν αντιστοιχείται κάποιο email με αυτόν τον λογαριασμό. Παρακαλούμε επικοινωνήστε με τον διαχειριστή σας. +disable_forgot_password_mail_admin=Η ανάκτηση λογαριασμού είναι διαθέσιμη μόνο όταν έχει οριστεί το email. Παρακαλούμε ορίστε το email σας για να ενεργοποιήσετε την ανάκτηση λογαριασμού. email_domain_blacklisted=Δεν μπορείτε να εγγραφείτε με τη διεύθυνση email σας. authorize_application=Εξουσιοδότηση Εφαρμογής authorize_redirect_notice=Θα μεταφερθείτε στο %s εάν εξουσιοδοτήσετε αυτήν την εφαρμογή. authorize_application_created_by=Αυτή η εφαρμογή δημιουργήθηκε από %s. authorize_application_description=Εάν παραχωρήσετε την πρόσβαση, θα μπορεί να έχει πρόσβαση και να γράφει σε όλες τις πληροφορίες του λογαριασμού σας, συμπεριλαμβανομένων των ιδιωτικών αποθετηρίων και οργανισμών. -authorize_title=Εξουσιοδότηση του "%s" για έχει πρόσβαση στο λογαριασμό σας; +authorize_title=Είστε βέβαιοι πως θέλετε να δώσετε πρόσβαση στον λογαριασμό σας στην εφαρμογή «%s»; authorization_failed=Αποτυχία εξουσιοδότησης authorization_failed_desc=Η εξουσιοδότηση απέτυχε επειδή εντοπίστηκε μια μη έγκυρη αίτηση. Παρακαλούμε επικοινωνήστε με το συντηρητή της εφαρμογής που προσπαθήσατε να εξουσιοδοτήσετε. sspi_auth_failed=Αποτυχία ταυτοποίησης SSPI -password_pwned=Ο κωδικός πρόσβασης που επιλέξατε είναι σε μια λίστα κλεμμένων κωδικών πρόσβασης που προηγουμένως εκτέθηκαν σε παραβίαση δημόσιων δεδομένων. Παρακαλώ δοκιμάστε ξανά με διαφορετικό κωδικό πρόσβασης και σκεφτείτε να αλλάξετε αυτόν τον κωδικό πρόσβασης όπου αλλού χρησιμοποιείται. +password_pwned=Ο κωδικός πρόσβασης που επιλέξατε βρίσκεται σε μια λίστα κλεμμένων κωδικών πρόσβασης που προηγουμένως εκτέθηκαν σε παραβίαση δημόσιων δεδομένων. Παρακαλώ δοκιμάστε ξανά με διαφορετικό κωδικό πρόσβασης και σκεφτείτε να αλλάξετε αυτόν τον κωδικό πρόσβασης όπου αλλού χρησιμοποιείται. password_pwned_err=Δεν ήταν δυνατή η ολοκλήρωση του αιτήματος προς το HaveIBeenPwned +change_unconfirmed_email_error = Δεν ήταν δυνατή η αλλαγή της διεύθυνσης email: %v +last_admin = Δεν μπορείτε να αφαιρέσετε τον μοναδικό διαχειριστή. Πρέπει να υπάρχει τουλάχιστον ένας διαχειριστής. +change_unconfirmed_email = Αν έχετε εισάγει μία λανθασμένη διεύθυνση email κατά την εγγραφή σας, μπορείτε να την αλλάξετε παρακάτω, έτσι ώστε να σας σταλεί εκ νέου ένα νέο email επιβεβαίωσης στην νέα σας διεύθυνση. +change_unconfirmed_email_summary = Αλλαγή της διεύθυνσης email στην οποία θα σταλεί το email επιβεβαίωσης. [mail] view_it_on=Δείτε το στο %s reply=ή απαντήστε απευθείας σε αυτό το email -link_not_working_do_paste=Δε λειτουργεί; Δοκιμάστε να το αντιγράψετε στο browser σας. -hi_user_x=Γειά σου %s, +link_not_working_do_paste=Δεν δουλεύει το link; Δοκιμάστε να το αντιγράψετε στην μπάρα διεύθυνσης (URL bar) του browser σας. +hi_user_x=Γειά %s, activate_account=Παρακαλώ ενεργοποιήστε το λογαριασμό σας -activate_account.title=%s, παρακαλώ ενεργοποιήστε το λογαριασμό σας +activate_account.title=%s, παρακαλώ ενεργοποιήστε τον λογαριασμό σας activate_account.text_1=Γεια σας %[1]s, ευχαριστούμε για την εγγραφή στο %[2]s! -activate_account.text_2=Παρακαλούμε κάντε κλικ στον παρακάτω σύνδεσμο για να ενεργοποιήσετε το λογαριασμό σας μέσα σε %s: +activate_account.text_2=Για να ενεργοποιήσετε τον λογαριασμό σας, παρακαλώ πατήστε τον σύνδεσμο που ακολουθεί μέσα σε %s: activate_email=Επιβεβαιώστε τη διεύθυνση email σας -activate_email.title=%s, παρακαλώ επαληθεύστε τη διεύθυνση email σας -activate_email.text=Παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για να επαληθεύσετε τη διεύθυνση email σας στο %s: +activate_email.title=%s, επαληθεύστε τη διεύθυνση email σας +activate_email.text=Για να επαληθεύσετε τη διεύθυνση email σας, παρακαλώ πατήστε τον ακόλουθο σύνδεσμο μέσα σε %s: register_notify=Καλώς ήλθατε στο Forgejo -register_notify.title=%[1]s, καλώς ήρθατε στο %[2]s -register_notify.text_1=αυτό είναι το email επιβεβαίωσης εγγραφής για το %s! -register_notify.text_2=Τώρα μπορείτε να συνδεθείτε μέσω του ονόματος χρήστη: %s. -register_notify.text_3=Εάν αυτός ο λογαριασμός έχει δημιουργηθεί για εσάς, παρακαλώ ορίστε πρώτα τον κωδικό πρόσβασής σας. +register_notify.title=%[1]s, καλώς ήλθατε στο %[2]s +register_notify.text_1=αυτό είναι το email επιβεβαίωσης εγγραφής σας για το %s! +register_notify.text_2=Μπορείτε να συνδεθείτε χρησιμοποιώντας το όνομα χρήστη σας: %s +register_notify.text_3=Εάν κάποιος άλλος δημιούργησε τον λογαριασμό για εσάς, παρακαλώ ορίστε πρώτα τον κωδικό πρόσβασής σας. reset_password=Ανάκτηση του λογαριασμού σας -reset_password.title=%s, ζητήσατε να ανακτήσετε το λογαριασμό σας -reset_password.text=Κάντε κλικ στον παρακάτω σύνδεσμο για να ανακτήσετε το λογαριασμό σας εντός %s: +reset_password.title=%s, λάβαμε ένα αίτημα ανάκτησης για τον λογαριασμό σας +reset_password.text=Εφόσον το αίτημα δημιουργήθηκε από εσάς, πατήστε τον ακόλουθο σύνδεσμο για να ανακτήσετε το λογαριασμό σας μέσα σε %s: -register_success=Επιτυχής εγγραφή +register_success=Η εγγραφή ολοκληρώθηκε επιτυχώς -issue_assigned.pull=@%[1]s σας έχει αναθέσει στο pull request %[2]s στο αποθετήριο %[3]s. -issue_assigned.issue=@%[1]s σας ανέθεσε το ζήτημα %[2]s στο αποθετήριο %[3]s. +issue_assigned.pull=Ο/Η @%[1]s σας έχει αναθέσει στο pull request %[2]s στο αποθετήριο %[3]s. +issue_assigned.issue=Ο/Η @%[1]s σας ανέθεσε το ζήτημα %[2]s στο αποθετήριο %[3]s. -issue.x_mentioned_you=@%s σας ανέφερε: -issue.action.force_push=%[1]s έκανε force-push το %[2]s από %[3]s σε %[4]s. -issue.action.push_1=@%[1]s έκανε push την υποβολή %[3]d στο %[2]s -issue.action.push_n=@%[1]s έκανε push τις υποβολές %[3]d στο %[2]s -issue.action.close=@%[1]s έκλεισε το #%[2]d. -issue.action.reopen=@%[1]s άνοιξε ξανά το #%[2]d. -issue.action.merge=@%[1]s συγχώνευσε το #%[2]d στο %[3]s. +issue.x_mentioned_you=Ο/Η @%s σας ανέφερε: +issue.action.force_push=Ο/Η %[1]s έκανε force-push το %[2]s από %[3]s σε %[4]s. +issue.action.push_1=Ο/Η @%[1]s έκανε push την υποβολή %[3]d στο %[2]s +issue.action.push_n=Ο/Η @%[1]s έκανε push τις υποβολές %[3]d στο %[2]s +issue.action.close=Ο/Η @%[1]s έκλεισε το #%[2]d. +issue.action.reopen=Ο/Η @%[1]s άνοιξε ξανά το #%[2]d. +issue.action.merge=Ο/Η @%[1]s συγχώνευσε το #%[2]d στο %[3]s. issue.action.approve=@%[1]s ενέκρινε αυτό το pull request. issue.action.reject=@%[1]s ζήτησε αλλαγές σε αυτό το pull request. issue.action.review=@%[1]s σχολίασε αυτό το pull request. issue.action.review_dismissed=@%[1]s απέρριψε την τελευταία αναθεώρηση από %[2]s για αυτό το pull request. -issue.action.ready_for_review=@%[1]s σημείωσε αυτό το pull request σαν έτοιμο για αναθεώρηση. -issue.action.new=@%[1]s δημιούργησε το #%[2]d. +issue.action.ready_for_review=Ο/Η @%[1]s επισήμανε πως αυτό το pull request είναι έτοιμο για αξιολόγηση. +issue.action.new=Ο/Η @%[1]s δημιούργησε το #%[2]d. issue.in_tree_path=Σε %s: -release.new.subject=%s σε %s κυκλοφόρησε -release.new.text=@%[1]s κυκλοφόρησε το %[2]s στο %[3]s +release.new.subject=Κυκλοφόρησε νέα έκδοση %s του %s +release.new.text=Ο/Η @%[1]s κυκλοφόρησε την έκδοση %[2]s στο %[3]s release.title=Τίτλος: %s release.note=Σημείωση: release.downloads=Λήψεις: -release.download.zip=Πηγαίος Κώδικας (Zip) +release.download.zip=Πηγαίος Κώδικας (ZIP) release.download.targz=Πηγαίος Κώδικας (TAR.GZ) -repo.transfer.subject_to=%s θα ήθελε να μεταφέρει το "%s" σε %s -repo.transfer.subject_to_you=`%s θα ήθελε να σας μεταφέρει το "%s"` +repo.transfer.subject_to=Ο/Η %s θα ήθελε να μεταφέρει το αποθετήριο «%s» σε %s +repo.transfer.subject_to_you=Ο/Η %s θα ήθελε να σας μεταφέρει το αποθετήριο «%s» repo.transfer.to_you=εσάς -repo.transfer.body=Για να το αποδεχτείτε ή να το απορρίψετε, επισκεφθείτε το %s ή απλά αγνοήστε το. +repo.transfer.body=Για να αποδεχτείτε ή να απορρίψετε το αίτημα αυτό, επισκεφθείτε το %s ή απλά αγνοήστε το. -repo.collaborator.added.subject=%s σας πρόσθεσε στο %s -repo.collaborator.added.text=Έχετε προστεθεί ως συνεργάτης του αποθετηρίου: +repo.collaborator.added.subject=Ο/Η %s σας πρόσθεσε στο %s ως συνεργάτη +repo.collaborator.added.text=Είστε πλέον συνεργάτης στο αποθετήριο: -team_invite.subject=%[1]s σας προσκάλεσε να συμμετέχετε στον οργανισμό %[2]s -team_invite.text_1=%[1]s σας προσκάλεσε να συμμετέχετε στην ομάδα %[2]s στον οργανισμός %[3]s. +team_invite.subject=Ο/Η %[1]s σας προσκάλεσε να συμμετέχετε στον οργανισμό %[2]s +team_invite.text_1=Ο/Η %[1]s σας προσκάλεσε να συμμετέχετε στην ομάδα %[2]s του οργανισμού %[3]s. team_invite.text_2=Παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για να συμμετάσχετε στην ομάδα: team_invite.text_3=Σημείωση: Αυτή η πρόσκληση προοριζόταν για %[1]s. Αν δεν περιμένατε αυτή την πρόσκληση, μπορείτε να αγνοήσετε αυτό το email. +admin.new_user.text = Παρακαλώ πατήστε εδώ για να διαχειριστείτε τον χρήστη μέσω του πίνακα διαχειριστών. +admin.new_user.subject = Εγγραφή νέου χρήστη %s +admin.new_user.user_info = Πληροφορίες Χρήστη [modal] yes=Ναι @@ -503,7 +528,7 @@ UserName=Όνομα Χρήστη RepoName=Όνομα αποθετηρίου Email=Διεύθυνση email Password=Κωδικός πρόσβασης -Retype=Επιβεβαίωση Κωδικού Πρόσβασης +Retype=Επιβεβαίωση κωδικού SSHTitle=Όνομα κλειδιού SSH HttpsUrl=HTTPS URL PayloadUrl=Payload URL @@ -522,18 +547,18 @@ SSPISeparatorReplacement=Διαχωριστικό SSPIDefaultLanguage=Προεπιλεγμένη Γλώσσα require_error=` δεν μπορεί να είναι κενό.` -alpha_dash_error=` πρέπει να περιέχει μόνο αλφαριθμητικά, παύλες ('-') και κάτω παύλες ('_').` -alpha_dash_dot_error=` πρέπει να περιέχει μόνο αλφαριθμητικά, παύλα ('-'), κάτω παύλα ('_') και τελείες ('.').` +alpha_dash_error=`: Πρέπει να περιέχει μόνο αλφαριθμητικά, παύλες ('-') και κάτω παύλες ('_').` +alpha_dash_dot_error=`: Πρέπει να περιέχει μόνο αλφαριθμητικά, παύλα ('-'), κάτω παύλα ('_') και τελείες ('.').` git_ref_name_error=` πρέπει να είναι ένα καλά διαμορφωμένο όνομα αναφοράς Git.` size_error=`πρέπει να έχει μέγεθος %s.` min_size_error=` πρέπει να περιέχει τουλάχιστον %s χαρακτήρες.` max_size_error=` πρέπει να περιέχει το πολύ %s χαρακτήρες.` email_error=` δεν είναι έγκυρη διεύθυνση email.` -url_error=`Το "%s" δεν είναι ένα έγκυρο URL.` -include_error=` πρέπει να περιέχει το μέρος "%s".` +url_error=`Το «%s» δεν είναι ένα έγκυρο URL.` +include_error=` πρέπει να περιέχει το μέρος «%s».` glob_pattern_error=` το μοτίβο ταιριάσματος (glob) δεν είναι έγκυρο: %s.` regex_pattern_error=` το μοτίβο regex δεν είναι έγκυρο: %s.` -username_error=` μπορεί να περιέχει μόνο αλφαριθμητικούς χαρακτήρες ('0-9','a-z','A-Z'), παύλα ('-'), κάτω παύλα ('_') και τελεία ('.'). Δεν μπορεί να ξεκινά ή να τελειώνει με μη αλφαριθμητικούς χαρακτήρες, επίσης απαγορεύονται οι διαδοχικοί μη αλφαριθμητικοί χαρακτήρες.` +username_error=`: Μπορεί να περιέχει μόνο αλφαριθμητικούς χαρακτήρες ('0-9','a-z','A-Z'), παύλα ('-'), κάτω παύλα ('_') και τελεία ('.'). Δεν μπορεί να αρχίζει ή να τελειώνει με μη αλφαριθμητικούς χαρακτήρες. Επίσης δεν επιτρέπεται η χρήση διαδοχικών μη αλφαριθμητικών χαρακτήρων.` invalid_group_team_map_error=` η αντιστοίχιση δεν είναι έγκυρη: %s` unknown_error=Άγνωστο σφάλμα: captcha_incorrect=Ο κωδικός CAPTCHA είναι λάθος. @@ -556,7 +581,7 @@ team_name_been_taken=Το όνομα της ομάδας χρησιμοποιε team_no_units_error=Να επιτρέπεται η πρόσβαση σε τουλάχιστον μία ενότητα αποθετηρίου. email_been_used=Η διεύθυνση email χρησιμοποιείται ήδη. email_invalid=Η διεύθυνση email δεν είναι έγκυρη. -openid_been_used=Η διεύθυνση OpenID "%s" χρησιμοποιείται ήδη. +openid_been_used=Η διεύθυνση OpenID «%s» χρησιμοποιείται ήδη. username_password_incorrect=Το όνομα χρήστη ή ο κωδικός πρόσβασης δεν είναι σωστά. password_complexity=Ο κωδικός πρόσβασης δεν περνά τις απαιτήσεις πολυπλοκότητας: password_lowercase_one=Τουλάχιστον ένα πεζό γράμμα @@ -569,51 +594,61 @@ enterred_invalid_owner_name=Το όνομα νέου ιδιοκτήτη δεν enterred_invalid_password=Ο κωδικός πρόσβασης που εισάγατε είναι λάθος. user_not_exist=Δεν υπάρχει ο χρήστης. team_not_exist=Δεν υπάρχει η ομάδα. -last_org_owner=Δεν μπορείτε να καταργήσετε τον τελευταίο χρήστη από την ομάδα 'ιδιοκτήτών'. Πρέπει να υπάρχει τουλάχιστον ένας ιδιοκτήτης για έναν οργανισμό. +last_org_owner=Δεν μπορείτε να καταργήσετε τον τελευταίο χρήστη από την ομάδα ιδιοκτητών. Πρέπει να υπάρχει τουλάχιστον ένας ιδιοκτήτης στον οργανισμό. cannot_add_org_to_team=Ένας οργανισμός δεν μπορεί να προστεθεί ως μέλος ομάδας. duplicate_invite_to_team=Ο χρήστης είχε ήδη προσκληθεί ως μέλος της ομάδας. -organization_leave_success=Έχετε εγκαταλείψει με επιτυχία τον οργανισμό %s. +organization_leave_success=Η αποχώρησή σας από τον οργανισμό %s ήταν επιτυχής. -invalid_ssh_key=Δεν είναι δυνατή η επαλήθευση του SSH κλειδιού σας: %s -invalid_gpg_key=Δεν είναι δυνατή η επαλήθευση του GPG κλειδιού σας: %s +invalid_ssh_key=Δεν είναι δυνατή η επαλήθευση του κλειδιού SSH σας: %s +invalid_gpg_key=Δεν είναι δυνατή η επαλήθευση του κλειδιού GPG σας: %s invalid_ssh_principal=Μη έγκυρη αρχή: %s must_use_public_key=Το κλειδί που δώσατε είναι ένα ιδιωτικό κλειδί. Παρακαλώ μην ανεβάζετε το ιδιωτικό σας κλειδί οπουδήποτε. Χρησιμοποιήστε το δημόσιο κλειδί αντί αυτού. -unable_verify_ssh_key=Αδυναμία επαλήθευσης του κλειδιού SSH, ελέγξτε το ξανά για λάθη. +unable_verify_ssh_key=Δεν είναι δυνατή η επαλήθευση του κλειδιού SSH, ελέγξτε το ξανά για λάθη. auth_failed=Αποτυχία ταυτοποίησης: %v -still_own_repo=Ο λογαριασμός σας κατέχει ένα ή περισσότερα αποθετήρια, διαγράψτε ή μεταφέρετε τα πρώτα. -still_has_org=Ο λογαριασμός σας είναι μέλος σε ένα ή περισσότερους οργανισμούς, φύγετε από αυτούς πρώτα. -still_own_packages=Ο λογαριασμός σας κατέχει ένα ή περισσότερα πακέτα, διαγράψτε τα πρώτα. -org_still_own_repo=Αυτός ο οργανισμός κατέχει ακόμα ένα ή περισσότερα αποθετήρια, διαγράψτε τα ή μεταφέρετε τα πρώτα. -org_still_own_packages=Αυτός ο οργανισμός κατέχει ακόμα ένα ή περισσότερα πακέτα, διαγράψτε τα πρώτα. +still_own_repo=Ο λογαριασμός σας κατέχει ένα ή περισσότερα αποθετήρια, πρέπει πρώτα να τα διαγράψετε ή να τα μεταφέρετε. +still_has_org=Ο λογαριασμός σας είναι μέλος σε έναν ή και περισσότερους οργανισμούς, πρέπει πρώτα να αποχωρήσετε από αυτούς. +still_own_packages=Ο λογαριασμός σας κατέχει ένα ή περισσότερα πακέτα, πρέπει πρώτα να τα διαγράψετε. +org_still_own_repo=Αυτός ο οργανισμός κατέχει ακόμα ένα ή περισσότερα αποθετήρια, πρέπει πρώτα να τα διαγράψετε ή να τα μεταφέρετε. +org_still_own_packages=Αυτός ο οργανισμός κατέχει ακόμα ένα ή περισσότερα πακέτα, πρέπει πρώτα να τα διαγράψετε. target_branch_not_exist=Ο κλάδος προορισμού δεν υπάρχει. +username_error_no_dots = `: Μπορεί να περιέχει μόνο αλφαριθμητικούς χαρακτήρες ('0-9','a-z','A-Z'), παύλα ('-') και κάτω παύλα ('_'). Δεν μπορεί να αρχίζει ή να τελειώνει με μη αλφαριθμητικούς χαρακτήρες. Επίσης δεν επιτρέπεται η χρήση διαδοχικών μη αλφαριθμητικών χαρακτήρων.` +admin_cannot_delete_self = Δεν μπορείτε να διαγράψετε τον εαυτό σας όσο είστε διαχειριστής. Πρέπει πρώτα να αφαιρέσετε τα δικαιώματα διαχειριστή σας. [user] change_avatar=Αλλαγή του avatar σας… -joined_on=Εγγράφηκε την %s +joined_on=Εγγράφηκε στις %s repositories=Αποθετήρια activity=Δημόσια Δραστηριότητα -followers=Ακόλουθοι +followers=ακόλουθοι starred=Αγαπημένα Αποθετήρια watched=Ακολουθούμενα Αποθετήρια code=Κώδικας projects=Έργα overview=Επισκόπηση -following=Ακολουθεί -follow=Ακολουθήστε +following=ακολουθεί +follow=Ακολούθηση unfollow=Να μην ακολουθώ user_bio=Βιογραφικό -disabled_public_activity=Αυτός ο χρήστης έχει απενεργοποιήσει τη δημόσια προβολή της δραστηριότητας. +disabled_public_activity=Αυτός ο χρήστης έχει απενεργοποιήσει τη δημόσια προβολή της δραστηριότητας του. email_visibility.limited=Η διεύθυνση email σας είναι ορατή σε όλους τους ταυτοποιημένους χρήστες email_visibility.private=Η διεύθυνση email σας είναι ορατή μόνο σε εσάς και στους διαχειριστές -show_on_map=Εμφάνιση της τοποθεσίας στο χάρτη -settings=Ρυθμίσεις Χρήστη +show_on_map=Δείτε το μέρος αυτό σε έναν χάρτη +settings=Ρυθμίσεις χρήστη -form.name_reserved=Το όνομα χρήστη "%s" είναι δεσμευμένο. -form.name_pattern_not_allowed=Το μοτίβο "%s" δεν επιτρέπεται μέσα σε ένα όνομα χρήστη. -form.name_chars_not_allowed=Το όνομα χρήστη "%s" περιέχει μη έγκυρους χαρακτήρες. +form.name_reserved=Το όνομα χρήστη «%s» είναι δεσμευμένο. +form.name_pattern_not_allowed=Το μοτίβο «%s» δεν επιτρέπεται μέσα σε ένα όνομα χρήστη. +form.name_chars_not_allowed=Το όνομα χρήστη «%s» περιέχει μη έγκυρους χαρακτήρες. +follow_blocked_user = Δεν μπορείτε να ακολουθήσετε τον χρήστη, επειδή τον έχετε αποκλείσει ή επειδή ο χρήστης σας έχει αποκλείσει. +unblock = Άρση αποκλεισμού +block = Αποκλεισμός +block_user = Αποκλεισμός χρήστη +block_user.detail_1 = Ο χρήστης δεν θα ακολουθεί πια τον λογαριασμό σας. +block_user.detail_2 = Ο χρήστης δεν θα μπορεί να αλληλεπιδράσει με τα αποθετήριά σας, να δημιουργήσει ζητήματα ή να αφήσει σχόλια. +block_user.detail_3 = Ο χρήστης δεν θα μπορέσει να σας προσθέσει στα αποθετήριά του ως συνεργάτη και αντίστοιχα δεν θα μπορείτε να τον προσθέσετε στα δικά σας αποθετήρια. +block_user.detail = Επισημαίνεται πως αν αποκλείσετε αυτόν τον χρήστη, θα προκύψουν ταυτόχρονα και άλλες ενέργειες. Μερικές από αυτές: [settings] profile=Προφίλ @@ -623,18 +658,18 @@ password=Κωδικός πρόσβασης security=Ασφάλεια avatar=Εικόνα ssh_gpg_keys=Κλειδιά SSH / GPG -social=Λογαριασμοί Κοινωνικών Δικτύων +social=Λογαριασμοί κοινωνικών δικτύων applications=Εφαρμογές -orgs=Διαχείριση Οργανισμών +orgs=Διαχείριση οργανισμών repos=Αποθετήρια -delete=Διαγραφή Λογαριασμού -twofa=Έλεγχος Ταυτότητας Δύο Παραγόντων +delete=Διαγραφή λογαριασμού +twofa=Πιστοποίηση Δύο Παραγόντων (TOTP) account_link=Συνδεδεμένοι Λογαριασμοί organization=Οργανισμοί uid=UID -webauthn=Κλειδιά Ασφαλείας +webauthn=Πιστοποίηση Δύο Παραγόντων (Κλειδιά Ασφαλείας) -public_profile=Δημόσιο Προφίλ +public_profile=Δημόσιο προφίλ biography_placeholder=Πείτε μας λίγο για τον εαυτό σας! (Μπορείτε να γράψετε με Markdown) location_placeholder=Μοιραστείτε την κατά προσέγγιση τοποθεσία σας με άλλους profile_desc=Ελέγξτε πώς εμφανίζεται το προφίλ σας σε άλλους χρήστες. Η κύρια διεύθυνση email σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση κωδικού πρόσβασης και λειτουργίες Git που βασίζονται στο web. @@ -645,12 +680,12 @@ location=Τοποθεσία update_theme=Ενημέρωση Θέματος Διεπαφής update_profile=Ενημέρωση Προφίλ update_language=Ενημέρωση Γλώσσας -update_language_not_found=Η γλώσσα "%s" δεν είναι διαθέσιμη. -update_language_success=Η γλώσσα ενημερώθηκε. +update_language_not_found=Η γλώσσα «%s» δεν είναι διαθέσιμη. +update_language_success=Η γλώσσα έχει ενημερωθεί. update_profile_success=Το προφίλ σας έχει ενημερωθεί. change_username=Το όνομα χρήστη σας έχει αλλάξει. -change_username_prompt=Σημείωση: Αλλάζοντας το όνομα χρήστη σας αλλάζει επίσης το URL του λογαριασμού σας. -change_username_redirect_prompt=Το παλιό όνομα χρήστη θα ανακατευθύνει μέχρι να ζητηθεί ξανά. +change_username_prompt=Επισήμανση: Η αλλαγή του ονόματος χρήστη σας θα αλλάξει και το URL του λογαριασμού σας. +change_username_redirect_prompt=Το παλιό όνομα χρήστη θα ανακατευθύνει έως ότου χρησιμοποιηθεί ξανά. continue=Συνέχεια cancel=Ακύρωση language=Γλώσσα @@ -662,7 +697,7 @@ hidden_comment_types.issue_ref_tooltip=Σχόλια όπου ο χρήστης comment_type_group_reference=Αναφορά comment_type_group_label=Σήμα comment_type_group_milestone=Ορόσημο -comment_type_group_assignee=Αποδέκτης +comment_type_group_assignee=Υπεύθυνος comment_type_group_title=Τίτλος comment_type_group_branch=Κλάδος comment_type_group_time_tracking=Καταγραφή Χρόνου @@ -675,30 +710,30 @@ comment_type_group_project=Έργο comment_type_group_issue_ref=Αναφορά ζητήματος saved_successfully=Οι ρυθμίσεις σας αποθηκεύτηκαν επιτυχώς. privacy=Απόρρητο -keep_activity_private=Απόκρυψη Δραστηριότητας από τη σελίδα προφίλ +keep_activity_private=Απόκρυψη δραστηριότητας από τη σελίδα προφίλ keep_activity_private_popup=Με αυτή την επιλογή η δραστηριότητα σας είναι ορατή μόνο σε εσάς και τους διαχειριστές lookup_avatar_by_mail=Αναζήτηση ενός Avatar με διεύθυνση email federated_avatar_lookup=Συνενωμένη Αναζήτηση Avatar -enable_custom_avatar=Χρήση Προσαρμοσμένης Εικόνας +enable_custom_avatar=Χρήση προσαρμοσμένης εικόνας προφίλ choose_new_avatar=Επιλέξτε νέα εικόνα update_avatar=Ενημέρωση Εικόνας delete_current_avatar=Διαγραφή Τρέχουσας Εικόνας uploaded_avatar_not_a_image=Το αρχείο που ανεβάσατε δεν είναι εικόνα. -uploaded_avatar_is_too_big=Το μέγεθος αρχείου που ανέβηκε (%d KiB) υπερβαίνει το μέγιστο μέγεθος (%d KiB). -update_avatar_success=Η εικόνα σας έχει ενημερωθεί. +uploaded_avatar_is_too_big=Το μέγεθος του ανεβασμένου αρχείου (%d KiB) υπερβαίνει το μέγιστο μέγεθος (%d KiB). +update_avatar_success=To avatar σας έχει ενημερωθεί. update_user_avatar_success=Το avatar του χρήστη ενημερώθηκε. -update_password=Ενημέρωση Κωδικού Πρόσβασης -old_password=Τρέχων Κωδικός Πρόσβασης -new_password=Νέος Κωδικός Πρόσβασης -retype_new_password=Επιβεβαίωση Νέου Κωδικού Πρόσβασης +update_password=Ενημέρωση κωδικού πρόσβασης +old_password=Τρέχων κωδικός πρόσβασης +new_password=Νέος κωδικός πρόσβασης +retype_new_password=Επιβεβαίωση νέου κωδικού password_incorrect=Ο τρέχων κωδικός πρόσβασης είναι λάθος. change_password_success=Ο κωδικός πρόσβασής σας έχει ενημερωθεί. Από εδώ και τώρα συνδέεστε χρησιμοποιώντας τον νέο κωδικό πρόσβασής σας. password_change_disabled=Οι μη τοπικοί χρήστες δεν μπορούν να ενημερώσουν τον κωδικό πρόσβασής τους μέσω του διεπαφής web του Forgejo. emails=Διευθύνσεις Email -manage_emails=Διαχείριση Διευθύνσεων Email +manage_emails=Διαχείριση διευθύνσεων email manage_themes=Επιλέξτε προεπιλεγμένο θέμα διεπαφής manage_openid=Διαχείριση Διευθύνσεων OpenID email_desc=Η κύρια διεύθυνση ηλεκτρονικού ταχυδρομείου σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση του κωδικού πρόσβασης και, εφόσον δεν είναι κρυμμένη, λειτουργίες Git στον ιστότοπο. @@ -719,31 +754,31 @@ theme_update_error=Το επιλεγμένο θέμα διεπαφής δεν υ openid_deletion=Αφαίρεση Διεύθυνσης OpenID openid_deletion_desc=Η κατάργηση αυτής της διεύθυνσης OpenID από τον λογαριασμό σας θα σας εμποδίσει να συνδέεστε με αυτό. Συνέχεια; openid_deletion_success=Η διεύθυνση OpenID αφαιρέθηκε. -add_new_email=Προσθήκη Νέας Διεύθυνσης Email +add_new_email=Προσθήκη νέας διεύθυνσης email add_new_openid=Προσθήκη Νέου OpenID URI add_email=Προσθήκη Διεύθυνσης Email add_openid=Προσθήκη OpenID URI -add_email_confirmation_sent=Μια επιβεβαίωση στάλθηκε στο email "%s". Ελέγξτε τα εισερχόμενα σας μέσα στα επόμενα %s για να επιβεβαιώσετε τη διεύθυνση email σας. +add_email_confirmation_sent=Ένα email επιβεβαίωσης έχει σταλεί στην διεύθυνση «%s». Για να επιβεβαιώσετε τη διεύθυνση email σας, παρακαλώ ελέγξτε τα εισερχόμενα σας μέσα σε %s. add_email_success=Η νέα διεύθυνση email έχει προστεθεί. email_preference_set_success=Οι προτιμήσεις email έχουν οριστεί επιτυχώς. add_openid_success=Προστέθηκε η νέα διεύθυνση OpenID. -keep_email_private=Απόκρυψη Διεύθυνσης Email -keep_email_private_popup=Αυτό θα κρύψει τη διεύθυνση ηλεκτρονικού ταχυδρομείου σας από το προφίλ σας, καθώς και όταν κάνετε ένα pull request ή επεξεργαστείτε ένα αρχείο χρησιμοποιώντας τη διεπαφή ιστού. Οι ωθούμενες υποβολές δεν θα τροποποιηθούν. Χρησιμοποιήστε το %s στις υποβολές για να τις συσχετίσετε με το λογαριασμό σας. +keep_email_private=Απόκρυψη διεύθυνσης email +keep_email_private_popup=Αυτό θα κρύψει τη διεύθυνση ηλεκτρονικού ταχυδρομείου σας από το προφίλ σας, καθώς και όταν κάνετε ένα pull request ή επεξεργαστείτε ένα αρχείο χρησιμοποιώντας την ιστοσελίδα. Οι υποβολές που ωθείτε δεν θα τροποποιηθούν. Χρησιμοποιήστε το %s στις υποβολές για να τις συσχετίσετε με το λογαριασμό σας. openid_desc=Το OpenID σας επιτρέπει να αναθέσετε τον έλεγχο ταυτότητας σε έναν εξωτερικό πάροχο. -manage_ssh_keys=Διαχείριση SSH Κλειδιών +manage_ssh_keys=Διαχείριση κλειδιών SSH manage_ssh_principals=Διαχείριση Των Αρχών Πιστοποιητικού SSH -manage_gpg_keys=Διαχείριση Κλειδιών GPG +manage_gpg_keys=Διαχείριση κλειδιών GPG add_key=Προσθήκη Κλειδιού -ssh_desc=Αυτά τα δημόσια SSH κλειδιά συνδέονται με το λογαριασμό σας. Τα αντίστοιχα ιδιωτικά κλειδιά επιτρέπουν πλήρη πρόσβαση στα αποθετήριά σας. +ssh_desc=Αυτά τα δημόσια κλειδιά SSH θα συσχετιθούν με το λογαριασμό σας. Τα ιδιωτικά κλειδιά που τους αντιστοιχούν θα επιτρέπουν πλήρη πρόσβαση στα αποθετήριά σας. Ένα επιβεβαιωμένο κλειδί SSH μπορεί να χρησιμοποιηθεί για την υπογραφή των υποβολών (commits) σας. principal_desc=Αυτές οι αρχές πιστοποιητικών SSH συνδέονται με το λογαριασμό σας και επιτρέπουν την πλήρη πρόσβαση στα αποθετήριά σας. -gpg_desc=Αυτά τα δημόσια κλειδιά GPG συνδέονται με το λογαριασμό σας. Κρατήστε τα ιδιωτικά κλειδιά σας ασφαλή καθώς επιτρέπουν την επαλήθευση των υποβολών. -ssh_helper=Χρειάζεστε βοήθεια; Συμβουλευτείτε τον οδηγό του GitHub για να δημιουργήσετε τα δικά σας SSH κλειδιά ή να επιλύσετε κοινά προβλήματα που ίσως αντιμετωπίσετε με τη χρήση του SSH. +gpg_desc=Αυτά τα δημόσια κλειδιά GPG συσχετίζονται με το λογαριασμό σας και επιτρέπουν την επικύρωση των υποβολών (commits) σας. Κρατήστε τα ιδιωτικά κλειδιά σας ασφαλή, καθώς επιτρέπουν την υπογραφή των υποβολών (commits) εκ μέρους σας. +ssh_helper=Χρειάζεστε βοήθεια; Συμβουλευτείτε τον οδηγό του GitHub για να δημιουργήσετε τα δικά σας κλειδιά SSH ή να επιλύσετε κοινά προβλήματα που ίσως αντιμετωπίσετε με τη χρήση του SSH. gpg_helper=Χρειάζεστε βοήθεια; Συμβουλευτείτε τον οδηγό του GitHub για το GPG. add_new_key=Προσθήκη SSH Κλειδιού add_new_gpg_key=Προσθήκη GPG Κλειδιού -key_content_ssh_placeholder=Ξεκινάει με 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', ή 'sk-ssh-ed25519@openssh.com' -key_content_gpg_placeholder=Ξεκινά με '-----BEGIN PGP PUBLIC KEY BLOCK-----' +key_content_ssh_placeholder=Αρχίζει με «ssh-ed25519», «ssh-rsa», «ecdsa-sha2-nistp256», «ecdsa-sha2-nistp384», «ecdsa-sha2-nistp521», «sk-ecdsa-sha2-nistp256@openssh.com», ή «sk-ssh-ed25519@openssh.com» +key_content_gpg_placeholder=Αρχίζει με «-----BEGIN PGP PUBLIC KEY BLOCK-----» add_new_principal=Προσθήκη Αρχής (Principal) ssh_key_been_used=Αυτό το κλειδί SSH έχει ήδη προστεθεί στο διακομιστή. ssh_key_name_used=Υπάρχει ήδη ένα SSH κλειδί με το ίδιο όνομα στο λογαριασμό σας. @@ -761,8 +796,8 @@ gpg_token=Διακριτικό gpg_token_help=Μπορείτε να δημιουργήσετε μια υπογραφή χρησιμοποιώντας: gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=Θωρακισμένη υπογραφή GPG -key_signature_gpg_placeholder=Ξεκινά με '-----BEGIN PGP SIGNATURE-----' -verify_gpg_key_success=Το κλειδί GPG "%s" επαληθεύτηκε. +key_signature_gpg_placeholder=Αρχίζει με «-----BEGIN PGP SIGNATURE-----» +verify_gpg_key_success=Το κλειδί GPG «%s» επαληθεύτηκε. ssh_key_verified=Επαληθευμένο Κλειδί ssh_key_verified_long=Το κλειδί έχει επαληθευτεί με ένα διακριτικό και μπορεί να χρησιμοποιηθεί για να επαληθεύσει τα commits που ταιριάζουν με οποιεσδήποτε ενεργοποιημένες διευθύνσεις ηλεκτρονικού ταχυδρομείου για αυτόν το χρήστη. ssh_key_verify=Επαλήθευση @@ -771,16 +806,16 @@ ssh_token_required=Πρέπει να δώσετε μια υπογραφή για ssh_token=Διακριτικό ssh_token_help=Μπορείτε να δημιουργήσετε μια υπογραφή χρησιμοποιώντας: ssh_token_signature=Θωρακισμένη υπογραφή SSH -key_signature_ssh_placeholder=Ξεκινά με '-----BEGIN SSH SIGNATURE-----' -verify_ssh_key_success=Το κλειδί SSH "%s" επαληθεύτηκε. +key_signature_ssh_placeholder=Αρχίζει με «-----BEGIN SSH SIGNATURE-----» +verify_ssh_key_success=Το κλειδί SSH «%s» επαληθεύτηκε. subkeys=Υποκλειδιά key_id=ID Κλειδιού -key_name=Όνομα Κλειδιού +key_name=Όνομα κλειδιού key_content=Περιεχόμενο principal_content=Περιεχόμενο -add_key_success=Το κλειδί SSH "%s" προστέθηκε. -add_gpg_key_success=Το κλειδί GPG "%s" προστέθηκε. -add_principal_success=Προστέθηκε η αρχή πιστοποίησης SSH "%s". +add_key_success=Το κλειδί SSH «%s» προστέθηκε. +add_gpg_key_success=Το κλειδί GPG «%s» προστέθηκε. +add_principal_success=Προστέθηκε η αρχή πιστοποίησης SSH «%s». delete_key=Διαγραφή ssh_key_deletion=Διαγραφή Κλειδιού SSH gpg_key_deletion=Διαγραφή Κλειδιού GPG @@ -807,17 +842,17 @@ ssh_disabled=SSH Απενεργοποιημένο ssh_signonly=Το SSH είναι απενεργοποιημένο αυτή τη στιγμή, έτσι αυτά τα κλειδιά είναι μόνο για την επαλήθευση υπογραφής των υποβολών. ssh_externally_managed=Αυτό το κλειδί SSH διαχειρίζεται εξωτερικά για αυτόν το χρήστη manage_social=Διαχείριση Συσχετιζόμενων Λογαριασμών Κοινωνικών Δικτύων -social_desc=Αυτοί οι κοινωνικοί λογαριασμοί μπορούν να χρησιμοποιηθούν για να συνδεθείτε στο λογαριασμό σας. Βεβαιωθείτε ότι τους αναγνωρίζετε όλους. +social_desc=Αυτοί οι λογαριασμοί κοινωνικών δικτύων μπορούν να χρησιμοποιηθούν για να συνδεθείτε στο λογαριασμό σας. Βεβαιωθείτε ότι τους αναγνωρίζετε όλους. unbind=Αποσύνδεση -unbind_success=Ο κοινωνικός λογαριασμός έχει διαγραφεί επιτυχώς. +unbind_success=Ο λογαριασμός κοινωνικού δικτύου έχει διαγραφεί επιτυχώς. -manage_access_token=Διαχείριση Διακριτικών Πρόσβασης -generate_new_token=Δημιουργία Νέου Διακριτικού +manage_access_token=Διαχείριση διακριτικών πρόσβασης (tokens) +generate_new_token=Δημιουργία νέου διακριτικού (token) tokens_desc=Αυτά τα διακριτικά (tokens) παρέχουν πρόσβαση στο λογαριασμό σας μέσω του API του Forgejo. -token_name=Όνομα Διακριτικού +token_name=Όνομα διακριτικού generate_token=Δημιουργία Διακριτικού generate_token_success=Το νέο διακριτικό σας έχει δημιουργηθεί. Αντιγράψτε το τώρα καθώς δεν θα εμφανιστεί ξανά. -generate_token_name_duplicate=Το %s έχει ήδη χρησιμοποιηθεί ως όνομα εφαρμογής. Παρακαλούμε χρησιμοποιήστε ένα νέο. +generate_token_name_duplicate=Το %s χρησιμοποιείται ήδη ως όνομα εφαρμογής. Παρακαλούμε χρησιμοποιήστε ένα νέο. delete_token=Διαγραφή access_token_deletion=Διαγραφή Διακριτικού Πρόσβασης access_token_deletion_cancel_action=Άκυρο @@ -831,11 +866,11 @@ select_permissions=Επιλέξτε δικαιώματα permission_no_access=Καμία Πρόσβαση permission_read=Αναγνωσμένες permission_write=Ανάγνωση και Εγγραφή -access_token_desc=Τα επιλεγμένα δικαιώματα διακριτικών περιορίζουν την άδεια μόνο στις αντίστοιχες διαδρομές API. Διαβάστε την τεκμηρίωση για περισσότερες πληροφορίες. +access_token_desc=Τα επιλεγμένα δικαιώματα διακριτικών περιορίζουν την άδεια μόνο στις αντίστοιχες διαδρομές API. Διαβάστε το εγχειρίδιο για περισσότερες πληροφορίες. at_least_one_permission=Πρέπει να επιλέξετε τουλάχιστον ένα δικαίωμα για να δημιουργήσετε ένα διακριτικό permissions_list=Δικαιώματα: -manage_oauth2_applications=Διαχείριση Εφαρμογών Oauth2 +manage_oauth2_applications=Διαχείριση εφαρμογών OAuth2 edit_oauth2_application=Επεξεργασία Εφαρμογής Oauth2 oauth2_applications_desc=Οι εφαρμογές OAuth2 επιτρέπουν στην εξωτερική εφαρμογή σας την ασφαλή ταυτοποίηση των χρηστών σε αυτό το Forgejo. remove_oauth2_application=Αφαίρεση Εφαρμογής Oauth2 @@ -843,9 +878,9 @@ remove_oauth2_application_desc=Η αφαίρεση μιας εφαρμογής O remove_oauth2_application_success=Η εφαρμογή έχει διαγραφεί. create_oauth2_application=Δημιουργία νέας εφαρμογής OAuth2 create_oauth2_application_button=Δημιουργία Εφαρμογής -create_oauth2_application_success=Έχετε δημιουργήσει με επιτυχία μια νέα εφαρμογή OAuth2. -update_oauth2_application_success=Έχετε ενημερώσει με επιτυχία την εφαρμογή OAuth2. -oauth2_application_name=Όνομα Εφαρμογής +create_oauth2_application_success=Δημιουργήσατε επιτυχώς μια νέα εφαρμογή OAuth2. +update_oauth2_application_success=Ενημερώσατε την εφαρμογή OAuth2 επιτυχώς. +oauth2_application_name=Όνομα εφαρμογής oauth2_confidential_client=Εμπιστευτικός Πελάτης. Επιλέξτε το για εφαρμογές που διατηρούν το μυστικό κωδικό κρυφό, όπως πχ οι εφαρμογές ιστού. Μην επιλέγετε για εγγενείς εφαρμογές, συμπεριλαμβανομένων εφαρμογών επιφάνειας εργασίας και εφαρμογών για κινητά. oauth2_redirect_uris=URI Ανακατεύθυνσης. Χρησιμοποιήστε μια νέα γραμμή για κάθε URI. save_application=Αποθήκευση @@ -857,36 +892,36 @@ oauth2_client_secret_hint=Το μυστικό δε θα εμφανιστεί ξ oauth2_application_edit=Επεξεργασία oauth2_application_create_description=Οι εφαρμογές OAuth2 δίνει πρόσβαση στην εξωτερική εφαρμογή σας σε λογαριασμούς χρηστών σε αυτή την υπηρεσία. oauth2_application_remove_description=Αφαιρώντας μια εφαρμογή OAuth2 θα αποτραπεί η πρόσβαση αυτής, σε εξουσιοδοτημένους λογαριασμούς χρηστών σε αυτή την υπηρεσία. Συνέχεια; -oauth2_application_locked=Το Gitea κάνει προεγγραφή σε μερικές εφαρμογές OAuth2 κατά την εκκίνηση αν είναι ενεργοποιημένες στις ρυθμίσεις. Για την αποφυγή απροσδόκητης συμπεριφοράς, αυτές δεν μπορούν ούτε να επεξεργαστούν ούτε να καταργηθούν. Παρακαλούμε ανατρέξτε στην τεκμηρίωση OAuth2 για περισσότερες πληροφορίες. +oauth2_application_locked=Το Forgejo κάνει προεγγραφή σε μερικές εφαρμογές OAuth2 κατά την εκκίνηση αν είναι ενεργοποιημένες στις ρυθμίσεις. Για την αποφυγή απροσδόκητης συμπεριφοράς, αυτές δεν μπορούν ούτε να επεξεργαστούν ούτε να καταργηθούν. Παρακαλούμε ανατρέξτε στην τεκμηρίωση OAuth2 για περισσότερες πληροφορίες. -authorized_oauth2_applications=Εξουσιοδοτημένες Εφαρμογές OAuth2 -authorized_oauth2_applications_description=Έχετε χορηγήσει πρόσβαση στον προσωπικό σας λογαριασμό σε αυτές τις εφαρμογές τρίτων. Ανακαλέστε την πρόσβαση για εφαρμογές που δεν χρειάζεστε πλέον. +authorized_oauth2_applications=Εξουσιοδοτημένες εφαρμογές OAuth2 +authorized_oauth2_applications_description=Έχετε χορηγήσει πρόσβαση στον προσωπικό σας λογαριασμό σε αυτές τις εφαρμογές τρίτων. Παρακαλείσθε να ανακαλέσετε την πρόσβαση για εφαρμογές που δεν χρειάζεστε πλέον. revoke_key=Ανάκληση revoke_oauth2_grant=Ανάκληση Πρόσβασης -revoke_oauth2_grant_description=Η ανάκληση πρόσβασης για αυτή την εξωτερική εφαρμογή θα αποτρέψει αυτή την εφαρμογή από την πρόσβαση στα δεδομένα σας. Σίγουρα; +revoke_oauth2_grant_description=Αν ανακαλέσετε την πρόσβαση αυτής της ανεξάρτητης («third-party») εφαρμογής, θα ανακληθεί ταυτόχρονα και η πρόσβασή της στα δεδομένα σας. Είστε βέβαιοι; revoke_oauth2_grant_success=Η πρόσβαση ανακλήθηκε επιτυχώς. twofa_desc=Ο έλεγχος ταυτότητας δύο παραγόντων ενισχύει την ασφάλεια του λογαριασμού σας. twofa_recovery_tip=Αν χάσετε τη συσκευή σας, θα είστε σε θέση να χρησιμοποιήσετε ένα κλειδί ανάκτησης μιας χρήσης για να ανακτήσετε την πρόσβαση στο λογαριασμό σας. -twofa_is_enrolled=Ο λογαριασμός σας είναι εγγεγραμμένος σε έλεγχο ταυτότητας δύο παραγόντων. -twofa_not_enrolled=Ο λογαριασμός σας δεν είναι εγγεγραμμένος σε έλεγχο ταυτότητας δύο παραγόντων. -twofa_disable=Απενεργοποίηση Ταυτοποίησης Δύο Παραμέτρων +twofa_is_enrolled=Ο λογαριασμός σας είναι εγγεγραμμένος στην πιστοποίηση δύο παραγόντων. +twofa_not_enrolled=Ο λογαριασμός σας δεν είναι εγγεγραμμένος στην πιστοποιήση δύο παραγόντων. +twofa_disable=Απενεργοποίηση πιστοποίησης δύο παραγόντων twofa_scratch_token_regenerate=Αναδημιουργία Διακριτικού Μίας Χρήσης twofa_scratch_token_regenerated=Το κλειδί ανάκτησης μιας χρήσης είναι τώρα %s. Αποθηκεύστε το σε ασφαλές μέρος, καθώς δε θα εμφανιστεί ξανά. -twofa_enroll=Εγγραφή στην ταυτοποίηση δύο παραγόντων -twofa_disable_note=Μπορείτε να απενεργοποιήσετε την ταυτοποίηση δύο παραγόντων αν χρειαστεί. -twofa_disable_desc=Η απενεργοποίηση της ταυτοποίησης δύο παραγόντων θα καταστήσει τον λογαριασμό σας λιγότερο ασφαλή. Συνέχεια; +twofa_enroll=Εγγραφή στην πιστοποίηση δύο παραγόντων +twofa_disable_note=Αν χρειαστεί, θα μπορέσετε να απενεργοποιήσετε την πιστοποίηση δύο παραγόντων μεταγενέστερα. +twofa_disable_desc=Η απενεργοποίηση της πιστοποίησης δύο παραγόντων θα καταστήσει τον λογαριασμό σας λιγότερο ασφαλή. Είστε βέβαιοι; regenerate_scratch_token_desc=Αν χάσατε το διακριτικό μίας χρήσης σας ή το έχετε ήδη χρησιμοποιήσει για να συνδεθείτε μπορείτε να το επαναφέρετε εδώ. -twofa_disabled=Η ταυτοποίηση δύο παραγόντων έχει απενεργοποιηθεί. +twofa_disabled=Η πιστοποίηση δύο παραγόντων έχει απενεργοποιηθεί. scan_this_image=Σαρώστε αυτή την εικόνα με την εφαρμογή ταυτοποίησης: or_enter_secret=Ή εισάγετε το μυστικό: %s then_enter_passcode=Και εισάγετε τον κωδικό που εμφανίζεται στην εφαρμογή: passcode_invalid=Ο κωδικός είναι λάθος. Δοκιμάστε ξανά. -twofa_enrolled=Ο λογαριασμός σας έχει εγγραφεί σε ταυτοποίηση δύο παραγόντων. Αποθηκεύστε το διακριτικό μιας χρήσης (%s) σε ασφαλές μέρος καθώς εμφανίζεται μόνο μία φορά! +twofa_enrolled=Ο λογαριασμός σας έχει εγγραφεί σε ταυτοποίηση δύο παραγόντων. Αποθηκεύστε το διακριτικό μιας χρήσης (%s) σε ένα ασφαλές μέρος, επειδή δεν θα ξαναεμφανιστεί. twofa_failed_get_secret=Αποτυχία λήψης μυστικού. -webauthn_desc=Τα κλειδιά ασφαλείας είναι συσκευές που περιέχουν κρυπτογραφικά κλειδιά. Μπορούν να χρησιμοποιηθούν για έλεγχο ταυτότητας δύο παραγόντων. Τα κλειδιά ασφαλείας πρέπει να υποστηρίζουν το πρότυπο WebAuthn Authn Authenticator. -webauthn_register_key=Προσθήκη Κλειδιού Ασφαλείας +webauthn_desc=Τα κλειδιά ασφαλείας είναι συσκευές που περιέχουν κρυπτογραφικά κλειδιά. Μπορούν να χρησιμοποιηθούν για την πιστοποίηση δύο παραγόντων. Τα κλειδιά ασφαλείας πρέπει να συνάδουν με τις προδιαγραφές που ορίζει το πρότυπο WebAuthn. +webauthn_register_key=Προσθήκη κλειδιού ασφαλείας webauthn_nickname=Ψευδώνυμο webauthn_delete_key=Αφαίρεση Κλειδιού Ασφαλείας webauthn_delete_key_desc=Αν αφαιρέσετε ένα κλειδί ασφαλείας δεν μπορείτε πλέον να συνδεθείτε με αυτό. Συνέχεια; @@ -897,21 +932,21 @@ manage_account_links=Διαχείριση Συνδεδεμένων Λογαρι manage_account_links_desc=Αυτοί οι εξωτερικοί λογαριασμοί είναι συνδεδεμένοι στον Forgejo λογαριασμό σας. account_links_not_available=Προς το παρόν δεν υπάρχουν εξωτερικοί λογαριασμοί συνδεδεμένοι με τον λογαριασμό σας στο Forgejo. link_account=Σύνδεση Λογαριασμού -remove_account_link=Αφαίρεση Συνδεδεμένου Λογαριασμού +remove_account_link=Αφαίρεση συνδεδεμένου λογαριασμού remove_account_link_desc=Η κατάργηση ενός συνδεδεμένου λογαριασμού θα ανακαλέσει την πρόσβασή του στο λογαριασμό σας στο Forgejo. Συνέχεια; remove_account_link_success=Ο συνδεδεμένος λογαριασμός έχει αφαιρεθεί. hooks.desc=Προσθήκη webhooks που θα ενεργοποιούνται για όλα τα αποθετήρια που σας ανήκουν. orgs_none=Δεν είστε μέλος σε κάποιο οργανισμό. -repos_none=Δεν κατέχετε κάποιο αποθετήριο. +repos_none=Δεν σας ανήκει κανένα κάποιο αποθετήριο. delete_account=Διαγραφή Του Λογαριασμού Σας delete_prompt=Αυτή η ενέργεια θα διαγράψει μόνιμα το λογαριασμό σας. ΔΕΝ ΘΑ ΜΠΟΡΕΙ να επανέλθει. delete_with_all_comments=Ο λογαριασμός σας είναι νεότερος από %s. Για να αποφύγετε τα σχόλια φαντάσματα, όλα τα σχόλια σε ζητήματα/PR θα διαγραφούν από αυτόν. confirm_delete_account=Επιβεβαίωση Διαγραφής delete_account_title=Διαγραφή Λογαριασμού Χρήστη -delete_account_desc=Είστε βέβαιοι ότι θέλετε να διαγράψετε μόνιμα αυτό τον λογαριασμό χρήστη; +delete_account_desc=Είστε βέβαιοι ότι θέλετε να διαγράψετε μόνιμα αυτό τον λογαριασμό; email_notifications.enable=Ενεργοποίηση Ειδοποιήσεων Μέσω Email email_notifications.onmention=Email Μόνο κατά την Αναφορά @@ -923,9 +958,15 @@ visibility=Ορατότητα χρήστη visibility.public=Δημόσια visibility.public_tooltip=Ορατό σε όλους visibility.limited=Περιορισμένη -visibility.limited_tooltip=Ορατό μόνο στους ταυτοποιημένους χρήστες +visibility.limited_tooltip=Ορατό μόνο σε ταυτοποιημένους χρήστες visibility.private=Ιδιωτική visibility.private_tooltip=Ορατό μόνο στα μέλη των οργανισμών που συμμετέχετε +blocked_users_none = Δεν έχετε αποκλείσει κανέναν χρήστη. +blocked_since = Αποκλεισμένος από %s +user_unblock_success = Η άρση αποκλεισμού του χρήστη ήταν επιτυχής. +change_password = Αλλαγή κωδικού πρόσβασης +blocked_users = Αποκλεισμένοι χρήστες +user_block_success = Ο αποκλεισμός του χρήστη ήταν επιτυχής. [repo] new_repo_helper=Ένα αποθετήριο περιέχει όλα τα αρχεία έργου, συμπεριλαμβανομένου του ιστορικού εκδόσεων. Ήδη φιλοξενείται αλλού; Μετεγκατάσταση αποθετηρίου. @@ -940,7 +981,7 @@ template_helper=Μετατροπή σε πρότυπο αποθετήριο template_description=Τα πρότυπα αποθετήρια επιτρέπουν στους χρήστες να δημιουργήσουν νέα αποθετήρια με την ίδια δομή, αρχεία και προαιρετικές ρυθμίσεις. visibility=Ορατότητα visibility_description=Μόνο ο ιδιοκτήτης ή τα μέλη του οργανισμού εάν έχουν δικαιώματα, θα είναι σε θέση να το δουν. -visibility_helper=Αλλάξτε το αποθετήριο σε ιδιωτικό +visibility_helper=Αλλαγή ορατότητας σε «Ιδιωτικό» visibility_helper_forced=Ο διαχειριστής σας αναγκάζει τα νέα αποθετήρια να είναι ιδιωτικά. visibility_fork_helper=(Αλλάζοντας αυτό θα επηρεάσει όλα τα forks.) clone_helper=Χρειάζεστε βοήθεια για τη κλωνοποίηση; Επισκεφθείτε τη Βοήθεια. @@ -957,7 +998,7 @@ clone_in_vsc=Κλωνοποίηση στο VS Code download_zip=Λήψη ZIP download_tar=Λήψη TAR.GZ download_bundle=Κατεβάστε Το ΔΕΜΑ -generate_repo=Δημιουργία Αποθετηρίου +generate_repo=Δημιουργία αποθετηρίου generate_from=Δημιουργία Από repo_desc=Περιγραφή repo_desc_helper=Εισάγετε μια σύντομη περιγραφή (προαιρετικό) @@ -977,19 +1018,19 @@ trust_model_helper=Επιλέξτε ένα μοντέλο εμπιστοσύνη trust_model_helper_collaborator=Συνεργάτης: Εμπιστοσύνη υπογραφών από συνεργάτες trust_model_helper_committer=Υποβολέας: Εμπιστοσύνη των υπογραφών που ταιριάζουν με τους υποβολείς trust_model_helper_collaborator_committer=Συνεργάτης+Υποβολέας: Εμπιστοσύνη των υπογραφών από συνεργάτες που ταιριάζουν με τον υποβολέα -trust_model_helper_default=Προεπιλογή: Χρησιμοποιήστε το προεπιλεγμένο μοντέλο εμπιστοσύνης για αυτήν την εγκατάσταση -create_repo=Δημιουργία Αποθετηρίου +trust_model_helper_default=Προεπιλογή: Χρήση προεπιλεγμένου μοντέλου εμπιστοσύνης για αυτήν την εγκατάσταση +create_repo=Δημιουργία αποθετηρίου default_branch=Προεπιλεγμένος Κλάδος default_branch_label=προεπιλογή default_branch_helper=Ο προεπιλεγμένος κλάδος είναι ο βασικός κλάδος για pull requests και υποβολές κώδικα. mirror_prune=Καθαρισμός mirror_prune_desc=Αφαίρεση παρωχημένων αναφορών απομακρυσμένης-παρακολούθησης -mirror_interval=Διάστημα ανανέωσης ειδώλου (έγκυρες μονάδες ώρας είναι 'h', 'm', 's'). 0 για απενεργοποίηση του αυτόματου συγχρονισμού. (Ελάχιστο διάστημα: %s) +mirror_interval=Διάστημα ανανέωσης ειδώλου (έγκυρες μονάδες ώρας είναι "h", "m", "s"). 0 για απενεργοποίηση του αυτόματου συγχρονισμού. (Ελάχιστο διάστημα: %s) mirror_interval_invalid=Το χρονικό διάστημα του ειδώλου δεν είναι έγκυρο. mirror_sync_on_commit=Συγχρονισμός κατά την ώθηση mirror_address=Κλωνοποίηση Από Το URL mirror_address_desc=Τοποθετήστε όλα τα απαιτούμενα διαπιστευτήρια στην ενότητα Εξουσιοδότηση. -mirror_address_url_invalid=Η διεύθυνση URL που δόθηκε δεν είναι έγκυρη. Πρέπει να κάνετε escape όλα τα στοιχεία του url σωστά. +mirror_address_url_invalid=Η παρεχόμενη διεύθυνση URL δεν είναι έγκυρη. Πρέπει να κάνετε escape όλα τα στοιχεία του url σωστά. mirror_address_protocol_invalid=Η παρεχόμενη διεύθυνση URL δεν είναι έγκυρη. Μόνο οι τοποθεσίες http(s):// ή git:// μπορούν να χρησιμοποιηθούν για τη δημιουργία ειδώλου. mirror_lfs=Large File Storage (LFS) mirror_lfs_desc=Ενεργοποίηση αντικατοπτρισμού δεδομένων LFS. @@ -1025,9 +1066,9 @@ tree_path_not_found_branch=Η διαδρομή %[1]s δεν υπάρχει στ tree_path_not_found_tag=Η διαδρομή %[1]s δεν υπάρχει στην ετικέτα %[2]s transfer.accept=Αποδοχή Μεταφοράς -transfer.accept_desc=`Μεταφορά στο "%s"` +transfer.accept_desc=`Μεταφορά στο «%s»` transfer.reject=Απόρριψη Μεταφοράς -transfer.reject_desc=`Ακύρωση μεταφοράς σε "%s"` +transfer.reject_desc=`Ακύρωση μεταφοράς στο «%s»` transfer.no_permission_to_accept=Δεν έχετε άδεια να αποδεχτείτε αυτή τη μεταφορά. transfer.no_permission_to_reject=Δεν έχετε άδεια να απορρίψετε αυτή τη μεταφορά. @@ -1055,11 +1096,11 @@ archive.pull.nocomment=Αυτό το repo αρχειοθετήθηκε. Δεν form.reach_limit_of_creation_1=Έχετε ήδη συμπληρώσει το όριο του %d αποθετηρίου. form.reach_limit_of_creation_n=Έχετε ήδη συμπληρώσει το όριο των %d αποθετηρίων. -form.name_reserved=Το όνομα αποθετηρίου "%s" είναι δεσμευμένο. -form.name_pattern_not_allowed=Το μοτίβο "%s" δεν επιτρέπεται στο όνομα του αποθετηρίου. +form.name_reserved=Το όνομα αποθετηρίου «%s» είναι δεσμευμένο. +form.name_pattern_not_allowed=Το μοτίβο «%s» δεν επιτρέπεται στο όνομα του αποθετηρίου. need_auth=Εξουσιοδότηση -migrate_options=Επιλογές Μεταφοράς +migrate_options=Ρυθμίσεις μεταφοράς migrate_service=Υπηρεσία Μεταφοράς migrate_options_mirror_helper=Αυτό το αποθετήριο θα είναι είδωλο migrate_options_lfs=Μεταφορά αρχείων LFS @@ -1067,7 +1108,7 @@ 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=Αντικείμενα μεταφοράς migrate_items_wiki=Wiki migrate_items_milestones=Ορόσημα migrate_items_labels=Σήματα @@ -1076,21 +1117,21 @@ 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 'κλωνοποίησης' ενός υπάρχοντος αποθετηρίου +migrate.clone_address=Μεταφορά / Κλωνοποίηση από το URL +migrate.clone_address_desc=Το HTTP(S) ή το Git URL «κλωνοποίησης» ενός υπάρχοντος αποθετηρίου migrate.github_token_desc=Μπορείτε να βάλετε ένα ή περισσότερα διακριτικά εδώ, χωρισμένα με κόμμα, για να κάνετε τη μετεγκατάσταση πιο γρήγορα, λόγω του ορίου ρυθμού του GitHub API. ΠΡΟΣΟΧΗ: Η κατάχρηση αυτής της δυνατότητας μπορεί να παραβιάσει την πολιτική του παρόχου υπηρεσιών και να οδηγήσει σε αποκλεισμό του λογαριασμού σας. migrate.clone_local_path=ή μια διαδρομή τοπικού διακομιστή migrate.permission_denied=Δεν επιτρέπεται η εισαγωγή τοπικών αποθετηρίων. migrate.permission_denied_blocked=Δεν μπορείτε να εισαγάγετε από μη επιτρεπόμενους υπολογιστές, παρακαλούμε ζητήστε από τον διαχειριστή να ελέγξει τις ρυθμίσεις ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. -migrate.invalid_local_path=Η τοπική διαδρομή δεν είναι έγκυρη. Δεν υπάρχει ή δεν είναι φάκελος. +migrate.invalid_local_path=Η τοποθεσία αρχείου δεν είναι έγκυρη. Το αρχείο δεν υπάρχει ή δεν είναι φάκελος. migrate.invalid_lfs_endpoint=Η διεύθυνση LFS δεν είναι έγκυρο. migrate.failed=Η μεταφορά απέτυχε: %v migrate.migrate_items_options=Το Διακριτικό Πρόσβασης απαιτείται για τη μεταφορά πρόσθετων στοιχείων migrated_from=Μεταφέρθηκε από %[2]s migrated_from_fake=Μεταφέρθηκε από %[1]s -migrate.migrate=Μεταφορά Από %s -migrate.migrating=Γίνεται μεταφορά από %s... -migrate.migrating_failed=Η μετεγρατάσταση από %s απέτυχε. +migrate.migrate=Μεταφορά από το %s +migrate.migrating=Γίνεται μεταφορά από το %s... +migrate.migrating_failed=Η μεταφορά από το %s απέτυχε. migrate.migrating_failed.error=Αποτυχία μεταφοράς: %s migrate.migrating_failed_no_addr=Η μεταφορά απέτυχε. migrate.github.description=Μεταφορά δεδομένων από το github.com ή άλλους διακομιστές GitHub. @@ -1117,7 +1158,7 @@ generated_from=παράγονται από fork_from_self=Δεν μπορείτε να κάνετε fork σε ένα αποθετήριο που κατέχετε. fork_guest_user=Συνδεθείτε για να κάνετε fork αυτό το αποθετήριο. watch_guest_user=Συνδεθείτε για να παρακολουθήσετε αυτό το αποθετήριο. -star_guest_user=Συνδεθείτε για να προτιμήσετε αυτό το αποθετήριο. +star_guest_user=Συνδεθείτε για να δώσετε ένα αστέρι σε αυτό το αποθετήριο. unwatch=Μη Παρακολούθηση watch=Παρακολούθηση unstar=Όχι Αστέρι @@ -1132,7 +1173,7 @@ clone_this_repo=Κλωνοποίηση αυτού του αποθετηρίου cite_this_repo=Αναφορά σε αυτό το αποθετήριο create_new_repo_command=Δημιουργία νέου αποθετηρίου στη γραμμή εντολών push_exist_repo=Προώθηση ενός υπάρχοντος αποθετηρίου από τη γραμμή εντολών -empty_message=Αυτό το αποθετήριο δεν περιέχει τίποτα. +empty_message=Αυτό το αποθετήριο δεν έχει περιεχόμενο. broken_message=Τα δεδομένα Git που διέπουν αυτό το αποθετήριο δεν μπορούν να διαβαστούν. Επικοινωνήστε με το διαχειριστή ή διαγράψτε αυτό το αποθετήριο. code=Κώδικας @@ -1148,7 +1189,7 @@ issues=Ζητήματα pulls=Pull Requests project_board=Έργα packages=Πακέτα -actions=Δράσεις +actions=Actions labels=Σήματα org_labels_desc=Τα σήματα στο επίπεδο οργανισμού, που μπορούν να χρησιμοποιηθούν με όλα τα αποθετήρια κάτω από αυτόν τον οργανισμό org_labels_desc_manage=διαχείριση @@ -1181,8 +1222,8 @@ escape_control_characters=Escape unescape_control_characters=Unescape file_copy_permalink=Αντιγραφή Permalink view_git_blame=Προβολή Git Blame -video_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 'video'. -audio_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 'audio'. +video_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 «video». +audio_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 «audio». stored_lfs=Αποθηκεύτηκε με το Git LFS symbolic_link=Symbolic link executable_file=Εκτελέσιμο Αρχείο @@ -1214,14 +1255,14 @@ editor.must_be_on_a_branch=Πρέπει να βρίσκεστε σε έναν κ editor.fork_before_edit=Πρέπει να κάνετε fork αυτό το αποθετήριο για να κάνετε ή να προτείνετε αλλαγές σε αυτό το αρχείο. editor.delete_this_file=Διαγραφή Αρχείου editor.must_have_write_access=Πρέπει να έχετε πρόσβαση εγγραφής για να κάνετε ή να προτείνετε αλλαγές σε αυτό το αρχείο. -editor.file_delete_success=Το αρχείο "%s" έχει διαγραφεί. +editor.file_delete_success=Το αρχείο «%s» έχει διαγραφεί. editor.name_your_file=Ονομάστε το αρχείο σας… -editor.filename_help=Προσθέστε έναν φάκελο πληκτρολογώντας το όνομά του, ακολουθούμενο από μια κάθετο ('/'). Αφαιρέστε ένα φάκελο πληκτρολογώντας ένα backspace στην αρχή του πεδίου. +editor.filename_help=Προσθέστε έναν φάκελο πληκτρολογώντας μια κάθετο ('/') και το όνομα του φακέλου. Αφαιρέστε έναν φάκελο πληκτρολογώντας ένα backspace στην αρχή του πεδίου. editor.or=ή editor.cancel_lower=Ακύρωση editor.commit_signed_changes=Υποβολή Υπογεγραμμένων Αλλαγών editor.commit_changes=Υποβολή Αλλαγών -editor.add_tmpl=Προσθήκη '' +editor.add_tmpl=Προσθήκη «» editor.add=Προσθήκη %s editor.update=Ενημέρωση %s editor.delete=Διαγραφή %s @@ -1241,36 +1282,36 @@ editor.cancel=Ακύρωση editor.filename_cannot_be_empty=Το όνομα αρχείου δεν μπορεί να είναι κενό. editor.filename_is_invalid=Το όνομα αρχείου δεν είναι έγκυρο: "%s". editor.branch_does_not_exist=Ο κλάδος "%s" δεν υπάρχει σε αυτό το αποθετήριο. -editor.branch_already_exists=Ο κλάδος "%s" υπάρχει ήδη σε αυτό το αποθετήριο. -editor.directory_is_a_file=Το όνομα φακέλου "%s" χρησιμοποιείται ήδη ως όνομα αρχείου σε αυτό το αποθετήριο. -editor.file_is_a_symlink=`Το "%s" είναι συμβολικός σύνδεσμος. Οι συμβολικοί σύνδεσμοι δεν μπορούν να επεξεργαστούν στην ενσωματωμένη εφαρμογή` -editor.filename_is_a_directory=Το όνομα αρχείου "%s" χρησιμοποιείται ήδη ως όνομα φακέλου σε αυτό το αποθετήριο. -editor.file_editing_no_longer_exists=Το αρχείο "%s" που επεξεργάζεται, δεν υπάρχει πλέον σε αυτό το αποθετήριο. -editor.file_deleting_no_longer_exists=Το αρχείο "%s" που διαγράφεται, δεν υπάρχει πλέον σε αυτό το αποθετήριο. +editor.branch_already_exists=Ο κλάδος «%s» υπάρχει ήδη σε αυτό το αποθετήριο. +editor.directory_is_a_file=Το όνομα φακέλου «%s» χρησιμοποιείται ήδη ως όνομα αρχείου σε αυτό το αποθετήριο. +editor.file_is_a_symlink=`Το «%s» είναι συμβολικός σύνδεσμος. Οι συμβολικοί σύνδεσμοι δεν μπορούν να επεξεργαστούν στην ενσωματωμένη εφαρμογή` +editor.filename_is_a_directory=Το όνομα αρχείου «%s» χρησιμοποιείται ήδη ως όνομα φακέλου σε αυτό το αποθετήριο. +editor.file_editing_no_longer_exists=Το αρχείο «%s», το οποίο επεξεργάζεστε, δεν υπάρχει πλέον σε αυτό το αποθετήριο. +editor.file_deleting_no_longer_exists=Το αρχείο «%s», το οποίο διαγράφεται, δεν υπάρχει πλέον σε αυτό το αποθετήριο. editor.file_changed_while_editing=Τα περιεχόμενα του αρχείου άλλαξαν από τότε που ξεκίνησε η επεξεργασία. Κάντε κλικ εδώ για να τα δείτε ή Υποβολή Αλλαγών ξανά για να τα αντικαταστήσετε. -editor.file_already_exists=Ένα αρχείο με το όνομα "%s" υπάρχει ήδη σε αυτό το αποθετήριο. +editor.file_already_exists=Υπάρχει ήδη ένα αρχείο με το όνομα «%s» σε αυτό το αποθετήριο. editor.commit_empty_file_header=Υποβολή ενός κενού αρχείου editor.commit_empty_file_text=Το αρχείο που πρόκειται να υποβληθεί είναι κενό. Συνέχεια; editor.no_changes_to_show=Δεν υπάρχουν αλλαγές για εμφάνιση. -editor.fail_to_update_file=Αποτυχία ενημέρωσης/δημιουργίας του αρχείου "%s". +editor.fail_to_update_file=Αποτυχία ενημέρωσης/δημιουργίας του αρχείου «%s». editor.fail_to_update_file_summary=Μήνυμα Σφάλματος: editor.push_rejected_no_message=Η αλλαγή απορρίφθηκε από το διακομιστή χωρίς κάποιο μήνυμα. Παρακαλώ ελέγξτε τα Άγκιστρα Git. editor.push_rejected=Η αλλαγή απορρίφθηκε από τον διακομιστή. Παρακαλώ ελέγξτε τα Άγκιστρα Git. editor.push_rejected_summary=Μήνυμα Πλήρους Απόρριψης: editor.add_subdir=Προσθήκη φακέλου… -editor.unable_to_upload_files=Αποτυχία αποστολής αρχείων στο "%s" με το σφάλμα: %v -editor.upload_file_is_locked=Το αρχείο "%s" είναι κλειδωμένο από %s. -editor.upload_files_to_dir=`Μεταφόρτωση αρχείων στο "%s"` -editor.cannot_commit_to_protected_branch=Δεν είναι δυνατή η υποβολή στον προστατευόμενο κλάδο "%s". +editor.unable_to_upload_files=Αποτυχία αποστολής αρχείων στο «%s» με το σφάλμα: %v +editor.upload_file_is_locked=Το αρχείο «%s» είναι κλειδωμένο από %s. +editor.upload_files_to_dir=Μεταφόρτωση αρχείων στο «%s» +editor.cannot_commit_to_protected_branch=Δεν είναι δυνατή η υποβολή στον προστατευόμενο κλάδο «%s». editor.no_commit_to_branch=Δεν είναι δυνατή η απευθείας υποβολή στο κλάδο επειδή: editor.user_no_push_to_branch=Ο χρήστης δεν μπορεί να κάνει push στο κλάδο editor.require_signed_commit=Ο κλάδος απαιτεί υπογεγραμμένη υποβολή -editor.cherry_pick=Ανθολόγηση (cherry-pic) του %s στο: +editor.cherry_pick=Ανθολόγηση (cherry-pick) του %s σε: editor.revert=Απόσυρση του %s στο: commits.desc=Δείτε το ιστορικό αλλαγών του πηγαίου κώδικα. commits.commits=Υποβολές -commits.no_commits=Δεν υπάρχουν κοινές υποβολές. Τα "%s" και "%s" έχουν εντελώς διαφορετικές ιστορίες. +commits.no_commits=Δεν υπάρχουν κοινές υποβολές. Τα «%s» και «%s» έχουν εντελώς διαφορετικές ιστορίες. commits.nothing_to_compare=Αυτοί οι κλάδοι είναι όμοιοι. commits.search=Αναζήτηση υποβολών… commits.search.tooltip=Μπορείτε να προθέτετε τις λέξεις-κλειδιά με "author:", "committer:", "after:", ή "before:", π.χ. "επαναφορά author:Alice before:2019-01-13". @@ -1293,8 +1334,8 @@ commit.revert=Απόσυρση commit.revert-header=Απόσυρση: %s commit.revert-content=Επιλέξτε κλάδο για απόσυρση σε αυτό: commit.cherry-pick=Cherry-pick -commit.cherry-pick-header=Ανθολόγηση: %s -commit.cherry-pick-content=Επιλέξτε κλάδο για να κάνετε ανθολόγηση σε αυτό: +commit.cherry-pick-header=Cherry-pick: %s +commit.cherry-pick-content=Επιλέξτε τον κλάδο που θέλετε να ανθολογήσετε (cherry-pick): commitstatus.error=Σφάλμα commitstatus.failure=Αποτυχία @@ -1312,41 +1353,41 @@ projects.create=Δημιουργία Έργου projects.title=Τίτλος projects.new=Νέο έργο projects.new_subheader=Συντονισμός, παρακολούθηση και ενημέρωση της δουλειάς σας σε ένα μέρος, έτσι ώστε τα έργα να παραμένουν διαφανή και μέσα στο χρονοδιάγραμμα. -projects.create_success=Το έργο "%s" δημιουργήθηκε. +projects.create_success=Το έργο «%s» δημιουργήθηκε. projects.deletion=Διαγραφή Έργου projects.deletion_desc=Η διαγραφή ενός έργου το αφαιρεί από όλα τα σχετιζόμενα ζητήματα. Συνέχεια; projects.deletion_success=Το έργο έχει διαγραφεί. projects.edit=Επεξεργασία Έργων projects.edit_subheader=Τα Έργα οργανώνουν τα ζητήματα και παρακολουθούν τη πρόοδο τους. projects.modify=Ενημέρωση Έργου -projects.edit_success=Το έργο "%s" ενημερώθηκε. +projects.edit_success=Το έργο «%s» ενημερώθηκε. projects.type.none=Κανένα projects.type.basic_kanban=Βασικό Kanban -projects.type.bug_triage=Διαλογή Σφαλμάτων +projects.type.bug_triage=Διαλογή σφαλμάτων projects.template.desc=Πρότυπο έργου projects.template.desc_helper=Επιλέξτε ένα πρότυπο έργου για να ξεκινήσετε projects.type.uncategorized=Χωρίς Κατηγορία -projects.column.edit=Επεξεργασία Στήλης +projects.column.edit=Επεξεργασία στήλης projects.column.edit_title=Όνομα projects.column.new_title=Όνομα -projects.column.new_submit=Δημιουργία Στήλης -projects.column.new=Νέα Στήλη -projects.column.set_default=Ορισμός Προεπιλογής -projects.column.set_default_desc=Ορίστε αυτή τη στήλη ως προεπιλογή για ζητήματα και pull requests χωρίς κατηγορία -projects.column.unset_default=Αφαίρεση Προεπιλογής +projects.column.new_submit=Δημιουργία στήλης +projects.column.new=Νέα στήλη +projects.column.set_default=Ορισμός προεπιλογής +projects.column.set_default_desc=Ορίστε αυτή τη στήλη ως προεπιλεγμένη για ζητήματα και pull requests που δεν ανήκουν σε κάποια κατηγορία +projects.column.unset_default=Αφαίρεση προεπιλογής projects.column.unset_default_desc=Αφαίρεση της προεπιλογής αυτής της στήλης -projects.column.delete=Διαγραφή Στήλης -projects.column.deletion_desc=Η διαγραφή μιας στήλης έργου μετακινεί όλα τα συναφή ζητήματα σε 'Χωρίς Κατηγορία'. Συνέχεια; +projects.column.delete=Διαγραφή στήλης +projects.column.deletion_desc=Όταν διαγράφεται μία στήλη έργου, όλα τα ζητήματα που ανήκουν σε αυτή θα μείνουν «Χωρίς Κατηγορία». Συνέχεια; projects.column.color=Έγχρωμο projects.open=Άνοιγμα projects.close=Κλείσιμο projects.column.assigned_to=Ανατέθηκε σε -projects.card_type.desc=Προεπισκοπήσεις Καρτών -projects.card_type.images_and_text=Εικόνες και Κείμενο -projects.card_type.text_only=Μόνο Κείμενο +projects.card_type.desc=Προεπισκοπήσεις καρτών +projects.card_type.images_and_text=Εικόνες και κείμενο +projects.card_type.text_only=Μόνο κείμενο issues.desc=Οργανώστε αναφορές σφαλμάτων, εργασίες και ορόσημα. -issues.filter_assignees=Φίλτρο Αποδέκτη +issues.filter_assignees=Φίλτρο υπεύθυνων issues.filter_milestones=Φίλτρο Ορόσημου issues.filter_projects=Φίλτρο Έργου issues.filter_labels=Φίλτρο Σημάτων @@ -1367,9 +1408,9 @@ issues.new.no_milestone=Χωρίς Ορόσημο issues.new.clear_milestone=Καθαρισμός ορόσημου issues.new.open_milestone=Ανοιχτά Ορόσημα issues.new.closed_milestone=Κλειστά Ορόσημα -issues.new.assignees=Αποδέκτες -issues.new.clear_assignees=Εκκαθάριση αποδεκτών -issues.new.no_assignees=Χωρίς Αποδέκτη +issues.new.assignees=Υπεύθυνοι +issues.new.clear_assignees=Εκκαθάριση υπεύθυνων +issues.new.no_assignees=Χωρίς υπεύθυνους issues.new.no_reviewers=Δεν υπάρχουν εξεταστές issues.choose.get_started=Ας Αρχίσουμε issues.choose.open_external_link=Άνοιγμα @@ -1385,10 +1426,10 @@ issues.new_label_placeholder=Όνομα σήματος issues.new_label_desc_placeholder=Περιγραφή issues.create_label=Δημιουργία Σήματος issues.label_templates.title=Χρήση ενός προκαθορισμένου συνόλου σημάτων -issues.label_templates.info=Δεν υπάρχουν σήματα ακόμα. Δημιουργήστε ένα σήμα με το 'Νέο Σήμα' ή χρησιμοποιήστε ένα σύνολο προκαθορισμένων σημάτων: +issues.label_templates.info=Δεν υπάρχουν σήματα ακόμα. Δημιουργήστε ένα σήμα με το κουμπί «Νέο Σήμα» ή χρησιμοποιήστε ένα σετ προκαθορισμένων σημάτων: issues.label_templates.helper=Επιλέξτε ένα σύνολο σημάτων issues.label_templates.use=Χρήση Συνόλου Σημάτων -issues.label_templates.fail_to_load_file=Αποτυχία φόρτωσης των προτύπων σημάτων από το αρχείο "%s": %v +issues.label_templates.fail_to_load_file=Αποτυχία φόρτωσης των προτύπων σημάτων από το αρχείο «%s»: %v issues.add_label=πρόσθεσε τη σήμανση %s %s issues.add_labels=πρόσθεσε τα σήματα %s %s issues.remove_label=αφαίρεσε το σήμα %s %s @@ -1403,7 +1444,7 @@ issues.remove_project_at=`το αφαίρεσε από το %s έργο % issues.deleted_milestone=`(διαγράφηκε)` issues.deleted_project=`(διαγράφηκε)` issues.self_assign_at=`ανέθεσε στον εαυτό του το %s` -issues.add_assignee_at=`ανατέθηκε από %s %s` +issues.add_assignee_at=`ανατέθηκε ως υπεύθυνος από τον/την %s %s` issues.remove_assignee_at=`αφαιρέθηκε η ανάθεση από %s %s` issues.remove_self_assignment=`αφαίρεσαν την ανάθεση τους %s` issues.change_title_at=`άλλαξε το τίτλο από %s σε %s %s` @@ -1425,7 +1466,7 @@ issues.filter_project_all=Όλα τα έργα issues.filter_project_none=Χωρίς έργα issues.filter_assignee=Αποδέκτης issues.filter_assginee_no_select=Όλοι οι αποδέκτες -issues.filter_assginee_no_assignee=Κανένας Αποδέκτης +issues.filter_assginee_no_assignee=Κανένας αποδέκτης issues.filter_poster=Συγγραφέας issues.filter_poster_no_select=Όλοι οι συγγραφείς issues.filter_type=Τύπος @@ -1440,15 +1481,15 @@ issues.filter_sort.latest=Νεότερα issues.filter_sort.oldest=Παλαιότερα issues.filter_sort.recentupdate=Ενημερώθηκαν πρόσφατα issues.filter_sort.leastupdate=Ενημερώθηκαν παλαιότερα -issues.filter_sort.mostcomment=Περισσότερο σχολιασμένα -issues.filter_sort.leastcomment=Λιγότερο σχολιασμένα -issues.filter_sort.nearduedate=Πλησιέστερη παράδοση -issues.filter_sort.farduedate=Απώτερη παράδοση +issues.filter_sort.mostcomment=Περισσότερα σχόλια +issues.filter_sort.leastcomment=Λιγότερα σχόλια +issues.filter_sort.nearduedate=Πλησιέστερη ημερομηνία παράδοσης +issues.filter_sort.farduedate=Απώτερη ημερομηνία παράδοσης issues.filter_sort.moststars=Περισσότερα αστέρια issues.filter_sort.feweststars=Λιγότερα αστέρια issues.filter_sort.mostforks=Περισσότερα forks issues.filter_sort.fewestforks=Λιγότερα forks -issues.keyword_search_unavailable=Η αναζήτηση μέσω λέξεων κλειδιών δεν είναι διαθέσιμη. Παρακαλώ επικοινωνήστε με το διαχειριστή. +issues.keyword_search_unavailable=Η αναζήτηση με λέξεις κλειδιά δεν είναι διαθέσιμη. Παρακαλώ επικοινωνήστε με τον διαχειριστή. issues.action_open=Άνοιγμα issues.action_close=Κλείσιμο issues.action_label=Σήμα @@ -1472,7 +1513,7 @@ issues.draft_title=Προσχέδιο issues.num_comments_1=%d σχόλιο issues.num_comments=%d σχόλια issues.commented_at=`σχολίασε %s` -issues.delete_comment_confirm=Θέλετε σίγουρα να διαγράψετε αυτό το σχόλιο; +issues.delete_comment_confirm=Είστε βέβαιοι πως θέλετε να διαγράψετε αυτό το σχόλιο; issues.context.copy_link=Αντιγραφή Συνδέσμου issues.context.quote_reply=Παράθεση Απάντησης issues.context.reference_issue=Αναφορά σε νέο ζήτημα @@ -1503,7 +1544,7 @@ issues.role.owner_helper=Αυτός ο χρήστης είναι ο ιδιοκτ issues.role.member=Μέλος issues.role.member_helper=Αυτός ο χρήστης είναι μέλος του οργανισμού που κατέχει αυτό το αποθετήριο. issues.role.collaborator=Συνεργάτης -issues.role.collaborator_helper=Αυτός ο χρήστης έχει προσκληθεί να συνεργαστεί στο αποθετήριο. +issues.role.collaborator_helper=Ο χρήστης έλαβε πρόσκληση συνεργασίας στο αποθετήριο. issues.role.first_time_contributor=Συντελεστής για πρώτη φορά issues.role.first_time_contributor_helper=Αυτή είναι η πρώτη συνεισφορά αυτού του χρήστη στο αποθετήριο. issues.role.contributor=Συντελεστής @@ -1533,106 +1574,106 @@ issues.label_edit=Επεξεργασία issues.label_delete=Διαγραφή issues.label_modify=Επεξεργασία Σήματος issues.label_deletion=Διαγραφή Σήματος -issues.label_deletion_desc=Η διαγραφή ενός σήματος την αφαιρεί από όλα τα ζητήματα. Συνέχεια +issues.label_deletion_desc=Η διαγραφή ενός σήματος θα το αφαιρέσει από όλα τα ζητήματα. Να γίνει συνέχεια; issues.label_deletion_success=Το σήμα έχει διαγραφεί. issues.label.filter_sort.alphabetically=Αλφαβητικά issues.label.filter_sort.reverse_alphabetically=Αντίστροφα αλφαβητικά issues.label.filter_sort.by_size=Μικρότερο μέγεθος issues.label.filter_sort.reverse_by_size=Μεγαλύτερο μέγεθος issues.num_participants=%d Συμμετέχοντες -issues.attachment.open_tab=`Κάντε κλικ για να δείτε το "%s" σε μια νέα καρτέλα` -issues.attachment.download=`Κάντε κλικ για να λάβετε το "%s"` +issues.attachment.open_tab=`Πατήστε εδώ για να ανοίξετε το «%s» σε μια νέα καρτέλα` +issues.attachment.download=`Πατήστε εδώ για να κατεβάσετε το «%s»` issues.subscribe=Εγγραφή issues.unsubscribe=Διαγραφή issues.unpin_issue=Άφεση Ζητήματος -issues.max_pinned=Δεν μπορείτε να διατηρήσετε περισσότερα ζητήματα -issues.pin_comment=διατήρησε αυτό %s -issues.unpin_comment=άφησε αυτό %s +issues.max_pinned=Δεν μπορείτε να καρφιτσώσετε περισσότερα ζητήματα +issues.pin_comment=καρφίτσωσε το %s +issues.unpin_comment=ξεκαρφίτσωσε το %s issues.lock=Κλείδωμα συνομιλίας issues.unlock=Ξεκλείδωμα συνομιλίας issues.lock.unknown_reason=Αδυναμία κλειδώματος ενός ζητήματος με άγνωστο λόγο. issues.lock_duplicate=Ένα ζήτημα δεν μπορεί να κλειδωθεί δύο φορές. issues.unlock_error=Δεν είναι δυνατό να ξεκλειδώσετε ένα ζήτημα που δεν είναι κλειδωμένο. -issues.lock_with_reason=κλειδωμένο ως %s και περιορισμένη συνομιλία με συνεργάτες %s -issues.lock_no_reason=κλειδωμένη και περιορισμένη συνομιλία με συνεργάτες %s -issues.unlock_comment=ξεκλείδωσε αυτή τη συνομιλία %s +issues.lock_with_reason=κλείδωσε το ζήτημα επειδή είναι %s και περιόρισε την συνομιλία σε συνεργάτες %s +issues.lock_no_reason=: κλείδωσε το ζήτημα και την περιόρισε σε συνεργάτες %s +issues.unlock_comment=: ξεκλείδωσε αυτή τη συνομιλία %s issues.lock_confirm=Κλείδωμα issues.unlock_confirm=Ξεκλείδωμα -issues.lock.notice_1=- Άλλοι χρήστες δεν μπορούν να προσθέσουν νέα σχόλια σε αυτό το ζήτημα. -issues.lock.notice_2=- Εσείς και άλλοι συνεργάτες με πρόσβαση σε αυτό το αποθετήριο μπορούν ακόμα να αφήσουν σχόλια που μπορούν να δουν άλλοι. -issues.lock.notice_3=- Μπορείτε πάντα να ξεκλειδώσετε αυτό το ζήτημα ξανά στο μέλλον. -issues.unlock.notice_1=- Όλοι θα ήταν σε θέση να σχολιάσουν αυτό το ζήτημα για άλλη μια φορά. -issues.unlock.notice_2=- Μπορείτε πάντα να κλειδώσετε αυτό το θέμα ξανά στο μέλλον. -issues.lock.reason=Λόγος κλειδώματος +issues.lock.notice_1=- Άλλοι χρήστες δεν μπορούν να αφήσουν νέα σχόλια σε αυτό το ζήτημα. +issues.lock.notice_2=- Εσείς και άλλοι συνεργάτες που έχουν πρόσβαση στο αποθετήριο θα μπορείτε ακόμα να αφήσετε σχόλια που θα είναι ορατά σε άλλους. +issues.lock.notice_3=- Θα μπορείτε να ξεκλειδώσετε αυτό το ζήτημα πιο μετά. +issues.unlock.notice_1=- Όλοι θα βρίσκονται πάλι σε θέση να αφήσουν σχόλιο σε αυτό το ζήτημα. +issues.unlock.notice_2=- Θα μπορείτε πάντα να ξανακλειδώσετε αυτό το ζήτημα πιο μετά. +issues.lock.reason=Αιτία κλειδώματος issues.lock.title=Κλείδωμα συνομιλίας σε αυτό το ζήτημα. issues.unlock.title=Ξεκλείδωμα συνομιλίας σε αυτό το ζήτημα. -issues.comment_on_locked=Δεν μπορείτε να σχολιάσετε ένα κλειδωμένο ζήτημα. +issues.comment_on_locked=Δεν μπορείτε να σχολιάσετε σε ένα κλειδωμένο ζήτημα. issues.delete=Διαγραφή -issues.delete.title=Διαγραφή αυτού του ζητήματος; -issues.delete.text=Θέλετε πραγματικά να διαγράψετε αυτό το ζήτημα; (Αυτό θα σβήσει οριστικά όλο το περιεχόμενο του. Εξετάστε αν θέλετε να το κλείσετε, αν σκοπεύεται να το αρχειοθετήσετε) -issues.tracker=Καταγραφή Χρόνου -issues.start_tracking_short=Εκκίνηση Χρονομέτρου -issues.start_tracking=Εκκίνηση Καταγραφής Χρόνου +issues.delete.title=Να διαγραφεί αυτό το ζήτημα; +issues.delete.text=Είστε βέβαιοι πως θέλετε να διαγράψετε αυτό το ζήτημα; (Αυτό θα σβήσει οριστικά όλο το περιεχόμενο του. Εξετάστε αν μήπως θέλετε να κλείσετε το ζήτημα, αν θα θέλατε να το κρατήσετε.) +issues.tracker=Καταγραφή χρόνου +issues.start_tracking_short=Εκκίνηση χρονόμετρου +issues.start_tracking=Εκκίνηση καταγραφής χρόνου issues.start_tracking_history=`ξεκίνησε να εργάζεται %s` -issues.tracker_auto_close=Το χρονόμετρο θα σταματήσει αυτόματα όταν κλείσει αυτό το ζήτημα -issues.tracking_already_started=`Έχετε ήδη ξεκινήσει την καταγραφή του χρόνου σε ένα άλλο ζήτημα!` -issues.stop_tracking=Διακοπή Χρονομέτρου +issues.tracker_auto_close=Το χρονόμετρο θα σταματήσει αυτόματα όταν αυτό το ζήτημα κλείσει +issues.tracking_already_started=`Έχετε ήδη ξεκινήσει να καταγράφετε τον χρόνο σας σε ένα άλλο ζήτημα!` +issues.stop_tracking=Διακοπή χρονόμετρου issues.stop_tracking_history=`σταμάτησε να εργάζεται %s` issues.cancel_tracking=Απόρριψη -issues.cancel_tracking_history=`ακύρωσε τη παρακολούθηση χρόνου %s` -issues.add_time=Χειροκίνητη Προσθήκη Ώρας -issues.del_time=Διαγραφή αυτού του αρχείου χρόνου -issues.add_time_short=Προσθήκη Χρόνου +issues.cancel_tracking_history=`ακύρωσε τη καταγραφή χρόνου %s` +issues.add_time=Χειροκίνητη προσθήκη ώρας +issues.del_time=Διαγραφή αυτής της καταγραφής χρόνου +issues.add_time_short=Προσθήκη χρόνου issues.add_time_cancel=Ακύρωση issues.add_time_history=`πρόσθεσε χρόνο που δαπανήθηκε %s` -issues.del_time_history=`διέγραψε το χρόνο που δαπανήθηκε %s` -issues.add_time_hours=Ώρες -issues.add_time_minutes=Λεπτά +issues.del_time_history=`διέγραψε χρόνο που δαπανήθηκε %s` +issues.add_time_hours=ώρες +issues.add_time_minutes=λεπτά issues.add_time_sum_to_small=Δεν εισήχθη χρόνος. -issues.time_spent_total=Συνολική Δαπάνη Χρόνου -issues.time_spent_from_all_authors=`Συνολική Δαπάνη Χρόνου: %s` -issues.due_date=Ημερομηνία Παράδοσης -issues.invalid_due_date_format=Η μορφή της ημερομηνίας παράδοσης πρέπει να είναι 'yyyy-mm-dd'. -issues.error_modifying_due_date=Αποτυχία τροποποίησης της ημερομηνίας παράδοσης. -issues.error_removing_due_date=Αποτυχία κατάργησης της ημερομηνίας παράδοσης. +issues.time_spent_total=Συνολική δαπάνη χρόνου +issues.time_spent_from_all_authors=`Συνολική δαπάνη χρόνου: %s` +issues.due_date=Ημερομηνία παράδοσης +issues.invalid_due_date_format=Η ημερομηνίας παράδοσης πρέπει να έχει την μορφή «εεεε-μμ-ηη». +issues.error_modifying_due_date=Προέκυψε σφάλμα κατά την αλλαγή ημερομηνίας παράδοσης. +issues.error_removing_due_date=Προέκυψε σφάλμα κατά την κατάργηση ημερομηνίας παράδοσης. issues.push_commit_1=πρόσθεσε %d υποβολή %s issues.push_commits_n=πρόσθεσε %d υποβολές %s -issues.force_push_codes=`force-pushed %[1]s από το %[2]s στο %[4]s %[6]s` +issues.force_push_codes=`έκανε force-push %[1]s από το %[2]s στο %[4]s %[6]s` issues.force_push_compare=Σύγκριση issues.due_date_form=εεεε-μμ-ηη issues.due_date_form_add=Προσθήκη ημερομηνίας παράδοσης -issues.due_date_form_edit=Επεξεργασία -issues.due_date_form_remove=Διαγραφή -issues.due_date_not_writer=Χρειάζεστε πρόσβαση εγγραφής στο αποθετήριο για να ενημερώσετε την ημερομηνία λήξης ενός προβλήματος. +issues.due_date_form_edit=Αλλαγή +issues.due_date_form_remove=Αφαίρεση +issues.due_date_not_writer=Χρειάζεστε πρόσβαση εγγραφής για να τροποποιήσετε την ημερομηνία παράδοσης του ζητήματος. issues.due_date_not_set=Δεν ορίστηκε ημερομηνία παράδοσης. -issues.due_date_added=πρόσθεσε την ημερομηνία παράδοσης %s %s +issues.due_date_added=όρισε την ημερομηνία παράδοσης %s %s issues.due_date_modified=τροποποίησε την ημερομηνία παράδοσης από %[2]s σε %[1]s %[3]s issues.due_date_remove=αφαίρεσε την ημερομηνία παράδοσης %s %s issues.due_date_overdue=Εκπρόθεσμο -issues.due_date_invalid=Η ημερομηνία παράδοσης δεν είναι έγκυρη ή εκτός εύρους. Παρακαλούμε χρησιμοποιήστε τη μορφή 'εεεε-μμ-ηη'. +issues.due_date_invalid=Η ημερομηνία παράδοσης δεν είναι έγκυρη ή εκτός εύρους. Παρακαλούμε χρησιμοποιήστε τη μορφή «εεεε-μμ-ηη». issues.dependency.title=Εξαρτήσεις issues.dependency.issue_no_dependencies=Δεν έχουν οριστεί εξαρτήσεις. issues.dependency.pr_no_dependencies=Δεν έχουν οριστεί εξαρτήσεις. -issues.dependency.no_permission_1=Δεν έχετε άδεια για ανάγνωση %d εξάρτηση -issues.dependency.no_permission_n=Δεν έχετε άδεια να ανάγνωση %d εξαρτήσεων -issues.dependency.no_permission.can_remove=Δεν έχετε άδεια για ανάγνωση αυτής της εξάρτησης, αλλά μπορείτε να τη καταργήσετε +issues.dependency.no_permission_1=Δεν έχετε άδεια ανάγνωσης για %d εξάρτηση +issues.dependency.no_permission_n=Δεν έχετε άδεια ανάγνωσης για %d εξαρτήσεις +issues.dependency.no_permission.can_remove=Δεν έχετε άδεια για ανάγνωση αυτής της εξάρτησης, αλλά μπορείτε να την καταργήσετε issues.dependency.add=Προσθήκη εξάρτησης… issues.dependency.cancel=Ακύρωση issues.dependency.remove=Διαγραφή issues.dependency.remove_info=Αφαίρεση αυτής της εξάρτησης issues.dependency.added_dependency=`πρόσθεσε μια νέα εξάρτηση %s` issues.dependency.removed_dependency=`αφαίρεσε μια εξάρτηση %s` -issues.dependency.pr_closing_blockedby=Το κλείσιμο αυτού pull request εμποδίζεται από τα ακόλουθα ζητήματα +issues.dependency.pr_closing_blockedby=Το κλείσιμο αυτού του pull request εμποδίζεται από τα ακόλουθα ζητήματα issues.dependency.issue_closing_blockedby=Το κλείσιμο αυτού του ζητήματος εμποδίζεται από τα ακόλουθα ζητήματα issues.dependency.issue_close_blocks=Αυτό το ζήτημα εμποδίζει το κλείσιμο των ακόλουθων ζητημάτων issues.dependency.pr_close_blocks=Αυτό το pull request εμποδίζει το κλείσιμο των ακόλουθων ζητημάτων -issues.dependency.issue_close_blocked=Πρέπει να κλείσετε όλα τα ζητήματα που εμποδίζουν αυτό το ζήτημα πριν το κλείσετε. -issues.dependency.issue_batch_close_blocked=Δεν είναι δυνατό το ομαδικό κλείσιμο ζητημάτων που επιλέξατε, επειδή το ζήτημα #%d ακόμα έχει ανοιχτές εξαρτήσεις +issues.dependency.issue_close_blocked=Για να κλείσετε το ζήτημα αυτό, πρέπει πρώτα να κλείσετε όλα τα ζητήματα που το εμποδίζουν. +issues.dependency.issue_batch_close_blocked=Δεν είναι δυνατό το κλείσιμο όλων των ζητημάτων που επιλέξατε, επειδή το ζήτημα #%d έχει ακόμα εξαρτήσεις που δεν έχουν κλειστεί issues.dependency.pr_close_blocked=Πρέπει να κλείσετε όλα τα ζητήματα που εμποδίζουν αυτό το pull request για να μπορέσετε να το συγχωνεύσετε. issues.dependency.blocks_short=Μπλοκάρει issues.dependency.blocked_by_short=Εξαρτάται από -issues.dependency.remove_header=Αφαίρεση Εξάρτησης -issues.dependency.issue_remove_text=Αυτό θα αφαιρέσει την εξάρτηση από αυτό το ζήτημα. Συνέχεια; +issues.dependency.remove_header=Αφαίρεση εξάρτησης +issues.dependency.issue_remove_text=Αυτό θα αφαιρέσει την εξάρτηση από αυτό το ζήτημα. Να γίνει συνέχεια; issues.dependency.pr_remove_text=Αυτό θα αφαιρέσει την εξάρτηση από αυτό το pull request. Συνέχεια; issues.dependency.setting=Ενεργοποίηση Εξαρτήσεων Για Ζητήματα και Pull Requests issues.dependency.add_error_same_issue=Δεν μπορείτε να εξαρτάτε ένα ζήτημα από τον εαυτό του. @@ -1645,17 +1686,17 @@ issues.review.self.approval=Δεν μπορείτε να εγκρίνετε το issues.review.self.rejection=Δεν μπορείτε να ζητήσετε αλλαγές στο δικό σας pull request. issues.review.approve=ενέκρινε αυτές τις αλλαγές %s issues.review.comment=αξιολόγησε %s -issues.review.dismissed=απέρριψε την αξιολόγηση %s %s +issues.review.dismissed=απέρριψε την αξιολόγηση του/της %s %s issues.review.dismissed_label=Απορρίφθηκε issues.review.left_comment=άφησε ένα σχόλιο issues.review.content.empty=Θα πρέπει να αφήσετε ένα σχόλιο υποδεικνύοντας την ζητούμενη αλλαγή(ές). issues.review.reject=ζήτησε αλλαγές %s -issues.review.wait=ζητήθηκε για αναθεώρηση %s -issues.review.add_review_request=ζητήθηκε αναθεώρηση από %s %s -issues.review.remove_review_request=αφαιρέθηκε αίτηση αναθεώρησης για %s %s -issues.review.remove_review_request_self=αρνήθηκε να αναθεωρήσει %s +issues.review.wait=ζητήθηκε για έλεγχο %s +issues.review.add_review_request=ζητήθηκε έλεγχος από %s %s +issues.review.remove_review_request=αφαιρέθηκε αίτημα ελέγχου για %s %s +issues.review.remove_review_request_self=αρνήθηκε να ελέγξει %s issues.review.pending=Εκκρεμεί -issues.review.pending.tooltip=Αυτό το σχόλιο προς το παρόν δεν είναι ορατό από άλλους χρήστες. Για να υποβάλετε τα σχόλιά σας, επιλέξτε "%s" -> "%s/%s/%s" στη κορυφή της σελίδας. +issues.review.pending.tooltip=Αυτό το σχόλιο προς το παρόν δεν είναι ορατό σε άλλους χρήστες. Για να υποβάλετε τα σχόλιά σας, επιλέξτε "%s" -> "%s/%s/%s" στη κορυφή της σελίδας. issues.review.review=Αξιολόγηση issues.review.reviewers=Εξεταστές issues.review.outdated=Παρωχημένο @@ -1712,9 +1753,9 @@ pulls.nothing_to_compare=Αυτοί οι κλάδοι είναι όμοιοι. pulls.nothing_to_compare_and_allow_empty_pr=Αυτοί οι κλάδοι είναι ίσοι. Αυτό το PR θα είναι κενό. pulls.has_pull_request=`Υπάρχει ήδη pull request μεταξύ αυτών των κλάδων: %[2]s#%[3]d` pulls.create=Δημιουργία Pull Request -pulls.title_desc=θέλει να συγχωνεύσει %[1]d υποβολές από %[2]s σε %[3]s -pulls.merged_title_desc=συγχώνευσε %[1]d υποβολές από %[2]s σε %[3]s %[4]s -pulls.change_target_branch_at=`άλλαξε τον κλάδο στόχο από %s σε %s %s` +pulls.title_desc_few=θέλει να συγχωνεύσει %[1]d υποβολές από %[2]s σε %[3]s +pulls.merged_title_desc_few=συγχώνευσε %[1]d υποβολές από %[2]s σε %[3]s %[4]s +pulls.change_target_branch_at=`άλλαξε τον κλάδο προορισμού από %s σε %s %s` pulls.tab_conversation=Συζήτηση pulls.tab_commits=Υποβολές pulls.tab_files=Αρχεία Με Αλλαγές @@ -1733,15 +1774,15 @@ pulls.add_prefix=Προσθήκη %s προθέματος pulls.remove_prefix=Αφαίρεση %s προθέματος pulls.data_broken=Αυτό το pull request είναι κατεστραμμένο λόγω των πληροφοριών του fork που λείπουν. pulls.files_conflicted=Αυτό το pull request περιέχει αλλαγές που συγκρούονται με το κλάδο προορισμού. -pulls.is_checking=Ο έλεγχος συγκρούσεων κατά την συγχώνευση είναι σε εξέλιξη. Δοκιμάστε ξανά σε λίγα λεπτά. +pulls.is_checking=Ο έλεγχος συγκρούσεων κατά την συγχώνευση βρίσκεται σε εξέλιξη. Δοκιμάστε ξανά σε λίγα λεπτά. pulls.is_ancestor=Αυτός ο κλάδος περιλαμβάνεται ήδη στον κλάδο προορισμού. Δεν υπάρχει τίποτα για συγχώνευση. -pulls.is_empty=Οι αλλαγές σε αυτόν τον κλάδο είναι ήδη στον κλάδο προορισμού. Θα είναι μια κενή υποβολή. +pulls.is_empty=Οι αλλαγές σε αυτόν τον κλάδο βρίσκονται ήδη στον κλάδο προορισμού. Θα είναι μια κενή υποβολή. pulls.required_status_check_failed=Ορισμένοι απαιτούμενοι έλεγχοι δεν ήταν επιτυχείς. pulls.required_status_check_missing=Λείπουν ορισμένοι απαιτούμενοι έλεγχοι. pulls.required_status_check_administrator=Ως διαχειριστής, μπορείτε ακόμα να συγχωνεύσετε αυτό το pull request. pulls.blocked_by_approvals=Το pull request δεν έχει ακόμα αρκετές εγκρίσεις. Δόθηκαν %d από %d εγκρίσεις. -pulls.blocked_by_rejection=Αυτό το Pull Request έχει αλλαγές που ζητούνται από έναν επίσημο εξεταστή. -pulls.blocked_by_official_review_requests=Αυτό το Pull Request έχει επίσημες αιτήσεις αξιολόγησης. +pulls.blocked_by_rejection=Αυτό το pull request έχει αλλαγές που ζητούνται από έναν επίσημο εξεταστή. +pulls.blocked_by_official_review_requests=Αυτό το pull request έχει επίσημες αιτήσεις αξιολόγησης. pulls.blocked_by_outdated_branch=Αυτό το pull request έχει αποκλειστεί επειδή είναι παρωχημένο. pulls.blocked_by_changed_protected_files_1=Αυτό το pull request έχει αποκλειστεί επειδή αλλάζει ένα προστατευμένο αρχείο: pulls.blocked_by_changed_protected_files_n=Αυτό το pull request έχει αποκλειστεί επειδή αλλάζει προστατευμένα αρχεία: @@ -1754,8 +1795,8 @@ pulls.approve_count_1=%d έγκριση pulls.approve_count_n=%d εγκρίσεις pulls.reject_count_1=%d αίτημα αλλαγής pulls.reject_count_n=%d αιτήματα αλλαγής -pulls.waiting_count_1=%d αναμονή αναθεώρησης -pulls.waiting_count_n=%d αναμονή αναθεωρήσεων +pulls.waiting_count_1=%d αναμονή αξιολόγησης +pulls.waiting_count_n=%d αναμονές αξιολόγησης pulls.wrong_commit_id=Το id υποβολής πρέπει να είναι ένα id υποβολής στον κλάδο προορισμού pulls.no_merge_desc=Αυτό το pull request δεν μπορεί να συγχωνευθεί επειδή όλες οι επιλογές συγχώνευσης αποθετηρίων είναι απενεργοποιημένες. @@ -1780,9 +1821,9 @@ pulls.unrelated_histories=H Συγχώνευση Απέτυχε: Η κεφαλή pulls.merge_out_of_date=Η συγχώνευση απέτυχε: Κατά τη δημιουργία της συγχώνευσης, η βάση ενημερώθηκε. Συμβουλή: Δοκιμάστε ξανά. pulls.head_out_of_date=Η συγχώνευση απέτυχε: Κατά τη δημιουργία της συγχώνευσης, το HEAD ενημερώθηκε. Συμβουλή: Δοκιμάστε ξανά. pulls.has_merged=Αποτυχία: Το pull request έχει συγχωνευθεί, δεν είναι δυνατή η συγχώνευση ξανά ή να αλλάξει ο κλάδος προορισμού. -pulls.push_rejected=Η συγχώνευση απέτυχε: Η ώθηση απορρίφθηκε. Ελέγξτε τα Άγκιστρα Git για αυτό το αποθετήριο. +pulls.push_rejected=Αποτυχία ώθησης: Η ώθηση απορρίφθηκε. Ελέγξτε τα Άγκιστρα Git για αυτό το αποθετήριο. pulls.push_rejected_summary=Μήνυμα Πλήρους Απόρριψης -pulls.push_rejected_no_message=H Συγχώνευση Aπέτυχε: Η ώθηση απορρίφθηκε, αλλά δεν υπήρχε απομακρυσμένο μήνυμα.
      Ελέγξτε τα Άγκιστρα Git για αυτό το αποθετήριο +pulls.push_rejected_no_message=H συγχώνευση απέτυχε: Η ώθηση απορρίφθηκε, αλλά δεν υπήρχε απομακρυσμένο μήνυμα.
      Ελέγξτε τα Git Hooks για αυτό το αποθετήριο pulls.open_unmerged_pull_exists=`Δεν μπορείτε να ανοίξετε εκ νέου, επειδή υπάρχει ένα εκκρεμές pull request (#%d) με πανομοιότυπες ιδιότητες.` pulls.status_checking=Μερικοί έλεγχοι εκκρεμούν pulls.status_checks_success=Όλοι οι έλεγχοι ήταν επιτυχείς @@ -1824,7 +1865,7 @@ pulls.auto_merge_canceled_schedule_comment=`ακύρωσε την αυτόματ pulls.delete.title=Διαγραφή αυτού του pull request; pulls.delete.text=Θέλετε πραγματικά να διαγράψετε αυτό το pull request; (Αυτό θα σβήσει οριστικά όλο το περιεχόμενο του. Εξετάστε αν θέλετε να το κλείσετε, αν σκοπεύεται να το αρχειοθετήσετε) -pulls.recently_pushed_new_branches=Ωθήσατε στο κλάδο %[1]s %[2]s +pulls.recently_pushed_new_branches=Ωθήσατε στο κλάδο %[1]s %[2]s pull.deleted_branch=(διαγράφηκε):%s @@ -1835,19 +1876,19 @@ milestones.no_due_date=Δεν υπάρχει ημερομηνία παράδοσ milestones.open=Άνοιγμα milestones.close=Κλείσιμο milestones.new_subheader=Τα ορόσημα μπορούν να σας βοηθήσουν να οργανώσετε τα ζητήματα και να παρακολουθείτε την πρόοδό τους. -milestones.completeness=%d%% Ολοκληρώθηκε +milestones.completeness=%d%% ολοκληρωμένο milestones.create=Δημιουργία Ορόσημου milestones.title=Τίτλος milestones.desc=Περιγραφή milestones.due_date=Ημερομηνία Απαίτησης (Προαιρετικό) milestones.clear=Εκκαθάριση -milestones.invalid_due_date_format=Η μορφή ημερομηνίας απαίτησης πρέπει να είναι 'yyyy-mm-dd'. -milestones.create_success=Το ορόσημο "%s" δημιουργήθηκε. +milestones.invalid_due_date_format=Η ημερομηνίας απαίτησης πρέπει να έχει την μορφή «εεεε-μμ-ηη». +milestones.create_success=Το ορόσημο «%s» δημιουργήθηκε. milestones.edit=Επεξεργασία Ορόσημου milestones.edit_subheader=Τα Ορόσημα οργανώνουν ζητήματα και καταγράφουν την πρόοδο. milestones.cancel=Ακύρωση milestones.modify=Ενημέρωση Ορόσημου -milestones.edit_success=Το ορόσημο "%s" ενημερώθηκε. +milestones.edit_success=Το ορόσημο «%s» ενημερώθηκε. milestones.deletion=Διαγραφή Ορόσημου milestones.deletion_desc=Η διαγραφή ενός ορόσημου το αφαιρεί από όλα τα συναφή ζητήματα. Συνέχεια; milestones.deletion_success=Το ορόσημο έχει διαγραφεί. @@ -1858,7 +1899,7 @@ milestones.filter_sort.most_complete=Περισσότερο πλήρη milestones.filter_sort.most_issues=Περισσότερα ζητήματα milestones.filter_sort.least_issues=Λιγότερα ζητήματα -signing.will_sign=Αυτή η υποβολή θα υπογραφεί με το κλειδί "%s". +signing.will_sign=Αυτή η υποβολή θα υπογραφεί με το κλειδί «%s». signing.wont_sign.error=Παρουσιάστηκε σφάλμα κατά τον έλεγχο για το αν η υποβολή μπορεί να υπογραφεί. signing.wont_sign.nokey=Δεν υπάρχει διαθέσιμο κλειδί για να υπογραφεί αυτή η υποβολή. signing.wont_sign.never=Οι υποβολές δεν υπογράφονται ποτέ. @@ -1877,8 +1918,8 @@ ext_wiki.desc=Σύνδεση σε ένα εξωτερικό wiki. wiki=Wiki wiki.welcome=Καλώς ήρθατε στο Wiki. -wiki.welcome_desc=Το wiki σας επιτρέπει να γράψετε και να μοιραστείτε τεκμηρίωση με συνεργάτες. -wiki.desc=Γράψτε και μοιραστείτε τεκμηρίωση με συνεργάτες. +wiki.welcome_desc=Το wiki σας επιτρέπει να γράψετε και να μοιραστείτε τεκμηριώσεις (documentation) με άλλους συνεργάτες. +wiki.desc=Γράψτε και μοιραστείτε τεκμηριώσεις με συνεργάτες. wiki.create_first_page=Δημιουργία της πρώτης σελίδας wiki.page=Σελίδα wiki.filter_page=Φιλτράρισμα σελίδας @@ -1894,12 +1935,12 @@ wiki.file_revision=Αναθεώρηση Σελίδας wiki.wiki_page_revisions=Αναθεωρήσεις Σελίδας Wiki wiki.back_to_wiki=Πίσω στη σελίδα wiki wiki.delete_page_button=Διαγραφή Σελίδας -wiki.delete_page_notice_1=Η διαγραφή της σελίδας wiki "%s" δεν μπορεί να αναιρεθεί. Συνέχεια; +wiki.delete_page_notice_1=Η διαγραφή της σελίδας wiki «%s» δεν μπορεί να αναιρεθεί. Συνέχεια; wiki.page_already_exists=Υπάρχει ήδη μια σελίδα wiki με το ίδιο όνομα. -wiki.reserved_page=Το όνομα σελίδας wiki "%s" είναι δεσμευμένο. +wiki.reserved_page=Το όνομα σελίδας wiki «%s» είναι δεσμευμένο. wiki.pages=Σελίδες wiki.last_updated=Τελευταία ενημέρωση %s -wiki.page_name_desc=Εισάγετε ένα όνομα για αυτή τη σελίδα Wiki. Μερικά ειδικά ονόματα είναι: 'Home', '_Sidebar' και '_Footer'. +wiki.page_name_desc=Εισάγετε ένα όνομα για αυτή τη σελίδα wiki. Μερικά ειδικά ονόματα είναι: «Home», «_Sidebar» και «_Footer». wiki.original_git_entry_tooltip=Προβολή του αρχικού αρχείου Git αντί ενός εμφανίσιμου συνδέσμου. activity=Δραστηριότητα @@ -1973,11 +2014,11 @@ contributors.contribution_type.commits=Υποβολές search=Αναζήτηση search.search_repo=Αναζήτηση αποθετηρίου search.type.tooltip=Τύπος αναζήτησης -search.fuzzy=Fuzzy +search.fuzzy=Όμοιο search.fuzzy.tooltip=Συμπερίληψη και των αποτελεσμάτων που είναι πλησιέστερα με τον όρο αναζήτησης search.match=Ταίριασμα search.match.tooltip=Συμπερίληψη μόνο των αποτελεσμάτων που ταιριάζουν ακριβώς με τον όρο αναζήτησης -search.results=Αποτελέσματα αναζήτησης για "%s" σε %s +search.results=Αποτελέσματα αναζήτησης για «%s» σε %s search.code_no_results=Δεν βρέθηκε πηγαίος κώδικας που να ταιριάζει με τον όρο αναζήτησης. search.code_search_unavailable=Η αναζήτηση κώδικα δεν είναι διαθέσιμη αυτή τη στιγμή. Παρακαλώ επικοινωνήστε με το διαχειριστή. @@ -2017,7 +2058,7 @@ settings.mirror_settings.push_mirror.add=Προσθήκη Είδωλου Push settings.mirror_settings.push_mirror.edit_sync_time=Επεξεργασία διαστήματος συγχρονισμού ειδώλου settings.sync_mirror=Συγχρονισμός Τώρα -settings.pull_mirror_sync_in_progress=Έλκονται αλλαγές από το απομακρυσμένο %s αυτή τη στιγμή. +settings.pull_mirror_sync_in_progress=Έλκονται αλλαγές από την απομακρυσμένη τοποθεσία %s αυτή τη στιγμή. settings.push_mirror_sync_in_progress=Ώθηση αλλαγών στο απομακρυσμένο %s αυτή τη στιγμή. settings.site=Ιστοσελίδα settings.update_settings=Ενημέρωση Ρυθμίσεων @@ -2047,8 +2088,8 @@ settings.tracker_issue_style.regexp=Κανονική Έκφραση settings.tracker_issue_style.regexp_pattern=Μοτίβο Κανονικής Έκφρασης settings.tracker_issue_style.regexp_pattern_desc=Η πρώτη ομάδα θα χρησιμοποιηθεί στη θέση του {index}. settings.tracker_url_format_desc=Χρησιμοποιήστε τα {user}, {repo} και {index} για το όνομα χρήστη, το όνομα αποθετηρίου και το ευρετήριο ζητημάτων. -settings.enable_timetracker=Ενεργοποίηση Καταγραφής Χρόνου -settings.allow_only_contributors_to_track_time=Μόνο οι Συμμετέχοντες να Καταγράφουν Χρόνο +settings.enable_timetracker=Ενεργοποίηση καταγραφής χρόνου +settings.allow_only_contributors_to_track_time=Να επιτρέπεται η καταγραφή χρόνου μόνο από συνεισφέροντες settings.pulls_desc=Ενεργοποίηση Pull Requests στο Αποθετήριο settings.pulls.ignore_whitespace=Αγνόηση των Κενών Χαρακτήρων στις Συγκρούσεις settings.pulls.enable_autodetect_manual_merge=Ενεργοποίηση αυτόματης ανίχνευσης συγχώνευσης (Σημείωση: σε ορισμένες ειδικές περιπτώσεις, μπορεί να προκύψουν εσφαλμένες κρίσεις) @@ -2102,7 +2143,7 @@ settings.trust_model.default=Προεπιλεγμένο Μοντέλο Εμπι settings.trust_model.default.desc=Χρησιμοποιήστε το προεπιλεγμένο μοντέλο εμπιστοσύνης αποθετηρίου για αυτήν την εγκατάσταση. settings.trust_model.collaborator=Συνεργάτης settings.trust_model.collaborator.long=Συνεργάτης: Εμπιστοσύνη υπογραφών από συνεργάτες -settings.trust_model.collaborator.desc=Οι έγκυρες υπογραφές από συνεργάτες αυτού του αποθετηρίου θα επισημαίνονται ως "αξιόπιστη" - (είτε ταιριάζουν με τον υποβολέα είτε όχι). Διαφορετικά, οι έγκυρες υπογραφές θα επισημανθούν "μη αξιόπιστη" αν η υπογραφή ταιριάζει με τον υποβολέα και "δεν ταιριάζει" αν όχι. +settings.trust_model.collaborator.desc=Οι έγκυρες υπογραφές που προέρχονται από συνεργάτες του αποθετηρίου θα επισημαίνονται ως "αξιόπιστες" - (ανεξάρτητα αν ο υπογραφόμενος συνεργάτης είναι και ο υποβολέας ή όχι). Διαφορετικά, οι έγκυρες υπογραφές θα επισημανθούν ως "αξιόπιστες" μόνο αν η υπογραφή ταιριάζει με τον υποβολέα και, αλλιώς θα χαρακτηριστούν ως "αταίριαστες". settings.trust_model.committer=Υποβολέας settings.trust_model.committer.long=Υποβολέας: Οι υπογραφές εμπιστοσύνης που ταιριάζουν σε υποβολείς (Αυτό ταιριάζει με το GitHub και θα αναγκάσει τις υπογεγραμμένες υποβολές από το Forgejo να το έχουν ως υποβολέα) settings.trust_model.committer.desc=Οι έγκυρες υπογραφές θα σημαίνονται ώς "αξιόπιστη" μόνο εάν ταιριάζουν με τον υποβολέα, διαφορετικά θα σημαίνωνται ως "δεν ταιριάζει". Αυτό αναγκάζει το Forgejo να είναι ο υποβολέας στις υπογεγραμμένες υποβολές με τον πραγματικό υποβολέα να αναφέρεται στην σημείωση Co-authored-by: και Co-committed-by: στην υποβολή. Το προεπιλεγμένο κλειδί Forgejo πρέπει να ταιριάζει σε ένα Χρήστη στη βάση δεδομένων. @@ -2111,29 +2152,29 @@ settings.trust_model.collaboratorcommitter.long=Συνεργάτης+Υποβο settings.trust_model.collaboratorcommitter.desc=Έγκυρες υπογραφές από συνεργάτες αυτού του αποθετηρίου θα επισημανθούν ως "αξιόπιστες" αν ταιριάζουν με τον υποβολέα. Διαφορετικά, οι έγκυρες υπογραφές θα φέρουν την ένδειξη "μη αξιόπιστη" αν η υπογραφή ταιριάζει με τον υποβολέα και "δεν ταιριάζει" διαφορετικά. Αυτό θα αναγκάσει το Forgejo να επισημανθεί ως ο υποβολέας στις υπογεγραμμένες υποβολές με τον πραγματικό υποβολέα να σημειώνεται ως Co-Authored-By: και Co-Committed-By: στο τέλος της υποβολής. Το προεπιλεγμένο κλειδί Forgejo πρέπει να ταιριάζει με έναν χρήστη στη βάση δεδομένων. settings.wiki_delete=Διαγραφή Δεδομένων Wiki settings.wiki_delete_desc=Η διαγραφή των δεδομένων του wiki του αποθετηρίου είναι μόνιμη και δεν μπορεί να αναιρεθεί. -settings.wiki_delete_notices_1=- Αυτό θα διαγράψει μόνιμα και θα απενεργοποιήσει το wiki του αποθετηρίου για %s. +settings.wiki_delete_notices_1=- Αυτή η ρύθμιση θα απενεργοποιήσει το wiki του αποθετηρίου για %s και θα το διαγράψει μόνιμα. settings.confirm_wiki_delete=Διαγραφή Δεδομένων Wiki settings.wiki_deletion_success=Τα δεδομένα wiki του αποθετηρίου έχουν διαγραφεί. settings.delete=Διαγραφή Αυτού Του Αποθετηρίου settings.delete_desc=Η διαγραφή ενός αποθετηρίου είναι μόνιμη και δεν μπορεί να αναιρεθεί. settings.delete_notices_1=- Αυτή η ενέργεια ΔΕΝ ΜΠΟΡΕΙ να αναιρεθεί. -settings.delete_notices_2=- Αυτή η ενέργεια θα διαγράψει μόνιμα το αποθετήριο %s συμπεριλαμβανομένου του κώδικα, των προβλημάτων, σχολίων, δεδομένων wiki και των ρυθμίσεων συνεργατών. +settings.delete_notices_2=- Αυτή η ενέργεια θα διαγράψει μόνιμα το αποθετήριο %s μαζί με τον κώδικα, τα ζητημάτα, τα σχόλια, τα δεδομένα των wiki και τις ρυθμίσεις συνεργατών που βρίσκονται μέσα σε αυτό. settings.delete_notices_fork_1=- Τα Forks αυτού του αποθετηρίου θα γίνουν ανεξάρτητα μετά τη διαγραφή. settings.deletion_success=Το αποθετήριο έχει διαγραφεί. settings.update_settings_success=Οι ρυθμίσεις του αποθετηρίου έχουν ενημερωθεί. settings.update_settings_no_unit=Το αποθετήριο θα πρέπει να επιτρέπει τουλάχιστον κάποιο είδος αλληλεπίδρασης. settings.confirm_delete=Διαγραφή Αποθετηρίου -settings.add_collaborator=Προσθήκη Συνεργάτη -settings.add_collaborator_success=Έχει προστεθεί ο συνεργάτης. +settings.add_collaborator=Προσθήκη συνεργάτη +settings.add_collaborator_success=Ο συνεργάτης προστέθηκε. settings.add_collaborator_inactive_user=Δεν είναι δυνατή η προσθήκη ενός ανενεργού χρήστη ως συνεργάτη. settings.add_collaborator_owner=Δεν είναι δυνατή η προσθήκη ενός ιδιοκτήτη σαν συνεργάτη. settings.add_collaborator_duplicate=Ο συνεργάτης έχει ήδη προστεθεί σε αυτό το αποθετήριο. -settings.delete_collaborator=Αφαίρεση -settings.collaborator_deletion=Αφαίρεση Συνεργάτη -settings.collaborator_deletion_desc=Η κατάργηση ενός συνεργάτη θα ανακαλέσει την πρόσβασή τους σε αυτό το αποθετήριο. Συνέχεια; -settings.remove_collaborator_success=Ο συνεργάτης έχει αφαιρεθεί. +settings.delete_collaborator=Κατάργηση +settings.collaborator_deletion=Κατάργηση συνεργάτη +settings.collaborator_deletion_desc=Η κατάργηση ενός συνεργάτη θα αφαιρέσει και την πρόσβασή του στο αποθετήριο. Είστε βέβαιοι; +settings.remove_collaborator_success=Ο συνεργάτης έχει καταργηθεί. settings.search_user_placeholder=Αναζήτηση χρήστη… -settings.org_not_allowed_to_be_collaborator=Οι οργανισμοί δεν μπορούν να προστεθούν ως συνεργάτης. +settings.org_not_allowed_to_be_collaborator=Δεν μπορείτε να προσθέσετε έναν οργανισμό ως συνεργάτη. settings.change_team_access_not_allowed=Η αλλαγή της πρόσβασης ομάδας για το αποθετήριο έχει περιοριστεί στον ιδιοκτήτη του οργανισμού settings.team_not_in_organization=Η ομάδα δεν είναι στον ίδιο οργανισμό με το αποθετήριο settings.teams=Ομάδες @@ -2270,7 +2311,7 @@ settings.title=Τίτλος settings.deploy_key_content=Περιεχόμενο settings.key_been_used=Ένα κλειδί διάθεσης με το ίδιο περιεχόμενο χρησιμοποιείται ήδη. settings.key_name_used=Ένα κλειδί διάθεσης με το ίδιο όνομα υπάρχει ήδη. -settings.add_key_success=Το κλειδί διάθεσης '%s' προστέθηκε. +settings.add_key_success=Το κλειδί διάθεσης «%s» προστέθηκε. settings.deploy_key_deletion=Αφαίρεση Κλειδιού Διάθεσης settings.deploy_key_deletion_desc=Η κατάργηση ενός κλειδί διάθεσης θα ανακαλέσει την πρόσβασή του σε αυτό το αποθετήριο. Συνέχεια; settings.deploy_key_deletion_success=Το κλειδί διάθεσης έχει αφαιρεθεί. @@ -2281,7 +2322,7 @@ settings.protected_branch.delete_rule=Διαγραφή Κανόνα settings.protected_branch_can_push=Επιτρέψτε ώθηση; settings.protected_branch_can_push_yes=Μπορείτε να ωθήσετε settings.protected_branch_can_push_no=Δεν μπορείτε να ωθήσετε -settings.branch_protection=Προστασία Κλάδου για το Κλάδο '%s' +settings.branch_protection=Κανόνες προστασίας για τον κλάδο «%s» settings.protect_this_branch=Ενεργοποίηση Προστασίας Κλάδου settings.protect_this_branch_desc=Αποτρέπει τη διαγραφή και περιορίζει το Git push και συγχώνευση στον κλάδο. settings.protect_disable_push=Απενεργοποίηση Ώθησης @@ -2328,9 +2369,9 @@ settings.protect_unprotected_file_patterns=Μοτίβα μη προστατευ settings.protect_unprotected_file_patterns_desc=Μη προστατευμένα αρχεία που επιτρέπεται να αλλάξουν απευθείας εάν ο χρήστης έχει πρόσβαση εγγραφής, παρακάμπτοντας τον περιορισμό ώθησης. Επιπλέων μοτίβα μπορούν να διαχωριστούν με ερωτηματικό (';'). Δείτε την τεκμηρίωση github.com/gobwas/glob για τη σύνταξη του μοτίβου. Πχ: .drone.yml, /docs/**/*.txt. settings.add_protected_branch=Ενεργοποίηση προστασίας settings.delete_protected_branch=Απενεργοποίηση προστασίας -settings.update_protect_branch_success=Η προστασία κλάδου για τον κανόνα "%s" ενημερώθηκε. +settings.update_protect_branch_success=Η προστασία κλάδου για τον κανόνα «%s» ενημερώθηκε. settings.remove_protected_branch_success=Η προστασία κλάδου για τον κανόνα "%s" αφαιρέθηκε. -settings.remove_protected_branch_failed=Η αφαίρεση του κανόνα προστασίας κλάδου "%s" απέτυχε. +settings.remove_protected_branch_failed=Η αφαίρεση του κανόνα προστασίας κλάδου «%s» απέτυχε. settings.protected_branch_deletion=Απενεργοποίηση Προστασίας Κλάδου settings.protected_branch_deletion_desc=Η απενεργοποίηση της προστασίας του κλάδου επιτρέπει στους χρήστες με άδεια εγγραφής να κάνουν push στον κλάδο. Συνέχεια; settings.block_rejected_reviews=Φραγή συγχώνευσης αν υπάρχουν απορριπτικές αξιολογήσεις @@ -2341,12 +2382,12 @@ settings.block_outdated_branch=Φραγή συγχώνευσης αν το pull settings.block_outdated_branch_desc=Η συγχώνευση δεν θα είναι δυνατή όταν ο κλάδος κεφαλής είναι πίσω από τον βασικό κλάδο. settings.default_branch_desc=Επιλέξτε έναν προεπιλεγμένο κλάδο αποθετηρίου για pull requests και υποβολές κώδικα: settings.merge_style_desc=Συγχώνευση Στυλ -settings.default_merge_style_desc=Προεπιλεγμένο στυλ συγχώνευσης για pull requests: +settings.default_merge_style_desc=Προεπιλεγμένο στυλ συγχώνευσης settings.choose_branch=Επιλέξτε έναν κλάδο… settings.no_protected_branch=Δεν υπάρχουν προστατευμένοι κλάδοι. settings.edit_protected_branch=Επεξεργασία settings.protected_branch_required_rule_name=Απαιτούμενο όνομα κανόνα -settings.protected_branch_duplicate_rule_name=Διπλότυπο όνομα κανόνα +settings.protected_branch_duplicate_rule_name=Υπάρχει ήδη ένας κανόνας για αυτούς τους κλάδους settings.protected_branch_required_approvals_min=Οι απαιτούμενες εγκρίσεις δεν μπορούν να είναι αρνητικές. settings.tags=Ετικέτες settings.tags.protection=Προστασία Ετικετών @@ -2365,14 +2406,14 @@ settings.matrix.homeserver_url=Homeserver URL settings.matrix.room_id=ID Δωματίου settings.matrix.message_type=Τύπος Μηνύματος settings.archive.button=Αρχειοθέτηση Αποθετηρίου -settings.archive.header=Αρχειοθέτηση Αυτού του Αποθετηρίου +settings.archive.header=Αρχειοθέτηση αποθετηρίου settings.archive.text=Η αρχειοθέτηση του αποθετηρίου θα το αλλάξει σε μόνο για ανάγνωση. Δε θα φαίνεται στον αρχικό πίνακα. Κανείς (ακόμα και εσείς!) δε θα μπορεί να κάνει νέες υποβολές, ή να ανοίξει ζητήματα ή pull request. settings.archive.success=Το αποθετήριο αρχειοθετήθηκε με επιτυχία. settings.archive.error=Παρουσιάστηκε σφάλμα κατά την προσπάθεια αρχειοθέτησης του αποθετηρίου. Δείτε το αρχείο καταγραφής για περισσότερες λεπτομέρειες. settings.archive.error_ismirror=Δε μπορείτε να αρχειοθετήσετε ένα είδωλο αποθετηρίου. settings.archive.branchsettings_unavailable=Οι ρυθμίσεις του κλάδου δεν είναι διαθέσιμες αν το αποθετήριο είναι αρχειοθετημένο. settings.archive.tagsettings_unavailable=Οι ρυθμίσεις της ετικέτας δεν είναι διαθέσιμες αν το αποθετήριο είναι αρχειοθετημένο. -settings.unarchive.button=Απο-Αρχειοθέτηση αποθετηρίου +settings.unarchive.button=Αναίρεση αρχειοθέτησης αποθετηρίου settings.unarchive.header=Απο-Αρχειοθέτηση του αποθετηρίου settings.unarchive.text=Η απο-αρχειοθέτηση του αποθετηρίου θα αποκαταστήσει την ικανότητά του να λαμβάνει υποβολές και ωθήσεις, καθώς και νέα ζητήματα και pull-requests. settings.unarchive.success=Το αποθετήριο απο-αρχειοθετήθηκε με επιτυχία. @@ -2516,28 +2557,28 @@ release.releases_for=Κυκλοφορίες για %s release.tags_for=Ετικέτες για %s branch.name=Όνομα Κλάδου -branch.already_exists=Ήδη υπάρχει ένας κλάδος με το όνομα "%s". +branch.already_exists=Ήδη υπάρχει ένας κλάδος με το όνομα «%s». branch.delete_head=Διαγραφή -branch.delete=`Διαγραφή του Κλάδου "%s"` +branch.delete=`Διαγραφή του Κλάδου «%s»` branch.delete_html=Διαγραφή Κλάδου branch.delete_desc=Η διαγραφή ενός κλάδου είναι μόνιμη. Αν και ο διαγραμμένος κλάδος μπορεί να συνεχίσει να υπάρχει για σύντομο χρονικό διάστημα πριν να αφαιρεθεί, ΔΕΝ ΜΠΟΡΕΙ να αναιρεθεί στις περισσότερες περιπτώσεις. Συνέχεια; -branch.deletion_success=Ο κλάδος "%s" διαγράφηκε. -branch.deletion_failed=Αποτυχία διαγραφής του κλάδου "%s". -branch.delete_branch_has_new_commits=Ο κλάδος "%s" δεν μπορεί να διαγραφεί επειδή προστέθηκαν νέες υποβολές μετά τη συγχώνευση. +branch.deletion_success=Ο κλάδος «%s» διαγράφηκε. +branch.deletion_failed=Η διαγραφή του κλάδου «%s» απέτυχε. +branch.delete_branch_has_new_commits=Ο κλάδος «%s» δεν μπορεί να διαγραφεί επειδή προστέθηκαν νέες υποβολές μετά τη συγχώνευση. branch.create_branch=Δημιουργία κλάδου %s -branch.create_from=`από το "%s"` -branch.create_success=Ο κλάδος "%s" δημιουργήθηκε. -branch.branch_already_exists=Ο κλάδος "%s" υπάρχει ήδη σε αυτό το αποθετήριο. -branch.branch_name_conflict=Το όνομα του κλάδου "%s" συγκρούεται με το ήδη υπάρχον κλάδο "%s". -branch.tag_collision=Ο κλάδος "%s" δεν μπορεί να δημιουργηθεί επειδή μια ετικέτα με το ίδιο όνομα υπάρχει ήδη στο αποθετήριο. +branch.create_from=`από το «%s»` +branch.create_success=Ο κλάδος «%s» δημιουργήθηκε. +branch.branch_already_exists=Ο κλάδος «%s» υπάρχει ήδη σε αυτό το αποθετήριο. +branch.branch_name_conflict=Το όνομα του κλάδου «%s» συγκρούεται με το ήδη υπάρχον κλάδο «%s». +branch.tag_collision=Ο κλάδος «%s» δεν μπορεί να δημιουργηθεί επειδή μια ετικέτα με το ίδιο όνομα υπάρχει ήδη στο αποθετήριο. branch.deleted_by=Διαγράφηκε από %s -branch.restore_success=Ο κλάδος "%s" επαναφέρθηκε. -branch.restore_failed=Αποτυχία επαναφοράς του κλάδου "%s". -branch.protected_deletion_failed=Ο κλάδος "%s" προστατεύεται. Δεν μπορεί να διαγραφεί. -branch.default_deletion_failed=Ο κλάδος "%s" είναι ο προεπιλεγμένος κλάδος. Δεν μπορεί να διαγραφεί. -branch.restore=`Επαναφορά του Κλάδου "%s"` -branch.download=`Λήψη του Κλάδου "%s"` -branch.rename=`Μετονομασία Κλάδου "%s"` +branch.restore_success=Ο κλάδος «%s» επαναφέρθηκε. +branch.restore_failed=Αποτυχία επαναφοράς του κλάδου «%s». +branch.protected_deletion_failed=Ο κλάδος «%s» είναι προστατευόμενος. Δεν μπορεί να διαγραφεί. +branch.default_deletion_failed=Ο κλάδος «%s» είναι προεπιλεγμένος κλάδος. Δεν μπορεί να διαγραφεί. +branch.restore=`Επαναφορά του κλάδου «%s»` +branch.download=`Λήψη του κλάδου «%s»` +branch.rename=`Μετονομασία κλάδου «%s»` branch.search=Αναζήτηση Κλάδου branch.included_desc=Αυτός ο κλάδος είναι μέρος του προεπιλεγμένου κλάδου branch.included=Περιλαμβάνεται @@ -2548,15 +2589,15 @@ branch.rename_branch_to=Μετονομασία του "%s" σε: branch.confirm_rename_branch=Μετονομασία κλάδου branch.create_branch_operation=Δημιουργία κλάδου branch.new_branch=Δημιουργία νέου κλάδου -branch.new_branch_from=`Δημιουργία νέου κλάδου από το "%s"` +branch.new_branch_from=`Δημιουργία νέου κλάδου από το «%s»` branch.renamed=Ο κλάδος %s μετονομάστηκε σε %s. tag.create_tag=Δημιουργία ετικέτας %s tag.create_tag_operation=Δημιουργία ετικέτας tag.confirm_create_tag=Δημιουργία ετικέτας -tag.create_tag_from=`Δημιουργία νέας ετικέτας από το "%s"` +tag.create_tag_from=`Δημιουργία νέας ετικέτας από το «%s»` -tag.create_success=Η ετικέτα "%s" δημιουργήθηκε. +tag.create_success=Η ετικέτα «%s» δημιουργήθηκε. topic.manage_topics=Διαχείριση Θεμάτων topic.done=Ολοκληρώθηκε @@ -2569,9 +2610,64 @@ find_file.no_matching=Δεν ταιριάζει κανένα αρχείο error.csv.too_large=Δεν είναι δυνατή η απόδοση αυτού του αρχείου επειδή είναι πολύ μεγάλο. error.csv.unexpected=Δεν είναι δυνατή η απόδοση αυτού του αρχείου, επειδή περιέχει έναν μη αναμενόμενο χαρακτήρα στη γραμμή %d και στη στήλη %d. error.csv.invalid_field_count=Δεν είναι δυνατή η απόδοση αυτού του αρχείου, επειδή έχει λάθος αριθμό πεδίων στη γραμμή %d. +commits.renamed_from = Μετονομάστηκε από %σ +settings.wiki_rename_branch_main_desc = Ο κλάδος, ο οποίος χρησιμοποιείται εσωτερικά από το wiki, θα μετονομαστεί σε «%s». Αυτή η αλλαγή είναι μόνιμη και μη αναστρέψιμη. +issues.comment.blocked_by_user = Δεν μπορείτε να αφήσετε σχόλιο σε αυτό το ζήτημα, επειδή ο κάτοχος του αποθετηρίου ή το άτομο που δημιούργησε το ζήτημα σας έχει αποκλείσει. +pulls.blocked_by_user = Δεν μπορείτε να δημιουργήσετε pull request σε αυτό το αποθετήριο, επειδή ο κάτοχος του αποθετηρίου σας έχει αποκλείσει. +pulls.made_using_agit = AGit +wiki.cancel = Ακύρωση +settings.units.add_more = Προσθήκη περισσότερων... +settings.new_owner_blocked_doer = Ο νέος κάτοχος του αποθετηρίου σας έχει αποκλείσει. +settings.enter_repo_name = Γράψτε το όνομα του κατόχου και του αποθετηρίου ακριβώς όπως το βλέπετε: +settings.confirmation_string = Κείμενο επιβεβαίωσης +settings.units.overview = Επισκόπηση +pulls.commit_ref_at = `ανέφερε το pull request στην υποβολή %[2]s` +contributors.contribution_type.filter_label = Είδος συνεισφοράς: +settings.wiki_rename_branch_main_notices_1 = Αυτή η ενέργεια ΔΕΝ αναιρείται. +activity.navbar.contributors = Συνεισφέροντες +contributors.contribution_type.additions = Προσθήκες +contributors.contribution_type.deletions = Διαγραφές +migrate.forgejo.description = Μεταφορά δεδομένων από το codeberg.org ή άλλων υπηρεσιών Forgejo. +rss.must_be_on_branch = Για να αποκτήσετε ένα RSS feed, πρέπει να βρίσκεστε σε έναν κλάδο. +clone_in_vscodium = Κλωνοποίηση στο VSCodium +editor.invalid_commit_mail = Αυτή η διεύθυνση email δεν είναι έγκυρη για την δημιουργία μίας υποβολής. +pulls.nothing_to_compare_have_tag = Ο επιλεγμένος κλάδος/tag είναι παρόμοιος. +issues.blocked_by_user = Δεν μπορείτε να δημιουργήσετε ζητήματα σε αυτό το αποθετήριο, επειδή ο κάτοχος του αποθετηρίου σας έχει αποκλείσει. +pulls.agit_explanation = Δημιουργημένο μέσω του AGit. Το AGit επιτρέπει σε συνεισφέροντες να προτείνουν αλλαγές χρησιμοποιώντας την εντολή «git push», χωρίς την δημιουργία fork ή έναν νέο κλάδο. +activity.navbar.recent_commits = Πρόσφατες υποβολές +settings.wiki_globally_editable = Να επιτρέπεται η επεξεργασία του wiki σε όλους +admin.manage_flags = Διαχείριση σημάνσεων +admin.enabled_flags = Το αποθετήριο έχει τις εξής σημάνσεις: +settings.mirror_settings.pushed_repository = Προοριζόμενο αποθετήριο +admin.flags_replaced = Οι σημάνσεις του αποθετηρίου αντικαταστάθηκαν +activity.navbar.code_frequency = Συχνότητα κώδικα +settings.wiki_branch_rename_success = Το όνομα κλάδου wiki του αποθετηρίου κανονικοποιήθηκε επιτυχώς. +settings.confirm_wiki_branch_rename = Μετονομασία κλάδου wiki +admin.update_flags = Ενημέρωση σημάνσεων +settings.wiki_rename_branch_main = Κανονικοποίηση ονόματος κλάδου wiki +admin.failed_to_replace_flags = Προέκυψε σφάλμα κατά την αντικατάσταση των σημάνσεων του αποθετηρίου +mirror_sync = σε συγχρονισμό +desc.sha256 = SHA256 +pulls.fast_forward_only_merge_pull_request = Μόνο fast-forward +pulls.reopen_failed.head_branch = Δεν είναι δυνατό το επανάνοιγμα του pull request, επειδή δεν υπάρχει πια ο κλάδος στο οποίο περιέχονται οι αλλαγές του (head branch). +settings.units.units = Μονάδες αποθετηρίου +settings.wiki_branch_rename_failure = Προέκυψε σφάλμα κατά την κανονικοποίηση του ονόματος κλάδου wiki του αποθετηρίου. +settings.ignore_stale_approvals = Να αγνοούνται οι παρωχημένες εγκρίσεις +pulls.reopen_failed.base_branch = Δεν είναι δυνατό το επανάνοιγμα του pull request, επειδή δεν υπάρχει πια ο κλάδος πάνω στο οποίο βασίζονται οι αλλαγές του (base branch). +activity.navbar.pulse = Παλμός +error.broken_git_hook = Τα Git hook του αποθετηρίου δεν φαίνονται να λειτουργούν. Παρακαλώ ακολουθήστε τον οδηγό για να τα διορθώσετε, και μετά ωθήστε μερικές υποβολές (commits) για να γίνει επανέλεγχος. +settings.add_collaborator_blocked_them = Δεν είναι δυνατή η προσθήκη του χρήστη ως συνεργάτη, καθώς έχει αποκλείσει τον κάτοχο του αποθετηρίου. +settings.wiki_rename_branch_main_notices_2 = Αυτό θα αλλάξει το όνομα του κλάδου που χρησιμοποιείται εσωτερικά για το wiki του αποθετηρίου %s. Αν έχετε ένα τοπικό αντίγραφο του αποθετηρίου, θα χρειαστεί να αλλάξετε τα checkout του. +settings.add_collaborator_blocked_our = Δεν είναι δυνατή η προσθήκη του χρήστη ως συνεργάτη, καθώς ο κάτοχος του αποθετηρίου τον έχει αποκλείσει. [graphs] component_loading_failed = Δεν ήταν δυνατή η φόρτωση του %s +component_loading = Γίνεται φόρτωση του %s... +component_loading_info = Αυτό μπορεί να πάρει λίγη ώρα… +component_failed_to_load = Προέκυψε ένα απρόσμενο σφάλμα. +contributors.what = συνεισφορές +recent_commits.what = πρόσφατες υποβολές +code_frequency.what = συχνότητα κώδικα [org] org_name_holder=Όνομα Οργανισμού @@ -2596,8 +2692,8 @@ team_permission_desc=Δικαίωμα team_unit_desc=Να επιτρέπεται η Πρόσβαση σε Τμήματα του Αποθετηρίου team_unit_disabled=(Απενεργοποιημένο) -form.name_reserved=Το όνομα οργανισμού "%s" είναι δεσμευμένο. -form.name_pattern_not_allowed=Το μοτίβο "%s" δεν επιτρέπεται μέσα σε ένα όνομα οργανισμού. +form.name_reserved=Το όνομα οργανισμού «%s» είναι δεσμευμένο. +form.name_pattern_not_allowed=Το μοτίβο «%s» δεν επιτρέπεται μέσα σε ένα όνομα οργανισμού. form.create_org_not_allowed=Δεν επιτρέπεται να δημιουργήσετε έναν οργανισμό. settings=Ρυθμίσεις @@ -2647,7 +2743,7 @@ members.invite_now=Πρόσκληση Τώρα teams.join=Συμμετοχή teams.leave=Αποχώρηση -teams.leave.detail=Αποχώρηση από %s; +teams.leave.detail=Αποχώρηση από την ομάδα %s; teams.can_create_org_repo=Δημιουργία αποθετηρίων teams.can_create_org_repo_helper=Τα μέλη μπορούν να δημιουργήσουν νέα αποθετήρια στον οργανισμό. Ο δημιουργός θα αποκτήσει πρόσβαση διαχειριστή στο νέο αποθετήριο. teams.none_access=Καμία Πρόσβαση @@ -2659,7 +2755,7 @@ teams.read_access_helper=Τα μέλη μπορούν να δουν και να teams.write_access=Εγγραφή teams.write_access_helper=Τα μέλη μπορούν να δουν και να κλωνοποιήσουν τα αποθετήρια της ομάδας. teams.admin_access=Πρόσβαση Διαχειριστή -teams.admin_access_helper=Τα μέλη μπορούν να κάνουν push και pull στα αποθετήρια της ομάδας όπως και να προσθέσουν συνεργάτες σε αυτά. +teams.admin_access_helper=Τα μέλη μπορούν να κάνουν push και pull στα αποθετήρια της ομάδας, καθώς και να προσθέσουν συνεργάτες σε αυτά. teams.no_desc=Η ομάδα δεν έχει περιγραφή teams.settings=Ρυθμίσεις teams.owners_permission_desc=Οι ιδιοκτήτες έχουν πλήρη πρόσβαση σε όλα τα αποθετήρια και έχουν πρόσβαση διαχειριστή στον οργανισμό. @@ -2667,14 +2763,14 @@ teams.members=Μέλη Ομάδας teams.update_settings=Ενημέρωση Ρυθμίσεων teams.delete_team=Διαγραφή Ομάδας teams.add_team_member=Προσθήκη Μέλους Ομάδας -teams.invite_team_member=Πρόσκληση στο %s +teams.invite_team_member=Πρόσκληση στην ομάδα %s teams.invite_team_member.list=Εκκρεμείς Προσκλήσεις teams.delete_team_title=Διαγραφή Ομάδας teams.delete_team_desc=Η διαγραφή μιας ομάδας ανακαλεί τη πρόσβαση στο αποθετήριο από τα μέλη της. Συνέχεια; teams.delete_team_success=Η ομάδα έχει διαγραφεί. teams.read_permission_desc=Αυτή η ομάδα χορηγεί πρόσβαση Ανάγνωσης: τα μέλη μπορούν να δουν και να κλωνοποιήσουν τα αποθετήρια της ομάδας. teams.write_permission_desc=Αυτή η ομάδα χορηγεί πρόσβαση Εγγραφής: τα μέλη μπορούν να διαβάσουν και να κάνουν push στα αποθετήρια της ομάδας. -teams.admin_permission_desc=Αυτή η ομάδα παρέχει πρόσβαση Διαχειριστή: τα μέλη μπορούν να διαβάσουν, να κάνουν push και να προσθέσουν συνεργάτες στα αποθετήρια της ομάδας. +teams.admin_permission_desc=Αυτή η ομάδα κατέχει δικαιώματα διαχειριστή: Τα μέλη της μπορούν να δουν αποθετήρια, να κάνουν push σε αυτά καθώς και να προσθέσουν συνεργάτες. teams.create_repo_permission_desc=Επιπλέον, αυτή η ομάδα χορηγεί άδεια Δημιουργία αποθετηρίου: τα μέλη μπορούν να δημιουργήσουν νέα αποθετήρια στον οργανισμό. teams.repositories=Αποθετήρια Ομάδας teams.search_repo_placeholder=Αναζήτηση αποθετηρίου… @@ -2684,7 +2780,7 @@ teams.add_all_repos_title=Προσθήκη όλων των αποθετηρίω teams.add_all_repos_desc=Αυτό θα προσθέσει όλα τα αποθετήρια του οργανισμού στην ομάδα. teams.add_nonexistent_repo=Το αποθετήριο που προσπαθείτε να προσθέσετε δεν υπάρχει, παρακαλώ δημιουργήστε το πρώτα. teams.add_duplicate_users=Ο χρήστης είναι ήδη μέλος της ομάδας. -teams.repos.none=Δεν ήταν δυνατή η πρόσβαση στα αποθετήρια από αυτήν την ομάδα. +teams.repos.none=Αυτή η ομάδα δεν έχει πρόσβαση σε κανένα αποθετήριο. teams.members.none=Δεν υπάρχουν μέλη σε αυτήν την ομάδα. teams.specific_repositories=Συγκεκριμένα αποθετήρια teams.specific_repositories_helper=Τα μέλη θα έχουν πρόσβαση μόνο σε αποθετήρια που προστίθενται ρητά στην ομάδα. Επιλέγοντας το δεν θα θα αφαιρεθούν αυτόματα τα αποθετήρια που έχουν ήδη προστεθεί με το Όλα τα αποθετήρια. @@ -2692,10 +2788,11 @@ teams.all_repositories=Όλα τα αποθετήρια teams.all_repositories_helper=Η ομάδα έχει πρόσβαση σε όλα τα αποθετήρια. Επιλέγοντας το θα προσθεθούν όλα τα υπάρχοντα αποθετήρια στην ομάδα. teams.all_repositories_read_permission_desc=Αυτή η ομάδα χορηγεί πρόσβαση Ανάγνωσης σε όλα τα αποθετήρια: τα μέλη μπορούν να δουν και να κλωνοποιήσουν αποθετήρια. teams.all_repositories_write_permission_desc=Αυτή η ομάδα χορηγεί πρόσβαση Εγγραφής σε όλα τα αποθετήρια: τα μέλη μπορούν να διαβάσουν και να κάνουν push σε αποθετήρια. -teams.all_repositories_admin_permission_desc=Αυτή η ομάδα παρέχει πρόσβαση Διαχείρισης σε όλα τα αποθετήρια: τα μέλη μπορούν να διαβάσουν, να κάνουν push και να προσθέσουν συνεργάτες στα αποθετήρια. +teams.all_repositories_admin_permission_desc=Αυτή η ομάδα παρέχει δικαιώματα διαχειριστή σε όλα τα αποθετήρια: Τα μέλη της μπορούν να δουν αποθετήρια, να κάνουν push σε αυτά, καθώς και να προσθέσουν συνεργάτες. teams.invite.title=Έχετε προσκληθεί να συμμετάσχετε στην ομάδα %s του οργανισμού %s. teams.invite.by=Προσκλήθηκε από %s teams.invite.description=Παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για συμμετοχή στην ομάδα. +follow_blocked_user = Δεν μπορείτε να ακολουθήσετε αυτόν τον οργανισμό, επειδή ο οργανισμός σας έχει αποκλείσει. [admin] dashboard=Πίνακας Ελέγχου @@ -2716,10 +2813,10 @@ last_page=Τελευταίο total=Σύνολο: %d settings=Ρυθμίσεις Διαχειριστή -dashboard.new_version_hint=Το Forgejo %s είναι διαθέσιμο, τώρα εκτελείτε το %s. Ανατρέξτε στο blog για περισσότερες λεπτομέρειες. +dashboard.new_version_hint=Το Forgejo %s είναι διαθέσιμο, χρησιμοποιείτε το %s. Ανατρέξτε στο blog για περισσότερες λεπτομέρειες. dashboard.statistic=Περίληψη -dashboard.operations=Λειτουργίες Συντήρησης -dashboard.system_status=Κατάσταση Συστήματος +dashboard.operations=Λειτουργίες συντήρησης +dashboard.system_status=Κατάσταση συστήματος dashboard.operation_name=Όνομα Λειτουργίας dashboard.operation_switch=Αλλαγή dashboard.operation_run=Εκτέλεση @@ -2738,7 +2835,7 @@ dashboard.cron.error=Σφάλμα στη Προγραμματισμένη Εργ dashboard.cron.finished=Προγραμματισμένη Εργασία: %[1]s τελείωσε dashboard.delete_inactive_accounts=Διαγραφή όλων των μη ενεργοποιημένων λογαριασμών dashboard.delete_inactive_accounts.started=Η διαγραφή όλων των μη ενεργοποιημένων λογαριασμών ξεκίνησε. -dashboard.delete_repo_archives=Διαγραφή όλων των αρχείων λήψης του αποθετηρίου (ZIP, TAR.GZ, κλπ..) +dashboard.delete_repo_archives=Διαγραφή όλων των αρχείων λήψης του αποθετηρίου (ZIP, TAR.GZ, κλπ.) dashboard.delete_repo_archives.started=Η διαγραφή όλων των αρχείων λήψης του αποθετηρίου ξεκίνησε. dashboard.delete_missing_repos=Διαγραφή όλων των αποθετηρίων που δεν έχουν τα αρχεία Git τους dashboard.delete_missing_repos.started=Η διαγραφή όλων των αποθετηρίων που δεν έχουν αρχεία Git τους, ξεκίνησε. @@ -2751,43 +2848,43 @@ dashboard.archive_cleanup=Διαγραφή παλαιών αρχείων λήψ dashboard.deleted_branches_cleanup=Εκκαθάριση διαγραμμένων κλάδων dashboard.update_migration_poster_id=Ενημέρωση των ID συντακτών στη μεταγκατάσταση dashboard.git_gc_repos=Garbage collect όλων των αποθετηρίων -dashboard.resync_all_sshkeys=Ενημέρωση του αρχείου '.ssh/authorized_keys' με τα κλειδιά SSH του Forgejo. -dashboard.resync_all_sshprincipals=Ενημέρωση του αρχείου '.ssh/authorized_principals' με τις αρχές SSH του Forgejo. -dashboard.resync_all_hooks=Επανασυγχρονισμός των hook pre-receive, update και post-receive όλων των αποθετηρίων. +dashboard.resync_all_sshkeys=Ενημέρωση του αρχείου «.ssh/authorized_keys» με τα κλειδιά SSH του Forgejo. +dashboard.resync_all_sshprincipals=Ενημέρωση του αρχείου «.ssh/authorized_principals» με τις αρχές SSH του Forgejo. +dashboard.resync_all_hooks=Επανασυγχρονισμός των hook pre-receive, update και post-receive όλων των αποθετηρίων dashboard.reinit_missing_repos=Επανεκκινήστε όλα τα αποθετήρια Git που λείπουν και για τα οποία υπάρχουν εγγραφές dashboard.sync_external_users=Συγχρονισμός δεδομένων εξωτερικών χρηστών dashboard.cleanup_hook_task_table=Εκκαθάριση πίνακα hook_task dashboard.cleanup_packages=Εκκαθάριση ληγμένων πακέτων -dashboard.cleanup_actions=Οι ενέργειες καθαρισμού καταγραφές και αντικείμενα -dashboard.server_uptime=Διάρκεια Διακομιστή +dashboard.cleanup_actions=Καθαρισμός παλιών αρχείων καταγραφής και προϊόντων από 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.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_time=Σύνολο Παύσης GC -dashboard.total_gc_pause=Σύνολο Παύσης GC -dashboard.last_gc_pause=Τελευταία Παύση GC -dashboard.gc_times=Πλήθος 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=Ελεγκτής ενημερώσεων @@ -2800,7 +2897,7 @@ dashboard.start_schedule_tasks=Έναρξη προγραμματισμένων dashboard.sync_branch.started=Ο Συγχρονισμός των Κλάδων ξεκίνησε dashboard.rebuild_issue_indexer=Αναδόμηση ευρετηρίου ζητημάτων -users.user_manage_panel=Διαχείριση Λογαριασμών Χρηστών +users.user_manage_panel=Διαχείριση λογαριασμών χρηστών users.new_account=Δημιουργία Λογαριασμού Χρήστη users.name=Όνομα Χρήστη users.full_name=Πλήρες Όνομα @@ -2808,15 +2905,15 @@ users.activated=Ενεργοποιήθηκε users.admin=Διαχειριστής users.restricted=Περιορισμένος users.reserved=Δεσμευμένο -users.bot=Bot +users.bot=Ρομπότ users.remote=Απομακρυσμένο users.2fa=2FA users.repos=Αποθετήρια users.created=Δημιουργήθηκε users.last_login=Τελευταία Σύνδεση users.never_login=Καμία Σύνδεση -users.send_register_notify=Αποστολή Ειδοποίησης Εγγραφής Χρήστη -users.new_success=Ο λογαριασμός χρήστη "%s" δημιουργήθηκε. +users.send_register_notify=Αποστολή ειδοποιήσεων εγγραφής χρήστη +users.new_success=Ο λογαριασμός χρήστη «%s» δημιουργήθηκε. users.edit=Επεξεργασία users.auth_source=Πηγή Ταυτοποίησης users.local=Τοπική @@ -2827,11 +2924,11 @@ users.edit_account=Επεξεργασία Λογαριασμού Χρήστη users.max_repo_creation=Μέγιστος Αριθμός Αποθετηρίων users.max_repo_creation_desc=(Ορίστε -1 για να χρησιμοποιήσετε το προκαθορισμένο όριο.) users.is_activated=Ο Λογαριασμός Χρήστη Ενεργοποιήθηκε -users.prohibit_login=Απενεργοποίηση Σύνδεσης +users.prohibit_login=Απενεργοποίηση εισόδου users.is_admin=Είναι Διαχειριστής users.is_restricted=Είναι Περιορισμένος users.allow_git_hook=Μπορεί Να Δημιουργεί Git Hooks -users.allow_git_hook_tooltip=Τα Git Hooks εκτελούνται ως ο χρήστης του ΛΣ που εκτελεί το Forgejo και θα έχουν το ίδιο επίπεδο πρόσβασης στο διακομιστή. Ως αποτέλεσμα, οι χρήστες με αυτό το ειδικό προνόμιο Git Hook μπορούν να έχουν πρόσβαση και να τροποποιήσουν όλα τα αποθετήρια του Forgejo, καθώς και τη βάση δεδομένων που χρησιμοποιεί το Forgejo. Κατά συνέπεια, είναι επίσης σε θέση να αποκτήσουν προνόμια διαχειριστή του Forgejo. +users.allow_git_hook_tooltip=Τα Git Hooks εκτελούνται μέσω του χρήστη που εκτελεί το Forgejo και θα έχουν το ίδιο επίπεδο πρόσβασης στο διακομιστή. Ως αποτέλεσμα, οι χρήστες με αυτό το ειδικό προνόμιο Git Hook μπορούν να έχουν πρόσβαση και να τροποποιήσουν όλα τα αποθετήρια του Forgejo, καθώς και τη βάση δεδομένων που χρησιμοποιεί το Forgejo. Κατά συνέπεια, αυτοί οι χρήστες θα είναι σε θέση να αποκτήσουν δικαιώματα διαχειριστή στο Forgejo. users.allow_import_local=Μπορεί Να Εισάγει Τοπικά Αποθετήρια users.allow_create_organization=Μπορεί Να Δημιουργεί Οργανισμούς users.update_profile=Ενημέρωση Λογαριασμού Χρήστη @@ -2840,7 +2937,7 @@ users.cannot_delete_self=Δεν μπορείτε να διαγράψετε το users.still_own_repo=Αυτός ο χρήστης εξακολουθεί να κατέχει ένα ή περισσότερα αποθετήρια. Διαγράψτε ή μεταφέρετε αυτά τα αποθετήρια πρώτα. users.still_has_org=Αυτός ο χρήστης είναι μέλος ενός οργανισμού. Αφαιρέστε πρώτα τον χρήστη από οποιονδήποτε οργανισμό. users.purge=Εκκαθάριση Χρήστη -users.purge_help=Αναγκαστική διαγραφή χρήστη και των αποθετηρίων, οργανισμών και πακέτων που του ανήκουν. Όλα τα σχόλια επίσης θα διαγραφούν. +users.purge_help=Εξαναγκαστική διαγραφή χρήστη καθώς και των αποθετηρίων, οργανισμών και πακέτων που του ανήκουν. Όλα τα σχόλια και τα ζητήματα του χρήστη θα διαγραφούν επίσης. users.still_own_packages=Αυτός ο χρήστης εξακολουθεί να κατέχει ένα ή περισσότερα πακέτα, διαγράψτε αυτά τα πακέτα πρώτα. users.deletion_success=Ο λογαριασμός χρήστη έχει διαγραφεί. users.reset_2fa=Επαναφορά 2FA @@ -2858,7 +2955,7 @@ users.list_status_filter.is_2fa_enabled=2FA Ενεργοποιημένο users.list_status_filter.not_2fa_enabled=2FA Απενεργοποιημένο users.details=Λεπτομέρειες Χρήστη -emails.email_manage_panel=Διαχείριση Email Χρήστη +emails.email_manage_panel=Διαχείριση email χρηστών emails.primary=Κύριο emails.activated=Ενεργοποιήθηκε emails.filter_sort.email=Email @@ -2871,13 +2968,13 @@ emails.duplicate_active=Αυτή η διεύθυνση email είναι ήδη emails.change_email_header=Ενημέρωση Ιδιοτήτων Email emails.change_email_text=Είστε βέβαιοι ότι θέλετε να ενημερώσετε αυτή τη διεύθυνση email; -orgs.org_manage_panel=Διαχείριση Οργανισμού +orgs.org_manage_panel=Διαχείριση οργανισμών orgs.name=Όνομα orgs.teams=Ομάδες orgs.members=Μέλη orgs.new_orga=Νέος Οργανισμός -repos.repo_manage_panel=Διαχείριση Αποθετηρίου +repos.repo_manage_panel=Διαχείριση αποθετηρίων repos.unadopted=Μη Υιοθετημένα Αποθετήρια repos.unadopted.no_more=Δεν βρέθηκαν μη υιοθετημένα αποθετήρια repos.owner=Ιδιοκτήτης @@ -2890,7 +2987,7 @@ repos.issues=Ζητήματα repos.size=Μέγεθος repos.lfs_size=Μέγεθος LFS -packages.package_manage_panel=Διαχείριση Πακέτων +packages.package_manage_panel=Διαχείριση πακέτων packages.total_size=Συνολικό Μέγεθος: %s packages.unreferenced_size=Μέγεθος Χωρίς Αναφορά: %s packages.cleanup=Εκκαθάριση ληγμένων δεδομένων @@ -2904,17 +3001,17 @@ packages.repository=Αποθετήριο packages.size=Μέγεθος packages.published=Δημοσιευμένα -defaulthooks=Προεπιλεγμένα Webhooks +defaulthooks=Προεπιλεγμένα webhooks defaulthooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούν ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ είναι προκαθορισμένα και θα αντιγραφούν σε όλα τα νέα αποθετήρια. Διαβάστε περισσότερα στον οδηγό webhooks. defaulthooks.add_webhook=Προσθήκη Προεπιλεγμένου Webhook defaulthooks.update_webhook=Ενημέρωση Προεπιλεγμένου Webhook -systemhooks=Webhooks Συστήματος +systemhooks=Webhooks συστήματος systemhooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούνται ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ θα ενεργούν σε όλα τα αποθετήρια του συστήματος, γι 'αυτό παρακαλώ εξετάστε τυχόν επιπτώσεις απόδοσης που μπορεί να έχει. Διαβάστε περισσότερα στον οδηγό webhooks. systemhooks.add_webhook=Προσθήκη Webhook Συστήματος systemhooks.update_webhook=Ενημέρωση Webhook Συστήματος -auths.auth_manage_panel=Διαχείριση ΠηγήςΤαυτοποίησης +auths.auth_manage_panel=Διαχείριση πηγών ταυτοποίησης auths.new=Προσθήκη Πηγής Ταυτοποίησης auths.name=Όνομα auths.type=Τύπος @@ -2945,7 +3042,7 @@ auths.search_page_size=Μέγεθος Σελίδας auths.filter=Φίλτρο Χρηστών auths.admin_filter=Φίλτρο Διαχειριστών auths.restricted_filter=Φίλτρο Περιορισμένων -auths.restricted_filter_helper=Αφήστε κενό για να μην ορίσετε κανέναν χρήστη ως περιορισμένο. Χρησιμοποιήστε έναν αστερίσκο ('*') για να ορίσετε όλους τους χρήστες που δεν ταιριάζουν με το φίλτρο διαχειριστή ως περιορισμένους. +auths.restricted_filter_helper=Αφήστε κενό για να μην περιορίσετε κανέναν χρήστη. Χρησιμοποιήστε έναν αστερίσκο ('*') για να περιορίσετε όλους τους χρήστες που δεν είναι διαχειριστές. auths.verify_group_membership=Επαλήθευση της συμμετοχής σε ομάδα στο LDAP (αφήστε το φίλτρο κενό για παράλειψη) auths.group_search_base=DN Βάσης Αναζήτησης Ομάδων auths.group_attribute_list_users=Χαρακτηριστικό Ομάδας Που Περιέχει Τη Λίστα Χρηστών @@ -2954,12 +3051,12 @@ auths.map_group_to_team=Αντιστοίχιση ομάδων LDAP σε ομάδ auths.map_group_to_team_removal=Αφαίρεση χρηστών από τις συγχρονισμένες ομάδες αν ο χρήστης δεν ανήκει στην αντίστοιχη ομάδα LDAP auths.enable_ldap_groups=Ενεργοποίηση ομάδων LDAP auths.ms_ad_sa=Χαρακτηριστικά Αναζήτησης Στο MS AD -auths.smtp_auth=Τύπος Ταυτοποίησης SMTP +auths.smtp_auth=Τύπος ταυτοποίησης SMTP auths.smtphost=Διακομιστής SMTP auths.smtpport=Θύρα SMTP auths.allowed_domains=Επιτρεπόμενα Domains auths.allowed_domains_helper=Αφήστε κενό για να επιτρέψετε όλα τα domains. Διαχωρίστε τα πολλαπλά domains με κόμμα (','). -auths.skip_tls_verify=Παράλειψη TLS Verify +auths.skip_tls_verify=Παράλειψη επαλήθευσης TLS auths.force_smtps=Αναγκαστικό SMTPS auths.force_smtps_helper=Το SMTPS χρησιμοποιείται συνήθως στη θύρα 465. Ορίστε αυτό το πεδίο για χρήση του SMTPS σε άλλες θύρες. (Αλλιώς το STARTTLS θα χρησιμοποιηθεί σε άλλες θύρες αν υποστηρίζεται από τον διακομιστή.) auths.helo_hostname=Όνομα διακομιστή στο HELO @@ -2990,7 +3087,7 @@ auths.oauth2_admin_group=Τιμή Group Claim για διαχειριστές. ( auths.oauth2_restricted_group=Τιμή Group Claim για περιορισμένους χρήστες (Προαιρετικό - απαιτεί όνομα claim παραπάνω) auths.oauth2_map_group_to_team=Αντιστοίχιση των απαιτούμενων ομάδων σε ομάδες Οργανισμού. (Προαιρετικό - απαιτείται το όνομα της απαίτησης παραπάνω) auths.oauth2_map_group_to_team_removal=Αφαίρεση χρηστών από τις συγχρονισμένες ομάδες, εάν ένας χρήστης δεν ανήκει στην αντίστοιχη ομάδα. -auths.enable_auto_register=Ενεργοποίηση Αυτόματης Εγγραφής +auths.enable_auto_register=Ενεργοποίηση αυτόματης εγγραφής auths.sspi_auto_create_users=Αυτόματη δημιουργία χρηστών auths.sspi_auto_create_users_helper=Επιτρέψτε στη μέθοδο πιστοποίησης SSPI να δημιουργεί αυτόματα νέους λογαριασμούς για χρήστες που συνδέονται για πρώτη φορά auths.sspi_auto_activate_users=Αυτόματη ενεργοποίηση χρηστών @@ -3005,10 +3102,10 @@ auths.tips=Συμβουλές auths.tips.oauth2.general=Ταυτοποίηση OAuth2 auths.tips.oauth2.general.tip=Κατά την εγγραφή μιας νέας ταυτοποίησης OAuth2, το URL κλήσης/ανακατεύθυνσης πρέπει να είναι: auths.tip.oauth2_provider=Πάροχος OAuth2 -auths.tip.bitbucket=Καταχωρήστε ένα νέο καταναλωτή OAuth στο https://bitbucket.org/account/user//oauth-consumers/new και προσθέστε το δικαίωμα 'Account' - 'Read' -auths.tip.nextcloud=`Καταχωρήστε ένα νέο καταναλωτή OAuth στην υπηρεσία σας χρησιμοποιώντας το παρακάτω μενού "Settings -> Security -> OAuth 2.0 client"` +auths.tip.bitbucket=Καταχωρήστε έναν νέο καταναλωτή OAuth στο https://bitbucket.org/account/user//oauth-consumers/new και προσθέστε το δικαίωμα 'Account' - 'Read' +auths.tip.nextcloud=Καταχωρήστε ένα νέο καταναλωτή OAuth στην υπηρεσία σας χρησιμοποιώντας το παρακάτω μενού "Settings -> Security -> OAuth 2.0 client" auths.tip.dropbox=Δημιουργήστε μια νέα εφαρμογή στο https://www.dropbox.com/developers/apps -auths.tip.facebook=`Καταχωρήστε μια νέα εφαρμογή στο https://developers.facebook.com/apps και προσθέστε το προϊόν "Facebook Login"` +auths.tip.facebook=Καταχωρήστε μια νέα εφαρμογή στο https://developers.facebook.com/apps και προσθέστε το προϊόν "Facebook Login" auths.tip.github=Καταχωρήστε μια νέα εφαρμογή OAuth στο https://github.com/settings/applications/new auths.tip.gitlab=Καταχωρήστε μια νέα εφαρμογή στο https://gitlab.com/profile/applications auths.tip.google_plus=Αποκτήστε τα διαπιστευτήρια πελάτη OAuth2 από την κονσόλα API της Google στο https://console.developers.google.com/ @@ -3019,57 +3116,57 @@ auths.tip.gitea=Καταχωρήστε μια νέα εφαρμογή OAuth2. Μ auths.tip.yandex=`Δημιουργήστε μια νέα εφαρμογή στο https://oauth.yandex.com/client/new. Επιλέξτε τα ακόλουθα δικαιώματα από την ενότητα "Yandex.Passport API": "Access to email address", "Access to user avatar" και "Access to username, first name and surname, gender"` auths.tip.mastodon=Εισαγάγετε ένα προσαρμομένο URL για την υπηρεσία mastodon με την οποία θέλετε να πιστοποιήσετε (ή να χρησιμοποιήσετε την προεπιλεγμένη) auths.edit=Επεξεργασία Πηγής Ταυτοποίησης -auths.activated=Αυτή η Πηγή Ταυτοποίησης είναι Ενεργοποιημένη -auths.new_success=Ο ταυτοποίηση "%s" προστέθηκε. -auths.update_success=Η πηγή ταυτοποίησης έχει ενημερωθεί. +auths.activated=Αυτή η πηγή είναι ενεργοποιημένη +auths.new_success=Το μέσο ταυτοποίησης «%s» προστέθηκε. +auths.update_success=Η πηγή ενημερώθηκε. auths.update=Ενημέρωση Πηγής Ταυτοποίησης auths.delete=Διαγραφή Πηγής Ταυτοποίησης auths.delete_auth_title=Διαγραφή Πηγής Ταυτοποίησης -auths.delete_auth_desc=Η διαγραφή μιας πηγής ταυτοποίησης αποτρέπει τους χρήστες να τη χρησιμοποιούν για να συνδεθούν. Συνέχεια; -auths.still_in_used=Η πηγή ταυτοποίησης είναι ακόμα σε χρήση. Μετατρέψτε ή διαγράψτε χρηστών που χρησιμοποιούν αυτήν την πηγή πρώτα. +auths.delete_auth_desc=Αν διαγράψετε την πηγής ταυτοποίησης, οι χρήστες σας δεν θα μπορέσουν να τη χρησιμοποιήσουν πλέον για να συνδεθούν. Συνέχεια; +auths.still_in_used=Η πηγή ταυτοποίησης βρίσκεται ακόμα σε χρήση. Για να συνεχίσετε, πρέπει πρώτα να διαγράψετε ή να μετατρέψετε τους χρήστες που χρησιμοποιούν αυτήν την πηγή. auths.deletion_success=Η πηγή ταυτοποίησης έχει διαγραφεί. -auths.login_source_exist=Υπάρχει ήδη η πηγή ταυτοποίησης "%s". -auths.login_source_of_type_exist=Υπάρχει ήδη πηγή ταυτοποίησης αυτού του τύπου. +auths.login_source_exist=Η πηγή ταυτοποίησης «%s» υπάρχει ήδη. +auths.login_source_of_type_exist=Υπάρχει ήδη μία πηγή ταυτοποίησης αυτού του τύπου. auths.unable_to_initialize_openid=Αδυναμία εκκίνησης του παρόχου OpenID Connect: %s auths.invalid_openIdConnectAutoDiscoveryURL=Μη έγκυρο Auto Discovery URL (πρέπει να είναι ένα έγκυρο URL που ξεκινά με http:// ή https://) -config.server_config=Ρυθμίσεις Διακομιστή -config.app_name=Τίτλος Ιστοτόπου +config.server_config=Ρυθμίσεις διακομιστή +config.app_name=Τίτλος ιστοτόπου config.app_ver=Έκδοση Forgejo -config.app_url=Βασικό URL Του Forgejo -config.custom_conf=Διαδρομή Αρχείου Ρυθμίσεων -config.custom_file_root_path=Προσαρμοσμένη Βασική Διαδρομή Αρχείου -config.domain=Domain Διακομιστή -config.offline_mode=Τοπική Λειτουργία -config.disable_router_log=Απενεργοποίηση Καταγραφής Δρομολογητή -config.run_user=Εκτέλεση Σαν Χρήστη -config.run_mode=Λειτουργία Εκτέλεσης +config.app_url=Βασικό URL +config.custom_conf=Τοποθεσία αρχείου ρυθμίσεων +config.custom_file_root_path=Προσαρμοσμένη τοποθεσία αρχείων +config.domain=Domain διακομιστή +config.offline_mode=Τοπική λειτουργία +config.disable_router_log=Απενεργοποίηση καταγραφής δρομολογητή +config.run_user=Εκτέλεση ως +config.run_mode=Λειτουργία εκτέλεσης config.git_version=Έκδοση Git -config.app_data_path=Διαδρομή Δεδομένων Εφαρμογής -config.repo_root_path=Ριζική Διαδρομή Αποθετηρίων -config.lfs_root_path=Ριζική Διαδρομή LFS -config.log_file_root_path=Διαδρομή Καταγραφών -config.script_type=Τύπος Σεναρίου -config.reverse_auth_user=Χρήστης ΑντίστροφηςΠιστοποίησης +config.app_data_path=Τοποθεσία δεδομένων εφαρμογής +config.repo_root_path=Τοποθεσία αποθετηρίων +config.lfs_root_path=Τοποθεσία LFS +config.log_file_root_path=Τοποθεσία αρχείων καταγραφής +config.script_type=Τύπος σεναρίου +config.reverse_auth_user=Χρήστης αντίστροφης πιστοποίησης -config.ssh_config=Ρύθμιση SSH +config.ssh_config=Ρυθμίσεις SSH config.ssh_enabled=Ενεργοποιημένο -config.ssh_start_builtin_server=Χρήση Ενσωματωμένου Διακομιστή -config.ssh_domain=Domain Διακομιστή SSH +config.ssh_start_builtin_server=Χρήση ενσωματωμένου διακομιστή +config.ssh_domain=Domain διακομιστή SSH config.ssh_port=Θύρα config.ssh_listen_port=Θύρα Ακρόασης config.ssh_root_path=Ριζική Διαδρομή -config.ssh_key_test_path=Διαδρομή Δοκιμής Κλειδιού -config.ssh_keygen_path=Διαδρομή Keygen ('ssh-keygen') -config.ssh_minimum_key_size_check=Έλεγχος Ελάχιστου Μεγέθους Κλειδιού -config.ssh_minimum_key_sizes=Ελάχιστα Μεγέθη Κλειδιών +config.ssh_key_test_path=Διαδρομή δοκιμής κλειδιού +config.ssh_keygen_path=Διαδρομή keygen («ssh-keygen») +config.ssh_minimum_key_size_check=Έλεγχος ελάχιστου μεγέθους κλειδιού +config.ssh_minimum_key_sizes=Ελάχιστα μεγέθη κλειδιών -config.lfs_config=Ρύθμιση LFS +config.lfs_config=Ρυθμίσεις LFS config.lfs_enabled=Ενεργοποιημένο -config.lfs_content_path=Διαδρομή Περιεχομένου LFS -config.lfs_http_auth_expiry=LFS Λήξη Ταυτοποίησης HTTP +config.lfs_content_path=Τοποθεσία περιεχομένου LFS +config.lfs_http_auth_expiry=Χρονικό όριο ταυτοποίησης HTTP LFS -config.db_config=Ρύθμιση Βάσης Δεδομένων +config.db_config=Ρυθμίσεις βάσης δεδομένων config.db_type=Τύπος config.db_host=Διακομιστής config.db_name=Όνομα @@ -3078,34 +3175,34 @@ config.db_schema=Σχήμα config.db_ssl_mode=SSL config.db_path=Διαδρομή -config.service_config=Ρυθμίσεις Υπηρεσίας -config.register_email_confirm=Απαιτείται Επιβεβαίωση του Email για Εγγραφή -config.disable_register=Απενεργοποίηση Αυτοεγγραφής -config.allow_only_internal_registration=Να Επιτρέπεται η Εγγραφή Μόνο Μέσω του Forgejo -config.allow_only_external_registration=Να Επιτρέπεται Η Εγγραφή Μόνο Μέσω Εξωτερικών Υπηρεσιών -config.enable_openid_signup=Ενεργοποίηση Αυτο-Εγγραφής OpenID -config.enable_openid_signin=Ενεργοποίηση Σύνδεσης μέσω OpenID -config.show_registration_button=Εμφάνιση Κουμπιού Εγγραφής -config.require_sign_in_view=Απαιτείται Είσοδος για Προβολή Σελίδων -config.mail_notify=Ενεργοποίηση Ειδοποιήσεων Email +config.service_config=Ρυθμίσεις υπηρεσίας +config.register_email_confirm=Να απαιτείται η επιβεβαίωση της διεύθυνσης email για την δημιουργία ενός λογαριασμού +config.disable_register=Απενεργοποίηση αυτο-εγγραφής +config.allow_only_internal_registration=Να επιτρέπονται εγγραφές μόνο μέσω του Forgejo +config.allow_only_external_registration=Να επιτρέπονται εγγραφές μόνο με την χρήση εξωτερικών υπηρεσιών +config.enable_openid_signup=Ενεργοποίηση αυτο-εγγραφής OpenID +config.enable_openid_signin=Ενεργοποίηση σύνδεσης μέσω OpenID +config.show_registration_button=Εμφάνιση κουμπιού εγγραφής +config.require_sign_in_view=Να απαιτείται είσοδος για την προβολή σελίδων +config.mail_notify=Ενεργοποίηση ειδοποιήσεων email config.enable_captcha=Ενεργοποίηση CAPTCHA -config.active_code_lives=Ζωή Ενεργού Κωδικού -config.reset_password_code_lives=Λήξη Χρόνου Κωδικού Ανάκτησης του Λογαριασμού -config.default_keep_email_private=Απόκρυψη Διευθύνσεων Email από Προεπιλογή -config.default_allow_create_organization=Να Επιτρέπεται η Δημιουργία Οργανισμών από Προεπιλογή -config.enable_timetracking=Ενεργοποίηση Καταγραφής Χρόνου -config.default_enable_timetracking=Ενεργοποίηση Καταγραφής Χρόνου σαν Προεπιλογή -config.default_allow_only_contributors_to_track_time=Επιτρέπονται Μόνο οι Συμμετέχοντες να Καταγράφουν Χρόνο -config.no_reply_address=Κρυφό Email Domain -config.default_visibility_organization=Προεπιλεγμένη ορατότητα για νέους οργανισμούς -config.default_enable_dependencies=Ενεργοποίηση Εξαρτήσεων Ζητημάτων από Προεπιλογή +config.active_code_lives=Χρόνος λήξης κωδικών ενεργοποίησης +config.reset_password_code_lives=Χρόνος λήξης κωδικού ανάκτησης ενός λογαριασμού +config.default_keep_email_private=Να αποκρύπτονται οι διευθύνσεις email από προεπιλογή +config.default_allow_create_organization=Να επιτρέπεται η δημιουργία οργανισμών από προεπιλογή +config.enable_timetracking=Ενεργοποίηση καταγραφής χρόνου +config.default_enable_timetracking=Ενεργοποίηση καταγραφής χρόνου από προεπιλογή +config.default_allow_only_contributors_to_track_time=Να επιτρέπεται η καταγραφή χρόνου μόνο από συνεισφέροντες +config.no_reply_address=Κρυφό email domain +config.default_visibility_organization=Προεπιλεγμένη ορατότητα νέων οργανισμών +config.default_enable_dependencies=Ενεργοποίηση εξαρτήσεων ζητημάτων από προεπιλογή -config.webhook_config=Ρύθμιση Webhook -config.queue_length=Μέγεθος Ουράς -config.deliver_timeout=Χρονικό Όριο Παράδοσης -config.skip_tls_verify=Παράλειψη Επαλήθευσης TLS +config.webhook_config=Ρύθμιση webhook +config.queue_length=Μέγεθος ουράς +config.deliver_timeout=Χρονικό όριο παράδοσης +config.skip_tls_verify=Παράλειψη επαλήθευσης TLS -config.mailer_config=Ρυθμίσεις Αλληλογραφίας +config.mailer_config=Ρυθμίσεις αλληλογραφίας config.mailer_enabled=Ενεργοποιημένο config.mailer_enable_helo=Ενεργοποίηση HELO config.mailer_name=Όνομα @@ -3121,56 +3218,56 @@ config.mailer_use_dummy=Ψεύτικο config.test_email_placeholder=Email (π.χ. test@example.com) config.send_test_mail=Αποστολή Δοκιμαστικού Email config.send_test_mail_submit=Αποστολή -config.test_mail_failed=Αποτυχία αποστολής ενός δοκιμαστικού email στο"%s": %v -config.test_mail_sent=Στάλθηκε ένα δοκιμαστικό email στο "%s". +config.test_mail_failed=Η αποστολή δοκιμαστικού email στο «%s» απέτυχε: %v +config.test_mail_sent=Στάλθηκε ένα δοκιμαστικό email στο «%s». config.oauth_config=Ρύθμιση Oauth config.oauth_enabled=Ενεργό -config.cache_config=Ρύθμιση Προσωρινής Αποθήκευσης -config.cache_adapter=Προσαρμογέας Προσωρινής Αποθήκευσης -config.cache_interval=Διάστημα Προσωρινής Αποθήκευσης +config.cache_config=Ρύθμιση προσωρινής αποθήκευσης +config.cache_adapter=Προσαρμογέας προσωρινής αποθήκευσης +config.cache_interval=Διάστημα προσωρινής αποθήκευσης config.cache_conn=Σύνδεση Προσωρινής Αποθήκευσης config.cache_item_ttl=TTL Στοιχείων Προσωρινής Αποθήκευσης -config.session_config=Ρύθμιση Συνεδρίας -config.session_provider=Πάροχος Συνεδρίας -config.provider_config=Ρυθμίσεις Πάροχου -config.cookie_name=Όνομα Cookie -config.gc_interval_time=Χρόνος Διαστήματος GC -config.session_life_time=Χρόνος Ζωής Συνεδρίας +config.session_config=Ρυθμίσεις συνεδρίας +config.session_provider=Πάροχος συνεδρίας +config.provider_config=Ρυθμίσεις παρόχου +config.cookie_name=Όνομα cookie +config.gc_interval_time=Χρόνος διαστήματος GC +config.session_life_time=Χρόνος ζωής συνεδρίας config.https_only=Μόνο HTTPS -config.cookie_life_time=Χρόνος Ζωής Cookie +config.cookie_life_time=Χρόνος ζωής Cookie -config.picture_config=Ρύθμιση Εικόνας και Avatar -config.picture_service=Υπηρεσία Εικόνας +config.picture_config=Ρυθμίσεις εικόνας και avatar +config.picture_service=Υπηρεσία εικόνας config.disable_gravatar=Απενεργοποίηση Gravatar -config.enable_federated_avatar=Ενεργοποίηση Ομόσπονδων Avatars +config.enable_federated_avatar=Ενεργοποίηση αποκεντρωμένων avatar -config.git_config=Ρύθμιση Git -config.git_disable_diff_highlight=Απενεργοποίηση Επισήμανσης Σύνταξης Diff -config.git_max_diff_lines=Μέγιστες γραμμές Diff (για ένα μόνο αρχείο) -config.git_max_diff_line_characters=Μέγιστος αριθμός χαρακτήρων Diff (για μία γραμμή) -config.git_max_diff_files=Μέγιστος αριθμός Diff αρχείων (για εμφάνιση) +config.git_config=Ρυθμίσεις Git +config.git_disable_diff_highlight=Απενεργοποίηση επισήμανσης σύνταξης diff +config.git_max_diff_lines=Μέγιστες γραμμές diff ανά αρχείο +config.git_max_diff_line_characters=Μέγιστος αριθμός χαρακτήρων diff ανά γραμμή +config.git_max_diff_files=Μέγιστος αριθμός εμφανιζόμενων αρχείων ανά diff config.git_gc_args=Παράμετροι GC -config.git_migrate_timeout=Χρονικό Όριο Μεταφοράς -config.git_mirror_timeout=Χρονικό Όριο Ενημέρωσης Ειδώλου -config.git_clone_timeout=Χρονικό Όριο Κλωνοποίησης -config.git_pull_timeout=Χρονικό Όριο Pull -config.git_gc_timeout=Χρονικό Όριο Λειτουργίας GC +config.git_migrate_timeout=Χρονικό όριο μεταφοράς +config.git_mirror_timeout=Χρονικό όριο ενημέρωσης ειδώλου +config.git_clone_timeout=Χρονικό όριο κλωνοποίησης +config.git_pull_timeout=Χρονικό όριο pull +config.git_gc_timeout=Χρονικό όριο λειτουργίας GC -config.log_config=Ρύθμιση Καταγραφών +config.log_config=Ρυθμίσεις Καταγραφών config.logger_name_fmt=Καταγραφέας: %s config.disabled_logger=Απενεργοποιημένο -config.access_log_mode=Λειτουργία Καταγραφών Πρόσβασης -config.access_log_template=Πρότυπο Καταγραφής Προσβάσεων +config.access_log_mode=Λειτουργία καταγραφών πρόσβασης +config.access_log_template=Πρότυπο καταγραφής προσβάσεων config.xorm_log_sql=Καταγραφή SQL -config.set_setting_failed=Αποτυχία ορισμού της ρύθμισης %s +config.set_setting_failed=Ο ορισμός της ρύθμισης %s απέτυχε monitor.stats=Στατιστικά -monitor.cron=Προγραμματισμένες Εργασίες +monitor.cron=Προγραμματισμένες εργασίες monitor.name=Όνομα monitor.schedule=Πρόγραμμα monitor.next=Επόμενη Ώρα @@ -3209,8 +3306,8 @@ 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.system_notice_list=Ειδοποιήσεις συστήματος +notices.view_detail_header=Προβολή λεπτομερειών ειδοποίησης notices.operations=Λειτουργίες notices.select_all=Επιλογή Όλων notices.deselect_all=Αποεπιλογή Όλων @@ -3223,6 +3320,13 @@ notices.type_2=Εργασία notices.desc=Περιγραφή notices.op=Λειτ. notices.delete_success=Οι ειδοποιήσεις του συστήματος έχουν διαγραφεί. +self_check.no_problem_found = Μέχρι τώρα, δεν έχει βρεθεί κάποιο πρόβλημα. +self_check.database_fix_mssql = Προς το παρόν, οι χρήστες του MSSQL μπορούν να διορθώσουν το πρόβλημα αυτό χειροκίνητα χρησιμοποιώντας τις εντολές SQL «ALTER ... COLLATE ...». +self_check = Αυτοέλεγχος +dashboard.sync_repo_tags = Συγχρονισμός tag από δεδομένα git στην βάση δεδομένων +dashboard.sync_tag.started = Ο συγχρονισμός tag έχει ξεκινήσει +self_check.database_inconsistent_collation_columns = Η βάση δεδομένων χρησιμοποιεί το collation %s, αλλά οι στήλες του χρησιμοποιούν collations που δεν αντιστοιχούν σε εκείνο το collation. Αυτό ενδέχεται να προκαλέσει μερικά απρόσμενα θέματα. +self_check.database_fix_mysql = Για τους χρήστες του MySQL/MariaDB: Μπορείτε να χρησιμοποιήσετε την εντολή «git doctor convert» για να διορθώσετε το collation ή να το διορθώσετε χειροκίνητα με τις εντολές «ALTER ... COLLATE ...». [action] @@ -3304,8 +3408,8 @@ 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.not_signed_commit=Η υποβολή δεν είναι υπογεγραμμένη +error.failed_retrieval_gpg_keys=Αποτυχία ανάκτησης κλειδιού που είναι συνδεδεμένο στο λογαριασμό του υποβολέα error.probable_bad_signature=ΠΡΟΣΟΧΗ! Αν και υπάρχει ένα κλειδί με αυτό το ID στη βάση δεδομένων δεν επαληθεύει αυτή την υποβολή! Αυτή η υποβολή είναι ΥΠΟΠΤΗ. error.probable_bad_default_signature=ΠΡΟΣΟΧΗ! Αν και το προεπιλεγμένο κλειδί έχει αυτό το ID, δεν επαληθεύει αυτή την υποβολή! Αυτή η υποβολή είναι ΥΠΟΠΤΗ. @@ -3318,9 +3422,9 @@ error.unit_not_allowed=Δεν σας επιτρέπεται να έχετε πρ title=Πακέτα desc=Διαχείριση πακέτων μητρώου. empty=Δεν υπάρχουν πακέτα ακόμα. -empty.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο πακέτων, ανατρέξτε στην τεκμηρίωση. +empty.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο πακέτων, συμβουλευτείτε τον οδηγό. empty.repo=Μήπως ανεβάσατε ένα πακέτο, αλλά δεν εμφανίζεται εδώ; Πηγαίνετε στις ρυθμίσεις πακέτων και συνδέστε το σε αυτό το αποθετήριο. -registry.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο %s, ανατρέξτε στη τεκμηρίωση . +registry.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο %s, συμβουλευτείτε τον οδηγό. filter.type=Τύπος filter.type.all=Όλα filter.no_result=Το φίλτρο δεν παρήγαγε αποτελέσματα. @@ -3429,10 +3533,10 @@ settings.link.success=Ο σύνδεσμος αποθετηρίου ενημερ settings.link.error=Αποτυχία ενημέρωσης συνδέσμου αποθετηρίου. settings.delete=Διαγραφή πακέτου settings.delete.description=Η διαγραφή ενός πακέτου είναι μόνιμη και δεν μπορεί να αναιρεθεί. -settings.delete.notice=Πρόκειται να διαγράψετε %s (%s). Αυτή η λειτουργία είναι μη αναστρέψιμη, είστε σίγουροι; +settings.delete.notice=Πρόκειται να διαγράψετε το %s (%s). Αυτή η διαδικασία είναι μη αναστρέψιμη, είστε σίγουροι; settings.delete.success=Το πακέτο έχει διαγραφεί. settings.delete.error=Αποτυχία διαγραφής του πακέτου. -owner.settings.cargo.title=Ευρετήριο Μητρώου Cargo +owner.settings.cargo.title=Ευρετήριο μητρώου Cargo owner.settings.cargo.initialize=Αρχικοποίηση Ευρετηρίου owner.settings.cargo.initialize.description=Απαιτείται ένα ειδικό αποθετήριο ευρετηρίου Git για τη χρήση του μητρώου Cargo. Χρησιμοποιώντας αυτή την επιλογή θα δημιουργηθεί ξανά το αποθετήριο και θα ρυθμιστεί αυτόματα. owner.settings.cargo.initialize.error=Αποτυχία αρχικοποίησης ευρετηρίου Cargo: %v @@ -3441,7 +3545,7 @@ 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.title=Διαχείριση κανόνων εκκαθάρισης owner.settings.cleanuprules.add=Προσθήκη Κανόνα Εκκαθάρισης owner.settings.cleanuprules.edit=Επεξεργασία Κανόνα Εκκαθάρισης owner.settings.cleanuprules.none=Δεν υπάρχουν διαθέσιμοι κανόνες εκκαθάρισης. Παρακαλούμε συμβουλευτείτε την τεκμηρίωση. @@ -3464,6 +3568,7 @@ owner.settings.cleanuprules.success.delete=Ο κανόνας καθαρισμο owner.settings.chef.title=Μητρώο Chef owner.settings.chef.keypair=Δημιουργία ζεύγους κλειδιών owner.settings.chef.keypair.description=Ένα ζεύγος κλειδιών είναι απαραίτητο για ταυτοποίηση στο μητρώο Chef. Αν έχετε δημιουργήσει ένα ζεύγος κλειδιών πριν, η δημιουργία ενός νέου ζεύγους κλειδιών θα απορρίψει το παλιό ζεύγος κλειδιών. +rpm.repository.multiple_groups = Αυτό το πακέτο είναι διαθέσιμο σε διαφορετικά group. [secrets] secrets=Μυστικά @@ -3472,7 +3577,7 @@ none=Δεν υπάρχουν ακόμα μυστικά. creation=Προσθήκη Μυστικού creation.name_placeholder=αλφαριθμητικοί χαρακτήρες ή κάτω παύλες μόνο, δεν μπορούν να ξεκινούν με GITEA_ ή GITHUB_ creation.value_placeholder=Εισάγετε οποιοδήποτε περιεχόμενο. Τα κενά στην αρχή παραλείπονται. -creation.success=Το μυστικό "%s" προστέθηκε. +creation.success=Το μυστικό «%s» προστέθηκε. creation.failed=Αποτυχία δημιουργίας μυστικού. deletion=Αφαίρεση μυστικού deletion.description=Η αφαίρεση ενός μυστικού είναι μόνιμη και δεν μπορεί να αναιρεθεί. Συνέχεια; @@ -3481,21 +3586,21 @@ deletion.failed=Αποτυχία αφαίρεσης μυστικού. management=Διαχείριση Μυστικών [actions] -actions=Δράσεις +actions=Actions unit.desc=Διαχείριση δράσεων -status.unknown=Άγνωστη -status.waiting=Αναμονή -status.running=Εκτελείται -status.success=Επιτυχές +status.unknown=Απροσδιόριστη +status.waiting=Σε αναμονή +status.running=Σε εκτέλεση +status.success=Επιτυχία status.failure=Αποτυχία status.cancelled=Ακυρώθηκε status.skipped=Παρακάμφθηκε status.blocked=Αποκλείστηκε runners=Εκτελεστές -runners.runner_manage_panel=Διαχείριση Εκτελεστών +runners.runner_manage_panel=Διαχείριση εκτελεστών runners.new=Δημιουργία νέου Εκτελεστή runners.new_notice=Πώς να ξεκινήσετε έναν εκτελεστή runners.status=Κατάσταση @@ -3547,9 +3652,9 @@ runs.no_runs=Η ροή εργασίας δεν έχει τρέξει ακόμα. runs.empty_commit_message=(κενό μήνυμα υποβολής) workflow.disable=Απενεργοποίηση Ροής Εργασιών -workflow.disable_success=Η ροή εργασίας '%s' απενεργοποιήθηκε επιτυχώς. +workflow.disable_success=Η ροή εργασίας «%s» απενεργοποιήθηκε επιτυχώς. workflow.enable=Ενεργοποίηση Ροής Εργασίας -workflow.enable_success=Η ροή εργασίας '%s' ενεργοποιήθηκε επιτυχώς. +workflow.enable_success=Η ροή εργασίας «%s» ενεργοποιήθηκε επιτυχώς. workflow.disabled=Η ροή εργασιών είναι απενεργοποιημένη. need_approval_desc=Πρέπει να εγκριθεί η εκτέλεση ροών εργασίας για pull request από fork. @@ -3565,12 +3670,13 @@ variables.edit=Επεξεργασία Μεταβλητής variables.deletion.failed=Αποτυχία αφαίρεσης της μεταβλητής. variables.deletion.success=Η μεταβλητή έχει αφαιρεθεί. variables.creation.failed=Αποτυχία προσθήκης μεταβλητής. -variables.creation.success=Η μεταβλητή "%s" έχει προστεθεί. +variables.creation.success=Η μεταβλητή «%s» προστέθηκε. variables.update.failed=Αποτυχία επεξεργασίας μεταβλητής. variables.update.success=Η μεταβλητή έχει τροποποιηθεί. variables.id_not_exist = Η μεταβλητή με id %d δεν υπάρχει. -runs.no_workflows.documentation = Για περισσότερες πληροφορίες σχετικά με τη Δράση Gitea, ανατρέξτε στην τεκμηρίωση. -runs.no_workflows.quick_start = Δεν ξέρετε πώς να ξεκινήσετε με τις Δράσεις Gitea; Συμβουλευτείτε τον οδηγό για γρήγορη αρχή. +runs.no_workflows.documentation = Για περισσότερες πληροφορίες σχετικά με το Forgejo Actions, συμβουλευτείτε τον οδηγό. +runs.no_workflows.quick_start = Δεν ξέρετε από που να πρωτοξεκινήσετε με το Forgejo Actions; Για μια γρήγορη αρχή, συμβουλευτείτε τον οδηγό μας. +runs.workflow = Ροή εργασίας [projects] type-1.display_name=Ατομικό Έργο diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 07ad19aa7a..50871be86d 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -34,8 +34,8 @@ password = Password access_token = Access token re_type = Confirm password captcha = CAPTCHA -twofa = Two-Factor Authentication -twofa_scratch = Two-Factor Scratch Code +twofa = Two-factor authentication +twofa_scratch = Two-factor scratch code passcode = Passcode webauthn_insert_key = Insert your security key @@ -55,15 +55,15 @@ webauthn_reload = Reload repository = Repository organization = Organization mirror = Mirror -new_repo = New Repository -new_migrate = New Migration -new_mirror = New Mirror -new_fork = New Repository Fork -new_org = New Organization -new_project = New Project -new_project_column = New Column -admin_panel = Site Administration -account_settings = Account Settings +new_repo = New repository +new_migrate = New migration +new_mirror = New mirror +new_fork = New repository fork +new_org = New organization +new_project = New project +new_project_column = New column +admin_panel = Site administration +account_settings = Account settings settings = Settings your_profile = Profile your_starred = Starred @@ -76,7 +76,7 @@ collaborative = Collaborative forks = Forks activities = Activities -pull_requests = Pull Requests +pull_requests = Pull requests issues = Issues milestones = Milestones @@ -164,7 +164,10 @@ footer.links = Links [heatmap] number_of_contributions_in_the_last_12_months = %s contributions in the last 12 months -no_contributions = No contributions +contributions_zero = No contributions +contributions_format = {contributions} on {month} {day}, {year} +contributions_one = contribution +contributions_few = contributions less = Less more = More @@ -210,15 +213,15 @@ license_desc = Go get documentation before changing any settings. require_db_desc = Forgejo requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). -db_title = Database Settings -db_type = Database Type +db_title = Database settings +db_type = Database type host = Host user = Username password = Password -db_name = Database Name +db_name = Database name db_schema = Schema db_schema_helper = Leave blank for database default ("public"). ssl_mode = SSL @@ -237,61 +240,62 @@ err_admin_name_is_reserved = Administrator Username is invalid, username is rese err_admin_name_pattern_not_allowed = Administrator username is invalid, the username matches a reserved pattern err_admin_name_is_invalid = Administrator Username is invalid -general_title = General Settings -app_name = Site Title +general_title = General settings +app_name = Instance title app_name_helper = You can enter your company name here. -repo_path = Repository Root Path +repo_path = Repository root path repo_path_helper = Remote Git repositories will be saved to this directory. -lfs_path = Git LFS Root Path +lfs_path = Git LFS root path lfs_path_helper = Files tracked by Git LFS will be stored in this directory. Leave empty to disable. -run_user = Run As Username +run_user = User to run as run_user_helper = The operating system username that Forgejo runs as. Note that this user must have access to the repository root path. -domain = Server Domain +domain = Server domain domain_helper = Domain or host address for the server. -ssh_port = SSH Server Port -ssh_port_helper = Port number your SSH server listens on. Leave empty to disable. -http_port = Forgejo HTTP Listen Port -http_port_helper = Port number the Forgejos web server will listen on. -app_url = Forgejo Base URL +ssh_port = SSH server port +ssh_port_helper = Port number that will be used by the SSH server. Leave empty to disable SSH server. +http_port = HTTP listen port +http_port_helper = Port number that will be used by the Forgejo web server. +app_url = Base URL app_url_helper = Base address for HTTP(S) clone URLs and email notifications. -log_root_path = Log Path +log_root_path = Log path log_root_path_helper = Log files will be written to this directory. -optional_title = Optional Settings -email_title = Email Settings -smtp_addr = SMTP Host -smtp_port = SMTP Port -smtp_from = Send Email As +optional_title = Optional settings +email_title = Email settings +smtp_addr = SMTP host +smtp_port = SMTP port +smtp_from = Send email as smtp_from_invalid = The "Send Email As" address is invalid smtp_from_helper = Email address Forgejo will use. Enter a plain email address or use the "Name" format. -mailer_user = SMTP Username -mailer_password = SMTP Password -register_confirm = Require Email Confirmation to Register -mail_notify = Enable Email Notifications -server_service_title = Server and Third-Party Service Settings -offline_mode = Enable Local Mode +mailer_user = SMTP username +mailer_password = SMTP password +register_confirm = Require email confirmation to register +mail_notify = Enable email notifications +server_service_title = Server and third-party service settings +offline_mode = Enable local mode offline_mode_popup = Disable third-party content delivery networks and serve all resources locally. disable_gravatar = Disable Gravatar disable_gravatar_popup = Disable Gravatar and third-party avatar sources. A default avatar will be used unless a user locally uploads an avatar. -federated_avatar_lookup = Enable Federated Avatars +federated_avatar_lookup = Enable federated avatars federated_avatar_lookup_popup = Enable federated avatar lookup using Libravatar. -disable_registration = Disable Self-Registration +disable_registration = Disable self-registration disable_registration_popup = Disable user self-registration. Only administrators will be able to create new user accounts. -allow_only_external_registration_popup = Allow Registration Only Through External Services -openid_signin = Enable OpenID Sign-In +allow_only_external_registration_popup = Allow registration only through external services +openid_signin = Enable OpenID sign-in openid_signin_popup = Enable user sign-in via OpenID. -openid_signup = Enable OpenID Self-Registration +openid_signup = Enable OpenID self-registration openid_signup_popup = Enable OpenID-based user self-registration. enable_captcha = Enable registration CAPTCHA enable_captcha_popup = Require a CAPTCHA for user self-registration. -require_sign_in_view = Require Sign-In to View Pages +require_sign_in_view = Require to sign-in to view instance content require_sign_in_view_popup = Limit page access to signed-in users. Visitors will only see the sign-in and registration pages. admin_setting_desc = Creating an administrator account is optional. The first registered user will automatically become an administrator. -admin_title = Administrator Account Settings -admin_name = Administrator Username +admin_title = Administrator account settings +admin_name = Administrator username admin_password = Password -confirm_password = Confirm Password -admin_email = Email Address +confirm_password = Confirm password +admin_email = Email address +config_location_hint = These configuration options will be saved in: install_btn_confirm = Install Forgejo test_git_failed = Could not test "git" command: %v sqlite3_not_available = This Forgejo version does not support SQLite3. Please download the official binary version from %s (not the "gobuild" version). @@ -299,26 +303,26 @@ invalid_db_setting = The database settings are invalid: %v invalid_db_table = The database table "%s" is invalid: %v invalid_repo_path = The repository root path is invalid: %v invalid_app_data_path = The app data path is invalid: %v -run_user_not_match = The "run as" username is not the current username: %s -> %s +run_user_not_match = The "user to run as" username is not the current username: %s -> %s internal_token_failed = Failed to generate internal token: %v secret_key_failed = Failed to generate secret key: %v save_config_failed = Failed to save configuration: %v -enable_update_checker_helper_forgejo = Periodically checks for new Forgejo versions by checking a DNS TXT record at release.forgejo.org. +enable_update_checker_helper_forgejo = It will periodically check for new Forgejo versions by checking a TXT DNS record at release.forgejo.org. invalid_admin_setting = Administrator account setting is invalid: %v invalid_log_root_path = The log path is invalid: %v -default_keep_email_private = Hide Email Addresses by Default +default_keep_email_private = Hide email addresses by default default_keep_email_private_popup = Hide email addresses of new user accounts by default. -default_allow_create_organization = Allow Creation of Organizations by Default +default_allow_create_organization = Allow creation of organizations by default default_allow_create_organization_popup = Allow new user accounts to create organizations by default. -default_enable_timetracking = Enable Time Tracking by Default +default_enable_timetracking = Enable time tracking by default default_enable_timetracking_popup = Enable time tracking for new repositories by default. allow_dots_in_usernames = Allow users to use dots in their usernames. Doesn't affect existing accounts. -no_reply_address = Hidden Email Domain +no_reply_address = Hidden email domain no_reply_address_helper = Domain name for users with a hidden email address. For example, the username "joe" will be logged in Git as "joe@noreply.example.org" if the hidden email domain is set to "noreply.example.org". -password_algorithm = Password Hash Algorithm +password_algorithm = Password hash algorithm invalid_password_algorithm = Invalid password hash algorithm password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems. -enable_update_checker = Enable Update Checker +enable_update_checker = Enable update checker enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io. env_config_keys = Environment Configuration env_config_keys_prompt = The following environment variables will also be applied to your configuration file: @@ -328,10 +332,9 @@ uname_holder = Username or Email address password_holder = Password switch_dashboard_context = Switch Dashboard Context my_repos = Repositories +my_orgs = Organizations show_more_repos = Show more repositories… collaborative_repos = Collaborative Repositories -my_orgs = My Organizations -my_mirrors = My Mirrors view_home = View %s search_repos = Find a repository… filter = Other Filters @@ -353,6 +356,10 @@ issues.in_your_repos = In your repositories [explore] repos = Repositories users = Users +stars_one = %d star +stars_few = %d stars +forks_one = %d fork +forks_few = %d forks organizations = Organizations search = Search go_to = Go to @@ -391,7 +398,7 @@ allow_password_change = Require user to change password (recommended) reset_password_mail_sent_prompt = A confirmation email has been sent to %s. Please check your inbox within the next %s to complete the account recovery process. active_your_account = Activate Your Account account_activated = Account has been activated -prohibit_login = Sign In Prohibited +prohibit_login = Signing in is prohibited prohibit_login_desc = Your account is prohibited from signing in, please contact your site administrator. resent_limit_prompt = You have already requested an activation email recently. Please wait 3 minutes and try again. has_unconfirmed_mail = Hi %s, you have an unconfirmed email address (%s). If you haven't received a confirmation email or need to resend a new one, please click on the button below. @@ -416,7 +423,9 @@ twofa_scratch_used = You have used your scratch code. You have been redirected t twofa_passcode_incorrect = Your passcode is incorrect. If you misplaced your device, use your scratch code to sign in. twofa_scratch_token_incorrect = Your scratch code is incorrect. login_userpass = Sign In -login_openid = OpenID +tab_signin = Sign In +tab_signup = Sign Up +tab_openid = OpenID oauth_signup_tab = Register New Account oauth_signup_title = Complete New Account oauth_signup_submit = Complete Account @@ -463,7 +472,7 @@ activate_email.title = %s, please verify your email address activate_email.text = Please click the following link to verify your email address within %s: admin.new_user.subject = New user %s just signed up -admin.new_user.user_info = User Information +admin.new_user.user_info = User information admin.new_user.text = Please click here to manage this user from the admin panel. register_notify = Welcome to Forgejo @@ -504,13 +513,13 @@ release.downloads = Downloads: release.download.zip = Source Code (ZIP) release.download.targz = Source Code (TAR.GZ) -repo.transfer.subject_to = %s would like to transfer "%s" to %s -repo.transfer.subject_to_you = %s would like to transfer "%s" to you +repo.transfer.subject_to = %s wants to transfer repository "%s" to %s +repo.transfer.subject_to_you = %s wants to transfer repository "%s" to you repo.transfer.to_you = you repo.transfer.body = To accept or reject it visit %s or just ignore it. -repo.collaborator.added.subject = %s added you to %s -repo.collaborator.added.text = You have been added as a collaborator of repository: +repo.collaborator.added.subject = %s added you to %s as collaborator +repo.collaborator.added.text = You have been added as a collaborator to repository: team_invite.subject = %[1]s has invited you to join the %[2]s organization team_invite.text_1 = %[1]s has invited you to join team %[2]s in organization %[3]s. @@ -529,7 +538,7 @@ UserName = Username RepoName = Repository name Email = Email address Password = Password -Retype = Confirm Password +Retype = Confirm password SSHTitle = SSH key name HttpsUrl = HTTPS URL PayloadUrl = Payload URL @@ -594,6 +603,8 @@ enterred_invalid_repo_name = The repository name you entered is incorrect. enterred_invalid_org_name = The organization name you entered is incorrect. enterred_invalid_owner_name = The new owner name is not valid. enterred_invalid_password = The password you entered is incorrect. +unset_password = The login user has not set the password. +unsupported_login_type = The login type is not supported to delete account. user_not_exist = The user does not exist. team_not_exist = The team does not exist. last_org_owner = You cannot remove the last user from the "owners" team. There must be at least one owner for an organization. @@ -622,16 +633,16 @@ admin_cannot_delete_self = You cannot delete yourself when you are an admin. Ple change_avatar = Change your avatar… joined_on = Joined on %s repositories = Repositories -activity = Public Activity +activity = Public activity followers = Followers -block_user = Block User +block_user = Block user block_user.detail = Please understand that if you block this user, other actions will be taken. Such as: block_user.detail_1 = You are being unfollowed from this user. block_user.detail_2 = This user cannot interact with your repositories, created issues and comments. block_user.detail_3 = This user cannot add you as a collaborator, nor can you add them as a collaborator. follow_blocked_user = You cannot follow this user because you have blocked this user or this user has blocked you. -starred = Starred Repositories -watched = Watched Repositories +starred = Starred repositories +watched = Watched repositories code = Code projects = Projects overview = Overview @@ -645,7 +656,7 @@ disabled_public_activity = This user has disabled the public visibility of the a email_visibility.limited = Your email address is visible to all authenticated users email_visibility.private = Your email address is only visible to you and administrators show_on_map = Show this place on a map -settings = User Settings +settings = User settings form.name_reserved = The username "%s" is reserved. form.name_pattern_not_allowed = The pattern "%s" is not allowed in a username. @@ -658,30 +669,30 @@ appearance = Appearance password = Password security = Security avatar = Avatar -ssh_gpg_keys = SSH / GPG Keys +ssh_gpg_keys = SSH / GPG keys social = Social Accounts applications = Applications orgs = Manage organizations repos = Repositories delete = Delete Account -twofa = Two-Factor Authentication (TOTP) +twofa = Two-factor authentication (TOTP) account_link = Linked Accounts organization = Organizations uid = UID -webauthn = Two-Factor Authentication (Security Keys) -blocked_users = Blocked Users +webauthn = Two-factor authentication (Security keys) +blocked_users = Blocked users public_profile = Public profile biography_placeholder = Tell us a little bit about yourself! (You can use Markdown) location_placeholder = Share your approximate location with others profile_desc = Control how your profile is shown to other users. Your primary email address will be used for notifications, password recovery and web-based Git operations. password_username_disabled = Non-local users are not allowed to change their username. Please contact your site administrator for more details. -full_name = Full Name +full_name = Full name website = Website location = Location -update_theme = Update Theme -update_profile = Update Profile -update_language = Update Language +update_theme = Change theme +update_profile = Update profile +update_language = Change language update_language_not_found = Language "%s" is not available. update_language_success = Language has been updated. update_profile_success = Your profile has been updated. @@ -702,32 +713,32 @@ comment_type_group_milestone = Milestone comment_type_group_assignee = Assignee comment_type_group_title = Title comment_type_group_branch = Branch -comment_type_group_time_tracking = Time Tracking +comment_type_group_time_tracking = Time tracking comment_type_group_deadline = Deadline comment_type_group_dependency = Dependency -comment_type_group_lock = Lock Status +comment_type_group_lock = Lock status comment_type_group_review_request = Review request comment_type_group_pull_request_push = Added commits comment_type_group_project = Project comment_type_group_issue_ref = Issue reference saved_successfully = Your settings were saved successfully. privacy = Privacy -keep_activity_private = Hide Activity from profile page -keep_activity_private_popup = Makes the activity visible only for you and the admins +keep_activity_private = Hide activity from profile page +keep_activity_private_popup = Your activity will only be visible to you and the instance admins -lookup_avatar_by_mail = Look Up Avatar by Email Address -federated_avatar_lookup = Federated Avatar Lookup +lookup_avatar_by_mail = Lookup avatar by email address +federated_avatar_lookup = Federated avatar lookup enable_custom_avatar = Use custom avatar choose_new_avatar = Choose new avatar -update_avatar = Update Avatar -delete_current_avatar = Delete Current Avatar +update_avatar = Update avatar +delete_current_avatar = Delete current avatar uploaded_avatar_not_a_image = The uploaded file is not an image. uploaded_avatar_is_too_big = The uploaded file size (%d KiB) exceeds the maximum size (%d KiB). update_avatar_success = Your avatar has been updated. update_user_avatar_success = The user's avatar has been updated. change_password = Change password -update_password = Update Password +update_password = Update password old_password = Current password new_password = New password retype_new_password = Confirm new password @@ -735,10 +746,10 @@ password_incorrect = The current password is incorrect. change_password_success = Your password has been updated. Sign in using your new password from now on. password_change_disabled = Non-local users cannot update their password through the Forgejo web interface. -emails = Email Addresses +emails = Email addresses manage_emails = Manage email addresses manage_themes = Select default theme -manage_openid = Manage OpenID Addresses +manage_openid = Manage OpenID addresses email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations. theme_desc = This will be your default theme across the site. primary = Primary @@ -759,7 +770,7 @@ openid_deletion_desc = Removing this OpenID address from your account will preve openid_deletion_success = The OpenID address has been removed. add_new_email = Add email address add_new_openid = Add New OpenID URI -add_email = Add Email Address +add_email = Add email address add_openid = Add OpenID URI add_email_confirmation_sent = A confirmation email has been sent to "%s". Please check your inbox within the next %s to confirm your email address. add_email_success = The new email address has been added. @@ -772,14 +783,14 @@ openid_desc = OpenID lets you delegate authentication to an external provider. manage_ssh_keys = Manage SSH keys manage_ssh_principals = Manage SSH Certificate Principals manage_gpg_keys = Manage GPG keys -add_key = Add Key +add_key = Add key ssh_desc = These public SSH keys are associated with your account. The corresponding private keys allow full access to your repositories. SSH keys that have been verified can be used to verify SSH-signed Git commits. principal_desc = These SSH certificate principals are associated with your account and allow full access to your repositories. gpg_desc = These public GPG keys are associated with your account and used to verify your commits. Keep your private keys safe as they allow to sign commits with your identity. ssh_helper = Need help? Have a look at the guide to create your own SSH keys or solve common problems you may encounter using SSH. gpg_helper = Need help? Have a look at the guide about GPG. -add_new_key = Add SSH Key -add_new_gpg_key = Add GPG Key +add_new_key = Add SSH key +add_new_gpg_key = Add GPG key key_content_ssh_placeholder = Begins with "ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "sk-ecdsa-sha2-nistp256@openssh.com", or "sk-ssh-ed25519@openssh.com" key_content_gpg_placeholder = Begins with "-----BEGIN PGP PUBLIC KEY BLOCK-----" add_new_principal = Add Principal @@ -790,7 +801,7 @@ gpg_key_id_used = A public GPG key with same ID already exists. gpg_no_key_email_found = This GPG key does not match any activated email address associated with your account. It may still be added if you sign the provided token. gpg_key_matched_identities = Matched Identities: gpg_key_matched_identities_long=The embedded identities in this key match the following activated email addresses for this user. Commits matching these email addresses can be verified with this key. -gpg_key_verified=Verified Key +gpg_key_verified=Verified key gpg_key_verified_long=Key has been verified with a token and can be used to verify commits matching any activated email addresses for this user in addition to any matched identities for this key. gpg_key_verify=Verify gpg_invalid_token_signature = The provided GPG key, signature and token do not match or token is out-of-date. @@ -801,7 +812,7 @@ gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature = Armored GPG signature key_signature_gpg_placeholder = Begins with "-----BEGIN PGP SIGNATURE-----" verify_gpg_key_success = GPG key "%s" has been verified. -ssh_key_verified=Verified Key +ssh_key_verified=Verified key ssh_key_verified_long=Key has been verified with a token and can be used to verify commits matching any activated email addresses for this user. ssh_key_verify=Verify ssh_invalid_token_signature = The provided SSH key, signature or token do not match or token is out-of-date. @@ -820,8 +831,8 @@ add_key_success = The SSH key "%s" has been added. add_gpg_key_success = The GPG key "%s" has been added. add_principal_success = The SSH certificate principal "%s" has been added. delete_key = Remove -ssh_key_deletion = Remove SSH Key -gpg_key_deletion = Remove GPG Key +ssh_key_deletion = Remove SSH key +gpg_key_deletion = Remove GPG key ssh_principal_deletion = Remove SSH Certificate Principal ssh_key_deletion_desc = Removing an SSH key revokes its access to your account. Continue? gpg_key_deletion_desc = Removing a GPG key un-verifies commits signed by it. Continue? @@ -853,11 +864,11 @@ manage_access_token = Manage access tokens generate_new_token = Generate new token tokens_desc = These tokens grant access to your account using the Forgejo API. token_name = Token name -generate_token = Generate Token +generate_token = Generate token generate_token_success = Your new token has been generated. Copy it now as it will not be shown again. generate_token_name_duplicate = %s has been used as an application name already. Please use a new one. delete_token = Delete -access_token_deletion = Delete Access Token +access_token_deletion = Delete access token access_token_deletion_cancel_action = Cancel access_token_deletion_confirm_action = Delete access_token_deletion_desc = Deleting a token will revoke access to your account for applications using it. This cannot be undone. Continue? @@ -880,11 +891,11 @@ remove_oauth2_application = Remove OAuth2 Application remove_oauth2_application_desc = Removing an OAuth2 application will revoke access to all signed access tokens. Continue? remove_oauth2_application_success = The application has been deleted. create_oauth2_application = Create a new OAuth2 application -create_oauth2_application_button = Create Application +create_oauth2_application_button = Create application create_oauth2_application_success = You have successfully created a new OAuth2 application. update_oauth2_application_success = You have successfully updated the OAuth2 application. oauth2_application_name = Application name -oauth2_confidential_client = Confidential Client. Select for apps that keep the secret confidential, such as web apps. Do not select for native apps including desktop and mobile apps. +oauth2_confidential_client = Confidential client. Select for apps that keep the secret confidential, such as web apps. Do not select for native apps including desktop and mobile apps. oauth2_redirect_uris = Redirect URIs. Please use a new line for every URI. save_application = Save oauth2_client_id = Client ID @@ -908,10 +919,10 @@ twofa_desc = To protect your account against password theft, you can use a smart twofa_recovery_tip = If you lose your device, you will be able to use a single-use recovery key to regain access to your account. twofa_is_enrolled = Your account is currently enrolled in two-factor authentication. twofa_not_enrolled = Your account is not currently enrolled in two-factor authentication. -twofa_disable = Disable Two-Factor Authentication -twofa_scratch_token_regenerate = Regenerate Single-Use Recovery Key +twofa_disable = Disable two-factor authentication +twofa_scratch_token_regenerate = Regenerate single-use recovery key twofa_scratch_token_regenerated = Your single-use recovery key is now %s. Store it in a safe place, as it will not be shown again. -twofa_enroll = Enroll into Two-Factor Authentication +twofa_enroll = Enroll into two-factor authentication twofa_disable_note = You can disable two-factor authentication if needed. twofa_disable_desc = Disabling two-factor authentication will make your account less secure. Continue? regenerate_scratch_token_desc = If you misplaced your recovery key or have already used it to sign in, you can reset it here. @@ -924,9 +935,9 @@ twofa_enrolled = Your account has been successfully enrolled. Store your single- twofa_failed_get_secret = Failed to get secret. webauthn_desc = Security keys are hardware devices containing cryptographic keys. They can be used for two-factor authentication. Security keys must support the WebAuthn Authenticator standard. -webauthn_register_key = Add Security Key +webauthn_register_key = Add security key webauthn_nickname = Nickname -webauthn_delete_key = Remove Security Key +webauthn_delete_key = Remove security key webauthn_delete_key_desc = If you remove a security key you can no longer sign in with it. Continue? webauthn_key_loss_warning = If you lose your security keys, you will lose access to your account. webauthn_alternative_tip = You may want to configure an additional authentication method. @@ -945,18 +956,18 @@ orgs_none = You are not a member of any organizations. repos_none = You do not own any repositories. blocked_users_none = There are no blocked users. -delete_account = Delete Your Account +delete_account = Delete your account delete_prompt = This operation will permanently delete your user account. It CANNOT be undone. delete_with_all_comments = Your account is younger than %s. To avoid ghost comments, all issue/PR comments will be deleted with it. -confirm_delete_account = Confirm Deletion -delete_account_title = Delete User Account +confirm_delete_account = Confirm deletion +delete_account_title = Delete user account delete_account_desc = Are you sure you want to permanently delete this user account? -email_notifications.enable = Enable Email Notifications -email_notifications.onmention = Only Email on Mention -email_notifications.disable = Disable Email Notifications -email_notifications.submit = Set Email Preference -email_notifications.andyourown = And Your Own Notifications +email_notifications.enable = Enable email notifications +email_notifications.onmention = Only email on mention +email_notifications.disable = Disable email notifications +email_notifications.submit = Set email preference +email_notifications.andyourown = And your own notifications visibility = User visibility visibility.public = Public @@ -993,10 +1004,10 @@ visibility = Visibility visibility_description = Only the owner or the organization members if they have rights, will be able to see it. visibility_helper = Make repository private visibility_helper_forced = Your site administrator forces new repositories to be private. -visibility_fork_helper = (Changing this will affect all forks.) +visibility_fork_helper = (Changing this will affect visibility of all forks.) clone_helper = Need help cloning? Visit Help. -fork_repo = Fork Repository -fork_from = Fork From +fork_repo = Fork repository +fork_from = Fork from already_forked = You've already forked %s fork_to_different_account = Fork to a different account fork_visibility_helper = The visibility of a forked repository cannot be changed. @@ -1004,53 +1015,52 @@ fork_branch = Branch to be cloned to the fork all_branches = All branches fork_no_valid_owners = This repository can not be forked because there are no valid owners. use_template = Use this template -clone_in_vsc = Clone in VS Code -clone_in_vscodium = Clone in VSCodium +open_with_editor = Open with %s download_zip = Download ZIP download_tar = Download TAR.GZ download_bundle = Download BUNDLE -generate_repo = Generate Repository -generate_from = Generate From +generate_repo = Generate repository +generate_from = Generate from repo_desc = Description repo_desc_helper = Enter short description (optional) repo_lang = Language repo_gitignore_helper = Select .gitignore templates. repo_gitignore_helper_desc = Choose which files not to track from a list of templates for common languages. Typical artifacts generated by each language's build tools are included on .gitignore by default. -issue_labels = Issue Labels +issue_labels = Issue labels issue_labels_helper = Select an issue label set. license = License license_helper = Select a license file. license_helper_desc = A license governs what others can and can't do with your code. Not sure which one is right for your project? See Choose a license. -object_format = Object Format +object_format = Object format object_format_helper = Object format of the repository. Cannot be changed later. SHA1 is most compatible. readme = README readme_helper = Select a README file template. readme_helper_desc = This is the place where you can write a complete description for your project. -auto_init = Initialize Repository (Adds .gitignore, License and README) +auto_init = Initialize repository (Adds .gitignore, License and README) trust_model_helper = Select trust model for signature verification. Possible options are: trust_model_helper_collaborator = Collaborator: Trust signatures by collaborators trust_model_helper_committer = Committer: Trust signatures that match committers trust_model_helper_collaborator_committer = Collaborator+Committer: Trust signatures by collaborators which match the committer trust_model_helper_default = Default: Use the default trust model for this installation -create_repo = Create Repository -default_branch = Default Branch +create_repo = Create repository +default_branch = Default branch default_branch_label = default default_branch_helper = The default branch is the base branch for pull requests and code commits. mirror_prune = Prune mirror_prune_desc = Remove obsolete remote-tracking references -mirror_interval = Mirror Interval (valid time units are "h", "m", "s"). 0 to disable periodic sync. (Minimum interval: %s) +mirror_interval = Mirror interval (valid time units are "h", "m", "s"). 0 to disable periodic sync. (Minimum interval: %s) mirror_interval_invalid = The mirror interval is not valid. mirror_sync = synced mirror_sync_on_commit = Sync when commits are pushed -mirror_address = Clone From URL +mirror_address = Clone from URL mirror_address_desc = Put any required credentials in the Authorization section. mirror_address_url_invalid = The provided URL is invalid. You must escape all components of the URL correctly. mirror_address_protocol_invalid = The provided URL is invalid. Only http(s):// or git:// locations can be used for mirroring. mirror_lfs = Large File Storage (LFS) mirror_lfs_desc = Activate mirroring of LFS data. -mirror_lfs_endpoint = LFS Endpoint +mirror_lfs_endpoint = LFS endpoint mirror_lfs_endpoint_desc = Sync will attempt to use the clone url to determine the LFS server. You can also specify a custom endpoint if the repository LFS data is stored somewhere else. -mirror_last_synced = Last Synchronized +mirror_last_synced = Last synchronized mirror_password_placeholder = (Unchanged) mirror_password_blank_placeholder = (Unset) mirror_password_help = Change the username to erase a stored password. @@ -1062,7 +1072,7 @@ reactions_more = and %d more unit_disabled = The site administrator has disabled this repository section. language_other = Other adopt_search = Enter username to search for unadopted repositories... (leave blank to find all) -adopt_preexisting_label = Adopt Files +adopt_preexisting_label = Adopt files adopt_preexisting = Adopt pre-existing files adopt_preexisting_content = Create repository from %s adopt_preexisting_success = Adopted files and created repository from %s @@ -1079,9 +1089,9 @@ tree_path_not_found_commit = Path %[1]s doesn't exist in commit %[2]s tree_path_not_found_branch = Path %[1]s doesn't exist in branch %[2]s tree_path_not_found_tag = Path %[1]s doesn't exist in tag %[2]s -transfer.accept = Accept Transfer +transfer.accept = Accept transfer transfer.accept_desc = Transfer to "%s" -transfer.reject = Reject Transfer +transfer.reject = Reject transfer transfer.reject_desc = Cancel transfer to "%s" transfer.no_permission_to_accept = You do not have permission to accept this transfer. transfer.no_permission_to_reject = You do not have permission to reject this transfer. @@ -1093,14 +1103,14 @@ desc.internal = Internal desc.archived = Archived desc.sha256 = SHA256 -template.items = Template Items -template.git_content = Git Content (Default Branch) -template.git_hooks = Git Hooks -template.git_hooks_tooltip = You are currently unable to modify or remove Git Hooks once added. Select this only if you trust the template repository. +template.items = Template items +template.git_content = Git content (Default branch) +template.git_hooks = Git hooks +template.git_hooks_tooltip = You are currently unable to modify or remove Git hooks once added. Select this only if you trust the template repository. template.webhooks = Webhooks template.topics = Topics template.avatar = Avatar -template.issue_labels = Issue Labels +template.issue_labels = Issue labels template.one_item = Must select at least one template item template.invalid = Must select a template repository @@ -1116,10 +1126,10 @@ form.name_pattern_not_allowed = The pattern "%s" is not allowed in a repository need_auth = Authorization migrate_options = Migration options -migrate_service = Migration Service +migrate_service = Migration service migrate_options_mirror_helper = This repository will be a mirror migrate_options_lfs = Migrate LFS files -migrate_options_lfs_endpoint.label = LFS Endpoint +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 @@ -1128,10 +1138,10 @@ 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_pullrequests = Pull requests +migrate_items_merge_requests = Merge requests migrate_items_releases = Releases -migrate_repo = Migrate Repository +migrate_repo = Migrate repository migrate.clone_address = Migrate / Clone from URL migrate.clone_address_desc = The HTTP(S) or Git "clone" URL of an existing repository migrate.github_token_desc = You can put one or more tokens with comma separated here to make migrating faster because of GitHub API rate limit. WARN: Abusing this feature may violate the service provider's policy and lead to account blocking. @@ -1141,15 +1151,15 @@ migrate.permission_denied_blocked = You cannot import from disallowed hosts, ple migrate.invalid_local_path = The local path is invalid. It doesn't exist or is not a directory. migrate.invalid_lfs_endpoint = The LFS endpoint is not valid. migrate.failed = Migration failed: %v -migrate.migrate_items_options = Access Token is required to migrate additional items +migrate.migrate_items_options = Access token is required to migrate additional items migrated_from = Migrated from %[2]s -migrated_from_fake = Migrated From %[1]s -migrate.migrate = Migrate From %s +migrated_from_fake = Migrated from %[1]s +migrate.migrate = Migrate from %s 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.github.description = Migrate data from github.com or other GitHub instances. +migrate.github.description = Migrate data from github.com or GitHub Enterprise server. migrate.git.description = Migrate a repository only from any Git service. migrate.gitlab.description = Migrate data from gitlab.com or other GitLab instances. migrate.forgejo.description = Migrate data from codeberg.org or other Forgejo instances. @@ -1158,14 +1168,14 @@ migrate.gogs.description = Migrate data from notabug.org or other Gogs instances migrate.onedev.description = Migrate data from code.onedev.io or other OneDev instances. migrate.codebase.description = Migrate data from codebasehq.com. migrate.gitbucket.description = Migrate data from GitBucket instances. -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.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 @@ -1180,11 +1190,11 @@ watch = Watch unstar = Unstar star = Star fork = Fork -download_archive = Download Repository -more_operations = More Operations +download_archive = Download repository +more_operations = More operations -no_desc = No Description -quick_guide = Quick Guide +no_desc = No description +quick_guide = Quick guide clone_this_repo = Clone this repository cite_this_repo = Cite this repository create_new_repo_command = Creating a new repository on the command line @@ -1202,7 +1212,7 @@ find_tag = Find tag branches = Branches tags = Tags issues = Issues -pulls = Pull Requests +pulls = Pull requests project_board = Projects packages = Packages actions = Actions @@ -1236,18 +1246,18 @@ ambiguous_character = `%[1]c [U+%04[1]X] can be confused with %[2]c [U+%04[2]X]` escape_control_characters = Escape unescape_control_characters = Unescape -file_copy_permalink = Copy Permalink -view_git_blame = View Git Blame +file_copy_permalink = Copy permalink +view_git_blame = View git blame video_not_supported_in_browser = Your browser does not support the HTML5 "video" tag. audio_not_supported_in_browser = Your browser does not support the HTML5 "audio" tag. stored_lfs = Stored with Git LFS symbolic_link = Symbolic link -executable_file = Executable File +executable_file = Executable file vendored = Vendored generated = Generated -commit_graph = Commit Graph +commit_graph = Commit graph commit_graph.select = Select branches -commit_graph.hide_pr_refs = Hide Pull Requests +commit_graph.hide_pr_refs = Hide pull requests commit_graph.monochrome = Mono commit_graph.color = Color commit.contained_in = This commit is contained in: @@ -1260,18 +1270,18 @@ line = line lines = lines from_comment = (comment) -editor.add_file = Add File -editor.new_file = New File -editor.upload_file = Upload File -editor.edit_file = Edit File -editor.preview_changes = Preview Changes +editor.add_file = Add file +editor.new_file = New file +editor.upload_file = Upload file +editor.edit_file = Edit file +editor.preview_changes = Preview changes editor.cannot_edit_lfs_files = LFS files cannot be edited in the web interface. editor.cannot_edit_non_text_files = Binary files cannot be edited in the web interface. -editor.edit_this_file = Edit File +editor.edit_this_file = Edit file editor.this_file_locked = File is locked editor.must_be_on_a_branch = You must be on a branch to make or propose changes to this file. editor.fork_before_edit = You must fork this repository to make or propose changes to this file. -editor.delete_this_file = Delete File +editor.delete_this_file = Delete file editor.must_have_write_access = You must have write access to make or propose changes to this file. editor.file_delete_success = File "%s" has been deleted. editor.name_your_file = Name your file… @@ -1314,8 +1324,8 @@ editor.commit_empty_file_text = The file you're about to commit is empty. Procee editor.no_changes_to_show = There are no changes to show. editor.fail_to_update_file = Failed to update/create file "%s". editor.fail_to_update_file_summary = Error Message: -editor.push_rejected_no_message = The change was rejected by the server without a message. Please check Git Hooks. -editor.push_rejected = The change was rejected by the server. Please check Git Hooks. +editor.push_rejected_no_message = The change was rejected by the server without a message. Please check Git hooks. +editor.push_rejected = The change was rejected by the server. Please check Git hooks. editor.push_rejected_summary = Full Rejection Message: editor.add_subdir = Add a directory… editor.unable_to_upload_files = Failed to upload files to "%s" with error: %v @@ -1346,8 +1356,8 @@ commits.newer = Newer commits.signed_by = Signed by commits.signed_by_untrusted_user = Signed by untrusted user commits.signed_by_untrusted_user_unmatched = Signed by untrusted user who does not match committer -commits.gpg_key_id = GPG Key ID -commits.ssh_key_fingerprint = SSH Key Fingerprint +commits.gpg_key_id = GPG key ID +commits.ssh_key_fingerprint = SSH key fingerprint commits.view_path=View at this point in history commit.operations = Operations @@ -1363,24 +1373,24 @@ commitstatus.failure = Failure commitstatus.pending = Pending commitstatus.success = Success -ext_issues = Access to External Issues +ext_issues = Access to external issues ext_issues.desc = Link to an external issue tracker. projects = Projects projects.desc = Manage issues and pulls in project boards. projects.description = Description (optional) projects.description_placeholder = Description -projects.create = Create Project +projects.create = Create project projects.title = Title -projects.new = New Project +projects.new = New project projects.new_subheader = Coordinate, track, and update your work in one place, so projects stay transparent and on schedule. projects.create_success = The project "%s" has been created. -projects.deletion = Delete Project +projects.deletion = Delete project projects.deletion_desc = Deleting a project removes it from all related issues. Continue? projects.deletion_success = The project has been deleted. -projects.edit = Edit Project +projects.edit = Edit project projects.edit_subheader = Projects organize issues and track progress. -projects.modify = Edit Project +projects.modify = Edit project projects.edit_success = Project "%s" has been updated. projects.type.none = None projects.type.basic_kanban = Basic Kanban @@ -1413,43 +1423,43 @@ issues.filter_milestones = Filter Milestone issues.filter_projects = Filter Project issues.filter_labels = Filter Label issues.filter_reviewers = Filter Reviewer -issues.new = New Issue +issues.new = New issue issues.new.title_empty = Title cannot be empty issues.new.labels = Labels -issues.new.no_label = No Label +issues.new.no_label = No label issues.new.clear_labels = Clear labels issues.new.projects = Projects issues.new.clear_projects = Clear projects issues.new.no_projects = No project -issues.new.open_projects = Open Projects -issues.new.closed_projects = Closed Projects +issues.new.open_projects = Open projects +issues.new.closed_projects = Closed projects issues.new.no_items = No items issues.new.milestone = Milestone -issues.new.no_milestone = No Milestone +issues.new.no_milestone = No milestone issues.new.clear_milestone = Clear milestone -issues.new.open_milestone = Open Milestones -issues.new.closed_milestone = Closed Milestones +issues.new.open_milestone = Open milestones +issues.new.closed_milestone = Closed milestones issues.new.assignees = Assignees issues.new.clear_assignees = Clear assignees -issues.new.no_assignees = No Assignees +issues.new.no_assignees = No assignees issues.new.no_reviewers = No reviewers -issues.choose.get_started = Get Started +issues.choose.get_started = Get started issues.choose.open_external_link = Open issues.choose.blank = Default issues.choose.blank_about = Create an issue from default template. issues.choose.ignore_invalid_templates = Invalid templates have been ignored issues.choose.invalid_templates = %v invalid template(s) found issues.choose.invalid_config = The issue config contains errors: -issues.no_ref = No Branch/Tag Specified -issues.create = Create Issue -issues.new_label = New Label +issues.no_ref = No Branch/Tag specified +issues.create = Create issue +issues.new_label = New label issues.new_label_placeholder = Label name issues.new_label_desc_placeholder = Description -issues.create_label = Create Label -issues.label_templates.title = Load a predefined set of labels -issues.label_templates.info = No labels exist yet. Create a label with "New Label" or use a predefined label set: -issues.label_templates.helper = Select a label set -issues.label_templates.use = Use Label Set +issues.create_label = Create label +issues.label_templates.title = Load a label preset +issues.label_templates.info = No labels exist yet. Create a label with "New label" or use a label preset: +issues.label_templates.helper = Select a label preset +issues.label_templates.use = Use label preset issues.label_templates.fail_to_load_file = Failed to load label template file "%s": %v issues.add_label = added the %s label %s issues.add_labels = added the %s labels %s @@ -1535,18 +1545,18 @@ issues.num_comments_1 = %d comment issues.num_comments = %d comments issues.commented_at = `commented %s` issues.delete_comment_confirm = Are you sure you want to delete this comment? -issues.context.copy_link = Copy Link -issues.context.quote_reply = Quote Reply -issues.context.reference_issue = Reference in New Issue +issues.context.copy_link = Copy link +issues.context.quote_reply = Quote reply +issues.context.reference_issue = Reference in a new issue issues.context.edit = Edit issues.context.delete = Delete issues.no_content = No description provided. -issues.close = Close Issue +issues.close = Close issue issues.comment_pull_merged_at = merged commit %[1]s into %[2]s %[3]s issues.comment_manually_pull_merged_at = manually merged commit %[1]s into %[2]s %[3]s -issues.close_comment_issue = Comment and Close +issues.close_comment_issue = Comment and close issues.reopen_issue = Reopen -issues.reopen_comment_issue = Comment and Reopen +issues.reopen_comment_issue = Comment and reopen issues.create_comment = Comment issues.closed_at = `closed this issue %[2]s` issues.reopened_at = `reopened this issue %[2]s` @@ -1584,7 +1594,7 @@ issues.label_title = Name issues.label_description = Description issues.label_color = Color issues.label_exclusive = Exclusive -issues.label_archive = Archive Label +issues.label_archive = Archive label issues.label_archived_filter = Show archived labels issues.label_archive_tooltip = Archived labels are excluded by default from the suggestions when searching by label. issues.label_exclusive_desc = Name the label scope/item to make it mutually exclusive with other scope/ labels. @@ -1593,20 +1603,20 @@ issues.label_count = %d labels issues.label_open_issues = %d open issues/pull requests issues.label_edit = Edit issues.label_delete = Delete -issues.label_modify = Edit Label -issues.label_deletion = Delete Label +issues.label_modify = Edit label +issues.label_deletion = Delete label issues.label_deletion_desc = Deleting a label removes it from all issues. Continue? issues.label_deletion_success = The label has been deleted. issues.label.filter_sort.alphabetically = Alphabetically issues.label.filter_sort.reverse_alphabetically = Reverse alphabetically issues.label.filter_sort.by_size = Smallest size issues.label.filter_sort.reverse_by_size = Largest size -issues.num_participants = %d Participants +issues.num_participants = %d participants issues.attachment.open_tab = `Click to see "%s" in a new tab` issues.attachment.download = `Click to download "%s"` issues.subscribe = Subscribe issues.unsubscribe = Unsubscribe -issues.unpin_issue = Unpin Issue +issues.unpin_issue = Unpin issue issues.max_pinned = You can't pin more issues issues.pin_comment = pinned this %s issues.unpin_comment = unpinned this %s @@ -1632,28 +1642,28 @@ issues.comment_on_locked = You cannot comment on a locked issue. issues.delete = Delete issues.delete.title = Delete this issue? issues.delete.text = Do you really want to delete this issue? (This will permanently remove all content. Consider closing it instead, if you intend to keep it archived) -issues.tracker = Time Tracker -issues.start_tracking_short = Start Timer -issues.start_tracking = Start Time Tracking +issues.tracker = Time tracker +issues.start_tracking_short = Start timer +issues.start_tracking = Start time tracking issues.start_tracking_history = `started working %s` issues.tracker_auto_close = Timer will be stopped automatically when this issue gets closed issues.tracking_already_started = `You have already started time tracking on another issue!` -issues.stop_tracking = Stop Timer +issues.stop_tracking = Stop timer issues.stop_tracking_history = `stopped working %s` issues.cancel_tracking = Discard issues.cancel_tracking_history = `canceled time tracking %s` -issues.add_time = Manually Add Time +issues.add_time = Manually add time issues.del_time = Delete this time log -issues.add_time_short = Add Time +issues.add_time_short = Add time issues.add_time_cancel = Cancel issues.add_time_history = `added spent time %s` issues.del_time_history= `deleted spent time %s` issues.add_time_hours = Hours issues.add_time_minutes = Minutes issues.add_time_sum_to_small = No time was entered. -issues.time_spent_total = Total Time Spent -issues.time_spent_from_all_authors = `Total Time Spent: %s` -issues.due_date = Due Date +issues.time_spent_total = Total time spent +issues.time_spent_from_all_authors = `Total time spent: %s` +issues.due_date = Due date issues.invalid_due_date_format = Due date format must be "yyyy-mm-dd". issues.error_modifying_due_date = Failed to modify the due date. issues.error_removing_due_date = Failed to remove the due date. @@ -1696,7 +1706,7 @@ issues.dependency.blocked_by_short = Depends on issues.dependency.remove_header = Remove Dependency issues.dependency.issue_remove_text = This will remove the dependency from this issue. Continue? issues.dependency.pr_remove_text = This will remove the dependency from this pull request. Continue? -issues.dependency.setting = Enable Dependencies For Issues and Pull Requests +issues.dependency.setting = Enable dependencies for issues and pull requests issues.dependency.add_error_same_issue = You cannot make an issue depend on itself. issues.dependency.add_error_dep_issue_not_exist = Dependent issue does not exist. issues.dependency.add_error_dep_not_exist = Dependency does not exist. @@ -1747,9 +1757,9 @@ compare.compare_base = base compare.compare_head = compare pulls.desc = Enable pull requests and code reviews. -pulls.new = New Pull Request -pulls.view = View Pull Request -pulls.compare_changes = New Pull Request +pulls.new = New pull request +pulls.view = View pull request +pulls.compare_changes = New pull request pulls.allow_edits_from_maintainers = Allow edits from maintainers pulls.allow_edits_from_maintainers_desc = Users with write access to the base branch can also push to this branch pulls.allow_edits_from_maintainers_err = Updating failed @@ -1776,13 +1786,15 @@ pulls.nothing_to_compare = These branches are equal. There is no need to create pulls.nothing_to_compare_have_tag = The selected branch/tag are equal. pulls.nothing_to_compare_and_allow_empty_pr = These branches are equal. This PR will be empty. pulls.has_pull_request = `A pull request between these branches already exists: %[2]s#%[3]d` -pulls.create = Create Pull Request -pulls.title_desc = wants to merge %[1]d commits from %[2]s into %[3]s -pulls.merged_title_desc = merged %[1]d commits from %[2]s into %[3]s %[4]s +pulls.create = Create pull request +pulls.title_desc_one = wants to merge %[1]d commit from %[2]s into %[3]s +pulls.title_desc_few = wants to merge %[1]d commits from %[2]s into %[3]s +pulls.merged_title_desc_one = merged %[1]d commit from %[2]s into %[3]s %[4]s +pulls.merged_title_desc_few = merged %[1]d commits from %[2]s into %[3]s %[4]s pulls.change_target_branch_at = `changed target branch from %s to %s %s` pulls.tab_conversation = Conversation pulls.tab_commits = Commits -pulls.tab_files = Files Changed +pulls.tab_files = Files changed pulls.reopen_to_merge = Please reopen this pull request to perform a merge. pulls.cant_reopen_deleted_branch = This pull request cannot be reopened because the branch was deleted. pulls.merged = Merged @@ -1847,9 +1859,9 @@ pulls.unrelated_histories = Merge Failed: The merge head and base do not share a pulls.merge_out_of_date = Merge Failed: Whilst generating the merge, the base was updated. Hint: Try again. pulls.head_out_of_date = Merge Failed: Whilst generating the merge, the head was updated. Hint: Try again. pulls.has_merged = Failed: The pull request has been merged, you cannot merge again or change the target branch. -pulls.push_rejected = Push Failed: The push was rejected. Review the Git Hooks for this repository. +pulls.push_rejected = Push Failed: The push was rejected. Review the Git hooks for this repository. pulls.push_rejected_summary = Full Rejection Message -pulls.push_rejected_no_message = Push Failed: The push was rejected but there was no remote message. Review the Git Hooks for this repository +pulls.push_rejected_no_message = Push Failed: The push was rejected but there was no remote message. Review the Git hooks for this repository pulls.open_unmerged_pull_exists = `You cannot perform a reopen operation because there is a pending pull request (#%d) with identical properties.` pulls.status_checking = Some checks are pending pulls.status_checks_success = All checks were successful @@ -1865,7 +1877,7 @@ pulls.update_branch_rebase = Update branch by rebase pulls.update_branch_success = Branch update was successful pulls.update_not_allowed = You are not allowed to update branch pulls.outdated_with_base_branch = This branch is out-of-date with the base branch -pulls.close = Close Pull Request +pulls.close = Close pull request pulls.closed_at = `closed this pull request %[2]s` pulls.reopened_at = `reopened this pull request %[2]s` pulls.commit_ref_at = `referenced this pull request from a commit %[2]s` @@ -1901,7 +1913,7 @@ pulls.recently_pushed_new_branches = You pushed on branch %d%%
      Completed -milestones.create = Create Milestone +milestones.create = Create milestone milestones.title = Title milestones.desc = Description -milestones.due_date = Due Date (optional) +milestones.due_date = Due date (optional) milestones.clear = Clear milestones.invalid_due_date_format = Due date format must be "yyyy-mm-dd". milestones.create_success = The milestone "%s" has been created. -milestones.edit = Edit Milestone +milestones.edit = Edit milestone milestones.edit_subheader = Milestones organize issues and track progress. milestones.cancel = Cancel -milestones.modify = Update Milestone +milestones.modify = Update milestone milestones.edit_success = Milestone "%s" has been updated. -milestones.deletion = Delete Milestone +milestones.deletion = Delete milestone milestones.deletion_desc = Deleting a milestone removes it from all related issues. Continue? milestones.deletion_success = The milestone has been deleted. -milestones.filter_sort.earliest_due_data = Earliest due date -milestones.filter_sort.latest_due_date = Latest due date +milestones.filter_sort.earliest_due_data = Nearest due date +milestones.filter_sort.latest_due_date = Farthest due date milestones.filter_sort.least_complete = Least complete milestones.filter_sort.most_complete = Most complete milestones.filter_sort.most_issues = Most issues @@ -1978,9 +1990,9 @@ wiki.original_git_entry_tooltip = View original Git file instead of using friend activity = Activity activity.navbar.pulse = Pulse -activity.navbar.code_frequency = Code Frequency +activity.navbar.code_frequency = Code frequency activity.navbar.contributors = Contributors -activity.navbar.recent_commits = Recent Commits +activity.navbar.recent_commits = Recent commits activity.period.filter_label = Period: activity.period.daily = 1 day activity.period.halfweekly = 3 days @@ -1990,38 +2002,38 @@ activity.period.quarterly = 3 months activity.period.semiyearly = 6 months activity.period.yearly = 1 year activity.overview = Overview -activity.active_prs_count_1 = %d Active Pull Request -activity.active_prs_count_n = %d Active Pull Requests -activity.merged_prs_count_1 = Merged Pull Request -activity.merged_prs_count_n = Merged Pull Requests -activity.opened_prs_count_1 = Proposed Pull Request -activity.opened_prs_count_n = Proposed Pull Requests +activity.active_prs_count_1 = %d Active pull request +activity.active_prs_count_n = %d Active pull requests +activity.merged_prs_count_1 = Merged pull request +activity.merged_prs_count_n = Merged pull requests +activity.opened_prs_count_1 = Proposed pull request +activity.opened_prs_count_n = Proposed pull requests activity.title.user_1 = %d user activity.title.user_n = %d users -activity.title.prs_1 = %d Pull request -activity.title.prs_n = %d Pull requests +activity.title.prs_1 = %d pull request +activity.title.prs_n = %d pull requests activity.title.prs_merged_by = %s merged by %s activity.title.prs_opened_by = %s proposed by %s activity.merged_prs_label = Merged activity.opened_prs_label = Proposed -activity.active_issues_count_1 = %d Active Issue -activity.active_issues_count_n = %d Active Issues -activity.closed_issues_count_1 = Closed Issue -activity.closed_issues_count_n = Closed Issues -activity.title.issues_1 = %d Issue -activity.title.issues_n = %d Issues +activity.active_issues_count_1 = %d active issue +activity.active_issues_count_n = %d active issues +activity.closed_issues_count_1 = Closed issue +activity.closed_issues_count_n = Closed issues +activity.title.issues_1 = %d issue +activity.title.issues_n = %d issues activity.title.issues_closed_from = %s closed from %s activity.title.issues_created_by = %s created by %s activity.closed_issue_label = Closed -activity.new_issues_count_1 = New Issue -activity.new_issues_count_n = New Issues +activity.new_issues_count_1 = New issue +activity.new_issues_count_n = New issues activity.new_issue_label = Opened -activity.title.unresolved_conv_1 = %d Unresolved Conversation -activity.title.unresolved_conv_n = %d Unresolved Conversations +activity.title.unresolved_conv_1 = %d unresolved conversation +activity.title.unresolved_conv_n = %d unresolved conversations activity.unresolved_conv_desc = These recently changed issues and pull requests have not been resolved yet. activity.unresolved_conv_label = Open -activity.title.releases_1 = %d Release -activity.title.releases_n = %d Releases +activity.title.releases_1 = %d release +activity.title.releases_n = %d releases activity.title.releases_published_by = %s published by %s activity.published_release_label = Published activity.no_git_activity = There has not been any commit activity in this period. @@ -2072,9 +2084,9 @@ settings.collaboration.read = Read settings.collaboration.owner = Owner settings.collaboration.undefined = Undefined settings.hooks = Webhooks -settings.githooks = Git Hooks -settings.basic_settings = Basic Settings -settings.mirror_settings = Mirror Settings +settings.githooks = Git hooks +settings.basic_settings = Basic settings +settings.mirror_settings = Mirror settings settings.mirror_settings.docs = Set up your repository to automatically synchronize commits, tags and branches with another repository. settings.mirror_settings.docs.disabled_pull_mirror.instructions = Set up your project to automatically push commits, tags and branches to another repository. Pull mirrors have been disabled by your site administrator. settings.mirror_settings.docs.disabled_push_mirror.instructions = Set up your project to automatically pull commits, tags and branches from another repository. @@ -2094,81 +2106,81 @@ settings.mirror_settings.direction.pull = Pull settings.mirror_settings.direction.push = Push settings.mirror_settings.last_update = Last update settings.mirror_settings.push_mirror.none = No push mirrors configured -settings.mirror_settings.push_mirror.remote_url = Git Remote Repository URL -settings.mirror_settings.push_mirror.add = Add Push Mirror +settings.mirror_settings.push_mirror.remote_url = Git remote repository URL +settings.mirror_settings.push_mirror.add = Add push mirror settings.mirror_settings.push_mirror.edit_sync_time = Edit mirror sync interval -settings.units.units = Repository Units +settings.units.units = Repository units settings.units.overview = Overview settings.units.add_more = Add more... -settings.sync_mirror = Synchronize Now +settings.sync_mirror = Synchronize now settings.pull_mirror_sync_in_progress = Pulling changes from the remote %s at the moment. settings.push_mirror_sync_in_progress = Pushing changes to the remote %s at the moment. settings.site = Website -settings.update_settings = Update Settings -settings.update_mirror_settings = Update Mirror Settings -settings.branches.switch_default_branch = Switch Default Branch -settings.branches.update_default_branch = Update Default Branch -settings.branches.add_new_rule = Add New Rule -settings.advanced_settings = Advanced Settings -settings.wiki_desc = Enable Repository Wiki -settings.wiki_globally_editable = Allow anyone to edit the Wiki -settings.use_internal_wiki = Use Built-In Wiki -settings.use_external_wiki = Use External Wiki -settings.external_wiki_url = External Wiki URL +settings.update_settings = Update settings +settings.update_mirror_settings = Update mirror settings +settings.branches.switch_default_branch = Switch default branch +settings.branches.update_default_branch = Update default branch +settings.branches.add_new_rule = Add new rule +settings.advanced_settings = Advanced settings +settings.wiki_desc = Enable repository wiki +settings.wiki_globally_editable = Allow anyone to edit the wiki +settings.use_internal_wiki = Use built-in wiki +settings.use_external_wiki = Use external wiki +settings.external_wiki_url = External wiki URL settings.external_wiki_url_error = The external wiki URL is not a valid URL. settings.external_wiki_url_desc = Visitors are redirected to the external wiki URL when clicking the wiki tab. -settings.issues_desc = Enable Repository Issue Tracker -settings.use_internal_issue_tracker = Use Built-In Issue Tracker -settings.use_external_issue_tracker = Use External Issue Tracker -settings.external_tracker_url = External Issue Tracker URL +settings.issues_desc = Enable repository issue tracker +settings.use_internal_issue_tracker = Use built-in issue tracker +settings.use_external_issue_tracker = Use external issue tracker +settings.external_tracker_url = External issue tracker URL settings.external_tracker_url_error = The external issue tracker URL is not a valid URL. settings.external_tracker_url_desc = Visitors are redirected to the external issue tracker URL when clicking on the issues tab. -settings.tracker_url_format = External Issue Tracker URL Format +settings.tracker_url_format = External issue tracker URL Format settings.tracker_url_format_error = The external issue tracker URL format is not a valid URL. -settings.tracker_issue_style = External Issue Tracker Number Format +settings.tracker_issue_style = External issue tracker Number Format settings.tracker_issue_style.numeric = Numeric settings.tracker_issue_style.alphanumeric = Alphanumeric settings.tracker_issue_style.regexp = Regular Expression settings.tracker_issue_style.regexp_pattern = Regular Expression Pattern settings.tracker_issue_style.regexp_pattern_desc = The first captured group will be used in place of {index}. settings.tracker_url_format_desc = Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index. -settings.enable_timetracker = Enable Time Tracking -settings.allow_only_contributors_to_track_time = Let Only Contributors Track Time -settings.pulls_desc = Enable Repository Pull Requests -settings.pulls.ignore_whitespace = Ignore Whitespace for Conflicts +settings.enable_timetracker = Enable time tracking +settings.allow_only_contributors_to_track_time = Let only contributors track time +settings.pulls_desc = Enable repository pull requests +settings.pulls.ignore_whitespace = Ignore whitespace for conflicts settings.pulls.enable_autodetect_manual_merge = Enable autodetect manual merge (Note: In some special cases, misjudgments can occur) settings.pulls.allow_rebase_update = Enable updating pull request branch by rebase settings.pulls.default_delete_branch_after_merge = Delete pull request branch after merge by default settings.pulls.default_allow_edits_from_maintainers = Allow edits from maintainers by default -settings.releases_desc = Enable Repository Releases -settings.packages_desc = Enable Repository Packages Registry -settings.projects_desc = Enable Repository Projects -settings.actions_desc = Enable Repository Actions -settings.admin_settings = Administrator Settings -settings.admin_enable_health_check = Enable Repository Health Checks (git fsck) -settings.admin_code_indexer = Code Indexer +settings.releases_desc = Enable repository releases +settings.packages_desc = Enable repository package registry +settings.projects_desc = Enable repository projects +settings.actions_desc = Enable repository actions +settings.admin_settings = Administrator settings +settings.admin_enable_health_check = Enable repository health checks (git fsck) +settings.admin_code_indexer = Code indexer settings.admin_stats_indexer = Code statistics indexer -settings.admin_indexer_commit_sha = Last Indexed SHA +settings.admin_indexer_commit_sha = Last indexed SHA settings.admin_indexer_unindexed = Unindexed -settings.reindex_button = Add to Reindex Queue -settings.reindex_requested=Reindex Requested +settings.reindex_button = Add to reindex queue +settings.reindex_requested=Reindex requested settings.admin_enable_close_issues_via_commit_in_any_branch = Close an issue via a commit made in a non default branch -settings.danger_zone = Danger Zone +settings.danger_zone = Danger zone settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name. settings.new_owner_blocked_doer = The new owner has blocked you. -settings.convert = Convert to Regular Repository +settings.convert = Convert to regular repository settings.convert_desc = You can convert this mirror into a regular repository. This cannot be undone. settings.convert_notices_1 = This operation will convert the mirror into a regular repository and cannot be undone. -settings.convert_confirm = Convert Repository +settings.convert_confirm = Convert repository settings.convert_succeed = The mirror has been converted into a regular repository. -settings.convert_fork = Convert to Regular Repository +settings.convert_fork = Convert to regular repository settings.convert_fork_desc = You can convert this fork into a regular repository. This cannot be undone. settings.convert_fork_notices_1 = This operation will convert the fork into a regular repository and cannot be undone. -settings.convert_fork_confirm = Convert Repository +settings.convert_fork_confirm = Convert repository settings.convert_fork_succeed = The fork has been converted into a regular repository. -settings.transfer = Transfer Ownership +settings.transfer = Transfer ownership settings.transfer.rejected = Repository transfer was rejected. settings.transfer.success = Repository transfer was successful. settings.transfer_abort = Cancel transfer @@ -2181,13 +2193,13 @@ settings.transfer_in_progress = There is currently an ongoing transfer. Please c settings.transfer_notices_1 = - You will lose access to the repository if you transfer it to an individual user. settings.transfer_notices_2 = - You will keep access to the repository if you transfer it to an organization that you (co-)own. settings.transfer_notices_3 = - If the repository is private and is transferred to an individual user, this action makes sure that the user does have at least read permission (and changes permissions if necessary). -settings.transfer_owner = New Owner -settings.transfer_perform = Perform Transfer +settings.transfer_owner = New owner +settings.transfer_perform = Perform transfer settings.transfer_started = This repository has been marked for transfer and awaits confirmation from "%s" settings.transfer_succeed = The repository has been transferred. -settings.signing_settings = Signing Verification Settings -settings.trust_model = Signature Trust Model -settings.trust_model.default = Default Trust Model +settings.signing_settings = Signing verification settings +settings.trust_model = Signature trust model +settings.trust_model.default = Default trust model settings.trust_model.default.desc= Use the default repository trust model for this installation. settings.trust_model.collaborator = Collaborator settings.trust_model.collaborator.long = Collaborator: Trust signatures by collaborators @@ -2201,16 +2213,16 @@ settings.trust_model.collaboratorcommitter.desc = Valid signatures by collaborat settings.wiki_rename_branch_main = Normalize the Wiki branch name settings.wiki_rename_branch_main_desc = Rename the branch used internally by the Wiki to "%s". This is a permanent and cannot be undone. settings.wiki_rename_branch_main_notices_1 = This operation CANNOT be undone. -settings.wiki_rename_branch_main_notices_2 = This will premanently rename the the internal branch of %s's repository wiki. Existing checkouts will need to be updated. +settings.wiki_rename_branch_main_notices_2 = This will permanently rename the the internal branch of %s's repository wiki. Existing checkouts will need to be updated. settings.wiki_branch_rename_success = The repository wiki's branch name has been successfully normalized. settings.wiki_branch_rename_failure = Failed to normalize the repository wiki's branch name. settings.confirm_wiki_branch_rename = Rename the wiki branch -settings.wiki_delete = Delete Wiki Data +settings.wiki_delete = Delete wiki data settings.wiki_delete_desc = Deleting repository wiki data is permanent and cannot be undone. settings.wiki_delete_notices_1 = - This will permanently delete and disable the repository wiki for %s. -settings.confirm_wiki_delete = Delete Wiki Data +settings.confirm_wiki_delete = Delete wiki data settings.wiki_deletion_success = The repository wiki data has been deleted. -settings.delete = Delete This Repository +settings.delete = Delete this repository settings.delete_desc = Deleting a repository is permanent and cannot be undone. settings.delete_notices_1 = - This operation CANNOT be undone. settings.delete_notices_2 = - This operation will permanently delete the %s repository including code, issues, comments, wiki data and collaborator settings. @@ -2218,8 +2230,8 @@ settings.delete_notices_fork_1 = - Forks of this repository will become independ settings.deletion_success = The repository has been deleted. settings.update_settings_success = The repository settings have been updated. settings.update_settings_no_unit = The repository should allow at least some sort of interaction. -settings.confirm_delete = Delete Repository -settings.add_collaborator = Add Collaborator +settings.confirm_delete = Delete repository +settings.add_collaborator = Add collaborator settings.add_collaborator_success = The collaborator has been added. settings.add_collaborator_inactive_user = Cannot add an inactive user as a collaborator. settings.add_collaborator_owner = Cannot add an owner as a collaborator. @@ -2235,20 +2247,20 @@ settings.org_not_allowed_to_be_collaborator = Organizations cannot be added as a settings.change_team_access_not_allowed = Changing team access for repository has been restricted to organization owner settings.team_not_in_organization = The team is not in the same organization as the repository settings.teams = Teams -settings.add_team = Add Team +settings.add_team = Add team settings.add_team_duplicate = Team already has the repository settings.add_team_success = The team now have access to the repository. -settings.search_team = Search Team… +settings.search_team = Search team… settings.change_team_permission_tip = Team's permission is set on the team setting page and can't be changed per repository settings.delete_team_tip = This team has access to all repositories and can't be removed settings.remove_team_success = The team's access to the repository has been removed. -settings.add_webhook = Add Webhook +settings.add_webhook = Add webhook settings.add_webhook.invalid_channel_name = Webhook channel name cannot be empty and cannot contain only a # character. settings.hooks_desc = Webhooks automatically make HTTP POST requests to a server when certain Forgejo events trigger. Read more in the webhooks guide. -settings.webhook_deletion = Remove Webhook +settings.webhook_deletion = Remove webhook settings.webhook_deletion_desc = Removing a webhook deletes its settings and delivery history. Continue? settings.webhook_deletion_success = The webhook has been removed. -settings.webhook.test_delivery = Test Delivery +settings.webhook.test_delivery = Test delivery settings.webhook.test_delivery_desc = Test this webhook with a fake event. settings.webhook.test_delivery_desc_disabled = To test this webhook with a fake event, activate it. settings.webhook.request = Request @@ -2259,26 +2271,26 @@ settings.webhook.body = Body settings.webhook.replay.description = Replay this webhook. settings.webhook.replay.description_disabled = To replay this webhook, activate it. settings.webhook.delivery.success = An event has been added to the delivery queue. It may take few seconds before it shows up in the delivery history. -settings.githooks_desc = Git Hooks are powered by Git itself. You can edit hook files below to set up custom operations. +settings.githooks_desc = Git hooks are powered by Git itself. You can edit hook files below to set up custom operations. settings.githook_edit_desc = If the hook is inactive, sample content will be presented. Leaving content to an empty value will disable this hook. -settings.githook_name = Hook Name -settings.githook_content = Hook Content -settings.update_githook = Update Hook -settings.add_webhook_desc = Forgejo will send POST requests with a specified content type to the target URL. Read more in the webhooks guide. +settings.githook_name = Hook name +settings.githook_content = Hook content +settings.update_githook = Update hook +settings.add_webhook_desc = Forgejo will send POST requests with a specified Content-Type to the target URL. Read more in the webhooks guide. settings.payload_url = Target URL -settings.http_method = HTTP Method -settings.content_type = POST Content Type +settings.http_method = HTTP method +settings.content_type = POST content type settings.secret = Secret settings.slack_username = Username settings.slack_icon_url = Icon URL settings.slack_color = Color settings.discord_username = Username settings.discord_icon_url = Icon URL -settings.event_desc = Trigger On: -settings.event_push_only = Push Events -settings.event_send_everything = All Events -settings.event_choose = Custom Events… -settings.event_header_repository = Repository Events +settings.event_desc = Trigger on: +settings.event_push_only = Push events +settings.event_send_everything = All events +settings.event_choose = Custom events… +settings.event_header_repository = Repository events settings.event_create = Create settings.event_create_desc = Branch or tag created. settings.event_delete = Delete @@ -2293,50 +2305,50 @@ settings.event_push = Push settings.event_push_desc = Git push to a repository. settings.event_repository = Repository settings.event_repository_desc = Repository created or deleted. -settings.event_header_issue = Issue Events +settings.event_header_issue = Issue events settings.event_issues = Issues settings.event_issues_desc = Issue opened, closed, reopened, or edited. -settings.event_issue_assign = Issue Assigned +settings.event_issue_assign = Issue assigned settings.event_issue_assign_desc = Issue assigned or unassigned. -settings.event_issue_label = Issue Labeled +settings.event_issue_label = Issue labeled settings.event_issue_label_desc = Issue labels updated or cleared. -settings.event_issue_milestone = Issue Milestoned +settings.event_issue_milestone = Issue milestoned settings.event_issue_milestone_desc = Issue milestoned or demilestoned. -settings.event_issue_comment = Issue Comment +settings.event_issue_comment = Issue comment settings.event_issue_comment_desc = Issue comment created, edited, or deleted. -settings.event_header_pull_request = Pull Request Events -settings.event_pull_request = Pull Request +settings.event_header_pull_request = Pull request events +settings.event_pull_request = Pull request settings.event_pull_request_desc = Pull request opened, closed, reopened, or edited. -settings.event_pull_request_assign = Pull Request Assigned +settings.event_pull_request_assign = Pull request assigned settings.event_pull_request_assign_desc = Pull request assigned or unassigned. -settings.event_pull_request_label = Pull Request Labeled +settings.event_pull_request_label = Pull request labeled settings.event_pull_request_label_desc = Pull request labels updated or cleared. -settings.event_pull_request_milestone = Pull Request Milestoned +settings.event_pull_request_milestone = Pull request milestoned settings.event_pull_request_milestone_desc = Pull request milestoned or demilestoned. -settings.event_pull_request_comment = Pull Request Comment +settings.event_pull_request_comment = Pull request comment settings.event_pull_request_comment_desc = Pull request comment created, edited, or deleted. -settings.event_pull_request_review = Pull Request Reviewed +settings.event_pull_request_review = Pull request reviewed settings.event_pull_request_review_desc = Pull request approved, rejected, or review comment. -settings.event_pull_request_sync = Pull Request Synchronized +settings.event_pull_request_sync = Pull request synchronized settings.event_pull_request_sync_desc = Pull request synchronized. -settings.event_pull_request_review_request = Pull Request Review Requested +settings.event_pull_request_review_request = Pull request review requested settings.event_pull_request_review_request_desc = Pull request review requested or review request removed. -settings.event_pull_request_approvals = Pull Request Approvals -settings.event_pull_request_merge = Pull Request Merge +settings.event_pull_request_approvals = Pull request approvals +settings.event_pull_request_merge = Pull request merge settings.event_package = Package settings.event_package_desc = Package created or deleted in a repository. settings.branch_filter = Branch filter settings.branch_filter_desc = Branch whitelist for push, branch creation and branch deletion events, specified as glob pattern. If empty or *, events for all branches are reported. See github.com/gobwas/glob documentation for syntax. Examples: master, {master,release*}. -settings.authorization_header = Authorization Header +settings.authorization_header = Authorization header settings.authorization_header_desc = Will be included as authorization header for requests when present. Examples: %s. settings.active = Active settings.active_helper = Information about triggered events will be sent to this webhook URL. settings.add_hook_success = The webhook has been added. -settings.update_webhook = Update Webhook +settings.update_webhook = Update webhook settings.update_hook_success = The webhook has been updated. -settings.delete_webhook = Remove Webhook -settings.recent_deliveries = Recent Deliveries -settings.hook_type = Hook Type +settings.delete_webhook = Remove webhook +settings.recent_deliveries = Recent deliveries +settings.hook_type = Hook type settings.slack_token = Token settings.slack_domain = Domain settings.slack_channel = Channel @@ -2358,10 +2370,10 @@ settings.web_hook_name_packagist = Packagist settings.packagist_username = Packagist username settings.packagist_api_token = API token settings.packagist_package_url = Packagist package URL -settings.deploy_keys = Deploy Keys -settings.add_deploy_key = Add Deploy Key +settings.deploy_keys = Deploy keys +settings.add_deploy_key = Add deploy key settings.deploy_key_desc = Deploy keys have read-only pull access to the repository. -settings.is_writable = Enable Write Access +settings.is_writable = Enable write access settings.is_writable_info = Allow this deploy key to push to the repository. settings.no_deploy_keys = There are no deploy keys yet. settings.title = Title @@ -2369,37 +2381,37 @@ settings.deploy_key_content = Content settings.key_been_used = A deploy key with identical content is already in use. settings.key_name_used = A deploy key with the same name already exists. settings.add_key_success = The deploy key "%s" has been added. -settings.deploy_key_deletion = Remove Deploy Key +settings.deploy_key_deletion = Remove reploy key settings.deploy_key_deletion_desc = Removing a deploy key will revoke its access to this repository. Continue? settings.deploy_key_deletion_success = The deploy key has been removed. settings.branches = Branches -settings.protected_branch = Branch Protection -settings.protected_branch.save_rule = Save Rule -settings.protected_branch.delete_rule = Delete Rule +settings.protected_branch = Branch protection +settings.protected_branch.save_rule = Save rule +settings.protected_branch.delete_rule = Delete rule settings.protected_branch_can_push = Allow push? settings.protected_branch_can_push_yes = You can push settings.protected_branch_can_push_no = You cannot push -settings.branch_protection = Branch Protection Rules for Branch "%s" -settings.protect_this_branch = Enable Branch Protection +settings.branch_protection = Branch protection rules for branch "%s" +settings.protect_this_branch = Enable branch protection settings.protect_this_branch_desc = Prevents deletion and restricts Git pushing and merging to the branch. -settings.protect_disable_push = Disable Push +settings.protect_disable_push = Disable push settings.protect_disable_push_desc = No pushing will be allowed to this branch. -settings.protect_enable_push = Enable Push +settings.protect_enable_push = Enable push settings.protect_enable_push_desc = Anyone with write access will be allowed to push to this branch (but not force push). -settings.protect_enable_merge = Enable Merge +settings.protect_enable_merge = Enable merge settings.protect_enable_merge_desc = Anyone with write access will be allowed to merge the pull requests into this branch. -settings.protect_whitelist_committers = Whitelist Restricted Push +settings.protect_whitelist_committers = Whitelist restricted push settings.protect_whitelist_committers_desc = Only whitelisted users or teams will be allowed to push to this branch (but not force push). settings.protect_whitelist_deploy_keys = Whitelist deploy keys with write access to push. settings.protect_whitelist_users = Whitelisted users for pushing: settings.protect_whitelist_search_users = Search users… settings.protect_whitelist_teams = Whitelisted teams for pushing: settings.protect_whitelist_search_teams = Search teams… -settings.protect_merge_whitelist_committers = Enable Merge Whitelist +settings.protect_merge_whitelist_committers = Enable merge whitelist settings.protect_merge_whitelist_committers_desc = Allow only whitelisted users or teams to merge pull requests into this branch. settings.protect_merge_whitelist_users = Whitelisted users for merging: settings.protect_merge_whitelist_teams = Whitelisted teams for merging: -settings.protect_check_status_contexts = Enable Status Check +settings.protect_check_status_contexts = Enable status check settings.protect_status_check_patterns = Status check patterns: settings.protect_status_check_patterns_desc = Enter patterns to specify which status checks must pass before branches can be merged into a branch that matches this rule. Each line specifies a pattern. Patterns cannot be empty. settings.protect_check_status_contexts_desc = Require status checks to pass before merging. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are matched, the last commit must be successful regardless of context. @@ -2417,9 +2429,9 @@ settings.dismiss_stale_approvals = Dismiss stale approvals settings.dismiss_stale_approvals_desc = When new commits that change the content of the pull request are pushed to the branch, old approvals will be dismissed. settings.ignore_stale_approvals = Ignore stale approvals settings.ignore_stale_approvals_desc = Do not count approvals that were made on older commits (stale reviews) towards how many approvals the PR has. Irrelevant if stale reviews are already dismissed. -settings.require_signed_commits = Require Signed Commits +settings.require_signed_commits = Require signed commits settings.require_signed_commits_desc = Reject pushes to this branch if they are unsigned or unverifiable. -settings.protect_branch_name_pattern = Protected Branch Name Pattern +settings.protect_branch_name_pattern = Protected branch name pattern settings.protect_branch_name_pattern_desc = Protected branch name patterns. See the documentation for pattern syntax. Examples: main, release/** settings.protect_patterns = Patterns settings.protect_protected_file_patterns = Protected file patterns (separated using semicolon ";"): @@ -2431,7 +2443,7 @@ settings.delete_protected_branch = Disable protection settings.update_protect_branch_success = Branch protection for rule "%s" has been updated. settings.remove_protected_branch_success = Branch protection for rule "%s" has been removed. settings.remove_protected_branch_failed = Removing branch protection rule "%s" failed. -settings.protected_branch_deletion = Delete Branch Protection +settings.protected_branch_deletion = Delete branch protection settings.protected_branch_deletion_desc = Disabling branch protection allows users with write permission to push to the branch. Continue? settings.block_rejected_reviews = Block merge on rejected reviews settings.block_rejected_reviews_desc = Merging will not be possible when changes are requested by official reviewers, even if there are enough approvals. @@ -2440,8 +2452,8 @@ settings.block_on_official_review_requests_desc = Merging will not be possible w settings.block_outdated_branch = Block merge if pull request is outdated settings.block_outdated_branch_desc = Merging will not be possible when head branch is behind base branch. settings.default_branch_desc = Select a default repository branch for pull requests and code commits: -settings.merge_style_desc = Merge Styles -settings.default_merge_style_desc = Default Merge Style +settings.merge_style_desc = Merge styles +settings.default_merge_style_desc = Default merge style settings.choose_branch = Choose a branch… settings.no_protected_branch = There are no protected branches. settings.edit_protected_branch = Edit @@ -2449,23 +2461,23 @@ settings.protected_branch_required_rule_name = Required rule name settings.protected_branch_duplicate_rule_name = There is already a rule for this set of branches settings.protected_branch_required_approvals_min = Required approvals cannot be negative. settings.tags = Tags -settings.tags.protection = Tag Protection -settings.tags.protection.pattern = Tag Pattern +settings.tags.protection = Tag protection +settings.tags.protection.pattern = Tag pattern settings.tags.protection.allowed = Allowed settings.tags.protection.allowed.users = Allowed users settings.tags.protection.allowed.teams = Allowed teams -settings.tags.protection.allowed.noone = No One -settings.tags.protection.create = Protect Tag +settings.tags.protection.allowed.noone = No one +settings.tags.protection.create = Add rule settings.tags.protection.none = There are no protected tags. settings.tags.protection.pattern.description = You can use a single name or a glob pattern or regular expression to match multiple tags. Read more in the protected tags guide. -settings.bot_token = Bot Token +settings.bot_token = Bot token settings.chat_id = Chat ID settings.thread_id = Thread ID settings.matrix.homeserver_url = Homeserver URL settings.matrix.room_id = Room ID -settings.matrix.message_type = Message Type -settings.archive.button = Archive Repo -settings.archive.header = Archive This Repo +settings.matrix.message_type = Message type +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. 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. @@ -2511,17 +2523,17 @@ settings.rename_branch_from=old branch name settings.rename_branch_to=new branch name settings.rename_branch=Rename branch -diff.browse_source = Browse Source +diff.browse_source = Browse source diff.parent = parent diff.commit = commit diff.git-notes = Notes -diff.data_not_available = Diff Content Not Available -diff.options_button = Diff Options -diff.show_diff_stats = Show Stats -diff.download_patch = Download Patch File -diff.download_diff = Download Diff File -diff.show_split_view = Split View -diff.show_unified_view = Unified View +diff.data_not_available = Diff content is not available +diff.options_button = Diff options +diff.show_diff_stats = Show stats +diff.download_patch = Download patch file +diff.download_diff = Download diff file +diff.show_split_view = Split view +diff.show_unified_view = Unified view diff.whitespace_button = Whitespace diff.whitespace_show_everything = Show all changes diff.whitespace_ignore_all_whitespace = Ignore whitespace when comparing lines @@ -2531,7 +2543,7 @@ diff.stats_desc = %d changed files with %d additionsdocumentation to fix them, then push some commits to refresh the status. [graphs] component_loading = Loading %s... @@ -2681,26 +2694,26 @@ contributors.what = contributions recent_commits.what = recent commits [org] -org_name_holder = Organization Name -org_full_name_holder = Organization Full Name +org_name_holder = Organization name +org_full_name_holder = Organization full name org_name_helper = Organization names should be short and memorable. -create_org = Create Organization +create_org = Create organization repo_updated = Updated members = Members teams = Teams code = Code lower_members = members lower_repositories = repositories -create_new_team = New Team -create_team = Create Team +create_new_team = New team +create_team = Create team org_desc = Description -team_name = Team Name +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_desc = Allow access to tepository sections team_unit_disabled = (Disabled) follow_blocked_user = You cannot follow this organisation because this organisation has blocked you. @@ -2710,40 +2723,40 @@ form.create_org_not_allowed = You are not allowed to create an organization. settings = Settings settings.options = Organization -settings.full_name = Full Name -settings.email = Contact Email +settings.full_name = Full name +settings.email = Contact email settings.website = Website settings.location = Location settings.permission = Permissions settings.repoadminchangeteam = Repository admin can add and remove access for teams settings.visibility = Visibility settings.visibility.public = Public -settings.visibility.limited = Limited (Visible to authenticated users only) +settings.visibility.limited = Limited (visible only to authenticated users) settings.visibility.limited_shortname = Limited -settings.visibility.private = Private (Visible only to organization members) +settings.visibility.private = Private (visible only to organization members) settings.visibility.private_shortname = Private -settings.update_settings = Update Settings +settings.update_settings = Update settings settings.update_setting_success = Organization settings have been updated. settings.change_orgname_prompt = Note: Changing the organization name will also change your organization's URL and free the old name. settings.change_orgname_redirect_prompt = The old name will redirect until it is claimed. settings.update_avatar_success = The organization's avatar has been updated. -settings.delete = Delete Organization -settings.delete_account = Delete This Organization +settings.delete = Delete organization +settings.delete_account = Delete this organization settings.delete_prompt = The organization will be permanently removed. This CANNOT be undone! -settings.confirm_delete_account = Confirm Deletion -settings.delete_org_title = Delete Organization +settings.confirm_delete_account = Confirm deletion +settings.delete_org_title = Delete organization settings.delete_org_desc = This organization will be deleted permanently. Continue? settings.hooks_desc = Add webhooks which will be triggered for all repositories under this organization. settings.labels_desc = Add labels which can be used on issues for all repositories under this organization. -members.membership_visibility = Membership Visibility: +members.membership_visibility = Membership visibility: members.public = Visible members.public_helper = make hidden members.private = Hidden members.private_helper = make visible -members.member_role = Member Role: +members.member_role = Member role: members.owner = Owner members.member = Member members.remove = Remove @@ -2751,40 +2764,40 @@ members.remove.detail = Remove %[1]s from %[2]s? members.leave = Leave members.leave.detail = Leave %s? members.invite_desc = Add a new member to %s: -members.invite_now = Invite Now +members.invite_now = Invite now teams.join = Join teams.leave = Leave teams.leave.detail = Leave %s? teams.can_create_org_repo = Create repositories teams.can_create_org_repo_helper = Members can create new repositories in organization. Creator will get administrator access to the new repository. -teams.none_access = No Access +teams.none_access = No access teams.none_access_helper = Members cannot view or do any other action on this unit. It has no effect for public repositories. -teams.general_access = General Access +teams.general_access = General access teams.general_access_helper = Members permissions will be decided by below permission table. teams.read_access = Read teams.read_access_helper = Members can view and clone team repositories. teams.write_access = Write teams.write_access_helper = Members can read and push to team repositories. -teams.admin_access = Administrator Access +teams.admin_access = Administrator access teams.admin_access_helper = Members can pull and push to team repositories and add collaborators to them. teams.no_desc = This team has no description teams.settings = Settings teams.owners_permission_desc = Owners have full access to all repositories and have administrator access to the organization. -teams.members = Team Members -teams.update_settings = Update Settings -teams.delete_team = Delete Team -teams.add_team_member = Add Team Member +teams.members = Team members +teams.update_settings = Update settings +teams.delete_team = Delete team +teams.add_team_member = Add team member teams.invite_team_member = Invite to %s -teams.invite_team_member.list = Pending Invitations -teams.delete_team_title = Delete Team +teams.invite_team_member.list = Pending invitations +teams.delete_team_title = Delete team teams.delete_team_desc = Deleting a team revokes repository access from its members. Continue? teams.delete_team_success = The team has been deleted. teams.read_permission_desc = This team grants Read access: members can view and clone team repositories. teams.write_permission_desc = This team grants Write access: members can read from and push to team repositories. teams.admin_permission_desc = This team grants Admin access: members can read from, push to and add collaborators to team repositories. teams.create_repo_permission_desc = Additionally, this team grants Create repository permission: members can create new repositories in organization. -teams.repositories = Team Repositories +teams.repositories = Team repositories teams.search_repo_placeholder = Search repository… teams.remove_all_repos_title = Remove all team repositories teams.remove_all_repos_desc = This will remove all repositories from the team. @@ -2807,28 +2820,30 @@ teams.invite.description = Please click the button below to join the team. [admin] dashboard = Dashboard -self_check = Self Check -identity_access = Identity & Access -users = User Accounts +self_check = Self check +identity_access = Identity & access +users = User accounts organizations = Organizations -assets = Code Assets +assets = Code assets repositories = Repositories hooks = Webhooks integrations = Integrations -authentication = Authentication Sources -emails = User Emails +authentication = Authentication sources +emails = User emails config = Configuration -notices = System Notices +notices = System notices +config_summary = Summary +config_settings = Settings monitor = Monitoring first_page = First last_page = Last total = Total: %d -settings = Admin Settings +settings = Admin settings dashboard.new_version_hint = Forgejo %s is now available, you are running %s. Check the blog for more details. dashboard.statistic = Summary -dashboard.operations = Maintenance Operations -dashboard.system_status = System Status +dashboard.operations = Maintenance operations +dashboard.system_status = System status dashboard.operation_name = Operation Name dashboard.operation_switch = Switch dashboard.operation_run = Run @@ -2852,9 +2867,9 @@ dashboard.delete_repo_archives.started = Delete all repository archives task sta dashboard.delete_missing_repos = Delete all repositories missing their Git files dashboard.delete_missing_repos.started = Delete all repositories missing their Git files task started. dashboard.delete_generated_repository_avatars = Delete generated repository avatars -dashboard.sync_repo_branches = Sync missed branches from git data to database -dashboard.sync_repo_tags = Sync tags from git data to database -dashboard.update_mirrors = Update Mirrors +dashboard.sync_repo_branches = Sync missed branches from Git data to database +dashboard.sync_repo_tags = Sync tags from Git data to database +dashboard.update_mirrors = Update mirrors dashboard.repo_health_check = Health check all repositories dashboard.check_repo_stats = Check all repository statistics dashboard.archive_cleanup = Delete old repository archives @@ -2869,35 +2884,34 @@ dashboard.sync_external_users = Synchronize external user data dashboard.cleanup_hook_task_table = Cleanup hook_task table dashboard.cleanup_packages = Cleanup expired packages dashboard.cleanup_actions = Cleanup expired logs and artifacts from actions -dashboard.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 = Since Last GC Time -dashboard.total_gc_time = Total GC Pause -dashboard.total_gc_pause = Total GC Pause -dashboard.last_gc_pause = Last GC Pause -dashboard.gc_times = GC Times +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 actions from database dashboard.delete_old_actions.started = Delete all old actions from database started. dashboard.update_checker = Update checker @@ -2911,10 +2925,10 @@ dashboard.sync_branch.started = Branches Sync started dashboard.sync_tag.started = Tags Sync started dashboard.rebuild_issue_indexer = Rebuild issue indexer -users.user_manage_panel = User Account Management +users.user_manage_panel = Manage user accounts users.new_account = Create User Account users.name = Username -users.full_name = Full Name +users.full_name = Full name users.activated = Activated users.admin = Admin users.restricted = Restricted @@ -2924,29 +2938,29 @@ users.remote = Remote users.2fa = 2FA users.repos = Repos users.created = Created -users.last_login = Last Sign-In -users.never_login = Never Signed-In -users.send_register_notify = Send User Registration Notification +users.last_login = Last sign-in +users.never_login = Never signed in +users.send_register_notify = Send user registration notification users.new_success = The user account "%s" has been created. users.edit = Edit -users.auth_source = Authentication Source +users.auth_source = Authentication source users.local = Local -users.auth_login_name = Authentication Sign-In Name +users.auth_login_name = Authentication sign-in name users.password_helper = Leave the password empty to keep it unchanged. users.update_profile_success = The user account has been updated. -users.edit_account = Edit User Account -users.max_repo_creation = Maximum Number of Repositories +users.edit_account = Edit user account +users.max_repo_creation = Maximum number of repositories users.max_repo_creation_desc = (Enter -1 to use the global default limit.) users.is_activated = User Account Is Activated -users.prohibit_login = Disable Sign-In -users.is_admin = Is Administrator -users.is_restricted = Is Restricted -users.allow_git_hook = May Create Git Hooks -users.allow_git_hook_tooltip = Git Hooks are executed as the OS user running Forgejo and will have the same level of host access. As a result, users with this special Git Hook privilege can access and modify all Forgejo repositories as well as the database used by Forgejo. Consequently they are also able to gain Forgejo administrator privileges. -users.allow_import_local = May Import Local Repositories -users.allow_create_organization = May Create Organizations -users.update_profile = Update User Account -users.delete_account = Delete User Account +users.prohibit_login = Disable sign-in +users.is_admin = Is administrator +users.is_restricted = Is restricted +users.allow_git_hook = Can create Git hooks +users.allow_git_hook_tooltip = Git hooks are executed as the OS user running Forgejo and will have the same level of host access. As a result, users with this special Git hook privilege can access and modify all Forgejo repositories as well as the database used by Forgejo. Consequently they are also able to gain Forgejo administrator privileges. +users.allow_import_local = Can import local repositories +users.allow_create_organization = Can create organizations +users.update_profile = Update user account +users.delete_account = Delete user account users.cannot_delete_self = You cannot delete yourself users.still_own_repo = This user still owns one or more repositories. Delete or transfer these repositories first. users.still_has_org = This user is a member of an organization. Remove the user from any organizations first. @@ -2969,27 +2983,27 @@ 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 = User Email Management +emails.email_manage_panel = Manage user emails emails.primary = Primary emails.activated = Activated emails.filter_sort.email = Email emails.filter_sort.email_reverse = Email (reverse) -emails.filter_sort.name = User Name -emails.filter_sort.name_reverse = User Name (reverse) +emails.filter_sort.name = Username +emails.filter_sort.name_reverse = Username (reverse) emails.updated = Email updated emails.not_updated = Failed to update the requested email address: %v emails.duplicate_active = This email address is already active for a different user. emails.change_email_header = Update Email Properties emails.change_email_text = Are you sure you want to update this email address? -orgs.org_manage_panel = Organization Management +orgs.org_manage_panel = Manage organizations orgs.name = Name orgs.teams = Teams orgs.members = Members -orgs.new_orga = New Organization +orgs.new_orga = New organization -repos.repo_manage_panel = Repository Management -repos.unadopted = Unadopted Repositories +repos.repo_manage_panel = Manage repositories +repos.unadopted = Unadopted repositories repos.unadopted.no_more = No more unadopted repositories found repos.owner = Owner repos.name = Name @@ -3001,7 +3015,7 @@ repos.issues = Issues repos.size = Size repos.lfs_size = LFS Size -packages.package_manage_panel = Package Management +packages.package_manage_panel = Manage packages packages.total_size = Total Size: %s packages.unreferenced_size = Unreferenced Size: %s packages.cleanup = Clean up expired data @@ -3015,70 +3029,70 @@ packages.repository = Repository packages.size = Size packages.published = Published -defaulthooks = Default Webhooks +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 -systemhooks = System Webhooks +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 -auths.auth_manage_panel = Authentication Source Management -auths.new = Add Authentication Source +auths.auth_manage_panel = Manage authentication sources +auths.new = Add authentication source auths.name = Name auths.type = Type auths.enabled = Enabled -auths.syncenabled = Enable User Synchronization +auths.syncenabled = Enable user synchronization auths.updated = Updated -auths.auth_type = Authentication Type -auths.auth_name = Authentication Name -auths.security_protocol = Security Protocol +auths.auth_type = Authentication type +auths.auth_name = Authentication name +auths.security_protocol = Security protocol auths.domain = Domain auths.host = Host auths.port = Port auths.bind_dn = Bind DN -auths.bind_password = Bind Password -auths.user_base = User Search Base +auths.bind_password = Bind password +auths.user_base = User search base auths.user_dn = User DN -auths.attribute_username = Username Attribute +auths.attribute_username = Username attribute auths.attribute_username_placeholder = Leave empty to use the username entered in Forgejo. -auths.attribute_name = First Name Attribute -auths.attribute_surname = Surname Attribute -auths.attribute_mail = Email Attribute -auths.attribute_ssh_public_key = Public SSH Key Attribute -auths.attribute_avatar = Avatar Attribute -auths.attributes_in_bind = Fetch Attributes in Bind DN Context +auths.attribute_name = First name attribute +auths.attribute_surname = Surname attribute +auths.attribute_mail = Email attribute +auths.attribute_ssh_public_key = Public SSH key attribute +auths.attribute_avatar = Avatar attribute +auths.attributes_in_bind = Fetch attributes in nind DN context auths.allow_deactivate_all = Allow an empty search result to deactivate all users -auths.use_paged_search = Use Paged Search -auths.search_page_size = Page Size -auths.filter = User Filter -auths.admin_filter = Admin Filter -auths.restricted_filter = Restricted Filter -auths.restricted_filter_helper = Leave empty to not set any users as restricted. Use an asterisk ("*") to set all users that do not match Admin Filter as restricted. +auths.use_paged_search = Use paged search +auths.search_page_size = Page size +auths.filter = User filter +auths.admin_filter = Admin filter +auths.restricted_filter = Restricted filter +auths.restricted_filter_helper = Leave empty to not set any users as restricted. Use an asterisk ("*") to set all users that do not match Admin filter as restricted. auths.verify_group_membership = Verify group membership in LDAP (leave the filter empty to skip) -auths.group_search_base = Group Search Base DN -auths.group_attribute_list_users = Group Attribute Containing List Of Users -auths.user_attribute_in_group = User Attribute Listed In Group +auths.group_search_base = Group search base DN +auths.group_attribute_list_users = Group attribute containing list of users +auths.user_attribute_in_group = User attribute listed in group auths.map_group_to_team = Map LDAP groups to Organization teams (leave the field empty to skip) auths.map_group_to_team_removal = Remove users from synchronized teams if user does not belong to corresponding LDAP group auths.enable_ldap_groups = Enable LDAP groups -auths.ms_ad_sa = MS AD Search Attributes -auths.smtp_auth = SMTP Authentication Type -auths.smtphost = SMTP Host -auths.smtpport = SMTP Port -auths.allowed_domains = Allowed Domains +auths.ms_ad_sa = MS AD search attributes +auths.smtp_auth = SMTP authentication type +auths.smtphost = SMTP host +auths.smtpport = SMTP port +auths.allowed_domains = Allowed domains auths.allowed_domains_helper = Leave empty to allow all domains. Separate multiple domains with a comma (","). -auths.skip_tls_verify = Skip TLS Verify +auths.skip_tls_verify = Skip TLS verification auths.force_smtps = Force SMTPS auths.force_smtps_helper = SMTPS is always used on port 465. Set this to force SMTPS on other ports. (Otherwise STARTTLS will be used on other ports if it is supported by the host.) -auths.helo_hostname = HELO Hostname +auths.helo_hostname = HELO hostname auths.helo_hostname_helper = Hostname sent with HELO. Leave blank to send current hostname. auths.disable_helo = Disable HELO -auths.pam_service_name = PAM Service Name -auths.pam_email_domain = PAM Email Domain (optional) -auths.oauth2_provider = OAuth2 Provider +auths.pam_service_name = PAM service name +auths.pam_email_domain = PAM email domain (optional) +auths.oauth2_provider = OAuth2 provider auths.oauth2_icon_url = Icon URL auths.oauth2_clientID = Client ID (Key) auths.oauth2_clientSecret = Client Secret @@ -3091,17 +3105,17 @@ auths.oauth2_emailURL = Email URL auths.skip_local_two_fa = Skip local 2FA auths.skip_local_two_fa_helper = Leaving unset means local users with 2FA set will still have to pass 2FA to log on auths.oauth2_tenant = Tenant -auths.oauth2_scopes = Additional Scopes -auths.oauth2_required_claim_name = Required Claim Name +auths.oauth2_scopes = Additional scopes +auths.oauth2_required_claim_name = Required claim name auths.oauth2_required_claim_name_helper = Set this name to restrict login from this source to users with a claim with this name -auths.oauth2_required_claim_value = Required Claim Value +auths.oauth2_required_claim_value = Required claim value auths.oauth2_required_claim_value_helper = Set this value to restrict login from this source to users with a claim with this name and value auths.oauth2_group_claim_name = Claim name providing group names for this source. (Optional) -auths.oauth2_admin_group = Group Claim value for administrator users. (Optional - requires claim name above) -auths.oauth2_restricted_group = Group Claim value for restricted users. (Optional - requires claim name above) -auths.oauth2_map_group_to_team = Map claimed groups to Organization teams. (Optional - requires claim name above) +auths.oauth2_admin_group = Group claim value for administrator users. (Optional - requires claim name above) +auths.oauth2_restricted_group = Group claim value for restricted users. (Optional - requires claim name above) +auths.oauth2_map_group_to_team = Map claimed groups to organization teams. (Optional - requires claim name above) auths.oauth2_map_group_to_team_removal = Remove users from synchronized teams if user does not belong to corresponding group. -auths.enable_auto_register = Enable Auto Registration +auths.enable_auto_register = Enable auto registration auths.sspi_auto_create_users = Automatically create users auths.sspi_auto_create_users_helper = Allow SSPI auth method to automatically create new accounts for users that login for the first time auths.sspi_auto_activate_users = Automatically activate users @@ -3113,9 +3127,10 @@ auths.sspi_separator_replacement_helper = The character to use to replace the se auths.sspi_default_language = Default user language auths.sspi_default_language_helper = Default language for users automatically created by SSPI auth method. Leave empty if you prefer language to be automatically detected. auths.tips = Tips -auths.tips.oauth2.general = OAuth2 Authentication +auths.tips.gmail_settings = Gmail settings: +auths.tips.oauth2.general = OAuth2 authentication auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be: -auths.tip.oauth2_provider = OAuth2 Provider +auths.tip.oauth2_provider = OAuth2 provider auths.tip.bitbucket = Register a new OAuth consumer on https://bitbucket.org/account/user//oauth-consumers/new and add the permission "Account" - "Read" auths.tip.nextcloud = Register a new OAuth consumer on your instance using the following menu "Settings -> Security -> OAuth 2.0 client" auths.tip.dropbox = Create a new application at https://www.dropbox.com/developers/apps @@ -3129,13 +3144,13 @@ auths.tip.discord = Register a new application on https://discordapp.com/develop auths.tip.gitea = Register a new OAuth2 application. Guide can be found at https://docs.gitea.com/development/oauth2-provider auths.tip.yandex = Create a new application at https://oauth.yandex.com/client/new. Select following permissions from the "Yandex.Passport API" section: "Access to email address", "Access to user avatar" and "Access to username, first name and surname, gender" auths.tip.mastodon = Input a custom instance URL for the mastodon instance you want to authenticate with (or use the default one) -auths.edit = Edit Authentication Source -auths.activated = This Authentication Source is Activated +auths.edit = Edit authentication source +auths.activated = This authentication source is activated auths.new_success = The authentication "%s" has been added. auths.update_success = The authentication source has been updated. -auths.update = Update Authentication Source -auths.delete = Delete Authentication Source -auths.delete_auth_title = Delete Authentication Source +auths.update = Update authentication source +auths.delete = Delete authentication source +auths.delete_auth_title = Delete authentication source auths.delete_auth_desc = Deleting an authentication source prevents users from using it to sign in. Continue? auths.still_in_used = The authentication source is still in use. Convert or delete any users using this authentication source first. auths.deletion_success = The authentication source has been deleted. @@ -3144,43 +3159,43 @@ auths.login_source_of_type_exist = An authentication source of this type already auths.unable_to_initialize_openid = Unable to initialize OpenID Connect Provider: %s auths.invalid_openIdConnectAutoDiscoveryURL = Invalid Auto Discovery URL (this must be a valid URL starting with http:// or https://) -config.server_config = Server Configuration -config.app_name = Site Title -config.app_ver = Forgejo Version -config.app_url = Forgejo Base URL -config.custom_conf = Configuration File Path -config.custom_file_root_path = Custom File Root Path -config.domain = Server Domain -config.offline_mode = Local Mode -config.disable_router_log = Disable Router Log -config.run_user = Run As Username -config.run_mode = Run Mode -config.git_version = Git Version -config.app_data_path = App Data Path -config.repo_root_path = Repository Root Path -config.lfs_root_path = LFS Root Path -config.log_file_root_path = Log Path -config.script_type = Script Type -config.reverse_auth_user = Reverse Authentication User +config.server_config = Server configuration +config.app_name = Instance title +config.app_ver = Forgejo version +config.app_url = Base URL +config.custom_conf = Configuration file path +config.custom_file_root_path = Custom file root path +config.domain = Server domain +config.offline_mode = Local mode +config.disable_router_log = Disable router log +config.run_user = User to run as +config.run_mode = Run mode +config.git_version = Git version +config.app_data_path = App data path +config.repo_root_path = Repository root path +config.lfs_root_path = LFS root path +config.log_file_root_path = Log path +config.script_type = Script type +config.reverse_auth_user = Reverse authentication user -config.ssh_config = SSH Configuration +config.ssh_config = SSH configuration config.ssh_enabled = Enabled -config.ssh_start_builtin_server = Use Built-In Server -config.ssh_domain = SSH Server Domain +config.ssh_start_builtin_server = Use built-in server +config.ssh_domain = SSH server domain config.ssh_port = Port -config.ssh_listen_port = Listen Port -config.ssh_root_path = Root Path -config.ssh_key_test_path = Key Test Path -config.ssh_keygen_path = Keygen ("ssh-keygen") Path -config.ssh_minimum_key_size_check = Minimum Key Size Check -config.ssh_minimum_key_sizes = Minimum Key Sizes +config.ssh_listen_port = Listen port +config.ssh_root_path = Root path +config.ssh_key_test_path = Key test path +config.ssh_keygen_path = Keygen ("ssh-keygen") path +config.ssh_minimum_key_size_check = Minimum key size check +config.ssh_minimum_key_sizes = Minimum key sizes -config.lfs_config = LFS Configuration +config.lfs_config = LFS configuration config.lfs_enabled = Enabled -config.lfs_content_path = LFS Content Path -config.lfs_http_auth_expiry = LFS HTTP Auth Expiry +config.lfs_content_path = LFS content path +config.lfs_http_auth_expiry = LFS HTTP auth expiration time -config.db_config = Database Configuration +config.db_config = Database configuration config.db_type = Type config.db_host = Host config.db_name = Name @@ -3189,99 +3204,100 @@ config.db_schema = Schema config.db_ssl_mode = SSL config.db_path = Path -config.service_config = Service Configuration -config.register_email_confirm = Require Email Confirmation to Register -config.disable_register = Disable Self-Registration -config.allow_only_internal_registration = Allow Registration Only Through Forgejo itself -config.allow_only_external_registration = Allow Registration Only Through External Services -config.enable_openid_signup = Enable OpenID Self-Registration -config.enable_openid_signin = Enable OpenID Sign-In -config.show_registration_button = Show Register Button -config.require_sign_in_view = Require Sign-In to View Pages -config.mail_notify = Enable Email Notifications +config.service_config = Service configuration +config.register_email_confirm = Require email confirmation to register +config.disable_register = Disable self-registration +config.allow_only_internal_registration = Allow registration only through Forgejo itself +config.allow_only_external_registration = Allow registration only through external Services +config.enable_openid_signup = Enable OpenID self-registration +config.enable_openid_signin = Enable OpenID sign-in +config.show_registration_button = Show register button +config.require_sign_in_view = Require to sign-in to view content +config.mail_notify = Enable email notifications config.enable_captcha = Enable CAPTCHA -config.active_code_lives = Active Code Lives -config.reset_password_code_lives = Recover Account Code Expiry Time -config.default_keep_email_private = Hide Email Addresses by Default -config.default_allow_create_organization = Allow Creation of Organizations by Default -config.enable_timetracking = Enable Time Tracking -config.default_enable_timetracking = Enable Time Tracking by Default -config.default_allow_only_contributors_to_track_time = Let Only Contributors Track Time -config.no_reply_address = Hidden Email Domain -config.default_visibility_organization = Default visibility for new Organizations -config.default_enable_dependencies = Enable Issue Dependencies by Default +config.active_code_lives = Activation code expiration time +config.reset_password_code_lives = Recovery code expiration time +config.default_keep_email_private = Hide email addresses by default +config.default_allow_create_organization = Allow creation of organizations by default +config.enable_timetracking = Enable time tracking +config.default_enable_timetracking = Enable time tracking by default +config.default_allow_only_contributors_to_track_time = Let only contributors track time +config.no_reply_address = Hidden email domain +config.default_visibility_organization = Default visibility of new organizations +config.default_enable_dependencies = Enable issue dependencies by default -config.webhook_config = Webhook Configuration -config.queue_length = Queue Length -config.deliver_timeout = Deliver Timeout -config.skip_tls_verify = Skip TLS Verification +config.webhook_config = Webhook configuration +config.queue_length = Queue length +config.deliver_timeout = Deliver timeout +config.skip_tls_verify = Skip TLS verification -config.mailer_config = Mailer Configuration +config.mailer_config = Mailer configuration config.mailer_enabled = Enabled config.mailer_enable_helo = Enable HELO config.mailer_name = Name config.mailer_protocol = Protocol -config.mailer_smtp_addr = SMTP Addr -config.mailer_smtp_port = SMTP Port +config.mailer_smtp_addr = SMTP host +config.mailer_smtp_port = SMTP port config.mailer_user = User config.mailer_use_sendmail = Use Sendmail -config.mailer_sendmail_path = Sendmail Path +config.mailer_sendmail_path = Sendmail path config.mailer_sendmail_args = Extra Arguments to Sendmail -config.mailer_sendmail_timeout = Sendmail Timeout +config.mailer_sendmail_timeout = Sendmail timeout config.mailer_use_dummy = Dummy config.test_email_placeholder = Email (e.g. test@example.com) -config.send_test_mail = Send Testing Email +config.send_test_mail = Send test email config.send_test_mail_submit = Send -config.test_mail_failed = Failed to send a testing email to "%s": %v -config.test_mail_sent = A testing email has been sent to "%s". +config.test_mail_failed = Failed to send a test email to "%s": %v +config.test_mail_sent = A test email has been sent to "%s". -config.oauth_config = OAuth Configuration +config.oauth_config = OAuth configuration config.oauth_enabled = Enabled -config.cache_config = Cache Configuration -config.cache_adapter = Cache Adapter -config.cache_interval = Cache Interval -config.cache_conn = Cache Connection -config.cache_item_ttl = Cache Item TTL +config.cache_config = Cache configuration +config.cache_adapter = Cache adapter +config.cache_interval = Cache interval +config.cache_conn = Cache connection +config.cache_item_ttl = Cache item TTL -config.session_config = Session Configuration -config.session_provider = Session Provider -config.provider_config = Provider Config -config.cookie_name = Cookie Name -config.gc_interval_time = GC Interval Time -config.session_life_time = Session Life Time -config.https_only = HTTPS Only -config.cookie_life_time = Cookie Life Time +config.session_config = Session configuration +config.session_provider = Session provider +config.provider_config = Provider config +config.cookie_name = Cookie name +config.gc_interval_time = GC interval time +config.session_life_time = Session lifetime +config.https_only = HTTPS only +config.cookie_life_time = Cookie lifetime -config.picture_config = Picture and Avatar Configuration -config.picture_service = Picture Service +config.picture_config = Picture and avatar configuration +config.picture_service = Picture service config.disable_gravatar = Disable Gravatar -config.enable_federated_avatar = Enable Federated Avatars +config.enable_federated_avatar = Enable federated avatars +config.open_with_editor_app_help = The "Open with" editors for the clone menu. If left empty, the default will be used. Expand to see the default. -config.git_config = Git Configuration -config.git_disable_diff_highlight = Disable Diff Syntax Highlight -config.git_max_diff_lines = Max Diff Lines (for a single file) -config.git_max_diff_line_characters = Max Diff Characters (for a single line) -config.git_max_diff_files = Max Diff Files (to be shown) -config.git_gc_args = GC Arguments -config.git_migrate_timeout = Migration Timeout -config.git_mirror_timeout = Mirror Update Timeout -config.git_clone_timeout = Clone Operation Timeout -config.git_pull_timeout = Pull Operation Timeout -config.git_gc_timeout = GC Operation Timeout +config.git_config = Git configuration +config.git_disable_diff_highlight = Disable diff syntax highlighting +config.git_max_diff_lines = Max diff lines per file +config.git_max_diff_line_characters = Max diff characters per line +config.git_max_diff_files = Max diff files shown +config.git_gc_args = GC arguments +config.git_migrate_timeout = Migration timeout +config.git_mirror_timeout = Mirror Update timeout +config.git_clone_timeout = Clone Operation timeout +config.git_pull_timeout = Pull Operation timeout +config.git_gc_timeout = GC Operation timeout -config.log_config = Log Configuration +config.log_config = Log configuration config.logger_name_fmt = Logger: %s config.disabled_logger = Disabled -config.access_log_mode = Access Log Mode -config.access_log_template = Access Log Template +config.access_log_mode = Access log mode +config.access_log_template = Access log template config.xorm_log_sql = Log SQL config.set_setting_failed = Set setting %s failed monitor.stats = Stats -monitor.cron = Cron Tasks +monitor.cron = Cron tasks monitor.name = Name monitor.schedule = Schedule monitor.next = Next Time @@ -3305,29 +3321,29 @@ 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.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.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 = View Notice Details +notices.system_notice_list = System notices +notices.view_detail_header = Notice details notices.operations = Operations -notices.select_all = Select All -notices.deselect_all = Deselect All -notices.inverse_selection = Inverse Selection -notices.delete_selected = Delete Selected -notices.delete_all = Delete All Notices +notices.select_all = Select all +notices.deselect_all = Deselect all +notices.inverse_selection = Inverse selection +notices.delete_selected = Delete selected +notices.delete_all = Delete all notices notices.type = Type notices.type_1 = Repository notices.type_2 = Task @@ -3452,9 +3468,9 @@ dependencies = Dependencies keywords = Keywords details = Details details.author = Author -details.project_site = Project Site -details.repository_site = Repository Site -details.documentation_site = Documentation Site +details.project_site = Project website +details.repository_site = Repository website +details.documentation_site = Documentation website details.license = License assets = Assets versions = Versions @@ -3551,19 +3567,20 @@ settings.delete.notice = You are about to delete %s (%s). This operation is irre 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 = 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 = Rebuild index owner.settings.cargo.rebuild.description = Rebuilding can be useful if the index is not synchronized with the stored Cargo packages. owner.settings.cargo.rebuild.error = Failed to rebuild Cargo index: %v owner.settings.cargo.rebuild.success = The Cargo index was successfully rebuild. +owner.settings.cargo.rebuild.no_index = Cannot rebuild, no index is initialized. owner.settings.cleanuprules.title = Manage cleanup rules -owner.settings.cleanuprules.add = Add Cleanup Rule -owner.settings.cleanuprules.edit = Edit Cleanup Rule +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 = 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.enabled = Enabled @@ -3596,7 +3613,7 @@ deletion = Remove secret deletion.description = Removing a secret is permanent and cannot be undone. Continue? deletion.success = The secret has been removed. deletion.failed = Failed to remove secret. -management = Secrets Management +management = Manage secrets [actions] actions = Actions @@ -3613,8 +3630,8 @@ status.skipped = Skipped status.blocked = Blocked runners = Runners -runners.runner_manage_panel = Runners Management -runners.new = Create new Runner +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 @@ -3622,7 +3639,7 @@ runners.name = Name runners.owner_type = Type runners.description = Description runners.labels = Labels -runners.last_online = Last Online Time +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. @@ -3632,7 +3649,7 @@ 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 = Update changes runners.update_runner_success = Runner updated successfully runners.update_runner_failed = Failed to update runner runners.delete_runner = Delete this runner @@ -3649,7 +3666,7 @@ 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.all_workflows = All workflows runs.commit = Commit runs.scheduled = Scheduled runs.pushed_by = pushed by @@ -3667,17 +3684,17 @@ runs.no_workflows.documentation = For more information on Forgejo Actions, see < runs.no_runs = The workflow has no runs yet. runs.empty_commit_message = (empty commit message) -workflow.disable = Disable Workflow +workflow.disable = Disable workflow workflow.disable_success = Workflow "%s" disabled successfully. -workflow.enable = Enable Workflow +workflow.enable = Enable workflow workflow.enable_success = Workflow "%s" enabled successfully. workflow.disabled = Workflow is disabled. need_approval_desc = Need approval to run workflows for fork pull request. variables = Variables -variables.management = Variables Management -variables.creation = Add Variable +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? @@ -3692,9 +3709,9 @@ variables.update.failed = Failed to edit variable. variables.update.success = The variable has been edited. [projects] -type-1.display_name = Individual Project -type-2.display_name = Repository Project -type-3.display_name = Organization Project +type-1.display_name = Individual project +type-2.display_name = Repository project +type-3.display_name = Organization project [git.filemode] changed_filemode = %[1]s → %[2]s diff --git a/options/locale/locale_eo.ini b/options/locale/locale_eo.ini index 0f3f2ea010..0f03a1b9ba 100644 --- a/options/locale/locale_eo.ini +++ b/options/locale/locale_eo.ini @@ -92,7 +92,7 @@ copy_error = Malsukcesis kopii copy = Kopii enabled = Ŝaltita rerun = Reruli -milestones = Celpunktoj +milestones = Celoj show_timestamps = Montri datojn rss_feed = RSS-fluo never = Neniam @@ -118,6 +118,23 @@ new_project_column = Novan kolumnon new_migrate = Novan enporton mirror = Spegulo powered_by = Servas vin %s +remove = Forigi +filter = Filtri +filter.is_archived = Arĥivita +filter.not_archived = Nearĥivita +filter.is_fork = Disbranĉigita +filter.not_fork = Nedisbranĉigita +filter.is_mirror = Spegulita +filter.not_mirror = Nespegulita +filter.is_template = Ŝablono +filter.not_template = Neŝablono +filter.public = Publika +filter.private = Privata +dashboard = Labortablo +toggle_menu = Baskuli menuon +access_token = Alira ĵetono +remove_all = Forigi ĉion +remove_label_str = Forigi «%s» [editor] buttons.list.ordered.tooltip = Aldoni nombran liston @@ -152,11 +169,12 @@ network_error = Reteraro invalid_csrf = Malvalida peto: malvalida CSRF-kodo occurred = Eraris iel missing_csrf = Malvalida peto: neniu CSRF-kodo +server_internal = Eraris interno de servilo [heatmap] less = Malpli number_of_contributions_in_the_last_12_months = %s kontribuoj dum la pasintaj 12 monatoj -no_contributions = Neniu kontribuo +contributions_zero = Neniu kontribuo more = Pli [startpage] @@ -280,6 +298,8 @@ env_config_keys = Mediagordoj enable_update_checker_helper = Foje kontrolas ĉu estas novaj versioj per konektiĝo al gitea.io. invalid_password_algorithm = Malvalida pasvorthakeita algoritmo password_algorithm_helper = Agordas la pasvorthaketigan algoritmon. Algoritmoj havas malsamajn postulojn kaj efikecojn. La algoritmo argon2 sufiĉe sekuras, sed postulas multan memoron kaj eble ne taŭgas por nepotencaj serviloj. +internal_token_failed = Malsukcesis krei internan ĵetonon: %v +smtp_from_invalid = La «Sendu retleterojn kiel» adreso malvalidas [admin] config.app_data_path = Programdatuja doseiervojo @@ -306,6 +326,7 @@ show_only_unarchived = Montras sole nearĥivitajn my_mirrors = Miaj speguloj show_only_archived = Montras sole arĥivitajn view_home = Vidi %s +switch_dashboard_context = Ŝanĝi labortablon [explore] search.match.tooltip = Inkluzivu sole rezultojn kiuj akordas precize la serĉomendon @@ -345,7 +366,7 @@ invalid_password = Via pasvorto ne samas tiun uzitan dum kreiĝo de via konto. send_reset_mail = Sendi retleteron por rehavigo de konto oauth_signin_title = Salutu por aprobi kontligiĝon reset_password_helper = Rehavigi konton -login_openid = OpenID +tab_openid = OpenID openid_connect_submit = Konekti authorization_failed = Aprobo malsukcesis oauth_signup_tab = Registri novan konton @@ -389,6 +410,13 @@ openid_register_desc = La elektita OpenID URI estas nekonata. Ligi ĝin al nova reset_password = Rehavigo de konto sspi_auth_failed = SSPI aŭtentikigo malsukcesis must_change_password = Ŝanĝu vian pasvorton +remember_me = Memoru ĉi tiun aparaton +account_activated = Konto aktivigita +resent_limit_prompt = Vi jam petis freŝe aktivigan retleteron. Bonvolu atendi 3 minutojn kaj reprovu. +change_unconfirmed_email_summary = Ŝanĝi al retpoŝtadreson al kiu la aktiviga retletero sendiĝu. +invalid_code_forgot_password = Via konfirmkodo malvalidas aŭ jam eksdatiĝis. Klaku ĉi tien por komenci novan saluton. +authorize_redirect_notice = Vi alidirektiĝos al %s se vi aprobus ĉi tiun programon. +active_your_account = Aktivigi vian konton [mail] activate_account.text_1 = Saluton %[1]s, dankon pro via registriĝo ĉe %[2]s! @@ -437,6 +465,9 @@ reset_password = Rehavigi vian konton issue.action.merge = @%[1]s kunfandis #%[2]d al %[3]s. issue.action.push_1 = @%[1]s puŝis %[3]d enmeton al %[2]s issue.action.push_n = @%[1]s puŝis %[3]d enmetojn al %[2]s +activate_account = Bonvolu aktivigi vian konton +activate_account.title = %s, bonvolu aktivigi vian konton +activate_account.text_2 = Bonvolu klaki la jenan ligilon por aktivigi vian konton antaŭ %s: [form] TeamName = Gruponomo @@ -715,3 +746,60 @@ followers = Abonantoj block_user.detail_2 = La uzanto ne povos interagi viajn deponejojn, erarojn, kaj komentojn. block_user = Bloki uzanton change_avatar = Ŝanĝi vian profilbildon… + + +[repo] +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. +archive.pull.nocomment = Ĉi tiu deponejo estas arĥivigita. Vi ne povas respondi tirpetojn. +migrate_items_pullrequests = Tirpetoj +migrate.migrating_pulls = Migrante tirpetojn +unwatch = Malspuri +watch = Spuri +star = Stelumi +unstar = Malstelumi +fork = Disbranĉigi +code = Fontkodo +branch = Branĉo +pulls = Tirpetoj +commits = Enmetoj +releases = Eldonoj +commit_graph.hide_pr_refs = Kaŝi tirpetojn +activity.title.prs_n = %d tirpetoj +activity.opened_prs_count_n = Proponitaj tirpetoj +contributors.contribution_type.commits = Enmetoj +settings = Agordoj +settings.pulls_desc = Ŝalti tirpetojn por deponejo +settings.event_fork = Disbranĉigi +release.tags = Etikedoj +release.releases = Eldonoj +release.tag_name = Etikeda nomo +release.delete_tag = Forigi etikedon +find_file.go_to_file = Iri al dosiero +settings.archive.tagsettings_unavailable = Etikedaj agordoj neredakteblas je arĥivita deponejo. +pulls.tab_commits = Enmetoj +topic.manage_topics = Mastrumi temojn +tag.create_success = Etikedo «%s» kreita. +default_branch_helper = La implicita branĉo estas la baza branĉo por tirpetoj kaj enmetoj. +tags = Etikedoj +issues.no_ref = Neniu branĉo/etikedo donita +release.tags_for = Etikedoj por %s +tag = Etikedo +settings.tags.protection = Gardado de etikedoj +settings.tags.protection.create = Gardi etikedon +release.add_tag = Krei sole etikedon +settings.default_branch_desc = Elekti implicutan deponejan branĉon por tirpetoj kaj enmetoj: +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 + +[org] +code = Fontkodo +settings = Agordoj +teams.settings = Agordoj + +[packages] +npm.details.tag = Etikedo \ No newline at end of file diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 57ab69b255..0b4f0bef33 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -150,7 +150,7 @@ footer.links=Enlaces [heatmap] number_of_contributions_in_the_last_12_months=%s contribuciones en los últimos 12 meses -no_contributions=No hay contribuciones +contributions_zero=No hay contribuciones less=Menos more=Más @@ -396,7 +396,7 @@ twofa_scratch_used=Ya ha utilizado su código de respaldo. Ha sido redirigido a twofa_passcode_incorrect=Su código de acceso es incorrecta. Si extravió el dispositivo, use su código de respaldo para iniciar sesión. twofa_scratch_token_incorrect=El código de respaldo es incorrecto. login_userpass=Iniciar sesión -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Registrar nueva cuenta oauth_signup_title=Completar Cuenta Nueva oauth_signup_submit=Completar Cuenta @@ -1734,8 +1734,8 @@ pulls.nothing_to_compare=Estas ramas son iguales. No hay necesidad para crear un pulls.nothing_to_compare_and_allow_empty_pr=Estas ramas son iguales. Este PR estará vacío. pulls.has_pull_request=`Ya existe un pull request entre estas ramas: %[2]s#%[3]d` pulls.create=Crear Pull Request -pulls.title_desc=desea fusionar %[1]d commits de %[2]s en %[3]s -pulls.merged_title_desc=fusionados %[1]d commits de %[2]s en %[3]s %[4]s +pulls.title_desc_few=desea fusionar %[1]d commits de %[2]s en %[3]s +pulls.merged_title_desc_few=fusionados %[1]d commits de %[2]s en %[3]s %[4]s pulls.change_target_branch_at=`cambió la rama objetivo de %s a %s %s` pulls.tab_conversation=Conversación pulls.tab_commits=Commits diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index 64d29cbda7..598c1636dc 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -24,12 +24,12 @@ licenses=گواهینامه ها return_to_gitea=بازگشت به Forgejo username=نام کاربری -email=آدرس ایمیل +email=نشانی رایانامه password=رمز عبور access_token=ژتون دسترسی -re_type=تکرارگذواژه +re_type=تأیید گذرواژه captcha=کپچا -twofa=احراز هویت دوگانه +twofa=احراز هویت دو مرحله‌ای twofa_scratch=کد احراز هویت passcode=رمز عبور @@ -39,7 +39,7 @@ organization=سازمان mirror=قرینه new_repo=مخزن جدید new_migrate=انتقال جدید -new_mirror=قرینه ای جدید +new_mirror=آینه جدید new_fork=انشعاب مخزن جدید new_org=سازمان جدید new_project=پروژه جدید @@ -100,6 +100,25 @@ concept_user_organization=سازمان name=نام +logo = نشان +sign_in_with_provider = ورود با %s +enable_javascript = این وب‌گاه به جاوا اسکریپت نیاز دارد. +webauthn_press_button = لطفاً دکمۀ روی کلید امنیتی را فشار دهید… +webauthn_unsupported_browser = مرورگر شما در حال حاضر از WebAuthn پشتیبانی نمی‌کند. +webauthn_error_unknown = خطایی ناشناخته رخ داد. لطفاً دوباره سعی کنید. +webauthn_error_unable_to_process = کارساز نتوانست درخواست شما را پردازش کند. +webauthn_reload = تازه‌سازی +new_project_column = ستون جدید +retry = سعی دوباره +rerun = اجرای دوباره +rerun_all = اجرای دوباره تمام کارها +rss_feed = خوراک RSS +pin = سنجاق +unpin = حذف سنجاق +locked = قفل شده +copy_hash = رونوشت هش +unknown = نامشخص +copy_type_unsupported = این نوع از فایل نمی‌تواند رونوشت شود. [aria] @@ -299,7 +318,7 @@ twofa_scratch_used=شما کد ابتدا خود استفاده کرده اند. twofa_passcode_incorrect=گذرواژه شما اشتباه است. اگر شما دستگاه خود را در جای اشتباه قرار داده اید, از کد ابتدای خود برای ورود به سیستم استفاده کنید. twofa_scratch_token_incorrect=کد ابتدا شما نادرست است. login_userpass=ورود -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=ثبت نام یک حساب جدید oauth_signup_title=تکمیل حساب جدید oauth_signup_submit=تکمیل حساب کاربری @@ -1307,8 +1326,8 @@ pulls.nothing_to_compare=این شاخه‎ها یکی هستند. نیازی ب pulls.nothing_to_compare_and_allow_empty_pr=این شاخه ها برابر هستند. این PR خالی خواهد بود. pulls.has_pull_request=`A درخواست pull بین این شاخه ها از قبل وجود دارد: %[2]s#%[3]d` pulls.create=ایجاد تقاضای واکشی -pulls.title_desc=قصد ادغام %[1]d تغییر را از %[2]s به %[3]s دارد -pulls.merged_title_desc=%[1]d کامیت ادغام شده از %[2]s به %[3]s %[4]s +pulls.title_desc_few=قصد ادغام %[1]d تغییر را از %[2]s به %[3]s دارد +pulls.merged_title_desc_few=%[1]d کامیت ادغام شده از %[2]s به %[3]s %[4]s pulls.change_target_branch_at=`هدف شاخه از %s به %s %s تغییر کرد` pulls.tab_conversation=گفتگو pulls.tab_commits=کامیت‌ها diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index ad45e55ade..c6c64ad6ce 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -308,7 +308,7 @@ twofa_scratch_used=Olet käyttänyt kertakäyttökoodisi. Sinut on uudelleenohja twofa_passcode_incorrect=Salasanasi on väärä. Jos olet hukannut laitteesi, käytäthän kertakäyttökoodia sisäänkirjautumiseen. twofa_scratch_token_incorrect=Kertakäyttökoodisi on virheellinen. login_userpass=Kirjaudu sisään -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Rekisteröi uusi tili oauth_signup_title=Viimeistele tili oauth_signup_submit=Viimeistele tili @@ -989,8 +989,8 @@ pulls.nothing_to_compare=Nämä haarat vastaavat toisiaan. Ei ole tarvetta luoda pulls.nothing_to_compare_and_allow_empty_pr=Nämä haarat vastaavat toisiaan. Vetopyyntö tulee olemaan tyhjä. pulls.has_pull_request=`Vetopyyntö haarojen välillä on jo olemassa: %[2]s#%[3]d` pulls.create=Luo Pull-pyyntö -pulls.title_desc=haluaa yhdistää %[1]d committia lähteestä %[2]s kohteeseen %[3]s -pulls.merged_title_desc=yhdistetty %[1]d committia lähteestä %[2]s kohteeseen %[3]s %[4]s +pulls.title_desc_few=haluaa yhdistää %[1]d committia lähteestä %[2]s kohteeseen %[3]s +pulls.merged_title_desc_few=yhdistetty %[1]d committia lähteestä %[2]s kohteeseen %[3]s %[4]s pulls.tab_conversation=Keskustelu pulls.tab_commits=Commitit pulls.tab_files=Muuttuneet tiedostot diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini new file mode 100644 index 0000000000..afe3d1cb8e --- /dev/null +++ b/options/locale/locale_fil.ini @@ -0,0 +1,899 @@ + + + +[common] +page = Pahina +language = Wika +mirrors = Mga Mirror +forks = Mga Fork +activities = Mga Aktibidad +pull_requests = Mga Pull Request +issues = Mga Isyu +milestones = Mga Milestone +ok = OK +cancel = Kanselahin +retry = Subukan Muli +rerun = Patakbuhin Muli +save = I-save +add = Magdagdag +remove_all = Tanggalin lahat +remove_label_str = Tanggalin ang item "%s" +edit = I-edit +enabled = Naka-enable +copy = Kopyahin +copy_content = Kopyahin ang content +copy_branch = Kopyahin ang pangalan ng branch +copy_success = Kinopya! +copy_error = Nabigo ang pagkopya +write = Magsulat +register = Magrehistro +enable_javascript = Nangangailangan ng JavaScript ang website na ito. +twofa_scratch = Scratch Code ng Two-Factor +sources = Mga Source +collaborative = Pagtutulungan +copy_type_unsupported = Hindi makokopya ang itong uri ng file +error404 = Ang pahina na sinusubukan mong bisitahin ay alinman hindi umiiral o wala kang pahintulot para itignan. +version = Bersyon +powered_by = Pinapatakbo ng %s +explore = Mag-explore +help = Tulong +logo = Logo +sign_in = Mag-Sign In +sign_in_with_provider = Mag-sign in gamit ng %s +sign_in_or = o +sign_out = Mag-Sign Out +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 +create_new = Gumawa… +user_profile_and_more = Profile at Mga Setting… +signed_in_as = Naka-sign in bilang +toc = Talaan ng Mga Nilalaman +licenses = Mga Lisensya +return_to_gitea = Bumalik sa Forgejo +toggle_menu = I-toggle ang Menu +username = Username +email = Email address +password = Password +access_token = Token ng pag-access +re_type = Kumpirmahin ang password +captcha = CAPTCHA +twofa = Two-Factor Authentication +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. +webauthn_reload = I-reload +repository = Repository +organization = Organisasyon +mirror = Mirror +new_repo = Bagong Repository +new_migrate = Bagong Migration +new_mirror = Bagong Mirror +new_fork = Bagong Repository Fork +new_org = Bagong Organisasyon +new_project = Bagong Proyekto +new_project_column = Bagong Column +admin_panel = Pangangasiwa ng Site +account_settings = Mga Setting ng Account +settings = Mga Setting +your_profile = Profile +your_starred = Naka-star +your_settings = Mga Setting +all = Lahat +go_back = Bumalik +never = Hindi Kailanman +unknown = Hindi Alam +rss_feed = Feed ng RSS +pin = I-pin +unpin = I-unpin +artifacts = Mga Artifact +archived = Naka-archive +concept_system_global = Global +concept_user_individual = Indibidwal +concept_code_repository = Repository +concept_user_organization = Organisasyon +show_timestamps = Ipakita ang mga timestamp +show_log_seconds = Ipakita ang segundo +show_full_screen = Ipakita ng full screen +download_logs = I-download ang mga log +name = Pangalan +value = Value +filter = I-filter +filter.clear = I-clear ang Filter +filter.is_archived = Naka-archive +filter.not_archived = Hindi naka-archive +filter.is_fork = Naka-fork +filter.not_fork = Hindi naka-fork +filter.is_mirror = Naka-mirror +filter.not_mirror = Hindi naka-mirror +filter.is_template = Template +filter.not_template = Hindi Template +filter.public = Publiko +filter.private = Pribado +notifications = Mga Abiso +active_stopwatch = Aktibong Tagasubaybay ng Oras +locked = Naka-lock +preview = I-preview +confirm_delete_artifact = Sigurado ka bang gusto mong burahin ang artifact na "%s"? +rerun_all = Patakbuhin muli ang lahat ng mga job +add_all = Idagdag lahat +copy_hash = Kopyahin ang hash +error = Error +remove = Tanggalin +loading = Naglo-load… +confirm_delete_selected = Kumpirmahin na burahin ang lahat ng piniling item? +home = Panimula +dashboard = Dashboard + +[home] +search_repos = Maghanap ng Repository… +switch_dashboard_context = Palitan ang Dashboard Context +show_only_unarchived = Pinapakita lang ang hindi naka-archive +password_holder = Password +my_repos = Mga Repository +show_more_repos = Magpakita ng higit pang repository… +collaborative_repos = Mga Collaboritive Repository +my_orgs = Aking Mga Organisasyon +my_mirrors = Aking Mga Mirror +view_home = Itignan ang %s +filter = Iba pang Mga Filter +filter_by_team_repositories = I-filter sa mga team repository +feed_of = Feed ng "%s" +show_archived = Naka-archive +show_both_archived_unarchived = Pinapakita ang naka-archive at hindi naka-archive +show_only_archived = Pinapakita lang ang naka-archive +show_private = Pribado +show_both_private_public = Pinapakita ang publiko at pribado +show_only_private = Pinapakita lang ang pribado +show_only_public = Pinapakita lang ang publiko +issues.in_your_repos = Sa iyong mga repository +uname_holder = Username o Email address + +[explore] +organizations = Mga Organisasyon +search.fuzzy.tooltip = Isali ang mga resulta na tumutugma sa search term nang malapit +repos = Mga Repository +users = Mga User +search = Maghanap +go_to = Pumunta sa +code = Code +search.type.tooltip = Uri ng paghahanap +search.fuzzy = Fuzzy +search.match = Tugma +search.match.tooltip = Isali lamang ang mga resulta na tugma sa eksaktong search term +repo_no_results = Walang tugmang repository na nahanap. +user_no_results = Walang tugmang user na nahanap. +org_no_results = Walang tugmang mga organisasyon na nahanap. +code_search_results = Mga resulta ng paghahanap para sa "%s" +code_last_indexed_at = Huling na-index %s +relevant_repositories_tooltip = Mga repository na isang fork o walang topic, icon, at deskripsyon ay nakatago. +code_search_unavailable = Kasalukuyang hindi available ang code search. Mangyaring makipag-ugnayan sa site administrator. +code_no_results = Walang source code na tumutugma sa iyong search term na nahanap. +relevant_repositories = Ang mga kaugnay na repository ay pinapakita, ipakita ang hindi naka-filter na resulta. + +[aria] +footer.software = Tungkol sa Software +navbar = Bar ng Nabigasyon +footer = Footer +footer.links = Mga Link + +[error] +report_message = Kung naniniwala kang ito ay isang bug ng Forgejo, mangyaring maghanap ng mga isyu sa Codeberg magbukas ng bagong isyu kapag kailangan. +occurred = May nangyaring error +missing_csrf = Masamang Kahilingan: walang CSRF token +invalid_csrf = Masamang Kahilingan: hindi angkop na CSRF token +not_found = Hindi mahanap ang target. +network_error = Error sa network +server_internal = Internal Server Error + +[install] +reinstall_error = Sinusubukan mong mag-install sa umiiral na Forgejo database +install = Pag-install +title = Paunang pagsasaayos +docker_helper = Kapag tinatakbo mo ang Forgejo sa loob ng Docker, mangyaring basahin ang dokumentasyon bago baguhin ang anumang mga setting. +require_db_desc = Kinakailangan ng Forgejo ang MySQL, PostgreSQL, MSSQL, SQLite3 o TiDB (MySQL protocol). +db_title = Mga setting ng database +db_type = Uri ng database +host = Host +user = Username +password = Password +db_name = Pangalan ng database +db_schema = Schema +db_schema_helper = Iwanang walang laman para sa default ng database ("public"). +ssl_mode = SSL +path = Path +sqlite_helper = File path para sa SQLite3 database.
      Maglagay ng absolute path kapag tinatakbo mo ang Forgejo bilang serbisyo. +reinstall_confirm_check_3 = Kinukumprima mo na sigurado ka talaga na ang Forgejo na ito ay tumatakbo sa tamang app.ini na lokasyon at sigurado ka na kailangan mo mag-reinstall. Kinukumpirma mo na kilalanin ang mga panganib sa itaas. +err_empty_db_path = Hindi maaring walang laman ang path ng SQLite database. +no_admin_and_disable_registration = Hindi mo maaring i-disable ang user self-registration nang hindi gumawa ng isang administrator account. +err_empty_admin_password = Hindi maaring walang laman ang administrator password. +err_empty_admin_email = Hindi maaring walang laman ang administrator email. +err_admin_name_is_reserved = Hindi angkop ang Administrator Username, naka-reserve ang username +err_admin_name_is_invalid = Hindi angkop ang Administrator Username +general_title = Mga General Setting +app_name = Pangalan ng Instansya +app_name_helper = Maari mong ilagay ang pangalan ng iyong kompanya dito. +repo_path_helper = Ang mga remote Git repository ay mase-save sa directory na ito. +repo_path = Root path ng Repository +lfs_path = Root path ng Git LFS +run_user = User na tatakbuhin bilang +run_user_helper = Ang operating system username na ang Forgejo ay tatakbo bilang. Tandaan mo na ang user ay kailangang may access sa repository root path. +domain = Domain ng server +domain_helper = Domain o host para sa server na ito. +ssh_port = Port ng SSH Server +http_port = HTTP listen port +lfs_path_helper = Ang mga file na naka-track sa Git LFS ay ilalagay sa directory na ito. Iwanang walang laman para i-disable. +reinstall_confirm_message = Ang pag-install muli na may umiiral na Forgejo database ay maaring magdulot ng mga problema. Sa karamihan ng mga kaso, dapat mong gamitin ang iyong umiiral na "app.ini" para patakbuhin ang Forgejo. Kung alam mo ang ginagawa mo, kumpirmahin ang mga sumusunod: +reinstall_confirm_check_1 = Ang data na naka-encrypt sa pamamagitan ng SECRET_KEY sa app.ini ay maaring mawala: baka hindi maka-log in ang mga user gamit ng 2FA/OTP at ang mga mirror ay maaring hindi gumana mg maayos. Sa pamamagitan ng pag-check ng box na ito kinukumpirma mo na ang kasalukuyang app.ini file ay naglalaman ng tamang SECRET_KEY. +reinstall_confirm_check_2 = Ang mga repository at mga setting ay maaring kailangang i-resynchronize. Sa pamamagitan ng pag-check ng box na ito kinukumprima mo na ire-resynchronize mo ang mga hook para sa mga repository at authorized_keys ng mano-mano. Kinukumpirma mo na sisiguraduhin mo na tama ang mga setting ng repository at mirror. +err_admin_name_pattern_not_allowed = Hindi angkop ang administrator username, ang username ay tumutugma sa reserved pattern +ssh_port_helper = Numero ng port na gagamitin ng SSH server. Iwanang walang laman para i-disable ang SSH server. +server_service_title = Mga setting ng server at third-party na serbisyo +offline_mode = Paganahin ang local mode +http_port_helper = Port number na gagamitin ng Forgejo web server. +app_url = Base URL +app_url_helper = Base address para sa mga HTTP(S) clone URL at mga email notification. +log_root_path = Path ng log +log_root_path_helper = Ang mga log file ay ilalagay sa directory na ito. +optional_title = Mga opsyonal na setting +email_title = Mga setting ng email +smtp_addr = Host ng SMTP +smtp_port = Port ng SMTP +smtp_from = Magpadala ng email bilang +smtp_from_invalid = Hindi angkop ang address ng "Magpadala ang Email Bilang" +smtp_from_helper = Ang email address na gagamitin ng Forgejo. Maglagay ng plain email address o gamitin ang "Pangalan" na format. +mailer_user = Username ng SMTP +mailer_password = Password ng SMTP +register_confirm = Kailanganin ang kumpirmasyon sa email para magrehistro +mail_notify = Paganahin ang mga email notification +disable_gravatar = I-disable ang Gravatar +federated_avatar_lookup = I-enable ang mga naka-federate na avatar +federated_avatar_lookup_popup = I-enable ang naka-federate na paghahanap ng avatar gamit ng Libravatar. +disable_registration = I-disable ang pansariling pagrehistro +allow_only_external_registration_popup = Payagan lang ang pagrehistro sa pamamagitan ng mga external na serbisyo +openid_signin = I-enable ang OpenID sign-in +openid_signin_popup = I-enable ang pag-sign in ng user gamit ng OpenID. +openid_signup = I-enable ang OpenID na pansariling pagrehistro +openid_signup_popup = I-enable ang OpenID-based na pansariling pagrehistro ng user. +enable_captcha = I-enable ang CAPTCHA sa pagrehistro +enable_captcha_popup = Kailanganin ang CAPTCHA sa pansariling pagrehistro ng user. +require_sign_in_view_popup = Limitahan ang access ng pahina sa mga naka-sign in na user. Makikita lang ng mga bisita ang sign-in at pagrehistro na mga pahina. +admin_title = Mga setting ng administrator account +admin_name = Username ng administrator +admin_password = Password +confirm_password = Kumpirmahin ang password +admin_email = Email address +config_location_hint = Ang mga opsyon sa configuration ay mase-save sa: +install_btn_confirm = I-install ang Forgejo +test_git_failed = Hindi masubukan ang "git" command: %v +invalid_db_setting = Hindi angkop ang mga database setting: %v +invalid_db_table = Hindi angkop ang database table na "%s": %v +invalid_repo_path = Hindi angkop ang repository root path: %v +invalid_app_data_path = Hindi angkop ang app data path: %v +run_user_not_match = Ang "tumakbo bilang" na username ay hindi ang kasulukuyang username: %s -> %s +internal_token_failed = Nabigong maka-generate ng internal token: %v +secret_key_failed = Nabigong maka-generate ng secret key: %v +save_config_failed = Nabigong i-save ang configuration: %v +invalid_admin_setting = Hindi angkop ang setting ng administrator account: %v +invalid_log_root_path = Hindi angkop ang log path: %v +default_keep_email_private = Itago ang mga email address bilang default +default_keep_email_private_popup = Itago ang mga email address ng mga bagong user account bilang default. +default_allow_create_organization_popup = Payagan ang mga bagong user account ng gumawa ng mga organisasyon bilang default. +default_enable_timetracking = I-enable ang pagsubaybay ng oras bilang default +default_enable_timetracking_popup = I-enable ang pagsubaybay ng oras sa mga bagong repository bilang default. +allow_dots_in_usernames = Payagan ang mga user na gamitin ang mga tuldok sa kanilang username. Hindi inaapektuhan ang mga umiiral na account. +no_reply_address = Domain ng nakatagong email +no_reply_address_helper = Domain name para sa mga user na may nakatagong email address. Halimbawa, ang username na "kita" ay mala-log sa Git bilang "kita@noreply.example.org" kapag ang nakatagong email domain ay nakatakda sa "noreply.example.org". +password_algorithm = Algorithm ng password hash +invalid_password_algorithm = Hindi angkop na algorithm ng password hash +password_algorithm_helper = Itakda ang password hashing algorithm. Ang mga algorithm ay may magkakaibang mga kinakailangan at lakas. Ang algorithm ng Argon2 ay sa halip ay ligtas ngunit gumagamit ng maraming memory at maaaring hindi naaangkop para sa mga maliliit na sistema. +enable_update_checker = I-enable ang tagasuri ng update +env_config_keys = Configuration ng Environment +env_config_keys_prompt = Ang mga sumusunod na mga environment variable ay ia-apply rin sa iyong configuration file: +offline_mode_popup = I-disable ang lahat ng mga third-party na content delivery network at ibahagi ang lahat ng mga resources ng locally. +require_sign_in_view = Kailanganin ang pag-sign in para tignan ang nilalaman ng instansya +enable_update_checker_helper_forgejo = Pansamantalang susuriin ito para sa mga bagong bersyon ng Forgejo sa pamamagitan ng pagsuri sa isang tala ng TXT DNS sa release.forgejo.org. +sqlite3_not_available = Ang itong bersyon ng Forgejo ay hindi sinusuportahan ang SQLite3. Paki-download ang opisyal na bersyon ng binary sa %s (hindi ang "gobuild" na bersyon). +default_allow_create_organization = Payagan ang paggawa ng mga organisasyon bilang default +disable_registration_popup = I-disable ang pansariling pagrehistro ng user. Ang mga administrator lamang ang makakagawa ng mga bagong user account. +disable_gravatar_popup = I-disable ang Gravatar at mga third-party na avatar source. Ang isang default na avatar ay gagamitin maliban kung maga-upload ng avatar ang user. +admin_setting_desc = Ang paggawa ng administrator account ay opsyonal. Ang pinakaunang nakarehistro na user ay awtomatikong magiging administrator. + +[heatmap] +number_of_contributions_in_the_last_12_months = %s mga kontribusyon sa nakalipas na 12 buwan +no_contributions = Walang mga kontribusyon +less = Kaunti +more = Marami + +[editor] +buttons.bold.tooltip = Magdagdag ng bold na text +buttons.italic.tooltip = Magdagdag ng italic text +buttons.quote.tooltip = I-quote ang text +buttons.code.tooltip = Magdagdag ng code +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 +buttons.enable_monospace_font = I-enable ang monospace font +buttons.disable_monospace_font = I-disable ang monospace font +buttons.list.ordered.tooltip = Magdagdag ng nakanumerong listahan +buttons.ref.tooltip = Magsangguni ng isyu o pull request +buttons.switch_to_legacy.tooltip = Gamitin ang legacy editor sa halip +buttons.heading.tooltip = Magdagdag ng heading + +[filter] +string.asc = A - Z +string.desc = Z - A + +[startpage] +app_desc = Isang hindi masakit, at naka self-host na Git service +install = Madaling i-install +platform = Cross-platform +platform_desc = Tumatakbo kahit saan ang Forgejo na ang Go ay nakaka-compile para sa: Windows, macOS, Linux, ARM, atbp. Piliin ang isa na gusto mo! +lightweight = Hindi Mabigat +lightweight_desc = Mababa ang minimal requirements ng Forgejo at tatakbo sa isang murang Raspberry Pi. Tipirin ang enerhiya ng iyong machine! +license = Open Source +install_desc = Patakbuhin ang binary para sa iyong platform, i-ship gamit ang Docker, o kunin ito nang naka-package. +license_desc = Kunin ang Forgejo! Sumali ka sa pamamagitan ng pag-contribute para gawing mas mahusay ang proyekto. Wag kang mahiya para maging isang contributor! + +[auth] +create_new_account = Magrehistro ng Account +register_helper_msg = May account ka na? Mag-sign in ngayon! +social_register_helper_msg = May account ka na? I-link ngayon! +disable_register_prompt = Naka-disable ang pagrehistro. Mangyaring makipag-ugnayan sa site administrator. +disable_register_mail = Ang kumpirmasyon sa pamamagitan ng Email sa pagrehistro ay naka-disable. +remember_me = Tandaan ang device na ito +forgot_password_title = Nakalimutan ang Password +forgot_password = Nakalimutan ang password? +sign_up_now = Kailangan ng isang account? Magrehistro ngayon. +sign_up_successful = Matagumpay na nagawa ang account. Maligayang pagdating! +must_change_password = Baguhin ang iyong password +allow_password_change = Kailanganin ang user na palitan ang password (inirerekomenda) +reset_password_mail_sent_prompt = Ang isang bagong email pang-kumpirma ay ipinadala sa %s. Pakisuri ang iyong inbox sa loob ng %s para tapusin ang proseso ng pag-recover ng account. +active_your_account = Aktibahin Ang Iyong Account +account_activated = Naaktiba na ang account +prohibit_login = Ipinagbawalan ang Pag-Sign In +prohibit_login_desc = Pinagbawalan ang iyong account sa pag-sign in, mangyaring makipag-ugnayan sa site administrator. +resent_limit_prompt = Humiling ka na ng activation email kamakailan. Mangyaring maghintay ng 3 minuto at subukang muli. +change_unconfirmed_email_summary = Palitan ang email address kung saan ipapadala ang activation email. +change_unconfirmed_email = Kung nagbigay ka ng maling email address habang nagpaparehistro, pwede mong palitan sa ibaba, at ang isang kumpirmasyon ay ipapadala sa bagong address sa halip. +change_unconfirmed_email_error = Hindi mapalitan ang email address: %v +resend_mail = Pindutin dito para ipadala muli ang activation email +email_not_associate = Ang email address ay hindi nauugnay sa anumang account. +send_reset_mail = Magpadala ng Account Recovery Email +reset_password = Pag-recover ng Account +reset_password_helper = I-recover ang Account +reset_password_wrong_user = Naka-sign in ka bilang %s, pero ang account recovery link ay para kay %s +password_too_short = Ang haba ng password ay hindi maaaring mas mababa sa %d character. +non_local_account = Ang mga non-local na user ay hindi makakabago ng kanilang password sa pamamagitan ng Forgejo web interface. +verify = I-verify +scratch_code = Scratch code +use_scratch_code = Gumamit ng scratch code +twofa_passcode_incorrect = Mali ang iyong passcode. Kung nawala mo ang iyong device, gamitin ang iyong scratch code para mag-sign in. +twofa_scratch_token_incorrect = Mali ang iyong scratch code. +login_userpass = Mag-Sign In +login_openid = OpenID +oauth_signup_tab = Mag-rehistro ng Bagong Account +oauth_signup_title = Kumpletuhin ang Bagong Account +oauth_signup_submit = Kumpletuhin ang Account +oauth_signin_tab = I-link sa Umiiral na Account +oauth_signin_submit = I-link ang Account +oauth.signin.error.access_denied = Tinanggihan ang hiling ng pahintulutan. +oauth.signin.error.temporarily_unavailable = Nabigo ang awtorisasyon dahil pansamantalang hindi available ang authentication server. Mangyaring subukan muli sa ibang pagkakataon. +openid_connect_submit = Kumonekta +openid_connect_title = Kumonekta sa umiiral na account +openid_connect_desc = Ang piniling OpenID URI ay hindi alam. Iugnay iyan sa bagong account dito. +invalid_code = Ang iyong confirmation code ay hindi wasto o nag-expire na. +oauth_signin_title = Mag-sign In para Pahintulutan ang Naka-link na Account +invalid_code_forgot_password = Ang iyong confirmation code ay hindi wasto o nag-expire na. Mag-click dito para magsimula ng bagong session. +confirmation_mail_sent_prompt = Ang isang bagong email pang-kumpirma ay ipinadala sa %s. Pakisuri ang iyong inbox sa loob ng %s para tapusin ang proseso ng pagrehistro. Kung mali ang email, maari kang mag-log in, at humingi ng isa pang email pang-kumpirma na ipapadala sa ibang address. +invalid_password = Ang iyong password ay hindi tugma sa password na ginamit para gawin ang account. +twofa_scratch_used = Ginamit mo na ang scratch code. Na-redirect ka sa two-factor settings page para tanggalin ang device enrollment o mag-generate ng bagong scratch code. +manual_activation_only = Makipag-ugnayan sa site administrator para kumpletuhin ang pagrehistro. +oauth.signin.error = Nagkaroon ng error sa pagproseso ng iyong hiling sa pahintulutan. Kung magpapatuloy ang error, mangyaring makipag-ugnayan sa site administrator. +remember_me.compromised = Ang login token ay hindi na wasto na maaaring magpahiwatig ng isang nakompromisong account. Pakisuri ang iyong account para sa mga hindi pangkaraniwang aktibidad. +has_unconfirmed_mail = Kamusta %s, mayroon kang isang hindi kinumpirmang email address (%s). Kung hindi ka pa nakatanggap ng email na pang-kumpirma o kailangang muling magpadala ng bago, mangyaring i-click ang button sa ibaba. +openid_register_title = Gumawa ng bagong account +openid_register_desc = Ang piniling OpenID URI ay hindi alam. Iugnay iyan sa bagong account dito. +openid_signin_desc = Ilagay ang iyong OpenID URI. Halimbawa: kita.openid.example.org o https://openid.example.org/kita. +disable_forgot_password_mail = Naka-disable ang account recovery dahil walang nakatakda na email. Mangyaring makipag-ugnayan sa site administrator. +disable_forgot_password_mail_admin = Available lamang ang account recovery kung may nakatakda na email. I-set up ang email para i-enable ang account recovery. +email_domain_blacklisted = Hindi ka makakapagrehistro gamit ng iyong email address. +authorize_application = Pahintulutan ang Aplikasyon +authorize_redirect_notice = Mare-redirect ka sa %s kapag pinahintulutan mo ang aplikasyon na ito. +authorize_application_created_by = Ginawa ni %s ang aplikasyon na ito. +authorize_application_description = Kung payagan mo ang access, maa-access at mababago nito ang lahat ng iyong impormasyon sa account, kasama ang mga pribadong repo at organisasyon. +authorize_title = Pahintulutan ang "%s" na i-access ang iyong account? +authorization_failed = Nabigo ang awtorisasyon +authorization_failed_desc = Nabigo ang awtorisasyon dahil may na-detect kami ng hindi angkop na hiling. Mangyaring makipag-ugnayan sa maintainer ng app na sinusubukan mong pahintulutan. +sspi_auth_failed = Nabigo ang SSPI authentication +password_pwned = Ang pinili mong password ay nasa listahan ng mga ninakaw na password na kasalukuyang napakita sa mga publikong data breach. Mangyaring subukang muli gamit ng ibang password at isaalang-alang palitan din ang password sa ibang lugar. +password_pwned_err = Hindi makumpleto ang request sa HaveIBeenPwned +last_admin = Hindi mo matatanggal ang pinakahuling admin. Kailangan may hindi bababa sa isang admin. + +[mail] +reply = o direktang tumugon sa email na ito +view_it_on = Tignan sa %s +issue.in_tree_path = Sa %s: +release.new.subject = Ang %s sa %s ay inilabas +link_not_working_do_paste = Hindi ba gumagana ang link? Subukang kopyahin at i-paste sa URL bar ng iyong browser. +hi_user_x = Kamusta %s, +activate_account = Paki-activate ang iyong account +activate_account.text_1 = Kamusta %[1]s, salamat sa pagrehistro sa %[2]s! +activate_account.text_2 = Paki-click ang sumusunod na link para i-activate ang iyong account sa loob ng %s: +activate_email = I-verify ang iyong email address +admin.new_user.subject = Nag-sign up lang ngayon ang user na si %s +admin.new_user.user_info = Impormasyon ng User +admin.new_user.text = Mangyaring mag-click dito para ipamahala ang user na ito sa admin panel. +register_notify = Maligayang Pagdating sa Forgejo +register_notify.title = %[1]s, maligayang pagdating sa %[2]s +register_notify.text_1 = ito ang iyong registration confirmation email para sa %s! +register_notify.text_2 = Maari kang mag-sign in sa iyong account gamit ng iyong username: %s +reset_password = I-recover ang iyong account +reset_password.title = %s, nagkaroon kami ng hiling para i-recover ang iyong account +reset_password.text = Kung ikaw ito, paki-click ang sumusunod na link para i-recover ang iyong account sa loob ng %s: +register_success = Matagumpay ang pag-rehistro +issue_assigned.issue = Itinalaga ka ni @%[1]s sa isyu na %[2]s sa repository na %[3]s. +issue.x_mentioned_you = Binanggit ka ni %s: +issue.action.force_push = Na-force push ni %[1]s ang %[2]s mula %[3]s sa %[4]s. +issue.action.push_n = Nag-push si @%[1]s ng %[3]d (mga) commit sa %[2]s +issue.action.close = Sinara ni @%[1]s ang #%[2]d. +issue.action.reopen = Binuksan muli ni @%[1]s ang #%[2]d. +issue.action.merge = Minerge ni @%[1]s ang #%[2]d sa %[3]s. +issue.action.approve = Inaprubahan ni @%[1]s ang pull request na ito. +issue.action.review = Nagkomento si @%[1]s sa pull request na ito. +issue.action.review_dismissed = Binalewala ni @%[1]s ang huling review galing sa %[2]s para sa pull request na ito. +issue.action.new = Ginawa ni @%[1]s ang #%[2]d. +release.title = Pamagat: %s +release.note = Note: +release.downloads = Mga Download: +release.download.zip = Source Code (ZIP) +release.download.targz = Source Code (TAR.GZ) +repo.transfer.subject_to_you = Gusto ilipat ni %s ang repository na "%s" sa iyo +repo.transfer.to_you = ikaw +repo.transfer.body = Para tanggapin o tanggihan bisitahin ang %s o huwag na lang pansinin. +repo.collaborator.added.subject = Dinagdag ka ni %s sa %s bilang tagaambag +team_invite.subject = Inimbitahan ka ni %[1]s para sumali sa organisasyong %[2]s +team_invite.text_1 = Inimbitahan ka ni %[1]s para sumali sa koponang %[2]s sa organisasyong %[3]s. +team_invite.text_2 = Paki-click ang sumusunod na link para sumali sa koponan: +activate_email.text = Paki-click ang sumusunod na link para i-verify ang iyong email address sa loob ng %s: +repo.collaborator.added.text = Dinagdag ka bilang tagaambag sa repository: +activate_email.title = %s, paki-verify ang iyong email address +issue.action.reject = Humingi ng mga pagbabago si @%[1]s sa pull request na ito. +activate_account.title = %s, paki-activate ang iyong account +register_notify.text_3 = Kung ginawa ng ibang tao ang account na ito para sa iyo, kailangan mong itakda ang iyong password muna. +issue_assigned.pull = Itinalaga ka ni @%[1]s sa pull request na %[2]s sa repository na %[3]s. +issue.action.push_1 = Nag-push si @%[1]s ng %[3]d commit sa %[2]s +issue.action.ready_for_review = Minarkahan ni @%[1]s ang pull request na ito bilang handa para suriin. +release.new.text = Inilabas ni @%[1]s ang %[2]s sa %[3]s +repo.transfer.subject_to = Gusto ni %s na ilipat ang repository na "%s" sa %s +team_invite.text_3 = Tandaan: Ang imbitasyong ito ay inilaan para sa %[1]s. Kung hindi mo inaasahan ang imbitasyong ito, maaari mong balewalain ang email na ito. + +[modal] +yes = Oo +no = Hindi +confirm = Kumpirmahin +cancel = Kanselahin +modify = Baguhin + +[form] +UserName = Username +RepoName = Pangalan ng repository +Email = Email address +Password = Password +Retype = Kumpirmahin ang Password +SSHTitle = Pangalan ng SSH key +HttpsUrl = HTTPS URL +PayloadUrl = Payload URL +TeamName = Pangalan ng koponan +AuthName = Pangalan ng awtorisasyon +AdminEmail = Admin email +NewBranchName = Bagong pangalan ng branch +CommitMessage = Mensahe ng commit +CommitChoice = Pagpili ng commit +TreeName = Path ng file +Content = Nilalaman +SSPISeparatorReplacement = Separator +SSPIDefaultLanguage = Default na Wika +CommitSummary = Pangkalahatang-ideya ng commit +glob_pattern_error = ` hindi angkop ang glob pattern: %s` +require_error = ` hindi maaring walang laman.` +alpha_dash_error = ` dapat maglaman lamang ng alphanumeric, dash ("-") at underscore ("_") na mga character.` +alpha_dash_dot_error = ` dapat maglaman lamang ng alphanumeric, dash ("-"), underscore ("_") at tuldok (".") na mga character.` +git_ref_name_error = ` dapat na mahusay na nabuong pangalan ng Git reference` +size_error = ` dapat sa laking %s.` +min_size_error = ` dapat maglaman ng hindi bababa sa %s (mga) character` +max_size_error = ` dapat maglaman ng hindi lalagpas sa %s (mga) character` +email_error = ` ay hindi angkop na email address.` +invalid_group_team_map_error = ` hindi angkop ang mapping: %s` +unknown_error = Hindi kilalang error: +captcha_incorrect = Mali ang CAPTCHA code. +password_not_match = Hindi tumutugma ang mga password. +lang_select_error = Pumili ng wika sa listahan. +username_been_taken = Kinuha na ang username. +username_change_not_local_user = Ang mga hindi lokal na user ay hindi pinapayagang palitan ang kanilang username. +username_has_not_been_changed = Hindi pinalitan ang username +repo_name_been_taken = Ginamit na ang pangalan ng repository. +repository_force_private = Naka-enable ang Force Private: hindi maaaring gawing pampubliko ang mga pribadong repository. +repository_files_already_exist = Ang mga file ay umiiral na sa repository na ito. Makipag-ugnayan sa system administrator. +repository_files_already_exist.delete = Ang mga file ay umiiral na sa repository na ito. Kailangan mo silang burahin. +repository_files_already_exist.adopt_or_delete = Umiiral na ang mga file para sa repository na ito. Alinman pagtibayin sila o burahin sila. +username_error_no_dots = ` maaari lamang maglaman ng mga alphanumeric na character ("0-9","a-z","A-Z"), gitling ("-") at underscore ("_"). Hindi ito maaaring magsimula o magtatapos sa mga hindi alphanumeric na character, at ipinagbabawal din ang magkakasunod na non-alphanumeric na character.` +include_error = ` dapat maglaman ng substring na "%s".` +regex_pattern_error = ` hindi angkop ang regex pattern: %s.` +url_error = ` hindi angkop na URL ang "%s".` +username_error = ` maaari lamang maglaman ng mga alphanumeric na character ("0-9","a-z","A-Z"), gitling ("-"), underscore ("_") at tuldok ("."). Hindi ito maaaring magsimula o magtatapos sa mga hindi alphanumeric na character, at ipinagbabawal din ang magkakasunod na non-alphanumeric na character.` +repository_files_already_exist.adopt = Umiiral na ang mga file para sa repository na ito at maaari lamang Pagtibayin. +2fa_auth_required = Kinailangan ng remote visit ng two factors authentication. +org_name_been_taken = Kinuha na ang pangalan ng organisasyon. +team_name_been_taken = Kinuha na ang pangalan ng koponan. +team_no_units_error = Payagan ang access sa kahit isang seksyon ng repository. +email_been_used = Ginamit na ang email address. +email_invalid = Hindi angkop ang email address. +openid_been_used = KInuha na ang OpenID address na "%s". +username_password_incorrect = Mali ang username o password. +password_complexity = Hindi napasa ng password ang kinakailangan na kahirapan: +password_lowercase_one = Kahit isang lowercase na character +password_uppercase_one = Kahit isang uppercase na character +password_digit_one = Kahit isang numero +password_special_one = Kahit isang espesyal na character (bantas, mga bracket, mga quote, atbp.) +enterred_invalid_repo_name = Mali ang inalagay mong pangalan ng repository. +enterred_invalid_org_name = Mali ang inalagay mong pangalan ng organisasyon. +enterred_invalid_owner_name = Hindi angkop ang pangalan ng bagong owner. +enterred_invalid_password = Mali ang password na inilagay mo. +unset_password = Hindi nagtakda ng password ang login user. +unsupported_login_type = Hindi sinusuportahan ang uri ng pag-login para burahin ang account. +user_not_exist = Hindi umiiral ang user. +team_not_exist = Hindi umiiral ang koponan. +last_org_owner = Hindi mo maaring tanggalin ang pinakahuling user sa "mga may-ari" na koponan. Kailangan may kahit isang may-ari para sa organisasyon. +cannot_add_org_to_team = Hindi maaring madagdag ang isang organisasyon bilang miyembro ng koponan. +duplicate_invite_to_team = Inimbita na ang user bilang miyembro ng koponan. +organization_leave_success = Matagumpay kang umalis sa organisasyon na %s. +invalid_ssh_key = Hindi ma-verify ang iyong SSH key: %s +invalid_gpg_key = Hindi ma-verify ang GPG key: %s +invalid_ssh_principal = Hindi angkop ang principal: %s +must_use_public_key = Ang key na ibinigay mo ay isang pribadong key. Huwag mong i-upload ang iyong pribadong key kahit saan. Gamitin ang iyong pampublikong key sa halip. +unable_verify_ssh_key = Hindi ma-verify ang SSH key, tiyakin ulit para sa mga pagkakamali. +auth_failed = Nabigo ang autentikasyon: %v +still_own_repo = Ang iyong account ay nagmamay-ari ng isa o higit pang mga repository, burahin o ilipat sila muna. +still_has_org = Ang iyong account ay isang miyembro ng isa o higit pang organisasyon, alisin muna sila. +still_own_packages = Ang iyong account ay nagmamay-ari ng isa o higit pang package, burahin sila muna. +org_still_own_repo = Ang organisasyon na ito ay nagmamay-ari ng isa o higit pang mga repository, burahin o ilipat sila muna. +org_still_own_packages = Ang organisasyon na ito ay nagmamay-ari ng isa o higit pang mga package, burahin sila muna. +target_branch_not_exist = Hindi umiiral ang target branch. +admin_cannot_delete_self = Hindi mo maaring burahin ang sarili mo kapag isa kang tagapangasiwa. Paki-tanggal ang iyong pribilehiyong tagapangasiwa muna. + +[user] +joined_on = Sumali sa %s +repositories = Mga Repository +activity = Pampublikong Aktibidad +followers = Mga Follower +block_user = I-block ang User +change_avatar = Palitan ang iyong avatar… +block_user.detail = Pakiunawa na kung i-block mo ang user na ito, isasagawa ang iba pang mga aksyon. Gaya ng: +block_user.detail_1 = Ina-unfollow ka sa user na ito. +block_user.detail_2 = Ang user na ito ay hindi maaaring makipag-ugnayan sa iyong mga repository, ginawang isyu at komento. +block_user.detail_3 = Hindi ka maaaring idagdag ng user na ito bilang isang collaborator, at hindi mo rin sila maidaragdag bilang isang collaborator. +follow_blocked_user = Hindi mo mapa-follow ang user na ito dahil na-block mo ang user na ito o na-block ka ng user na ito. +starred = Mga Naka-star na Repository +watched = Mga Sinusubaybayan na Repository +code = Code +projects = Mga Proyekto +overview = Pangkalahatang Ideya +following = Pina-follow +follow = I-follow +unfollow = I-unfollow +block = I-block +unblock = I-unblock +user_bio = Byograpya +email_visibility.limited = Ang iyong email address ay makikita ng lahat ng mga naka-authenticate na user +email_visibility.private = Makikita mo lang at mga administrator ang iyong email address +show_on_map = Ipakita ang lugar na ito sa mapa +settings = Mga Setting ng User +form.name_pattern_not_allowed = Ang pattern na "%s" ay hindi pinapayagan sa username. +disabled_public_activity = Hindi pinagana ng user na ito ang publikong kakayahang pagpakita ng aktibidad. +form.name_reserved = Nakareserba ang username na "%s". +form.name_chars_not_allowed = Naglalaman ng mga hindi angkop na character ang username. + +[settings] +profile = Profile +account = Account +appearance = Hitsura +password = Password +security = Seguridad +avatar = Avatar +ssh_gpg_keys = Mga SSH / GPG Key +applications = Mga Aplikasyon +orgs = Pamahalaan ng mga organisasyon +repos = Mga Repository +delete = Burahin ang Account +twofa = Authentikasyong Two-Factor (TOTP) +account_link = Mga Naka-link na Account +uid = UID +webauthn = Authentikasyong Two-Factor (Mga Security Key) +blocked_users = Mga Naka-block na User +public_profile = Pampublikong Profile +location_placeholder = Ibahagi ang iyong tinatayang lokasyon sa iba +password_username_disabled = Ang mga di-lokal na gumagamit ay hindi pinapayagan na baguhin ang kanilang username. Mangyaring makipag-ugnayan sa iyong tagapangasiwa ng site para sa higit pang mga detalye. +full_name = Punong Pangalan +website = Website +location = Lokasyon +update_theme = I-update ang Theme +update_profile = I-update ang Profile +update_language = I-update ang Wika +update_language_not_found = Hindi available ang wikang "%s". +update_language_success = Pinalitan na ang wika. +update_profile_success = Pinalitan na ang iyong profile. +change_username = Pinalitan na ang iyong username. +change_username_redirect_prompt = Magre-redirect ang lumang username hanggat may kumuha niyan. +continue = Magpatuloy +cancel = Kanselahin +language = Wika +ui = Tema +hidden_comment_types = Mga nakatagong uri ng komento +hidden_comment_types.ref_tooltip = Mga komento kung saan sinangguni ang isyu na ito galing sa ibang isyu/commit/… +hidden_comment_types.issue_ref_tooltip = Mga komento kung saan pinalitan ng user ang branch/tag na nakaugnay sa isyu +comment_type_group_reference = Sangguni +comment_type_group_label = Label +comment_type_group_title = Pamagat +comment_type_group_branch = Branch +comment_type_group_time_tracking = Pagsubaybay ng Oras +comment_type_group_deadline = Deadline +comment_type_group_dependency = Dependency +comment_type_group_lock = Estado ng Lock +comment_type_group_review_request = Hiling sa Pagsuri +comment_type_group_pull_request_push = Mga nadagdag na commit +comment_type_group_project = Proyekto +saved_successfully = Matagumpay na na-save ang iyong mga setting. +privacy = Privacy +keep_activity_private = Itago ang Aktibidad mula sa pahina ng profile +lookup_avatar_by_mail = Hanapin Ang Avatar sa pamamagitan ng Email Address +federated_avatar_lookup = Naka-federate na Paghahanap ng Avatar +enable_custom_avatar = Gumamit ng custom na avatar +choose_new_avatar = Pumili ng bagong avatar +update_avatar = I-update ang avatar +delete_current_avatar = Burahin ang Kasalukuyang Avatar +uploaded_avatar_not_a_image = Ang na-upload na file ay hindi isang larawan. +comment_type_group_assignee = Mangangasiwa +social = Mga Social Account +biography_placeholder = Sabihin sa amin ng kaunti tungkol sa iyong sarili! (Maaari mong gamitin ang Markdown) +change_username_prompt = Tandaan: Ang pagpalit ng username ay papalitan din ang URL ng iyong account. +organization = Mga Organisasyon +profile_desc = Kontrolin kung paano ipinapakita ang iyong profile sa ibang mga gumagamit. Ang iyong pangunahing email address ay gagamitin para sa mga abiso, pagbawi ng password at mga Git operation na batay sa web. +hidden_comment_types_description = Ang mga uri ng komento na naka-check dito ay hindi ipapakita sa loob ng mga pahina ng isyu. Halimbawa ang pag-check ng "Label" ay tatanggalin lahat ng mga "Dinagdag/tinanggal ni ang
      de cette organisation. members.membership_visibility=Visibilité des membres: -members.public=Public +members.public=Visible members.public_helper=rendre caché members.private=Caché members.private_helper=rendre visible @@ -2856,7 +2886,7 @@ dashboard.update_migration_poster_id=Actualiser les ID des affiches de migration dashboard.git_gc_repos=Exécuter le ramasse-miette des dépôts dashboard.resync_all_sshkeys=Mettre à jour le fichier « ssh/authorized_keys » avec les clés SSH Forgejo. dashboard.resync_all_sshprincipals=Mettre à jour le fichier « .ssh/authorized_principals » avec les principaux de Forgejo SSH. -dashboard.resync_all_hooks=Re-synchroniser les déclencheurs Git pre-receive, update et post-receive de tous les dépôts. +dashboard.resync_all_hooks=Re-synchroniser les déclencheurs Git pre-receive, update et post-receive de tous les dépôts dashboard.reinit_missing_repos=Réinitialiser tous les dépôts Git manquants pour lesquels un enregistrement existe dashboard.sync_external_users=Synchroniser les données de l’utilisateur externe dashboard.cleanup_hook_task_table=Nettoyer la table hook_task @@ -2871,25 +2901,25 @@ 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.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_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.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=Allocation de l'autre Système obtenue -dashboard.next_gc_recycle=Traitement GC suivant -dashboard.last_gc_time=Depuis le dernier GC +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_time=Pause GC -dashboard.total_gc_pause=Pause GC -dashboard.last_gc_pause=Dernière Pause 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 actions de la base de données dashboard.delete_old_actions.started=Suppression de toutes les anciennes actions de la base de données démarrée. @@ -2962,7 +2992,7 @@ 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 emails des utilisateurs +emails.email_manage_panel=Gestion des courriels des utilisateurs emails.primary=Principale emails.activated=Activée emails.filter_sort.email=Courriel @@ -3013,7 +3043,7 @@ defaulthooks.desc=Les webhooks font automatiquement des requêtes POST HTTP à u defaulthooks.add_webhook=Ajouter un déclencheur web par défaut defaulthooks.update_webhook=Mettre à jour le déclencheur web par défaut -systemhooks=Webhooks système +systemhooks=Déclencheurs système systemhooks.desc=Les webhooks font automatiquement des requêtes POST HTTP à un serveur spécifié lorsque certains événements Forgejo se déclenchent. Ceux créé ici agiront sur tous les dépôts, ce qui peux impacter les performances du système. Pour plus d’information, consultez le guide des webhooks. systemhooks.add_webhook=Ajouter un rappel système systemhooks.update_webhook=Mettre à jour un rappel système @@ -3049,7 +3079,7 @@ auths.search_page_size=Taille de la page auths.filter=Filtre utilisateur auths.admin_filter=Filtre administrateur auths.restricted_filter=Filtre restrictif -auths.restricted_filter_helper=Laisser vide pour ne définir aucun utilisateur comme restreint. Utilisez un astérisque ('*') pour définir tous les utilisateurs qui ne correspondent pas au filtre Admin comme restreint. +auths.restricted_filter_helper=Laisser vide pour ne définir aucun utilisateur comme restreint. Utilisez un astérisque ("*") pour définir tous les utilisateurs qui ne correspondent pas au filtre Admin comme restreint. auths.verify_group_membership=Vérifier l’appartenance au groupe LDAP (laisser vide pour ignorer) auths.group_search_base=DN de recherche du groupe auths.group_attribute_list_users=Attribut de groupe contenant la liste des utilisateurs @@ -3062,7 +3092,7 @@ auths.smtp_auth=Type d'authentification SMTP auths.smtphost=Hôte SMTP auths.smtpport=Port SMTP auths.allowed_domains=Domaines autorisés -auths.allowed_domains_helper=Laisser ce champ vide autorise tous les domaines. Separez les domaines multiples avec une virgule (","). +auths.allowed_domains_helper=Laisser ce champ vide autorise tous les domaines. Séparez les domaines multiples avec une virgule (","). auths.skip_tls_verify=Ne pas vérifier TLS auths.force_smtps=Forcer SMTPS auths.force_smtps_helper=SMTPS est toujours utilisé sur le port 465. Définissez ceci pour forcer SMTPS sur d'autres ports. (STARTTLS sera utilisé sur d'autres ports si cela est supporté par l'hôte.) @@ -3145,16 +3175,16 @@ config.custom_conf=Chemin du fichier de configuration config.custom_file_root_path=Emplacement personnalisé du fichier racine config.domain=Domaine du serveur config.offline_mode=Mode hors-ligne -config.disable_router_log=Désactiver la Journalisation du Routeur +config.disable_router_log=Désactiver la journalisation du routeur config.run_user=Exécuter avec l'utilisateur -config.run_mode=Mode d'Éxécution +config.run_mode=Mode d’exécution config.git_version=Version de Git -config.app_data_path=Chemin App Data +config.app_data_path=Chemin des données d'application config.repo_root_path=Emplacement des Dépôts config.lfs_root_path=Répertoire racine LFS config.log_file_root_path=Chemin des fichiers logs -config.script_type=Type de Script -config.reverse_auth_user=Annuler l'Authentification de l'Utilisateur +config.script_type=Type de script +config.reverse_auth_user=Annuler l'authentification de l'utilisateur config.ssh_config=Configuration SSH config.ssh_enabled=Activé @@ -3170,7 +3200,7 @@ config.ssh_minimum_key_sizes=Tailles de clé minimales config.lfs_config=Configuration LFS config.lfs_enabled=Activé -config.lfs_content_path=Chemin de contenu LFS +config.lfs_content_path=Chemin du contenu LFS config.lfs_http_auth_expiry=Expiration de l'authentification HTTP LFS config.db_config=Configuration de la base de données @@ -3191,22 +3221,22 @@ config.enable_openid_signup=Activer l'inscription avec OpenID config.enable_openid_signin=Activer la connexion avec OpenID config.show_registration_button=Afficher le bouton d'enregistrement config.require_sign_in_view=Exiger la connexion pour afficher les pages -config.mail_notify=Activer les notifications par e-mail +config.mail_notify=Activer les notifications par courriel config.enable_captcha=Activer le CAPTCHA -config.active_code_lives=Limites de Code Actif +config.active_code_lives=Date d'expiration du code d'activation config.reset_password_code_lives=Durée d'expiration du code de récupération de compte -config.default_keep_email_private=Masquer les adresses e-mail par défaut +config.default_keep_email_private=Masquer les adresses courriel par défaut config.default_allow_create_organization=Autoriser la création d'organisations par défaut config.enable_timetracking=Activer le suivi du temps config.default_enable_timetracking=Activer le suivi de temps par défaut config.default_allow_only_contributors_to_track_time=Restreindre le suivi de temps aux contributeurs -config.no_reply_address=Domaine pour les e-mails cachés +config.no_reply_address=Domaine pour les courriels cachés config.default_visibility_organization=Visibilité par défaut des nouvelles organisations config.default_enable_dependencies=Activer les dépendances pour les tickets par défaut -config.webhook_config=Configuration Webhook +config.webhook_config=Configuration des déclencheurs config.queue_length=Longueur de la file d'attente -config.deliver_timeout=Expiration d'Envoi +config.deliver_timeout=Expiration d'envoi config.skip_tls_verify=Passer la vérification TLS config.mailer_config=Configuration du service SMTP @@ -3232,8 +3262,8 @@ config.oauth_config=Configuration OAuth config.oauth_enabled=Activé config.cache_config=Configuration du cache -config.cache_adapter=Adaptateur du Cache -config.cache_interval=Intervales du Cache +config.cache_adapter=Adaptateur du cache +config.cache_interval=Intervales du cache config.cache_conn=Liaison du Cache config.cache_item_ttl=Durée de vie des éléments dans le cache @@ -3241,27 +3271,27 @@ config.session_config=Configuration de session config.session_provider=Fournisseur de session config.provider_config=Configuration du fournisseur config.cookie_name=Nom du cookie -config.gc_interval_time=Intervals GC +config.gc_interval_time=Intervalles GC config.session_life_time=Durée des sessions config.https_only=HTTPS uniquement config.cookie_life_time=Expiration du cookie config.picture_config=Configuration de l'avatar -config.picture_service=Service d'Imagerie +config.picture_service=Service d'imagerie config.disable_gravatar=Désactiver Gravatar -config.enable_federated_avatar=Activer les avatars unifiés +config.enable_federated_avatar=Activer les avatars fédérés config.git_config=Configuration de Git -config.git_disable_diff_highlight=Désactiver la surbrillance syntaxique de Diff -config.git_max_diff_lines=Lignes de Diff Max (pour un seul fichier) -config.git_max_diff_line_characters=Nombre max de caractères de Diff (pour une seule ligne) -config.git_max_diff_files=Nombre max de fichiers de Diff (à afficher) +config.git_disable_diff_highlight=Désactiver la surbrillance syntaxique de diff +config.git_max_diff_lines=Lignes de diff Max (pour un seul fichier) +config.git_max_diff_line_characters=Nombre max de caractères de diff (pour une seule ligne) +config.git_max_diff_files=Nombre max de fichiers de diff (à afficher) config.git_gc_args=Arguments de GC config.git_migrate_timeout=Délai imparti pour une migration config.git_mirror_timeout=Délai imparti pour mettre à jour le miroir -config.git_clone_timeout=`Délai imparti pour l'opération "Clone"` -config.git_pull_timeout=`Délai imparti pour l'opération "Pull"` -config.git_gc_timeout=`Délai imparti pour l'opération "GC"` +config.git_clone_timeout=Délai imparti pour l'opération "clone" +config.git_pull_timeout=Délai imparti pour l'opération "Pull" +config.git_gc_timeout=Délai imparti pour l'opération "GC" config.log_config=Configuration du journal config.logger_name_fmt=Logger: %s @@ -3313,8 +3343,8 @@ 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=Informations -notices.view_detail_header=Voir les détails de l'information système +notices.system_notice_list=Notifications systèmes +notices.view_detail_header=Voir les détails de la notification notices.operations=Opérations notices.select_all=Tout Sélectionner notices.deselect_all=Tout désélectionner @@ -3422,11 +3452,11 @@ 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=Signature inconnue de Forgejo +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 cette livraison ! Cette livraison est SUSPECTE. -error.probable_bad_default_signature=AVERTISSEMENT ! Bien que la clé par défaut ait cet ID, elle ne vérifie pas cette livraison ! Cette livraison est SUSPECTE. +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é @@ -3552,7 +3582,7 @@ 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.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 @@ -3674,7 +3704,7 @@ runs.empty_commit_message=(message de révision vide) workflow.disable=Désactiver le flux de travail workflow.disable_success=Le flux de travail « %s » a bien été désactivé. workflow.enable=Activer le flux de travail -workflow.enable_success=Le flux de travail « %s » a bien été activé. +workflow.enable_success=Le workflow « %s » a bien été activé. 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. @@ -3697,6 +3727,7 @@ variables.update.success=La variable a bien été modifiée. runs.no_workflows.quick_start = Vous ne savez pas comment commencer avec Forgejo Action ? Consultez le guide de démarrage rapide. runs.no_workflows.documentation = Pour plus d’informations sur les Actions Forgejo, voir la documentation. variables.id_not_exist = La variable numéro %d n’existe pas. +runs.workflow = Workflow [projects] type-1.display_name=Projet personnel @@ -3719,4 +3750,6 @@ component_loading_info = Cela peut prendre du temps… component_failed_to_load = Une erreur inattendue s'est produite. contributors.what = contributions component_loading = Chargement %s... -component_loading_failed = Échec de chargement de %s \ No newline at end of file +component_loading_failed = Échec de chargement de %s +code_frequency.what = fŕequence de code +recent_commits.what = commits récents diff --git a/options/locale/locale_gl.ini b/options/locale/locale_gl.ini index 3ccdfc2bbc..7c77357e3b 100644 --- a/options/locale/locale_gl.ini +++ b/options/locale/locale_gl.ini @@ -131,7 +131,7 @@ footer.software = Sobre o Software footer.links = Ligazóns [heatmap] -no_contributions = Sen Achegas +contributions_zero = Sen Achegas less = Menos more = Máis number_of_contributions_in_the_last_12_months = %s de contribucións nos últimos 12 meses @@ -140,4 +140,4 @@ number_of_contributions_in_the_last_12_months = %s de contribucións nos último buttons.heading.tooltip = Engadir Título buttons.italic.tooltip = Engade texto en cursiva buttons.quote.tooltip = Texto de cita -buttons.bold.tooltip = Engadir texto en negriña \ No newline at end of file +buttons.bold.tooltip = Engadir texto en negriña diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 17f5310276..3764f6fa61 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -265,7 +265,7 @@ twofa_scratch_used=Ön már használta a kaparós kódját. Visszairányítottuk twofa_passcode_incorrect=A kód hibás. Ha nem találja az eszközét, akkor használja a kaparós kódját a bejelentkezéshez. twofa_scratch_token_incorrect=A kaparós kód nem megfelelő. login_userpass=Bejelentkezés -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Új fiók létrehozása oauth_signup_submit=Fiók befejezése oauth_signin_tab=Csatlakoztatás egy már meglévő fiókhoz @@ -933,8 +933,8 @@ pulls.filter_branch=Ágra szűrés pulls.no_results=Nincs találat. pulls.nothing_to_compare=Ezek az ágak egyenlőek. Nincs szükség egyesítési kérésre. pulls.create=Egyesítési kérés létrehozása -pulls.title_desc=egyesíteni szeretné %[1]d változás(oka)t a(z) %[2]s-ból %[3]s-ba -pulls.merged_title_desc=egyesítve %[1]d változás(ok) a %[2]s-ból %[3]s-ba %[4]s +pulls.title_desc_few=egyesíteni szeretné %[1]d változás(oka)t a(z) %[2]s-ból %[3]s-ba +pulls.merged_title_desc_few=egyesítve %[1]d változás(ok) a %[2]s-ból %[3]s-ba %[4]s pulls.tab_conversation=Beszélgetés pulls.tab_commits=Commit-ok pulls.tab_files=Módosított fájlok diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 5c5632474a..574063bcaa 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -186,7 +186,7 @@ twofa_scratch_used=Anda telah menggunakan kode coretan anda. Anda telah dialihka twofa_passcode_incorrect=Kata sandi Anda salah. Jika Anda salah tempatkan perangkat Anda, gunakan kode gosok Anda untuk masuk. twofa_scratch_token_incorrect=Kode coretan anda tidak tepat. login_userpass=Masuk -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Daftar Akun Baru oauth_signup_submit=Akun Lengkap oauth_signin_tab=Tautkan ke Akun yang Tersedia @@ -752,8 +752,8 @@ pulls.compare_changes=Permintaan Tarik Baru pulls.filter_branch=Penyaringan cabang pulls.no_results=Hasil tidak ditemukan. pulls.create=Buat Permintaan Tarik -pulls.title_desc=ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s -pulls.merged_title_desc=commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s +pulls.title_desc_few=ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s +pulls.merged_title_desc_few=commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s pulls.tab_conversation=Percakapan pulls.tab_commits=Melakukan pulls.reopen_to_merge=Tolong buka kembali permintaan tarik ini untuk melaksanakan penggabungan. diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index fb39bfbf3d..27b9a4b17c 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -272,7 +272,7 @@ scratch_code=Skrapkóði use_scratch_code=Nota skrapkóða twofa_scratch_token_incorrect=Skrapkóði þinn er rangur. login_userpass=Skrá Inn -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Skrá Nýjan Notanda oauth_signup_title=Klára Nýjum Notanda oauth_signup_submit=Klára Notanda @@ -889,7 +889,7 @@ pulls.new=Ný Sameiningarbeiðni pulls.view=Skoða Sameiningarbeiðni pulls.compare_changes=Ný Sameiningarbeiðni pulls.create=Skapa Sameiningarbeiðni -pulls.title_desc=vill sameina %[1]d framlög frá %[2]s í %[3]s +pulls.title_desc_few=vill sameina %[1]d framlög frá %[2]s í %[3]s pulls.tab_conversation=Umræða pulls.tab_commits=Framlög pulls.tab_files=Skráum Breytt diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 9a3979808a..2d41edf59d 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -51,14 +51,14 @@ webauthn_reload=Ricarica repository=Repository organization=Organizzazione mirror=Mirror -new_repo=Nuovo Repository -new_migrate=Nuova Migrazione -new_mirror=Nuovo Mirror -new_fork=Nuovo Fork +new_repo=Nuovo progetto +new_migrate=Nuova migrazione +new_mirror=Nuovo mirror +new_fork=Nuovo fork new_org=Nuova organizzazione new_project=Nuovo progetto manage_org=Gestisci le organizzazioni -admin_panel=Amministrazione Sito +admin_panel=Amministrazione sito account_settings=Impostazioni dell'account settings=Impostazioni your_profile=Profilo @@ -72,7 +72,7 @@ collaborative=Condivisi forks=Fork activities=Attivitá -pull_requests=Pull Request +pull_requests=Pull request issues=Problemi milestones=Milestones @@ -141,8 +141,20 @@ show_full_screen = Mostra a schermo intero download_logs = Scarica logs confirm_delete_selected = Confermare l'eliminazione di tutti gli elementi selezionati? sign_in_with_provider = Accedi con %s -new_project_column = Nuova Colonna +new_project_column = Nuova colonna toggle_menu = Mostra/Nascondi Menu +filter.not_fork = Non derivato +filter = Filtro +filter.clear = Azzera filtro +filter.is_archived = Archiviato +filter.not_archived = Non archiviato +filter.is_fork = Derivato +filter.is_mirror = Specchiato +filter.not_mirror = Non specchiato +filter.is_template = Modello base +filter.not_template = Non modello base +filter.public = Pubblico +filter.private = Privato [aria] footer.links = Collegamenti @@ -152,9 +164,12 @@ footer.software = A proposito del Software [heatmap] more = Più -no_contributions = Nessun contributo +contributions_zero = Nessun contributo less = Meno number_of_contributions_in_the_last_12_months = %s contributi negli ultimi 12 mesi +contributions_format = {contributions} il {day} {month} {year} +contributions_one = contribuzione +contributions_few = contribuzioni [editor] buttons.heading.tooltip = Aggiungi intestazione @@ -198,10 +213,10 @@ install_desc = Semplicemente la documentazione prima di cambiare qualsiasi impostazione. require_db_desc=Forgejo requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). -db_title=Impostazioni Database +db_title=Impostazioni database db_type=Tipo di database host=Host user=Nome utente @@ -225,84 +240,84 @@ err_admin_name_is_reserved=Nome utente Administrator non valido, nome utente ris err_admin_name_pattern_not_allowed=Nome utente dell'amministratore non valido. Il nome utente fornito corrisponde ad un pattern riservato err_admin_name_is_invalid=Il nome utente Administrator non è valido -general_title=Impostazioni Generali -app_name=Titolo del Sito +general_title=Impostazioni generali +app_name=Titolo dell'istanza app_name_helper=Qui puoi inserire il nome della tua società. -repo_path=Percorso Root del Repository +repo_path=Percorso radice del progetto repo_path_helper=Le Remote Git repositories saranno salvate in questa directory. -lfs_path=Percorso radice di Git LFS +lfs_path=Percorso radice di git LFS lfs_path_helper=I file trovati da Git LFS saranno salvati in questa directory. Lasciare vuoto per disattivare. -run_user=Esegui come Nome utente -domain=Dominio Server +run_user=Nome utente col quale eseguire +domain=Dominio server domain_helper=Dominio o indirizzo host per il server. -ssh_port=Porta Server SSH -ssh_port_helper=Numero di porta in ascolto sul server SSH. Lasciare vuoto per disattivare. -http_port=Porta in ascolto HTTP Forgejo -http_port_helper=Numero della porta sul quale i server web Forgejo ascolteranno. -app_url=URL di base di Forgejo +ssh_port=Porta server SSH +ssh_port_helper=Numero della porta che verrà usata dal server SSH, Lasciare vuoto per disattivare. +http_port=Porta in ascolto HTTP +http_port_helper=Numero della porta che sarà usata dal server web Forgejo. +app_url=URL di base app_url_helper=URL di base per gli HTTP(S) clone URLs e notifiche email. log_root_path=Percorso dei log log_root_path_helper=I file di log saranno scritti in questa directory. -optional_title=Impostazioni Facoltative -email_title=Impostazioni Email +optional_title=Impostazioni facoltative +email_title=Impostazioni e-mail smtp_addr=Host SMTP smtp_port=Porta SMTP -smtp_from=Invia Email come +smtp_from=Invia e-mail come smtp_from_helper=Indirizzo Email che Forgejo utilizzerà. Inserisci un indirizzo email o usa il formato "Name" . mailer_user=Nome utente SMTP mailer_password=Password SMTP -register_confirm=Richiedere la conferma Email per registrarsi -mail_notify=Attiva le notifiche Email -server_service_title=Impostazioni Server e Servizi di Terza Parte -offline_mode=Attiva la Modalità in Locale +register_confirm=Richiedere conferma e-mail per registrarsi +mail_notify=Attiva le notifiche e-mail +server_service_title=Impostazioni server e servizi di terze parti +offline_mode=Attiva modalità in locale offline_mode_popup=Disattiva le reti di distribuzione dei contenuti di terze parti e fornisci tutte le risorse localmente. disable_gravatar=Disattiva Gravatar disable_gravatar_popup=Disattiva Gravatar e le fonti di avatar di terze parti. Verrà usato un avatar predefinito almeno che un utente non carichi un avatar in locale. -federated_avatar_lookup=Attiva i Federated Avatar +federated_avatar_lookup=Attiva le immagini profilo federate federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar. -disable_registration=Disattiva Self-Registration +disable_registration=Disattiva auto-registrazione disable_registration_popup=Disattiva la user self-registration. Solo gli amministratori saranno in grado di creare account. allow_only_external_registration_popup=Attiva la registrazione solo tramite servizi esterni -openid_signin=Attiva l'accesso OpenID +openid_signin=Attiva l'accesso con OpenID openid_signin_popup=Attiva registrazione utente via OpenID. -openid_signup=Attiva OpenID Self-Registration +openid_signup=Attiva auto-registrazione con OpenID openid_signup_popup=Attiva OpenID-based user self-registration. enable_captcha=Abilita CAPTCHA per registrazione enable_captcha_popup=Richiedi convalida captcha per i nuovi utenti. -require_sign_in_view=Richiedi l'accesso per visualizzare le pagine +require_sign_in_view=Richiedi l'accesso per visualizzare il contenuto dell'istanza admin_setting_desc=Creare un account amministratore è opzionale. Il primo utente registrato sarà automaticamente un amministratore. -admin_title=Impostazioni Account Amministratore -admin_name=Nome utente dell'Amministratore +admin_title=Impostazioni profilo amministratorə +admin_name=Nome utente dell'amministratorə admin_password=Password -confirm_password=Conferma Password -admin_email=Indirizzo Email +confirm_password=Conferma password +admin_email=Indirizzo e-mail install_btn_confirm=Installare Forgejo test_git_failed=Non è stato possibile testare il comando "git": %v sqlite3_not_available=Questa versione di Forgejo non supporta SQLite3. Si prega di scaricare la versione binaria ufficiale da %s (non la versione "gobuild"). invalid_db_setting=Le impostazioni del database sono invalide: %v invalid_repo_path=Il percorso radice del Repository è invalido: %v invalid_app_data_path=Il percorso dati dell'app non è valido: %v -run_user_not_match=Il nome utente "Esegui come Nome Utente" non è il nome utente attuale: %s -> %s +run_user_not_match=Il nome utente "utente con cui eseguire" non è il nome utente attuale: %s -> %s internal_token_failed=Generazione del token interno non riuscita: %v secret_key_failed=Generazione della chiave segreta non riuscita: %v save_config_failed=Salvataggio della configurazione non riuscito: %v invalid_admin_setting=Le impostazioni dell'account amministratore sono invalide: %v invalid_log_root_path=Il percorso del log non è valido: %v -default_keep_email_private=Nascondi Indirizzo Email di Default +default_keep_email_private=Nascondi Indirizzo e-mail come impostazione predefinita default_keep_email_private_popup=Nasconi l'indirizzo email dei nuovi account utente di default. -default_allow_create_organization=Consenti la Creazione di Organizzazioni di Default +default_allow_create_organization=Consenti la creazione di organizzazioni come impostazione predefinita default_allow_create_organization_popup=Consenti ai nuovi account utente di creare organizzazioni di default. -default_enable_timetracking=Attiva il cronografo di Default +default_enable_timetracking=Attiva il cronografo come impostazione predefinita default_enable_timetracking_popup=Attiva il cronografo per le nuove repositories di default. -no_reply_address=Dominio email nascosto +no_reply_address=Dominio e-mail nascosto no_reply_address_helper=Nome di dominio per utenti con un indirizzo email nascosto. Ad esempio, il nome utente "joe" accederà a Git come "joe@noreply.example.org" se il dominio email nascosto è impostato a "noreply.example.org". -password_algorithm=Algoritmo Password Hash +password_algorithm=Algoritmo per hash delle password smtp_from_invalid = L'indirizzo "Invia Email come" non è valido -enable_update_checker_helper_forgejo = Verifica periodicamente nuove versioni di Forgejo controllando il record DNS TXT di release.forgejo.org. +enable_update_checker_helper_forgejo = Verificherà periodicamente nuove versioni di Forgejo controllando il record DNS TXT di release.forgejo.org. invalid_db_table = La tabella del database "%s" non è valida: %v invalid_password_algorithm = Algoritmo di hash della password non valido -enable_update_checker = Attiva il Controllo degli Aggiornamenti +enable_update_checker = Attiva il controllo degli aggiornamenti enable_update_checker_helper = Verifica periodicamente la presenza di nuove versioni tramite gitea.io. env_config_keys = Configurazione Ambiente env_config_keys_prompt = Le seguenti variabili di ambiente saranno anche applicate al tuo file di configurazione: @@ -310,6 +325,7 @@ run_user_helper = Il nome utente del sistema operativo con il quale Forgejo vien password_algorithm_helper = Imposta l'algoritmo di hashing della password. Gli algoritmi hanno requisiti e punti di forza diversi. L'algoritmo argon2 è relativamente sicuro ma usa un sacco di memoria e potrebbe non essere appropriato a piccoli sistemi. require_sign_in_view_popup = Limita l'accesso ad utenti autenticati. I visitatori vedranno solo le pagine di accesso e registrazione. allow_dots_in_usernames = Consenti l'uso del punto nel nome utente. Non impatta gli account già esistenti. +config_location_hint = Queste opzioni di configurazione saranno salvate in: [home] uname_holder=Nome utente o indirizzo Email @@ -318,7 +334,7 @@ switch_dashboard_context=Cambia Dashboard Context my_repos=Repositories show_more_repos=Mostra altre repositories… collaborative_repos=Repository Condivisi -my_orgs=Le mie Organizzazioni +my_orgs=Organizzazioni my_mirrors=I miei Mirror view_home=Vedi %s search_repos=Trova un repository… @@ -359,6 +375,10 @@ code_search_results = Risultati di ricerca per "%s" relevant_repositories_tooltip = Repository che sono fork o che non hanno un argomento, icona, né descrizione sono nascosti. relevant_repositories = Solo le repository pertinenti sono visibili, mostra risultati non filtrati. search.match.tooltip = Includi solo risultati che combaciano perfettamente con i termini di ricerca +stars_few = %d stelle +forks_one = %d fork +forks_few = %d fork +stars_one = %d stella [auth] create_new_account=Registra un account @@ -377,7 +397,7 @@ allow_password_change=Richiede all'utente di cambiare la password (scelta consig reset_password_mail_sent_prompt=Una email di conferma è stata inviata a %s. Per favore controlla la tua posta in arrivo nelle prossime %s per completare il processo di reset della password. active_your_account=Attiva il tuo Account account_activated=L'account è stato attivato -prohibit_login=Accesso proibito +prohibit_login=Accedere è proibito resent_limit_prompt=Hai già richiesto un'e-mail d'attivazione recentemente. Si prega di attenere 3 minuti e poi riprovare. has_unconfirmed_mail=Ciao %s, hai un indirizzo di posta elettronica non confermato (%s). Se non hai ricevuto una e-mail di conferma o vuoi riceverla nuovamente, fare clic sul pulsante qui sotto. resend_mail=Clicca qui per inviare nuovamente l'e-mail di attivazione @@ -395,7 +415,7 @@ twofa_scratch_used=Hai usato il tuo codice zero. Sei stato reindirizzato alla pa twofa_passcode_incorrect=Il tuo passcode non è corretto. Se hai smarrito il tuo dispositivo, utilizza il tuo scratch code per accedere. twofa_scratch_token_incorrect=I tuo codice scratch non è corretto. login_userpass=Accedi -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Creare nuovo account oauth_signup_title=Completa Nuovo Account oauth_signup_submit=Completa l'Account @@ -434,6 +454,8 @@ last_admin = Non puoi rimuovere l'ultimo amministratore. Deve esserci almeno un prohibit_login_desc = Al tuo account non è consentito effettuare il login, contatta l'amministratore del sito. openid_signin_desc = Inserisci il tuo URI OpenID. Per esempio: alice.openid.example.org o https://openid.example.org/alice. password_pwned = La password che hai scelto è in un elenco di password rubate precedentemente esposte a violazioni di dati pubblici. Riprova con una password diversa e valuta di modificare questa password anche altrove. +tab_signup = Registrati +tab_signin = Accedi [mail] view_it_on=Visualizza su %s @@ -486,16 +508,16 @@ release.downloads=Scaricamenti: release.download.zip=Codice Sorgente (Zip) release.download.targz=Codice Sorgente (Tar.Gz) -repo.transfer.subject_to=%s vorrebbe trasferire "%s" a %s -repo.transfer.subject_to_you=%s vorrebbe trasferire "%s" a te +repo.transfer.subject_to=%s vorrebbe trasferire il progetto "%s" presso %s +repo.transfer.subject_to_you=%s vorrebbe trasferire il progetto "%s" a te repo.transfer.to_you=tu repo.transfer.body=Per accettare o respingerla visita %s o semplicemente ignorarla. -repo.collaborator.added.subject=%s ti ha aggiunto a %s -repo.collaborator.added.text=Sei stato aggiunto come collaboratore del repository: +repo.collaborator.added.subject=%s ti ha aggiunto a %s come collaboratorə +repo.collaborator.added.text=Sei statə aggiuntə come collaboratorə al progetto: reply = o rispondi direttamente a questa email admin.new_user.subject = Il nuovo utente %s si è appena registrato -admin.new_user.user_info = Informazioni Utente +admin.new_user.user_info = Informazioni utente team_invite.text_2 = Fai click sul seguente link per far parte del team: team_invite.subject = %[1]s ti ha invitato a far parte dell'organizzazione %[2]s activate_email.title = %s, verifica il tuo indirizzo email @@ -516,7 +538,7 @@ UserName=Nome utente RepoName=Nome Repository Email=Indirizzo E-mail Password=Password -Retype=Conferma Password +Retype=Conferma password SSHTitle=Nome chiave SSH HttpsUrl=URL HTTPS PayloadUrl=URL Payload @@ -603,6 +625,8 @@ must_use_public_key = La chiave che hai fornito è una chiave privata. Non caric still_own_repo = Il tuo account è ancora proprietario di una o più repository, devi prima eliminarle o trasferirle. duplicate_invite_to_team = L'utente è già stato invitato ad essere un membro del team. still_has_org = Il tuo account è ancora membro di una o più organizzazioni, devi prima abbandonarle. +unsupported_login_type = Il tipo di accesso non è supportato per cancellare il profilo. +unset_password = L'utente non ha impostato la password. [user] @@ -610,8 +634,8 @@ change_avatar=Modifica il tuo avatar… repositories=Repository activity=Attività pubblica followers=Seguaci -starred=Repositories votate -watched=Repository Osservate +starred=Repository preferite +watched=Repository osservate projects=Progetti overview=Riepilogo following=Seguiti @@ -620,7 +644,7 @@ unfollow=Non seguire più user_bio=Biografia disabled_public_activity=L'utente ha disabilitato la vista pubblica dell'attività. joined_on = Membro dal %s -block_user = Blocca Utente +block_user = Blocca utente block_user.detail_1 = Questo utente non ti seguirà più. block_user.detail_2 = Questo utente non potrà interagire con le tue repository, con i problemi che hai creato o con i tuoi commenti. block_user.detail_3 = Questo utente non ti potrà aggiungere come un collaboratore, né potrai tu aggiungerlo come un collaboratore. @@ -630,7 +654,7 @@ unblock = Sblocca email_visibility.limited = Il tuo indirizzo email è visibile a tutti gli utenti autenticati email_visibility.private = Il tuo indirizzo email è visibile solo a te e agli amministratori show_on_map = Mostra questo posto su una mappa -settings = Impostazioni Utente +settings = Impostazioni utente form.name_reserved = Il nome utente "%s" è riservato. form.name_chars_not_allowed = Il nome utente "%s" contiene caratteri non validi. block_user.detail = Tieni presente che se blocchi questo utente, verranno eseguite altre azioni. Per esempio: @@ -654,16 +678,16 @@ delete=Elimina account twofa=Verifica in due passaggi account_link=Account collegati organization=Organizzazioni -webauthn=Chiavi Di Sicurezza +webauthn=Autenticazione a due passaggi (Chiavi di sicurezza) public_profile=Profilo pubblico password_username_disabled=Gli utenti non locali non hanno il permesso di cambiare il proprio nome utente. per maggiori dettagli si prega di contattare l'amministratore del sito. -full_name=Nome Completo +full_name=Nome completo website=Sito web location=Posizione -update_theme=Aggiorna tema -update_profile=Aggiorna Profilo -update_language=Aggiorna Lingua +update_theme=Cambia tema +update_profile=Aggiorna profilo +update_language=Cambia lingua update_language_success=La lingua è stata aggiornata. update_profile_success=Il tuo profilo è stato aggiornato. change_username=Il tuo nome utente è stato modificato. @@ -678,36 +702,36 @@ comment_type_group_milestone=Traguardo comment_type_group_assignee=Assegnatario comment_type_group_title=Titolo comment_type_group_branch=Ramo -comment_type_group_time_tracking=Cronografo +comment_type_group_time_tracking=Tracciamento del tempo comment_type_group_deadline=Scadenza comment_type_group_dependency=Dipendenza -comment_type_group_lock=Stato Blocco +comment_type_group_lock=Stato blocco comment_type_group_review_request=Richiesta di revisione comment_type_group_pull_request_push=Aggiunti commit comment_type_group_project=Progetto comment_type_group_issue_ref=Riferimento del problema saved_successfully=Le impostazioni sono state salvate correttamente. privacy=Privacy -keep_activity_private_popup=Rendi l'attività visibile solo da te e dagli amministratori +keep_activity_private_popup=La tua attività sarà visibile solo a te e agli amministratori dell'istanza -lookup_avatar_by_mail=Cerca Avatar per indirizzo Email -federated_avatar_lookup=Ricerca per avatar federata +lookup_avatar_by_mail=Cerca avatar per indirizzo email +federated_avatar_lookup=Ricerca federata dell'avatar enable_custom_avatar=Abilita avatar personalizzato choose_new_avatar=Scegli un nuovo avatar -update_avatar=Aggiorna Avatar -delete_current_avatar=Elimina Avatar attuale +update_avatar=Aggiorna avatar +delete_current_avatar=Elimina avatar attuale uploaded_avatar_not_a_image=Il file caricato non è un'immagine. update_avatar_success=Il tuo avatar è stato aggiornato. update_user_avatar_success=L'avatar dell'utente è stato aggiornato. -update_password=Aggiorna Password +update_password=Aggiorna password old_password=Password attuale new_password=Nuova password password_incorrect=La password attuale non è corretta. change_password_success=La password è stata aggiornata. Utilizza la nuova password la prossima volta che effettui il login. password_change_disabled=Gli utenti non locali non possono cambiare la loro password attraverso l'interfaccia web. -emails=Indirizzi e-mail +emails=Indirizzi email manage_emails=Gestisci indirizzi email manage_themes=Seleziona il tema predefinito manage_openid=Gestisci gli indirizzi OpenID @@ -740,16 +764,16 @@ openid_desc=OpenID consente di delegare l'autenticazione ad un provider esterno. manage_ssh_keys=Gestisci chiavi SSH manage_ssh_principals=Gestisci i Certificati SSH manage_gpg_keys=Gestisci chiavi GPG -add_key=Aggiungi Chiave +add_key=Aggiungi chiave ssh_desc=Queste chiavi SSH pubbliche sono associate con il tuo account. Le corrispondenti chiavi private consentono l'accesso completo alle tue repositories. Le chiavi SSH che sono state verificate possono essere usate per verificare commit Git firmati tramite SSH. principal_desc=Questi certificati SSH principali sono associati al tuo account e permettono l'accesso completo alle tue repository. -gpg_desc=Queste chiavi GPG pubbliche sono associate con il tuo account. Proteggi le tue chiavi private perché permettono di verificare i commits. +gpg_desc=Queste chiavi GPG pubbliche sono associate con il tuo account e sono usate per verificare i tuoi commit. Proteggi le tue chiavi private perché permettono di firmare i commit con la tue identità. ssh_helper= Hai bisogno di aiuto? Dai un'occhiata alla guida di GitHub percrea le tue chiavi SSH o risolvere problemi comuni che potresti trovare utilizzando SSH. gpg_helper=Hai bisogno di aiuto? Dai un'occhiata alla guida di GitHub riguardo il GPG. -add_new_key=Aggiungi Chiave SSH -add_new_gpg_key=Aggiungi Chiave GPG -key_content_ssh_placeholder=Inizia con 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', o 'sk-ssh-ed25519@openssh.com' -key_content_gpg_placeholder=Comincia con '-----BEGIN PGP PUBLIC KEY BLOCK-----' +add_new_key=Aggiungi chiave SSH +add_new_gpg_key=Aggiungi chiave GPG +key_content_ssh_placeholder=Inizia con "ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "sk-ecdsa-sha2-nistp256@openssh.com", o "sk-ssh-ed25519@openssh.com" +key_content_gpg_placeholder=Inizia con "-----BEGIN PGP PUBLIC KEY BLOCK-----" add_new_principal=Aggiungi Principal ssh_key_been_used=Questa chiave SSH è già stata aggiunta al server. ssh_key_name_used=Una chiave SSH con lo stesso nome esiste già sul tuo account. @@ -758,7 +782,7 @@ gpg_key_id_used=Esiste già una chiave GPG pubblica con lo stesso ID. gpg_no_key_email_found=Questa chiave GPG non corrisponde a nessun indirizzo email attivato associato al tuo account. Potrebbe essere ancora aggiunto se firmi il token fornito. gpg_key_matched_identities=Identità Corrispondenti: gpg_key_matched_identities_long=Le identità incorporate in questa chiave corrispondono ai seguenti indirizzi email attivati per questo utente. I commit che corrispondono a questi indirizzi email possono essere verificati con questa chiave. -gpg_key_verified=Chiave Verificata +gpg_key_verified=Chiave verificata gpg_key_verified_long=La chiave è stata verificata con un token e può essere utilizzata per verificare che i commit corrispondano a tutti gli indirizzi email attivati per questo utente oltre a qualsiasi identità corrispondente per questa chiave. gpg_key_verify=Verifica gpg_invalid_token_signature=La chiave GPG fornita, la firma e il token non corrispondono o il token è obsoleto. @@ -767,8 +791,8 @@ gpg_token=Token gpg_token_help=È possibile generare una firma utilizzando: gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=Firma GPG corazzata -key_signature_gpg_placeholder=Comincia con '-----BEGIN PGP SIGNATURE-----' -ssh_key_verified=Chiave Verificata +key_signature_gpg_placeholder=Inizia con "-----BEGIN PGP SIGNATURE-----" +ssh_key_verified=Chiave verificata ssh_key_verified_long=La chiave è stata verificata con un token e può essere utilizzata per verificare che i commit corrispondano a tutti gli indirizzi email attivati per questo utente. ssh_key_verify=Verifica ssh_invalid_token_signature=La chiave SSH fornita, la firma o il token non corrispondono o il token è obsoleto. @@ -776,10 +800,10 @@ ssh_token_required=Devi fornire una firma per il token sottostante ssh_token=Token ssh_token_help=È possibile generare una firma utilizzando: ssh_token_signature=Firma SSH corazzata -key_signature_ssh_placeholder=Comincia con '-----BEGIN SSH SIGNATURE-----' +key_signature_ssh_placeholder=Inizia con "-----BEGIN SSH SIGNATURE-----" subkeys=Sottochiavi key_id=ID chiave -key_name=Nome della Chiave +key_name=Nome della chiave key_content=Contenuto principal_content=Contenuto delete_key=Rimuovi @@ -807,11 +831,11 @@ ssh_externally_managed=Questa chiave SSH è gestita esternamente per questo uten manage_social=Gestisci gli Account Sociali Associati unbind=Rimuovi il collegamento -manage_access_token=Gestisci i tokens di accesso -generate_new_token=Genera Nuovo Token +manage_access_token=Gestisci i token di accesso +generate_new_token=Genera nuovo token tokens_desc=Questi tokens garantiscono l'accesso al tuo account utilizzando l'API di Forgejo. -token_name=Nome Token -generate_token=Genera Token +token_name=Nome token +generate_token=Genera token generate_token_success=Il nuovo token è stato generato. Copia ora in quanto non verrà mostrato nuovamente. generate_token_name_duplicate=%s è già stato utilizzato come nome dell'applicazione. Si prega di usarne uno nuovo. delete_token=Elimina @@ -849,7 +873,7 @@ twofa_desc=L'autenticazione a due fattori migliora la sicurezza del tuo account. twofa_is_enrolled=La verifica in due passaggi è attualmente abilitata sul tuo account. twofa_not_enrolled=La verifica in due passaggi al momento non è abilitata sul tuo account. twofa_disable=Disattiva la verifica in due passaggi -twofa_scratch_token_regenerate=Rigenera il token di sicurezza +twofa_scratch_token_regenerate=Rigenera la chiave di recupero monouso twofa_enroll=Iscriviti alla verifica in due passaggi twofa_disable_note=Se necessario, è possibile disattivare la verifica in due passaggi. twofa_disable_desc=Disattivare la verifica in due passaggi renderà il tuo account meno sicuro. Continuare? @@ -863,9 +887,9 @@ twofa_enrolled=Il tuo account è stato registrato alla verifica in due passaggi. twofa_failed_get_secret=Impossibile ottenere il segreto. webauthn_desc=Le chiavi di sicurezza sono dispositivi hardware contenenti chiavi crittografiche. Possono essere utilizzate per l'autenticazione a due fattori. Le chiavi di sicurezza devono supportare lo standard WebAuthenticator di WebAuthn. -webauthn_register_key=Aggiungi Chiave Di Sicurezza +webauthn_register_key=Aggiungi chiave di sicurezza webauthn_nickname=Soprannome -webauthn_delete_key=Rimuovi Chiave Di Sicurezza +webauthn_delete_key=Rimuovi chiave di sicurezza webauthn_delete_key_desc=Se si rimuove una chiave di sicurezza non è più possibile accedere con esso. Continuare? manage_account_links=Gestisci gli account collegati @@ -879,18 +903,18 @@ remove_account_link_success=L'account collegato è stato rimosso. orgs_none=Non sei membro di alcuna organizzazione. -delete_account=Elimina Account +delete_account=Elimina account delete_prompt=Questa operazione eliminerà permanentemente il tuo account utente. NON PUÒ essere annullata. delete_with_all_comments=Il tuo account è più recente di %s giorni. Per evitare commenti fantasma, tutti i commenti relativi a issue/PR verranno eliminati con esso. -confirm_delete_account=Conferma Eliminazione +confirm_delete_account=Conferma eliminazione delete_account_title=Elimina account utente delete_account_desc=Sei sicuro di voler rimuovere questo account utente permanentemente? -email_notifications.enable=Abilita Notifiche Email -email_notifications.onmention=Solo email su Menzione +email_notifications.enable=Abilita notifiche email +email_notifications.onmention=Solo email su menzione email_notifications.disable=Disabilita notifiche email -email_notifications.submit=Imposta Preferenze Email -email_notifications.andyourown=E Le Tue Notifiche +email_notifications.submit=Imposta preferenze email +email_notifications.andyourown=E le tue notifiche visibility=Visibilità utente visibility.public=Pubblico @@ -902,10 +926,10 @@ biography_placeholder = Parlaci un po' di te! (Puoi usare Markdown) location_placeholder = Condividi la tua posizione approssimativa con gli altri update_language_not_found = Il linguaggio "%s" non è disponibile. change_username_prompt = Nota: Il cambiamento del tuo nome utente cambierà anche l'URL del tuo account. -keep_activity_private = Nascondi Attività dalla pagina del profilo +keep_activity_private = Nascondi attività dalla pagina del profilo retype_new_password = Conferma nuova password can_not_add_email_activations_pending = C'è una verifica attualmente in corso, riprova tra qualche minuto se vuoi aggiungere una nuova email. -blocked_users = Utenti Bloccati +blocked_users = Utenti bloccati change_password = Modifica password uploaded_avatar_is_too_big = La dimensione del file caricato (%d KiB) supera il limite massimo (%d KiB). uid = UID @@ -914,11 +938,52 @@ permissions_public_only = Solo pubblico profile_desc = Controlla come il tuo profilo viene mostrato agli altri utenti. Il tuo indirizzo email principale sarà usato per inviarti notifiche, ripristino di password e per le operazioni Git effettuate da web. email_desc = Il tuo indirizzo email principale sarà usato per inviarti notifiche, ripristino di password e, se non è stato nascosto, per le operazioni Git effettuate da web. add_email_confirmation_sent = Una email di conferma è stata inviata a "%s". Verifica la posta in arrivo entro %s per confermare il tuo indirizzo email. +hidden_comment_types_description = I tipi di commenti spuntati qui non saranno mostrati nelle pagine delle segnalazioni. Per esempio, spuntare "Etichetta" rimuove tutti i commenti " ha aggiunto/rimosso ". +unbind_success = Il profilo social è stato rimosso con successo. +hidden_comment_types.ref_tooltip = Commenti in cui questa issue è stata citata da un altra issue/commit/… +verify_ssh_key_success = La chiave SSH "%s" è stata verificata. +valid_until_date = Valido fino a %s +ssh_signonly = SSH è attualmente disabilitato quindi queste chiavi sono usate solo per la firma di verifica dei commit. +social_desc = Questi account social possono essere usati per accedere al tuo account. Assicurati di riconoscerli tutti. +permission_write = Leggi e scrivi +access_token_desc = I permessi token selezionati limitano l'autorizzazione solo alle corrispondenti vie API. Leggi la documentazione per ulteriori informazioni. +create_oauth2_application_success = Hai correttamente creato una nuova applicazione OAuth2. +update_oauth2_application_success = Hai correttamente aggiornato l'applicazione OAuth2. +oauth2_redirect_uris = URI per la reindirizzazione. Usa una nuova riga per ogni URI. +authorized_oauth2_applications_description = Hai consentito l'accesso al tuo account personale Forgejo a queste applicazioni di terze parti. Per favore, revoca l'accesso alle applicazioni che non sono più in uso. +revoke_oauth2_grant_success = Accesso revocato correttamente. +twofa_recovery_tip = Se perdi il tuo dispositivo potrai usare una chiave di recupero monouso per riottenere l'accesso al tuo account. +twofa_scratch_token_regenerated = La tua chiave di recupero monouso è ora %s. Conservala in un posto sicuro dato che non verrà mostrata nuovamente. +webauthn_key_loss_warning = Se perdi la tua chiave di sicurezza perderai accesso al tuo account. +webauthn_alternative_tip = Potresti voler configurare un metodo di autenticazione aggiuntivo. +visibility.public_tooltip = Visibile a tutti +visibility.limited_tooltip = Visibile solo agli utenti autenticati +visibility.private_tooltip = Visibile solo a membri di organizzazioni di cui fai parte +blocked_since = Bloccato da %s +user_unblock_success = L'utente è stato bloccato correttamente. +user_block_success = L'utente è stato bloccato correttamente. +at_least_one_permission = Devi selezionare almeno un permesso per creare un token +oauth2_confidential_client = Client confidenziale. Seleziona per applicazioni che tengono il segreto confidenziale, come le applicazioni web. Non selezionare per applicazioni native incluse quelle desktop e mobile. +hidden_comment_types.issue_ref_tooltip = Commenti in cui l'utente ha cambiato la branch/tag associata con l'issue +add_key_success = La chiave SSH "%s" è stata aggiunta. +add_gpg_key_success = La chiave GPG "%s" è stata aggiunta. +add_principal_success = Il certificato principale SSH "%s" è stato aggiunto. +repo_and_org_access = Accesso al progetto e all'organizzazione +permissions_access_all = Tutto (publico, privato e limitato) +oauth2_client_secret_hint = Il segreto non verrà mostrato nuovamente dopo che lasci o ricarichi questa pagina. Assicurati di averlo salvato. +oauth2_application_remove_description = Rimuovere un applicazione OAuth2 gli impedirà di accedere ad account utenti autorizzati su questa istanza. Continuare? +oauth2_application_locked = Forgejo preregistra alcune applicazioni OAuth2 all'avvio, se abilitato nella configurazione. Per prevenire comportamenti imprevisti, queste non possono essere né modificate né rimosse. Fai riferimento alla documentazione di OAuth2 per ulteriori informazioni. +hooks.desc = Aggiungi richiami HTTP che saranno innescati per tutti i progetti che possiedi. +repos_none = Non possiedi alcun progetto. +blocked_users_none = Non ci sono utenti bloccati. +keep_email_private_popup = Questo nasconderà il tuo indirizzo email nel tuo profilo, nelle pull request e quando modifichi un file usando l'interfaccia web. I commit inoltrati non saranno modificati. Usa %s nei commit per associarli al tuo account. +verify_gpg_key_success = La chiave GPG "%s" è stata verificata. +added_on = Aggiunto su %s [repo] owner=Proprietario owner_helper=Alcune organizzazioni potrebbero non essere visualizzate nel menu a discesa a causa di un limite massimo al numero di repository. -repo_name=Nome Repository +repo_name=Nome progetto repo_name_helper=Un buon nome per un repository è costituito da parole chiave corte, facili da ricordare e uniche. repo_size=Dimensione repository template=Modello @@ -928,10 +993,10 @@ template_description=I modelli di repository consentono agli utenti di generare visibility=Visibilità visibility_description=Solo il proprietario o i membri dell'organizzazione se hanno diritti, saranno in grado di vederlo. visibility_helper_forced=L'amministratore del sito impone che le nuove repository siano private. -visibility_fork_helper=(Questa modifica avrà effetto su tutti i fork) +visibility_fork_helper=(Questa modifica influenzerà la visibilità di tutti i fork.) clone_helper=Hai bisogno di aiuto per la clonazione? Visita Help. -fork_repo=Forka Repository -fork_from=Forka da +fork_repo=Deriva progetto +fork_from=Deriva da already_forked=Hai già fatto il fork di %s fork_to_different_account=Fai Fork a un account diverso fork_visibility_helper=La visibilità di un repository forkato non può essere modificata. @@ -940,14 +1005,14 @@ clone_in_vsc=Clona nel codice VS download_zip=Scarica ZIP download_tar=Scarica TAR.GZ download_bundle=Scarica BUNDLE -generate_repo=Genera repository +generate_repo=Genera progetto generate_from=Genera da repo_desc=Descrizione repo_desc_helper=Inserisci una breve descrizione (opzionale) repo_lang=Lingua repo_gitignore_helper=Seleziona i template di .gitignore. repo_gitignore_helper_desc=Scegli di quali file non tenere traccia da un elenco di modelli per le lingue comuni. Gli artefatti tipici generati dagli strumenti di build di ogni lingua sono inclusi su .gitignore per impostazione predefinita. -issue_labels=Etichette Issue +issue_labels=Etichette segnalazioni issue_labels_helper=Seleziona un set di etichette per problemi. license=Licenza license_helper=Seleziona un file di licenza. @@ -955,18 +1020,18 @@ license_helper_desc=Una licenza governa ciò che gli altri possono e non possono readme=LEGGIMI readme_helper=Seleziona un template per il file LEGGIMI. readme_helper_desc=Qui puoi scrivere una descrizione completa del progetto. -auto_init=Inizializza Repository (Aggiungi .gitignore, Licenza e LEGGIMI) +auto_init=Inizializza progetto (Aggiunge .gitignore, Licenza e LEGGIMI) trust_model_helper=Seleziona il modello di fiducia per la verifica della firma. Le opzioni possibili sono: trust_model_helper_collaborator=Collaboratore: Fidati delle firme da parte dei collaboratori trust_model_helper_committer=Committer: Fidati delle Firme che corrispondono ai committenti trust_model_helper_collaborator_committer=Collaboratore+Committer: Fidati delle firme da parte dei collaboratori che corrispondono al committer trust_model_helper_default=Predefinito: utilizzare il modello di trust predefinito per questa installazione -create_repo=Crea Repository -default_branch=Ramo (Branch) predefinito +create_repo=Crea progetto +default_branch=Ramo predefinito default_branch_helper=Il ramo predefinito è il ramo base per le richieste di pull e i commit di codice. mirror_prune=Rimuovi mirror_prune_desc=Rimuovi i riferimenti di puntamento-remoto obsoleti -mirror_interval=Intervallo di specchio (le unità di tempo valide sono 'h', 'm', 's'). 0 per disabilitare la sincronizzazione periodica. (Intervallo minimo: %s) +mirror_interval=Intervallo di specchio (le unità di tempo valide sono "h", "m", "s"). 0 per disabilitare la sincronizzazione periodica. (Intervallo minimo: %s) mirror_interval_invalid=L'intervallo di aggiornamento dei mirror non è valido. mirror_sync_on_commit=Sincronizzazione quando i commit vengono premuti mirror_address=Clona da URL @@ -986,7 +1051,7 @@ reactions_more=e %d più unit_disabled=L'amministratore ha disabilitato questa sezione del repository. language_other=Altro adopt_search=Inserisci il nome utente per cercare i repository non adottati... (lascia vuoto per trovare tutti) -adopt_preexisting_label=Adotta File +adopt_preexisting_label=Adotta file adopt_preexisting=Adottare file preesistenti adopt_preexisting_content=Crea repository da %s adopt_preexisting_success=File adottati e repository creati da %s @@ -1009,13 +1074,13 @@ desc.internal=Interno desc.archived=Archiviato template.items=Elementi del modello -template.git_content=Contenuto di Git (Ramo predefinito) +template.git_content=Contenuto di git (Ramo predefinito) template.git_hooks=Git Hooks -template.git_hooks_tooltip=Al momento non sei in grado di modificare o rimuovere Git Hooks una volta aggiunto. Selezionare questa opzione solo se ti fidi del template repository. +template.git_hooks_tooltip=Al momento non sei in grado di modificare o rimuovere Git hook una volta aggiunti. Selezionare questa opzione solo se ti fidi del progetto modello. template.webhooks=Webhooks template.topics=Argomenti template.avatar=Avatar -template.issue_labels=Etichette Issue +template.issue_labels=Etichette segnalazioni template.one_item=Deve selezionare almeno un elemento del modello template.invalid=Devi selezionare un modello di repository @@ -1038,26 +1103,26 @@ migrate_items_wiki=Wiki migrate_items_milestones=Milestone migrate_items_labels=Etichette migrate_items_issues=Issues -migrate_items_pullrequests=Pull request -migrate_items_merge_requests=Richieste di Merge +migrate_items_pullrequests=Richieste di modifica +migrate_items_merge_requests=Richieste di fusione migrate_items_releases=Rilasci -migrate_repo=Migra Repository +migrate_repo=Migra progetto migrate.clone_address=Migra / Clona da URL -migrate.clone_address_desc=URL HTTP (S) o Git 'clone' di un repository esistente +migrate.clone_address_desc=URL HTTP(S) o Git "clone" di un progetto esistente migrate.github_token_desc=È possibile mettere uno o più token con virgola separati qui per rendere la migrazione più veloce a causa del limite di velocità API GitHub. ATTENZIONE: L'abuso di questa funzione potrebbe violare la politica del fornitore di servizi e portare al blocco dell'account. migrate.clone_local_path=o un percorso del server locale migrate.permission_denied=Non è consentito importare repository locali. migrate.permission_denied_blocked=Non è possibile importare da host non consentiti, si prega di chiedere all'amministratore di controllare ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS impostazioni. migrate.invalid_lfs_endpoint=Il punto d'accesso LFS non è valido. migrate.failed=Migrazione non riuscita: %v -migrate.migrate_items_options=Il Token di accesso è richiesto per migrare elementi aggiuntivi +migrate.migrate_items_options=Il token di accesso è richiesto per migrare elementi aggiuntivi migrated_from=Migrato da %[2]s migrated_from_fake=Migrato da %[1]s 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.github.description=Migrare i dati da github.com o da altre istanze di GitHub. +migrate.github.description=Migrare i dati da github.com o da server GitHub Enterprise. migrate.git.description=Migra un repository solo da qualsiasi servizio Git. migrate.gitlab.description=Migrare i dati da gitlab.com o da altre istanze di GitLab. migrate.gitea.description=Migrare i dati da gitea.com o altre istanze di Gitea/Forgejo. @@ -1065,13 +1130,13 @@ migrate.gogs.description=Migrare i dati da notabug.org o da altre istanze Gogs. migrate.onedev.description=Migrare i dati da code.onedev.io o da altre istanze OneDev. migrate.codebase.description=Migrare i dati da codebasehq.com. migrate.gitbucket.description=Migra i dati dalle istanze di GitBucket. -migrate.migrating_git=Migrazione dei Dati Git -migrate.migrating_topics=Migrazione dei topic -migrate.migrating_milestones=Migrazione dei traguardi +migrate.migrating_git=Migrazione dei dati Git +migrate.migrating_topics=Migrazione degli argomenti +migrate.migrating_milestones=Migrazione delle pietre miliari migrate.migrating_labels=Migrazione delle etichette -migrate.migrating_releases=Migrazione delle uscite -migrate.migrating_issues=Migrazione dei problemi -migrate.migrating_pulls=Migrazione delle Pull Request +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=forkato da @@ -1085,7 +1150,7 @@ watch=Segui unstar=Togli il voto star=Vota fork=Forka -download_archive=Scarica Repository +download_archive=Scarica progetto no_desc=Nessuna descrizione quick_guide=Guida rapida @@ -1105,7 +1170,7 @@ find_tag=Trova etichetta branches=Rami (Branch) tags=Tag issues=Problemi -pulls=Pull Requests +pulls=Richieste di modifica project_board=Progetti packages=Pacchetti labels=Etichette @@ -1133,15 +1198,15 @@ ambiguous_character=`%[1]c [U+%04[1]X] è confondibile con %[2]c [U+%04[2]X]` escape_control_characters=Fuga unescape_control_characters=Unescape -file_copy_permalink=Copia Permalink -view_git_blame=Visualizza Git Blame -video_not_supported_in_browser=Il tuo browser non supporta i tag "video" di HTML5. -audio_not_supported_in_browser=Il tuo browser non supporta il tag "video" di HTML5. +file_copy_permalink=Copia collegamento permanente +view_git_blame=Visualizza git incolpa +video_not_supported_in_browser=Il tuo browser non supporta le etichette "video" di HTML5. +audio_not_supported_in_browser=Il tuo browser non supporta le etichette "audio" di HTML5. stored_lfs=Memorizzati con Git LFS symbolic_link=Link Simbolico commit_graph=Grafico dei commit commit_graph.select=Seleziona rami -commit_graph.hide_pr_refs=Nascondi Pull Requests +commit_graph.hide_pr_refs=Nascondi richieste di modifica commit_graph.monochrome=Mono commit_graph.color=Colore blame=Blame @@ -1152,8 +1217,8 @@ lines=righe editor.add_file=Aggiungi file editor.new_file=Nuovo file -editor.upload_file=Carica File -editor.edit_file=Modifica File +editor.upload_file=Carica file +editor.edit_file=Modifica file editor.preview_changes=Anteprima modifiche editor.cannot_edit_lfs_files=I file LFS non possono essere modificati nell'interfaccia web. editor.cannot_edit_non_text_files=I file binari non possono essere modificati tramite interfaccia web. @@ -1169,7 +1234,7 @@ editor.or=o editor.cancel_lower=Annulla editor.commit_signed_changes=Conferma modifiche firmate editor.commit_changes=Apporta le modifiche -editor.add_tmpl=Aggiungi '' +editor.add_tmpl=Aggiungi "" editor.patch=Applica Patch editor.patching=Patching: editor.new_patch=Nuova Patch @@ -1187,8 +1252,8 @@ editor.commit_empty_file_header=Commit di un file vuoto editor.commit_empty_file_text=Il file che stai per effettuare il commit è vuoto. Procedere? editor.no_changes_to_show=Non ci sono cambiamenti da mostrare. editor.fail_to_update_file_summary=Messaggio d'errore: -editor.push_rejected_no_message=La modifica è stata rifiutata dal server senza un messaggio. Controlla Git Hooks. -editor.push_rejected=La modifica è stata rifiutata dal server. Controlla Git Hooks. +editor.push_rejected_no_message=La modifica è stata rifiutata dal server senza un messaggio. Controlla Git hooks. +editor.push_rejected=La modifica è stata rifiutata dal server. Controlla Git hooks. editor.push_rejected_summary=Messaggio Di Rifiuto Completo: editor.add_subdir=Aggiungi una directory… editor.no_commit_to_branch=Impossibile effettuare il commit direttamente sul branch perché: @@ -1211,8 +1276,8 @@ commits.newer=Più recente commits.signed_by=Firmato da commits.signed_by_untrusted_user=Firmato da un utente non attendibile commits.signed_by_untrusted_user_unmatched=Firmato da un utente non attendibile che non corrisponde al committer -commits.gpg_key_id=ID Chiave GPG -commits.ssh_key_fingerprint=Impronta Digitale Chiave SSH +commits.gpg_key_id=ID chiave GPG +commits.ssh_key_fingerprint=Impronta chiave SSH commit.revert=Ripristina commit.revert-header=Ripristina: %s @@ -1224,14 +1289,14 @@ commit.cherry-pick-content=Seleziona il ramo su cui scegliere: commitstatus.error=Errore commitstatus.pending=In sospeso -ext_issues=Accesso ai Problemi Esterni +ext_issues=Accesso a segnalazioni esterne ext_issues.desc=Collegamento al puntatore di una issue esterna. projects=Progetti projects.desc=Gestisci problemi e pull nelle schede di progetto. projects.description=Descrizione (opzionale) projects.description_placeholder=Descrizione -projects.create=Crea un progetto +projects.create=Crea progetto projects.title=Titolo projects.new=Nuovo progetto projects.new_subheader=Coordina, traccia e aggiorna il tuo lavoro in un unico posto, quindi i progetti rimangono trasparenti e in programma. @@ -1242,9 +1307,9 @@ projects.edit=Modifica progetto projects.edit_subheader=I progetti organizzano i problemi e monitorano i progressi. projects.modify=Aggiorna progetto projects.type.none=Nessuno -projects.type.basic_kanban=Basic Kanban +projects.type.basic_kanban=Kanban semplice projects.type.bug_triage=Bug Triage -projects.template.desc=Template di progetto +projects.template.desc=Modello projects.template.desc_helper=Seleziona un modello di progetto per iniziare projects.type.uncategorized=Senza categoria projects.column.edit_title=Nome @@ -1259,7 +1324,7 @@ issues.filter_milestones=Filtra traguardo issues.filter_projects=Filtra Progetti issues.filter_labels=Filtra etichetta issues.filter_reviewers=Filtra revisore -issues.new=Nuovo Problema +issues.new=Nuova segnalazione issues.new.title_empty=L'intestazione non può essere vuota issues.new.labels=Etichette issues.new.no_label=Nessuna etichetta @@ -1267,32 +1332,32 @@ issues.new.clear_labels=Pulisci le etichette issues.new.projects=Progetti issues.new.clear_projects=Cancella progetti issues.new.no_projects=Nessun progetto -issues.new.open_projects=Apri Progetti +issues.new.open_projects=Apri progetti issues.new.closed_projects=Progetti chiusi issues.new.no_items=Nessun elemento issues.new.milestone=Traguardo -issues.new.no_milestone=Nessuna milestone +issues.new.no_milestone=Nessuna pietra miliare issues.new.clear_milestone=Milestone pulita -issues.new.open_milestone=Apri Milestone -issues.new.closed_milestone=Milestone chiuse +issues.new.open_milestone=Apri pietra miliare +issues.new.closed_milestone=Pietre miliari chiuse issues.new.assignees=Assegnatari issues.new.clear_assignees=Cancella assegnatari -issues.new.no_assignees=Nessuna assegnatario +issues.new.no_assignees=Nessun assegnatario issues.new.no_reviewers=Nessun revisore issues.choose.get_started=Inizia issues.choose.open_external_link=Apri issues.choose.blank=Default issues.choose.blank_about=Crea un problema dal modello predefinito. -issues.no_ref=Nessun Branch/Tag specificato -issues.create=Crea Problema +issues.no_ref=Nessun ramo/etichetta specificato +issues.create=Crea segnalazione issues.new_label=Nuova etichetta issues.new_label_placeholder=Nome etichetta issues.new_label_desc_placeholder=Descrizione -issues.create_label=Crea Etichetta -issues.label_templates.title=Carica un set predefinito di etichette -issues.label_templates.info=Non esistono etichette. Crea una etichetta con 'Nuova Etichetta' o usa un set predefinito di etichette: -issues.label_templates.helper=Scegli un set di etichette -issues.label_templates.use=Usa Set Etichette +issues.create_label=Crea etichetta +issues.label_templates.title=Carica un'etichetta predefinita +issues.label_templates.info=Non esistono etichette. Crea una etichetta con "Nuova etichetta" o usa un'etichetta predefinita: +issues.label_templates.helper=Seleziona un'etichetta predefinita +issues.label_templates.use=Usa etichetta predefinita issues.add_label=ha aggiunto l'etichetta %s %s issues.add_labels=ha aggiunto le %s etichette %s issues.remove_label=rimosso l'etichetta %s %s @@ -1366,14 +1431,14 @@ issues.draft_title=Bozza issues.num_comments=%d commenti issues.commented_at=`%s ha commentato` issues.delete_comment_confirm=Sei sicuro/a di voler eliminare questo commento? -issues.context.copy_link=Copia link -issues.context.quote_reply=Quota risposta -issues.context.reference_issue=Fai riferimento in un nuovo problema +issues.context.copy_link=Copia collegamento +issues.context.quote_reply=Cita risposta +issues.context.reference_issue=Crea riferimento in una nuova segnalazione issues.context.edit=Modifica issues.context.delete=Elimina -issues.close_comment_issue=Commenta e Chiudi +issues.close_comment_issue=Commenta e chiudi issues.reopen_issue=Riapri -issues.reopen_comment_issue=Commenta e Riapri +issues.reopen_comment_issue=Commenta e riapri issues.create_comment=Commento issues.closed_at=`chiuso questo probleam %[2]s` issues.reopened_at=`riaperto questo problema %[2]s` @@ -1405,15 +1470,15 @@ issues.label_count=%d etichette issues.label_open_issues=%d problemi aperti issues.label_edit=Modifica issues.label_delete=Elimina -issues.label_modify=Modifica Etichetta -issues.label_deletion=Elimina Etichetta +issues.label_modify=Modifica etichetta +issues.label_deletion=Elimina etichetta issues.label_deletion_desc=Eliminare un'etichetta la rimuove da tutte le issue. Continuare? issues.label_deletion_success=L'etichetta è stata eliminata. issues.label.filter_sort.alphabetically=In ordine alfabetico issues.label.filter_sort.reverse_alphabetically=In ordine alfabetico inverso issues.label.filter_sort.by_size=Dimensione più piccola issues.label.filter_sort.reverse_by_size=Dimensione più grande -issues.num_participants=%d Partecipanti +issues.num_participants=%d partecipanti issues.attachment.open_tab=`Clicca per vedere "%s" in una nuova scheda` issues.attachment.download=`Clicca qui per scaricare "%s"` issues.subscribe=Iscriviti @@ -1428,7 +1493,7 @@ issues.lock_no_reason=ha bloccato e limitato la conversazione ai collaboratori % issues.unlock_comment=ha sbloccato questa conversazione %s issues.lock_confirm=Blocca issues.unlock_confirm=Sblocca -issues.lock.notice_1=- Altri utenti non possono aggiungere nuovi commenti a questo problema. +issues.lock.notice_1=- Altri utenti non possono aggiungere nuovi commenti a questa segnalazione. issues.lock.notice_2=- Tu e altri collaboratori con accesso a questo repository potete ancora lasciare commenti che altri possono vedere. issues.lock.notice_3=- Puoi sempre sbloccare questo problema in futuro. issues.unlock.notice_1=- Tutti potranno commentare nuovamente questo problema. @@ -1449,7 +1514,7 @@ issues.tracking_already_started=`Hai già avviato il monitoraggio del tempo su < issues.stop_tracking=Ferma timer issues.stop_tracking_history=`ha smesso di funzionare %s` issues.cancel_tracking=Scarta -issues.add_time=Aggiungi Tempo manualmente +issues.add_time=Aggiungi tempo manualmente issues.del_time=Elimina questo registro di tempo issues.add_time_short=Aggiungi tempo issues.add_time_cancel=Annulla @@ -1460,24 +1525,24 @@ issues.add_time_minutes=Minuti issues.add_time_sum_to_small=Non è stato inserito alcun tempo. issues.time_spent_total=Tempo totale trascorso issues.time_spent_from_all_authors=`Totale tempo trascorso: %s` -issues.due_date=Data di scadenza -issues.invalid_due_date_format=Il formato della data di scadenza deve essere 'yyyy-mm-dd'. -issues.error_modifying_due_date=Impossibile modificare la data di scadenza. -issues.error_removing_due_date=Impossibile rimuovere la data di scadenza. +issues.due_date=Scadenza +issues.invalid_due_date_format=Il formato della scadenza deve essere "aaaa-mm-dd". +issues.error_modifying_due_date=Impossibile modificare la scadenza. +issues.error_removing_due_date=Impossibile rimuovere la scadenza. issues.push_commit_1=aggiunto %d commit %s issues.push_commits_n=aggiunto %d commit %s issues.force_push_codes=`force-pushed %[1]s from %[2]s to %[4]s %[6]s` issues.force_push_compare=Confronta -issues.due_date_form=yyyy-mm-dd -issues.due_date_form_add=Aggiungi data di scadenza +issues.due_date_form=aaaa-mm-dd +issues.due_date_form_add=Aggiungi scadenza issues.due_date_form_edit=Modifica issues.due_date_form_remove=Rimuovi -issues.due_date_not_set=Nessuna data di scadenza impostata. -issues.due_date_added=la data di scadenza %s è stata aggiunta %s -issues.due_date_modified=ha modificato la data di scadenza da %[2]s a %[1]s %[3]s s -issues.due_date_remove=rimossa la data di scadenza %s %s +issues.due_date_not_set=Nessuna scadenza impostata. +issues.due_date_added=la scadenza %s è stata aggiunta %s +issues.due_date_modified=ha modificato la scadenza da %[2]s a %[1]s %[3]s s +issues.due_date_remove=rimossa la scadenza %s %s issues.due_date_overdue=Scaduto -issues.due_date_invalid=La data di scadenza non è valida o fuori intervallo. Si prega di utilizzare il formato 'aaaa-mm-dd'. +issues.due_date_invalid=La scadenza non è valida o fuori intervallo. Si prega di utilizzare il formato "aaaa-mm-dd". issues.dependency.title=Dipendenze issues.dependency.issue_no_dependencies=Nessuna dipendenza impostata. issues.dependency.pr_no_dependencies=Nessuna dipendenza impostata. @@ -1498,7 +1563,7 @@ issues.dependency.blocked_by_short=Dipende da issues.dependency.remove_header=Rimuovi Dipendenza issues.dependency.issue_remove_text=Questo rimuoverà la dipendenza da questa issue. Continuare? issues.dependency.pr_remove_text=Questo rimuoverà la dipendenza da questa pull request. Continuare? -issues.dependency.setting=Abilita le dipendenze per problemi e Pull Requests +issues.dependency.setting=Abilita le dipendenze per segnalazioni e richieste di modifica issues.dependency.add_error_same_issue=Non si può fare dipendere un problema da se stesso. issues.dependency.add_error_dep_issue_not_exist=Il problema dipendente non esiste. issues.dependency.add_error_dep_not_exist=La dipendenza non esiste. @@ -1509,15 +1574,15 @@ issues.review.self.approval=Non puoi approvare la tua pull request. issues.review.self.rejection=Non puoi richiedere modifiche sulla tua pull request. issues.review.approve=hanno approvato queste modifiche %s issues.review.comment=revisionato %s -issues.review.dismissed=recensione %s di %s respinta +issues.review.dismissed=revisione %s di %s respinta issues.review.dismissed_label=Respinta issues.review.left_comment=lascia un commento issues.review.content.empty=Devi lasciare un commento che indichi la modifica richiesta. -issues.review.reject=richieste modifiche %s +issues.review.reject=ha richiesto modifiche %s issues.review.wait=è stato richiesto per la revisione %s -issues.review.add_review_request=recensione richiesta da %s %s +issues.review.add_review_request=revisione richiesta da %s %s issues.review.remove_review_request=ha rimosso la richiesta di revisione per %s %s -issues.review.remove_review_request_self=ha rifiutato di rivedere %s +issues.review.remove_review_request_self=ha rifiutato di revisionare %s issues.review.pending=In sospeso issues.review.review=Revisiona issues.review.reviewers=Revisori @@ -1543,9 +1608,9 @@ compare.compare_base=base compare.compare_head=confronta pulls.desc=Attiva pull request e revisioni di codice. -pulls.new=Nuova Pull Request -pulls.view=Visualizza Pull Request -pulls.compare_changes=Nuova Pull Request +pulls.new=Nuova richiesta di modifica +pulls.view=Visualizza richiesta di modifica +pulls.compare_changes=Nuova richiesta di modifica pulls.allow_edits_from_maintainers=Consenti modifiche dai manutentori pulls.allow_edits_from_maintainers_desc=Gli utenti con accesso in scrittura al ramo base possono anche inviare a questo ramo pulls.allow_edits_from_maintainers_err=Aggiornamento non riuscito @@ -1562,9 +1627,9 @@ pulls.no_results=Nessun risultato trovato. pulls.nothing_to_compare=Questi rami sono uguali. Non c'è alcuna necessità di creare una pull request. pulls.nothing_to_compare_and_allow_empty_pr=Questi rami sono uguali. Questa PR sarà vuota. pulls.has_pull_request=`Una pull request tra questi rami esiste già: %[2]s#%[3]d` -pulls.create=Crea Pull Request -pulls.title_desc=vorrebbe unire %[1]d commit da %[2]s a %[3]s -pulls.merged_title_desc=ha unito %[1]d commit da %[2]s a %[3]s %[4]s +pulls.create=Crea richiesta di modifica +pulls.title_desc_few=vorrebbe unire %[1]d commit da %[2]s a %[3]s +pulls.merged_title_desc_few=ha unito %[1]d commit da %[2]s a %[3]s %[4]s pulls.change_target_branch_at=`cambiato il branch di destinazione da %s a %s %s` pulls.tab_conversation=Conversazione pulls.tab_commits=Commit @@ -1581,8 +1646,8 @@ pulls.add_prefix=Aggiungi prefisso %s pulls.remove_prefix=Rimuovi il prefisso %s pulls.data_broken=Questa pull request è rovinata a causa di informazioni mancanti del fork. pulls.files_conflicted=Questa pull request ha modifiche in conflitto con il branch di destinazione. -pulls.is_checking=Verifica dei conflitti di merge in corso. Riprova tra qualche istante. -pulls.is_ancestor=Questo ramo è già incluso nel ramo di destinazione. Non c'è nulla da unire. +pulls.is_checking=Verifica dei conflitti di fusione in corso. Riprova tra qualche istante. +pulls.is_ancestor=Questo ramo è già incluso nel ramo di destinazione. Non c'è nulla da fondere. pulls.is_empty=Le modifiche di questo ramo sono già nel ramo di destinazione. Questo sarà un commit vuoto. pulls.required_status_check_failed=Alcuni controlli richiesti non hanno avuto successo. pulls.required_status_check_missing=Mancano alcuni controlli richiesti. @@ -1591,14 +1656,14 @@ pulls.can_auto_merge_desc=La pull request può essere unita automaticamente. pulls.cannot_auto_merge_desc=Questa pull request non può essere unita automaticamente a causa di conflitti. pulls.cannot_auto_merge_helper=Unire manualmente per risolvere i conflitti. pulls.num_conflicting_files_1=%d file in conflitto -pulls.num_conflicting_files_n=%d files in conflitto +pulls.num_conflicting_files_n=%d file in conflitto pulls.approve_count_1=%d approvazione pulls.approve_count_n=%d approvazioni pulls.reject_count_1=%d richiesta di cambiamento pulls.reject_count_n=%d richieste di cambiamento pulls.waiting_count_1=%d in attesa di revisione pulls.waiting_count_n=%d in attesa di revisione -pulls.wrong_commit_id=l'id del commit deve essere un id del commit nel branch di destinazione +pulls.wrong_commit_id=l'id del commit deve essere un id del commit nel ramo di destinazione pulls.no_merge_desc=Questa pull request non può essere unita perché tutte le opzioni di merge del repository sono disattivate. pulls.no_merge_helper=Attiva le opzioni di merge nelle impostazioni del repository o unisci la pull request manualmente. @@ -1621,9 +1686,9 @@ pulls.rebase_conflict_summary=Messaggio d'Errore pulls.unrelated_histories=Unione fallita: gli Head del ramo da unire e la base non condividono una storia cronologica in comune. Suggerimento: prova una strategia diversa pulls.merge_out_of_date=Unione fallita: Durante la generazione del merge, la base è stata aggiornata. Suggerimento: Riprova. pulls.head_out_of_date=Unione non riuscita: durante la generazione della fusione, la testa è stata aggiornata. Suggerimento: Riprova. -pulls.push_rejected=Unisci non riuscito: il push è stato rifiutato. Rivedi gli Hooks Git per questo repository. +pulls.push_rejected=Immissione respinta. Rivedi gli hooks Git per questo progetto. pulls.push_rejected_summary=Messaggio Di Rifiuto Completo -pulls.push_rejected_no_message=Unione non riuscita: il push è stato rifiutato ma non c'è stato un messaggio remoto.
      Controlla gli Hooks di Git per questo repository +pulls.push_rejected_no_message=Immissione respinta: nessun messaggio remoto. Controlla gli hooks di Git per questo progetto pulls.open_unmerged_pull_exists=`Non è possibile riaprire questa pull request perché ne esiste un'altra (#%d) con proprietà identiche.` pulls.status_checking=Alcuni controlli sono in sospeso pulls.status_checks_success=Tutti i controlli sono stati effettuati con successo @@ -1657,19 +1722,19 @@ pulls.delete.text=Vuoi davvero eliminare questo problema? (Questo rimuoverà per -milestones.new=Nuova Milestone +milestones.new=Nuova pietra miliare milestones.closed=Chiuso %s milestones.no_due_date=Nessuna data di scadenza milestones.open=Apri milestones.close=Chiudi milestones.completeness=%d%% Completato -milestones.create=Crea Milestone +milestones.create=Crea pietra miliare milestones.title=Titolo milestones.desc=Descrizione -milestones.due_date=Data di scadenza (opzionale) +milestones.due_date=Scadenza (opzionale) milestones.clear=Pulisci -milestones.invalid_due_date_format=Il formato della data di scadenza deve essere 'yyyy-mm-dd'. -milestones.edit=Modifica Milestone +milestones.invalid_due_date_format=Il formato della scadenza deve essere 'aaaa-mm-dd'. +milestones.edit=Modifica pietra miliare milestones.edit_subheader=Le pietre miliari organizzano le issue e tengono conto del progresso. milestones.cancel=Annulla milestones.modify=Aggiorna pietra miliare @@ -1705,7 +1770,7 @@ wiki.delete_page_button=Cancella Pagina wiki.page_already_exists=Esiste già una pagina Wiki con questo stesso nome. wiki.pages=Pagine wiki.last_updated=Ultimo aggiornamento: %s -wiki.page_name_desc=Inserisci un nome per questa pagina Wiki. Alcuni nomi speciali sono: 'Home', '_Sidebar' e '_Footer'. +wiki.page_name_desc=Inserisci un nome per questa pagina wiki. Alcuni nomi speciali sono: "Home", "_Sidebar" e "_Footer". activity=Attività activity.period.filter_label=Periodo: @@ -1717,38 +1782,38 @@ activity.period.quarterly=3 mesi activity.period.semiyearly=6 mesi activity.period.yearly=1 anno activity.overview=Riepilogo -activity.active_prs_count_1=%d Pull Request attiva -activity.active_prs_count_n=%d Pull Request attive -activity.merged_prs_count_1=Pull Request Unita -activity.merged_prs_count_n=Pull request unite -activity.opened_prs_count_1=Pull Request proposta -activity.opened_prs_count_n=Pull Request proposte +activity.active_prs_count_1=%d richiesta di modifica attiva +activity.active_prs_count_n=%d richieste di modifiche attive +activity.merged_prs_count_1=Richiesta di modifica fusa +activity.merged_prs_count_n=Richieste di modifica fuse +activity.opened_prs_count_1=Richiesta di modifica proposta +activity.opened_prs_count_n=Richieste di modifica proposte activity.title.user_1=%d utente activity.title.user_n=%d utenti -activity.title.prs_1=%d Pull request -activity.title.prs_n=%d Pull request +activity.title.prs_1=%d richiesta di modifica +activity.title.prs_n=%d richieste di modifica activity.title.prs_merged_by=%s unita da %s activity.title.prs_opened_by=%s proposta da %s activity.merged_prs_label=Unite activity.opened_prs_label=Proposta -activity.active_issues_count_1=%d Issue attiva -activity.active_issues_count_n=%d Issue attive -activity.closed_issues_count_1=Issue chiusa -activity.closed_issues_count_n=Issue chiuse -activity.title.issues_1=%d Issue -activity.title.issues_n=%d Issue +activity.active_issues_count_1=%d segnalazione attiva +activity.active_issues_count_n=%d segnalazioni attive +activity.closed_issues_count_1=Segnalazione chiusa +activity.closed_issues_count_n=Segnalazioni chiuse +activity.title.issues_1=%d segnalazione +activity.title.issues_n=%d segnalazioni activity.title.issues_closed_from=%s chiusa da %s activity.title.issues_created_by=%s creata da %s activity.closed_issue_label=Chiusa -activity.new_issues_count_1=Nuova issue -activity.new_issues_count_n=Nuove issue +activity.new_issues_count_1=Nuova segnalazione +activity.new_issues_count_n=Nuove segnalazioni activity.new_issue_label=Aperta -activity.title.unresolved_conv_1=%d Conversazione non risolta -activity.title.unresolved_conv_n=%d Conversazioni non risolte +activity.title.unresolved_conv_1=%d conversazione non risolta +activity.title.unresolved_conv_n=%d conversazioni non risolte activity.unresolved_conv_desc=Queste issue e pull request cambiate di recente non sono ancora state risolte. activity.unresolved_conv_label=Aperta -activity.title.releases_1=%d Release -activity.title.releases_n=%d Release +activity.title.releases_1=%d rilascio +activity.title.releases_n=%d rilasci activity.title.releases_published_by=%s pubblicata da %s activity.published_release_label=Pubblicata activity.no_git_activity=In questo periodo non c'è stata alcuna attività di commit. @@ -1793,38 +1858,38 @@ settings.collaboration.read=Lettura settings.collaboration.owner=Proprietario settings.collaboration.undefined=Non definito settings.hooks=Webhooks -settings.githooks=Git Hooks -settings.basic_settings=Impostazioni di Base -settings.mirror_settings=Impostazioni di mirror +settings.githooks=Hook git +settings.basic_settings=Impostazioni di base +settings.mirror_settings=Impostazioni dello specchio settings.mirror_settings.mirrored_repository=Repository replicata settings.mirror_settings.direction=Direzione settings.mirror_settings.direction.pull=Tira settings.mirror_settings.direction.push=Push settings.mirror_settings.last_update=Ultimo aggiornamento settings.mirror_settings.push_mirror.none=Nessun mirror push configurato -settings.mirror_settings.push_mirror.remote_url=Url Del Repository Remoto Git -settings.mirror_settings.push_mirror.add=Aggiungi Push Mirror +settings.mirror_settings.push_mirror.remote_url=URL del progetto git remoto +settings.mirror_settings.push_mirror.add=Aggiungi specchio di immissione settings.sync_mirror=Sincronizza ora settings.site=Sito web -settings.update_settings=Aggiorna Impostazioni -settings.branches.update_default_branch=Aggiorna Ramo Predefinito +settings.update_settings=Aggiorna impostazioni +settings.branches.update_default_branch=Aggiorna ramo predefinito settings.advanced_settings=Opzioni avanzate -settings.wiki_desc=Abilita Wiki Repository +settings.wiki_desc=Abilita wiki del progetto settings.use_internal_wiki=Utilizza la wiki incorporata -settings.use_external_wiki=Usa Wiki esterna -settings.external_wiki_url=URL Wiki esterno +settings.use_external_wiki=Usa wiki esterna +settings.external_wiki_url=URL wiki esterno settings.external_wiki_url_error=L'URL della wiki esterna non è un URL valido. settings.external_wiki_url_desc=I visitatori verranno reindirizzati all'URL della wiki esterna cliccando sulla scheda di wiki. -settings.issues_desc=Abilità il tracciatore delle issue del repository -settings.use_internal_issue_tracker=Usa il tracciatore di issue incorporato -settings.use_external_issue_tracker=Usa un tracciatore di issue esterno -settings.external_tracker_url=URL del tracciatore di issue esterno +settings.issues_desc=Abilità il tracciatore delle segnalazioni del progetto +settings.use_internal_issue_tracker=Usa il tracciatore di segnalazioni incorporato +settings.use_external_issue_tracker=Usa un tracciatore di segnalazioni esterno +settings.external_tracker_url=URL del tracciatore di segnalazioni esterno settings.external_tracker_url_error=L'URL del tracciatore di issue esterno non è un URL valido. settings.external_tracker_url_desc=I visitatori verranno reindirizzati all'URL del tracciatore di issue esterno cliccando sulla scheda delle issue. -settings.tracker_url_format=Formato URL Gestore Problemi Esterno +settings.tracker_url_format=Formato URL del gestore segnalazioni esterno settings.tracker_url_format_error=L'URL del tracker di problemi esterno non è un URL valido. -settings.tracker_issue_style=Formato numerico del tracciatore di issue esterno +settings.tracker_issue_style=Formato numerico del tracciatore di segnalazioni esterno settings.tracker_issue_style.numeric=Numerico settings.tracker_issue_style.alphanumeric=Alfanumerico settings.tracker_issue_style.regexp=Espressione Regolare @@ -1833,33 +1898,33 @@ settings.tracker_issue_style.regexp_pattern_desc=Il primo gruppo catturato verr settings.tracker_url_format_desc=Usa i segnaposto {user}, {repo} e {index} per il nome utente, il nome del repository e l'indice delle issue. settings.enable_timetracker=Abilita il cronografo settings.allow_only_contributors_to_track_time=Consenti soltanto ai contributori di utilizzare il cronografo -settings.pulls_desc=Abilita le pull request del repository +settings.pulls_desc=Abilita le richieste di modifica del progetto settings.pulls.ignore_whitespace=Ignora gli spazi bianchi per evitare conflitti settings.pulls.enable_autodetect_manual_merge=Abilita il rilevamento automatico della fusione manuale (Nota: in alcuni casi speciali possono verificarsi errori) settings.pulls.allow_rebase_update=Abilita l'aggiornamento del ramo pull request per rebase settings.pulls.default_delete_branch_after_merge=Elimina il ramo pull request dopo la fusione per impostazione predefinita -settings.packages_desc=Abilita Il Registro Dei Pacchetti Repository -settings.projects_desc=Abilita Progetti Repository +settings.packages_desc=Abilita registro dei pacchetti del progetto +settings.projects_desc=Abilita progetti del progetto settings.admin_settings=Impostazioni amministratore -settings.admin_enable_health_check=Abilita verifica dell'integrità del repository (git fsck) +settings.admin_enable_health_check=Abilita verifica dell'integrità del progetto (git fsck) settings.admin_code_indexer=Indicizzatore del codice settings.admin_stats_indexer=Indicizzatore di statistiche del codice -settings.admin_indexer_commit_sha=Hash SHA dell'ultimo commit indicizzato +settings.admin_indexer_commit_sha=Ultimo SHA indicizzato settings.admin_indexer_unindexed=Non indicizzato settings.reindex_button=Aggiungi alla coda di re-indicizzazione settings.reindex_requested=Re-indicizzazione richiesta settings.admin_enable_close_issues_via_commit_in_any_branch=Chiudi un issue tramite un commit eseguito in un branch non predefinito -settings.danger_zone=Zona Pericolosa +settings.danger_zone=Zona pericolosa settings.new_owner_has_same_repo=Il nuovo proprietario ha già un repository con lo stesso nome. Per favore scegli un altro nome. -settings.convert=Converti in un repository regolare +settings.convert=Converti in un progetto regolare settings.convert_desc=È possibile convertire questo mirror in un repository regolare. Questa operazione non può essere annullata. settings.convert_notices_1=- Questa operazione convertirà questo mirror in una repository regolare e non potrà essere annullata. -settings.convert_confirm=Converti Repository +settings.convert_confirm=Converti progetto settings.convert_succeed=Il mirror è stato convertito in un repository regolare. -settings.convert_fork=Converti in un repository regolare +settings.convert_fork=Converti in un progetto regolare settings.convert_fork_desc=Puoi convertire questo fork in un normale repository. Questo non può essere annullato. settings.convert_fork_notices_1=Questa operazione convertirà il fork in un normale repository e non può essere annullata. -settings.convert_fork_confirm=Converti Repository +settings.convert_fork_confirm=Converti progetto settings.convert_fork_succeed=Il fork è stato convertito in un repository regolare. settings.transfer=Trasferisci proprietà settings.transfer.rejected=Il trasferimento del repository è stato rifiutato. @@ -1872,13 +1937,13 @@ settings.transfer_in_progress=Al momento c'è un trasferimento in corso. Si preg settings.transfer_notices_1=-Si perderà l'accesso al repository se lo si trasferisce ad un utente singolo. settings.transfer_notices_2=-Si manterrà l'accesso al repository se si trasferisce in un'organizzazione che possiedi (o condividi con qualcun'altro). settings.transfer_notices_3=- Se il repository è privato e viene trasferito a un singolo utente, questa azione si assicura che l'utente abbia almeno i permessi di lettura (e le modifiche se necessario). -settings.transfer_owner=Nuovo Proprietario +settings.transfer_owner=Nuovo proprietario settings.transfer_perform=Esegui trasferimento settings.transfer_started=`Questo repository è stato contrassegnato per il trasferimento e attende conferma da "%s"` settings.transfer_succeed=Il repository è stato trasferito. -settings.signing_settings=Impostazioni Verifica Firma -settings.trust_model=Modello di Fiducia per la Firma -settings.trust_model.default=Modello Di Fiducia Predefinito +settings.signing_settings=Impostazioni verifica firma +settings.trust_model=Modello di fiducia per la firma +settings.trust_model.default=Modello di fiducia predefinito settings.trust_model.default.desc=Usa il modello di trust del repository predefinito per questa installazione. settings.trust_model.collaborator=Collaboratore settings.trust_model.collaborator.long=Collaboratore: Firme di fiducia da parte dei collaboratori @@ -1888,19 +1953,19 @@ settings.trust_model.committer.long=Committer: firme affidabili che corrispondon settings.trust_model.collaboratorcommitter=Collaboratore+Committer settings.trust_model.collaboratorcommitter.long=Collaboratore+Committer: Firme di fiducia da parte dei collaboratori che corrispondono al committer settings.trust_model.collaboratorcommitter.desc=Le firme valide da parte dei collaboratori di questa repository saranno contrassegnate "fidate" se corrispondono al committer. Altrimenti le firme saranno contrassegnate con "untrusted" se la firma corrisponde al committer non corrisponde. Questo costringerà Forgejo a essere contrassegnato come committer su impegni firmati con l'effettivo committer contrassegnato come Co-Authored-By: e Co-Committed-By: nel commit. La chiave Forgejo predefinita deve corrispondere a un utente nel database. -settings.wiki_delete=Elimina dati Wiki +settings.wiki_delete=Elimina dati wiki settings.wiki_delete_desc=L'eliminazione dei dati della wiki del repository è permanente e non può essere annullata. settings.wiki_delete_notices_1=-Questa operazione eliminerà permanentemente e disabiliterà la wiki repository per %s. -settings.confirm_wiki_delete=Elimina dati Wiki +settings.confirm_wiki_delete=Elimina dati wiki settings.wiki_deletion_success=I dati della repository wiki sono stati eliminati. -settings.delete=Elimina questo repository +settings.delete=Elimina questo progetto settings.delete_desc=L'eliminazione di un repository è un'operazione permanente e non può essere annullata. settings.delete_notices_1=-Questa operazione NON PUÒ essere annullata. settings.delete_notices_2=-Questa operazione eliminerà definitivamente il repository %s inclusi codice, issue, commenti, dati wiki e impostazioni collaboratore. settings.delete_notices_fork_1=-I fork di questo repository diventeranno indipendenti dopo la cancellazione. settings.deletion_success=Il repository è stato eliminato. settings.update_settings_success=Le impostazioni del repository sono state aggiornate. -settings.confirm_delete=Elimina repository +settings.confirm_delete=Elimina progetto settings.add_collaborator=Aggiungi collaboratore settings.add_collaborator_success=Il collaboratore è stato aggiunto. settings.add_collaborator_inactive_user=Non posso aggiungere un utente inattivo come collaboratore. @@ -1914,17 +1979,17 @@ settings.org_not_allowed_to_be_collaborator=Le organizzazioni non possono essere settings.change_team_access_not_allowed=La modifica dell'accesso al team per il repository è stato limitato al solo proprietario dell'organizzazione settings.team_not_in_organization=Il team non è nella stessa organizzazione del repository settings.teams=Gruppi -settings.add_team=Aggiungi Squadra +settings.add_team=Aggiungi squadra settings.add_team_duplicate=Il team ha già il repository settings.add_team_success=Il team ha ora accesso al repository. -settings.search_team=Cerca Squadra… +settings.search_team=Cerca squadra… settings.change_team_permission_tip=Il permesso del team è impostato sulla pagina delle impostazioni del team e non può essere modificato per repository settings.delete_team_tip=Questo team ha accesso a tutte le repository e non può essere rimosso settings.remove_team_success=L'accesso del team al repository è stato rimosso. -settings.add_webhook=Aggiungi Webhook +settings.add_webhook=Aggiungi richiamo HTTP settings.add_webhook.invalid_channel_name=Il canale Webhook non può essere vuoto e contenere solo un # carattere. settings.hooks_desc=I Webhook effettuano automaticamente richieste HTTP POST ad un server quando si verificano determinati eventi Forgejo. Per saperne di più leggi la guida ai webhooks. -settings.webhook_deletion=Rimuovi Webhook +settings.webhook_deletion=Rimuovi richiamo HTTP settings.webhook_deletion_desc=Rimuovere un webhook rimuove le sue impostazioni e la sua cronologia di consegna. Continuare? settings.webhook_deletion_success=Il webhook è stato rimosso. settings.webhook.test_delivery=Test di consegna @@ -1936,12 +2001,12 @@ settings.webhook.payload=Contenuto settings.webhook.body=Corpo settings.webhook.replay.description=Riproduci questo webhook. settings.webhook.delivery.success=Un evento è stato aggiunto alla coda di consegna. Potrebbe volerci qualche secondo prima che venga visualizzato nella cronologia delle consegne. -settings.githooks_desc=Git Hooks è alimentato da Git stesso. È possibile modificare i file hook qui sotto per impostare operazioni personalizzate. +settings.githooks_desc=I Git hook sono alimentati da Git stesso. È possibile modificare i file hook qui sotto per impostare operazioni personalizzate. settings.githook_edit_desc=Se l'hook è inattivo, sarà presentato un contenuto esempio. Lasciando il contenuto vuoto disattiverai questo hook. settings.githook_name=Nome hook settings.githook_content=Contenuto hook -settings.update_githook=Aggiorna Hook -settings.add_webhook_desc=Forgejo invierà richieste POST con un tipo di contenuto specifico all'URL di destinazione. Per saperne di più leggi la guida ai webhook. +settings.update_githook=Aggiorna hook +settings.add_webhook_desc=Forgejo invierà richieste POST con un tipo di contenuto specifico all'URL di destinazione. Per saperne di più leggi la guida ai richiami HTTP. settings.payload_url=URL di destinazione settings.http_method=Metodo HTTP settings.content_type=Tipo di contenuto POST @@ -1951,11 +2016,11 @@ settings.slack_icon_url=URL icona settings.slack_color=Colore settings.discord_username=Nome utente settings.discord_icon_url=URL icona -settings.event_desc=Attivato su: -settings.event_push_only=Pusha eventi +settings.event_desc=Innesco su: +settings.event_push_only=Immetti eventi settings.event_send_everything=Tutti gli eventi settings.event_choose=Eventi personalizzati… -settings.event_header_repository=Eventi del repository +settings.event_header_repository=Eventi del progetto settings.event_create=Crea settings.event_create_desc=Branch o tag creato. settings.event_delete=Elimina @@ -1969,31 +2034,31 @@ settings.event_push=Push settings.event_push_desc=Git push in un repository. settings.event_repository=Repository settings.event_repository_desc=Repository creato o eliminato. -settings.event_header_issue=Eventi dei Problemi +settings.event_header_issue=Eventi delle segnalazioni settings.event_issues=Issues settings.event_issues_desc=Issue aperto, chiuso, riaperto o modificato. -settings.event_issue_assign=Issue Assegnato +settings.event_issue_assign=Segnalazione assegnata settings.event_issue_assign_desc=Issue assegnata o non assegnata. -settings.event_issue_label=Issue etichettato +settings.event_issue_label=Segnalazione etichettata settings.event_issue_label_desc=Etichette dei Problemi aggiornate o cancellate. -settings.event_issue_milestone=Obiettivo Raggiunto +settings.event_issue_milestone=Segnalazione risolta settings.event_issue_milestone_desc=Obiettivo raggiunto o abbandonato. -settings.event_issue_comment=Commento Issue +settings.event_issue_comment=Commento segnalazione settings.event_issue_comment_desc=Commento issue creato, modificato o rimosso. -settings.event_header_pull_request=Eventi di Pull Request -settings.event_pull_request=Pull Request +settings.event_header_pull_request=Eventi di richieste di modifiche +settings.event_pull_request=Richiesta di modifica settings.event_pull_request_desc=Pull request aperta, chiusa, riaperta o modificata. -settings.event_pull_request_assign=Pull Request assegnata +settings.event_pull_request_assign=Richiesta di modifica assegnata settings.event_pull_request_assign_desc=Pull request assegnata o non assegnata. -settings.event_pull_request_label=Pull Request etichettata +settings.event_pull_request_label=Richiesta di modifica etichettata settings.event_pull_request_label_desc=Etichette Pull request aggiornate o cancellate. -settings.event_pull_request_milestone=Pull Request raggiunta +settings.event_pull_request_milestone=Richiesta di modifica risolta settings.event_pull_request_milestone_desc=Pull request raggiunto o abbandonato. -settings.event_pull_request_comment=Commento su questa richiesta di pull +settings.event_pull_request_comment=Commento su richiesta di modifica settings.event_pull_request_comment_desc=Commento della Pull request creato, modificato o cancellato. -settings.event_pull_request_review=Pull Request Revisionata +settings.event_pull_request_review=Richiesta di modifica revisionata settings.event_pull_request_review_desc=Pull request approvata, respinta o recensione commento. -settings.event_pull_request_sync=Richiesta Pull Sincronizzata +settings.event_pull_request_sync=Richiesta di modifica sincronizzata settings.event_pull_request_sync_desc=Pull request sincronizzata. settings.event_package=Pacchetto settings.event_package_desc=Pacchetto creato o eliminato in un repository. @@ -2094,21 +2159,21 @@ settings.no_protected_branch=Non ci sono branch protetti. settings.edit_protected_branch=Modifica settings.protected_branch_required_approvals_min=Le autorizzazioni richieste non possono essere negative. settings.tags=Etichette -settings.tags.protection=Protezione Etichetta -settings.tags.protection.pattern=Sequenza Etichetta +settings.tags.protection=Protezione dell'etichetta +settings.tags.protection.pattern=Sequenza etichetta settings.tags.protection.allowed=Consentito settings.tags.protection.allowed.users=Utenti ammessi settings.tags.protection.allowed.teams=Squadre ammesse settings.tags.protection.allowed.noone=Nessuno -settings.tags.protection.create=Proteggi Etichetta +settings.tags.protection.create=Proteggi etichetta settings.tags.protection.none=Non ci sono etichette protette. -settings.bot_token=Token del Bot +settings.bot_token=Token del bot settings.chat_id=ID chat settings.matrix.homeserver_url=URL Homeserver settings.matrix.room_id=ID della stanza settings.matrix.message_type=Tipo di messaggio -settings.archive.button=Archivia Repo -settings.archive.header=Archivia questo Repo +settings.archive.button=Archivia progetto +settings.archive.header=Archivia questo progetto settings.archive.success=Il repo è stato archiviato con successo. settings.archive.error=Si è verificato un errore durante il tentativo di archiviare il repo. Vedi il log per maggiori dettagli. settings.archive.error_ismirror=Non puoi archiviare un mirror repo. @@ -2122,7 +2187,7 @@ settings.lfs_findcommits=Cerca commit settings.lfs_lfs_file_no_commits=Nessun commit trovato per questo file LFS settings.lfs_noattribute=Questo percorso non ha l'attributo bloccabile nel ramo predefinito settings.lfs_delete=Elimina file LFS con OID %s -settings.lfs_delete_warning=Eliminare un file LFS può causare errori tipo 'oggetto non esiste' al checkout. Sei sicuro? +settings.lfs_delete_warning=Eliminare un file LFS può causare errori tipo "oggetto non esiste" al passaggio. Sei sicuro? settings.lfs_findpointerfiles=Trova files puntatori settings.lfs_locks=Blocca settings.lfs_invalid_locking_path=Percorso non valido: %s @@ -2151,11 +2216,11 @@ diff.browse_source=Sfoglia il codice sorgente diff.parent=parent diff.commit=commit diff.git-notes=Note -diff.data_not_available=Dati Diff non disponibili -diff.options_button=Opzioni Diff +diff.data_not_available=Differenze non disponibili +diff.options_button=Opzioni differenze diff.show_diff_stats=Mostra statistiche -diff.download_patch=Scarica il file Patch -diff.download_diff=Scarica il file Diff +diff.download_patch=Scarica il file toppa +diff.download_diff=Scarica il file differenza diff.show_split_view=Visualizzazione separata diff.show_unified_view=Visualizzazione unificata diff.whitespace_button=Spazi bianchi @@ -2167,7 +2232,7 @@ diff.stats_desc=%d ha cambiato i file con %d aggiunte%s branch.deleted_by=Eliminato da %s branch.included_desc=Questo ramo fa parte del ramo predefinito @@ -2274,12 +2339,12 @@ pulls.cmd_instruction_merge_title = Merge pulls.cmd_instruction_checkout_desc = Dalla tua repository del progetto, accedi ad un nuovo ramo e prova le modifiche. milestones.new_subheader = I traguardi possono aiutarti ad organizzare i problemi e a tracciare i loro progressi. activity.navbar.contributors = Contributori -migrate.cancel_migrating_title = Annulla Migrazione -more_operations = Più Operazioni +migrate.cancel_migrating_title = Annulla migrazione +more_operations = Ulteriori operazioni actions = Azioni commit.operations = Operazioni issues.action_check = Seleziona/Deseleziona -issues.close = Chiudi Problema +issues.close = Chiudi segnalazione issues.role.collaborator = Collaboratore desc.sha256 = SHA256 editor.add = Aggiungi %s @@ -2291,9 +2356,9 @@ contributors.contribution_type.deletions = Rimozioni settings.protect_patterns = Sequenze milestones.update_ago = Aggiornato %s mirror_sync = sincronizzato -object_format = Formato Oggetti +object_format = Formato oggetti from_comment = (commento) -executable_file = File Eseguibile +executable_file = File eseguibile commits.browse_further = Esplora di più commitstatus.success = Successo projects.column.edit = Modifica Colonna @@ -2323,37 +2388,305 @@ settings.units.overview = Panoramica all_branches = Tutti i rami projects.column.assigned_to = Assegnato a pulls.cmd_instruction_hint = `Visualizza istruzioni per la riga di comando.` +settings.add_collaborator_blocked_them = Non si può aggiungere il collaboratore perché ha bloccato il proprietario del progetto. +branch.protected_deletion_failed = Il ramo "%s" è protetto. Non può essere eliminato. +branch.default_deletion_failed = Il ramo "%s" è il ramo predefinito. Non può essere eliminato. +branch.tag_collision = Il ramo "%s" non può essere creato perché un'etichetta con lo stesso nome esiste già nel progetto. +topic.format_prompt = Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ("-") e punti ("."), possono arrivare fino a 35 caratteri di lunghezza. Le lettere devono essere minuscole. +error.broken_git_hook = Le hook Git di questo progetto sembrano rotte. Segui la documentazione per ripararli, poi immetti alcuni commit per aggiornare lo stato. +wiki.reserved_page = La nome della pagina della wiki "%s" è riservato. +wiki.delete_page_notice_1 = La rimozione della pagina della wiki "%s" non può essere annullata. Continuare? +settings.webhook.test_delivery_desc_disabled = Per testare questo richiamo HTTP con un evento finto, attivalo. +settings.protected_branch_duplicate_rule_name = Esiste già una regola per questo insieme di rami +rss.must_be_on_branch = Devi essere su ramo per avere un feed RSS. +admin.manage_flags = Gestisci flag +admin.enabled_flags = Flag abilitate per il progetto: +admin.update_flags = Aggiorna flag +admin.failed_to_replace_flags = Impossibile sostituire flag del progetto +admin.flags_replaced = Flag del progetto sostituite +fork_branch = Ramo da clonare sulla derivazione +fork_no_valid_owners = Questo progetto non può essere derivato perché non ci sono validi proprietari. +mirror_address_url_invalid = L'URL fornito è invalido. Devi eseguire l'escape di tutti i componenti dell'URL correttamente. +mirror_address_protocol_invalid = L'URL fornito è invalido. Solo posizioni http(s):// o git:// possono essere usate come specchio. +stars_remove_warning = Questo rimuoverà tutte le stelle da questo progetto. +blame.ignore_revs = Le revisioni in .git-blame-ignore-revs sono ignorate. Clicca qui per bypassare e vedere la vista incolpa normale. +archive.title = Questo progetto è archiviato. Puoi vedere i file e clonarlo, ma non puoi immettere o aprire segnalazioni o richieste di modifica. +archive.title_date = Questo progetto è stato archiviato il %s. Puoi vedere i file e clonarlo, ma non puoi immettere o aprire segnalazioni o richieste di modifica. +form.name_pattern_not_allowed = La sequenza "%s" non è ammessa nel nome di un progetto. +migrate.invalid_local_path = Il percorso locale è invalido. Non esiste o non è una cartella. +migrate.migrating_failed.error = Impossibile migrare: %s +migrate.forgejo.description = Migra dati da codeberg.org o da altre istanze Forgejo. +cite_this_repo = Cita questo progetto +file_follow = Segui Symlink +invisible_runes_header = `Questo file contiene caratteri Unicode invisibili` +ambiguous_runes_header = `Questo file contiene caratteri Unicode ambigui` +ambiguous_runes_description = `Questo file contiene caratteri Unicode che potrebbero essere confusi con altri caratteri. Se pensi che questo sia intenzionale puoi tranquillamente ignorare questo avviso. Usa il tasto Escape per rivelarli.` +vendored = Vendored +generated = Generato +commit.contained_in = Questo commit è contenuto in: +commit.contained_in_default_branch = Questo commit è parte del ramo predefinito +commit.load_referencing_branches_and_tags = Carica rami ed etichette che fanno riferimento a questo commit +editor.fail_to_apply_patch = Impossibile applicare toppa "%s" +editor.new_branch_name = Dai un nome al nuovo ramo per questo commit +editor.branch_already_exists = Il ramo "%s" esiste già nel progetto. +editor.directory_is_a_file = Il nome cartella "%s" è già usato come nome file in questo progetto. +editor.file_is_a_symlink = `"%s" è un collegamento simbolico. I collegamenti simbolici non possono essere modificati nell'editor web` +editor.filename_is_a_directory = Il nome file "%s" è già usato come nome cartella in questo progetto. +editor.file_editing_no_longer_exists = Il file in modifica, "%s", non esiste più in questo progetto. +editor.file_deleting_no_longer_exists = Il file in eliminazione, "%s", non esiste più in questo progetto. +editor.file_already_exists = Un file chiamato "%s" esiste già in questo progetto. +editor.fail_to_update_file = Impossibile aggiornare/creare il file "%s". +editor.upload_file_is_locked = Il file "%s" è bloccato da %s. +editor.cannot_commit_to_protected_branch = Non si può fare commit sul ramo protetto "%s". +commits.search.tooltip = Puoi prefissare parole chiave con "autore:", "committer:", "dopo:", o "prima:", esempio "ripristino autore:Alice prima:2019-01-13". +issues.filter_project_all = Tutti i progetti +issues.label_exclusive_desc = Dai all'etichetta il nome ambito/oggetto per renderla mutualmente esclusiva con altre etichette in ambito/. +issues.pin_comment = ha fissato questo %s +issues.max_pinned = Non puoi fissare ulteriori segnalazioni +issues.unpin_comment = ha sbloccato questo %s +issues.cancel_tracking_history = `disabilitato tracciamento del tempo %s` +issues.due_date_not_writer = Ti serve il permesso di scrittura a questo progetto per poter aggiornare la scadenza di una segnalazione. +issues.dependency.no_permission_1 = Non hai il permesso per leggere %s dipendenza +issues.dependency.issue_batch_close_blocked = Non puoi chiudere segnalazioni in gruppo perché la segnalazione #%d ha ancora dipendenze aperte +issues.review.option.show_outdated_comments = Mostra commenti vecchi +issues.review.option.hide_outdated_comments = Nascondi commenti vecchi +issues.comment.blocked_by_user = Non puoi creare un commento su questa segnalazione perché sei bloccato dal proprietario del progetto o dall'autore della segnalazione. +pulls.expand_files = Espandi tutti i file +pulls.collapse_files = Collassa tutti i file +pulls.show_all_commits = Mostra tutti i commit +pulls.showing_only_single_commit = Mostrando solo cambiamenti del commit %[1]s +pulls.showing_specified_commit_range = Mostrando solo cambiamenti tra %[1]s..%[2]s +pulls.select_commit_hold_shift_for_range = Seleziona commit. Premi maiusc + click per selezionare un intervallo +pulls.filter_changes_by_commit = Filtra per commit +pulls.nothing_to_compare_have_tag = I rami/etichette selezionati sono uguali. +pulls.merged_success = Richiesta di modifica fusa correttamente e chiusa +pulls.closed = Richiesta di modifica chiusa +pulls.merged_info_text = Il ramo %s può essere eliminato ora. +pulls.blocked_by_user = Non puoi creare una richiesta di modifica in questo progetto perché sei bloccato dal proprietario. +pulls.status_checks_hide_all = Nascondi tutti i controlli +pulls.status_checks_show_all = Mostra tutti i controlli +pulls.clear_merge_message_hint = Cancellare il messaggio di fusione rimuoverà solo il messaggio di commit e terrà le sequenze generate da git come "Co-Authored-By ..". +pulls.close = Chiudi la richiesta di modifica +pulls.reopen_failed.head_branch = La richiesta di modifica non può essere riaperta perché il ramo genitore non esiste più. +pulls.clear_merge_message = Cancella messaggio di fusione +pulls.reopen_failed.base_branch = La richiesta di modifica non può essere riaperta perché il ramo di base non esiste più. +pulls.agit_explanation = Creata usando flusso di lavoro AGit. AGit permette ai contributori di proporre modifiche usando "git push" senza creare una derivazione o un nuovo ramo. +pulls.recently_pushed_new_branches = Hai immesso sul ramo %[1]s %[2]s +milestones.filter_sort.earliest_due_data = Scadenza più vicina +signing.wont_sign.twofa = Devi avere la verifica a due fattori abilitata per firmare i commit. +signing.wont_sign.parentsigned = Il commit non verrà firmato dato che il commit genitore non è firmato. +signing.wont_sign.basesigned = La fusione non verrà firmata dato che il commit base non è firmato. +signing.wont_sign.headsigned = La fusione non verrà firmata dato che il commit genitore non è firmato. +signing.wont_sign.not_signed_in = Non hai effettuato l'accesso. +wiki.original_git_entry_tooltip = Visualizza file Git originali invece di usare collegamenti amichevoli. +activity.navbar.pulse = Battito +activity.navbar.code_frequency = Frequenza del codice +activity.navbar.recent_commits = Commit recenti +contributors.contribution_type.filter_label = Tipi di contributo: +search.type.tooltip = Cerca tipo +search.match.tooltip = Includi solo risultati che corrispondono esattamente ai termini di ricerca +settings.mirror_settings.docs.disabled_push_mirror.instructions = Imposta il tuo progetto in modo che prelevi commit, etichette e rami da un altro progetto. +settings.mirror_settings.docs.no_new_mirrors = Il tuo progetto sta specchiando i cambiamenti a/da un altro progetto. Tieni a mente che non puoi creare nuovi specchi al momento. +settings.mirror_settings.docs.can_still_use = Nonostante tu non possa modificare specchi esistenti o crearne di nuovi puoi comunque il tuo specchio esistente. +settings.mirror_settings.docs.pull_mirror_instructions = Per impostare uno specchio di prelievo consulta: +settings.mirror_settings.docs.doc_link_title = Come specchio progetti? +settings.mirror_settings.docs.doc_link_pull_section = la sezione "Prelievo da un progetto remoto" della documentazione. +settings.mirror_settings.docs.pulling_remote_title = Prelievo da un progetto remote +settings.transfer_abort_success = Il trasferimento del progetto a %s è stato correttamente cancellato. +settings.enter_repo_name = Inserisci il proprietario e il nome del progetto esattamente come mostrato: +settings.confirmation_string = Stringa di conferma +settings.wiki_rename_branch_main = Normalizza il nome del ramo della wiki +settings.wiki_rename_branch_main_desc = Rinomina il ramo usato internamente dalla wiki come "%s". Questo è permanente è non può essere annullato. +settings.wiki_rename_branch_main_notices_1 = Questa operazione NON PUÒ essere annullata. +settings.wiki_branch_rename_success = Il nome del ramo della wiki del progetto è stato normalizzato correttamente. +settings.wiki_branch_rename_failure = Impossibile normalizzare il nome del ramo della wiki del progetto. +settings.confirm_wiki_branch_rename = Rinomina ramo della wiki +settings.wiki_rename_branch_main_notices_2 = Questo rinominerà permanentemente il ramo interno della wiki del progetto di %s. Passaggi esistenti dovranno essere aggiornati. +settings.add_collaborator_blocked_our = Non si può aggiungere il collaboratore perché il proprietario del progetto lo ha bloccato. +settings.webhook.replay.description_disabled = Per riprodurre questo richiamo HTTP, attivalo. +settings.event_wiki_desc = Pagina wiki creata, rinominata, modificata o rimossa. +settings.event_pull_request_review_request = Richiesta di modifica revisionata +branch.branch_already_exists = Il ramo "%s" esiste già nel progetto. +branch.branch_name_conflict = Il nome del ramo "%s" è in conflitto con il ramo già esiste "%s". +branch.restore_success = Il ramo "%s" è stato ripristinato. +branch.restore_failed = Impossibile ripristinare il ramo "%s". +branch.warning_rename_default_branch = Stai rinominando il ramo predefinito. +branch.rename_branch_to = Rinomina "%s" come: +branch.new_branch_from = Crea nuovo ramo da "%s" +tag.create_tag_from = Crea nuova etichetta da "%s" +tag.create_success = Etichetta "%s" creata. +settings.unarchive.button = Disarchivia progetto +release.tags_for = Etichette per %s +branch.delete = Elimina ramo "%s" +issues.role.first_time_contributor_helper = Questa non è il primo contributo di questo utente al progetto. +issues.role.contributor_helper = Questo utente ha precedentemente fatto commit al progetto. +pulls.blocked_by_official_review_requests = Questa richiesta di modifica è bloccata perché manca l'approvazione di uno o più revisori ufficiali. +pulls.blocked_by_changed_protected_files_1 = Questa richiesta di modifica è bloccata perché modifica un file protetto: +pulls.blocked_by_changed_protected_files_n = Questa richiesta di modifica è bloccata perché modifica file protetti: +pulls.has_merged = Respinto: la richiesta di modifica è stata fusa, non puoi fonderla di nuovo o cambiare il ramo di destinazione. +milestones.filter_sort.latest_due_date = Scadenza più lontana +settings.mirror_settings.docs = Imposta il tuo progetto in modo che sincronizzi automaticamente commit, etichette e rami con un altro progetto. +settings.mirror_settings.docs.disabled_pull_mirror.instructions = Imposta il tuo progetto in modo che immetta commit, etichette e rami in un altro progetto automaticamente. Gli specchi di prelievo sono stati disabilitati dall'amministratore del sito. +settings.mirror_settings.docs.disabled_push_mirror.info = Gli specchi di immissione sono stati disabilitati dall'amministratore del sito. +settings.mirror_settings.docs.more_information_if_disabled = Puoi scoprire di più riguardo specchi di prelievo e di immissione qui: +commits.view_path = Vista a questo punto nella cronologia +issues.action_check_all = Seleziona/deseleziona tutti gli elementi +issues.num_comments_1 = %d commento +issues.no_content = Descrizione non fornita. +issues.unpin_issue = Sblocca segnalazione +new_repo_helper = Un progetto contiene tutti i file, inclusa la storia delle revisioni. Ne ospiti già una altrove? Migra il progetto. +projects.card_type.images_and_text = Immagini e testo +object_format_helper = Formato oggetti del progetto. Non può essere cambiato in seguito. SHA1 è il più compatibile. +editor.file_delete_success = Il file "%s" è stato eliminato. +editor.upload_files_to_dir = Cari file su "%s" +commits.no_commits = Nessun commit in comune. "%s" e "%s" hanno cronologie completamente diverse. +commits.renamed_from = Rinomina da %s +invisible_runes_description = `Questo file contiene caratteri Unicode invisibili che sono indistinguibili agli umani ma potrebbero essere processati diversamente da un computer. Se pensi che questo sia intenzionale puoi tranquillamente ignorare questo avviso. Usa il tasto Escape per rivelarli.` +issues.filter_type.reviewed_by_you = Revisionati da te +projects.edit_success = Il progetto "%s" è stato aggiornato. +issues.keyword_search_unavailable = La ricerca per parola chiave non è attualmente disponibile. Contatta l'amministratore del sito. +issues.role.collaborator_helper = Questo utente è stato invitato a collaborare sul progetto. +pulls.commit_ref_at = `ha fatto riferimento a questa richiesta di modifica da un commit %[2]s` +settings.thread_id = ID della discussione +release.title = Titolo del rilascio +visibility_helper = Rendi progetto privato +clone_in_vscodium = Clona in VSCodium +blame.ignore_revs.failed = Impossibile ignorare le revisioni in .git-blame-ignore-revs. +author_search_tooltip = Mostra un massimo di 30 utenti +tree_path_not_found_commit = Il percorso %[1]s non esiste nel commit %[2]s +tree_path_not_found_branch = Il percorso %[1]s non esiste nel ramo %[2]s +tree_path_not_found_tag = Il percorso %[1]s non esiste nell'etichetta %[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 cancellare 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. +projects.column.set_default_desc = Imposta questa colonna come predefinita per segnalazioni e richieste di modifica non categorizzate +projects.column.unset_default_desc = Disattiva questa colonna come predefinita +projects.column.deletion_desc = Eliminare la colonna di un progetto sposta tutte le relative segnalazioni in "Non categorizzate". Continuare? +issues.choose.ignore_invalid_templates = I modelli non validi sono stati ignorati +issues.choose.invalid_templates = trovati %v modello/i non valido/i +issues.choose.invalid_config = La configurazione della segnalazione contiene errori: +issues.label_templates.fail_to_load_file = Impossibile caricare file di modello etichetta "%s": %v +issues.role.member_helper = questo utente è un membro dell'organizzazione che possiede questo progetto. +issues.review.pending.tooltip = Questo commento non è attualmente visibile ad altri utenti. Per inviare il tuo commento in attesa selezione "%s" -> "%s/%s/%s" in cima alla pagina. +pulls.blocked_by_approvals = Questa richiesta di modifica non ha ancora sufficienti approvazioni. %d di %d approvazioni concesse. +pulls.blocked_by_rejection = Questa richiesta di modifica ha modifiche richieste da un revisore ufficiale. +pulls.blocked_by_outdated_branch = Questa richiesta di modifica è bloccata perché è obsoleta. +pulls.fast_forward_only_merge_pull_request = Solo fast-forward +signing.will_sign = Questo commit verrà firmato con la chiave "%s". +signing.wont_sign.error = C'è stato un errore durante il controllo per la firma del commit. +signing.wont_sign.nokey = Non c'è una chiave disponibile per firmare questo commit. +signing.wont_sign.never = I commit non sono mai firmati. +signing.wont_sign.always = I commit sono sempre firmati. +signing.wont_sign.approved = La fusione non sarà firmata dato che la RM non è approvata. +wiki.page_title = Titolo della pagina +wiki.page_content = Contenuto della pagina +settings.mirror_settings.pushed_repository = Progetto immesso +settings.mirror_settings.push_mirror.edit_sync_time = Modifica intervallo di sincronizzazione degli specchi +settings.units.units = Unità del progetto +settings.units.add_more = Aggiungi ancora... +settings.wiki_globally_editable = Consenti a tutti di modificare la wiki +settings.pull_mirror_sync_in_progress = Prelevando cambiamenti dal progetto remoto %s. +settings.push_mirror_sync_in_progress = Immettendo cambiamenti al progetto remoto %s. +settings.update_mirror_settings = Aggiorna impostazioni dello specchio +settings.branches.switch_default_branch = Passa al ramo predefinito +settings.branches.add_new_rule = Aggiungi nuova regola +settings.actions_desc = Abilita azioni del progetto +settings.new_owner_blocked_doer = Il nuovo proprietario ti ha bloccato. +settings.update_settings_no_unit = Ili progetto dovrebbe consentire almeno qualche tipo di interazione. +settings.add_collaborator_owner = Non si può aggiungere un proprietario come collaboratore. +branch.delete_desc = Eliminare un ramo è permanente. Nonostante il ramo eliminato potrebbe continuare ad esistere per un breve periodo di tempo prima di essere realmente eliminato, l'eliminazione NON PUÒ essere annullata in molti casi. Continuare? +editor.invalid_commit_mail = Email invalida per creare un commit. +editor.branch_does_not_exist = Il ramo "%s" non esiste nel progetto. +issues.label_archive = Archivia etichetta +issues.label_archived_filter = Mostra etichette archiviate +issues.dependency.no_permission_n = Non hai il permesso di lettura per leggere %s dipendenze +branch.restore = Ripristina il ramo "%s" +issues.dependency.no_permission.can_remove = Non ha il permesso per leggere questa dipendenza ma puoi rimuovere questa dipendenza +issues.review.outdated_description = Il contenuto è cambiato da quando questo commento è stato fatto +settings.tags.protection.pattern.description = Puoi usare un singolo nome o un glob pattern o un'espressione regolare per selezionare più etichette. Leggi di più nella guide sulle etichette protette. +issues.author_helper = Questo utente è l'autore. +issues.comment_pull_merged_at = fondi commit %[1]s in %[2]s %[3]s +issues.comment_manually_pull_merged_at = fondi commit %[1]s in %[2]s %[3]s manualmente +pulls.review_only_possible_for_full_diff = La revisione è possibile solo quando visualizzando le differenze complete +issues.role.first_time_contributor = Contributore per la prima volta +issues.label_archive_tooltip = Le etichette archiviate sono escluse dai suggerimenti quando si ricerca un'etichetta. +form.name_reserved = Il nome progetto "%s" è riservato. +release.message = Descrivi questo rilascio +branch.deletion_success = Il ramo "%s" è stato eliminato. +branch.deletion_failed = Impossibile eliminare il ramo "%s". +branch.delete_branch_has_new_commits = Il ramo "%s" non può essere eliminato perché nuovi commit sono stati aggiunti dopo la fusione. +branch.create_from = da "%s" +branch.create_success = Il ramo "%s" non può essere creato. +branch.download = Scarica il ramo "%s" +branch.rename = Rinomina il ramo "%s" +branch.search = Cerca ramo +issues.blocked_by_user = Non puoi creare una segnalazione su questo progetto perché sei bloccato dal proprietario. +settings.archive.text = Archiviare il progetto lo renderà unicamente di sola lettura. Sarà nascosto nel pannello di controllo. Nessuno (neanche tu!) potrai fare nuovi commit, o aprire segnalazioni o richieste di modifica. +settings.unarchive.header = Disarchivia questo progetto +settings.archive.mirrors_unavailable = Gli specchi non sono disponibile se il progetto è archiviato. +issues.role.owner_helper = Questo utente è il proprietariə di questo progetto. +issues.label_exclusive_warning = Ogni etichetta in conflitto in un ambito sarà rimossa durante la modifica di un'etichetta di una segnalazione o di una richiesta di modifica. +pulls.show_changes_since_your_last_review = Mostra cambiamenti dalla tua ultima revisione +signing.wont_sign.commitssigned = La fusione non verrà firmata dato che tutti i commit associati non sono firmati. +settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Al momento questo può essere fatto solo nel menù "Nuova migrazione". Per ulteriori informazioni consulta: +settings.pulls.default_allow_edits_from_maintainers = Consenti modifica dai manutentori in modo predefinito +settings.trust_model.committer.desc = Firme valide saranno etichettate "fidata" se corrispondo all'autore del commit, altrimenti saranno etichettate "non corrisponde". Questo costringe Forgejo ad esse l'autore dei commit firmati, con il vero autore etichettato con le sequenze Co-authored-by: e Co-commited-by: nel commit. La chiave predefinita di Forgejo deve corrispondere ad un utente nella base di dati. +signing.wont_sign.pubkey = Il commit non verrà firmato perché non hai una chiave pubblica associata al tuo account. +settings.releases_desc = Abilita rilasci del progetto +settings.unarchive.text = Disarchiviare il progetto ripristinerà la sua abilità di ricevere commit e immissioni, oltre che nuove segnalazioni e richieste di modifica. +settings.unarchive.success = Il progetto è stato disarchiviato correttamente. +settings.unarchive.error = Si è verificato un errore durante la disarchiviazione del progetto. Vedi il log per ulteriori dettagli. +release.title_empty = Il titolo non può essere vuoto. +release.releases_for = Rilasci per %s +diff.comment.add_line_comment = Aggiungi riga commento +diff.show_file_tree = Mostra albero dei file +diff.hide_file_tree = Nascondi albero dei file +tag.ahead.target = a %s da questa etichetta +release.tag_helper_new = Nuova etichetta. Questa etichetta sarà creata dalla destinazione. +release.tag_helper_existing = Etichetta esistente. +release.deletion_desc = La rimozione di un rilascio lo rimuove solo da Forgejo. Non influenza l'etichetta Git, i contenuti del tuo progetto o la sua cronologia. Continuare? +branch.already_exists = Un ramo chiamato "%s" esiste già. [graphs] contributors.what = contribuzioni +component_loading_failed = Impossibile caricare %s +component_loading_info = Questo potrebbe richiedere un po'… +component_loading = Caricando %s... +code_frequency.what = frequenza del codice +recent_commits.what = commit recenti +component_failed_to_load = Si è verificato un errore inaspettato. [org] -org_name_holder=Nome dell'Organizzazione +org_name_holder=Nome dell'organizzazione org_full_name_holder=Nome completo dell'organizzazione org_name_helper=I nomi delle organizzazioni devono essere brevi e semplici da ricordare. -create_org=Crea Organizzazione +create_org=Crea organizzazione repo_updated=Aggiornato members=Membri teams=Team lower_members=membri lower_repositories=repository -create_new_team=Nuovo Team -create_team=Crea Team +create_new_team=Nuovo team +create_team=Crea squadra org_desc=Descrizione -team_name=Nome Team +team_name=Nome squadra team_desc=Descrizione team_name_helper=I nomi dei team devono essere brevi e semplici da ricordare. team_desc_helper=Descrivi lo scopo o il ruolo del team. team_access_desc=Accesso al repository team_permission_desc=Autorizzazione -team_unit_desc=Consentire l'accesso a sezioni di Repository +team_unit_desc=Consenti l'accesso a sezioni di progetto team_unit_disabled=(Disabilitato) form.create_org_not_allowed=Non disponi dell'autorizzazione per creare un organizzazione. settings=Impostazioni settings.options=Organizzazione -settings.full_name=Nome Completo +settings.full_name=Nome completo settings.website=Sito Web settings.location=Residenza settings.permission=Autorizzazioni @@ -2361,17 +2694,17 @@ settings.repoadminchangeteam=L'amministratore del repository può aggiungere e r settings.visibility=Visibilità settings.visibility.public=Pubblico settings.visibility.limited_shortname=Limitato -settings.visibility.private=Privato (Visibile solo ai membri dell'organizzazione) +settings.visibility.private=Privato (visibile solo ai membri dell'organizzazione) settings.visibility.private_shortname=Privato -settings.update_settings=Aggiorna Impostazioni +settings.update_settings=Aggiorna impostazioni settings.update_setting_success=Le impostazioni dell'organizzazione sono state aggiornate. settings.change_orgname_redirect_prompt=Il vecchio nome reindirizzerà fino a quando non sarà richiesto. settings.update_avatar_success=L'avatar dell'organizzazione è stato aggiornato. settings.delete=Elimina organizzazione settings.delete_account=Elimina questa organizzazione settings.delete_prompt=L'organizzazione verrà rimossa definitivamente. Questa operazione NON PUÒ essere annullata! -settings.confirm_delete_account=Conferma Eliminazione +settings.confirm_delete_account=Conferma eliminazione settings.delete_org_title=Elimina organizzazione settings.delete_org_desc=Questa organizzazione verrà eliminata definitivamente. Continuare? settings.hooks_desc=Aggiungi i webhooks che verranno attivati per tutti i repository sotto questa organizzazione. @@ -2398,9 +2731,9 @@ teams.leave=Abbandona teams.leave.detail=Lasciare %s? teams.can_create_org_repo=Crea repository teams.can_create_org_repo_helper=I membri possono creare nuovi repository nell'organizzazione. Il creatore otterrà l'accesso di amministratore alla nuova repository. -teams.none_access=Nessun Accesso +teams.none_access=Nessun accesso teams.none_access_helper=I membri non possono visualizzare o fare altre azioni su questa unità. -teams.general_access=Accesso Generale +teams.general_access=Accesso generale teams.general_access_helper=I permessi dei membri saranno decisi dalla seguente tabella dei permessi. teams.read_access=Lettura teams.read_access_helper=I membri possono visualizzare e clonare i repository del team. @@ -2411,18 +2744,18 @@ teams.admin_access_helper=I membri possono pullare e pushare sulle repository de teams.no_desc=Questo team non ha alcuna descrizione teams.settings=Impostazioni teams.owners_permission_desc=I proprietari hanno pieno accesso a tutti i repository e hanno diritti di amministratore nell'organizzazione. -teams.members=Membri del Team -teams.update_settings=Aggiorna Impostazioni -teams.delete_team=Elimina team -teams.add_team_member=Aggiungi un Membro al Team -teams.delete_team_title=Elimina team +teams.members=Membri della squadra +teams.update_settings=Aggiorna impostazioni +teams.delete_team=Elimina squadra +teams.add_team_member=Aggiungi un membro alla squadra +teams.delete_team_title=Elimina squadra teams.delete_team_desc=Eliminare un team revocherà l'accesso al repository da parte dei suoi membri. Continuare? teams.delete_team_success=Il team è stato eliminato. teams.read_permission_desc=Questo team concede l'accesso di lettura: i membri possono visualizzare e clonare i repository del team. teams.write_permission_desc=Questo team concede l'accesso di Scrittura: i membri possono leggere da e pushare sui repository del team. teams.admin_permission_desc=Questo team concede l'accesso di Amministratore: i membri possono leggere da, pushare su e aggiungere collaboratori ai repository del team. teams.create_repo_permission_desc=Inoltre, questo team concede il permesso di Creare repository: i membri possono creare nuove repository nell'organizzazione. -teams.repositories=Repository di Squadra +teams.repositories=Progetti della squadra teams.search_repo_placeholder=Ricerca repository… teams.remove_all_repos_title=Rimuovi tutti i repository del team teams.remove_all_repos_desc=Questo rimuoverà tutte le repository dal team. @@ -2439,15 +2772,27 @@ teams.all_repositories_read_permission_desc=Questo team concede permessi teams.all_repositories_write_permission_desc=Questo team concede permessi di scrittura accesso a tutte le repository: i membri possono leggere e pushare le repository. teams.all_repositories_admin_permission_desc=Questo team concede a Amministratore l'accesso a tutte le repository: i membri possono leggere, pushare e aggiungere collaboratori alle repository. code = Codice +form.name_pattern_not_allowed = La sequenza "%s" non è consentita nel nome di un'organizzazione. +settings.change_orgname_prompt = Nota: cambiare il nome dell'organizzazione cambierà anche l'URL dell'organizzazione e libererà il vecchio nome. +teams.invite_team_member = Invita in %s +teams.invite_team_member.list = Inviti in sospeso +teams.add_nonexistent_repo = Il progetto che stai cercando di aggiungere non esiste, crealo prima. +teams.invite.title = Sei stato invitato ad unirti alla squadra %s nell'organizzazione %s. +teams.invite.by = Invitato da %s +teams.invite.description = Clicca il tasto sotto per entrare nella squadra. +follow_blocked_user = Non puoi seguire questa organizzazione perché ti ha bloccato. +form.name_reserved = Il nome di organizzazione "%s" è riservato. +settings.email = Email di contatto +settings.visibility.limited = Limitato (visibile solo agli utenti autenticati) [admin] dashboard=Pannello di Controllo -users=Account utenti +users=Profili utenti organizations=Organizzazioni repositories=Repository hooks=Webhooks authentication=Fonti di autenticazione -emails=Email Utente +emails=Email utenti config=Configurazione notices=Avvisi di sistema monitor=Monitoraggio @@ -2474,63 +2819,63 @@ dashboard.cron.error=Errore in Cron: %s: %[3]s dashboard.cron.finished=Cron: %[1]s ha finito dashboard.delete_inactive_accounts=Elimina tutti gli account non attivati dashboard.delete_inactive_accounts.started=Attività di eliminazione degli account non attivati iniziata. -dashboard.delete_repo_archives=Elimina tutti gli archivi dei repository (ZIP, TAR.GZ, etc..) +dashboard.delete_repo_archives=Elimina tutti gli archivi dei progetti (ZIP, TAR.GZ, ecc..) dashboard.delete_repo_archives.started=Attività di eliminazione degli archivi del repository iniziata. dashboard.delete_missing_repos=Elimina tutti i repository mancanti dei loro file Git dashboard.delete_missing_repos.started=Elimina tutti i repository mancanti dei loro file Git. dashboard.delete_generated_repository_avatars=Elimina gli avatar generati nelle repository -dashboard.update_mirrors=Aggiorna Mirror +dashboard.update_mirrors=Aggiorna specchi dashboard.repo_health_check=Controlla integrità di tutti i repository dashboard.check_repo_stats=Controlla tutte le statistiche del repository dashboard.archive_cleanup=Elimina vecchi archivi del repository dashboard.deleted_branches_cleanup=Pulisci branch eliminati dashboard.update_migration_poster_id=Aggiorna gli ID del poster di migrazione dashboard.git_gc_repos=Esegui la garbage collection su tutti i repository -dashboard.resync_all_sshkeys=Aggiornare il file '.ssh/authorized_keys' con le chiavi SSH Forgejo. -dashboard.resync_all_sshprincipals=Aggiornare il file '.ssh/authorized_keys' con le chiavi SSH Forgejo. -dashboard.resync_all_hooks=Sincronizza nuovamente gli hook di pre-ricezione, di aggiornamento e di post-ricezione di tutti i repository. +dashboard.resync_all_sshkeys=Aggiornare il file ".ssh/authorized_keys" con le chiavi SSH di Forgejo. +dashboard.resync_all_sshprincipals=Aggiorna il file ".ssh/authorized_keys" con le chiavi principali SSH Forgejo. +dashboard.resync_all_hooks=Sincronizza nuovamente gli hook di pre-ricezione, di aggiornamento e di post-ricezione di tutti i progetti dashboard.reinit_missing_repos=Reinizializza tutti i repository Git mancanti per i quali esistono cambiamenti registrati esistenti 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 in Attività del Server -dashboard.current_goroutine=Goroutine Correnti -dashboard.current_memory_usage=Utilizzo di Memoria Corrente -dashboard.total_memory_allocated=Memoria Allocata Totale -dashboard.memory_obtained=Memoria Ottenuta -dashboard.pointer_lookup_times=Ricerche del Puntatore +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 Corrente -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 di Bootstrap -dashboard.stack_memory_obtained=Memoria Stack 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=Tabella di Hash del Bucket Ottenuta -dashboard.gc_metadata_obtained=Metadata della GC ottenuta -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.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_time=Pausa Totale della GC -dashboard.total_gc_pause=Pausa Totale della GC -dashboard.last_gc_pause=Ultima pausa della 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 dashboard.delete_old_system_notices=Elimina tutte le vecchie notifiche di sistema dal database -users.user_manage_panel=Gestione account utente +users.user_manage_panel=Gestici account utente users.new_account=Crea account utente users.name=Nome utente -users.full_name=Nome Completo +users.full_name=Nome completo users.activated=Attivato users.admin=Amministratore users.restricted=Limitato @@ -2546,24 +2891,24 @@ users.local=Locale users.auth_login_name=Nome utente per l'autenticazione users.password_helper=Lascia la password vuota per non modificarla. users.update_profile_success=L'account utente è stato aggiornato. -users.edit_account=Modifica account utente -users.max_repo_creation=Numero massimo di repository +users.edit_account=Modifica profilo utente +users.max_repo_creation=Numero massimo di progetti users.max_repo_creation_desc=(Inserire -1 per utilizzare il limite predefinito globale.) users.is_activated=Account utente attivato -users.prohibit_login=Disattiva login +users.prohibit_login=Disattiva accesso users.is_admin=È amministratore users.is_restricted=È limitato -users.allow_git_hook=Può creare Git Hook -users.allow_git_hook_tooltip=Git Hooks sono eseguiti come l'utente OS che esegue Forgejo e avrà lo stesso livello di accesso host. Di conseguenza, gli utenti con questo speciale privilegio Git Hook possono accedere e modificare tutti i repository Forgejo e il database utilizzato da Forgejo. Di conseguenza sono anche in grado di ottenere privilegi di amministratore Forgejo. -users.allow_import_local=Può importare repository locali +users.allow_git_hook=Può creare hook Git +users.allow_git_hook_tooltip=Gli hook Git sono eseguiti come l'utente OS che esegue Forgejo e avrà lo stesso livello di accesso host. Di conseguenza, gli utenti con questo speciale privilegio hook Git, possono accedere e modificare tutti i repository Forgejo e il database utilizzato da Forgejo. Di conseguenza sono anche in grado di ottenere privilegi di amministratore Forgejo. +users.allow_import_local=Può importare progetti locali users.allow_create_organization=Può creare organizzazioni -users.update_profile=Aggiorna account utente -users.delete_account=Elimina account utente +users.update_profile=Aggiorna profilo utente +users.delete_account=Elimina profilo utente users.cannot_delete_self=Non puoi eliminare te stesso users.still_own_repo=Questo utente possiede ancora una o più repository. Eliminare o trasferire questi repository prima di continuare. users.still_has_org=Questo utente è membro di un'organizzazione. Rimuovi l'utente da tutte le organizzazioni prima di proseguire. users.purge=Elimina Utente -users.purge_help=Eliminare forzatamente l'utente e tutti i depositi, le organizzazioni e i pacchetti di proprietà dell'utente. Tutti i commenti verranno eliminati troppo. +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 @@ -2579,26 +2924,26 @@ users.list_status_filter.not_prohibit_login=Consenti Login users.list_status_filter.is_2fa_enabled=2FA Abilitato users.list_status_filter.not_2fa_enabled=2FA Disabilitato -emails.email_manage_panel=Gestione delle Email Utente +emails.email_manage_panel=Gestisci email dell'utente emails.primary=Primario emails.activated=Attivato emails.filter_sort.email=Email emails.filter_sort.email_reverse=Email (inverso) -emails.filter_sort.name=Nome Utente +emails.filter_sort.name=Nome utente emails.filter_sort.name_reverse=Nome utente (inverso) emails.updated=Email aggiornata emails.not_updated=Impossibile aggiornare l'indirizzo email richiesto: %v emails.duplicate_active=Questo indirizzo email risulta già attivo per un altro utente. emails.change_email_header=Aggiorna proprietà email -orgs.org_manage_panel=Gestione Organizzazione +orgs.org_manage_panel=Gestisci organizzazioni orgs.name=Nome orgs.teams=Team orgs.members=Membri -orgs.new_orga=Nuova Organizzazione +orgs.new_orga=Nuova organizzazione -repos.repo_manage_panel=Gestione Repository -repos.unadopted=Depositi Non Adottati +repos.repo_manage_panel=Gestisci progetti +repos.unadopted=Progetti non adottati repos.unadopted.no_more=Nessun repository non adottato trovato repos.owner=Proprietario repos.name=Nome @@ -2609,7 +2954,7 @@ repos.forks=Fork repos.issues=Problemi repos.size=Dimensione -packages.package_manage_panel=Gestione Pacchetti +packages.package_manage_panel=Gestisci pacchetti packages.total_size=Dimensione totale: %s packages.owner=Proprietario packages.creator=Creatore @@ -2620,11 +2965,11 @@ packages.repository=Repository packages.size=Dimensione packages.published=Pubblicata -defaulthooks=Webhook predefiniti +defaulthooks=Richiami HTTP predefiniti defaulthooks.add_webhook=Aggiungi Webhook predefinito defaulthooks.update_webhook=Aggiorna Webhook predefinito -systemhooks=Webhooks di Sistema +systemhooks=Richiami HTTP di sistema systemhooks.add_webhook=Aggiungi Webhook di Sistema systemhooks.update_webhook=Aggiorna Webhook di Sistema @@ -2642,7 +2987,7 @@ auths.domain=Dominio auths.host=Host auths.port=Porta auths.bind_dn=Binda DN -auths.bind_password=Binda Password +auths.bind_password=Associa password auths.user_base=Base ricerca utente auths.user_dn=DN dell'utente auths.attribute_username=Attributo nome utente @@ -2651,19 +2996,19 @@ auths.attribute_name=Attributo nome auths.attribute_surname=Attributo cognome auths.attribute_mail=Attributo email auths.attribute_ssh_public_key=Attributo chiave SSH pubblica -auths.attribute_avatar=Attributo Avatar -auths.attributes_in_bind=Estrai Attributi dal Contesto Bind DN +auths.attribute_avatar=Attributo avatar +auths.attributes_in_bind=Estrai attributi dal contesto nind DN auths.allow_deactivate_all=Consenti un risultato di ricerca vuoto per disattivare tutti gli utenti auths.use_paged_search=Utilizza ricerca per pagina auths.search_page_size=Dimensioni pagina -auths.filter=Fitro utente -auths.admin_filter=Filtro Amministratore +auths.filter=Filtro utente +auths.admin_filter=Filtro amministratore auths.restricted_filter=Filtro riservato -auths.restricted_filter_helper=Lasciare vuoto per non impostare alcun utente come limitato. Utilizzare un asterisco ('*') per impostare tutti gli utenti che non corrispondono al filtro amministratore. +auths.restricted_filter_helper=Lascia vuoto per non impostare alcun utente come limitato. Utilizzare un asterisco ("*") per impostare tutti gli utenti che non corrispondono al filtro amministratore. auths.verify_group_membership=Verifica l'appartenenza al gruppo in LDAP (lascia vuoto il filtro per saltare) -auths.group_search_base=Ricerca Gruppo Base DN -auths.group_attribute_list_users=Gruppo Attributo Contenente Elenco Utenti -auths.user_attribute_in_group=Attributo Utente Elencato nel Gruppo +auths.group_search_base=Ricerca gruppo base DN +auths.group_attribute_list_users=Raggruppa attributo contente lista di utenti +auths.user_attribute_in_group=Attributo utente elencato nel gruppo auths.map_group_to_team=Mappa i gruppi LDAP alle squadre dell'organizzazione (lasciare vuoto il campo per saltare) auths.map_group_to_team_removal=Rimuovi gli utenti dai team sincronizzati se l'utente non appartiene al gruppo LDAP corrispondente auths.enable_ldap_groups=Abilita gruppi LDAP @@ -2672,16 +3017,16 @@ auths.smtp_auth=Tipo di autenticazione SMTP auths.smtphost=Host SMTP auths.smtpport=Porta SMTP auths.allowed_domains=Domini consentiti -auths.allowed_domains_helper=Lasciare vuoto per ammettere tutti i domini. Separare più domini con una virgola (','). +auths.allowed_domains_helper=Lasciare vuoto per ammettere tutti i domini. Separare più domini con una virgola (","). auths.skip_tls_verify=Salta verifica TLS auths.force_smtps=Forza SMTPS auths.force_smtps_helper=SMTPS è sempre utilizzato sulla porta 465. Impostalo per forzare SMTPS su altre porte. (Otherwise STARTTLS sarà utilizzato su altre porte se è supportato dall'host.) auths.helo_hostname=HELO nome dell'host auths.helo_hostname_helper=Nome host inviato con HELO. Lasciare vuoto per inviare il nome host corrente. auths.disable_helo=Disattiva HELO -auths.pam_service_name=Nome del Servizio PAM -auths.pam_email_domain=Dominio Email PAM (opzionale) -auths.oauth2_provider=OAuth2 Provider +auths.pam_service_name=Nome del servizio PAM +auths.pam_email_domain=Dominio email PAM (opzionale) +auths.oauth2_provider=Fornitore OAuth2 auths.oauth2_icon_url=URL icona auths.oauth2_clientID=ID Client (Chiave) auths.oauth2_clientSecret=Segreto del client @@ -2694,15 +3039,15 @@ auths.oauth2_emailURL=URL email auths.skip_local_two_fa=Salta 2FA locale auths.skip_local_two_fa_helper=Lasciare l'azzeramento significa che gli utenti locali con il set 2FA dovranno ancora passare 2FA per accedere auths.oauth2_tenant=Comproprietà -auths.oauth2_scopes=Ambiti Aggiuntivi -auths.oauth2_required_claim_name=Nome Richiesto +auths.oauth2_scopes=Ambiti aggiuntivi +auths.oauth2_required_claim_name=Nome richiesto auths.oauth2_required_claim_name_helper=Imposta questo nome per limitare il login da questa fonte agli utenti con un reclamo con questo nome -auths.oauth2_required_claim_value=Valore Richiesto +auths.oauth2_required_claim_value=Valore richiesto auths.oauth2_required_claim_value_helper=Imposta questo valore per limitare il login da questa fonte agli utenti con un reclamo con questo nome e valore auths.oauth2_group_claim_name=Riscatta nome che fornisce nomi di gruppo per questa fonte (facoltativo) auths.oauth2_admin_group=Valore del reclamo di gruppo per gli utenti amministratori. (Opzionale - richiede il nome della richiesta sopra) auths.oauth2_restricted_group=Valore di reclamo di gruppo per utenti ristretti. (Facoltativo - richiede il nome di reclamo sopra) -auths.enable_auto_register=Abilitare Registrazione Automatica +auths.enable_auto_register=Abilita registrazione automatica auths.sspi_auto_create_users=Crea automaticamente gli utenti auths.sspi_auto_create_users_helper=Permetti al metodo di autenticazione SSPI di creare automaticamente nuovi account per gli utenti che accedono per la prima volta auths.sspi_auto_activate_users=Attiva automaticamente gli utenti @@ -2715,8 +3060,8 @@ auths.sspi_default_language=Lingua predefinita dell'utente auths.sspi_default_language_helper=Lingua predefinita per gli utenti creati automaticamente dal metodo di autenticazione SSPI. Lascia vuoto se preferisci che la lingua venga rilevata automaticamente. auths.tips=Consigli auths.tips.oauth2.general=Autenticazione OAuth2 -auths.tip.oauth2_provider=OAuth2 Provider -auths.tip.bitbucket=Registra un nuovo cliente OAuth su https://bitbucket.org/account/user//oauth-consumers/new e aggiungi il permesso 'Account' - 'Read' +auths.tip.oauth2_provider=Fornitore OAuth2 +auths.tip.bitbucket=Registra un nuovo cliente OAuth su https://bitbucket.org/account/user//oauth-consumers/new e aggiungi il permesso "Account" - "Read" auths.tip.nextcloud=`Registra un nuovo OAuth sulla tua istanza utilizzando il seguente menu "Impostazioni -> Sicurezza -> OAuth 2.0 client"` auths.tip.dropbox=Crea una nuova applicazione su https://www.dropbox.com/developers/apps auths.tip.facebook=`Registra una nuova applicazione su https://developers.facebook.com/apps e aggiungi il prodotto "Facebook Login"` @@ -2739,33 +3084,33 @@ auths.still_in_used=La fonte di autenticazione è ancora in uso. Convertire o el auths.deletion_success=La fonte d'autenticazione è stata eliminata. auths.login_source_of_type_exist=Esiste già una fonte di autenticazione di questo tipo. -config.server_config=Configurazione Server -config.app_name=Titolo del Sito +config.server_config=Configurazione server +config.app_name=Titolo del sito config.app_ver=Versione Forgejo config.app_url=URL di base di Forgejo config.custom_conf=Percorso file di configurazione -config.custom_file_root_path=Percorso Root File Personalizzato -config.domain=Dominio Server +config.custom_file_root_path=Percorso root file personalizzato +config.domain=Dominio server config.offline_mode=Modalità locale -config.disable_router_log=Disattivare Log del Router -config.run_user=Esegui come Nome utente -config.run_mode=Modalità Esecuzione +config.disable_router_log=Disattivare log del router +config.run_user=Nome utente con cui eseguire +config.run_mode=Modalità di esecuzione config.git_version=Versione Git -config.repo_root_path=Percorso radice del Repository -config.lfs_root_path=Percorso file LFS +config.repo_root_path=Percorso radice del progetto +config.lfs_root_path=Percorso radice LFS config.log_file_root_path=Percorso dei log -config.script_type=Tipo di Script -config.reverse_auth_user=Autenticazione Utente Inversa +config.script_type=Tipo di script +config.reverse_auth_user=Autenticazione utente inversa config.ssh_config=Configurazione SSH config.ssh_enabled=Attivo -config.ssh_start_builtin_server=Usa il server integrato -config.ssh_domain=Dominio Server Ssh +config.ssh_start_builtin_server=Usa il server incorporato +config.ssh_domain=Dominio server SSH config.ssh_port=Porta config.ssh_listen_port=Porta in ascolto -config.ssh_root_path=Percorso Root +config.ssh_root_path=Percorso radice config.ssh_key_test_path=Percorso chiave di test -config.ssh_keygen_path=Percorso Keygen ('ssh-keygen') +config.ssh_keygen_path=Percorso keygen ("ssh-keygen") config.ssh_minimum_key_size_check=Verifica delle dimensioni minime della chiave config.ssh_minimum_key_sizes=Dimensioni minime della chiave @@ -2774,7 +3119,7 @@ config.lfs_enabled=Abilitato config.lfs_content_path=Percorso del contenuto LFS config.lfs_http_auth_expiry=Scadenza autenticazione LFS HTTP -config.db_config=Configurazione Database +config.db_config=Configurazione base di dati config.db_type=Tipo config.db_host=Host config.db_name=Nome @@ -2783,31 +3128,31 @@ config.db_schema=Schema config.db_ssl_mode=SSL config.db_path=Percorso -config.service_config=Configurazione Servizio -config.register_email_confirm=Richiedere la conferma Email per registrarsi -config.disable_register=Disattiva Self-Registration +config.service_config=Configurazione servizio +config.register_email_confirm=Richiedi conferma email per registrarsi +config.disable_register=Disattiva auto registrazione config.allow_only_internal_registration=Consenti la registrazione solo tramite Forgejo stessa config.allow_only_external_registration=Attiva la registrazione solo tramite servizi esterni -config.enable_openid_signup=Attiva OpenID Self-Registration +config.enable_openid_signup=Attiva auto registrazione OpenID config.enable_openid_signin=Attiva l'accesso tramite OpenID -config.show_registration_button=Mostra Pulsane Registrazione -config.require_sign_in_view=Richiedi l'accesso per visualizzare le pagine -config.mail_notify=Attila le notifiche Email +config.show_registration_button=Mostra tasto per la registrazione +config.require_sign_in_view=Richiedi l'accesso per visualizzare il contenuto +config.mail_notify=Attila le notifiche email config.enable_captcha=Attiva CAPTCHA -config.active_code_lives=Attiva Vita del Codice -config.reset_password_code_lives=Recupera il codice di scadenza del tempo del tuo account -config.default_keep_email_private=Nascondi Indirizzo Email di Default -config.default_allow_create_organization=Consenti la Creazione di Organizzazioni di Default +config.active_code_lives=Attiva scadenza del codice +config.reset_password_code_lives=Tempo di scadenza per il codice di recupero +config.default_keep_email_private=Nascondi indirizzo email in modo predefinito +config.default_allow_create_organization=Consenti la creazione di organizzazioni in modo predefinito config.enable_timetracking=Abilita il cronografo -config.default_enable_timetracking=Attiva il cronografo di Default +config.default_enable_timetracking=Attiva il cronografo di default config.default_allow_only_contributors_to_track_time=Consenti soltanto ai contributori di utilizzare il cronografo config.no_reply_address=Dominio email nascosto config.default_visibility_organization=Visibilità predefinita per le nuove organizzazioni -config.default_enable_dependencies=Abilita i problemi delle dipendenze di default +config.default_enable_dependencies=Abilita le segnalazioni delle dipendenze in modo predefinito -config.webhook_config=Configurazione Webhook +config.webhook_config=Configurazione richiami HTTP config.queue_length=Lunghezza della coda -config.deliver_timeout=Tempo Limite di Consegna +config.deliver_timeout=Tempo limite di consegna config.skip_tls_verify=Salta autenticazione TLS config.mailer_config=Configurazione Mailer @@ -2829,46 +3174,46 @@ config.send_test_mail=Invia email di prova config.oauth_config=Configurazione OAuth config.oauth_enabled=Attivo -config.cache_config=Configurazione Cache -config.cache_adapter=Adattatore Cache -config.cache_interval=Intervallo Cache -config.cache_conn=Connessione Cache -config.cache_item_ttl=Cache degli elementi TTL +config.cache_config=Configurazione cache +config.cache_adapter=Adattatore cache +config.cache_interval=Intervallo cache +config.cache_conn=Connessione cache +config.cache_item_ttl=TTL degli elementi della cache -config.session_config=Configurazione Sessione -config.session_provider=Fornitore Sessione -config.provider_config=Impostazioni Provider -config.cookie_name=Nome del Cookie -config.gc_interval_time=Intervallo di tempo della GC -config.session_life_time=Durata Sessione +config.session_config=Configurazione sessione +config.session_provider=Fornitore sessione +config.provider_config=Impostazioni fornitore +config.cookie_name=Nome del cookie +config.gc_interval_time=Intervallo di tempo del GC +config.session_life_time=Durata sessione config.https_only=Solo HTTPS -config.cookie_life_time=Durata Cookie +config.cookie_life_time=Durata cookie -config.picture_config=Configurazione Immagine profilo e Avatar +config.picture_config=Configurazione Immagine profilo e avatar config.picture_service=Servizio foto config.disable_gravatar=Disabilita Gravatar -config.enable_federated_avatar=Attiva i Federated Avatar +config.enable_federated_avatar=Attiva gli avatar federati config.git_config=Configurazione Git -config.git_disable_diff_highlight=Disattiva evidenziatore Diff Syntax -config.git_max_diff_lines=Numero massimo di righe di diff (per singolo file) -config.git_max_diff_line_characters=Numero massimo di caratteri di diff (per singola riga) -config.git_max_diff_files=Numero massimo di file diff mostrati +config.git_disable_diff_highlight=Disattiva sintassi evidenziate per le differenze +config.git_max_diff_lines=Numero massimo di righe delle differenze (per singolo file) +config.git_max_diff_line_characters=Numero massimo di caratteri delle differenze (per singola riga) +config.git_max_diff_files=Numero massimo di file differenze mostrati config.git_gc_args=Parametri GC config.git_migrate_timeout=Timeout per la migrazione -config.git_mirror_timeout=Timeout per l'aggiornamento del mirror +config.git_mirror_timeout=Timeout per l'aggiornamento dello specchio config.git_clone_timeout=Tempo limite operazione di clone -config.git_pull_timeout=Tempo limite operazione di pull +config.git_pull_timeout=Tempo limite operazione di prelievo config.git_gc_timeout=Timeout operazione GC -config.log_config=Configurazione Log +config.log_config=Configurazione log config.disabled_logger=Disabilitato config.access_log_mode=Modalità log di accesso config.xorm_log_sql=Log SQL -monitor.cron=Incarichi Cron +monitor.cron=Compiti cron monitor.name=Nome monitor.schedule=Agenda monitor.next=La Prossima Volta @@ -2890,17 +3235,17 @@ monitor.queue=Coda: %s monitor.queue.name=Nome monitor.queue.type=Tipo monitor.queue.exemplar=Tipo di esemplare -monitor.queue.numberworkers=Numero di workers -monitor.queue.maxnumberworkers=Massimo numero di Workers +monitor.queue.numberworkers=Numero di lavoratori +monitor.queue.maxnumberworkers=Massimo numero di lavoratori monitor.queue.numberinqueue=Numero in coda -monitor.queue.settings.title=Impostazioni pool +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 +monitor.queue.settings.submit=Aggiorna impostazioni +monitor.queue.settings.changed=Impostazioni aggiornate -notices.system_notice_list=Avvisi di Sistema +notices.system_notice_list=Avvisi di sistema notices.view_detail_header=Visualizza dettagli dell'avviso notices.select_all=Seleziona tutto notices.deselect_all=Deseleziona tutto @@ -2920,6 +3265,62 @@ users.reserved = Riservato notices.operations = Operazioni users.bot = Bot config.send_test_mail_submit = Invia +dashboard.cron.cancelled = Cron: %[1]s cancellato: %[3]s +dashboard.new_version_hint = Forgejo %s è ora disponibile, stai eseguendo %s. Controlla il blog per ulteriori dettagli. +dashboard.sync_repo_branches = Sincronizza rami omessi dai dati Git nella base di dati +dashboard.gc_lfs = Oggetti meta LFS riciclati +dashboard.sync_tag.started = Sincronizzazione delle etichette iniziata +self_check = Auto controllo +identity_access = Identità e accesso +assets = Risorse codice +settings = Impostazioni amministratore +dashboard.task.cancelled = Compito: %[1]s cancellato: %[3]s +dashboard.sync_repo_tags = Sincronizza etichette dai dati Git alla base di dati +users.new_success = Il profilo utente "%s" è stato creato. +users.still_own_packages = Questo utente possiede ancora uno o più pacchetti, elimina questi pacchetti prima. +auths.oauth2_map_group_to_team = Associa gruppi reclamati a squadre di organizzazioni. (opzionale - richiede il nome reclamo sopra) +auths.tip.gitea = Registra una nuova applicazione OAuth2. La guida può essere trovata a https://docs.gitea.com/development/oauth2-provider +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 +dashboard.stop_endless_tasks = Termina compiti senza fine +dashboard.start_schedule_tasks = Inizia pianificazione compiti +dashboard.cancel_abandoned_jobs = Cancella lavori abbandonati +auths.login_source_exist = La fonte di autenticazione "%s" esiste già. +auths.invalid_openIdConnectAutoDiscoveryURL = URL di auto scoperta invalido (questo deve essere un URL valido che inizia con http:// o con https://) +config.access_log_template = Modello log di accesso +config.set_setting_failed = Impossibile impostare il parametro %s +auths.unable_to_initialize_openid = Impossibile inizializzare OpenID Connect Provider: %s +dashboard.stop_zombie_tasks = Termina compiti zombie +dashboard.sync_branch.started = Sincronizzazione dei rami iniziata +dashboard.rebuild_issue_indexer = Ricostruzione dell'indicizzatore delle segnalazioni +emails.change_email_text = Sei sicuro di voler aggiornare questo indirizzo email? +repos.lfs_size = Dimensione LFS +packages.unreferenced_size = Dimensione senza riferimenti: %s +packages.cleanup.success = Dati scaduti puliti correttamente +defaulthooks.desc = I richiami HTTP fanno automaticamente richieste POST al server innescati da alcuni eventi di Forgejo. I richiami HTTP definiti qui sono predefiniti e saranno copiati in tutti i nuovi progetti. Leggi di più nella guida sui richiami HTTP. +auths.oauth2_map_group_to_team_removal = Rimuovi utenti dalle squadre sincronizzate se l'utente non appartiene al gruppo corrispondente. +auths.tips.oauth2.general.tip = Quando si registra una nuova autenticazione OAuth2, l'URL di richiamata/reindirizzamento dovrebbe essere: +config.logger_name_fmt = Logger: %s +systemhooks.desc = I richiami HTTP fanno automaticamente richieste POST al server innescati da alcuni eventi di Forgejo. I richiami HTTP definiti qui agiranno su tutti i progetti nel sistema, quindi considera li implicazioni sulle prestazioni che questi possono avere. Leggi di più nella guida sui richiami HTTP. +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 = Nessun problema trovato. +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 "gitea 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. +self_check.database_fix_mssql = Gli utenti MSSQL possono provare a risolvere il problema tramite SQL con "ALTER ... COLLATE ..." manualmente, per il momento. [action] @@ -2952,6 +3353,8 @@ review_dismissed_reason=Motivo: create_branch=ha creato il ramo %[3]s in %[4]s starred_repo=ha salvato come preferito %[2]s watched_repo=ha iniziato a guardare %[2]s +commit_repo = immesso a %[3]s a %[4]s +auto_merge_pull_request = `richiesta di modifica %[3]s#%[2]s fusa automaticamente` [tool] now=ora @@ -2990,6 +3393,8 @@ 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 @@ -2998,7 +3403,7 @@ 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 all'account del committer +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. @@ -3026,7 +3431,7 @@ dependencies=Dipendenze keywords=Parole Chiave details=Dettagli details.author=Autore -details.project_site=Sito Del Progetto +details.project_site=Sito del progetto details.license=Licenza assets=Asset versions=Versioni @@ -3103,6 +3508,32 @@ 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 progetto +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 progetto +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 progetto 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 progetto +go.install = Installa il pacchetto dalla riga di comando: +rpm.distros.suse = sulle distribuzioni basate su SUSE +rpm.repository = Informazioni sul progetto +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 $progetto 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 [secrets] secrets = Segreti diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index fcccac21cc..1aa00440d9 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -39,12 +39,12 @@ passcode=パスコード webauthn_insert_key=セキュリティキーを挿入 webauthn_sign_in=セキュリティキーのボタンを押してください。セキュリティキーにボタンが無い場合は、挿入しなおしてください。 -webauthn_press_button=セキュリティキーのボタンを押してください... +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_insecure=WebAuthn はセキュアな接続のみをサポートしています。HTTP 経由でテストする場合は、"localhost" または "127.0.0.1" のオリジンが使用できます webauthn_error_unable_to_process=サーバーがリクエストを処理できませんでした。 webauthn_error_duplicated=このリクエストに対しては、許可されていないセキュリティキーです。 キーが未登録であることを確認してください。 webauthn_error_empty=このキーに名前を設定する必要があります。 @@ -88,8 +88,8 @@ rerun_all=すべてのジョブを再実行 save=保存 add=追加 add_all=すべて追加 -remove=除去 -remove_all=すべて除去 +remove=削除 +remove_all=すべて削除 remove_label_str=アイテム「%s」を削除 edit=編集 view=表示 @@ -142,6 +142,19 @@ confirm_delete_selected=選択したすべてのアイテムを削除してよ name=名称 value=値 +filter.is_archived = アーカイブ +filter.not_archived = 非アーカイブ +filter.is_fork = フォーク +filter.is_mirror = ミラー +filter.not_mirror = 非ミラー +filter.is_template = テンプレート +filter = フィルター +filter.not_fork = 非フォーク +filter.clear = フィルタをクリアする +filter.public = 公開 +filter.private = 非公開 +toggle_menu = トグルメニュー +filter.not_template = テンプレートではない [aria] navbar=ナビゲーションバー @@ -150,8 +163,8 @@ footer.software=ソフトウェアについて footer.links=リンク [heatmap] -number_of_contributions_in_the_last_12_months=過去 12 か月間で %s 個の貢献 -no_contributions=貢献なし +number_of_contributions_in_the_last_12_months=過去 12 か月間で %s 件の貢献 +contributions_zero=貢献なし less=少 more=多 @@ -225,7 +238,7 @@ err_admin_name_pattern_not_allowed=管理者のユーザー名が不正です。 err_admin_name_is_invalid=管理者のユーザー名が不正です general_title=基本設定 -app_name=サイトタイトル +app_name=インスタンスの名前 app_name_helper=企業名をここに入れることができます。 repo_path=リポジトリのルートパス repo_path_helper=リモートGitリポジトリはこのディレクトリに保存されます。 @@ -237,7 +250,7 @@ domain=サーバードメイン domain_helper=サーバーのドメインまたはホストアドレス。 ssh_port=SSHサーバーのポート ssh_port_helper=SSHサーバーが使うポート番号。 空の場合はSSHサーバーを無効にします。 -http_port=Forgejo HTTPポート +http_port=HTTPポート http_port_helper=ForgejoのWebサーバーが使うポート番号。 app_url=ForgejoのベースURL app_url_helper=HTTP(S)のクローンURLとメール通知で使うベースアドレス。 @@ -280,7 +293,7 @@ confirm_password=パスワード確認 admin_email=メールアドレス install_btn_confirm=Forgejoをインストール test_git_failed="git"コマンドが確認できません: %v -sqlite3_not_available=ForgejoのこのバージョンはSQLite3をサポートしていません。 公式のバイナリ版を %s からダウンロードしてください。 ("gobuild"版でないもの) +sqlite3_not_available=ForgejoのこのバージョンはSQLite3をサポートしていません。 公式のバイナリ版を %s からダウンロードしてください ("gobuild"版でないもの)。 invalid_db_setting=データベース設定が無効です: %v invalid_db_table=データベーステーブルの "%s" が無効です: %v invalid_repo_path=リポジトリのルートパスが無効です: %v @@ -307,6 +320,9 @@ enable_update_checker_helper=gitea.ioに接続して定期的に新しいバー env_config_keys=環境設定 env_config_keys_prompt=以下の環境変数も設定ファイルに適用されます: allow_dots_in_usernames = ユーザー名にドットを使用できるようにします。既存のアカウントには影響しません。 +smtp_from_invalid = メール送信者のアドレスが無効です +enable_update_checker_helper_forgejo = Forgejoの最新バージョンを、release.forgejo.orgのDNSのTXTレコードを定期的に参照して取得します。 +config_location_hint = この設定は次に保存されます: [home] uname_holder=ユーザー名またはメールアドレス @@ -398,7 +414,7 @@ twofa_scratch_used=あなたはスクラッチコードを使用しました。 twofa_passcode_incorrect=パスコードが正しくありません。デバイスを紛失した場合は、スクラッチコードを使ってサインインしてください。 twofa_scratch_token_incorrect=スクラッチコードが正しくありません。 login_userpass=サインイン -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=新規アカウント登録 oauth_signup_title=新規アカウントの仕上げ oauth_signup_submit=アカウント登録完了 @@ -602,6 +618,8 @@ admin_cannot_delete_self = 管理者である場合、自分自身を削除す username_error_no_dots = `英数字 (「0-9」、「a-z」、「A-Z」)、ダッシュ (「-」)、およびアンダースコア (「_」) のみを含めることができます。英数字以外の文字で開始または終了することはできず、連続した英数字以外の文字も禁止されています。` admin_cannot_delete_self=あなたが管理者である場合、自分自身を削除することはできません。最初に管理者権限を削除してください。 +unset_password = ログインしたユーザーにパスワードが設定されていません。 +unsupported_login_type = このログインタイプでは、アカウントの削除はサポートされていません。 [user] change_avatar=アバターを変更… @@ -634,6 +652,7 @@ block_user.detail_3 = このユーザーはあなたをコラボレーターと block_user = ユーザーをブロック unblock = ブロックを解除 block = ブロック +block_user.detail = このユーザーをブロックした場合、下記の事などが起こります。例えば: [settings] profile=プロフィール @@ -1199,9 +1218,9 @@ file_view_raw=Rawデータを見る file_permalink=パーマリンク file_too_large=このファイルは大きすぎるため、表示できません。 invisible_runes_header=このファイルには不可視のUnicode文字が含まれています -invisible_runes_description=このファイルには人間が識別できない不可視のUnicode文字が含まれており、コンピューターによって特殊な処理が行われる可能性があります。 それが意図的なものと考えられる場合は、この警告を無視して構いません。 不可視文字を表示するにはエスケープボタンを使用します。 +invisible_runes_description=`このファイルには人間が識別できない不可視のUnicode文字が含まれており、コンピューターによって特殊な処理が行われる可能性があります。 それが意図的なものと考えられる場合は、この警告を無視して構いません。 不可視文字を表示するにはエスケープボタンを使用します。` ambiguous_runes_header=このファイルには曖昧(ambiguous)なUnicode文字が含まれています -ambiguous_runes_description=このファイルには、他の文字と見間違える可能性があるUnicode文字が含まれています。 それが意図的なものと考えられる場合は、この警告を無視して構いません。 それらの文字を表示するにはエスケープボタンを使用します。 +ambiguous_runes_description=`このファイルには、他の文字と見間違える可能性があるUnicode文字が含まれています。 それが意図的なものと考えられる場合は、この警告を無視して構いません。 それらの文字を表示するにはエスケープボタンを使用します。` invisible_runes_line=`この行には不可視のUnicode文字があります` ambiguous_runes_line=`この行には曖昧(ambiguous)なUnicode文字があります` ambiguous_character=`%[1]c [U+%04[1]X] は %[2]c [U+%04[2]X] と混同するおそれがあります` @@ -1534,7 +1553,7 @@ issues.role.member_helper=このユーザーはこのリポジトリを所有し issues.role.collaborator=共同作業者 issues.role.collaborator_helper=このユーザーはリポジトリ上で共同作業するように招待されています。 issues.role.first_time_contributor=初めての貢献者 -issues.role.first_time_contributor_helper=これは、このユーザーのリポジトリへの最初の貢献です。 +issues.role.first_time_contributor_helper=これは、このユーザーによるリポジトリへの最初の貢献です。 issues.role.contributor=貢献者 issues.role.contributor_helper=このユーザーは以前にリポジトリにコミットしています。 issues.re_request_review=レビューを再依頼 @@ -1741,8 +1760,8 @@ pulls.nothing_to_compare=同じブランチ同士のため、 プルリクエス pulls.nothing_to_compare_and_allow_empty_pr=これらのブランチは内容が同じです。 空のプルリクエストになります。 pulls.has_pull_request=`同じブランチのプルリクエストはすでに存在します: %[2]s#%[3]d` pulls.create=プルリクエストを作成 -pulls.title_desc=が %[2]s から %[3]s への %[1]d コミットのマージを希望しています -pulls.merged_title_desc=が %[1]d 個のコミットを %[2]s から %[3]s へマージ %[4]s +pulls.title_desc_few=が %[2]s から %[3]s への %[1]d コミットのマージを希望しています +pulls.merged_title_desc_few=が %[1]d 個のコミットを %[2]s から %[3]s へマージ %[4]s pulls.change_target_branch_at=`がターゲットブランチを %s から %s に変更 %s` pulls.tab_conversation=会話 pulls.tab_commits=コミット @@ -2035,7 +2054,8 @@ settings.mirror_settings.docs.more_information_if_disabled=プッシュミラー settings.mirror_settings.docs.doc_link_title=リポジトリをミラーリングするには? settings.mirror_settings.docs.doc_link_pull_section=ドキュメントの「リモートリポジトリからのプル」セクション。 settings.mirror_settings.docs.pulling_remote_title=リモートリポジトリからのプル -settings.mirror_settings.mirrored_repository=同期するリポジトリ +settings.mirror_settings.mirrored_repository=ミラー元のリポジトリ +settings.mirror_settings.pushed_repository=プッシュ先のリポジトリ settings.mirror_settings.direction=方向 settings.mirror_settings.direction.pull=プル settings.mirror_settings.direction.push=プッシュ @@ -2471,7 +2491,7 @@ diff.too_many_files=変更されたファイルが多すぎるため、一部の diff.show_more=さらに表示 diff.load=差分を読み込み diff.generated=generated -diff.vendored=vendored +diff.vendored=vendor済み diff.comment.add_line_comment=行コメントを追加 diff.comment.placeholder=コメントを残す diff.comment.markdown_info=Markdownによる書式設定をサポートしています。 @@ -2601,6 +2621,44 @@ error.csv.invalid_field_count=このファイルは %d 行目のフィールド admin.enabled_flags = このリポジトリで有効になっているフラグたち: clone_in_vscodium = VSCodiumでcloneする desc.sha256 = SHA256 +wiki.cancel = キャンセル +activity.navbar.contributors = 貢献者 +contributors.contribution_type.filter_label = 貢献の種類: +activity.navbar.recent_commits = 最近の貢献者 +admin.failed_to_replace_flags = リポジトリのフラグを変更するのに失敗しました +file_follow = シンボリックリンクを辿る +mirror_sync = 同期済み +generated = 生成された +editor.invalid_commit_mail = コミットを作成するためには無効なメールアドレスです。 +issues.blocked_by_user = あなたはこのリポジトリの所有者からブロックされているため、Issueを作成できません。 +pulls.nothing_to_compare_have_tag = 選択されたブランチまたはタグは同一です。 +pulls.blocked_by_user = あなたはこのリポジトリの所有者からブロックされているため、プルリクエストを作成できません。 +rss.must_be_on_branch = RSSフィードを見るためには、ブランチを閲覧する必要があります。 +migrate.forgejo.description = codeberge.orgまたは他のインスタンスからデータを移行する。 +commits.browse_further = もっと見る +issues.comment.blocked_by_user = あなたはこのリポジトリの所有者か、Issueの投稿者からブロックされているため、このIssueにコメントできません。 +pulls.reopen_failed.head_branch = ブランチがもう存在しないため、このプルリクエストはreopenできません。 +pulls.reopen_failed.base_branch = ベースブランチがもう存在しないため、このプルリクエストは再開できません。 +settings.units.overview = 概要 +settings.new_owner_blocked_doer = 新しい所有者が、あなたをブロックしています。 +settings.enter_repo_name = 所有者とリポジトリ名を、以下のように正確に衆力してください: +settings.wiki_rename_branch_main = wikiのブランチ名を正規化する +settings.wiki_rename_branch_main_desc = wikiによって内部的に使われているブランチ名を "%s" に変更します。これは恒久的で元に戻すことはできません。 +contributors.contribution_type.additions = 追加 +vendored = vendor済み +pulls.commit_ref_at = `このプルリクエストを言及するコミット %[2]s` +pulls.fast_forward_only_merge_pull_request = Fast-forwardのみ +admin.manage_flags = フラグ管理 +admin.update_flags = フラグを更新 +admin.flags_replaced = リポジトリのフラグは更新されました +commits.renamed_from = %sから名前を変更 +pulls.made_using_agit = Agit +pulls.agit_explanation = Agitによるワークフローを作成します。Agitでは、貢献者は変更をforkしたりブランチを作るのではなく、"git push"して提案します。 +contributors.contribution_type.deletions = 削除 +settings.units.add_more = さらに... +settings.wiki_globally_editable = 誰にでもWikiの編集を許す +settings.confirmation_string = 確認 +settings.wiki_rename_branch_main_notices_1 = この操作は 取り消しできません 。 [graphs] @@ -3574,6 +3632,8 @@ runs.actors_no_select=すべてのアクター runs.status_no_select=すべてのステータス runs.no_results=一致する結果はありません。 runs.no_workflows=ワークフローはまだありません。 +runs.no_workflows.quick_start=Gitea Actions の始め方がわからない? ではクイックスタートガイドをご覧ください。 +runs.no_workflows.documentation=Gitea Actions の詳細については、ドキュメントを参照してください。 runs.no_runs=ワークフローはまだ実行されていません。 runs.empty_commit_message=(空のコミットメッセージ) diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index 89ea2f3335..fa6df2d2a1 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -251,7 +251,7 @@ twofa_scratch_used=스크래치 코드를 사용하셨습니다. 이중인증 twofa_passcode_incorrect=패스코드가 맞지 않습니다. 기기를 잘못 등록 한 경우, 스크래치 코드를 이용해 로그인 하십시오. twofa_scratch_token_incorrect=스크래치 코드가 올바르지 않습니다. login_userpass=로그인 -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=새 계정 등록하기 oauth_signup_submit=등록 완료 oauth_signin_tab=기존 계정으로 연결하기 @@ -847,8 +847,8 @@ pulls.compare_compare=다음으로부터 풀 pulls.filter_branch=Filter Branch pulls.no_results=결과 없음 pulls.create=풀 리퀘스트 생성 -pulls.title_desc="%[2]s 에서 %[3]s 로 %[1]d commits 를 머지하려 합니다" -pulls.merged_title_desc=%[2]s 에서 %[3]s 로 %[1]d commits 를 머지했습니다 %[4]s +pulls.title_desc_few="%[2]s 에서 %[3]s 로 %[1]d commits 를 머지하려 합니다" +pulls.merged_title_desc_few=%[2]s 에서 %[3]s 로 %[1]d commits 를 머지했습니다 %[4]s pulls.tab_conversation=대화 pulls.tab_commits=커밋 pulls.tab_files=파일 변경됨 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 718f3dc9a4..ce8f05e4b5 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -150,7 +150,7 @@ footer.links=Saites [heatmap] number_of_contributions_in_the_last_12_months=%s darbības pēdējo 12 mēnešu laikā -no_contributions=Nav aktivitātes +contributions_zero=Nav aktivitātes less=Mazāk more=Vairāk @@ -395,7 +395,7 @@ twofa_scratch_used=Vienreizējais kods tika izmantots. Notika pārvirzīšana uz twofa_passcode_incorrect=Piekļuves kods nav pareizs. Ja esat pazaudējis ierīci, izmantojiet vienreizējo kodu, lai pieteiktos. twofa_scratch_token_incorrect=Ievadīts nepareizs vienreizējais kods. login_userpass=Pieteikties -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Reģistrēt jaunu kontu oauth_signup_title=Pabeigt konta veidošanu oauth_signup_submit=Pabeigt reģistrāciju @@ -1713,8 +1713,8 @@ pulls.nothing_to_compare=Nav ko salīdzināt, jo bāzes un salīdzināmie atzari pulls.nothing_to_compare_and_allow_empty_pr=Šie atzari ir vienādi. Izveidotais izmaiņu pieprasījums būs tukšs. pulls.has_pull_request=`Izmaiņu pieprasījums starp šiem atzariem jau eksistē: %[2]s#%[3]d` pulls.create=Izveidot izmaiņu pieprasījumu -pulls.title_desc=vēlas sapludināt %[1]d revīzijas no %[2]s uz %[3]s -pulls.merged_title_desc=sapludināja %[1]d revīzijas no %[2]s uz %[3]s %[4]s +pulls.title_desc_few=vēlas sapludināt %[1]d revīzijas no %[2]s uz %[3]s +pulls.merged_title_desc_few=sapludināja %[1]d revīzijas no %[2]s uz %[3]s %[4]s pulls.change_target_branch_at=`nomainīja mērķa atzaru no %s uz %s %s` pulls.tab_conversation=Saruna pulls.tab_commits=Revīzijas diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 4d494a6d8b..95d370661d 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -143,6 +143,18 @@ unpin = Ontpinnen remove_label_str = Verwijder punt "%s" confirm_delete_artifact = Weet u zeker dat u het artefact "%s" wilt verwijderen? toggle_menu = Menu schakelen +filter.clear = Filter wissen +filter.is_archived = Gearchiveerd +filter.is_fork = Geforkt +filter.not_fork = Niet geforkt +filter.is_mirror = Gespiegeld +filter.not_mirror = Niet gespiegeld +filter.is_template = Sjabloon +filter.not_template = Geen sjabloon +filter.public = Publiek +filter.private = Privé +filter = Filter +filter.not_archived = Niet gearchiveerd [aria] navbar = Navigatiebalk @@ -152,7 +164,7 @@ footer.links = Verwijzingen [heatmap] number_of_contributions_in_the_last_12_months = %s contributies in de laatste 12 maanden -no_contributions = Geen contributies +contributions_zero = Geen contributies less = Minder more = Meer @@ -225,41 +237,41 @@ err_admin_name_is_reserved=Gebruikersnaam van beheerder is ongeldig, gebruikersn err_admin_name_pattern_not_allowed=Gebruikersnaam van beheerder is ongeldig, de gebruikersnaam is gereserveerd err_admin_name_is_invalid=Gebruikersnaam van beheerder is ongeldig -general_title=Algemene Instellingen -app_name=Naam site +general_title=Algemene instellingen +app_name=Instantienaam app_name_helper=U kan de naam van uw bedrijf hier invullen. -repo_path=Repositories basis map +repo_path=Repository hoofdpad repo_path_helper=Externe git repositories worden opgeslagen in deze map. lfs_path=Git LFS root pad lfs_path_helper=Bestanden bijgehouden door Git LFS zullenworden opgeslagen in deze map. Laat leeg om uit te schakelen. -run_user=Uitvoeren als gebruiker -domain=Server Domein +run_user=Gebruiker om als uit te voeren +domain=Serverdomein domain_helper=Domein of hostadres voor de server. ssh_port=SSH server-poort -ssh_port_helper=Nummer van de poort die uw SSH-server gebruikt. Laat dit veld leeg om de SSH functie uit te schakelen. -http_port=Forgejo HTTP-poort -http_port_helper=De poort waar de web server van Forgejo naar gaat luisteren. -app_url=Forgejo base URL +ssh_port_helper=Poortnummer dat zal worden gebruikt door de SSH-server. Leeg laten om SSH-server uit te schakelen. +http_port=HTTP luisterpoort +http_port_helper=Poortnummer dat zal worden gebruikt door de Forgejo webserver. +app_url=Basis URL app_url_helper=Basisadres voor HTTP(S) kloon URL's en e-mailmeldingen. log_root_path=Log-pad log_root_path_helper=Logboekbestanden worden geschreven naar deze map. optional_title=Optionele instellingen email_title=E-mail instellingen -smtp_addr=SMTP Host -smtp_port=SMTP Poort +smtp_addr=SMTP-host +smtp_port=SMTP-poort smtp_from=E-mails versturen als smtp_from_helper=E-mailadres dat Forgejo gaat gebruiken. Voer een gewoon e-mailadres in of gebruik de "Naam" -indeling. mailer_user=SMTP gebruikersnaam mailer_password=SMTP wachtwoord register_confirm=E-mailbevestiging vereist bij registreren mail_notify=Activeer e-mailnotificaties -server_service_title=Server en Third-Party Service-instellingen +server_service_title=Server en service-instellingen van derden offline_mode=Lokale modus inschakelen offline_mode_popup=Schakel third-party content uit en gebruik alleen lokale middelen. disable_gravatar=Gravatar uitschakelen disable_gravatar_popup=Gravatar en derden avatar bronnen uitschakelen. Een standaard avatar zal worden gebruikt, tenzij een gebruiker een lokale avatar uploadt. -federated_avatar_lookup=Federated Avatars toestaan +federated_avatar_lookup=Federated avatars toestaan federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar. disable_registration=Schakel zelf registratie uit disable_registration_popup=Schakel zelfregistratie uit, alleen admins kunnen accounts maken. @@ -270,7 +282,7 @@ openid_signup=OpenID zelf-registratie inschakelen openid_signup_popup=OpenID zelfregistratie inschakelen. enable_captcha=Registratie CAPTCHA inschakelen enable_captcha_popup=Vereis captcha validatie voor zelf-registratie van gebruiker. -require_sign_in_view=Vereis inloggen om pagina's te kunnen bekijken +require_sign_in_view=Aanmelden vereist om inhoud van instantie te bekijken admin_setting_desc=Het creëren van een administrator-account is optioneel. De eerste geregistreerde gebruiker wordt automatisch de beheerder. admin_title=Instellingen beheerdersaccount admin_name=Admin gebruikersnaam @@ -297,7 +309,7 @@ default_enable_timetracking=Tijdregistratie standaard inschakelen default_enable_timetracking_popup=Tijdsregistratie voor nieuwe repositories standaard inschakelen. no_reply_address=Verborgen e-maildomein no_reply_address_helper=Domeinnaam voor gebruikers met een verborgen e-mailadres. Bijvoorbeeld zal de gebruikersnaam "joe" in Git worden geregistreerd als "joe@noreply.example.org" als het verborgen email domein is ingesteld op "noreply.example.org". -password_algorithm=Wachtwoord Hash Algoritme +password_algorithm=Wachtwoord hash-algoritme env_config_keys = Configuratie Omgeving env_config_keys_prompt = De volgende omgevingsvariabelen worden ook toegepast op je configuratiebestand: invalid_db_table = De database tabel "%s" is ongeldig: %v @@ -307,9 +319,10 @@ invalid_password_algorithm = Ongeldig wachtwoord hash-algoritme password_algorithm_helper = Stel het hashing-algoritme voor wachtwoorden in. De algoritmes hebben verschillende vereisten en sterkte. Het argon2-algoritme is tamelijk veilig, maar gebruikt veel geheugen en kan ongeschikt zijn voor kleine systemen. run_user_helper = De gebruikersnaam van het besturingssysteem waaronder Forgejo draait. Merk op dat deze gebruiker toegang moet hebben tot de hoofdmap van de repository. require_sign_in_view_popup = Beperk de toegang tot de pagina's tot ingelogde gebruikers. Bezoekers zien alleen de aanmeldings- en registratiepagina's. -enable_update_checker_helper_forgejo = Controleert periodiek op nieuwe versies van Forgejo door een DNS TXT-record op release.forgejo.org te controleren. +enable_update_checker_helper_forgejo = Het zal periodiek controleren op nieuwe Forgejo-versies door een TXT DNS-record op release.forgejo.org te controleren. enable_update_checker_helper = Controleert periodiek op nieuwe versies door verbinding te maken met gitea.io. smtp_from_invalid = Het adres "E-mails versturen als" is ongeldig +config_location_hint = Deze configuratieopties worden opgeslagen in: [home] uname_holder=Gebruikersnaam of e-mailadres @@ -395,7 +408,7 @@ twofa_scratch_used=Je hebt je eenmalige code gebruikt. Je wordt omgeleid naar de twofa_passcode_incorrect=Uw wachtwoord is onjuist. Als u uw apparaat kwijt bent, gebruik dan je eenmalige code om in te loggen. twofa_scratch_token_incorrect=Je eenmalige code is onjuist. login_userpass=Inloggen -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Registreer nieuw account oauth_signup_title=Voltooi nieuw account oauth_signup_submit=Account voltooien @@ -486,12 +499,12 @@ release.downloads=Downloads: release.download.zip=Broncode (ZIP) release.download.targz=Broncode (TAR.GZ) -repo.transfer.subject_to=%s zou "%s" willen overdragen aan %s -repo.transfer.subject_to_you=%s wil "%s" aan jou overdragen +repo.transfer.subject_to=%s wil repository "%s" overdragen aan %s +repo.transfer.subject_to_you=%s wilt repository "%s" naar u overdragen repo.transfer.to_you=jij repo.transfer.body=Om het te accepteren of afwijzen, bezoek %s of negeer het gewoon. -repo.collaborator.added.subject=%s heeft jou toegevoegd aan %s +repo.collaborator.added.subject=%s heeft jou toegevoegd aan %s als samenwerker repo.collaborator.added.text=U bent toegevoegd als een samenwerker van de repository: reply = of antwoord op deze e-mail admin.new_user.subject = Nieuwe gebruiker %s heeft zich zojuist geregisteerd @@ -603,6 +616,8 @@ username_error_no_dots = ` kan alleen alfanumerieke karakters ("0-9","a-z","A-Z" invalid_group_team_map_error = ` mapping is ongeldig: %s" org_still_own_repo = Deze organisatie is eigenaar van één of meer repositories, verwijder of draag deze eerst over. org_still_own_packages = Deze organisatie is eigenaar van één of meer pakketten, verwijder deze eerst. +unset_password = De inloggebruiker heeft het wachtwoord niet ingesteld. +unsupported_login_type = Het aanmeldtype wordt niet ondersteund om accounts te verwijderen. [user] @@ -1603,8 +1618,8 @@ pulls.nothing_to_compare=Deze branches zijn gelijk. Er is geen pull request nodi pulls.nothing_to_compare_and_allow_empty_pr=Deze branches zijn gelijk. Deze pull verzoek zal leeg zijn. pulls.has_pull_request=`Een pull-verzoek tussen deze branches bestaat al: %[2]s#%[3]d` pulls.create=Pull verzoek aanmaken -pulls.title_desc=wil %[1]d commits van %[2]s samenvoegen met %[3]s -pulls.merged_title_desc=heeft %[1]d commits samengevoegd van %[2]s naar %[3]s %[4]s +pulls.title_desc_few=wil %[1]d commits van %[2]s samenvoegen met %[3]s +pulls.merged_title_desc_few=heeft %[1]d commits samengevoegd van %[2]s naar %[3]s %[4]s pulls.change_target_branch_at='doelbranch aangepast van %s naar %s %s' pulls.tab_conversation=Discussie pulls.tab_commits=Commits @@ -1661,9 +1676,9 @@ pulls.rebase_conflict_summary=Foutmelding pulls.unrelated_histories=Samenvoegen mislukt: de HEAD en base delen geen gemeenschappelijke geschiedenis. Tip: Probeer een andere strategie pulls.merge_out_of_date=Samenvoegen mislukt: Tijdens het samenvoegen is de basis bijgewerkt. Tip: Probeer het opnieuw. pulls.head_out_of_date=Samenvoegen mislukt: tijdens het genereren van de samenvoeging is de kop bijgewerkt. Tip: Probeer het opnieuw. -pulls.push_rejected=Samenvoegen mislukt: De push is geweigerd. Controleer de Git Hooks voor deze repository. +pulls.push_rejected=Push mislukt: De push is geweigerd. Controleer de Git Hooks voor deze repository. pulls.push_rejected_summary=Volledig afwijzingsbericht -pulls.push_rejected_no_message=Samenvoegen mislukt: De push is afgewezen, maar er was geen extern bericht.
      Controleer de Git Hooks voor deze repository +pulls.push_rejected_no_message=Push mislukt: De push is afgewezen maar er was geen remote bericht. Bekijk de Git Hooks voor dit repository pulls.open_unmerged_pull_exists=`Je kan deze pull request niet opnieuw openen omdat er een andere (#%d) met identieke eigenschappen open staat.` pulls.status_checking=Sommige controles zijn in behandeling pulls.status_checks_success=Alle checks waren succesvol @@ -2089,7 +2104,7 @@ settings.matrix.homeserver_url=Homeserver URL settings.matrix.room_id=Kamer ID settings.matrix.message_type=Bericht type settings.archive.button=Repo archiveren -settings.archive.header=Deze Repo archiveren +settings.archive.header=Archiveer deze repo settings.archive.success=De repo is succesvol gearchiveerd. settings.archive.error=Er is een fout opgetreden tijdens het archiveren van de repo. Zie het logboek voor meer informatie. settings.archive.error_ismirror=U kunt geen gespiegelde repo archiveren. @@ -2423,7 +2438,7 @@ signing.wont_sign.never = Commits worden nooit ondertekend. signing.wont_sign.error = Er is een fout opgetreden tijdens het controleren of de commit ondertekend kon worden. signing.will_sign = Deze commit wordt ondertekend met sleutel "%s". milestones.filter_sort.latest_due_date = Uiterste vervaldatum -milestones.filter_sort.earliest_due_data = Vroegste vervaldatum +milestones.filter_sort.earliest_due_data = Dichtstbijzijnde vervaldatum milestones.edit_success = Mijlpaal "%s" is bijgewerkt. milestones.create_success = De mijlpaal "%s" is aangemaakt. signing.wont_sign.not_signed_in = U bent niet ingelogd. @@ -2651,6 +2666,7 @@ settings.confirmation_string = Confirmatie string activity.navbar.code_frequency = Code Frequentie activity.navbar.recent_commits = Recente commits file_follow = Volg Symlink +error.broken_git_hook = it hooks van deze repository lijken kapot te zijn. Volg alsjeblieft de documentatie om ze te repareren, push daarna wat commits om de status te vernieuwen. @@ -2797,7 +2813,7 @@ total=Totaal: %d dashboard.statistic=Overzicht dashboard.operations=Onderhoudswerkzaamheden -dashboard.system_status=Systeemtatus +dashboard.system_status=Systeemstatus dashboard.operation_name=Bewerking naam dashboard.operation_switch=Omschakelen dashboard.operation_run=Uitvoeren @@ -2831,17 +2847,17 @@ dashboard.resync_all_hooks=Opnieuw synchroniseren van pre-ontvangst, bewerk en p dashboard.reinit_missing_repos=Herinitialiseer alle ontbrekende Git repositories waarvoor records bestaan dashboard.sync_external_users=Externe gebruikersgegevens synchroniseren dashboard.server_uptime=Uptime server -dashboard.current_goroutine=Huidige Goroutines +dashboard.current_goroutine=Huidige goroutines dashboard.current_memory_usage=Huidig geheugen gebruik dashboard.total_memory_allocated=Totaal toegewezen geheugen dashboard.memory_obtained=Geheugen gebruikt dashboard.pointer_lookup_times=Aanwijzer Lookup keer dashboard.memory_allocate_times=Geheugentoewijzingen dashboard.memory_free_times=Geheugen vrjigemaakt -dashboard.current_heap_usage=Huidige Heap gebruik +dashboard.current_heap_usage=Huidige heap gebruik dashboard.heap_memory_obtained=Heap geheugen verkregen dashboard.heap_memory_idle=Heap geheugen inactief -dashboard.heap_memory_in_use=Heap geheugen In gebruik +dashboard.heap_memory_in_use=Heap geheugen in gebruik dashboard.heap_memory_released=Heap geheugen vrijgegeven dashboard.heap_objects=Heap-objecten dashboard.bootstrap_stack_usage=Bootstrap Stack gebruik @@ -2851,7 +2867,7 @@ dashboard.mspan_structures_obtained=MSpan structuren verkregen dashboard.mcache_structures_usage=MCache structuren gebruik dashboard.mcache_structures_obtained=MCache structuren verkregen dashboard.profiling_bucket_hash_table_obtained=Profilering emmer hashtabel verkregen -dashboard.gc_metadata_obtained=GC Metadada verkregen +dashboard.gc_metadata_obtained=GC metadada verkregen dashboard.other_system_allocation_obtained=Andere systeem toewijzing verkregen dashboard.next_gc_recycle=Volgende GC recycle dashboard.last_gc_time=Sinds vorige GC verwerkingstijd @@ -2977,8 +2993,8 @@ auths.group_search_base=Groep zoekbasis DN auths.group_attribute_list_users=Groep Attribuut met lijst van gebruikers auths.user_attribute_in_group=Gebruikerskenmerken vermeld in groep auths.smtp_auth=SMTP-authenticatietype -auths.smtphost=SMTP host -auths.smtpport=SMTP poort +auths.smtphost=SMTP-host +auths.smtpport=SMTP-poort auths.allowed_domains=Toegelaten domeinen auths.allowed_domains_helper=Laat leeg om alle domeinen toe te staan. Meerdere domeinen scheiden met een komma (","). auths.skip_tls_verify=TLS-verificatie overslaan @@ -3023,37 +3039,37 @@ auths.still_in_used=De authenticatiebron is nog in gebruik. Converteer of verwij auths.deletion_success=De authenticatie-bron is verwijderd. config.server_config=Serverconfiguratie -config.app_name=Naam site +config.app_name=Instantietitel config.app_ver=Forgejo versie -config.app_url=Forgejo basis URL +config.app_url=Basis URL config.custom_conf=Configuratiebestandspad config.custom_file_root_path=Hoofdmap voor aangepaste bestanden config.offline_mode=Lokale modus config.disable_router_log=Router-log uitschakelen -config.run_user=Uitvoeren als gebruiker +config.run_user=Gebruiker om als uit te voeren config.run_mode=Uitvoer modus config.git_version=Git versie config.repo_root_path=Repository basis pad config.lfs_root_path=LFS rootpad config.log_file_root_path=Log-pad -config.script_type=Script type +config.script_type=Script soort config.reverse_auth_user=Omgekeerde verificatie gebruiker config.ssh_config=SSH-configuratie config.ssh_enabled=Ingeschakeld config.ssh_start_builtin_server=Gebruik de ingebouwde server config.ssh_port=Poort -config.ssh_listen_port=Luister op poort +config.ssh_listen_port=Luisterpoort config.ssh_root_path=Root-pad config.ssh_key_test_path=Pad voor key-tests config.ssh_keygen_path=Pad van keygen ("ssh-keygen") config.ssh_minimum_key_size_check=Controleer minimale key-lengte config.ssh_minimum_key_sizes=Minimale key-lengtes -config.lfs_config=LFS Configuratie +config.lfs_config=LFS configuratie config.lfs_enabled=Ingeschakeld config.lfs_content_path=LFS inhoudspad -config.lfs_http_auth_expiry=LFS HTTP Auth Vervaltijd +config.lfs_http_auth_expiry=LFS HTTP auth vervaltijd config.db_config=Databaseconfiguratie config.db_type=Type @@ -3074,8 +3090,8 @@ config.show_registration_button=Registeren knop weergeven config.require_sign_in_view=Vereis inloggen om pagina's te kunnen bekijken config.mail_notify=Activeer e-mailnotificaties config.enable_captcha=CAPTCHA inschakelen -config.active_code_lives=Actieve Code leven -config.reset_password_code_lives=Herstel accountcode vervaltijd +config.active_code_lives=Vervaltijd activeringscode +config.reset_password_code_lives=Vervaltijd herstelcode config.default_keep_email_private=Verberg standaard alle e-mailadressen config.default_allow_create_organization=Standaard toestaan om organisaties aan te maken config.enable_timetracking=Tijdregistratie inschakelen @@ -3113,12 +3129,12 @@ config.cache_item_ttl=Cache-item TTL config.session_config=Sessieconfiguratie config.session_provider=Sessieprovider -config.provider_config=Provider config +config.provider_config=Configuratie provider config.cookie_name=Cookie naam -config.gc_interval_time=GC interval time +config.gc_interval_time=GC intervaltijd config.session_life_time=Sessie duur config.https_only=Alleen HTTPS -config.cookie_life_time=Cookie duur leeftijd +config.cookie_life_time=Levensduur cookie config.picture_config=Foto en avatar configuratie config.picture_service=Foto service @@ -3126,12 +3142,12 @@ config.disable_gravatar=Gravatar uitschakelen config.enable_federated_avatar=Federated avatars toestaan config.git_config=Git configuratie -config.git_disable_diff_highlight=Uitschakelen Diff Syntaxis-Highlight -config.git_max_diff_lines=Maximum Diff Lijnen (voor een enkel bestand) -config.git_max_diff_files=Max Diff bestanden (om weer te geven) -config.git_gc_args=GC Parameters +config.git_disable_diff_highlight=Diff syntax highlighting uitschakelen +config.git_max_diff_lines=Max diff regels per bestand +config.git_max_diff_files=Max. getoonde diff-bestanden +config.git_gc_args=GC-argumenten config.git_migrate_timeout=Migratie time-out -config.git_mirror_timeout=Kopie Update Timeout +config.git_mirror_timeout=Time-out spiegelupdate config.git_clone_timeout=Kloon operatie timeout config.git_pull_timeout=Pull operatie timeout config.git_gc_timeout=GC operatie timeout @@ -3172,7 +3188,7 @@ monitor.queue.settings.maxnumberworkers.error=Maximaal aantal workers moet een n monitor.queue.settings.submit=Instellingen bijwerken monitor.queue.settings.changed=Instellingen bijgewerkt -notices.system_notice_list=Systeem aankondigingen +notices.system_notice_list=Systeemmeldingen notices.view_detail_header=Bekijk notificatie details notices.select_all=Alles selecteren notices.deselect_all=Alles deselecteren @@ -3217,7 +3233,7 @@ packages.version = Versie packages.published = Gepubliceerd defaulthooks = Standaard webhooks defaulthooks.update_webhook = Standaard webhook bijwerken -auths.auth_manage_panel = Authenticatie Bronbeheer +auths.auth_manage_panel = Authenticatie bronbeheer auths.oauth2_required_claim_value = Vereiste claimwaarde auths.oauth2_group_claim_name = Claimnaam die groepsnamen geeft voor deze bron. (Optioneel) auths.oauth2_admin_group = Groepsclaimwaarde voor beheerdersgebruikers. (Optioneel - vereist bovenstaande claimnaam) @@ -3293,7 +3309,7 @@ config.test_mail_sent = Er is een testmail verzonden naar "%s". config.test_mail_failed = Er is geen testmail verzonden naar "%s": %v config.access_log_template = Sjabloon voor toegangslogboek config.logger_name_fmt = Logger: %s -config.git_max_diff_line_characters = Max Diff tekens (voor een enkele regel) +config.git_max_diff_line_characters = Max diff tekens per regel auths.sspi_strip_domain_names_helper = Als deze optie is aangevinkt, worden domeinnamen verwijderd uit inlognamen (bijv. "DOMEIN\gebruiker" en "gebruiker@example.org" worden beide gewoon "gebruiker"). auths.map_group_to_team_removal = Gebruikers verwijderen uit gesynchroniseerde teams als gebruiker niet tot overeenkomstige LDAP-groep behoort config.send_test_mail_submit = Stuur diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 3b2fcd9ec0..a254a912bd 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -334,7 +334,7 @@ twofa_scratch_used=Użyłeś/aś swojego kodu jednorazowego. Przekierowano Cię twofa_passcode_incorrect=Twój kod autoryzacji jest niepoprawny. Jeśli zapodziałeś(-aś) swoje urządzenie, użyj swojego kodu jednorazowego do zalogowania. twofa_scratch_token_incorrect=Twój kod jednorazowy jest niepoprawny. login_userpass=Zaloguj się -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Utwórz nowe konto oauth_signup_title=Ukończ nowe konto oauth_signup_submit=Utwórz konto @@ -1302,8 +1302,8 @@ pulls.no_results=Nie znaleziono wyników. pulls.nothing_to_compare=Te gałęzie są sobie równe. Nie ma potrzeby tworzyć Pull Requesta. pulls.nothing_to_compare_and_allow_empty_pr=Te gałęzie są równe. Ten PR będzie pusty. pulls.create=Utwórz Pull Request -pulls.title_desc=chce scalić %[1]d commity/ów z %[2]s do %[3]s -pulls.merged_title_desc=scala %[1]d commity/ów z %[2]s do %[3]s %[4]s +pulls.title_desc_few=chce scalić %[1]d commity/ów z %[2]s do %[3]s +pulls.merged_title_desc_few=scala %[1]d commity/ów z %[2]s do %[3]s %[4]s pulls.change_target_branch_at=`zmienia gałąź docelową z %s na %s %s` pulls.tab_conversation=Dyskusja pulls.tab_commits=Commity diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index e6fc0df5ef..ade7c6c704 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -29,7 +29,7 @@ return_to_gitea=Volte para Forgejo username=Nome de usuário email=Endereço de e-mail password=Senha -access_token=Token de Acesso +access_token=Token de acesso re_type=Confirmar senha captcha=CAPTCHA twofa=Autenticação de dois fatores @@ -142,6 +142,12 @@ view = Visualizar copy_hash = Copiar hash tracked_time_summary = Resumo do tempo de rastreamento baseado em filtros da lista de issues confirm_delete_artifact = Tem certeza de que deseja excluir o artefato "%s"? +filter = Filtro +filter.clear = Limpar filtro +filter.is_archived = Arquivado +filter.public = Público +filter.is_template = Modelo +filter.private = Privado [aria] navbar=Barra de navegação @@ -151,7 +157,7 @@ footer.links=Links [heatmap] number_of_contributions_in_the_last_12_months=%s contribuições nos últimos 12 meses -no_contributions=Sem contribuições +contributions_zero=Sem contribuições less=Menos more=Mais @@ -398,7 +404,7 @@ twofa_scratch_used=Você usou seu código de backup. Você foi redirecionado par twofa_passcode_incorrect=Seu código de acesso está incorreto. Se você perdeu seu dispositivo, use seu código de backup para acessar. twofa_scratch_token_incorrect=Seu código de backup está incorreto. login_userpass=Acessar -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Cadastrar nova conta oauth_signup_title=Completar Nova Conta oauth_signup_submit=Completar conta @@ -1718,8 +1724,8 @@ pulls.nothing_to_compare=Estes branches são iguais. Não há nenhuma necessidad pulls.nothing_to_compare_and_allow_empty_pr=Estes branches são iguais. Este PR ficará vazio. pulls.has_pull_request=`Um pull request entre esses branches já existe: %[2]s#%[3]d` pulls.create=Criar pull request -pulls.title_desc=quer aplicar o merge de %[1]d commits de %[2]s em %[3]s -pulls.merged_title_desc=aplicou merge dos %[1]d commits de %[2]s em %[3]s %[4]s +pulls.title_desc_few=quer aplicar o merge de %[1]d commits de %[2]s em %[3]s +pulls.merged_title_desc_few=aplicou merge dos %[1]d commits de %[2]s em %[3]s %[4]s pulls.change_target_branch_at=`mudou o branch de destino de %s para %s %s` pulls.tab_conversation=Conversação pulls.tab_commits=Commits @@ -3579,4 +3585,4 @@ executable_file = Arquivo executável component_loading = Carregando %s... component_loading_failed = Não foi possível carregar o(a) %s component_loading_info = Pode demorar um pouco… -contributors.what = contribuições \ No newline at end of file +contributors.what = contribuições diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 649ddfd819..3538ac9460 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -151,7 +151,7 @@ footer.links=Ligações [heatmap] number_of_contributions_in_the_last_12_months=%s contribuições nos últimos 12 meses -no_contributions=Nenhuma contribuição +contributions_zero=Nenhuma contribuição less=Menos more=Mais @@ -396,7 +396,7 @@ twofa_scratch_used=Você usou o seu código de recuperação. Foi reencaminhado twofa_passcode_incorrect=A senha está errada. Se perdeu o seu dispositivo, use o código de recuperação para iniciar a sessão. twofa_scratch_token_incorrect=O código de recuperação está errado. login_userpass=Iniciar sessão -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Fazer inscrição oauth_signup_title=Completar a nova conta oauth_signup_submit=Completar conta @@ -1722,8 +1722,8 @@ pulls.nothing_to_compare_have_tag=O ramo/etiqueta escolhidos são iguais. pulls.nothing_to_compare_and_allow_empty_pr=Estes ramos são iguais. Este pedido de integração ficará vazio. pulls.has_pull_request=`Já existe um pedido de integração entre estes ramos: %[2]s#%[3]d` pulls.create=Criar um pedido de integração -pulls.title_desc=quer integrar %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s -pulls.merged_title_desc=integrou %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s %[4]s +pulls.title_desc_few=quer integrar %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s +pulls.merged_title_desc_few=integrou %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s %[4]s pulls.change_target_branch_at=`mudou o ramo de destino de %s para %s %s` pulls.tab_conversation=Diálogo pulls.tab_commits=Cometimentos diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 131aa521c1..e355d4c9c9 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1,6 +1,6 @@ [common] home=Главная -dashboard=Панель управления +dashboard=Главная explore=Обзор help=Помощь logo=Логотип @@ -56,7 +56,7 @@ mirror=Зеркало new_repo=Новый репозиторий new_migrate=Новая миграция new_mirror=Новое зеркало -new_fork=Новый форк репозитория +new_fork=Новое ответвление репозитория new_org=Новая организация new_project=Новый проект new_project_column=Новый столбец @@ -72,7 +72,7 @@ all=Все sources=Собственные mirrors=Зеркала collaborative=Совместные -forks=Форки +forks=Ответвления activities=Активности pull_requests=Слияния @@ -143,6 +143,18 @@ tracked_time_summary = Сводка отслеженного времени на view = Просмотр confirm_delete_artifact = Вы точно хотите удалить артефакт «%s»? toggle_menu = Показать/скрыть меню +filter.not_archived = Не архивированные +filter = Фильтры +filter.clear = Очистить фильтры +filter.is_fork = Ответвления +filter.not_fork = Не ответвления +filter.is_mirror = Зеркала +filter.is_template = Шаблоны +filter.not_template = Не шаблоны +filter.public = Публичные +filter.private = Приватные +filter.is_archived = Архивированные +filter.not_mirror = Не зеркала [aria] navbar=Панель навигации @@ -152,7 +164,10 @@ footer.links=Ссылки [heatmap] number_of_contributions_in_the_last_12_months=Принимал(а) участие %s раз за последние 12 месяцев -no_contributions=Не принимал(а) участия +contributions_zero=Действий не было +contributions_format = {contributions} {day} {month} {year} +contributions_one=действие +contributions_few=действий less=Меньше more=Больше @@ -226,7 +241,7 @@ err_admin_name_pattern_not_allowed=Неверное имя администра err_admin_name_is_invalid=Неверное имя администратора general_title=Основные настройки -app_name=Название сайта +app_name=Название сервера app_name_helper=Здесь вы можете ввести название своей компании. repo_path=Путь до каталога репозиториев repo_path_helper=Все удалённые Git репозитории будут сохранены в этом каталоге. @@ -242,7 +257,7 @@ http_port=Forgejo HTTP порт http_port_helper=Номер порта, который будет прослушиваться Forgejo веб-сервером. app_url=Базовый URL Forgejo app_url_helper=Этот параметр влияет на URL для клонирования по HTTP/HTTPS и на некоторые уведомления по эл. почте. -log_root_path=Путь к журналу +log_root_path=Путь журналов log_root_path_helper=Файлы журнала будут записываться в этот каталог. optional_title=Расширенные настройки @@ -254,13 +269,13 @@ smtp_from_helper=Адрес эл. почты, который будет испо mailer_user=Логин SMTP mailer_password=Пароль SMTP register_confirm=Требовать подтверждение по эл. почте для регистрации -mail_notify=Разрешить почтовые уведомления -server_service_title=Сервер и настройки внешних служб -offline_mode=Включить локальный режим +mail_notify=Уведомления по эл. почте +server_service_title=Настройки сервера и внешних служб +offline_mode=Локальный режим offline_mode_popup=Отключить сторонние сети доставки контента и передавать все ресурсы из их локальных копий. disable_gravatar=Отключить Gravatar disable_gravatar_popup=Отключить Gravatar и сторонние источники аватаров. Если пользователь не загрузит аватар локально, то по умолчанию будет использоваться стандартный аватар. -federated_avatar_lookup=Включить федерированные аватары +federated_avatar_lookup=Федерированные аватары federated_avatar_lookup_popup=Включите поиск федеративного аватара для использования службы с открытым исходным кодом на основе libravatar. disable_registration=Отключить самостоятельную регистрацию disable_registration_popup=Отключить самостоятельную регистрацию. Только администраторы смогут создавать новые учётные записи пользователей. @@ -298,18 +313,19 @@ default_allow_create_organization=Разрешить создание орган default_allow_create_organization_popup=Разрешить новым учётным записям пользователей создавать организации по умолчанию. default_enable_timetracking=Включить отслеживание времени по умолчанию default_enable_timetracking_popup=Включить отслеживание времени для новых репозиториев по умолчанию. -no_reply_address=Скрытый почтовый домен +no_reply_address=Домен скрытых адресов почты no_reply_address_helper=Доменное имя для пользователей со скрытым адресом эл. почты. Например, пользователь «joe» будет зарегистрирован в Git как «joe@noreply.example.org», если скрытый домен эл. почты задан как «noreply.example.org». -password_algorithm=Алгоритм хеширования пароля +password_algorithm=Алгоритм хеширования паролей invalid_password_algorithm=Некорректный алгоритм хеширования пароля password_algorithm_helper=Задайте алгоритм хеширования паролей. Алгоритмы имеют различные требования и стойкость. Алгоритм argon2 довольно безопасен, но он использует много памяти и может не подходить для слабых систем. -enable_update_checker=Включить проверку обновлений +enable_update_checker=Проверка обновлений enable_update_checker_helper=Периодически проверяет наличие новых версий, подключаясь к gitea.io. env_config_keys=Настройка окружения env_config_keys_prompt=Следующие переменные окружения также будут применены к вашему конфигурационному файлу: enable_update_checker_helper_forgejo = Периодически проверять наличие новых версий Forgejo через DNS-запись TXT на release.forgejo.org. allow_dots_in_usernames = Разрешить точки в логинах пользователей. Это не повлияет на уже созданные учётные записи. smtp_from_invalid = Адрес для отправки писем некорректен +config_location_hint = Эти настройки конфигурации будут сохранены в: [home] uname_holder=Логин или адрес эл. почты @@ -374,7 +390,7 @@ forgot_password=Забыли пароль? sign_up_now=Нужна учётная запись? Зарегистрируйтесь. sign_up_successful=Учётная запись успешно создана. Добро пожаловать! confirmation_mail_sent_prompt=Новое письмо для подтверждения направлено на %s. Пожалуйста, проверьте ваш почтовый ящик в течение %s для завершения регистрации. -must_change_password=Обновить пароль +must_change_password=Обновите пароль allow_password_change=Требовать смену пароля пользователем (рекомендуется) reset_password_mail_sent_prompt=Письмо с подтверждением отправлено на %s. Пожалуйста, проверьте входящую почту в течение %s, чтобы завершить процесс восстановления учётной записи. active_your_account=Активируйте свою учётную запись @@ -401,7 +417,9 @@ twofa_scratch_used=Вы использовали scratch-код. Вы были twofa_passcode_incorrect=Ваш пароль неверен. Если вы потеряли устройство, используйте ваш scratch-код. twofa_scratch_token_incorrect=Неверный scratch-код. login_userpass=Вход -login_openid=OpenID +tab_signin = Войти +tab_signup = Зарегистрироваться +tab_openid=OpenID oauth_signup_tab=Зарегистрировать новую учётную запись oauth_signup_title=Полная новая учётная запись oauth_signup_submit=Полная учётная запись @@ -453,7 +471,7 @@ activate_email.text=Для подтверждения эл. почты пере register_notify=Добро пожаловать в Forgejo register_notify.title=%[1]s, добро пожаловать в %[2]s register_notify.text_1=это письмо с вашим подтверждением регистрации в %s! -register_notify.text_2=Теперь вы можете войти в учётную запись, используя логин: %s. +register_notify.text_2=Теперь вы можете войти в учётную запись, используя логин: %s register_notify.text_3=Если эта учётная запись создана кем-то для вас, сперва будет необходимо задать пароль. reset_password=Восстановление учётной записи @@ -462,21 +480,21 @@ reset_password.text=Если этот запрос ваш, для восстан register_success=Регистрация прошла успешно -issue_assigned.pull=@%[1]s вы назначены на запрос слияния %[2]s в репозитории %[3]s. +issue_assigned.pull=@%[1]s назначил(а) вам запрос на слияние %[2]s в репозитории %[3]s. issue_assigned.issue=@%[1]s назначил(а) вам задачу %[2]s в репозитории %[3]s. issue.x_mentioned_you=@%s упомянул(а) вас: issue.action.force_push=%[1]s форсировал(а) отправку в %[2]s изменений %[4]s вместо %[3]s. -issue.action.push_1=@%[1]s отправил(а) %[3]d изменение в %[2]s -issue.action.push_n=@%[1]s отправил(а) %[3]d изменений в %[2]s +issue.action.push_1=@%[1]s отправлено %[3]d изменение в %[2]s +issue.action.push_n=@%[1]s отправлены %[3]d изменений в %[2]s issue.action.close=@%[1]s закрыл(а) #%[2]d. issue.action.reopen=@%[1]s переоткрыл(а) #%[2]d. issue.action.merge=@%[1]s слил(а) #%[2]d в %[3]s. -issue.action.approve=@%[1]s одобрение этого слияния. -issue.action.reject=@%[1]s запрос изменений в этом запросе на слияние. -issue.action.review=@%[1]s комментарий для этого запроса на слияние. -issue.action.review_dismissed=@%[1]s отклонён последний отзыв от %[2]s для этого запроса на слияние. -issue.action.ready_for_review=@%[1]s запрос на слияние отмечен как готовый к рецензии. +issue.action.approve=@%[1]s одобрил(а) этот запрос на слияние. +issue.action.reject=@%[1]s запросил(а) изменения в этом запросе на слияние. +issue.action.review=@%[1]s прокомментировал(а) этот запрос на слияние. +issue.action.review_dismissed=@%[1]s отклонил(а) последний отзыв с %[2]s для этого запроса на слияние. +issue.action.ready_for_review=@%[1]s отметил(а) этот запрос на слияние как готовый к рассмотрению. issue.action.new=@%[1]s создал(а) #%[2]d. issue.in_tree_path=В %s: @@ -488,12 +506,12 @@ release.downloads=Загрузки: release.download.zip=Исходный код (ZIP) release.download.targz=Исходный код (TAR.GZ) -repo.transfer.subject_to=%s хочет передать «%s» в %s -repo.transfer.subject_to_you=%s хочет передать вам «%s» +repo.transfer.subject_to=%s хочет передать репозиторий «%s» в %s +repo.transfer.subject_to_you=%s хочет передать вам репозиторий «%s» repo.transfer.to_you=вам repo.transfer.body=Чтобы принять или отклонить передачу, перейдите по ссылке %s или просто проигнорируйте этот запрос. -repo.collaborator.added.subject=%s вы добавлены в %s +repo.collaborator.added.subject=%s вы добавлены как соучастник в %s repo.collaborator.added.text=Вы были добавлены в качестве соучастника репозитория: team_invite.subject=%[1]s приглашает вас присоединиться к организации %[2]s @@ -603,6 +621,8 @@ org_still_own_packages=Эта организация всё ещё владее target_branch_not_exist=Целевая ветка не существует. admin_cannot_delete_self = Вы не можете удалить свою учётную запись, будучи администратором. Сперва снимите с себя роль администратора. username_error_no_dots = ` может состоять только из латинских букв («a-z», «A-Z»), цифр («0-9»), знаков минуса («-») и нижнего подчёркивания («_»). Знаки не могут стоять в начале или в конце, а также идти подряд.` +unsupported_login_type = Удаление аккаунта невозможно с этим типом авторизации. +unset_password = У пользователя не задан пароль. [user] @@ -651,7 +671,7 @@ applications=Приложения orgs=Управление организациями repos=Репозитории delete=Удалить учётную запись -twofa=Двухфакторная аутентификация +twofa=Двухфакторная аутентификация (TOTP) account_link=Привязанные учётные записи organization=Организации uid=UID @@ -662,12 +682,12 @@ biography_placeholder=Расскажите немного о себе! (Можн location_placeholder=Поделитесь своим приблизительным местоположением с другими profile_desc=Как ваш профиль будет отображаться для других пользователей. Ваш основной адрес эл. почты будет использоваться для уведомлений, восстановления пароля и веб-операций с Git. password_username_disabled=Нелокальным пользователям запрещено изменение их имени пользователя. Для получения более подробной информации обратитесь к администратору сайта. -full_name=Имя и фамилия +full_name=Полное имя website=Веб-сайт location=Местоположение -update_theme=Обновить тему +update_theme=Изменить тему update_profile=Обновить профиль -update_language=Обновить язык +update_language=Сменить язык update_language_not_found=Язык «%s» недоступен. update_language_success=Язык обновлён. update_profile_success=Ваш профиль успешно обновлён. @@ -691,7 +711,7 @@ comment_type_group_branch=Операции с ветками comment_type_group_time_tracking=Отслеживание времени comment_type_group_deadline=Модификации сроков выполнения comment_type_group_dependency=Модификации зависимостей -comment_type_group_lock=Смена статуса ограничения на обсуждение +comment_type_group_lock=Статус ограничения comment_type_group_review_request=Запросы на рецензию comment_type_group_pull_request_push=Добавленные коммиты comment_type_group_project=Проект @@ -723,7 +743,7 @@ password_change_disabled=Нелокальные учётные записи не emails=Email адреса manage_emails=Управление адресами эл. почты manage_themes=Выберите тему по умолчанию -manage_openid=Управление OpenID +manage_openid=Управление адресами OpenID email_desc=Ваш основной адрес эл. почты будет использоваться для уведомлений, восстановления пароля и, если он не скрыт, для действий с Git в веб-интерфейсе. theme_desc=Это будет темой по умолчанию для всего сайта. primary=Основной @@ -744,7 +764,7 @@ openid_deletion_desc=После удаления адреса OpenID вы не openid_deletion_success=Адрес OpenID удален. add_new_email=Добавить адрес эл. почты add_new_openid=Добавить новый OpenID URI -add_email=Добавить новый адрес +add_email=Добавить адрес эл. почты add_openid=Добавить адрес OpenID add_email_confirmation_sent=Письмо для подтверждения отправлено на «%s». Пожалуйста, проверьте ваш почтовый ящик в течение %s, чтобы завершить процесс подтверждения. add_email_success=Добавлен новый адрес эл. почты. @@ -926,11 +946,11 @@ repos_none=Вы не владеете ни одним репозиторием. delete_account=Удаление учётной записи delete_prompt=Эта операция навсегда удалит вашу учётную запись. Это НЕВОЗМОЖНО будет отменить. delete_with_all_comments=Ваша учётная запись младше %s. Чтобы избежать комментариев к плану, все комментарии к ней будут удалены. -confirm_delete_account=Подтвердите удаление +confirm_delete_account=Подтвердить удаление delete_account_title=Удалить эту учётную запись delete_account_desc=Вы уверены, что хотите навсегда удалить эту учётную запись? -email_notifications.enable=Включить почтовые уведомления +email_notifications.enable=Разрешить уведомления по почте email_notifications.onmention=Посылать письмо на эл. почту только при упоминании email_notifications.disable=Отключить почтовые уведомления email_notifications.submit=Установить настройки эл. почты @@ -970,21 +990,21 @@ visibility=Видимость visibility_description=Это увидят только владелец организации или участники при наличии прав. visibility_helper=Сделать репозиторий приватным visibility_helper_forced=Администратор сайта настроил параметр видимости новых репозиториев. Репозиторий приватный по умолчанию. -visibility_fork_helper=(Изменение этого повлияет на все форки.) +visibility_fork_helper=(Это изменит видимость всех ответвлений.) clone_helper=Нужна помощь в клонировании? Посетите страницу помощи. -fork_repo=Форкнуть репозиторий -fork_from=Форк от -already_forked=Вы уже форкнули %s +fork_repo=Создать ответвление +fork_from=Ответвить от +already_forked=У вас уже есть ответвление %s fork_to_different_account=Ответвление для другой учётной записи -fork_visibility_helper=Видимость форкнутого репозитория изменить нельзя. -fork_branch=Ветка для клонирования в форк +fork_visibility_helper=Нельзя изменить видимость ответвлённого репозитория. +fork_branch=Ветка, клонируемая в ответвление all_branches=Все ветки use_template=Использовать этот шаблон clone_in_vsc=Клонировать в VS Code download_zip=Скачать ZIP download_tar=Скачать TAR.GZ download_bundle=Скачать BUNDLE -generate_repo=Создать репозиторий +generate_repo=Сгенерировать репозиторий generate_from=Создать из repo_desc=Описание repo_desc_helper=Добавьте краткое описание (необязательно) @@ -1011,7 +1031,7 @@ default_branch_label=по умолчанию default_branch_helper=Ветка по умолчанию является базовой веткой для запросов на слияние и коммитов кода. mirror_prune=Очистить mirror_prune_desc=Удаление устаревших отслеживаемых ссылок -mirror_interval=Интервал зеркалирования (допустимы единицы времени 'h', 'm', 's'). Значение 0 отключает периодическую синхронизацию. (Минимальный интервал: %s) +mirror_interval=Интервал зеркалирования (единицы времени: «h», «m», «s»). Значение 0 отключит периодическую синхронизацию. (Мин. интервал: %s) mirror_interval_invalid=Недопустимый интервал зеркалирования. mirror_sync_on_commit=Синхронизировать при отправке коммитов mirror_address=Клонировать по URL @@ -1027,7 +1047,7 @@ mirror_password_help=Смените имя пользователя для уд watchers=Наблюдатели stargazers=Звездочеты stars_remove_warning=Данное действие удалит все звёзды из этого репозитория. -forks=Форки +forks=Ответвления reactions_more=и ещё %d unit_disabled=Администратор сайта отключил этот раздел репозитория. language_other=Разное @@ -1046,9 +1066,9 @@ author_search_tooltip=Показывает максимум 30 пользова tree_path_not_found_commit=Путь %[1]s не существует в коммите %[2]s tree_path_not_found_branch=Путь %[1]s не существует в ветке %[2]s -transfer.accept=Принять перенос +transfer.accept=Принять передачу transfer.accept_desc=Переместить в «%s» -transfer.reject=Отказаться от перемещения +transfer.reject=Отказаться от передачи transfer.reject_desc=Отменить перемещение в «%s» desc.private=Приватный @@ -1077,14 +1097,14 @@ form.name_reserved=Название репозитория «%s» зарезер form.name_pattern_not_allowed=Шаблон «%s» не допускается в названии репозитория. need_auth=Авторизация -migrate_options=Параметры миграции +migrate_options=Параметры переноса migrate_service=Сервис миграции migrate_options_mirror_helper=Этот репозиторий будет зеркалом migrate_options_lfs=Перенос LFS файлов migrate_options_lfs_endpoint.label=Конечная точка LFS -migrate_options_lfs_endpoint.description=Миграция попытается использовать ваш Git удаленно, чтобы определить сервер LFS. Вы также можете указать пользовательскую конечную точку, если данные хранятся в другом месте. +migrate_options_lfs_endpoint.description=При переносе будет произведена попытка определить сервер LFS автоматически через Git. Если данные LFS хранятся в другом месте, укажите конечную точку самостоятельно. migrate_options_lfs_endpoint.description.local=Также поддерживается путь на локальном сервере. -migrate_items=Элементы миграции +migrate_items=Переносимые элементы migrate_items_wiki=Вики migrate_items_milestones=Этапы migrate_items_labels=Метки @@ -1101,16 +1121,16 @@ migrate.permission_denied=У вас нет прав на импорт локал migrate.permission_denied_blocked=Вы не можете импортировать с запрещённых хостов, пожалуйста, попросите администратора проверить настройки ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. migrate.invalid_local_path=Недопустимый локальный путь. Он не существует или не является каталогом. migrate.invalid_lfs_endpoint=Конечная точка LFS недействительна. -migrate.failed=Миграция не удалась: %v -migrate.migrate_items_options=Токен доступа необходим для миграции дополнительных элементов +migrate.failed=Перенос не удался: %v +migrate.migrate_items_options=Токен доступа необходим для переноса дополнительных элементов migrated_from=Перенесено из %[2]s migrated_from_fake=Перенесено из %[1]s -migrate.migrate=Миграция из %s +migrate.migrate=Перенос из %s migrate.migrating=Перенос из %s... migrate.migrating_failed=Перенос из %s не удался. migrate.migrating_failed.error=Не удалось мигрировать: %s -migrate.migrating_failed_no_addr=Миграция не удалась. -migrate.github.description=Переносите данные с github.com или других серверов GitHub. +migrate.migrating_failed_no_addr=Перенос не удался. +migrate.github.description=Перенесите данные с github.com или сервера GitHub Enterprise. migrate.git.description=Перенести только репозиторий из любого Git сервиса. migrate.gitlab.description=Перенести данные с gitlab.com или других серверов GitLab. migrate.gitea.description=Перенести данные с gitea.com или других серверов Gitea/Forgejo. @@ -1118,28 +1138,28 @@ migrate.gogs.description=Перенести данные с notabug.org или migrate.onedev.description=Перенести данные с code.onedev.io или других серверов OneDev. migrate.codebase.description=Перенос данных с codebasehq.com. migrate.gitbucket.description=Перенести данные из экземпляров GitBucket. -migrate.migrating_git=Перенос Git данных -migrate.migrating_topics=Миграция тем +migrate.migrating_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.migrating_labels=Перенос меток +migrate.migrating_releases=Перенос выпусков +migrate.migrating_issues=Перенос задач +migrate.migrating_pulls=Перенос запросов на слияние +migrate.cancel_migrating_title=Отменить перенос migrate.cancel_migrating_confirm=Вы хотите отменить эту миграцию? mirror_from=зеркало из forked_from=ответвлено от generated_from=создано из -fork_from_self=Вы не можете форкнуть ваш собственный репозиторий. -fork_guest_user=Войдите, чтобы форкнуть репозиторий. +fork_from_self=Вы не можете создать ответвление собственного репозитория. +fork_guest_user=Войдите, чтобы создать ответвление репозитория. watch_guest_user=Войдите, чтобы отслеживать этот репозиторий. star_guest_user=Войдите, чтобы добавить в избранное этот репозиторий. unwatch=Не отслеживать watch=Отслеживать unstar=Убр. из избранного star=В избранное -fork=Форкнуть +fork=Ответвление download_archive=Скачать репозиторий more_operations=Ещё действия @@ -1226,7 +1246,7 @@ editor.cannot_edit_non_text_files=Двоичные файлы нельзя ре editor.edit_this_file=Редактировать файл editor.this_file_locked=Файл заблокирован editor.must_be_on_a_branch=Чтобы внести или предложить изменения этого файла, необходимо выбрать ветку. -editor.fork_before_edit=Необходимо сделать форк этого репозитория, чтобы внести или предложить изменения этого файла. +editor.fork_before_edit=Необходимо сделать ответвление этого репозитория, чтобы внести или предложить изменения этого файла. editor.delete_this_file=Удалить файл editor.must_have_write_access=Вам необходимо иметь права на запись, чтобы вносить или предлагать изменения этого файла. editor.file_delete_success=Файл «%s» удалён. @@ -1449,19 +1469,19 @@ issues.filter_type.created_by_you=Созданные вами issues.filter_type.mentioning_you=Вы упомянуты issues.filter_type.review_requested=Проверка запрошена issues.filter_type.reviewed_by_you=Проверенные вами -issues.filter_sort=Сортировать +issues.filter_sort=Сортировка issues.filter_sort.latest=Новейшие issues.filter_sort.oldest=Старейшие issues.filter_sort.recentupdate=Недавно обновленные issues.filter_sort.leastupdate=Давно обновленные issues.filter_sort.mostcomment=Больше комментариев issues.filter_sort.leastcomment=Меньше комментариев -issues.filter_sort.nearduedate=Ближайшее по дате завершения -issues.filter_sort.farduedate=Удалённое по дате завершения +issues.filter_sort.nearduedate=Ближайший срок выполнения +issues.filter_sort.farduedate=Поздний срок выполнения issues.filter_sort.moststars=Больше звезд issues.filter_sort.feweststars=Меньше звезд -issues.filter_sort.mostforks=Больше форков -issues.filter_sort.fewestforks=Меньше форков +issues.filter_sort.mostforks=Больше ответвлений +issues.filter_sort.fewestforks=Меньше ответвлений issues.keyword_search_unavailable=В настоящее время поиск по ключевым словам недоступен. Обратитесь к администратору сайта. issues.action_open=Открыть issues.action_close=Закрыть @@ -1485,7 +1505,7 @@ issues.closed_title=Закрыто issues.draft_title=Черновик issues.num_comments_1=%d комментарий issues.num_comments=комментариев: %d -issues.commented_at=`прокомментировал(а) %s` +issues.commented_at=`оставлен комментарий в %s` issues.delete_comment_confirm=Вы уверены, что хотите удалить этот комментарий? issues.context.copy_link=Копировать ссылку issues.context.quote_reply=Цитировать ответ @@ -1494,21 +1514,21 @@ issues.context.edit=Редактировать issues.context.delete=Удалить issues.no_content=Описание отсутствует. issues.close=Закрыть задачу -issues.comment_pull_merged_at=слил(а) коммит %[1]s в %[2]s %[3]s -issues.comment_manually_pull_merged_at=вручную слил(а) коммит %[1]s в %[2]s %[3]s +issues.comment_pull_merged_at=коммит %[1]s был добавлен в %[2]s %[3]s +issues.comment_manually_pull_merged_at=коммит %[1]s был вручную добавлен в %[2]s %[3]s issues.close_comment_issue=Прокомментировать и закрыть issues.reopen_issue=Открыть снова issues.reopen_comment_issue=Прокомментировать и открыть снова issues.create_comment=Комментировать -issues.closed_at=`закрыл(а) эту задачу %[2]s` -issues.reopened_at=`переоткрыл(а) эту проблему %[2]s` +issues.closed_at=`задача была закрыта %[2]s` +issues.reopened_at=`задача была открыта снова %[2]s` issues.commit_ref_at=`упоминание этой задачи в коммите %[2]s` issues.ref_issue_from=`упоминание этой задачи %[4]s %[2]s` issues.ref_pull_from=`упоминание этого запроса слияния %[4]s %[2]s` issues.ref_closing_from=`упоминание запроса слияния %[4]s, закрывающего эту задачу %[2]s` issues.ref_reopening_from=`упоминание запроса слияния %[4]s, повторно открывающего эту задачу %[2]s` issues.ref_closed_from=`закрыл этот запрос %[4]s %[2]s` -issues.ref_reopened_from=`переоткрыл эту задачу %[4]s %[2]s` +issues.ref_reopened_from=`задача была открыта снова %[4]s %[2]s` issues.ref_from=`из %[1]s` issues.author=Автор issues.author_helper=Этот пользователь является автором. @@ -1535,7 +1555,7 @@ issues.label_title=Имя метки issues.label_description=Описание метки issues.label_color=Цвет метки issues.label_exclusive=Эксклюзивный -issues.label_archive=Метка архива +issues.label_archive=Архивная метка issues.label_archived_filter=Показать архивированные метки issues.label_archive_tooltip=Архивированные метки исключаются по умолчанию из подсказок при поиске по метке. issues.label_exclusive_desc=Назовите метку область/элемент, чтобы сделать ее взаимоисключающей с другими метками область/. @@ -1550,8 +1570,8 @@ issues.label_deletion_desc=Удаление метки удаляет ее из issues.label_deletion_success=Метка удалена. issues.label.filter_sort.alphabetically=По алфавиту issues.label.filter_sort.reverse_alphabetically=С конца алфавита -issues.label.filter_sort.by_size=Минимальный размер -issues.label.filter_sort.reverse_by_size=Максимальный размер +issues.label.filter_sort.by_size=Меньший размер +issues.label.filter_sort.reverse_by_size=Больший размер issues.num_participants=%d участвующих issues.attachment.open_tab=`Нажмите, чтобы увидеть «%s» в новой вкладке` issues.attachment.download=`Нажмите, чтобы скачать «%s»` @@ -1583,17 +1603,17 @@ issues.comment_on_locked=Вы не можете оставить коммент issues.delete=Удалить issues.delete.title=Удалить эту задачу? issues.delete.text=Вы точно хотите удалить эту задачу? (Это навсегда удалит всё её содержимое. Возможно, лучше закрыть её в архивных целях) -issues.tracker=Отслеживание времени -issues.start_tracking_short=Запустить таймер -issues.start_tracking=Начать отслеживание времени +issues.tracker=Подсчёт времени +issues.start_tracking_short=Запустить подсчёт +issues.start_tracking=Начать подсчёт времени issues.start_tracking_history=`начал(а) работать %s` issues.tracker_auto_close=Таймер будет остановлен автоматически, когда эта проблема будет закрыта issues.tracking_already_started=`Вы уже начали отслеживать время для другой задачи!` -issues.stop_tracking=Остановить таймер +issues.stop_tracking=Остановить подсчёт issues.stop_tracking_history=`перестал(а) работать %s` issues.cancel_tracking=Отмена issues.cancel_tracking_history=`отменил(а) отслеживание времени %s` -issues.add_time=Вручную добавить время +issues.add_time=Добавить время вручную issues.del_time=Удалить этот журнал времени issues.add_time_short=Добавить время issues.add_time_cancel=Отмена @@ -1602,10 +1622,10 @@ issues.del_time_history=`удалил(а) потраченное время %s` issues.add_time_hours=Часы issues.add_time_minutes=Минуты issues.add_time_sum_to_small=Время не было введено. -issues.time_spent_total=Общее затраченное время -issues.time_spent_from_all_authors=`Общее затраченное время: %s` +issues.time_spent_total=Всего затрачено времени +issues.time_spent_from_all_authors=`Всего затрачено времени: %s` issues.due_date=Срок выполнения -issues.invalid_due_date_format=Дата окончания должна быть в формате «гггг-мм-дд». +issues.invalid_due_date_format=Срок выполнения должен быть в формате «гггг-мм-дд». issues.error_modifying_due_date=Не удалось изменить срок выполнения. issues.error_removing_due_date=Не удалось убрать срок выполнения. issues.push_commit_1=добавлен %d коммит %s @@ -1614,12 +1634,12 @@ issues.force_push_codes=`форсировал(а) отправку измене issues.force_push_compare=Сравнить issues.due_date_form=гггг-мм-дд issues.due_date_form_add=Добавить срок выполнения -issues.due_date_form_edit=Редактировать +issues.due_date_form_edit=Изменить issues.due_date_form_remove=Удалить issues.due_date_not_set=Срок выполнения не установлен. issues.due_date_added=добавлен срок выполнения %s %s -issues.due_date_modified=изменил(а) срок выполнения с %[2]s на %[1]s %[3]s -issues.due_date_remove=убрал(а) срок выполнения %s %s +issues.due_date_modified=срок выполнения передвинут с %[2]s на %[1]s %[3]s +issues.due_date_remove=убран срок выполнения %s %s issues.due_date_overdue=Просроченные issues.due_date_invalid=Срок выполнения недействителен или находится за пределами допустимого диапазона. Пожалуйста, используйте формат «гггг-мм-дд». issues.dependency.title=Зависимости @@ -1721,8 +1741,10 @@ pulls.nothing_to_compare=Нечего сравнивать, родительск pulls.nothing_to_compare_and_allow_empty_pr=Ветки идентичны. Этот PR будет пустым. pulls.has_pull_request=`Запрос на слияние этих веток уже существует: %[2]s#%[3]d` pulls.create=Создать запрос на слияние -pulls.title_desc=хочет влить %[1]d коммит(ов) из %[2]s в %[3]s -pulls.merged_title_desc=слито %[1]d коммит(ов) из %[2]s в %[3]s %[4]s +pulls.title_desc_one=хочет влить %[1]d коммит из %[2]s в %[3]s +pulls.title_desc_few=хочет влить %[1]d коммит(ов) из %[2]s в %[3]s +pulls.merged_title_desc_one=слит %[1]d коммит из %[2]s в %[3]s %[4]s +pulls.merged_title_desc_few=слито %[1]d коммит(ов) из %[2]s в %[3]s %[4]s pulls.change_target_branch_at=`изменил(а) целевую ветку с %s на %s %s` pulls.tab_conversation=Обсуждение pulls.tab_commits=Коммиты @@ -1740,7 +1762,7 @@ pulls.cannot_merge_work_in_progress=Этот запрос на слияние п pulls.still_in_progress=Всё ещё в процессе? pulls.add_prefix=Добавить %s префикс pulls.remove_prefix=Удалить %s префикс -pulls.data_broken=Содержимое этого запроса было нарушено вследствие удаления информации форка. +pulls.data_broken=Содержимое этого слияния нарушено из-за удаления информации об ответвлении. pulls.files_conflicted=Этот запрос на слияние имеет изменения конфликтующие с целевой веткой. pulls.is_checking=Продолжается проверка конфликтов. Повторите попытку позже. pulls.is_ancestor=Эта ветка уже включена в целевую ветку. Объединять нечего. @@ -1784,9 +1806,9 @@ pulls.rebase_conflict_summary=Сообщение об ошибке pulls.unrelated_histories=Слияние не удалось: у источника и цели слияния нет общей истории. Совет: попробуйте другую стратегию pulls.merge_out_of_date=Слияние не удалось: при создании слияния база данных была обновлена. Подсказка: попробуйте ещё раз. pulls.head_out_of_date=Слияние не удалось: во время слияния головной коммит был обновлён. Попробуйте ещё раз. -pulls.push_rejected=Слияние не удалось: отправка была отклонена. Проверьте Git-хуки для этого репозитория. +pulls.push_rejected=Отправка была отклонена. Проверьте Git-хуки этого репозитория. pulls.push_rejected_summary=Полная ошибка отклонения -pulls.push_rejected_no_message=Слияние не удалось: отправка была отклонена, но сервер не указал причину.
      Проверьте Git-хуки для этого репозитория +pulls.push_rejected_no_message=Отправка была отклонена и удалённый сервер не указал причину. Проверьте Git-хуки этого репозитория pulls.open_unmerged_pull_exists=`Вы не можете снова открыть, поскольку уже существует запрос на слияние (#%d) из того же репозитория с той же информацией о слиянии и ожидающий слияния.` pulls.status_checking=Выполняются некоторые проверки pulls.status_checks_success=Все проверки выполнены успешно @@ -1839,9 +1861,9 @@ milestones.completeness=%d%% выполнено milestones.create=Создать этап milestones.title=Заголовок milestones.desc=Описание -milestones.due_date=Дата окончания (опционально) +milestones.due_date=Срок выполнения (опционально) milestones.clear=Очистить -milestones.invalid_due_date_format=Дата выполнения должна быть в формате «гггг-мм-дд». +milestones.invalid_due_date_format=Срок выполнения должен быть в формате «гггг-мм-дд». milestones.create_success=Этап «%s» создан. milestones.edit=Редактировать этап milestones.edit_subheader=Используйте лучшее описание контрольной точки, во избежание непонимания со стороны других людей. @@ -1851,12 +1873,12 @@ milestones.edit_success=Этап «%s» обновлён. milestones.deletion=Удалить этап milestones.deletion_desc=Удаление этапа приведет к его удалению из всех связанных задач. Продолжить? milestones.deletion_success=Этап успешно удалён. -milestones.filter_sort.earliest_due_data=По возрастанию даты завершения -milestones.filter_sort.latest_due_date=По убыванию даты завершения +milestones.filter_sort.earliest_due_data=Ближайший срок выполнения +milestones.filter_sort.latest_due_date=Поздний срок выполнения milestones.filter_sort.least_complete=Менее полное milestones.filter_sort.most_complete=Более полное -milestones.filter_sort.most_issues=Большинство задач -milestones.filter_sort.least_issues=Меньшинство задач +milestones.filter_sort.most_issues=Больше задач +milestones.filter_sort.least_issues=Меньше задач signing.will_sign=Этот коммит будет подписан ключом «%s». signing.wont_sign.never=Коммиты никогда не подписываются. @@ -2071,24 +2093,24 @@ settings.convert_notices_1=Эта операция преобразует это settings.convert_confirm=Подтвердите преобразование settings.convert_succeed=Репозиторий успешно преобразован в обычный. settings.convert_fork=Преобразовать в обычный репозиторий -settings.convert_fork_desc=Вы можете преобразовать этот форк в обычный репозиторий. Это не может быть отменено. -settings.convert_fork_notices_1=Эта операция преобразует этот форк в обычный репозиторий, и не может быть отменена. -settings.convert_fork_confirm=Преобразовать Репозиторий -settings.convert_fork_succeed=Форк преобразован в обычный репозиторий. +settings.convert_fork_desc=Вы можете преобразовать это ответвление в обычный репозиторий. Это не может быть отменено. +settings.convert_fork_notices_1=Эта операция преобразует этот ответвление в обычный репозиторий, и не может быть отменена. +settings.convert_fork_confirm=Преобразовать репозиторий +settings.convert_fork_succeed=Ответвление преобразовано в обычный репозиторий. settings.transfer=Передать права собственности -settings.transfer.rejected=Перенос репозитория отменён. -settings.transfer.success=Перенос репозитория выполнен успешно. -settings.transfer_abort=Отменить перенос -settings.transfer_abort_invalid=Невозможно отменить перенос несуществующего репозитория. +settings.transfer.rejected=Передача репозитория отменена. +settings.transfer.success=Передача репозитория выполнена успешно. +settings.transfer_abort=Отменить передачу +settings.transfer_abort_invalid=Невозможно отменить передачу несуществующего репозитория. settings.transfer_abort_success=Передача репозитория %s успешно отменена. settings.transfer_desc=Передать репозиторий другому пользователю или организации где у вас есть права администратора. settings.transfer_form_title=Введите сопутствующую информацию для подтверждения операции: -settings.transfer_in_progress=Имеется текущий перенос. Отмените его, если хотите выполнить перенос другому пользователю. +settings.transfer_in_progress=Имеется текущая передача. Отмените её, если хотите выполнить передачу другому пользователю. settings.transfer_notices_1=- Вы можете потерять доступ, если новый владелец является отдельным пользователем. settings.transfer_notices_2=- Вы сохраните доступ, если новым владельцем станет организация, владельцем которой вы являетесь. settings.transfer_notices_3=- если репозиторий является приватным и передается отдельному пользователю, это действие позволяет убедиться, что пользователь имеет хотя бы права на чтение (и при необходимости изменяет права доступа). settings.transfer_owner=Новый владелец -settings.transfer_perform=Выполнить перенос +settings.transfer_perform=Выполнить передачу settings.transfer_started=Репозиторий ожидает подтверждения передачи от «%s» settings.transfer_succeed=Репозиторий перенесён. settings.signing_settings=Настройки подписи верификации @@ -2113,7 +2135,7 @@ settings.delete=Удалить этот репозиторий settings.delete_desc=Будьте внимательны! Как только вы удалите репозиторий — пути назад не будет. settings.delete_notices_1=- Эта операция НЕ МОЖЕТ быть отменена. settings.delete_notices_2=- Эта операция навсегда удалит всё из репозитория %s, включая данные Git, связанные с ним задачи, комментарии и права доступа для сотрудников. -settings.delete_notices_fork_1=- Все форки станут независимыми репозиториями после удаления. +settings.delete_notices_fork_1=- После удаления все ответвления станут независимыми репозиториями. settings.deletion_success=Репозиторий удалён. settings.update_settings_success=Настройки репозитория обновлены. settings.update_settings_no_unit=Должно быть разрешено хоть какое-то взаимодействие с репозиторием. @@ -2178,8 +2200,8 @@ settings.event_create=Создать settings.event_create_desc=Ветка или тэг созданы. settings.event_delete=Удалить settings.event_delete_desc=Ветка или тег удалены. -settings.event_fork=Форкнуть -settings.event_fork_desc=Репозиторий форкнут. +settings.event_fork=Ответвление +settings.event_fork_desc=Ответвление создано. settings.event_wiki=Вики settings.event_wiki_desc=Страница вики создана, переименована, изменена или удалена. settings.event_release=Выпуск @@ -2235,7 +2257,7 @@ settings.hook_type=Тип хука settings.slack_token=Slack токен settings.slack_domain=Домен settings.slack_channel=Канал -settings.add_web_hook_desc=Интегрировать %s в ваш репозиторий. +settings.add_web_hook_desc=Интегрируйте %s с этим репозиторием . settings.web_hook_name_gitea=Gitea settings.web_hook_name_forgejo = Forgejo settings.web_hook_name_gogs=Gogs @@ -2557,7 +2579,7 @@ error.csv.too_large=Не удается отобразить этот файл, error.csv.unexpected=Не удается отобразить этот файл, потому что он содержит неожиданный символ в строке %d и столбце %d. error.csv.invalid_field_count=Не удается отобразить этот файл, потому что он имеет неправильное количество полей в строке %d. mirror_address_protocol_invalid = Эта ссылка недействительна. Для зеркалирования можно использовать только расположения http(s):// и git:// . -fork_no_valid_owners = Этот репозиторий не может быть форкнут, т.к. здесь нет действующих владельцев. +fork_no_valid_owners = Невозможно создать ответвление этого репозитория, т.к. здесь нет действующих владельцев. new_repo_helper = Репозиторий содержит все файлы проекта и историю изменений. Уже где-то есть репозиторий? Выполните миграцию. mirror_address_url_invalid = Эта ссылка недействительна. Необходимо правильно указать все части адреса. issues.comment.blocked_by_user = Вы не можете комментировать под этой задачей, т.к. вы заблокированы владельцем репозитория или автором задачи. @@ -2591,11 +2613,11 @@ diff.comment.add_line_comment = Добавить комментарий к ст tree_path_not_found_tag = Путь %[1]s отсутствует в теге %[2]s migrate_options_lfs_endpoint.placeholder = Если не заполнено, конечная точка будет определена из URL клонирования invisible_runes_description = `Этот файл содержит невидимые символы Юникода, которые невозможно заметить, но которые потенциально будут влиять на обработку файла. Если так и должно быть, можете спокойно игнорировать это предупреждение. Отобразить символы можно кнопкой Экранирования.` -transfer.no_permission_to_accept = У вас недостаточно прав для принятия этого переноса. -transfer.no_permission_to_reject = У вас недостаточно прав для отклонения этого переноса. +transfer.no_permission_to_accept = У вас недостаточно прав для принятия этой передачи. +transfer.no_permission_to_reject = У вас недостаточно прав для отклонения этой передачи. commits.view_path = Просмотреть в этом моменте истории commits.renamed_from = Переименован с %s -issues.due_date_not_writer = Для обновления даты выполнения задачи требуются на запись в этом репозитории. +issues.due_date_not_writer = Для обновления срока выполнения задачи требуется право на запись в этом репозитории. issues.review.outdated_description = С момента добавления этого комментария содержимое изменилось pulls.nothing_to_compare_have_tag = Выбранные ветки/теги идентичны. pulls.select_commit_hold_shift_for_range = Выберите коммит. Зажмите Shift, чтобы выбрать диапазон @@ -2643,13 +2665,15 @@ activity.navbar.recent_commits = Недавние коммиты settings.confirmation_string = Подтверждение settings.archive.text = Архивация репозитория сделает всё его содержимое доступным только для чтения. Он будет скрыт с домашнего экрана. Никто (включая вас!) не сможет добавлять коммиты, открывать задачи и запросы слияний. release.deletion_desc = Удаление выпуска удаляет его только в Forgejo. Это действие не затронет тег в git, содержимое репозитория и его историю. Продолжить? -pulls.agit_explanation = Создано через рабочий поток AGit. С ним можно предлагать изменения, используя команду «git push», без необходимости в создании форка или новой ветки. +pulls.agit_explanation = Создано через рабочий поток AGit. С ним можно предлагать изменения, используя команду «git push», без необходимости в создании ответвления или новой ветки. settings.webhook.replay.description_disabled = Активируйте веб-хук для повторения отправки. activity.navbar.pulse = Недавняя активность settings.tags.protection.pattern.description = Можно указать название тега. Для выбора нескольких тегов можно указать поисковый шаблон или регулярное выражение. Подробнее о защищённых тегах. file_follow = Пройти по мягкой ссылке settings.pull_mirror_sync_in_progress = Идёт получение изменений из удалённого репозитория %s. settings.ignore_stale_approvals_desc = Не учитывать одобрения, оставленные для старых коммитов (устаревшие отзывы), при подсчёте общего числа одобрений у запроса на слияние. Не относится к отклонённым отзывам. +settings.mirror_settings.docs.doc_link_pull_section = раздел документации «Pulling from a remote repository». +wiki.original_git_entry_tooltip = Перейти по настоящему пути вместо читабельной ссылки. [graphs] @@ -2664,7 +2688,7 @@ teams=Команды code=Код lower_members=участники lower_repositories=репозитории -create_new_team=Создание команды +create_new_team=Новая команда create_team=Создать команду org_desc=Описание team_name=Название команды @@ -2683,17 +2707,17 @@ form.create_org_not_allowed=Этому пользователю не разре settings=Настройки settings.options=Организация settings.full_name=Полное имя -settings.email=Почта для связи +settings.email=Эл. почта для связи settings.website=Сайт settings.location=Местоположение settings.permission=Разрешения settings.repoadminchangeteam=Администратор репозитория может добавлять и удалять права доступа для команд settings.visibility=Видимость settings.visibility.public=Публичный -settings.visibility.limited=Ограниченный (Видимый только выполнившим вход пользователям) +settings.visibility.limited=Ограниченная (видна только авторизованным пользователям) settings.visibility.limited_shortname=Ограниченный -settings.visibility.private=Приватный (Видимый только для участников организации) -settings.visibility.private_shortname=Приватный +settings.visibility.private=Частная (видна только участникам организации) +settings.visibility.private_shortname=Частная settings.update_settings=Обновить настройки settings.update_setting_success=Настройки организации обновлены. @@ -2704,13 +2728,13 @@ settings.delete=Удалить организацию settings.delete_account=Удалить эту организацию settings.delete_prompt=Это действие БЕЗВОЗВРАТНО удалит эту организацию навсегда! settings.confirm_delete_account=Подтвердить удаление -settings.delete_org_title=Удалить организацию +settings.delete_org_title=Удаление организации settings.delete_org_desc=Эта организация будет безвозвратно удалена. Продолжить? settings.hooks_desc=Добавьте веб-хуки, которые будет вызываться для всех репозиториев под этой организации. settings.labels_desc=Добавьте метки, которые могут быть использованы в задачах для всех репозиториев этой организации. -members.membership_visibility=Видимость участника команды: +members.membership_visibility=Видимость участника: members.public=Видимый members.public_helper=скрыть members.private=Скрыт @@ -2748,14 +2772,14 @@ teams.delete_team=Удалить команду teams.add_team_member=Добавить участника teams.invite_team_member=Пригласить в %s teams.invite_team_member.list=Приглашения в ожидании -teams.delete_team_title=Удалить команду +teams.delete_team_title=Удаление команды teams.delete_team_desc=Удаление команды отменяет доступ к репозиторию для её членов. Продолжить? teams.delete_team_success=Команда удалена. teams.read_permission_desc=Эта команда предоставляет доступ на Чтение: члены могут просматривать и клонировать репозитории команды. teams.write_permission_desc=Эта команда предоставляет доступ на Запись: члены могут получать и выполнять push команды в репозитории. teams.admin_permission_desc=Эта команда даёт административный доступ: участники могут читать, отправлять изменения и добавлять соучастников к её репозиториям. teams.create_repo_permission_desc=Кроме того, эта команда предоставляет право Создание репозитория: участники команды могут создавать новые репозитории в организации. -teams.repositories=Репозитории группы разработки +teams.repositories=Репозитории команды teams.search_repo_placeholder=Поиск репозитория… teams.remove_all_repos_title=Удалить все репозитории команды teams.remove_all_repos_desc=Удаляет все репозитории из команды. @@ -2776,9 +2800,10 @@ teams.invite.title=Вас пригласили присоединиться к teams.invite.by=Приглашен(а) %s teams.invite.description=Нажмите на кнопку ниже, чтобы присоединиться к команде. follow_blocked_user = Вы не можете подписаться на эту организацию, т.к. вы в ней заблокированы. +teams.general_access = Настраиваемый доступ [admin] -dashboard=Панель +dashboard=Панель управления identity_access=Идентификация и доступ users=Пользователи organizations=Организации @@ -2796,7 +2821,7 @@ total=Всего: %d dashboard.new_version_hint=Доступна новая версия Forgejo %s, вы используете %s. Более подробную информацию читайте в блоге. dashboard.statistic=Статистика -dashboard.operations=Операции +dashboard.operations=Обслуживание dashboard.system_status=Состояние системы dashboard.operation_name=Имя операции dashboard.operation_switch=Переключить @@ -2830,58 +2855,58 @@ dashboard.update_migration_poster_id=Обновить ИД плакатов ми dashboard.git_gc_repos=Выполнить сборку мусора для всех репозиториев dashboard.resync_all_sshkeys=Обновить файл '.ssh/authorized_keys' с ключами SSH Forgejo. dashboard.resync_all_sshprincipals=Обновите файл '.ssh/authorized_principals' SSH данными участника Forgejo. -dashboard.resync_all_hooks=Пересинхронизировать хуки pre-receive, update и post-receive всех репозиториев. +dashboard.resync_all_hooks=Пересинхронизировать хуки pre-receive, update и post-receive всех репозиториев dashboard.reinit_missing_repos=Переинициализировать все отсутствующие Git репозитории, для которых существуют записи dashboard.sync_external_users=Синхронизировать данные сторонних пользователей dashboard.cleanup_hook_task_table=Очистить таблицу hook_task dashboard.cleanup_packages=Очистка устаревших пакетов -dashboard.server_uptime=Время непрерывной работы сервера -dashboard.current_goroutine=Текущее количество Goroutines +dashboard.server_uptime=Время работы +dashboard.current_goroutine=Количество goroutines dashboard.current_memory_usage=Текущее использование памяти -dashboard.total_memory_allocated=Всего памяти выделено -dashboard.memory_obtained=Памяти использовано -dashboard.pointer_lookup_times=Запросов указателя +dashboard.total_memory_allocated=Всего памяти выделялось +dashboard.memory_obtained=Получено памяти +dashboard.pointer_lookup_times=Поисков указателя dashboard.memory_allocate_times=Выделений памяти -dashboard.memory_free_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_idle=Простаивающая динамическая память +dashboard.heap_memory_in_use=Используемая динамическая память dashboard.heap_memory_released=Освобождено динамической памяти dashboard.heap_objects=Объектов динамической памяти dashboard.bootstrap_stack_usage=Использование стека загрузчика -dashboard.stack_memory_obtained=Память, занятая под стек +dashboard.stack_memory_obtained=Стековой памяти выделялось dashboard.mspan_structures_usage=Использование структур MSpan -dashboard.mspan_structures_obtained=Получено структур 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.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_time=Итоговая задержка GC -dashboard.total_gc_pause=Итоговая задержка GC -dashboard.last_gc_pause=Последняя пауза сборщика мусора -dashboard.gc_times=Количество сборок мусора -dashboard.delete_old_actions=Удалите все старые действия из базы данных -dashboard.delete_old_actions.started=Удалите все старые действия из запущенной базы данных. +dashboard.total_gc_pause=Итоговая задержка сборщика +dashboard.last_gc_pause=Последняя пауза сборщика +dashboard.gc_times=Сборок мусора +dashboard.delete_old_actions=Удалить все старые действия из базы данных +dashboard.delete_old_actions.started=Запущено удаление всех старых действий из БД. dashboard.update_checker=Проверка обновлений dashboard.delete_old_system_notices=Удалить все старые системные уведомления из базы данных dashboard.gc_lfs=Выполнить сборку мусора метаобъектов LFS dashboard.stop_zombie_tasks=Остановить задания-зомби -dashboard.stop_endless_tasks=Остановить бесконечные задания +dashboard.stop_endless_tasks=Остановить непрекращающиеся задания dashboard.cancel_abandoned_jobs=Отменить брошенные задания dashboard.start_schedule_tasks=Запустить запланированные задания -users.user_manage_panel=Панель управления пользователями +users.user_manage_panel=Управление пользователями users.new_account=Создать новую учётную запись users.name=Имя пользователя users.full_name=Полное имя users.activated=Активирован users.admin=Администратор users.restricted=Ограничено -users.reserved=Зарезервировано +users.reserved=Резерв users.bot=Бот users.2fa=Двухфакторная авторизация users.repos=Репозитории @@ -2931,13 +2956,13 @@ users.list_status_filter.is_2fa_enabled=2FA включено users.list_status_filter.not_2fa_enabled=2FA отключено users.details=О пользователе -emails.email_manage_panel=Управление эл. почтой пользователя +emails.email_manage_panel=Управление адресами эл. почты пользователей emails.primary=Первичный emails.activated=Активирован -emails.filter_sort.email=Эл. почта -emails.filter_sort.email_reverse=Эл. почта (обратный) -emails.filter_sort.name=Имя пользователя -emails.filter_sort.name_reverse=Имя пользователя (обратное) +emails.filter_sort.email=По эл. почте +emails.filter_sort.email_reverse=По эл. почте (наоборот) +emails.filter_sort.name=По имени пользователя +emails.filter_sort.name_reverse=По имени пользователя (наоборот) emails.updated=Email обновлён emails.not_updated=Не удалось обновить запрошенный адрес эл. почты: %v emails.duplicate_active=Этот адрес эл. почты уже активирован для другого пользователя. @@ -2958,7 +2983,7 @@ repos.name=Название repos.private=Личный repos.watches=Следят repos.stars=Звезды -repos.forks=Форки +repos.forks=Ответвления repos.issues=Задачи repos.size=Размер repos.lfs_size=Размер LFS @@ -2977,7 +3002,7 @@ packages.repository=Репозиторий packages.size=Размер packages.published=Опубликовано -defaulthooks=Стандартные Веб-хуки +defaulthooks=Стандартные веб-хуки defaulthooks.add_webhook=Добавить стандартный Веб-хук defaulthooks.update_webhook=Обновить стандартный Веб-хук @@ -2985,7 +3010,7 @@ systemhooks=Системные веб-хуки systemhooks.add_webhook=Добавить системный веб-хук systemhooks.update_webhook=Обновить системный веб-хук -auths.auth_manage_panel=Управление аутентификацией +auths.auth_manage_panel=Управление источниками аутентификации auths.new=Добавить новый источник auths.name=Имя auths.type=Тип @@ -3030,7 +3055,7 @@ auths.smtphost=Адрес SMTP auths.smtpport=Порт SMTP auths.allowed_domains=Разрешенные домены auths.allowed_domains_helper=Разделяйте домены запятыми («,»). Оставьте пустым, чтобы разрешить все домены. -auths.skip_tls_verify=Пропустить проверку TLS +auths.skip_tls_verify=Пропуск проверки TLS auths.force_smtps=Принудительный SMTPS auths.force_smtps_helper=SMTPS всегда использует 465 порт. Установите это, что бы принудительно использовать SMTPS на других портах. (Иначе STARTTLS будет использоваться на других портах, если это поддерживается хостом.) auths.helo_hostname=HELO Hostname @@ -3103,22 +3128,22 @@ auths.unable_to_initialize_openid=Не удалось инициализиров auths.invalid_openIdConnectAutoDiscoveryURL=Неверный URL для автоматического обнаружения (это должен быть валидный URL, начинающийся с http:// или https://) config.server_config=Конфигурация сервера -config.app_name=Название сайта +config.app_name=Название сервера config.app_ver=Версия Forgejo config.app_url=Базовый URL Forgejo config.custom_conf=Путь к файлу конфигурации -config.custom_file_root_path=Пользовательский путь до каталога с файлами +config.custom_file_root_path=Путь до каталога с файлами для персонализации config.domain=Домен сервера config.offline_mode=Локальный режим config.disable_router_log=Отключение журнала маршрутизатора config.run_user=Запуск от имени пользователя -config.run_mode=Режим выполнения -config.git_version=Версия Git +config.run_mode=Режим работы +config.git_version=Версия git config.app_data_path=Путь к данным приложения config.repo_root_path=Путь до каталога репозиториев config.lfs_root_path=Корневой путь LFS -config.log_file_root_path=Путь к журналу -config.script_type=Тип скрипта +config.log_file_root_path=Путь журналов +config.script_type=Тип сценария config.reverse_auth_user=Имя пользователя для авторизации на reverse proxy config.ssh_config=Конфигурация SSH @@ -3129,14 +3154,14 @@ config.ssh_port=Порт config.ssh_listen_port=Прослушиваемый порт config.ssh_root_path=Корневой путь config.ssh_key_test_path=Путь к тестовому ключу -config.ssh_keygen_path=Путь к генератору ключей ('ssh-keygen') +config.ssh_keygen_path=Путь до генератора ключей («ssh-keygen») config.ssh_minimum_key_size_check=Минимальный размер ключа проверки config.ssh_minimum_key_sizes=Минимальные размеры ключа config.lfs_config=Конфигурация LFS config.lfs_enabled=Включено -config.lfs_content_path=Путь к контенту LFS -config.lfs_http_auth_expiry=Устаревание HTTP-аутентификации LFS +config.lfs_content_path=Путь к содержимому LFS +config.lfs_http_auth_expiry=Срок действия HTTP-аутентификации LFS config.db_config=Конфигурация базы данных config.db_type=Тип @@ -3147,34 +3172,34 @@ config.db_schema=Схема config.db_ssl_mode=SSL config.db_path=Путь -config.service_config=Сервисная конфигурация -config.register_email_confirm=Требуется подтверждение по эл. почте -config.disable_register=Отключить самостоятельную регистрацию -config.allow_only_internal_registration=Разрешить регистрацию только через Forgejo +config.service_config=Конфигурация служб +config.register_email_confirm=Требовать подтверждение по эл. почте для регистрации +config.disable_register=Саморегистрация отключена +config.allow_only_internal_registration=Разрешить регистрацию только напрямую через Forgejo config.allow_only_external_registration=Разрешить регистрацию только через сторонние сервисы -config.enable_openid_signup=Включить cамостоятельную регистрацию OpenID -config.enable_openid_signin=Включение входа через OpenID -config.show_registration_button=Показать кнопку регистрации +config.enable_openid_signup=Cаморегистрация через OpenID +config.enable_openid_signin=Вход через OpenID +config.show_registration_button=Кнопка регистрации config.require_sign_in_view=Для просмотра необходима авторизация -config.mail_notify=Почтовые уведомления -config.enable_captcha=Включить CAPTCHA -config.active_code_lives=Время жизни кода для активации +config.mail_notify=Уведомления по эл. почте +config.enable_captcha=CAPTCHA +config.active_code_lives=Срок действия кода активации учётной записи config.reset_password_code_lives=Срок действия кода восстановления учётной записи config.default_keep_email_private=Скрывать адреса эл. почты по умолчанию config.default_allow_create_organization=Разрешить создание организаций по умолчанию -config.enable_timetracking=Включить отслеживание времени +config.enable_timetracking=Отслеживание времени config.default_enable_timetracking=Включить отслеживание времени по умолчанию config.default_allow_only_contributors_to_track_time=Подсчитывать время могут только соавторы -config.no_reply_address=No-reply адрес -config.default_visibility_organization=Видимость по умолчанию для новых организаций +config.no_reply_address=Домен скрытых адресов почты +config.default_visibility_organization=Видимость новых организаций по умолчанию config.default_enable_dependencies=Включение зависимостей для задач по умолчанию config.webhook_config=Конфигурация веб-хуков config.queue_length=Длина очереди config.deliver_timeout=Задержка доставки -config.skip_tls_verify=Пропустить проверку TLS +config.skip_tls_verify=Пропуск проверки TLS -config.mailer_config=Настройки почты +config.mailer_config=Конфигурация почтового сервера config.mailer_enabled=Почта включена config.mailer_enable_helo=Включить HELO config.mailer_name=Имя @@ -3185,7 +3210,7 @@ config.mailer_user=Пользователь config.mailer_use_sendmail=Использовать Sendmail config.mailer_sendmail_path=Путь к Sendmail config.mailer_sendmail_args=Дополнительные аргументы для Sendmail -config.mailer_sendmail_timeout=Тайм-аут Sendmail +config.mailer_sendmail_timeout=Истечение ожидания Sendmail config.mailer_use_dummy=Заглушка config.test_email_placeholder=Эл. почта (например, test@example.com) config.send_test_mail=Отправить тестовое письмо @@ -3196,7 +3221,7 @@ config.test_mail_sent=Тестовое письмо было отправлен config.oauth_config=Конфигурация OAuth config.oauth_enabled=OAuth включен -config.cache_config=Настройки кеша +config.cache_config=Конфигурация кэша config.cache_adapter=Адаптер кэша config.cache_interval=Интервал кэширования config.cache_conn=Подключение кэша @@ -3211,22 +3236,22 @@ config.session_life_time=Время жизни сессии config.https_only=Только HTTPS config.cookie_life_time=Время жизни файла cookie -config.picture_config=Настройка изображения -config.picture_service=Сервис изображений +config.picture_config=Конфигурация аватаров и изображений +config.picture_service=Служба изображений config.disable_gravatar=Отключить Gravatar -config.enable_federated_avatar=Включить федерированные аватары +config.enable_federated_avatar=Федерированные аватары config.git_config=Конфигурация Git config.git_disable_diff_highlight=Отключить подсветку синтаксиса при сравнении -config.git_max_diff_lines=Макс. количество строк (на файл) при сравнении -config.git_max_diff_line_characters=Макс. количество символов (в строке) при сравнении +config.git_max_diff_lines=Макс. количество строк в файле при сравнении +config.git_max_diff_line_characters=Макс. количество символов в строке при сравнении config.git_max_diff_files=Макс. отображаемое количество файлов при сравнении -config.git_gc_args=Аргументы GC -config.git_migrate_timeout=Лимит времени миграций -config.git_mirror_timeout=Лимит времени обновления зеркал -config.git_clone_timeout=Лимит времени операции клонирования -config.git_pull_timeout=Лимит времени получения изменений -config.git_gc_timeout=Лимит времени сборки мусора +config.git_gc_args=Аргументы сборщика мусора +config.git_migrate_timeout=Ограничение времени миграций +config.git_mirror_timeout=Ограничение времени на синхронизацию зеркала +config.git_clone_timeout=Ограничение времени операций клонирования +config.git_pull_timeout=Ограничение времени на получение изменений +config.git_gc_timeout=Ограничение времени на сборку мусора config.log_config=Конфигурация журнала config.logger_name_fmt=Журнал: %s @@ -3239,7 +3264,7 @@ config.set_setting_failed=Задать параметр %s не удалось monitor.stats=Статистика -monitor.cron=Запланированные задания +monitor.cron=Плановые задания monitor.name=Название monitor.schedule=Расписание monitor.next=Следующий раз @@ -3310,15 +3335,16 @@ self_check = Самопроверка dashboard.rebuild_issue_indexer = Пересобрать индексатор задач systemhooks.desc = Веб-хуки автоматически совершают POST запросы до указанного HTTP сервера, когда в Forgejo происходят определённые события. Заданные здесь веб-хуки будут срабатывать во всех репозиториях на этом сервере и могут привести к проблемам с производительностью. Подробнее о веб-хуках. defaulthooks.desc = Веб-хуки автоматически совершают POST запросы до указанного HTTP сервера, когда в Forgejo происходят определённые события. Заданные здесь веб-хуки используются по умолчанию и будут добавлены во все новые репозитории. Подробнее о веб-хуках. +users.remote = Дистанц [action] create_repo=создал(а) репозиторий %s rename_repo=переименовал(а) репозиторий из %[1]s на %[3]s commit_repo=отправил(а) изменения в %[3]s в %[4]s -create_issue=`открыл(а) задачу %[3]s#%[2]s` +create_issue=`задача создана %[3]s#%[2]s` close_issue=`закрыл(а) задачу %[3]s#%[2]s` -reopen_issue=`переоткрыл(а) задачу %[3]s#%[2]s` +reopen_issue=`задача снова открыта %[3]s#%[2]s` create_pull_request=`создал(а) запрос на слияние %[3]s#%[2]s` close_pull_request=`закрыл(а) запрос на слияние %[3]s#%[2]s` reopen_pull_request=`переоткрыл(а) запрос на слияние %[3]s#%[2]s` diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 318eb24df8..7cb1768d22 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -287,7 +287,7 @@ twofa_scratch_used=ඔබ ඔබේ සීරීම් කේතය භාවි twofa_passcode_incorrect=ඔබගේ මුර කේතය වැරදිය. ඔබ ඔබේ උපාංගය අස්ථානගත කර ඇත්නම්, පුරනය වීමට ඔබේ සීරීම් කේතය භාවිතා කරන්න. twofa_scratch_token_incorrect=ඔබේ සීරීම් කේතය වැරදියි. login_userpass=පිවිසෙන්න -login_openid=විවෘතහැඳු. +tab_openid=විවෘතහැඳු. oauth_signup_tab=නව ගිණුමක් ලියාපදිංචි කරන්න oauth_signup_title=නව ගිණුම සම්පූර්ණ කරන්න oauth_signup_submit=ගිණුම සම්පූර්ණ කරන්න @@ -579,7 +579,7 @@ gpg_invalid_token_signature=සපයන ලද GPG යතුර, අත්ස gpg_token_required=පහත ටෝකනය සඳහා ඔබ අත්සනක් ලබා දිය යුතුය gpg_token=ටෝකනය gpg_token_help=ඔබට අත්සනක් ජනනය කළ හැකිය: -gpg_token_code=දෝංකාරය "%s" | gpg -a -පැහැර හැරීම-යතුර %s —වෙන්ච-සිග් +gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=සන්නද්ධ GPG අත්සන key_signature_gpg_placeholder=ආරම්භ වන්නේ '—ආරම්භ කරන්න PGP සිග්නේටුර්—' ssh_key_verified=සත්යාපිත යතුර @@ -1269,8 +1269,8 @@ pulls.nothing_to_compare=මෙම ශාඛා සමාන වේ. අදි pulls.nothing_to_compare_and_allow_empty_pr=මෙම ශාඛා සමාන වේ. මෙම මහජන සම්බන්ධතා හිස් වනු ඇත. pulls.has_pull_request=`මෙම ශාඛා අතර අදින්න ඉල්ලීම දැනටමත් පවතී: %[2]s #%[3]d` pulls.create=අදින්න ඉල්ලීම නිර්මාණය -pulls.title_desc=%[1]d සිට %[2]s දක්වා %[3]s -pulls.merged_title_desc=මර්ජ්%[1]d සිට %[2]s දක්වා %[3]s %[4]s +pulls.title_desc_few=%[1]d සිට %[2]s දක්වා %[3]s +pulls.merged_title_desc_few=මර්ජ්%[1]d සිට %[2]s දක්වා %[3]s %[4]s pulls.change_target_branch_at=`ඉලක්කගත ශාඛාව %s සිට %s %sදක්වා වෙනස් කර ඇත` pulls.tab_conversation=සංවාදය pulls.tab_commits=විවරයන් diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index 758b01573d..549e3a7a1f 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -149,7 +149,7 @@ footer.links=Odkazy [heatmap] number_of_contributions_in_the_last_12_months=%s príspevkov za posledných 12 mesiacov -no_contributions=Žiadne príspevky +contributions_zero=Žiadne príspevky less=Menej more=Viac @@ -384,7 +384,7 @@ twofa_scratch_used=Použili ste pomocný kód. Boli ste presmerovaní na stránk twofa_passcode_incorrect=Váš prístupový kód je nesprávny. Ak ste vaše zariadenie umiestnili nesprávne, použite pomocný kód na prihlásenie. twofa_scratch_token_incorrect=Váš pomocný kód je nesprávny. login_userpass=Prihlásiť sa -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Zaregistrovať nový účet oauth_signup_title=Dokončiť nový účet oauth_signup_submit=Dokončiť účet diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index d117230aa2..01404dd0b4 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -122,6 +122,14 @@ confirm_delete_artifact = Ste prepričani, da želite izbrisati artefakt "%s"? concept_code_repository = Repozitorij error404 = Stran, ki jo poskušate doseči, ne obstaja ali niste pooblaščeni za njen ogled. toggle_menu = Toggle Meni +filter.is_fork = Vtičnica +filter.not_fork = Ni vstavljena vilica +filter.is_mirror = Zrcalni +filter.not_mirror = Ni zrcaljen +filter.public = Javni +filter.private = Zasebno +filter.not_archived = Ni arhivirano +pull_requests = Zahteve za umik [install] reinstall_confirm_check_3 = Potrjujete, da ste popolnoma prepričani, da se ta program Forgejo izvaja s pravilno lokacijo app.ini, in da ste prepričani, da ga morate znova namestiti. Potrjujete, da se zavedate zgoraj navedenih tveganj. @@ -131,8 +139,8 @@ reinstall_confirm_message = Ponovna namestitev z obstoječo zbirko podatkov Forg err_admin_name_is_reserved = Administrator Uporabniško ime je neveljavno, uporabniško ime je rezervirano disable_gravatar_popup = Onemogočite vire avatarjev Gravatar in avatarje tretjih oseb. Uporabi se privzeti avatar, razen če uporabnik lokalno naloži avatar. install = Namestitev -title = Začetna konfiguracija -db_title = Nastavitve zbirke podatkov +title = Začetna nastavitev +db_title = Nastavitve podatkovne zbirke db_type = Vrsta zbirke podatkov host = Gostitelj db_schema_helper = Pustite prazno za privzeto zbirko podatkov ("public"). @@ -230,6 +238,8 @@ enable_update_checker = Omogočite preverjanje posodobitev enable_update_checker_helper = Redno preverja izdaje novih različic tako, da se poveže s spletnim mestom gitea.io. env_config_keys = Konfiguracija okolja env_config_keys_prompt = V konfiguracijski datoteki bodo uporabljene tudi naslednje okoljske spremenljivke: +smtp_from_invalid = Naslov "Pošlji e-pošto kot" je neveljaven +smtp_from_helper = e-poštni naslov, ki ga bo uporabljal Forgejo. Vnesite navaden e-poštni naslov ali uporabite obliko "Ime" . [admin] users.allow_git_hook_tooltip = Kljuke Git se izvajajo kot uporabnik operacijskega sistema, v katerem je nameščen program Forgejo, in imajo enako raven dostopa do gostitelja. Uporabniki s tem posebnim privilegijem Git Hook lahko dostopajo do vseh skladišč Forgejo in spreminjajo vse zbirke Forgejo ter podatkovno bazo, ki jo uporablja Forgejo. Posledično lahko pridobijo tudi skrbniške privilegije Forgejo. @@ -239,6 +249,24 @@ users.purge_help = Prisilno izbrišite uporabnika in vsa skladišča, organizaci auths.sspi_default_language_helper = Privzet jezik za uporabnike, samodejno ustvarjene z metodo avtentikacije SSPI. Pustite prazno, če želite, da se jezik zazna samodejno. auths.restricted_filter_helper = Pustite prazno, če ne želite nastaviti nobenega uporabnika kot omejenega. Uporabite zvezdico ("*"), če želite vse uporabnike, ki se ne ujemajo z administratorskim filtrom, nastaviti kot omejene. auths.tip.twitter = Pojdite na https://dev.twitter.com/apps, ustvarite aplikacijo in preverite, ali je omogočena možnost "Allow this application to be used to Sign in with Twitter" +auths.tip.yandex = Ustvarite novo aplikacijo na spletnem mestu https://oauth.yandex.com/client/new. V razdelku "Yandex.Passport API" izberite naslednja dovoljenja: "Dostop do e-poštnega naslova", "Dostop do avatarja uporabnika" in "Dostop do uporabniškega imena, imena in priimka, spola" +config.git_migrate_timeout = Časovna omejitev migracije +config.git_gc_args = Argumenti GC +config.git_max_diff_files = Prikazane največje razlike v datotekah +notices.system_notice_list = Sistemska obvestila +monitor.cron = Opravila Cron +config.access_log_template = Predloga dnevnika dostopa +config.access_log_mode = Način beleženja dostopa +config.git_gc_timeout = GC Časovni rok delovanja +config.git_pull_timeout = Potegnite Časovni rok delovanja +config.git_clone_timeout = Časovni limit operacije kloniranja +config.git_mirror_timeout = Časovna omejitev posodobitve zrcala +config.git_max_diff_lines = Največ razlikovalnih vrstic na datoteko +config.git_disable_diff_highlight = Onemogočite razlikovanje sintakse +config.git_config = Konfiguracija Git +config.git_max_diff_line_characters = Največ različnih znakov na vrstico +notices.view_detail_header = Podrobnosti obvestila +config.log_config = Konfiguracija dnevnika [repo] migrate.github_token_desc = Tu lahko vstavite enega ali več žetonov, ločenih z vejico, da bo selitev hitrejša zaradi omejitve hitrosti GitHub API. OPOZORILO: Zloraba te funkcije lahko krši pravila ponudnika storitev in povzroči blokado računa. @@ -250,6 +278,10 @@ settings.githook_edit_desc = Če kavelj ni aktiven, se prikaže vzorčna vsebina editor.file_changed_while_editing = Vsebina datoteke se je od začetka urejanja spremenila. Klikni tukaj, da si jih ogledaš, ali Sprejemi spremembe znova, da jih prepišeš. settings.webhook.delivery.success = Dogodek je bil dodan v čakalno vrsto za dostavo. Lahko traja nekaj sekund, preden se prikaže v zgodovini dostave. editor.filename_help = Dodajte imenik tako, da vnesete njegovo ime, ki mu sledi poševnica ("/"). Imenik odstranite tako, da na začetku vnosnega polja vtipkate backspace. +settings.transfer_notices_3 = - Če je skladišče zasebno in je preneseno na posameznega uporabnika, to dejanje zagotovi, da ima uporabnik vsaj dovoljenje za branje (in po potrebi spremeni dovoljenja). +settings.protect_status_check_patterns_desc = Vnesite vzorce, s katerimi določite, kateri pregledi stanja morajo biti uspešno opravljeni, preden se veje lahko združijo v vejo, ki ustreza temu pravilu. Vsaka vrstica določa vzorec. Vzorci ne smejo biti prazni. +settings.unarchive.button = Odprava arhiviranja repozitorija +settings.archive.header = Arhiviranje tega repozitorija [editor] buttons.list.ordered.tooltip = Dodajte oštevilčen seznam @@ -340,11 +372,12 @@ webauthn = Dvofaktorsko preverjanje pristnosti (varnostni ključi) change_username_redirect_prompt = Staro uporabniško ime bo preusmerjeno, dokler ga nekdo ne prevzame. orgs = Upravljanje organizacij public_profile = Javni profil +gpg_key_verified_long = Ključ je bil preverjen z žetonom in ga je mogoče uporabiti za preverjanje zavez, ki ustrezajo vsem aktiviranim e-poštnim naslovom tega uporabnika, poleg vseh ujemajočih se identitet za ta ključ. [heatmap] less = Manj number_of_contributions_in_the_last_12_months = %s prispevkov v zadnjih 12 mesecih -no_contributions = Ni prispevkov +contributions_zero = Ni prispevkov more = Več [filter] @@ -461,7 +494,7 @@ email_domain_blacklisted = S svojim e-poštnim naslovom se ne morete registrirat authorize_application = Odobritev vloge authorize_application_description = Če mu dovolite dostop, bo lahko dostopal do vseh informacij o vašem računu, vključno z zasebnimi skladišči in organizacijami, in pisal vanje. remember_me = Ne pozabite na to napravo -login_openid = Odprta identiteta +tab_openid = Odprta identiteta [home] show_both_archived_unarchived = Prikazovanje arhiviranih in nearhiviranih @@ -536,6 +569,13 @@ release.new.text = @%[1]s izdal %[2]s v %[3]s repo.transfer.subject_to = %s želi prenesti "%s" na %s repo.transfer.subject_to_you = %s želi prenesti "%s" na vas repo.collaborator.added.text = Dodani ste kot sodelavec repozitorija: +issue.action.merge = @%[1]s je združil #%[2]d v %[3]s. +issue.action.push_1 = @%[1]s potisnil %[3]d commit v %[2]s +issue.action.push_n = @%[1]s potisnil %[3]d zavez v %[2]s +issue.action.review = @%[1]s je komentiral/a to zahtevo. +issue.action.review_dismissed = @%[1]s je zavrnil zadnji pregled %[2]s za to zahtevo. +issue.action.ready_for_review = @%[1]s je to zahtevo označil kot pripravljeno za pregled. +issue.in_tree_path = V %s: [modal] confirm = Potrdite @@ -613,6 +653,15 @@ regex_pattern_error = ` regex vzorec je neveljaven: %s.` url_error = `"%s" ni veljaven naslov URL.` TreeName = Pot do datoteke username_error_no_dots = ` lahko vsebuje samo alfanumerične znake ("0-9", "a-z", "A-Z"), pomišljaj ("-") in podčrtaj ("_"). Ne sme se začeti ali končati z nealfanumeričnimi znaki, prepovedani pa so tudi zaporedni nealfanumerični znaki.` +AdminEmail = E-pošta upravitelja +CommitSummary = Povzetek obveznosti +CommitMessage = Sporočilo o zavezanosti +CommitChoice = Izbira obveznosti +invalid_ssh_key = Ni mogoče preveriti vašega ključa SSH: %s +invalid_gpg_key = Ni mogoče preveriti ključa GPG: %s +unable_verify_ssh_key = Ključa SSH ni mogoče preveriti, zato ga dvakrat preverite, ali v njem ni napak. +last_org_owner = Zadnjega uporabnika ne morete odstraniti iz skupine lastnikov. V organizaciji mora biti vsaj en lastnik. +cannot_add_org_to_team = Organizacije ni mogoče dodati kot člana skupine. [user] form.name_chars_not_allowed = Uporabniško ime "%s" vsebuje neveljavne znake. @@ -639,4 +688,7 @@ follow_blocked_user = Temu uporabniku ne morete slediti, ker ste ga blokirali al code = Koda [packages] -owner.settings.chef.keypair.description = Za preverjanje pristnosti v registru Chef je potreben par ključev. Če ste par ključev ustvarili že prej, se pri ustvarjanju novega para ključev stari par ključev zavrže. \ No newline at end of file +owner.settings.chef.keypair.description = Za preverjanje pristnosti v registru Chef je potreben par ključev. Če ste par ključev ustvarili že prej, se pri ustvarjanju novega para ključev stari par ključev zavrže. + +[actions] +runners.runner_manage_panel = Upravljanje tekačev diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 5c5736013c..38382a6f66 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -274,7 +274,7 @@ twofa_scratch_used=Du har använt din skrapkod. Du har blivit omdirigerad till t twofa_passcode_incorrect=Din kod är inte giltig. Om du har tappat bort din enhet, använd din skrapkod för att logga in. twofa_scratch_token_incorrect=Din skrapkod är ogiltlig. login_userpass=Logga in -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Skapa nytt konto oauth_signup_title=Slutför nytt konto oauth_signup_submit=Slutför kontot @@ -1088,8 +1088,8 @@ pulls.filter_branch=Filtrera gren pulls.no_results=Inga resultat hittades. pulls.nothing_to_compare=Dessa brancher är ekvivalenta. Det finns ingen anledning att skapa en pull-request. pulls.create=Skapa Pullförfrågan -pulls.title_desc=vill sammanfoga %[1]d incheckningar från s[2]s in i %[3]s -pulls.merged_title_desc=sammanfogade %[1]d incheckningar från %[2]s in i %[3]s %[4]s +pulls.title_desc_few=vill sammanfoga %[1]d incheckningar från s[2]s in i %[3]s +pulls.merged_title_desc_few=sammanfogade %[1]d incheckningar från %[2]s in i %[3]s %[4]s pulls.change_target_branch_at=`ändrade mål-branch från %s till %s%s` pulls.tab_conversation=Konversation pulls.tab_commits=Incheckningar diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 0702262f9c..74ef77eb19 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -150,7 +150,7 @@ footer.links=Bağlantılar [heatmap] number_of_contributions_in_the_last_12_months=son 12 ayda %s katkı -no_contributions=Katkı yapılmamış +contributions_zero=Katkı yapılmamış less=Daha az more=Daha Fazla @@ -395,7 +395,7 @@ twofa_scratch_used=Geçici kodunuzu kullandınız. İki aşamalı ayarlar sayfas twofa_passcode_incorrect=Şifreniz yanlış. Aygıtınızı yanlış yerleştirdiyseniz, oturum açmak için çizgi kodunuzu kullanın. twofa_scratch_token_incorrect=Çizgi kodunuz doğru değildir. login_userpass=Oturum Aç -login_openid=Açık Kimlik +tab_openid=Açık Kimlik oauth_signup_tab=Yeni Hesap Oluştur oauth_signup_title=Yeni Hesabı Tamamla oauth_signup_submit=Hesabı Tamamla @@ -1712,8 +1712,8 @@ pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gere pulls.nothing_to_compare_and_allow_empty_pr=Bu dallar eşittir. Bu Dİ boş olacak. pulls.has_pull_request=`Bu dallar arasında zaten bir değişiklik isteği var: %[2]s#%[3]d` pulls.create=Değişiklik İsteği Oluştur -pulls.title_desc=%[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirmek istiyor -pulls.merged_title_desc=%[4]s %[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirdi +pulls.title_desc_few=%[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirmek istiyor +pulls.merged_title_desc_few=%[4]s %[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirdi pulls.change_target_branch_at='hedef dal %s adresinden %s%s adresine değiştirildi' pulls.tab_conversation=Sohbet pulls.tab_commits=İşleme diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index a5a2050577..0a79e54010 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -353,7 +353,7 @@ twofa_scratch_used=Ви використовували одноразовий п twofa_passcode_incorrect=Ваш пароль є невірним. Якщо ви втратили пристрій, використовуйте ваш одноразовий пароль. twofa_scratch_token_incorrect=Невірний одноразовий пароль. login_userpass=Увійти -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=Зареєструвати обліковий запис oauth_signup_title=Повний новий обліковий запис oauth_signup_submit=Повний обліковий запис @@ -1385,8 +1385,8 @@ pulls.nothing_to_compare=Ці гілки однакові. Немає необх pulls.nothing_to_compare_and_allow_empty_pr=Одинакові гілки. Цей PR буде порожнім. pulls.has_pull_request=`Запит злиття для цих гілок вже існує: %[2]s#%[3]d` pulls.create=Створити запит на злиття -pulls.title_desc=хоче злити %[1]d комітів з %[2]s в %[3]s -pulls.merged_title_desc=злито %[1]d комітів з %[2]s до %[3]s %[4]s +pulls.title_desc_few=хоче злити %[1]d комітів з %[2]s в %[3]s +pulls.merged_title_desc_few=злито %[1]d комітів з %[2]s до %[3]s %[4]s pulls.change_target_branch_at=`змінена цільова гілка з %s на %s %s` pulls.tab_conversation=Обговорення pulls.tab_commits=Коміти diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 8e2dee8ec2..bc768afc6f 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -124,7 +124,7 @@ pin=固定 unpin=取消置顶 artifacts=制品 -confirm_delete_artifact=您确定要删除制品'%s'吗? +confirm_delete_artifact=您确定要删除制品“%s”吗? archived=已归档 @@ -142,6 +142,19 @@ confirm_delete_selected=确认删除所有选中项目? name=名称 value=值 +filter = 筛选 +filter.clear = 清除筛选条件 +filter.is_archived = 已归档 +filter.not_archived = 未归档 +filter.is_fork = 已派生 +filter.not_fork = 未派生 +filter.is_mirror = 已镜像 +filter.not_mirror = 未镜像 +filter.is_template = 模板 +filter.not_template = 非模板 +filter.public = 公开 +filter.private = 私有 +toggle_menu = 菜单 [aria] navbar=导航栏 @@ -151,9 +164,10 @@ footer.links=链接 [heatmap] number_of_contributions_in_the_last_12_months=一年内 %s 次贡献 -no_contributions=目前还没有贡献。 +contributions_zero=目前还没有贡献。 less=更少的 more=更多的 +contributions_format = {contributions} 于 {month} {day}, {year} [editor] buttons.heading.tooltip=添加标题 @@ -182,6 +196,7 @@ missing_csrf=错误的请求:没有 CSRF 令牌 invalid_csrf=错误的请求:无效的 CSRF 令牌 not_found=找不到目标。 network_error=网络错误 +server_internal = 服务器内部错误 [startpage] app_desc=一款极易搭建的自助 Git 服务 @@ -234,7 +249,7 @@ run_user=以用户名运行 run_user_helper=输入 Forgejo 运行的操作系统用户名。请注意,此用户必须具有对仓库根路径的访问权限。 domain=服务器域名 domain_helper=服务器的域名或主机地址。 -ssh_port=SSH 服务端口 +ssh_port=SSH 服务器端口 ssh_port_helper=SSH 服务器的端口号,为空则禁用它。 http_port=HTTP 服务端口 http_port_helper=Forgejos web 服务器将侦听的端口号。 @@ -278,8 +293,8 @@ admin_password=管理员密码 confirm_password=确认密码 admin_email=电子邮件地址 install_btn_confirm=立即安装 -test_git_failed=无法识别 'git' 命令:%v -sqlite3_not_available=您所使用的发行版不支持 SQLite3,请从 %s 下载官方构建版,而不是 gobuild 版本。 +test_git_failed=无法识别 “git” 命令:%v +sqlite3_not_available=当前 Forgejo 版本不支持 SQLite3。请从 %s 下载官方构建版(注:请勿下载标有 “gobuild” 的版本)。 invalid_db_setting=数据库设置无效: %v invalid_db_table=数据库表 '%s' 无效: %v invalid_repo_path=仓库根目录设置无效:%v @@ -297,7 +312,7 @@ default_allow_create_organization_popup=默认情况下, 允许新用户帐户 default_enable_timetracking=默认情况下启用时间跟踪 default_enable_timetracking_popup=默认情况下启用新仓库的时间跟踪。 no_reply_address=隐藏电子邮件 -no_reply_address_helper=具有隐藏电子邮件地址的用户的域名。例如, 用户名 "joe" 将以 "joe@noreply.example.org" 的身份登录到 Git 中. 如果隐藏的电子邮件域设置为 "noreply.example.org"。 +no_reply_address_helper=用于设置隐藏电子邮件地址的用户使用的电子邮件域名。例如,如果用于隐藏电子邮件地址的域名设为“noreply.example.org”,则用户名 “joe” 在 Git 中将以 “joe@noreply.example.org” 表示。 password_algorithm=密码哈希算法 invalid_password_algorithm=无效的密码哈希算法 password_algorithm_helper=设置密码散列算法。算法有不同的要求和强度。 argon2 算法相当安全,但使用大量内存,因此可能不适合小型系统。 @@ -307,6 +322,8 @@ env_config_keys=环境配置 env_config_keys_prompt=以下环境变量也将应用于您的配置文件: allow_dots_in_usernames = 允许用户在用户名中使用英文句号。不影响已有的帐户。 enable_update_checker_helper_forgejo = 通过检查 release.forgejo.org 上的 DNS TXT 记录,定期检查 Forgejo 的新版本。 +smtp_from_invalid = 电子邮件发件人地址无效 +config_location_hint = 这些配置项将被保存在: [home] uname_holder=用户名或邮箱 @@ -356,6 +373,10 @@ code_search_results=“%s” 的搜索结果是 code_last_indexed_at=最后索引于 %s relevant_repositories_tooltip=派生的仓库,以及缺少主题、图标和描述的仓库将被隐藏。 relevant_repositories=只显示相关的仓库, 显示未过滤结果。 +stars_one = %d 点赞 +stars_few = %d 点赞 +forks_one = %d 派生 +forks_few = %d 派生 [auth] create_new_account=注册帐号 @@ -398,7 +419,7 @@ twofa_scratch_used=你已经使用了你的验证口令。你将会转到两步 twofa_passcode_incorrect=你的验证码不正确。如果你丢失了你的设备,请使用你的验证口令。 twofa_scratch_token_incorrect=你的验证口令不正确。 login_userpass=登录 -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=注册帐号 oauth_signup_title=完成新帐户 oauth_signup_submit=完成账号 @@ -428,11 +449,16 @@ sspi_auth_failed=SSPI 认证失败 password_pwned=此密码出现在 被盗密码 列表上并且曾经被公开。 请使用另一个密码再试一次。 password_pwned_err=无法完成对 HaveIBeenPwned 的请求 last_admin=您不能删除最后一个管理员。必须至少保留一个管理员。 +change_unconfirmed_email = 如果您在注册时提供了错误的邮箱地址,您可以在下方修改,激活邮件会发送到修改后的邮箱地址。 +change_unconfirmed_email_summary = 修改用来接收激活邮件的邮箱地址。 +change_unconfirmed_email_error = 无法修改邮箱地址: %v +tab_signin = 登录 +tab_signup = 注册 [mail] view_it_on=在 %s 上查看 reply=或直接回复此邮件 -link_not_working_do_paste=不起作用?尝试复制并粘贴到您的浏览器。 +link_not_working_do_paste=链接点不了?尝试复制并粘贴到您的浏览器。 hi_user_x=%s 您好, activate_account=请激活您的帐户 @@ -447,12 +473,12 @@ activate_email.text=请在 %s 时间内,点击以下链接,以验证 register_notify=欢迎来到 Forgejo register_notify.title=%[1]s,欢迎来到 %[2]s register_notify.text_1=这是您的 %s 注册确认电子邮件 ! -register_notify.text_2=您现在可以以用户名 %s 登录。 -register_notify.text_3=如果此账户已为您创建,请先 设置您的密码。 +register_notify.text_2=您现在可以以用户名 %s 登录 +register_notify.text_3=如果此账户是别人为您创建的,请先 设置您的密码。 reset_password=恢复您的账户 -reset_password.title=%s,您已请求恢复您的帐户 -reset_password.text=请在 %s 时间内,点击以下链接,恢复你的账户: +reset_password.title=%s,我们收到了恢复您的帐户的请求 +reset_password.text=如果此请求是您本人作出的,则请在 %s 时间内,点击以下链接,恢复你的账户: register_success=注册成功 @@ -482,12 +508,12 @@ release.downloads=下载: release.download.zip=源代码 (ZIP) release.download.targz=源代码 (TAR.GZ) -repo.transfer.subject_to=%s 想要将 "%s" 转让给 %s -repo.transfer.subject_to_you=%s 想要将 "%s" 转让给你 +repo.transfer.subject_to=%s 想要将 "%s" 仓库转让给 %s +repo.transfer.subject_to_you=%s 想要将 "%s" 仓库转让给你 repo.transfer.to_you=你 repo.transfer.body=访问 %s 以接受或拒绝转移,亦可忽略此邮件。 -repo.collaborator.added.subject=%s 把你添加到了 %s +repo.collaborator.added.subject=%s 已将你作为协作者添加到 %s repo.collaborator.added.text=您已被添加为代码库的协作者: team_invite.subject=%[1]s 邀请您加入组织 %[2]s @@ -529,8 +555,8 @@ SSPISeparatorReplacement=分隔符 SSPIDefaultLanguage=默认语言 require_error=不能为空。 -alpha_dash_error=应该只包含字母数字、破折号 ('-') 和下划线 ('_') 字符。 -alpha_dash_dot_error=` 应该只包含字母数字, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。` +alpha_dash_error=` 只允许包含字母数字、破折号(“-”)和下划线(“_”)字符。 +alpha_dash_dot_error=` 应该只包含半角字母、数字、破折号(“-”)、下划线(“_”)和半角句号(“.”) 。` git_ref_name_error=` 必须是格式良好的 git 引用名称。` size_error=长度必须为 %s。 min_size_error=长度最小为 %s 个字符。 @@ -540,7 +566,7 @@ url_error=`'%s' 不是一个有效的 URL。` include_error=`必须包含子字符串 "%s"。` glob_pattern_error=`匹配模式无效:%s.` regex_pattern_error=`正则表达式无效:%s.` -username_error=` 只能包含字母数字字符('0-9','a-z','A-Z'), 破折号 ('-'), 下划线 ('_') 和点 ('.'). 不能以非字母数字字符开头或结尾,并且不允许连续的非字母数字字符。` +username_error=` 只允许包含字母数字字符(“0-9”、“a-z”、“A-Z”)、破折号(“-”)、下划线(“_”)和点(“.”)。不能以非字母数字字符开头或结尾,并且不允许连续的非字母数字字符。` invalid_group_team_map_error=`映射无效: %s` unknown_error=未知错误: captcha_incorrect=验证码不正确。 @@ -576,7 +602,7 @@ enterred_invalid_owner_name=新的所有者名称无效。 enterred_invalid_password=输入的密码不正确 user_not_exist=该用户不存在 team_not_exist=团队不存在 -last_org_owner=您不能从 "所有者" 团队中删除最后一个用户。组织中必须至少有一个所有者。 +last_org_owner=您不能从“所有者”团队中删除最后一个用户。组织中必须至少有一个所有者。 cannot_add_org_to_team=组织不能被加入到团队中。 duplicate_invite_to_team=此用户已被邀请为团队成员。 organization_leave_success=您已成功离开组织 %s。 @@ -589,16 +615,18 @@ unable_verify_ssh_key=无法验证 SSH 密钥,请仔细检查是否有错误 auth_failed=授权验证失败:%v still_own_repo=此帐户仍拥有至少一个仓库,您需要先删除或转移它们。 -still_has_org=此帐户隶属于一个或多个组织,请先退出这些组织。 +still_has_org=此帐户目前是一个或多个组织的成员,请先退出这些组织。 still_own_packages=您的账户拥有一个或多个软件包,请先删除它们。 -org_still_own_repo=该组织仍然是某些仓库的拥有者,请先删除或转移它们! -org_still_own_packages=该组织仍然是一个或多个软件包的拥有者,请先删除它们。 +org_still_own_repo=该组织下仍有仓库,请先删除或转移它们。 +org_still_own_packages=该组织下仍有软件包,请先删除它们。 target_branch_not_exist=目标分支不存在。 -username_error_no_dots = ` 只能包含英文字母与数字 ('0-9','a-z','A-Z'), 连字符 ('-') 与下划线 ('_')。 开头与结尾的字符只能使用英文字母或数字,且不能包含连续的非字母非数字字符。` +username_error_no_dots = ` 只能包含英文字母与数字(“0-9”、“a-z”、“A-Z”)、横杠(“-”) 与下划线(“_”)。 开头与结尾的字符只能使用英文字母或数字,且不能包含连续的非字母非数字字符。` admin_cannot_delete_self = 您无法以管理员的身份删除自己。请先移除您的管理员权限。 admin_cannot_delete_self=当您是管理员时,您不能删除自己。请先移除您的管理员权限 +unsupported_login_type = 该账号使用的登录方式不支持删除此账户。 +unset_password = 当前登录用户尚未设置密码。 [user] change_avatar=修改头像 @@ -657,12 +685,12 @@ biography_placeholder=告诉我们一点您自己! (您可以使用Markdown) location_placeholder=与他人分享你的大概位置 profile_desc=控制您的个人资料对其他用户的显示方式。您的主要电子邮件地址将用于通知、密码恢复和基于网页界面的 Git 操作。 password_username_disabled=不允许非本地用户更改他们的用户名。更多详情请联系您的系统管理员。 -full_name=自定义名称 +full_name=全名 website=个人网站 location=所在地区 -update_theme=更新主题 -update_profile=更新信息 -update_language=更新语言 +update_theme=更换主题 +update_profile=更新个人资料 +update_language=更改语言 update_language_not_found=语言 %s 不可用。 update_language_success=语言已更新。 update_profile_success=您的资料信息已经更新 @@ -696,7 +724,7 @@ privacy=隐私设置 keep_activity_private=隐藏个人资料页面中的活动 keep_activity_private_popup=使活动仅对您和管理员可见 -lookup_avatar_by_mail=从电子邮箱地址查找头像 +lookup_avatar_by_mail=使用电子邮箱地址查找头像 federated_avatar_lookup=Federated Avatar 查找 enable_custom_avatar=启动自定义头像 choose_new_avatar=选择新的头像 @@ -737,7 +765,7 @@ theme_update_error=所选主题不存在。 openid_deletion=移除 OpenID 地址 openid_deletion_desc=删除此 OpenID 地址将会阻止你使用它进行登录。你确定要继续吗? openid_deletion_success=OpenID地址已被移除。 -add_new_email=添加新的邮箱地址 +add_new_email=添加邮箱地址 add_new_openid=添加新的 OpenID URI add_email=增加电子邮件地址 add_openid=添加 OpenID URI @@ -745,7 +773,7 @@ add_email_confirmation_sent=一封确认邮件已经被发送至 %s,请检查 add_email_success=新的电子邮件地址已添加。 email_preference_set_success=电子邮件首选项已成功设置。 add_openid_success=新的 OpenID 地址已添加。 -keep_email_private=隐藏电子邮件地址 +keep_email_private=隐藏邮箱地址 keep_email_private_popup=这将会隐藏您的电子邮件地址,不仅在您的个人资料中,还在您使用Web界面创建拉取请求或编辑文件时。已推送的提交将不会被修改。 openid_desc=OpenID 让你可以将认证转发到外部服务。 @@ -753,15 +781,15 @@ manage_ssh_keys=管理 SSH 密钥 manage_ssh_principals=管理SSH证书规则 manage_gpg_keys=管理 GPG 密钥 add_key=增加密钥 -ssh_desc=这些 SSH 公钥已经关联到你的账号。相应的私钥拥有完全操作你的仓库的权限。 +ssh_desc=这些 SSH 公钥已经关联到你的账号。相应的私钥拥有完全操作你的仓库的权限,且已验证的 SSH 密钥可以用于验证 SSH 签名的 Git 提交。 principal_desc=这些SSH证书规则已关联到你的账号将允许完全访问你的所有仓库。 -gpg_desc=这些 GPG 公钥已经关联到你的账号。请妥善保管你的私钥因为他们将被用于认证提交。 +gpg_desc=这些 GPG 公钥已经关联到你的账号,并用于验证您的提交。请妥善保管你的私钥,因为这些私钥可以用于以你的身份签名提交。 ssh_helper=需要帮助? 请查看有关 如何生成 SSH 密钥常见 SSH 问题 寻找答案。 gpg_helper=需要帮助吗?看一看 GitHub 关于GPG 的指导。 add_new_key=增加 SSH 密钥 add_new_gpg_key=添加的 GPG 密钥 -key_content_ssh_placeholder=以 'ssh-ed25519'、 'ssh-rsa'、 'ecdsa-sha2-nistp256'、'ecdsa-sha2-nistp384'、'ecdsa-sha2-nistp521'、 'sk-ecdsa-sha2-nistp256@openssh.com' 或 'sk-ssh-ed25519@openssh.com' 开头 -key_content_gpg_placeholder=以 '-----BEGIN PGP PUBLIC KEY BLOCK-----' 开头 +key_content_ssh_placeholder=以“ssh-ed25519”、“ssh-rsa”、“ecdsa-sha2-nistp256”、“ecdsa-sha2-nistp384”、“ecdsa-sha2-nistp521”、“sk-ecdsa-sha2-nistp256@openssh.com” 或 “sk-ssh-ed25519@openssh.com” 开头 +key_content_gpg_placeholder=以 “-----BEGIN PGP PUBLIC KEY BLOCK-----” 开头 add_new_principal=添加规则 ssh_key_been_used=此 SSH 密钥已添加到服务器。 ssh_key_name_used=使用相同名称的SSH公钥已经存在! @@ -779,7 +807,7 @@ gpg_token=令牌 gpg_token_help=您可以使用以下方式生成签名: gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=GPG 增强签名 -key_signature_gpg_placeholder=以 '-----BEGIN PGP PUBLIC KEY BLOCK-----' 开头 +key_signature_gpg_placeholder=以 “-----BEGIN PGP SIGNATURE-----” 开头 verify_gpg_key_success=GPG 密钥 %s 已被验证。 ssh_key_verified=已验证的密钥 ssh_key_verified_long=密钥已经用令牌进行了验证,并且可以用来验证匹配此用户任何已激活电子邮件地址的提交。 @@ -789,7 +817,7 @@ ssh_token_required=您必须为下面的令牌提供签名 ssh_token=令牌 ssh_token_help=您可以使用以下方式生成签名: ssh_token_signature=增强 SSH 签名 -key_signature_ssh_placeholder=以 '-----BEGIN SSH SIGNATURE -----' 开头 +key_signature_ssh_placeholder=以 “-----BEGIN SSH SIGNATURE -----” 开头 verify_ssh_key_success=SSH 密钥 %s 已被验证。 subkeys=子项 key_id=键ID @@ -829,7 +857,7 @@ social_desc=这些社交账户可以用来登录您的账户。确保您认识 unbind=取消链接 unbind_success=社交账户已成功移除。 -manage_access_token=管理 Access Token +manage_access_token=管理访问令牌 generate_new_token=生成新的令牌 tokens_desc=这些令牌拥有通过 Forgejo API 对您的帐户的访问权限。 token_name=令牌名称 @@ -837,7 +865,7 @@ generate_token=生成令牌 generate_token_success=新令牌生成成功。请拷贝因为令牌将只会显示一次。 generate_token_name_duplicate=%s 已被用作应用程序名称。请使用一个新的名称。 delete_token=删除令牌 -access_token_deletion=删除 Access Token +access_token_deletion=删除访问令牌 access_token_deletion_cancel_action=取消 access_token_deletion_confirm_action=刪除 access_token_deletion_desc=删除令牌将撤销程序对您账户的访问权限。此操作无法撤消。是否继续? @@ -878,7 +906,7 @@ oauth2_application_remove_description=移除一个OAuth2应用将会阻止它访 oauth2_application_locked=如果配置启用,Forgejo预注册一些OAuth2应用程序。 为了防止意外的行为, 这些应用既不能编辑也不能删除。请参阅OAuth2文档以获取更多信息。 authorized_oauth2_applications=已授权的 OAuth2 应用 -authorized_oauth2_applications_description=您已授予这些第三方应用程序访问您的个人 Forgejo 账户的权限。请撤销那些您不再需要的应用程序的访问权限。 +authorized_oauth2_applications_description=您已授予这些第三方应用程序访问您的个人 Forgejo 账户的权限。请撤销那些您不再使用的应用程序的访问权限。 revoke_key=撤销 revoke_oauth2_grant=撤回权限 revoke_oauth2_grant_description=确定撤销此三方应用程序的授权,并阻止此应用程序访问您的数据? @@ -889,7 +917,7 @@ twofa_recovery_tip=如果您丢失了您的设备,您将能够使用一次性 twofa_is_enrolled=你的账号已启用了两步验证。 twofa_not_enrolled=你的账号未开启两步验证。 twofa_disable=禁用两步认证 -twofa_scratch_token_regenerate=重新生成初始令牌 +twofa_scratch_token_regenerate=重新生成一次性恢复令牌 twofa_scratch_token_regenerated=您的临时令牌现在是 %s。将其存放在安全的地方,它将不会再次显示。 twofa_enroll=启用两步验证 twofa_disable_note=如果需要, 可以禁用双因素身份验证。 @@ -945,10 +973,11 @@ visibility.limited_tooltip=仅对已认证的用户可见 visibility.private=私有 visibility.private_tooltip=仅对您已加入的组织的成员可见。 blocked_users = 已屏蔽的用户 -blocked_users_none = 您未屏蔽任何用户。 +blocked_users_none = 黑名单中没有用户。 blocked_since = 自 %s 起被屏蔽 user_unblock_success = 已成功取消对该用户的屏蔽。 user_block_success = 已成功屏蔽该用户。 +change_password = 更改密码 [repo] new_repo_helper=代码仓库包含了所有的项目文件,包括版本历史记录。已经在其他地方托管了?迁移仓库。 @@ -1009,7 +1038,7 @@ default_branch_label=默认 default_branch_helper=默认分支是用于合并请求和代码提交的基础分支。 mirror_prune=修剪 mirror_prune_desc=删除过时的远程跟踪引用 -mirror_interval=镜像间隔 (有效的时间单位是 'h', 'm', 's')。0 禁用自动定期同步 (最短间隔: %s) +mirror_interval=镜像间隔(有效的时间单位是 “h"、“m”、“s”)。填 0 禁用自动定期同步。(最短间隔:%s) mirror_interval_invalid=镜像间隔无效。 mirror_sync=已同步 mirror_sync_on_commit=推送提交时同步 @@ -1104,12 +1133,12 @@ migrate_items_merge_requests=合并请求 migrate_items_releases=版本发布 migrate_repo=迁移仓库 migrate.clone_address=从 URL 迁移/克隆 -migrate.clone_address_desc=现有仓库的 HTTP(s) 或 Git "clone" URL +migrate.clone_address_desc=现有仓库的 HTTP(s) 或 Git “clone” URL migrate.github_token_desc=由于 GitHub API 速率限制,您可以在此处放置一个或多个以逗号分隔的令牌,以加快迁移速度。 警告:滥用此功能可能会违反服务提供商的政策并导致帐户被封。 migrate.clone_local_path=或服务器本地路径 migrate.permission_denied=您没有获得导入本地仓库的权限。 migrate.permission_denied_blocked=您不能从不允许的主机导入,请询问管理员以检查 ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS 设置。 -migrate.invalid_local_path=本地路径无效。它不存在或不是一个目录。 +migrate.invalid_local_path=本地路径不存在或不是一个目录。 migrate.invalid_lfs_endpoint=LFS 网址无效。 migrate.failed=迁移失败:%v migrate.migrate_items_options=需要访问令牌来迁移额外的内容 @@ -1129,12 +1158,12 @@ migrate.onedev.description=从 code.onedev.io 或其他 OneDev 实例迁移数 migrate.codebase.description=从 codebasehq.com 迁移数据 migrate.gitbucket.description=从 GitBucket 实例迁移数据 migrate.migrating_git=迁移Git数据 -migrate.migrating_topics=迁移主题 -migrate.migrating_milestones=迁移里程碑 -migrate.migrating_labels=迁移标签 -migrate.migrating_releases=迁移发布 -migrate.migrating_issues=迁移工单 -migrate.migrating_pulls=迁移合并请求 +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=您想要取消此次迁移吗? @@ -1206,10 +1235,10 @@ ambiguous_character=`%[1]c [U+%04[1]X] 容易和 %[2]c [U+%04[2]X] 混淆` escape_control_characters=Escape unescape_control_characters=Unescape -file_copy_permalink=复制永久链接 +file_copy_permalink=复制固定链接 view_git_blame=查看 Git Blame -video_not_supported_in_browser=您的浏览器不支持使用 HTML5 'video' 标签。 -audio_not_supported_in_browser=您的浏览器不支持使用 HTML5 'video' 标签。 +video_not_supported_in_browser=您的浏览器不支持 HTML5 “video” 标签。 +audio_not_supported_in_browser=您的浏览器不支持 HTML5 “audio” 标签。 stored_lfs=存储到Git LFS symbolic_link=符号链接 executable_file=可执行文件 @@ -1245,12 +1274,12 @@ editor.delete_this_file=删除文件 editor.must_have_write_access=您必须具有写权限才能对此文件进行修改操作。 editor.file_delete_success=文件 %s 已被删除。 editor.name_your_file=命名文件... -editor.filename_help=通过键入名称后跟斜线 ("/") 来添加目录。通过在输入框的开头键入 "退格" 来删除目录。 +editor.filename_help=通过键入名称后跟半角斜杠(“/”)来添加目录。在输入框的开头退格来删除目录。 editor.or=或 editor.cancel_lower=取消 editor.commit_signed_changes=提交已签名的更改 editor.commit_changes=提交变更 -editor.add_tmpl=添加 '' +editor.add_tmpl=添加 “” editor.add=添加 %s editor.update=更新 %s editor.delete=删除 %s @@ -1361,12 +1390,12 @@ projects.column.new_title=名称 projects.column.new_submit=创建列 projects.column.new=创建列 projects.column.set_default=设为默认 -projects.column.set_default_desc=设置此列为未分类问题和合并请求的默认值 +projects.column.set_default_desc=设置此列为未分类工单和合并请求的默认值 projects.column.unset_default=取消设为默认 projects.column.unset_default_desc=取消此列为默认值 projects.column.delete=删除列 -projects.column.deletion_desc=删除项目列会将所有相关问题移到“未分类”。是否继续? -projects.column.color=彩色 +projects.column.deletion_desc=删除项目列会将所有相关工单移到“未分类”。是否继续? +projects.column.color=颜色 projects.open=开启 projects.close=关闭 projects.column.assigned_to=指派给 @@ -1407,14 +1436,14 @@ issues.choose.blank_about=从默认模板创建一个工单。 issues.choose.ignore_invalid_templates=已忽略无效模板 issues.choose.invalid_templates=发现了 %v 个无效模板 issues.choose.invalid_config=问题配置包含错误: -issues.no_ref=分支/标记未指定 +issues.no_ref=未指定分支或标签 issues.create=创建工单 issues.new_label=创建标签 issues.new_label_placeholder=标签名称 issues.new_label_desc_placeholder=描述 issues.create_label=创建标签 issues.label_templates.title=加载预定义的标签模板 -issues.label_templates.info=还没有任何标签。您可以使用'创建标签'按钮或者加载预定义的标签集创建标签 +issues.label_templates.info=还没有任何标签。您可以使用“创建标签”按钮或者加载预定义的标签集创建标签: issues.label_templates.helper=选择标签模板 issues.label_templates.use=使用标签集 issues.label_templates.fail_to_load_file=加载标签模板文件 %s 时发生错误:%v @@ -1575,19 +1604,19 @@ issues.subscribe=订阅 issues.unsubscribe=取消订阅 issues.unpin_issue=取消置顶 issues.max_pinned=您不能置顶更多工单 -issues.pin_comment=于 %s 被置顶 -issues.unpin_comment=于 %s 取消置顶 +issues.pin_comment=于 %s 置顶本工单 +issues.unpin_comment=于 %s 取消置顶本工单 issues.lock=锁定对话 issues.unlock=解锁对话 issues.lock.unknown_reason=由于未知原因无法锁定。 issues.lock_duplicate=一个工单不能被锁定两次。 issues.unlock_error=无法解锁一个未锁定的工单。 -issues.lock_with_reason=因为 %s 而锁定,并将对话限制为协作者 %s -issues.lock_no_reason=锁定并限制仅协作者 %s -issues.unlock_comment=解锁此对话 %s +issues.lock_with_reason=于 %[2]s 以 %[1]s 锁定本工单,并限制仅限协作者发言 +issues.lock_no_reason=于 %s 锁定本议题并限制仅协作者可发言 +issues.unlock_comment=于 %s 解锁此议题 issues.lock_confirm=锁定 issues.unlock_confirm=解锁​​​​ -issues.lock.notice_1=- 其他用户不能对这个工单添加新的评论。 +issues.lock.notice_1=- 其他用户不能评论此工单。 issues.lock.notice_2=- 您和仓库其他协作者仍可评论并可见。 issues.lock.notice_3=- 您可以在未来再次解锁这个工单。 issues.unlock.notice_1=- 每个人都可以再次就这一工单发表评论。 @@ -1609,7 +1638,7 @@ issues.stop_tracking=停止计时器 issues.stop_tracking_history=`停止工作 %s` issues.cancel_tracking=放弃 issues.cancel_tracking_history=`取消时间跟踪 %s` -issues.add_time=手动添加时间 +issues.add_time=手动记录时间 issues.del_time=删除此时间跟踪日志 issues.add_time_short=添加时间 issues.add_time_cancel=取消 @@ -1621,24 +1650,24 @@ issues.add_time_sum_to_small=没有输入时间。 issues.time_spent_total=总用时 issues.time_spent_from_all_authors=`总花费时间:%s` issues.due_date=到期时间 -issues.invalid_due_date_format=到期时间的格式必须是 'yyyy-mm-dd' 的形式。 +issues.invalid_due_date_format=到期时间的格式必须是“yyyy-mm-dd”。 issues.error_modifying_due_date=修改到期时间失败。 issues.error_removing_due_date=删除到期时间失败。 issues.push_commit_1=于 %[2]s 推送了 %[1]d 个提交 issues.push_commits_n=于 %[2]s 推送了 %[1]d 个提交 issues.force_push_codes=`于 %[6]s 强制推送 %[1]s,从 %[2]s,至 %[4]s` issues.force_push_compare=比较 -issues.due_date_form=yyyy年mm月dd日 +issues.due_date_form=yyyy-mm-dd issues.due_date_form_add=设置到期时间 issues.due_date_form_edit=编辑 issues.due_date_form_remove=删除 issues.due_date_not_writer=您需要该仓库的写权限才能更新工单的到期日期。 issues.due_date_not_set=未设置到期时间。 issues.due_date_added=于 %[2]s 设置到期时间为 %[1]s -issues.due_date_modified=将到期日从 %[2]s 修改为 %[1]s %[3]s +issues.due_date_modified=于 %[3]s 将到期日从 %[2]s 修改为 %[1]s issues.due_date_remove=于 %[2]s 删除了到期时间 %[1]s -issues.due_date_overdue=过期 -issues.due_date_invalid=到期日期无效或超出范围。请使用 'yyyy-mm-dd' 格式。 +issues.due_date_overdue=超期 +issues.due_date_invalid=到期日期无效或超出范围。请使用“yyyy-mm-dd”格式。 issues.dependency.title=依赖工单 issues.dependency.issue_no_dependencies=没有设置依赖项。 issues.dependency.pr_no_dependencies=没有设置依赖项。 @@ -1678,11 +1707,11 @@ issues.review.dismissed=于 %[2]s 取消了 %[1]s 的评审 issues.review.dismissed_label=已取消 issues.review.left_comment=留下了一条评论 issues.review.content.empty=您需要留下一个注释,表明需要的更改。 -issues.review.reject=请求变更 %s -issues.review.wait=已请求 %s 审核 +issues.review.reject=于 %s 请求变更 +issues.review.wait=于 %s 请求审核 issues.review.add_review_request=于 %[2]s 请求 %[1]s 评审 -issues.review.remove_review_request=取消对 %s 的评审请求 %s -issues.review.remove_review_request_self=拒绝审核 %s +issues.review.remove_review_request=于 %[2]s 取消对 %[1]s 的评审请求 +issues.review.remove_review_request_self=于 %s 拒绝审核 issues.review.pending=待定 issues.review.pending.tooltip=此评论目前对其他用户不可见。 若要提交您的待定评论,请在页面顶部选择 %s -> %s/%s/%s。 issues.review.review=评审 @@ -1713,7 +1742,7 @@ compare.compare_head=比较 pulls.desc=启用合并请求和代码评审。 pulls.new=创建合并请求 -pulls.view=查看拉取请求 +pulls.view=审阅合并请求 pulls.compare_changes=创建合并请求 pulls.allow_edits_from_maintainers=允许维护者编辑 pulls.allow_edits_from_maintainers_desc=对基础分支有写入权限的用户也可以推送到此分支 @@ -1742,8 +1771,8 @@ pulls.nothing_to_compare_have_tag=所选分支/标签相同。 pulls.nothing_to_compare_and_allow_empty_pr=这些分支是相等的,此合并请求将为空。 pulls.has_pull_request=这些分支之间的合并请求已存在: %[2]s#%[3]d pulls.create=创建合并请求 -pulls.title_desc=请求将 %[1]d 次代码提交从 %[2]s 合并至 %[3]s -pulls.merged_title_desc=于 %[4]s 将 %[1]d 次代码提交从 %[2]s合并至 %[3]s +pulls.title_desc_few=请求将 %[1]d 次代码提交从 %[2]s 合并至 %[3]s +pulls.merged_title_desc_few=于 %[4]s 将 %[1]d 次代码提交从 %[2]s合并至 %[3]s pulls.change_target_branch_at=将目标分支从 %s 更改为 %s %s pulls.tab_conversation=对话内容 pulls.tab_commits=代码提交 @@ -1764,29 +1793,29 @@ pulls.remove_prefix=删除 %s 前缀 pulls.data_broken=此合并请求因为派生仓库信息缺失而中断。 pulls.files_conflicted=此合并请求有变更与目标分支冲突。 pulls.is_checking=正在进行合并冲突检测,请稍后再试。 -pulls.is_ancestor=此分支已经包含在目标分支中,没有什么可以合并。 +pulls.is_ancestor=此分支已经包含在目标分支中,没有更改可以合并。 pulls.is_empty=此分支上的更改已经在目标分支上。这将是一个空提交。 pulls.required_status_check_failed=一些必要的检查没有成功 pulls.required_status_check_missing=缺少一些必要的检查。 pulls.required_status_check_administrator=作为管理员,您仍可合并此合并请求 -pulls.blocked_by_approvals=此合并请求没有通过审批。已获取审批数%d个,共需要审批数%d个。 +pulls.blocked_by_approvals=此合并请求当前还没有通过审批。已获取审批数%d个,共需要审批数%d个。 pulls.blocked_by_rejection=此合并请求有官方审核员请求的更改。 -pulls.blocked_by_official_review_requests=此合并请求已被阻止,需要一名或多名审核员审阅批准。 +pulls.blocked_by_official_review_requests=此合并请求需要一名或多名审核员审阅批准。 pulls.blocked_by_outdated_branch=此合并请求因过期而被阻止。 -pulls.blocked_by_changed_protected_files_1=此合并请求被阻止是因为修改了被保护的文件: -pulls.blocked_by_changed_protected_files_n=此合并请求被阻止是因为修改了被保护的文件: +pulls.blocked_by_changed_protected_files_1=此合并请求因修改了下列被保护的文件而被阻止: +pulls.blocked_by_changed_protected_files_n=此合并请求因修改了下列被保护的文件而被阻止: pulls.can_auto_merge_desc=该合并请求可以进行自动合并操作。 pulls.cannot_auto_merge_desc=该合并请求存在冲突,无法进行自动合并操作。 pulls.cannot_auto_merge_helper=手动合并解决此冲突 pulls.num_conflicting_files_1=%d 个冲突文件 pulls.num_conflicting_files_n=%d 个冲突文件 pulls.approve_count_1=%d 项批准 -pulls.approve_count_n=%d 批准的 +pulls.approve_count_n=%d 项批准 pulls.reject_count_1=%d 变更请求 pulls.reject_count_n=%d 变更请求 pulls.waiting_count_1=%d 个正在等待审核 pulls.waiting_count_n=%d 个正在等待审核 -pulls.wrong_commit_id=提交 id 必须在目标分支 上 +pulls.wrong_commit_id=提交 ID 必须是目标分支上的提交的 ID pulls.no_merge_desc=由于未启用合并选项,此合并请求无法被合并。 pulls.no_merge_helper=在仓库设置中启用合并选项或者手工合并请求。 @@ -1810,9 +1839,9 @@ pulls.unrelated_histories=合并失败:两个分支没有共同历史。提示 pulls.merge_out_of_date=合并失败:在生成合并时,主分支已更新。提示:再试一次。 pulls.head_out_of_date=合并失败:在生成合并时,head 已更新。提示:再试一次。 pulls.has_merged=失败:合并请求已经被合并,您不能再次合并或更改目标分支。 -pulls.push_rejected=合并失败:推送被拒绝。审查此仓库的 Git 钩子。 +pulls.push_rejected=合并失败:推送被拒绝。请查看此仓库的 Git 钩子。 pulls.push_rejected_summary=详细拒绝信息 -pulls.push_rejected_no_message=合并失败:此推送被拒绝但未提供其他信息。
      请检查此仓库的 Git Hook。 +pulls.push_rejected_no_message=推送失败:此推送被拒绝但未提供其他信息。请检查此仓库的 Git Hook pulls.open_unmerged_pull_exists=`您不能执行重新打开操作, 因为已经存在相同的合并请求 (#%d)。` pulls.status_checking=一些检测仍在等待运行 pulls.status_checks_success=所有检测均成功 @@ -1871,7 +1900,7 @@ milestones.title=标题 milestones.desc=描述 milestones.due_date=截止日期(可选) milestones.clear=清除 -milestones.invalid_due_date_format=到期时间的格式必须是 'yyyy-mm-dd' 的形式。 +milestones.invalid_due_date_format=到期时间的格式必须是“yyyy-mm-dd”。 milestones.create_success=里程碑 %s 创建成功。 milestones.edit=编辑里程碑 milestones.edit_subheader=里程碑组织工单,合并请求和跟踪进度。 @@ -1929,7 +1958,7 @@ wiki.page_already_exists=相同名称的 Wiki 页面已经存在。 wiki.reserved_page=百科页面名称 %s 是被保留的。 wiki.pages=所有页面 wiki.last_updated=最后更新于 %s -wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称有:'Home', '_Sidebar' 和 '_Footer'。 +wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称包括“Home”、“_Sidebar”和“_Footer”。 wiki.original_git_entry_tooltip=查看原始的 Git 文件而不是使用友好链接。 activity=动态 @@ -1945,14 +1974,14 @@ activity.period.yearly=1年 activity.overview=概览 activity.active_prs_count_1=%d 合并请求 activity.active_prs_count_n=%d 合并请求 -activity.merged_prs_count_1=合并请求 -activity.merged_prs_count_n=合并请求 +activity.merged_prs_count_1=已合并的合并请求 +activity.merged_prs_count_n=已合并的合并请求 activity.opened_prs_count_1=新合并请求 activity.opened_prs_count_n=新合并请求 activity.title.user_1=%d 用户 activity.title.user_n=%d 用户 -activity.title.prs_1=%d 合并请求 -activity.title.prs_n=%d 合并请求 +activity.title.prs_1=%d 个合并请求 +activity.title.prs_n=%d 个合并请求 activity.title.prs_merged_by=%[2]s 由 %[1]s 合并 activity.title.prs_opened_by=%[2]s 创建了 %[1]s activity.merged_prs_label=已合并 @@ -2051,7 +2080,7 @@ settings.mirror_settings.push_mirror.remote_url=Git 远程仓库链接 settings.mirror_settings.push_mirror.add=添加推送镜像 settings.mirror_settings.push_mirror.edit_sync_time=编辑镜像同步间隔 -settings.sync_mirror=同步 +settings.sync_mirror=立即同步 settings.pull_mirror_sync_in_progress=正在从远程 %s 拉取更改。 settings.push_mirror_sync_in_progress=正在推送变更到远程 %s 。 settings.site=网站 @@ -2316,7 +2345,7 @@ settings.protected_branch.delete_rule=删除规则 settings.protected_branch_can_push=允许推吗? settings.protected_branch_can_push_yes=你可以推 settings.protected_branch_can_push_no=你不能推 -settings.branch_protection=分支 '%s' 的分支保护 +settings.branch_protection=分支 “%s” 的保护规则 settings.protect_this_branch=启用分支保护 settings.protect_this_branch_desc=阻止删除并限制Git推送和合并到分支。 settings.protect_disable_push=禁用推送 @@ -2359,10 +2388,10 @@ settings.require_signed_commits_desc=拒绝推送未签名或无法验证的提 settings.protect_branch_name_pattern=受保护的分支名称模式 settings.protect_branch_name_pattern_desc=分支保护的名称匹配规则。语法请参阅 文档 。如:main, release/** settings.protect_patterns=规则 -settings.protect_protected_file_patterns=受保护的文件模式(使用分号 ';' 分隔): -settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用分号 (';') 分隔多个模式。 见github.com/gobwas/glob文档了解模式语法。例如: .drone.yml, /docs/**/*.txt -settings.protect_unprotected_file_patterns=不受保护的文件模式(使用分号 ';' 分隔): -settings.protect_unprotected_file_patterns_desc=如果用户有写权限,则允许直接更改的不受保护的文件,以绕过推送限制。可以使用分号分隔多个模式 (';')。 见 github.com/gobwas/glob 文档了解模式语法。例如: .drone.yml, /docs/**/*.txt +settings.protect_protected_file_patterns=受保护的文件模式(使用半角分号“;”分隔): +settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用半角分号(“;”)分隔多个模式。 见github.com/gobwas/glob文档了解模式语法。例如: .drone.yml, /docs/**/*.txt。 +settings.protect_unprotected_file_patterns=不受保护的文件模式(使用半角分号“;”分隔): +settings.protect_unprotected_file_patterns_desc=在用户有写权限的情况下允许绕过限制,直接修改设为不保护的文件。如有多个匹配模式,则可用半角分号(“;”)分隔开。见 github.com/gobwas/glob 的文档以了解匹配模式的格式。例子: .drone.yml/docs/**/*.txt。 settings.add_protected_branch=启用保护 settings.delete_protected_branch=禁用保护 settings.update_protect_branch_success=分支保护规则 %s 更新成功。 @@ -2383,7 +2412,7 @@ settings.choose_branch=选择一个分支... settings.no_protected_branch=没有受保护的分支 settings.edit_protected_branch=编辑 settings.protected_branch_required_rule_name=必须填写规则名称 -settings.protected_branch_duplicate_rule_name=规则名称已存在 +settings.protected_branch_duplicate_rule_name=这些分支已设有规则 settings.protected_branch_required_approvals_min=所需的审批数不能为负数。 settings.tags=标签 settings.tags.protection=Git标签保护 @@ -2423,7 +2452,7 @@ settings.lfs_findcommits=查找提交 settings.lfs_lfs_file_no_commits=没有找到关于此 LFS 文件的提交 settings.lfs_noattribute=此路径在默认分支中没有可锁定的属性 settings.lfs_delete=删除 OID 为 %s 的 LFS 文件 -settings.lfs_delete_warning=删除一个 LFS 文件可能导致检出时显示'对象不存在'的错误。确定继续吗? +settings.lfs_delete_warning=删除一个 LFS 文件可能导致检出时出现“对象不存在”的错误。确定继续吗? settings.lfs_findpointerfiles=查找指针文件 settings.lfs_locks=锁定 settings.lfs_invalid_locking_path=无效路径:%s @@ -2599,7 +2628,7 @@ tag.create_success=标签"%s"已存在 topic.manage_topics=管理主题 topic.done=保存 topic.count_prompt=您最多选择25个主题 -topic.format_prompt=主题必须以字母或数字开头,可以包含连字符 ('-') 和句点 ('.'),长度不得超过35个字符。字符必须为小写。 +topic.format_prompt=主题必须以字母或数字开头,可以包含半角连字符(“-”)和句点(“.”),长度不得超过35个字符。字符必须为小写。 find_file.go_to_file=转到文件 find_file.no_matching=没有找到匹配的文件 @@ -2631,25 +2660,34 @@ pulls.nothing_to_compare_have_tag = 所选分支/标签相同。 wiki.cancel = 取消 settings.wiki_globally_editable = 允许任何人编辑百科 settings.mirror_settings.pushed_repository = 已推送的仓库 -settings.new_owner_blocked_doer = 新所有者屏蔽了你。 -settings.enter_repo_name = 输入仓库名称以确认: +settings.new_owner_blocked_doer = 新所有者已将你拉黑。 +settings.enter_repo_name = 输入所有者和仓库的名称: settings.wiki_rename_branch_main = 标准化 Wiki 分支名称 settings.wiki_rename_branch_main_notices_1 = 此操作无法撤消。 settings.wiki_branch_rename_success = wiki 仓库的分支名称已成功规范化。 settings.confirm_wiki_branch_rename = 重命名 wiki 分支 -pulls.commit_ref_at = `提交 %[2]s 引用了此合并请求` +pulls.commit_ref_at = `在提交 %[2]s 中引用了此合并请求` desc.sha256 = SHA256 settings.ignore_stale_approvals = 忽略过时的批准 -settings.ignore_stale_approvals_desc = 不对旧的提交(过时的审查)计入已批准的合并请求数量。 +settings.ignore_stale_approvals_desc = 不对旧的提交(过时的审查)计入已批准的合并请求数量。注:如过期的审核已被取消,则无需设置。 settings.archive.mirrors_unavailable = 如果仓库已存档,则仓库镜像不再可用。 settings.wiki_rename_branch_main_notices_2 = 这将预先重命名 %s 的存储库 wiki 的内部分支。 现存的检出方式需要更新。 settings.wiki_branch_rename_failure = 无法标准化存储库 wiki 的分支名称。 -settings.add_collaborator_blocked_our = 无法添加协作者,因为仓库所有者已屏蔽他们。 -settings.add_collaborator_blocked_them = 无法添加协作者,因为已屏蔽仓库所有者。 +settings.add_collaborator_blocked_our = 因仓库所有者已将其拉黑,不能添加该用户为协作者。 +settings.add_collaborator_blocked_them = 因该用户已将仓库所有者拉黑,不能添加该用户为协作者。 settings.units.units = 仓库单元 pulls.fast_forward_only_merge_pull_request = 仅快速向前 settings.units.overview = 概览 settings.units.add_more = 添加更多 +file_follow = 跟随符号链接 +pulls.reopen_failed.head_branch = 因头部分支不再存在,该合并请求不能再被重新打开。 +pulls.reopen_failed.base_branch = 因基础分支不再存在,该合并请求不能再被重新打开。 +pulls.made_using_agit = AGit +activity.navbar.pulse = 动态 +activity.navbar.code_frequency = 代码频率 +activity.navbar.recent_commits = 近期提交 +pulls.agit_explanation = 该合并请求是用 AGit 创建的。AGit 是一种可以让贡献者直接通过 “git push” 提出更改代码而不需要派生或建立新分支。 +error.broken_git_hook = 该仓库的 Git 钩子似乎已经损坏,请按照 此文档来修复这些问题,然后推送一些提交来刷新状态。 [graphs] component_loading=正在加载 %s... @@ -2657,6 +2695,8 @@ component_loading_failed=无法加载 %s component_loading_info=这可能需要一点… component_failed_to_load=意外的错误发生了。 contributors.what=贡献 +recent_commits.what = 近期提交 +code_frequency.what = 代码频率 [org] org_name_holder=组织名称 @@ -2825,12 +2865,12 @@ dashboard.cron.error=任务中的错误: %s: %[3]s dashboard.cron.finished=任务:%[1]s 已经完成 dashboard.delete_inactive_accounts=删除所有未激活的帐户 dashboard.delete_inactive_accounts.started=删除所有未激活的账户任务已启动。 -dashboard.delete_repo_archives=删除所有代码库的存档 (ZIP、 TAR、GZ, 等...) +dashboard.delete_repo_archives=删除所有仓库的存档(ZIP、 TAR.GZ 等…) dashboard.delete_repo_archives.started=删除所有仓库存档任务已启动。 dashboard.delete_missing_repos=删除所有丢失 Git 文件的仓库 dashboard.delete_missing_repos.started=删除所有丢失 Git 文件的仓库任务已启动。 dashboard.delete_generated_repository_avatars=删除生成的仓库头像 -dashboard.sync_repo_branches=将缺少的分支从 git 数据同步到数据库 +dashboard.sync_repo_branches=将缺少的分支从 Git 数据同步到数据库 dashboard.sync_repo_tags=从 git 数据同步标签到数据库 dashboard.update_mirrors=更新镜像仓库 dashboard.repo_health_check=健康检查所有仓库 @@ -2839,8 +2879,8 @@ dashboard.archive_cleanup=删除旧的仓库存档 dashboard.deleted_branches_cleanup=清理已删除的分支 dashboard.update_migration_poster_id=更新迁移的发表者ID dashboard.git_gc_repos=对仓库进行垃圾回收 -dashboard.resync_all_sshkeys=使用 Forgejo 的 SSH 密钥更新「.ssh/authorized_keys」文件。 -dashboard.resync_all_sshprincipals=使用 Forgejo 的 SSH 规则更新「.ssh/authorized_principals」文件。 +dashboard.resync_all_sshkeys=使用 Forgejo 的 SSH 密钥更新“.ssh/authorized_keys”文件。 +dashboard.resync_all_sshprincipals=使用 Forgejo 的 SSH 规则更新“.ssh/authorized_principals”文件。 dashboard.resync_all_hooks=重新同步所有仓库的 pre-receive、update 和 post-receive 钩子 dashboard.reinit_missing_repos=重新初始化所有丢失的 Git 仓库存在的记录 dashboard.sync_external_users=同步外部用户数据 @@ -2918,7 +2958,7 @@ users.max_repo_creation_desc=(设置为 -1 表示使用全局默认值) users.is_activated=该用户已被激活 users.prohibit_login=禁用登录 users.is_admin=是管理员 -users.is_restricted=受限 +users.is_restricted=受限制的 users.allow_git_hook=允许创建 Git 钩子 users.allow_git_hook_tooltip=Git 钩子将会被以操作系统用户运行,将会拥有同样的主机访问权限。因此,拥有此特殊的Git 钩子权限将能够访问合修改所有的 Forgejo 仓库或者Forgejo的数据库。同时也能获得Forgejo的管理员权限。 users.allow_import_local=允许导入本地仓库 @@ -2947,7 +2987,7 @@ users.list_status_filter.is_2fa_enabled=已启用 2FA users.list_status_filter.not_2fa_enabled=未启用 2FA users.details=用户详细信息 -emails.email_manage_panel=邮件管理 +emails.email_manage_panel=管理用户邮件地址 emails.primary=主要的 emails.activated=已激活 emails.filter_sort.email=电子邮件 @@ -3047,7 +3087,7 @@ auths.smtp_auth=SMTP 认证类型 auths.smtphost=SMTP 主机地址 auths.smtpport=SMTP 主机端口 auths.allowed_domains=域名白名单 -auths.allowed_domains_helper=置空将允许所有域名,每个域名用逗号分隔。 +auths.allowed_domains_helper=每个域名用逗号分隔,如要允许任意域名则留空。 auths.skip_tls_verify=忽略 TLS 验证 auths.force_smtps=强制 SMTPS auths.force_smtps_helper=SMTPS 始终用于 465 端口。设置此项会强制其他端口也使用 SMTPS。(否则,如果主机支持,将在其他端口上使用 STARTTLS。) @@ -3094,7 +3134,7 @@ auths.tips=帮助提示 auths.tips.oauth2.general=OAuth2 认证 auths.tips.oauth2.general.tip=当注册新的 OAuth2 身份验证时,回调/重定向 URL 应该是: auths.tip.oauth2_provider=OAuth2 提供程序 -auths.tip.bitbucket=`在 https://bitbucket.org/account/user//oauth-consumers/new 注册新的 OAuth 消费者同时添加权限"帐户"-"读"` +auths.tip.bitbucket=`在 https://bitbucket.org/account/user//oauth-consumers/new 注册新的 OAuth consumer 并添加权限“Account” 和 “Read”` auths.tip.nextcloud=使用下面的菜单“设置(Settings) -> 安全(Security) -> OAuth 2.0 client”在您的实例上注册一个新的 OAuth 客户端。 auths.tip.dropbox=在 https://www.dropbox.com/developers/apps 上创建一个新的应用程序 auths.tip.facebook=`在 https://developers.facebook.com/apps 注册一个新的应用,并添加产品"Facebook 登录"` @@ -3149,7 +3189,7 @@ config.ssh_port=端口 config.ssh_listen_port=监听端口 config.ssh_root_path=根目录 config.ssh_key_test_path=密钥测试路径 -config.ssh_keygen_path=密钥生成器('ssh-keygen')路径 +config.ssh_keygen_path=密钥生成器(“ssh-keygen”)路径 config.ssh_minimum_key_size_check=密钥最小长度检查 config.ssh_minimum_key_sizes=密钥最小长度限制 @@ -3328,6 +3368,7 @@ self_check.database_collation_case_insensitive=数据库正在使用一个校验 self_check.database_inconsistent_collation_columns=数据库正在使用%s的排序规则,但是这些列使用了不匹配的排序规则。这可能会造成一些意外问题。 self_check.database_fix_mysql=对于MySQL/MariaDB用户,您可以使用“gitea doctor convert”命令来解决校验问题。 或者您也可以通过 "ALTER ... COLLATE ..." 这样的SQL 来手动解决这个问题。 self_check.database_fix_mssql=对于MSSQL用户,您现在只能通过"ALTER ... COLLATE ..."SQLs手动解决这个问题。 +auths.tips.gmail_settings = Gmail 设置: [action] create_repo=创建了仓库 %s @@ -3408,10 +3449,10 @@ error.extract_sign=无法提取签名 error.generate_hash=无法生成提交的哈希 error.no_committer_account=没有帐户链接到提交者的电子邮件 error.no_gpg_keys_found=找不到此签名对应的密钥 -error.not_signed_commit=未签名的提交 +error.not_signed_commit=提交未签名 error.failed_retrieval_gpg_keys=找不到任何与该提交者账号相关的密钥 -error.probable_bad_signature=警告!虽然数据库中有一个此ID的密钥,但它没有验证此提交!此提交是有疑问的。 -error.probable_bad_default_signature=警告!虽然默认密钥拥有此ID,但它没有验证此提交!此提交是有疑问的。 +error.probable_bad_signature=警告!虽然数据库中有一个此ID对应的密钥,但这个密钥不能验证此提交!该提交可疑。 +error.probable_bad_default_signature=警告!虽然默认密钥拥有此ID,但不能验证此提交!该提交可疑。 [units] unit=单元 @@ -3572,6 +3613,7 @@ owner.settings.chef.keypair.description=需要密钥对才能向 Chef 注册中 rpm.repository = 仓库信息 rpm.repository.architectures = 架构 rpm.repository.multiple_groups = 该软件包可在多个组中使用。 +owner.settings.cargo.rebuild.no_index = 无法重建,未初始化任何索引。 [secrets] secrets=密钥 @@ -3679,6 +3721,7 @@ variables.creation.failed=添加变量失败。 variables.creation.success=变量 “%s” 添加成功。 variables.update.failed=编辑变量失败。 variables.update.success=该变量已被编辑。 +runs.workflow = 工作流 [projects] type-1.display_name=个人项目 diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini index 289a3e7b5d..5c1e234392 100644 --- a/options/locale/locale_zh-HK.ini +++ b/options/locale/locale_zh-HK.ini @@ -484,7 +484,7 @@ pulls.compare_changes=建立合併請求 pulls.filter_branch=過濾分支 pulls.no_results=未找到結果 pulls.create=建立合併請求 -pulls.merged_title_desc=於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s +pulls.merged_title_desc_few=於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s pulls.tab_conversation=對話內容 pulls.tab_commits=程式碼提交 pulls.reopen_to_merge=請重新開啟合併請求來完成合併操作。 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index e49be30691..5bfff77fd2 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -28,7 +28,7 @@ return_to_gitea=返回 Forgejo username=帳號 email=電子信箱 password=密碼 -access_token=Access Token +access_token=訪問令牌(Access Token) re_type=確認密碼 captcha=驗證碼 twofa=兩步驟驗證 @@ -54,7 +54,7 @@ organization=組織 mirror=鏡像 new_repo=新增儲存庫 new_migrate=遷移外部儲存庫 -new_mirror=新鏡像 +new_mirror=建立新的鏡像 new_fork=新增儲存庫 Fork new_org=新增組織 new_project=新增專案 @@ -125,6 +125,36 @@ concept_user_organization=組織 name=名稱 value=值 +go_back = 返回 +sign_in_with_provider = 使用 %s 登入 +tracked_time_summary = 基於 issue 清單篩選器的追蹤時間摘要 +locked = 已鎖定 +rerun = 重新執行 +rerun_all = 重新執行所有作業 +copy_hash = 複製哈希值 +toggle_menu = 切換選單 +concept_system_global = 全局 +view = 查看 +filter = 篩選 +filter.clear = 清除篩選條件 +filter.is_archived = 已歸檔 +filter.not_archived = 未歸檔 +filter.is_fork = 已派生 +filter.not_fork = 未派生 +filter.is_mirror = 已鏡像 +filter.not_mirror = 未鏡像 +filter.is_template = 模板 +filter.not_template = 非模版 +filter.public = 公開 +filter.private = 私有 +artifacts = 製品 +concept_user_individual = 個人 +show_timestamps = 顯示時間戳 +show_log_seconds = 顯示秒數 +show_full_screen = 全屏顯示 +download_logs = 下載日誌 +confirm_delete_selected = 確認刪除所有選中專案? +confirm_delete_artifact = 您確定要刪除製品“%s”嗎? [aria] navbar=導航列 @@ -134,7 +164,7 @@ footer.links=連結 [heatmap] number_of_contributions_in_the_last_12_months=過去十二個月內有 %s 個貢獻 -no_contributions=沒有貢獻 +contributions_zero=沒有貢獻 less=少 more=多 @@ -152,6 +182,7 @@ buttons.mention.tooltip=提及使用者或團隊 buttons.ref.tooltip=參考問題或合併請求 buttons.enable_monospace_font=啟用等寬字型 buttons.disable_monospace_font=停用等寬字型 +buttons.switch_to_legacy.tooltip = 使用舊版編輯器 [filter] string.asc=A - Z @@ -163,6 +194,8 @@ missing_csrf=錯誤的請求:未提供 CSRF token invalid_csrf=錯誤的請求:無效的 CSRF token not_found=找不到目標。 network_error=網路錯誤 +report_message = 如果您確定這是一個 Forgejo bug,請在 Codeberg 上搜索問題,或在必要時建立一個新工單。 +server_internal = 伺服器內部錯誤 [startpage] app_desc=一套極易架設的 Git 服務 @@ -282,6 +315,7 @@ invalid_password_algorithm=無效的密碼雜湊演算法 password_algorithm_helper=設定密碼雜湊演算法。演算法有不同的需求與強度。argon2 演算法雖然較安全但會使用大量記憶體,可能不適用於小型系統。 enable_update_checker=啟用更新檢查器 enable_update_checker_helper=定期連線到 gitea.io 檢查更新。 +run_user_helper = 輸入 Forgejo 執行的作業系統使用者名稱。請注意,此使用者必須具有對儲存庫根路徑的訪問許可權。 [home] uname_holder=帳號或電子信箱 @@ -367,7 +401,7 @@ twofa_scratch_used=您已經用掉了備用驗證碼。您已被重新導向到 twofa_passcode_incorrect=您的驗證碼不正確。如果您遺失設備,請使用您的備用驗證碼登入。 twofa_scratch_token_incorrect=您的備用驗證碼不正確 login_userpass=登入 -login_openid=OpenID +tab_openid=OpenID oauth_signup_tab=註冊新帳戶 oauth_signup_title=完成新帳戶 oauth_signup_submit=完成帳戶 @@ -1558,8 +1592,8 @@ pulls.nothing_to_compare=這些分支的內容相同,無需建立合併請求 pulls.nothing_to_compare_and_allow_empty_pr=這些分支的內容相同,此合併請求將會是空白的。 pulls.has_pull_request=`已有介於這些分支間的合併請求:%[2]s#%[3]d` pulls.create=建立合併請求 -pulls.title_desc=請求將 %[1]d 次程式碼提交從 %[2]s 合併至 %[3]s -pulls.merged_title_desc=將 %[1]d 次提交從 %[2]s 合併至 %[3]s %[4]s +pulls.title_desc_few=請求將 %[1]d 次程式碼提交從 %[2]s 合併至 %[3]s +pulls.merged_title_desc_few=將 %[1]d 次提交從 %[2]s 合併至 %[3]s %[4]s pulls.change_target_branch_at=`將目標分支從 %s 更改為 %s %s` pulls.tab_conversation=對話內容 pulls.tab_commits=程式碼提交 diff --git a/package-lock.json b/package-lock.json index 6d1d2850c1..37f620f6ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "packages": { "": { "dependencies": { - "@citation-js/core": "0.7.6", - "@citation-js/plugin-bibtex": "0.7.8", - "@citation-js/plugin-csl": "0.7.6", + "@citation-js/core": "0.7.9", + "@citation-js/plugin-bibtex": "0.7.9", + "@citation-js/plugin-csl": "0.7.9", "@citation-js/plugin-software-formats": "0.6.1", "@claviska/jquery-minicolors": "2.3.6", "@github/markdown-toolbar-element": "2.2.3", @@ -15,7 +15,6 @@ "@github/text-expander-element": "2.6.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.8.0", - "@webcomponents/custom-elements": "1.6.0", "add-asset-webpack-plugin": "2.0.1", "ansi_up": "6.0.2", "asciinema-player": "3.7.0", @@ -24,30 +23,31 @@ "chartjs-plugin-zoom": "2.0.1", "clippie": "4.0.7", "css-loader": "6.10.0", - "css-variables-parser": "1.0.1", "dayjs": "1.11.10", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", - "esbuild-loader": "4.0.3", + "esbuild-loader": "4.1.0", "escape-goat": "4.0.0", "fast-glob": "3.3.2", - "htmx.org": "1.9.10", + "htmx.org": "1.9.11", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "10.8.0", + "mermaid": "10.9.0", "mini-css-extract-plugin": "2.8.1", "minimatch": "9.0.3", - "monaco-editor": "0.46.0", + "monaco-editor": "0.47.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", "postcss": "8.4.35", "postcss-loader": "8.1.1", + "postcss-nesting": "12.1.0", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.11.8", + "swagger-ui-dist": "5.12.0", "tailwindcss": "3.4.1", + "temporal-polyfill": "0.2.3", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", @@ -67,7 +67,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", "@playwright/test": "1.42.1", "@stoplight/spectral-cli": "6.11.0", - "@stylistic/eslint-plugin-js": "1.6.3", + "@stylistic/eslint-plugin-js": "1.7.0", "@stylistic/stylelint-plugin": "2.1.0", "@vitejs/plugin-vue": "5.0.4", "eslint": "8.57.0", @@ -77,12 +77,12 @@ "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-regexp": "2.2.0", + "eslint-plugin-regexp": "2.3.0", "eslint-plugin-sonarjs": "0.24.0", "eslint-plugin-unicorn": "51.0.1", - "eslint-plugin-vitest": "0.3.22", + "eslint-plugin-vitest": "0.3.26", "eslint-plugin-vitest-globals": "1.4.0", - "eslint-plugin-vue": "9.22.0", + "eslint-plugin-vue": "9.23.0", "eslint-plugin-vue-scoped-css": "2.7.2", "eslint-plugin-wc": "2.0.4", "jsdom": "24.0.0", @@ -92,7 +92,7 @@ "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.4", "svgo": "3.2.0", - "updates": "15.1.2", + "updates": "15.3.1", "vite-string-plugin": "1.1.5", "vitest": "1.3.1" }, @@ -323,9 +323,9 @@ "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" }, "node_modules/@citation-js/core": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.6.tgz", - "integrity": "sha512-qbB6RjwSsx/AjlCSAqoWKN05VxpjADYe8GmnPJnRB7QeNiVmqaRc8NSQDdvQ+4qhCkQOtMH15Sa2Nde4cvlXhw==", + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.9.tgz", + "integrity": "sha512-fSbkB32JayDChZnAYC/kB+sWHRvxxL7ibVetyBOyzOc+5aCnjb6UVsbcfhnkOIEyAMoRRvWDyFmakEoTtA5ttQ==", "dependencies": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -353,9 +353,9 @@ } }, "node_modules/@citation-js/plugin-bibtex": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.8.tgz", - "integrity": "sha512-20fUXe1zm1oCONFflGj3mgIk6DHspPjWrBirGfsyHmVSR/4xqnSbrqtztLiV15zt3tbKLepTaHm3ZTrcLOK0MA==", + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.9.tgz", + "integrity": "sha512-gIJpCd6vmmTOcRfDrSOjtoNhw2Mi94UwFxmgJ7GwkXyTYcNheW5VlMMo1tlqjakJGARQ0eOsKcI57gSPqJSS2g==", "dependencies": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -381,9 +381,9 @@ } }, "node_modules/@citation-js/plugin-csl": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.6.tgz", - "integrity": "sha512-H/dhzU56+D71Hzjto1x9PDtvsWaiI+Dx6Jj1vjiFtCCnbU/Zvqo5xFZNPstee+hFE6AsJ2xYlI8QujrGH+V1pQ==", + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.9.tgz", + "integrity": "sha512-mbD7CnUiPOuVnjeJwo+d0RGUcY0PE8n01gHyjq0qpTeS42EGmQ9+LzqfsTUVWWBndTwc6zLRuIF1qFAUHKE4oA==", "dependencies": { "@citation-js/date": "^0.5.0", "citeproc": "^2.4.6" @@ -466,9 +466,9 @@ } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.0.tgz", - "integrity": "sha512-YfEHq0eRH98ffb5/EsrrDspVWAuph6gDggAE74ZtjecsmyyWpW768hOyiONa8zwWGbIWYfa2Xp4tRTrpQQ00CQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.1.tgz", + "integrity": "sha512-ubEkAaTfVZa+WwGhs5jbo5Xfqpeaybr/RvWzvFxRs4jfq16wH8l8Ty/QEEpINxll4xhuGfdMbipRyz5QZh9+FA==", "dev": true, "funding": [ { @@ -484,13 +484,13 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^2.2.3" + "@csstools/css-tokenizer": "^2.2.4" } }, "node_modules/@csstools/css-tokenizer": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.3.tgz", - "integrity": "sha512-pp//EvZ9dUmGuGtG1p+n17gTHEOqu9jO+FiCUjNN3BDmyhdA2Jq9QsVeR7K8/2QCK17HSsioPlTW9ZkzoWb3Lg==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.4.tgz", + "integrity": "sha512-PuWRAewQLbDhGeTvFuq2oClaSCKPIBmHyIobCV39JHRYN0byDcUWJl5baPeNUcqrjtdMNqFooE0FGl31I3JOqw==", "dev": true, "funding": [ { @@ -507,9 +507,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.8.tgz", - "integrity": "sha512-DiD3vG5ciNzeuTEoh74S+JMjQDs50R3zlxHnBnfd04YYfA/kh2KiBCGhzqLxlJcNq+7yNQ3stuZZYLX6wK/U2g==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.9.tgz", + "integrity": "sha512-qqGuFfbn4rUmyOB0u8CVISIp5FfJ5GAR3mBrZ9/TKndHakdnm6pY0L/fbLcpPnrzwCyyTEZl1nUcXAYHEWneTA==", "dev": true, "funding": [ { @@ -525,15 +525,35 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.6.0", - "@csstools/css-tokenizer": "^2.2.3" + "@csstools/css-parser-algorithms": "^2.6.1", + "@csstools/css-tokenizer": "^2.2.4" + } + }, + "node_modules/@csstools/selector-resolve-nested": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-1.1.0.tgz", + "integrity": "sha512-uWvSaeRcHyeNenKg8tp17EVDRkpflmdyvbE0DHo6D/GdBb6PDnCYYU6gRpXhtICMGMcahQmj2zGxwFM/WC8hCg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.13" } }, "node_modules/@csstools/selector-specificity": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.0.2.tgz", "integrity": "sha512-RpHaZ1h9LE7aALeQXmXrJkRG84ZxIsctEN2biEUmFyKpzFM3zZ35eUMcIzZFsw/2olQE6v69+esEqU2f1MKycg==", - "dev": true, "funding": [ { "type": "github", @@ -560,9 +580,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", "cpu": [ "ppc64" ], @@ -575,9 +595,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", "cpu": [ "arm" ], @@ -590,9 +610,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", "cpu": [ "arm64" ], @@ -605,9 +625,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", "cpu": [ "x64" ], @@ -620,9 +640,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", "cpu": [ "arm64" ], @@ -635,9 +655,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", "cpu": [ "x64" ], @@ -650,9 +670,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", "cpu": [ "arm64" ], @@ -665,9 +685,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", "cpu": [ "x64" ], @@ -680,9 +700,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", "cpu": [ "arm" ], @@ -695,9 +715,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", "cpu": [ "arm64" ], @@ -710,9 +730,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", "cpu": [ "ia32" ], @@ -725,9 +745,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", "cpu": [ "loong64" ], @@ -740,9 +760,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", "cpu": [ "mips64el" ], @@ -755,9 +775,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", "cpu": [ "ppc64" ], @@ -770,9 +790,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", "cpu": [ "riscv64" ], @@ -785,9 +805,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", "cpu": [ "s390x" ], @@ -800,9 +820,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", "cpu": [ "x64" ], @@ -815,9 +835,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", "cpu": [ "x64" ], @@ -830,9 +850,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", "cpu": [ "x64" ], @@ -845,9 +865,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", "cpu": [ "x64" ], @@ -860,9 +880,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", "cpu": [ "arm64" ], @@ -875,9 +895,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", "cpu": [ "ia32" ], @@ -890,9 +910,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "cpu": [ "x64" ], @@ -1230,12 +1250,12 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -1460,9 +1480,9 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", "cpu": [ "arm" ], @@ -1473,9 +1493,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", "cpu": [ "arm64" ], @@ -1486,9 +1506,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", "cpu": [ "arm64" ], @@ -1499,9 +1519,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", "cpu": [ "x64" ], @@ -1512,9 +1532,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", "cpu": [ "arm" ], @@ -1525,9 +1545,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", "cpu": [ "arm64" ], @@ -1538,9 +1558,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", "cpu": [ "arm64" ], @@ -1551,9 +1571,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", "cpu": [ "riscv64" ], @@ -1564,9 +1584,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", - "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", "cpu": [ "x64" ], @@ -1577,9 +1597,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", "cpu": [ "x64" ], @@ -1590,9 +1610,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", "cpu": [ "arm64" ], @@ -1603,9 +1623,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", "cpu": [ "ia32" ], @@ -1616,9 +1636,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", "cpu": [ "x64" ], @@ -2086,9 +2106,9 @@ "dev": true }, "node_modules/@stylistic/eslint-plugin-js": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.6.3.tgz", - "integrity": "sha512-ckdz51oHxD2FaxgY2piJWJVJiwgp8Uu96s+as2yB3RMwavn3nHBrpliVukXY9S/DmMicPRB2+H8nBk23GDG+qA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.0.tgz", + "integrity": "sha512-PN6On/+or63FGnhhMKSQfYcWutRlzOiYlVdLM6yN7lquoBTqUJHYnl4TA4MHwiAt46X5gRxDr1+xPZ1lOLcL+Q==", "dev": true, "dependencies": { "@types/eslint": "^8.56.2", @@ -2236,9 +2256,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "20.11.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.27.tgz", + "integrity": "sha512-qyUZfMnCg1KEz57r7pzFtSGt49f6RPkPBis3Vo4PbS7roQEDn22hiHzl/Lo1q4i4hDEgBJmBF/NTNg2XR0HbFg==", "dependencies": { "undici-types": "~5.26.4" } @@ -2281,16 +2301,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.0.tgz", - "integrity": "sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz", + "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.1.0", - "@typescript-eslint/type-utils": "7.1.0", - "@typescript-eslint/utils": "7.1.0", - "@typescript-eslint/visitor-keys": "7.1.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/type-utils": "7.2.0", + "@typescript-eslint/utils": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -2316,15 +2336,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz", - "integrity": "sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.1.0", - "@typescript-eslint/types": "7.1.0", - "@typescript-eslint/typescript-estree": "7.1.0", - "@typescript-eslint/visitor-keys": "7.1.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4" }, "engines": { @@ -2344,13 +2364,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.0.tgz", - "integrity": "sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.0", - "@typescript-eslint/visitor-keys": "7.1.0" + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2361,13 +2381,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.0.tgz", - "integrity": "sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz", + "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.1.0", - "@typescript-eslint/utils": "7.1.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/utils": "7.2.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -2388,9 +2408,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz", - "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2401,13 +2421,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz", - "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.0", - "@typescript-eslint/visitor-keys": "7.1.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2429,17 +2449,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.0.tgz", - "integrity": "sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", + "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.1.0", - "@typescript-eslint/types": "7.1.0", - "@typescript-eslint/typescript-estree": "7.1.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", "semver": "^7.5.4" }, "engines": { @@ -2454,12 +2474,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz", - "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/types": "7.2.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2559,9 +2579,9 @@ } }, "node_modules/@vitest/snapshot/node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -2650,9 +2670,9 @@ } }, "node_modules/@vue/compiler-sfc/node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -2714,9 +2734,9 @@ "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==" }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -2733,9 +2753,9 @@ "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", @@ -2753,14 +2773,14 @@ "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { @@ -2785,26 +2805,26 @@ "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", @@ -2812,22 +2832,22 @@ } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", @@ -2836,19 +2856,14 @@ } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, - "node_modules/@webcomponents/custom-elements": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz", - "integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==" - }, "node_modules/@webpack-cli/configtest": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", @@ -3410,11 +3425,14 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/boolbase": { @@ -3564,9 +3582,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001591", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", - "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", + "version": "1.0.30001597", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz", + "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==", "funding": [ { "type": "opencollective", @@ -4032,35 +4050,6 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/css-variables-parser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-variables-parser/-/css-variables-parser-1.0.1.tgz", - "integrity": "sha512-GWaqrwGtAWVr/yjjE17iyvbcy+W3voe0vko1/xLCwFeYd3kTLstzUdVH+g5TTXejrtlsb1FS4L9rP6PmeTa8wQ==", - "dependencies": { - "postcss": "^7.0.36" - } - }, - "node_modules/css-variables-parser/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "node_modules/css-variables-parser/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -4158,9 +4147,9 @@ } }, "node_modules/d3": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", - "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", "dependencies": { "d3-array": "3", "d3-axis": "3", @@ -4365,9 +4354,9 @@ } }, "node_modules/d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", "dependencies": { "d3-array": "2.5.0 - 3" }, @@ -4477,9 +4466,9 @@ } }, "node_modules/d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" @@ -4885,9 +4874,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.690", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.690.tgz", - "integrity": "sha512-+2OAGjUx68xElQhydpcbqH50hE8Vs2K6TkAeLhICYfndb67CVH0UsZaijmRUE3rHlIxU1u0jxwhgVe6fK3YANA==" + "version": "1.4.706", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.706.tgz", + "integrity": "sha512-fO01fufoGd6jKK3HR8ofBapF3ZPfgxNJ/ua9xQAhFu93TwWIs4d+weDn3kje3GB4S7aGUTfk5nvdU5F7z5mF9Q==" }, "node_modules/elkjs": { "version": "0.9.2", @@ -4908,9 +4897,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.1.tgz", - "integrity": "sha512-3d3JRbwsCLJsYgvb6NuWEG44jjPSOMuS73L/6+7BZuoKm3W+qXnSoIYVHi8dG7Qcg4inAY4jbzkZ7MnskePeDg==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -5133,9 +5122,9 @@ } }, "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -5144,37 +5133,37 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" } }, "node_modules/esbuild-loader": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.0.3.tgz", - "integrity": "sha512-YpaSRisj7TSg6maKKKG9OJGGm0BZ7EXeov8J8cXEYdugjlAJ0wL7aj2JactoQvPJ113v2Ar204pdJWrZsAQc8Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.1.0.tgz", + "integrity": "sha512-543TtIvqbqouEMlOHg4xKoDQkmdImlwIpyAIgpUtDPvMuklU/c2k+Qt2O3VeDBgAwozxmlEbjOzV+F8CZ0g+Bw==", "dependencies": { - "esbuild": "^0.19.0", + "esbuild": "^0.20.0", "get-tsconfig": "^4.7.0", "loader-utils": "^2.0.4", "webpack-sources": "^1.4.3" @@ -5707,9 +5696,9 @@ } }, "node_modules/eslint-plugin-regexp": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.2.0.tgz", - "integrity": "sha512-0kwpiWiLRVBkVr3oIRQLl196sXP/NF6DQFefv9jtR4ZOgQR+6WID2pIZ0I+wIt54qgBPwBB7Gm2a+ueh8/WsFQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.3.0.tgz", + "integrity": "sha512-T8JUs7ssRGbuXb+CGfdUJbcxTBMCNOpNqNBLuC8JUKAEIez72J37RaOi5/4dAUsGz92GbWVtqTLPSJZGyP/sQA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -5773,12 +5762,12 @@ } }, "node_modules/eslint-plugin-vitest": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.22.tgz", - "integrity": "sha512-atkFGQ7aVgcuSeSMDqnyevIyUpfBPMnosksgEPrKE7Y8xQlqG/5z2IQ6UDau05zXaaFv7Iz8uzqvIuKshjZ0Zw==", + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.26.tgz", + "integrity": "sha512-oxe5JSPgRjco8caVLTh7Ti8PxpwJdhSV0hTQAmkFcNcmy/9DnqLB/oNVRA11RmVRP//2+jIIT6JuBEcpW3obYg==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "^6.21.0" + "@typescript-eslint/utils": "^7.1.1" }, "engines": { "node": "^18.0.0 || >= 20.0.0" @@ -5802,110 +5791,10 @@ "integrity": "sha512-WE+YlK9X9s4vf5EaYRU0Scw7WItDZStm+PapFSYlg2ABNtaQ4zIG7wEqpoUB3SlfM+SgkhgmzR0TeJOO5k3/Nw==", "dev": true }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/eslint-plugin-vue": { - "version": "9.22.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.22.0.tgz", - "integrity": "sha512-7wCXv5zuVnBtZE/74z4yZ0CM8AjH6bk4MQGm7hZjUC2DBppKU5ioeOk5LGSg/s9a1ZJnIsdPLJpXnu1Rc+cVHg==", + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.23.0.tgz", + "integrity": "sha512-Bqd/b7hGYGrlV+wP/g77tjyFmp81lh5TMw0be9093X02SyelxRRfCI6/IsGq/J7Um0YwB9s0Ry0wlFyjPdmtUw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -6542,9 +6431,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", + "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -6817,9 +6706,9 @@ "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==" }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -6891,9 +6780,9 @@ } }, "node_modules/htmx.org": { - "version": "1.9.10", - "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.10.tgz", - "integrity": "sha512-UgchasltTCrTuU2DQLom3ohHrBvwr7OqpwyAVJ9VxtNBng4XKkVsqrv0Qr3srqvM9ZNI3f1MmvVQQqK7KW/bTA==" + "version": "1.9.11", + "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.11.tgz", + "integrity": "sha512-WlVuICn8dfNOOgYmdYzYG8zSnP3++AdHkMHooQAzGZObWpVXYathpz/I37ycF4zikR6YduzfCvEcxk20JkIUsw==" }, "node_modules/http-proxy-agent": { "version": "7.0.2", @@ -7061,9 +6950,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -7301,10 +7190,13 @@ } }, "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7414,10 +7306,13 @@ } }, "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7504,10 +7399,13 @@ } }, "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7525,13 +7423,16 @@ } }, "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8373,9 +8274,9 @@ } }, "node_modules/mermaid": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.8.0.tgz", - "integrity": "sha512-9CzfSreRjdDJxX796+jW4zjEq0DVw5xVF0nWsqff8OTbrt+ml0TZ5PyYUjjUZJa2NYxYJZZXewEquxGiM8qZEA==", + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.0.tgz", + "integrity": "sha512-swZju0hFox/B/qoLKK0rOxxgh8Cf7rJSfAUc1u8fezVihYMvrJAS45GzAxTVf4Q+xn9uMgitBcmWk7nWGXOs/g==", "dependencies": { "@braintree/sanitize-url": "^6.0.1", "@types/d3-scale": "^4.0.3", @@ -8388,6 +8289,7 @@ "dayjs": "^1.11.7", "dompurify": "^3.0.5", "elkjs": "^0.9.0", + "katex": "^0.16.9", "khroma": "^2.0.0", "lodash-es": "^4.17.21", "mdast-util-from-markdown": "^1.3.0", @@ -8934,9 +8836,9 @@ } }, "node_modules/monaco-editor": { - "version": "0.46.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.46.0.tgz", - "integrity": "sha512-ADwtLIIww+9FKybWscd7OCfm9odsFYHImBRI1v9AviGce55QY8raT+9ihH8jX/E/e6QVSGM+pKj4jSUSRmALNQ==" + "version": "0.47.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.47.0.tgz", + "integrity": "sha512-VabVvHvQ9QmMwXu4du008ZDuyLnHs9j7ThVFsiJoXSOQk18+LF89N4ADzPbFenm0W4V2bGHnFBztIRQTgBfxzw==" }, "node_modules/monaco-editor-webpack-plugin": { "version": "7.1.0", @@ -9846,6 +9748,32 @@ "postcss": "^8.2.14" } }, + "node_modules/postcss-nesting": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.0.tgz", + "integrity": "sha512-QOYnosaZ+mlP6plQrAxFw09UUp2Sgtxj1BVHN+rSVbtV0Yx48zRt9/9F/ZOoxOKBBEsaJk2MYhhVRjeRRw5yuw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/selector-resolve-nested": "^1.1.0", + "@csstools/selector-specificity": "^3.0.2", + "postcss-selector-parser": "^6.0.13" + }, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, "node_modules/postcss-resolve-nested-selector": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", @@ -9895,9 +9823,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -10501,13 +10429,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -10638,17 +10566,17 @@ } }, "node_modules/seroval": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.4.tgz", - "integrity": "sha512-qQs/N+KfJu83rmszFQaTxcoJoPn6KNUruX4KmnmyD0oZkUoiNvJ1rpdYKDf4YHM05k+HOgCxa3yvf15QbVijGg==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.5.tgz", + "integrity": "sha512-TM+Z11tHHvQVQKeNlOUonOWnsNM+2IBwZ4vwoi4j3zKzIpc5IDw8WPwCfcc8F17wy6cBcJGbZbFOR0UCuTZHQA==", "engines": { "node": ">=10" } }, "node_modules/seroval-plugins": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.4.tgz", - "integrity": "sha512-DQ2IK6oQVvy8k+c2V5x5YCtUa/GGGsUwUBNN9UqohrZ0rWdUapBFpNMYP1bCyRHoxOJjdKGl+dieacFIpU/i1A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.5.tgz", + "integrity": "sha512-8+pDC1vOedPXjKG7oz8o+iiHrtF2WswaMQJ7CKFpccvSYfrzmvKY9zOJWCg+881722wIHfwkdnRmiiDm9ym+zQ==", "engines": { "node": ">=10" }, @@ -10657,17 +10585,17 @@ } }, "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "dependencies": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11454,9 +11382,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.11.8", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.8.tgz", - "integrity": "sha512-IfPtCPdf6opT5HXrzHO4kjL1eco0/8xJCtcs7ilhKuzatrpF2j9s+3QbOag6G3mVFKf+g+Ca5UG9DquVUs2obA==" + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.12.0.tgz", + "integrity": "sha512-Rt1xUpbHulJVGbiQjq9yy9/r/0Pg6TmpcG+fXTaMePDc8z5WUw4LfaWts5qcNv/8ewPvBIbY7DKq7qReIKNCCQ==" }, "node_modules/symbol-tree": { "version": "3.2.4", @@ -11597,10 +11525,23 @@ "node": ">=6" } }, + "node_modules/temporal-polyfill": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.2.3.tgz", + "integrity": "sha512-7ZJRc7wq/1XjrOQYkkNpgo2qfE9XLrUU8D/DS+LAC/T0bYqZ46rW6dow0sOTXTPZS4bwer8bD/0OyuKQBfA3yw==", + "dependencies": { + "temporal-spec": "^0.2.0" + } + }, + "node_modules/temporal-spec": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.2.0.tgz", + "integrity": "sha512-r1AT0XdEp8TMQ13FLvOt8mOtAxDQsRt2QU5rSWCA7YfshddU651Y1NHVrceLANvixKdf9fYO8B/S9fXHodB7HQ==" + }, "node_modules/terser": { - "version": "5.28.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz", - "integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -11825,9 +11766,9 @@ "integrity": "sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ==" }, "node_modules/ts-api-utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", - "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { "node": ">=16" @@ -11986,9 +11927,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "devOptional": true, "peer": true, "bin": { @@ -12092,9 +12033,9 @@ } }, "node_modules/updates": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/updates/-/updates-15.1.2.tgz", - "integrity": "sha512-+/JT4NChl82iexV9G80TY5HF3ubQ5O9UTOk3LlCo4Y4aRCYvo1h4bJE8YkP0PE7KiFRWIQq/rPmUYrY2QF8wVA==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/updates/-/updates-15.3.1.tgz", + "integrity": "sha512-DqHT1aJ6p6jVLWRiAeuVx/TQotvEwUjgrY1Mlc0a2qYk+eKEQVXugQ4M+6QoVMA3X1NFAVsb02d93pmWam4bBA==", "dev": true, "bin": { "updates": "bin/updates.js" @@ -12190,9 +12131,9 @@ } }, "node_modules/vite": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", - "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", + "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", "dev": true, "dependencies": { "esbuild": "^0.19.3", @@ -12272,12 +12213,418 @@ "integrity": "sha512-KRCIFX3PWVUuEjpi9O7EKLT9E27OqOA3RimIvVx6cziLAUxvnk2VvHQfMrP+mKkqyqqSmnnYyTig3OyDnK/zlA==", "dev": true }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/vite/node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -12293,9 +12640,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -12308,19 +12655,19 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.12.0", - "@rollup/rollup-android-arm64": "4.12.0", - "@rollup/rollup-darwin-arm64": "4.12.0", - "@rollup/rollup-darwin-x64": "4.12.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", - "@rollup/rollup-linux-arm64-gnu": "4.12.0", - "@rollup/rollup-linux-arm64-musl": "4.12.0", - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-musl": "4.12.0", - "@rollup/rollup-win32-arm64-msvc": "4.12.0", - "@rollup/rollup-win32-ia32-msvc": "4.12.0", - "@rollup/rollup-win32-x64-msvc": "4.12.0", + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", "fsevents": "~2.3.2" } }, @@ -12390,9 +12737,9 @@ } }, "node_modules/vitest/node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -12518,9 +12865,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -12832,31 +13179,34 @@ } }, "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -13045,9 +13395,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", - "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", "bin": { "yaml": "bin.mjs" }, diff --git a/package.json b/package.json index 1152bfef72..1be87e8b39 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "node": ">= 18.0.0" }, "dependencies": { - "@citation-js/core": "0.7.6", - "@citation-js/plugin-bibtex": "0.7.8", - "@citation-js/plugin-csl": "0.7.6", + "@citation-js/core": "0.7.9", + "@citation-js/plugin-bibtex": "0.7.9", + "@citation-js/plugin-csl": "0.7.9", "@citation-js/plugin-software-formats": "0.6.1", "@claviska/jquery-minicolors": "2.3.6", "@github/markdown-toolbar-element": "2.2.3", @@ -14,7 +14,6 @@ "@github/text-expander-element": "2.6.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.8.0", - "@webcomponents/custom-elements": "1.6.0", "add-asset-webpack-plugin": "2.0.1", "ansi_up": "6.0.2", "asciinema-player": "3.7.0", @@ -23,30 +22,31 @@ "chartjs-plugin-zoom": "2.0.1", "clippie": "4.0.7", "css-loader": "6.10.0", - "css-variables-parser": "1.0.1", "dayjs": "1.11.10", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", - "esbuild-loader": "4.0.3", + "esbuild-loader": "4.1.0", "escape-goat": "4.0.0", "fast-glob": "3.3.2", - "htmx.org": "1.9.10", + "htmx.org": "1.9.11", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "10.8.0", + "mermaid": "10.9.0", "mini-css-extract-plugin": "2.8.1", "minimatch": "9.0.3", - "monaco-editor": "0.46.0", + "monaco-editor": "0.47.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", "postcss": "8.4.35", "postcss-loader": "8.1.1", + "postcss-nesting": "12.1.0", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.11.8", + "swagger-ui-dist": "5.12.0", "tailwindcss": "3.4.1", + "temporal-polyfill": "0.2.3", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", @@ -66,7 +66,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", "@playwright/test": "1.42.1", "@stoplight/spectral-cli": "6.11.0", - "@stylistic/eslint-plugin-js": "1.6.3", + "@stylistic/eslint-plugin-js": "1.7.0", "@stylistic/stylelint-plugin": "2.1.0", "@vitejs/plugin-vue": "5.0.4", "eslint": "8.57.0", @@ -76,12 +76,12 @@ "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-regexp": "2.2.0", + "eslint-plugin-regexp": "2.3.0", "eslint-plugin-sonarjs": "0.24.0", "eslint-plugin-unicorn": "51.0.1", - "eslint-plugin-vitest": "0.3.22", + "eslint-plugin-vitest": "0.3.26", "eslint-plugin-vitest-globals": "1.4.0", - "eslint-plugin-vue": "9.22.0", + "eslint-plugin-vue": "9.23.0", "eslint-plugin-vue-scoped-css": "2.7.2", "eslint-plugin-wc": "2.0.4", "jsdom": "24.0.0", @@ -91,7 +91,7 @@ "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.4", "svgo": "3.2.0", - "updates": "15.1.2", + "updates": "15.3.1", "vite-string-plugin": "1.1.5", "vitest": "1.3.1" }, diff --git a/public/assets/img/svg/gitea-bitbucket.svg b/public/assets/img/svg/gitea-bitbucket.svg index b900335ea1..83e4c5c6e7 100644 --- a/public/assets/img/svg/gitea-bitbucket.svg +++ b/public/assets/img/svg/gitea-bitbucket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-facebook.svg b/public/assets/img/svg/gitea-facebook.svg index cbeb76b127..6101becad2 100644 --- a/public/assets/img/svg/gitea-facebook.svg +++ b/public/assets/img/svg/gitea-facebook.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-jetbrains.svg b/public/assets/img/svg/gitea-jetbrains.svg new file mode 100644 index 0000000000..5821736225 --- /dev/null +++ b/public/assets/img/svg/gitea-jetbrains.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-microsoftonline.svg b/public/assets/img/svg/gitea-microsoftonline.svg index ce4f1a5c8f..f2ce13ac22 100644 --- a/public/assets/img/svg/gitea-microsoftonline.svg +++ b/public/assets/img/svg/gitea-microsoftonline.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-open-with-jetbrains.svg b/public/assets/img/svg/gitea-open-with-jetbrains.svg new file mode 100644 index 0000000000..2b1491b541 --- /dev/null +++ b/public/assets/img/svg/gitea-open-with-jetbrains.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-open-with-vscode.svg b/public/assets/img/svg/gitea-open-with-vscode.svg new file mode 100644 index 0000000000..151c45e210 --- /dev/null +++ b/public/assets/img/svg/gitea-open-with-vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-open-with-vscodium.svg b/public/assets/img/svg/gitea-open-with-vscodium.svg new file mode 100644 index 0000000000..9f70878ba6 --- /dev/null +++ b/public/assets/img/svg/gitea-open-with-vscodium.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-twitter.svg b/public/assets/img/svg/gitea-twitter.svg index 5d11c6eaec..5ed1e264ca 100644 --- a/public/assets/img/svg/gitea-twitter.svg +++ b/public/assets/img/svg/gitea-twitter.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-vscode.svg b/public/assets/img/svg/gitea-vscode.svg deleted file mode 100644 index 453b9befcc..0000000000 --- a/public/assets/img/svg/gitea-vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/assets/img/svg/gitea-vscodium.svg b/public/assets/img/svg/gitea-vscodium.svg new file mode 100644 index 0000000000..6aad3d3a64 --- /dev/null +++ b/public/assets/img/svg/gitea-vscodium.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index a7cb31288c..ff6ec5bd54 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -15,7 +15,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" - secret_module "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/actions" @@ -32,14 +31,24 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv return nil, false, nil } + secrets, err := secret_model.GetSecretsOfTask(ctx, t) + if err != nil { + return nil, false, fmt.Errorf("GetSecretsOfTask: %w", err) + } + + vars, err := actions_model.GetVariablesOfRun(ctx, t.Job.Run) + if err != nil { + return nil, false, fmt.Errorf("GetVariablesOfRun: %w", err) + } + actions.CreateCommitStatus(ctx, t.Job) task := &runnerv1.Task{ Id: t.ID, WorkflowPayload: t.Job.WorkflowPayload, Context: generateTaskContext(t), - Secrets: getSecretsOfTask(ctx, t), - Vars: getVariablesOfTask(ctx, t), + Secrets: secrets, + Vars: vars, } if needs, err := findTaskNeeds(ctx, t); err != nil { @@ -55,71 +64,6 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv return task, true, nil } -func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string { - secrets := map[string]string{} - - secrets["GITHUB_TOKEN"] = task.Token - secrets["GITEA_TOKEN"] = task.Token - - if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget { - // ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_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 - } - - ownerSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID}) - if err != nil { - log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err) - // go on - } - repoSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID}) - if err != nil { - log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) - // go on - } - - for _, secret := range append(ownerSecrets, repoSecrets...) { - if v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data); err != nil { - log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err) - // go on - } else { - secrets[secret.Name] = v - } - } - - return secrets -} - -func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string { - variables := map[string]string{} - - // Global - globalVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{}) - if err != nil { - log.Error("find global variables: %v", err) - } - - // Org / User level - ownerVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID}) - if err != nil { - log.Error("find variables of org: %d, error: %v", task.Job.Run.Repo.OwnerID, err) - } - - // Repo level - repoVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{RepoID: task.Job.Run.RepoID}) - if err != nil { - log.Error("find variables of repo: %d, error: %v", task.Job.Run.RepoID, err) - } - - // Level precedence: Repo > Org / User > Global - for _, v := range append(globalVariables, append(ownerVariables, repoVariables...)...) { - variables[v.Name] = v.Data - } - - return variables -} - func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { event := map[string]any{} _ = json.Unmarshal([]byte(t.Job.Run.EventPayload), &event) diff --git a/routers/api/forgejo/v1/api.go b/routers/api/forgejo/v1/api.go index 33e9eb1967..88c7502e66 100644 --- a/routers/api/forgejo/v1/api.go +++ b/routers/api/forgejo/v1/api.go @@ -5,10 +5,14 @@ package v1 import ( "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/shared" ) func Routes() *web.Route { m := web.NewRoute() + + m.Use(shared.Middlewares()...) + forgejo := NewForgejo() m.Get("", Root) m.Get("/version", forgejo.GetVersion) diff --git a/routers/api/shared/middleware.go b/routers/api/shared/middleware.go new file mode 100644 index 0000000000..e2ff004024 --- /dev/null +++ b/routers/api/shared/middleware.go @@ -0,0 +1,152 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package shared + +import ( + "net/http" + + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/services/auth" + "code.gitea.io/gitea/services/context" + + "github.com/go-chi/cors" +) + +func Middlewares() (stack []any) { + stack = append(stack, securityHeaders()) + + if setting.CORSConfig.Enabled { + stack = append(stack, cors.Handler(cors.Options{ + AllowedOrigins: setting.CORSConfig.AllowDomain, + AllowedMethods: setting.CORSConfig.Methods, + AllowCredentials: setting.CORSConfig.AllowCredentials, + AllowedHeaders: append([]string{"Authorization", "X-Gitea-OTP", "X-Forgejo-OTP"}, setting.CORSConfig.Headers...), + MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), + })) + } + return append(stack, + context.APIContexter(), + + checkDeprecatedAuthMethods, + // Get user from session if logged in. + apiAuth(buildAuthGroup()), + verifyAuthWithOptions(&common.VerifyOptions{ + SignInRequired: setting.Service.RequireSignInView, + }), + ) +} + +func buildAuthGroup() *auth.Group { + group := auth.NewGroup( + &auth.OAuth2{}, + &auth.HTTPSign{}, + &auth.Basic{}, // FIXME: this should be removed once we don't allow basic auth in API + ) + if setting.Service.EnableReverseProxyAuthAPI { + group.Add(&auth.ReverseProxy{}) + } + + if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) { + group.Add(&auth.SSPI{}) // it MUST be the last, see the comment of SSPI + } + + return group +} + +func apiAuth(authMethod auth.Method) func(*context.APIContext) { + return func(ctx *context.APIContext) { + ar, err := common.AuthShared(ctx.Base, nil, authMethod) + if err != nil { + ctx.Error(http.StatusUnauthorized, "APIAuth", err) + return + } + ctx.Doer = ar.Doer + ctx.IsSigned = ar.Doer != nil + ctx.IsBasicAuth = ar.IsBasicAuth + } +} + +// verifyAuthWithOptions checks authentication according to options +func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + // Check prohibit login users. + if ctx.IsSigned { + if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { + ctx.Data["Title"] = ctx.Tr("auth.active_your_account") + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": "This account is not activated.", + }) + return + } + if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin { + log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr()) + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": "This account is prohibited from signing in, please contact your site administrator.", + }) + return + } + + if ctx.Doer.MustChangePassword { + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password", + }) + return + } + } + + // Redirect to dashboard if user tries to visit any non-login page. + if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" { + ctx.Redirect(setting.AppSubURL + "/") + return + } + + if options.SignInRequired { + if !ctx.IsSigned { + // Restrict API calls with error message. + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": "Only signed in user is allowed to call APIs.", + }) + return + } else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { + ctx.Data["Title"] = ctx.Tr("auth.active_your_account") + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": "This account is not activated.", + }) + return + } + } + + if options.AdminRequired { + if !ctx.Doer.IsAdmin { + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": "You have no permission to request for this.", + }) + return + } + } + } +} + +// check for and warn against deprecated authentication options +func checkDeprecatedAuthMethods(ctx *context.APIContext) { + if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" { + ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") + } +} + +func securityHeaders() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + // CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers + // http://stackoverflow.com/a/3146618/244009 + resp.Header().Set("x-content-type-options", "nosniff") + next.ServeHTTP(resp, req) + }) + } +} diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 64315108b0..87a5b28fad 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -133,7 +133,7 @@ func CreateUser(ctx *context.APIContext) { u.UpdatedUnix = u.CreatedUnix } - if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil { + if err := user_model.AdminCreateUser(ctx, u, overwriteDefault); err != nil { if user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err) || db.IsErrNameReserved(err) || @@ -147,6 +147,11 @@ func CreateUser(ctx *context.APIContext) { } return } + + if !user_model.IsEmailDomainAllowed(u.Email) { + ctx.Resp.Header().Add("X-Gitea-Warning", fmt.Sprintf("the domain of user email %s conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", u.Email)) + } + log.Trace("Account created by admin (%s): %s", ctx.Doer.Name, u.Name) // Send email notification. @@ -209,7 +214,7 @@ func EditUser(ctx *context.APIContext) { } if form.Email != nil { - if err := user_service.AddOrSetPrimaryEmailAddress(ctx, ctx.ContextUser, *form.Email); err != nil { + if err := user_service.AdminAddOrSetPrimaryEmailAddress(ctx, ctx.ContextUser, *form.Email); err != nil { switch { case user_model.IsErrEmailCharIsNotSupported(err), user_model.IsErrEmailInvalid(err): ctx.Error(http.StatusBadRequest, "EmailInvalid", err) @@ -220,6 +225,10 @@ func EditUser(ctx *context.APIContext) { } return } + + if !user_model.IsEmailDomainAllowed(*form.Email) { + ctx.Resp.Header().Add("X-Gitea-Warning", fmt.Sprintf("the domain of user email %s conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", *form.Email)) + } } opts := &user_service.UpdateOptions{ diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index c296cac799..b202e32e4e 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -72,7 +72,6 @@ import ( actions_model "code.gitea.io/gitea/models/actions" auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" @@ -84,6 +83,7 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/shared" "code.gitea.io/gitea/routers/api/v1/activitypub" "code.gitea.io/gitea/routers/api/v1/admin" "code.gitea.io/gitea/routers/api/v1/misc" @@ -93,7 +93,6 @@ import ( "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/settings" "code.gitea.io/gitea/routers/api/v1/user" - "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -101,7 +100,6 @@ import ( _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation "gitea.com/go-chi/binding" - "github.com/go-chi/cors" ) func sudo() func(ctx *context.APIContext) { @@ -731,98 +729,6 @@ func bind[T any](_ T) any { } } -func buildAuthGroup() *auth.Group { - group := auth.NewGroup( - &auth.OAuth2{}, - &auth.HTTPSign{}, - &auth.Basic{}, // FIXME: this should be removed once we don't allow basic auth in API - ) - if setting.Service.EnableReverseProxyAuthAPI { - group.Add(&auth.ReverseProxy{}) - } - - if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) { - group.Add(&auth.SSPI{}) // it MUST be the last, see the comment of SSPI - } - - return group -} - -func apiAuth(authMethod auth.Method) func(*context.APIContext) { - return func(ctx *context.APIContext) { - ar, err := common.AuthShared(ctx.Base, nil, authMethod) - if err != nil { - ctx.Error(http.StatusUnauthorized, "APIAuth", err) - return - } - ctx.Doer = ar.Doer - ctx.IsSigned = ar.Doer != nil - ctx.IsBasicAuth = ar.IsBasicAuth - } -} - -// verifyAuthWithOptions checks authentication according to options -func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIContext) { - return func(ctx *context.APIContext) { - // Check prohibit login users. - if ctx.IsSigned { - if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { - ctx.Data["Title"] = ctx.Tr("auth.active_your_account") - ctx.JSON(http.StatusForbidden, map[string]string{ - "message": "This account is not activated.", - }) - return - } - if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin { - log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr()) - ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") - ctx.JSON(http.StatusForbidden, map[string]string{ - "message": "This account is prohibited from signing in, please contact your site administrator.", - }) - return - } - - if ctx.Doer.MustChangePassword { - ctx.JSON(http.StatusForbidden, map[string]string{ - "message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password", - }) - return - } - } - - // Redirect to dashboard if user tries to visit any non-login page. - if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" { - ctx.Redirect(setting.AppSubURL + "/") - return - } - - if options.SignInRequired { - if !ctx.IsSigned { - // Restrict API calls with error message. - ctx.JSON(http.StatusForbidden, map[string]string{ - "message": "Only signed in user is allowed to call APIs.", - }) - return - } else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { - ctx.Data["Title"] = ctx.Tr("auth.active_your_account") - ctx.JSON(http.StatusForbidden, map[string]string{ - "message": "This account is not activated.", - }) - return - } - } - - if options.AdminRequired { - if !ctx.Doer.IsAdmin { - ctx.JSON(http.StatusForbidden, map[string]string{ - "message": "You have no permission to request for this.", - }) - return - } - } - } -} - func individualPermsChecker(ctx *context.APIContext) { // org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked. if ctx.ContextUser.IsIndividual() { @@ -841,37 +747,11 @@ func individualPermsChecker(ctx *context.APIContext) { } } -// check for and warn against deprecated authentication options -func checkDeprecatedAuthMethods(ctx *context.APIContext) { - if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" { - ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") - } -} - // Routes registers all v1 APIs routes to web application. func Routes() *web.Route { m := web.NewRoute() - m.Use(securityHeaders()) - if setting.CORSConfig.Enabled { - m.Use(cors.Handler(cors.Options{ - AllowedOrigins: setting.CORSConfig.AllowDomain, - AllowedMethods: setting.CORSConfig.Methods, - AllowCredentials: setting.CORSConfig.AllowCredentials, - AllowedHeaders: append([]string{"Authorization", "X-Gitea-OTP", "X-Forgejo-OTP"}, setting.CORSConfig.Headers...), - MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), - })) - } - m.Use(context.APIContexter()) - - m.Use(checkDeprecatedAuthMethods) - - // Get user from session if logged in. - m.Use(apiAuth(buildAuthGroup())) - - m.Use(verifyAuthWithOptions(&common.VerifyOptions{ - SignInRequired: setting.Service.RequireSignInView, - })) + m.Use(shared.Middlewares()...) m.Group("", func() { // Miscellaneous (no scope required) @@ -1627,14 +1507,3 @@ func Routes() *web.Route { return m } - -func securityHeaders() func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - // CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers - // http://stackoverflow.com/a/3146618/244009 - resp.Header().Set("x-content-type-options", "nosniff") - next.ServeHTTP(resp, req) - }) - } -} diff --git a/routers/api/v1/misc/markup_test.go b/routers/api/v1/misc/markup_test.go index f499501c2f..5236fd06ae 100644 --- a/routers/api/v1/misc/markup_test.go +++ b/routers/api/v1/misc/markup_test.go @@ -20,9 +20,9 @@ import ( ) const ( - AppURL = "http://localhost:3000/" - Repo = "gogits/gogs" - AppSubURL = AppURL + Repo + "/" + AppURL = "http://localhost:3000/" + Repo = "gogits/gogs" + FullURL = AppURL + Repo + "/" ) func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, responseCode int) { @@ -74,20 +74,20 @@ func TestAPI_RenderGFM(t *testing.T) { // rendered `

      Wiki! Enjoy :)

      `, // Guard wiki sidebar: special syntax `[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`, // rendered - `

      Guardfile-DSL / Configuring-Guard

      + `

      Guardfile-DSL / Configuring-Guard

      `, // special syntax `[[Name|Link]]`, // rendered - `

      Name

      + `

      Name

      `, // empty ``, @@ -111,8 +111,8 @@ Here are some links to the most important topics. You can find the full list of

      Wine Staging on website wine-staging.com.

      Here are some links to the most important topics. You can find the full list of pages at the sidebar.

      -

      Configuration -images/icon-bug.png

      +

      Configuration +images/icon-bug.png

      `, } diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index af3f890f88..843da55139 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -269,28 +269,28 @@ func SearchIssues(ctx *context.APIContext) { } if since != 0 { - searchOpt.UpdatedAfterUnix = &since + searchOpt.UpdatedAfterUnix = optional.Some(since) } if before != 0 { - searchOpt.UpdatedBeforeUnix = &before + searchOpt.UpdatedBeforeUnix = optional.Some(before) } if ctx.IsSigned { ctxUserID := ctx.Doer.ID if ctx.FormBool("created") { - searchOpt.PosterID = &ctxUserID + searchOpt.PosterID = optional.Some(ctxUserID) } if ctx.FormBool("assigned") { - searchOpt.AssigneeID = &ctxUserID + searchOpt.AssigneeID = optional.Some(ctxUserID) } if ctx.FormBool("mentioned") { - searchOpt.MentionID = &ctxUserID + searchOpt.MentionID = optional.Some(ctxUserID) } if ctx.FormBool("review_requested") { - searchOpt.ReviewRequestedID = &ctxUserID + searchOpt.ReviewRequestedID = optional.Some(ctxUserID) } if ctx.FormBool("reviewed") { - searchOpt.ReviewedID = &ctxUserID + searchOpt.ReviewedID = optional.Some(ctxUserID) } } @@ -368,7 +368,7 @@ func ListIssues(ctx *context.APIContext) { // required: false // - name: created_by // in: query - // description: Only show items which were created by the the given user + // description: Only show items which were created by the given user // type: string // - name: assigned_by // in: query @@ -502,10 +502,10 @@ func ListIssues(ctx *context.APIContext) { SortBy: issue_indexer.SortByCreatedDesc, } if since != 0 { - searchOpt.UpdatedAfterUnix = &since + searchOpt.UpdatedAfterUnix = optional.Some(since) } if before != 0 { - searchOpt.UpdatedBeforeUnix = &before + searchOpt.UpdatedBeforeUnix = optional.Some(before) } if len(labelIDs) == 1 && labelIDs[0] == 0 { searchOpt.NoLabelOnly = true @@ -526,13 +526,13 @@ func ListIssues(ctx *context.APIContext) { } if createdByID > 0 { - searchOpt.PosterID = &createdByID + searchOpt.PosterID = optional.Some(createdByID) } if assignedByID > 0 { - searchOpt.AssigneeID = &assignedByID + searchOpt.AssigneeID = optional.Some(assignedByID) } if mentionedByID > 0 { - searchOpt.MentionID = &mentionedByID + searchOpt.MentionID = optional.Some(mentionedByID) } ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index f78e34d7b3..c2d86541b6 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1065,6 +1065,8 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) return nil, nil, nil, nil, "", "" } headBranch = headInfos[1] + // The head repository can also point to the same repo + isSameRepo = ctx.Repo.Owner.ID == headUser.ID } else { ctx.NotFound() diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 6860d6e773..9ccbb57c52 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -692,7 +692,7 @@ func prepareSingleReview(ctx *context.APIContext) (*issues_model.Review, *issues return nil, nil, true } - // validate the the review is for the given PR + // validate the review is for the given PR if review.IssueID != pr.IssueID { ctx.NotFound("ReviewNotInPR") return nil, nil, true diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 40960144c7..316a1161da 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -720,7 +720,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err if ctx.Repo.GitRepo == nil && !repo.IsEmpty { var err error - ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository) + ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, repo) if err != nil { ctx.Error(http.StatusInternalServerError, "Unable to OpenRepository", err) return err @@ -731,7 +731,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err // Default branch only updated if changed and exist or the repository is empty if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && (repo.IsEmpty || ctx.Repo.GitRepo.IsBranchExist(*opts.DefaultBranch)) { if !repo.IsEmpty { - if err := ctx.Repo.GitRepo.SetDefaultBranch(*opts.DefaultBranch); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, *opts.DefaultBranch); err != nil { if !git.IsErrUnsupportedVersion(err) { ctx.Error(http.StatusInternalServerError, "SetDefaultBranch", err) return err diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index 53711bedeb..9e36ea0aed 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -14,7 +14,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" - files_service "code.gitea.io/gitea/services/repository/files" + commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus" ) // NewCommitStatus creates a new CommitStatus @@ -64,7 +64,7 @@ func NewCommitStatus(ctx *context.APIContext) { Description: form.Description, Context: form.Context, } - if err := files_service.CreateCommitStatus(ctx, ctx.Repo.Repository, ctx.Doer, sha, status); err != nil { + if err := commitstatus_service.CreateCommitStatus(ctx, ctx.Repo.Repository, ctx.Doer, sha, status); err != nil { ctx.Error(http.StatusInternalServerError, "CreateCommitStatus", err) return } diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index ada6759f8e..bcbfd93bd3 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -5,6 +5,7 @@ package user import ( std_ctx "context" + "fmt" "net/http" asymkey_model "code.gitea.io/gitea/models/asymkey" @@ -198,6 +199,11 @@ func GetPublicKey(ctx *context.APIContext) { // CreateUserPublicKey creates new public key to given user by ID. func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid int64) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { + ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + return + } + content, err := asymkey_model.CheckPublicKeyString(form.Key) if err != nil { repo.HandleCheckKeyStringError(ctx, err) @@ -263,6 +269,11 @@ func DeletePublicKey(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { + ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + return + } + id := ctx.ParamsInt64(":id") externallyManaged, err := asymkey_model.PublicKeyIsExternallyManaged(ctx, id) if err != nil { diff --git a/routers/common/markup.go b/routers/common/markup.go index 7819ee7227..2d5638ef61 100644 --- a/routers/common/markup.go +++ b/routers/common/markup.go @@ -34,7 +34,8 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr if err := markdown.RenderRaw(&markup.RenderContext{ Ctx: ctx, Links: markup.Links{ - Base: urlPrefix, + AbsolutePrefix: true, + Base: urlPrefix, }, }, strings.NewReader(text), ctx.Resp); err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) @@ -79,7 +80,8 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr if err := markup.Render(&markup.RenderContext{ Ctx: ctx, Links: markup.Links{ - Base: urlPrefix, + AbsolutePrefix: true, + Base: urlPrefix, }, Metas: meta, IsWiki: wiki, diff --git a/routers/private/default_branch.go b/routers/private/default_branch.go index 2e323129ef..33890be6a9 100644 --- a/routers/private/default_branch.go +++ b/routers/private/default_branch.go @@ -9,6 +9,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/private" gitea_context "code.gitea.io/gitea/services/context" ) @@ -20,7 +21,7 @@ func SetDefaultBranch(ctx *gitea_context.PrivateContext) { branch := ctx.Params(":branch") ctx.Repo.Repository.DefaultBranch = branch - if err := ctx.Repo.GitRepo.SetDefaultBranch(ctx.Repo.Repository.DefaultBranch); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil { if !git.IsErrUnsupportedVersion(err) { ctx.JSON(http.StatusInternalServerError, private.Response{ Err: fmt.Sprintf("Unable to set default branch on repository: %s/%s Error: %v", ownerName, repoName, err), diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go index f4c698d3ea..f5527cb15b 100644 --- a/routers/private/hook_post_receive.go +++ b/routers/private/hook_post_receive.go @@ -8,9 +8,11 @@ import ( "net/http" "strconv" + git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" repo_module "code.gitea.io/gitea/modules/repository" @@ -27,6 +29,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) { // We don't rely on RepoAssignment here because: // a) we don't need the git repo in this function + // OUT OF DATE: we do need the git repo to sync the branch to the db now. // b) our update function will likely change the repository in the db so we will need to refresh it // c) we don't always need the repo @@ -34,7 +37,11 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) { repoName := ctx.Params(":repo") // defer getting the repository at this point - as we should only retrieve it if we're going to call update - var repo *repo_model.Repository + var ( + repo *repo_model.Repository + gitRepo *git.Repository + ) + defer gitRepo.Close() // it's safe to call Close on a nil pointer updates := make([]*repo_module.PushUpdateOptions, 0, len(opts.OldCommitIDs)) wasEmpty := false @@ -75,6 +82,61 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) { } if repo != nil && len(updates) > 0 { + branchesToSync := make([]*repo_module.PushUpdateOptions, 0, len(updates)) + for _, update := range updates { + if !update.RefFullName.IsBranch() { + continue + } + if repo == nil { + repo = loadRepository(ctx, ownerName, repoName) + if ctx.Written() { + return + } + wasEmpty = repo.IsEmpty + } + + if update.IsDelRef() { + if err := git_model.AddDeletedBranch(ctx, repo.ID, update.RefFullName.BranchName(), update.PusherID); err != nil { + log.Error("Failed to add deleted branch: %s/%s Error: %v", ownerName, repoName, err) + ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{ + Err: fmt.Sprintf("Failed to add deleted branch: %s/%s Error: %v", ownerName, repoName, err), + }) + return + } + } else { + branchesToSync = append(branchesToSync, update) + } + } + if len(branchesToSync) > 0 { + if gitRepo == nil { + var err error + gitRepo, err = gitrepo.OpenRepository(ctx, repo) + if err != nil { + log.Error("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err) + ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{ + Err: fmt.Sprintf("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err), + }) + return + } + } + + var ( + branchNames = make([]string, 0, len(branchesToSync)) + commitIDs = make([]string, 0, len(branchesToSync)) + ) + for _, update := range branchesToSync { + branchNames = append(branchNames, update.RefFullName.BranchName()) + commitIDs = append(commitIDs, update.NewCommitID) + } + + if err := repo_service.SyncBranchesToDB(ctx, repo.ID, opts.UserID, branchNames, commitIDs, gitRepo.GetCommit); err != nil { + ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{ + Err: fmt.Sprintf("Failed to sync branch to DB in repository: %s/%s Error: %v", ownerName, repoName, err), + }) + return + } + } + if err := repo_service.PushUpdates(updates); err != nil { log.Error("Failed to Update: %s/%s Total Updates: %d", ownerName, repoName, len(updates)) for i, update := range updates { diff --git a/routers/web/admin/admin_test.go b/routers/web/admin/admin_test.go index c73780d60f..3518869ede 100644 --- a/routers/web/admin/admin_test.go +++ b/routers/web/admin/admin_test.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/services/contexttest" + "github.com/stretchr/testify/assert" ) diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go index d9b1973332..2f5f17e201 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -7,11 +7,11 @@ package admin import ( "net/http" "net/url" + "strconv" "strings" system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -24,7 +24,10 @@ import ( "gitea.com/go-chi/session" ) -const tplConfig base.TplName = "admin/config" +const ( + tplConfig base.TplName = "admin/config" + tplConfigSettings base.TplName = "admin/config_settings" +) // SendTestMail send test mail to confirm mail service is OK func SendTestMail(ctx *context.Context) { @@ -98,8 +101,9 @@ func shadowPassword(provider, cfgItem string) string { // Config show admin config page func Config(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("admin.config") + ctx.Data["Title"] = ctx.Tr("admin.config_summary") ctx.Data["PageIsAdminConfig"] = true + ctx.Data["PageIsAdminConfigSummary"] = true ctx.Data["CustomConf"] = setting.CustomConf ctx.Data["AppUrl"] = setting.AppURL @@ -161,23 +165,70 @@ func Config(ctx *context.Context) { ctx.Data["Loggers"] = log.GetManager().DumpLoggers() config.GetDynGetter().InvalidateCache() - ctx.Data["SystemConfig"] = setting.Config() prepareDeprecatedWarningsAlert(ctx) ctx.HTML(http.StatusOK, tplConfig) } +func ConfigSettings(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("admin.config_settings") + ctx.Data["PageIsAdminConfig"] = true + ctx.Data["PageIsAdminConfigSettings"] = true + ctx.Data["DefaultOpenWithEditorAppsString"] = setting.DefaultOpenWithEditorApps().ToTextareaString() + ctx.HTML(http.StatusOK, tplConfigSettings) +} + func ChangeConfig(ctx *context.Context) { key := strings.TrimSpace(ctx.FormString("key")) value := ctx.FormString("value") cfg := setting.Config() - allowedKeys := container.SetOf(cfg.Picture.DisableGravatar.DynKey(), cfg.Picture.EnableFederatedAvatar.DynKey()) - if !allowedKeys.Contains(key) { + + marshalBool := func(v string) (string, error) { + if b, _ := strconv.ParseBool(v); b { + return "true", nil + } + return "false", nil + } + marshalOpenWithApps := func(value string) (string, error) { + lines := strings.Split(value, "\n") + var openWithEditorApps setting.OpenWithEditorAppsType + for _, line := range lines { + line = strings.TrimSpace(line) + if line == "" { + continue + } + displayName, openURL, ok := strings.Cut(line, "=") + displayName, openURL = strings.TrimSpace(displayName), strings.TrimSpace(openURL) + if !ok || displayName == "" || openURL == "" { + continue + } + openWithEditorApps = append(openWithEditorApps, setting.OpenWithEditorApp{ + DisplayName: strings.TrimSpace(displayName), + OpenURL: strings.TrimSpace(openURL), + }) + } + b, err := json.Marshal(openWithEditorApps) + if err != nil { + return "", err + } + return string(b), nil + } + marshallers := map[string]func(string) (string, error){ + cfg.Picture.DisableGravatar.DynKey(): marshalBool, + cfg.Picture.EnableFederatedAvatar.DynKey(): marshalBool, + cfg.Repository.OpenWithEditorApps.DynKey(): marshalOpenWithApps, + } + marshaller, hasMarshaller := marshallers[key] + if !hasMarshaller { ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key)) return } - if err := system_model.SetSettings(ctx, map[string]string{key: value}); err != nil { - log.Error("set setting failed: %v", err) + marshaledValue, err := marshaller(value) + if err != nil { + ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key)) + return + } + if err = system_model.SetSettings(ctx, map[string]string{key: marshaledValue}); err != nil { ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key)) return } diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index a34e0d0f0d..6dfcfc3d9a 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -177,7 +177,7 @@ func NewUserPost(ctx *context.Context) { u.MustChangePassword = form.MustChangePassword } - if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil { + if err := user_model.AdminCreateUser(ctx, u, overwriteDefault); err != nil { switch { case user_model.IsErrUserAlreadyExist(err): ctx.Data["Err_UserName"] = true @@ -202,6 +202,11 @@ func NewUserPost(ctx *context.Context) { } return } + + if !user_model.IsEmailDomainAllowed(u.Email) { + ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", u.Email)) + } + log.Trace("Account created by admin (%s): %s", ctx.Doer.Name, u.Name) // Send email notification. @@ -412,7 +417,7 @@ func EditUserPost(ctx *context.Context) { } if form.Email != "" { - if err := user_service.AddOrSetPrimaryEmailAddress(ctx, u, form.Email); err != nil { + if err := user_service.AdminAddOrSetPrimaryEmailAddress(ctx, u, form.Email); err != nil { switch { case user_model.IsErrEmailCharIsNotSupported(err), user_model.IsErrEmailInvalid(err): ctx.Data["Err_Email"] = true @@ -425,6 +430,9 @@ func EditUserPost(ctx *context.Context) { } return } + if !user_model.IsEmailDomainAllowed(form.Email) { + ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", form.Email)) + } } opts := &user_service.UpdateOptions{ diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 09469385d0..2a8f5c05d7 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -135,9 +135,21 @@ func resetLocale(ctx *context.Context, u *user_model.User) error { return nil } +func RedirectAfterLogin(ctx *context.Context) { + redirectTo := ctx.FormString("redirect_to") + if redirectTo == "" { + redirectTo = ctx.GetSiteCookie("redirect_to") + } + middleware.DeleteRedirectToCookie(ctx.Resp) + nextRedirectTo := setting.AppSubURL + string(setting.LandingPageURL) + if setting.LandingPageURL == setting.LandingPageLogin { + nextRedirectTo = setting.AppSubURL + "/" // do not cycle-redirect to the login page + } + ctx.RedirectToFirst(redirectTo, nextRedirectTo) +} + func CheckAutoLogin(ctx *context.Context) bool { - // Check auto-login - isSucceed, err := autoSignIn(ctx) + isSucceed, err := autoSignIn(ctx) // try to auto-login if err != nil { ctx.ServerError("autoSignIn", err) return true @@ -146,17 +158,10 @@ func CheckAutoLogin(ctx *context.Context) bool { redirectTo := ctx.FormString("redirect_to") if len(redirectTo) > 0 { middleware.SetRedirectToCookie(ctx.Resp, redirectTo) - } else { - redirectTo = ctx.GetSiteCookie("redirect_to") } if isSucceed { - middleware.DeleteRedirectToCookie(ctx.Resp) - nextRedirectTo := setting.AppSubURL + string(setting.LandingPageURL) - if setting.LandingPageURL == setting.LandingPageLogin { - nextRedirectTo = setting.AppSubURL + "/" // do not cycle-redirect to the login page - } - ctx.RedirectToFirst(redirectTo, nextRedirectTo) + RedirectAfterLogin(ctx) return true } @@ -171,6 +176,11 @@ func SignIn(ctx *context.Context) { return } + if ctx.IsSigned { + RedirectAfterLogin(ctx) + return + } + oauth2Providers, err := oauth2.GetOAuth2Providers(ctx, optional.Some(true)) if err != nil { ctx.ServerError("UserSignIn", err) diff --git a/routers/web/auth/auth_test.go b/routers/web/auth/auth_test.go new file mode 100644 index 0000000000..c6afbf877c --- /dev/null +++ b/routers/web/auth/auth_test.go @@ -0,0 +1,43 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "net/http" + "net/url" + "testing" + + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/services/contexttest" + + "github.com/stretchr/testify/assert" +) + +func TestUserLogin(t *testing.T) { + ctx, resp := contexttest.MockContext(t, "/user/login") + SignIn(ctx) + assert.Equal(t, http.StatusOK, resp.Code) + + ctx, resp = contexttest.MockContext(t, "/user/login") + ctx.IsSigned = true + SignIn(ctx) + assert.Equal(t, http.StatusSeeOther, resp.Code) + assert.Equal(t, "/", test.RedirectURL(resp)) + + ctx, resp = contexttest.MockContext(t, "/user/login?redirect_to=/other") + ctx.IsSigned = true + SignIn(ctx) + assert.Equal(t, "/other", test.RedirectURL(resp)) + + ctx, resp = contexttest.MockContext(t, "/user/login") + ctx.Req.AddCookie(&http.Cookie{Name: "redirect_to", Value: "/other-cookie"}) + ctx.IsSigned = true + SignIn(ctx) + assert.Equal(t, "/other-cookie", test.RedirectURL(resp)) + + ctx, resp = contexttest.MockContext(t, "/user/login?redirect_to="+url.QueryEscape("https://example.com")) + ctx.IsSigned = true + SignIn(ctx) + assert.Equal(t, "/", test.RedirectURL(resp)) +} diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go index 2cde8b655e..75bd0f3d24 100644 --- a/routers/web/explore/code.go +++ b/routers/web/explore/code.go @@ -6,6 +6,7 @@ package explore import ( "net/http" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" code_indexer "code.gitea.io/gitea/modules/indexer/code" @@ -35,7 +36,7 @@ func Code(ctx *context.Context) { keyword := ctx.FormTrim("q") queryType := ctx.FormTrim("t") - isMatch := queryType == "match" + isFuzzy := queryType != "match" ctx.Data["Keyword"] = keyword ctx.Data["Language"] = language @@ -77,7 +78,16 @@ func Code(ctx *context.Context) { ) if (len(repoIDs) > 0) || isAdmin { - total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) + total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{ + RepoIDs: repoIDs, + Keyword: keyword, + IsKeywordFuzzy: isFuzzy, + Language: language, + Paginator: &db.ListOptions{ + Page: page, + PageSize: setting.UI.RepoSearchPagingNum, + }, + }) if err != nil { if code_indexer.IsAvailable(ctx) { ctx.ServerError("SearchResults", err) diff --git a/routers/web/repo/badges/badges.go b/routers/web/repo/badges/badges.go index 7f4549d606..ed40e982a1 100644 --- a/routers/web/repo/badges/badges.go +++ b/routers/web/repo/badges/badges.go @@ -18,8 +18,8 @@ import ( func getBadgeURL(ctx *context_module.Context, label, text, color string) string { sb := &strings.Builder{} _ = setting.Badges.GeneratorURLTemplateTemplate.Execute(sb, map[string]string{ - "label": url.PathEscape(label), - "text": url.PathEscape(text), + "label": url.PathEscape(strings.ReplaceAll(label, "-", "--")), + "text": url.PathEscape(strings.ReplaceAll(text, "-", "--")), "color": url.PathEscape(color), }) diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index ae51f0596b..f879a98786 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -148,12 +148,7 @@ func RestoreBranchPost(ctx *context.Context) { return } - objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath()) - if err != nil { - log.Error("RestoreBranch: CreateBranch: %w", err) - ctx.Flash.Error(ctx.Tr("repo.branch.restore_failed", deletedBranch.Name)) - return - } + objectFormat := git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName) // Don't return error below this if err := repo_service.PushUpdate( diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 2e2d12672e..e4193e9b79 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2636,9 +2636,9 @@ func SearchIssues(ctx *context.Context) { } } - var projectID *int64 + projectID := optional.None[int64]() if v := ctx.FormInt64("project"); v > 0 { - projectID = &v + projectID = optional.Some(v) } // this api is also used in UI, @@ -2667,28 +2667,28 @@ func SearchIssues(ctx *context.Context) { } if since != 0 { - searchOpt.UpdatedAfterUnix = &since + searchOpt.UpdatedAfterUnix = optional.Some(since) } if before != 0 { - searchOpt.UpdatedBeforeUnix = &before + searchOpt.UpdatedBeforeUnix = optional.Some(before) } if ctx.IsSigned { ctxUserID := ctx.Doer.ID if ctx.FormBool("created") { - searchOpt.PosterID = &ctxUserID + searchOpt.PosterID = optional.Some(ctxUserID) } if ctx.FormBool("assigned") { - searchOpt.AssigneeID = &ctxUserID + searchOpt.AssigneeID = optional.Some(ctxUserID) } if ctx.FormBool("mentioned") { - searchOpt.MentionID = &ctxUserID + searchOpt.MentionID = optional.Some(ctxUserID) } if ctx.FormBool("review_requested") { - searchOpt.ReviewRequestedID = &ctxUserID + searchOpt.ReviewRequestedID = optional.Some(ctxUserID) } if ctx.FormBool("reviewed") { - searchOpt.ReviewedID = &ctxUserID + searchOpt.ReviewedID = optional.Some(ctxUserID) } } @@ -2795,9 +2795,9 @@ func ListIssues(ctx *context.Context) { } } - var projectID *int64 + projectID := optional.None[int64]() if v := ctx.FormInt64("project"); v > 0 { - projectID = &v + projectID = optional.Some(v) } isPull := optional.None[bool]() @@ -2835,10 +2835,10 @@ func ListIssues(ctx *context.Context) { SortBy: issue_indexer.SortByCreatedDesc, } if since != 0 { - searchOpt.UpdatedAfterUnix = &since + searchOpt.UpdatedAfterUnix = optional.Some(since) } if before != 0 { - searchOpt.UpdatedBeforeUnix = &before + searchOpt.UpdatedBeforeUnix = optional.Some(before) } if len(labelIDs) == 1 && labelIDs[0] == 0 { searchOpt.NoLabelOnly = true @@ -2859,13 +2859,13 @@ func ListIssues(ctx *context.Context) { } if createdByID > 0 { - searchOpt.PosterID = &createdByID + searchOpt.PosterID = optional.Some(createdByID) } if assignedByID > 0 { - searchOpt.AssigneeID = &assignedByID + searchOpt.AssigneeID = optional.Some(assignedByID) } if mentionedByID > 0 { - searchOpt.MentionID = &mentionedByID + searchOpt.MentionID = optional.Some(mentionedByID) } ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index c93d342e62..8727f3d1e3 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -186,7 +186,7 @@ func updateForkRepositoryInContext(ctx *context.Context, forkRepo *repo_model.Re ctx.Data["ContextUser"] = orgs[0] } else { ctx.Data["CanForkRepo"] = false - ctx.Flash.Error(ctx.Tr("repo.fork_no_valid_owners"), true) + ctx.RenderWithErr(ctx.Tr("repo.fork_no_valid_owners"), tplFork, nil) return false } diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 77aab8354b..07e6c937b0 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -35,6 +35,7 @@ import ( "code.gitea.io/gitea/services/forms" repo_service "code.gitea.io/gitea/services/repository" archiver_service "code.gitea.io/gitea/services/repository/archiver" + commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus" ) const ( @@ -542,9 +543,13 @@ func InitiateDownload(ctx *context.Context) { // SearchRepo repositories via options func SearchRepo(ctx *context.Context) { + page := ctx.FormInt("page") + if page <= 0 { + page = 1 + } opts := &repo_model.SearchRepoOptions{ ListOptions: db.ListOptions{ - Page: ctx.FormInt("page"), + Page: page, PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), }, Actor: ctx.Doer, @@ -630,30 +635,14 @@ func SearchRepo(ctx *context.Context) { return } - // collect the latest commit of each repo - // at most there are dozens of repos (limited by MaxResponseItems), so it's not a big problem at the moment - repoBranchNames := make(map[int64]string, len(repos)) - for _, repo := range repos { - repoBranchNames[repo.ID] = repo.DefaultBranch - } - - repoIDsToLatestCommitSHAs, err := git_model.FindBranchesByRepoAndBranchName(ctx, repoBranchNames) + latestCommitStatuses, err := commitstatus_service.FindReposLastestCommitStatuses(ctx, repos) if err != nil { - log.Error("FindBranchesByRepoAndBranchName: %v", err) - return - } - - // call the database O(1) times to get the commit statuses for all repos - repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoIDsToLatestCommitSHAs, db.ListOptionsAll) - if err != nil { - log.Error("GetLatestCommitStatusForPairs: %v", err) + log.Error("FindReposLastestCommitStatuses: %v", err) return } results := make([]*repo_service.WebSearchRepository, len(repos)) for i, repo := range repos { - latestCommitStatus := git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID]) - results[i] = &repo_service.WebSearchRepository{ Repository: &api.Repository{ ID: repo.ID, @@ -667,8 +656,11 @@ func SearchRepo(ctx *context.Context) { Link: repo.Link(), Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate, }, - LatestCommitStatus: latestCommitStatus, - LocaleLatestCommitStatus: latestCommitStatus.LocaleString(ctx.Locale), + } + + if latestCommitStatuses[i] != nil { + results[i].LatestCommitStatus = latestCommitStatuses[i] + results[i].LocaleLatestCommitStatus = latestCommitStatuses[i].LocaleString(ctx.Locale) } } diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index 74970c02c9..d22a691a70 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -6,6 +6,7 @@ package repo import ( "net/http" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/setting" @@ -21,7 +22,7 @@ func Search(ctx *context.Context) { keyword := ctx.FormTrim("q") queryType := ctx.FormTrim("t") - isMatch := queryType == "match" + isFuzzy := queryType != "match" ctx.Data["Keyword"] = keyword ctx.Data["Language"] = language @@ -43,8 +44,16 @@ func Search(ctx *context.Context) { if setting.Indexer.RepoIndexerEnabled { ctx.Data["CodeIndexerEnabled"] = true - total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, []int64{ctx.Repo.Repository.ID}, - language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) + total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{ + RepoIDs: []int64{ctx.Repo.Repository.ID}, + Keyword: keyword, + IsKeywordFuzzy: isFuzzy, + Language: language, + Paginator: &db.ListOptions{ + Page: page, + PageSize: setting.UI.RepoSearchPagingNum, + }, + }) if err != nil { if code_indexer.IsAvailable(ctx) { ctx.ServerError("SearchResults", err) diff --git a/routers/web/repo/setting/default_branch.go b/routers/web/repo/setting/default_branch.go index 610fc5bcdf..d0b32ef079 100644 --- a/routers/web/repo/setting/default_branch.go +++ b/routers/web/repo/setting/default_branch.go @@ -8,6 +8,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/web/repo" @@ -40,7 +41,7 @@ func SetDefaultBranchPost(ctx *context.Context) { return } else if repo.DefaultBranch != branch { repo.DefaultBranch = branch - if err := ctx.Repo.GitRepo.SetDefaultBranch(branch); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, repo, branch); err != nil { if !git.IsErrUnsupportedVersion(err) { ctx.ServerError("SetDefaultBranch", err) return diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index 4e967c86d6..2e703a197a 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -616,6 +616,7 @@ func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) { return nil, nil } ctx.Data["BaseLink"] = orCtx.Link + ctx.Data["BaseLinkNew"] = orCtx.LinkNew var w *webhook.Webhook if orCtx.RepoID > 0 { @@ -684,12 +685,7 @@ func TestWebhook(ctx *context.Context) { commit := ctx.Repo.Commit if commit == nil { ghost := user_model.NewGhostUser() - objectFormat, err := git.GetObjectFormatOfRepo(ctx, ctx.Repo.Repository.RepoPath()) - if err != nil { - ctx.Flash.Error("GetObjectFormatOfRepo: " + err.Error()) - ctx.Status(http.StatusInternalServerError) - return - } + objectFormat := git.ObjectFormatFromName(ctx.Repo.Repository.ObjectFormatName) commit = &git.Commit{ ID: objectFormat.EmptyObjectID(), Author: ghost.NewGitSig(), diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 27984e7621..8ddfd92aa1 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -36,6 +36,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -43,6 +44,7 @@ import ( repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/web/feed" @@ -811,7 +813,7 @@ func Home(ctx *context.Context) { return } - renderCode(ctx) + renderHomeCode(ctx) } // LastCommit returns lastCommit data for the provided branch/tag/commit and directory (in url) and filenames in body @@ -931,9 +933,33 @@ func renderRepoTopics(ctx *context.Context) { ctx.Data["Topics"] = topics } -func renderCode(ctx *context.Context) { +func prepareOpenWithEditorApps(ctx *context.Context) { + var tmplApps []map[string]any + apps := setting.Config().Repository.OpenWithEditorApps.Value(ctx) + if len(apps) == 0 { + apps = setting.DefaultOpenWithEditorApps() + } + for _, app := range apps { + schema, _, _ := strings.Cut(app.OpenURL, ":") + var iconHTML template.HTML + if schema == "vscode" || schema == "vscodium" || schema == "jetbrains" { + iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-open-with-%s", schema), 16, "gt-mr-3") + } else { + iconHTML = svg.RenderHTML("gitea-git", 16, "gt-mr-3") // TODO: it could support user's customized icon in the future + } + tmplApps = append(tmplApps, map[string]any{ + "DisplayName": app.DisplayName, + "OpenURL": app.OpenURL, + "IconHTML": iconHTML, + }) + } + ctx.Data["OpenWithEditorApps"] = tmplApps +} + +func renderHomeCode(ctx *context.Context) { ctx.Data["PageIsViewCode"] = true ctx.Data["RepositoryUploadEnabled"] = setting.Repository.Upload.Enabled + prepareOpenWithEditorApps(ctx) if ctx.Repo.Commit == nil || ctx.Repo.Repository.IsEmpty || ctx.Repo.Repository.IsBroken() { showEmpty := true @@ -996,6 +1022,8 @@ func renderCode(ctx *context.Context) { return } + checkOutdatedBranch(ctx) + checkCitationFile(ctx, entry) if ctx.Written() { return @@ -1071,7 +1099,7 @@ func renderCode(ctx *context.Context) { if err != nil { continue } - defaultBranch, err := gitRepo.GetDefaultBranch() + defaultBranch, err := gitrepo.GetDefaultBranch(ctx, repo) if err != nil { continue } @@ -1121,18 +1149,43 @@ PostRecentBranchCheck: ctx.HTML(http.StatusOK, tplRepoHome) } +func checkOutdatedBranch(ctx *context.Context) { + if !(ctx.Repo.IsAdmin() || ctx.Repo.IsOwner()) { + return + } + + // get the head commit of the branch since ctx.Repo.CommitID is not always the head commit of `ctx.Repo.BranchName` + commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.BranchName) + if err != nil { + log.Error("GetBranchCommitID: %v", err) + // Don't return an error page, as it can be rechecked the next time the user opens the page. + return + } + + dbBranch, err := git_model.GetBranch(ctx, ctx.Repo.Repository.ID, ctx.Repo.BranchName) + if err != nil { + log.Error("GetBranch: %v", err) + // Don't return an error page, as it can be rechecked the next time the user opens the page. + return + } + + if dbBranch.CommitID != commit.ID.String() { + ctx.Flash.Warning(ctx.Tr("repo.error.broken_git_hook", "https://docs.gitea.com/help/faq#push-hook--webhook--actions-arent-running"), true) + } +} + // RenderUserCards render a page show users according the input template func RenderUserCards(ctx *context.Context, total int, getter func(opts db.ListOptions) ([]*user_model.User, error), tpl base.TplName) { page := ctx.FormInt("page") if page <= 0 { page = 1 } - pager := context.NewPagination(total, setting.ItemsPerPage, page, 5) + pager := context.NewPagination(total, setting.MaxUserCardsPerPage, page, 5) ctx.Data["Page"] = pager items, err := getter(db.ListOptions{ Page: pager.Paginater.Current(), - PageSize: setting.ItemsPerPage, + PageSize: setting.MaxUserCardsPerPage, }) if err != nil { ctx.ServerError("getter", err) @@ -1173,12 +1226,12 @@ func Forks(ctx *context.Context) { page = 1 } - pager := context.NewPagination(ctx.Repo.Repository.NumForks, setting.ItemsPerPage, page, 5) + pager := context.NewPagination(ctx.Repo.Repository.NumForks, setting.MaxForksPerPage, page, 5) ctx.Data["Page"] = pager forks, err := repo_model.GetForks(ctx, ctx.Repo.Repository, db.ListOptions{ Page: pager.Paginater.Current(), - PageSize: setting.ItemsPerPage, + PageSize: setting.MaxForksPerPage, }) if err != nil { ctx.ServerError("GetForks", err) diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go index 49c83cfef5..719cca3049 100644 --- a/routers/web/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -79,7 +79,7 @@ func assertPagesMetas(t *testing.T, expectedNames []string, metas any) { func TestWiki(t *testing.T) { unittest.PrepareTestEnv(t) - ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki/?action=_pages") + ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki") ctx.SetParams("*", "Home") contexttest.LoadRepo(t, ctx, 1) Wiki(ctx) diff --git a/routers/web/shared/packages/packages.go b/routers/web/shared/packages/packages.go index 57671ad8f1..af960f1c0c 100644 --- a/routers/web/shared/packages/packages.go +++ b/routers/web/shared/packages/packages.go @@ -4,16 +4,19 @@ package packages import ( + "errors" "fmt" "net/http" "time" "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" + repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -29,6 +32,12 @@ func SetPackagesContext(ctx *context.Context, owner *user_model.User) { } ctx.Data["CleanupRules"] = pcrs + + ctx.Data["CargoIndexExists"], err = repo_model.IsRepositoryModelExist(ctx, owner, cargo_service.IndexRepositoryName) + if err != nil { + ctx.ServerError("IsRepositoryModelExist", err) + return + } } func SetRuleAddContext(ctx *context.Context) { @@ -240,7 +249,11 @@ func RebuildCargoIndex(ctx *context.Context, owner *user_model.User) { err := cargo_service.RebuildIndex(ctx, owner, owner) if err != nil { log.Error("RebuildIndex failed: %v", err) - ctx.Flash.Error(ctx.Tr("packages.owner.settings.cargo.rebuild.error", err)) + if errors.Is(err, util.ErrNotExist) { + ctx.Flash.Error(ctx.Tr("packages.owner.settings.cargo.rebuild.no_index")) + } else { + ctx.Flash.Error(ctx.Tr("packages.owner.settings.cargo.rebuild.error", err)) + } } else { ctx.Flash.Success(ctx.Tr("packages.owner.settings.cargo.rebuild.success")) } diff --git a/routers/web/user/code.go b/routers/web/user/code.go index eb711b76eb..d2afdd8905 100644 --- a/routers/web/user/code.go +++ b/routers/web/user/code.go @@ -6,6 +6,7 @@ package user import ( "net/http" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/base" code_indexer "code.gitea.io/gitea/modules/indexer/code" @@ -40,7 +41,7 @@ func CodeSearch(ctx *context.Context) { keyword := ctx.FormTrim("q") queryType := ctx.FormTrim("t") - isMatch := queryType == "match" + isFuzzy := queryType != "match" ctx.Data["Keyword"] = keyword ctx.Data["Language"] = language @@ -75,7 +76,16 @@ func CodeSearch(ctx *context.Context) { ) if len(repoIDs) > 0 { - total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) + total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, &code_indexer.SearchOptions{ + RepoIDs: repoIDs, + Keyword: keyword, + IsKeywordFuzzy: isFuzzy, + Language: language, + Paginator: &db.ListOptions{ + Page: page, + PageSize: setting.UI.RepoSearchPagingNum, + }, + }) if err != nil { if code_indexer.IsAvailable(ctx) { ctx.ServerError("SearchResults", err) diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 3ff4efc489..e58fb95131 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -743,7 +743,6 @@ func UsernameSubRoute(ctx *context.Context) { return } if reloadParam(".rss") { - context.UserAssignmentWeb()(ctx) feed.ShowUserFeedRSS(ctx) } case strings.HasSuffix(username, ".atom"): @@ -792,15 +791,15 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod case issues_model.FilterModeYourRepositories: openClosedOpts.AllPublic = false case issues_model.FilterModeAssign: - openClosedOpts.AssigneeID = &doerID + openClosedOpts.AssigneeID = optional.Some(doerID) case issues_model.FilterModeCreate: - openClosedOpts.PosterID = &doerID + openClosedOpts.PosterID = optional.Some(doerID) case issues_model.FilterModeMention: - openClosedOpts.MentionID = &doerID + openClosedOpts.MentionID = optional.Some(doerID) case issues_model.FilterModeReviewRequested: - openClosedOpts.ReviewRequestedID = &doerID + openClosedOpts.ReviewRequestedID = optional.Some(doerID) case issues_model.FilterModeReviewed: - openClosedOpts.ReviewedID = &doerID + openClosedOpts.ReviewedID = optional.Some(doerID) } openClosedOpts.IsClosed = optional.Some(false) ret.OpenCount, err = issue_indexer.CountIssues(ctx, openClosedOpts) @@ -818,23 +817,23 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod if err != nil { return nil, err } - ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = &doerID })) + ret.AssignCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AssigneeID = optional.Some(doerID) })) if err != nil { return nil, err } - ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = &doerID })) + ret.CreateCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.PosterID = optional.Some(doerID) })) if err != nil { return nil, err } - ret.MentionCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.MentionID = &doerID })) + ret.MentionCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.MentionID = optional.Some(doerID) })) if err != nil { return nil, err } - ret.ReviewRequestedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewRequestedID = &doerID })) + ret.ReviewRequestedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewRequestedID = optional.Some(doerID) })) if err != nil { return nil, err } - ret.ReviewedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewedID = &doerID })) + ret.ReviewedCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.ReviewedID = optional.Some(doerID) })) if err != nil { return nil, err } diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index ef02cd24a7..386309aa71 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -19,6 +19,8 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/auth" + "code.gitea.io/gitea/services/auth/source/db" + "code.gitea.io/gitea/services/auth/source/smtp" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" @@ -251,11 +253,24 @@ func DeleteAccount(ctx *context.Context) { ctx.Data["PageIsSettingsAccount"] = true if _, _, err := auth.UserSignIn(ctx, ctx.Doer.Name, ctx.FormString("password")); err != nil { - if user_model.IsErrUserNotExist(err) { + switch { + case user_model.IsErrUserNotExist(err): + loadAccountData(ctx) + + ctx.RenderWithErr(ctx.Tr("form.user_not_exist"), tplSettingsAccount, nil) + case errors.Is(err, smtp.ErrUnsupportedLoginType): + loadAccountData(ctx) + + ctx.RenderWithErr(ctx.Tr("form.unsupported_login_type"), tplSettingsAccount, nil) + case errors.As(err, &db.ErrUserPasswordNotSet{}): + loadAccountData(ctx) + + ctx.RenderWithErr(ctx.Tr("form.unset_password"), tplSettingsAccount, nil) + case errors.As(err, &db.ErrUserPasswordInvalid{}): loadAccountData(ctx) ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsAccount, nil) - } else { + default: ctx.ServerError("UserSignIn", err) } return diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go index cb01913bda..d2b60fc809 100644 --- a/routers/web/user/setting/keys.go +++ b/routers/web/user/setting/keys.go @@ -159,6 +159,11 @@ func KeysPost(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.verify_gpg_key_success", keyID)) ctx.Redirect(setting.AppSubURL + "/user/settings/keys") case "ssh": + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { + ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + return + } + content, err := asymkey_model.CheckPublicKeyString(form.Content) if err != nil { if db.IsErrSSHDisabled(err) { @@ -198,6 +203,11 @@ func KeysPost(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.add_key_success", form.Title)) ctx.Redirect(setting.AppSubURL + "/user/settings/keys") case "verify_ssh": + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { + ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + return + } + token := asymkey_model.VerificationToken(ctx.Doer, 1) lastToken := asymkey_model.VerificationToken(ctx.Doer, 0) @@ -240,6 +250,11 @@ func DeleteKey(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.gpg_key_deletion_success")) } case "ssh": + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureManageSSHKeys) { + ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + return + } + keyID := ctx.FormInt64("id") external, err := asymkey_model.PublicKeyIsExternallyManaged(ctx, keyID) if err != nil { @@ -318,4 +333,5 @@ func loadKeysData(ctx *context.Context) { ctx.Data["VerifyingID"] = ctx.FormString("verify_gpg") ctx.Data["VerifyingFingerprint"] = ctx.FormString("verify_ssh") + ctx.Data["UserDisabledFeatures"] = &setting.Admin.UserDisabledFeatures } diff --git a/routers/web/web.go b/routers/web/web.go index 114a50cf08..5cd7d112b0 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -691,6 +691,7 @@ func registerRoutes(m *web.Route) { m.Get("", admin.Config) m.Post("", admin.ChangeConfig) m.Post("/test_mail", admin.SendTestMail) + m.Get("/settings", admin.ConfigSettings) }) m.Group("/monitor", func() { @@ -1413,7 +1414,7 @@ func registerRoutes(m *web.Route) { }) m.Post("/cancel", reqRepoActionsWriter, actions.Cancel) m.Post("/approve", reqRepoActionsWriter, actions.Approve) - m.Post("/artifacts", actions.ArtifactsView) + m.Get("/artifacts", actions.ArtifactsView) m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) m.Delete("/artifacts/{artifact_name}", reqRepoActionsWriter, actions.ArtifactsDeleteView) m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) diff --git a/services/actions/auth.go b/services/actions/auth.go index e0f9a9015d..8e934d89a8 100644 --- a/services/actions/auth.go +++ b/services/actions/auth.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -21,17 +22,41 @@ type actionsClaims struct { TaskID int64 RunID int64 JobID int64 + Ac string `json:"ac"` } +type actionsCacheScope struct { + Scope string + Permission actionsCachePermission +} + +type actionsCachePermission int + +const ( + actionsCachePermissionRead = 1 << iota + actionsCachePermissionWrite +) + func CreateAuthorizationToken(taskID, runID, jobID int64) (string, error) { now := time.Now() + ac, err := json.Marshal(&[]actionsCacheScope{ + { + Scope: "", + Permission: actionsCachePermissionWrite, + }, + }) + if err != nil { + return "", err + } + claims := actionsClaims{ RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(24 * time.Hour)), NotBefore: jwt.NewNumericDate(now), }, Scp: fmt.Sprintf("Actions.Results:%d:%d", runID, jobID), + Ac: string(ac), TaskID: taskID, RunID: runID, JobID: jobID, diff --git a/services/actions/auth_test.go b/services/actions/auth_test.go index 1f62f17f52..f73ae8ae4c 100644 --- a/services/actions/auth_test.go +++ b/services/actions/auth_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" "github.com/golang-jwt/jwt/v5" @@ -29,6 +30,14 @@ func TestCreateAuthorizationToken(t *testing.T) { taskIDClaim, ok := claims["TaskID"] assert.True(t, ok, "Has TaskID claim in jwt token") assert.Equal(t, float64(taskID), taskIDClaim, "Supplied taskid must match stored one") + acClaim, ok := claims["ac"] + assert.True(t, ok, "Has ac claim in jwt token") + ac, ok := acClaim.(string) + assert.True(t, ok, "ac claim is a string for buildx gha cache") + scopes := []actionsCacheScope{} + err = json.Unmarshal([]byte(ac), &scopes) + assert.NoError(t, err, "ac claim is a json list for buildx gha cache") + assert.GreaterOrEqual(t, len(scopes), 1, "Expected at least one action cache scope for buildx gha cache") } func TestParseAuthorizationToken(t *testing.T) { diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 19a5c66b71..7c01226c95 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -154,7 +154,7 @@ func notify(ctx context.Context, input *notifyInput) error { return fmt.Errorf("gitRepo.GetCommit: %w", err) } - if skipWorkflowsForCommit(input, commit) { + if skipWorkflows(input, commit) { return nil } @@ -232,8 +232,8 @@ func SkipPullRequestEvent(ctx context.Context, event webhook_module.HookEventTyp return exist } -func skipWorkflowsForCommit(input *notifyInput, commit *git.Commit) bool { - // skip workflow runs with a configured skip-ci string in commit message if the event is push or pull_request(_sync) +func skipWorkflows(input *notifyInput, commit *git.Commit) bool { + // skip workflow runs with a configured skip-ci string in commit message or pr title if the event is push or pull_request(_sync) // https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs skipWorkflowEvents := []webhook_module.HookEventType{ webhook_module.HookEventPush, @@ -242,6 +242,10 @@ func skipWorkflowsForCommit(input *notifyInput, commit *git.Commit) bool { } if slices.Contains(skipWorkflowEvents, input.Event) { for _, s := range setting.Actions.SkipWorkflowStrings { + if input.PullRequest != nil && strings.Contains(input.PullRequest.Issue.Title, s) { + log.Debug("repo %s: skipped run for pr %v because of %s string", input.Repo.RepoPath(), input.PullRequest.Issue.ID, s) + return true + } if strings.Contains(commit.CommitMessage, s) { log.Debug("repo %s with commit %s: skipped run because of %s string", input.Repo.RepoPath(), commit.ID, s) return true @@ -305,7 +309,18 @@ func handleWorkflows( run.NeedApproval = need } - jobs, err := jobparser.Parse(dwf.Content) + if err := run.LoadAttributes(ctx); err != nil { + log.Error("LoadAttributes: %v", err) + continue + } + + vars, err := actions_model.GetVariablesOfRun(ctx, run) + if err != nil { + log.Error("GetVariablesOfRun: %v", err) + continue + } + + jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars)) if err != nil { log.Error("jobparser.Parse: %v", err) continue diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go index ac32647839..c3edae4ab6 100644 --- a/services/auth/source/oauth2/providers.go +++ b/services/auth/source/oauth2/providers.go @@ -59,7 +59,7 @@ func (p *AuthSourceProvider) DisplayName() string { func (p *AuthSourceProvider) IconHTML(size int) template.HTML { if p.iconURL != "" { - img := fmt.Sprintf(`%s`, + img := fmt.Sprintf(`%s`, size, size, html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()), diff --git a/services/context/base.go b/services/context/base.go index c4aa467ff4..25ff935055 100644 --- a/services/context/base.go +++ b/services/context/base.go @@ -256,7 +256,7 @@ func (b *Base) Redirect(location string, status ...int) { code = status[0] } - if strings.Contains(location, "://") || strings.HasPrefix(location, "//") { + if httplib.IsRiskyRedirectURL(location) { // Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path // 1. the first request to "/my-path" contains cookie // 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking) diff --git a/services/context/base_test.go b/services/context/base_test.go new file mode 100644 index 0000000000..823f20e00b --- /dev/null +++ b/services/context/base_test.go @@ -0,0 +1,47 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package context + +import ( + "net/http" + "net/http/httptest" + "testing" + + "code.gitea.io/gitea/modules/setting" + + "github.com/stretchr/testify/assert" +) + +func TestRedirect(t *testing.T) { + req, _ := http.NewRequest("GET", "/", nil) + + cases := []struct { + url string + keep bool + }{ + {"http://test", false}, + {"https://test", false}, + {"//test", false}, + {"/://test", true}, + {"/test", true}, + } + for _, c := range cases { + resp := httptest.NewRecorder() + b, cleanup := NewBaseContext(resp, req) + resp.Header().Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "dummy"}).String()) + b.Redirect(c.url) + cleanup() + has := resp.Header().Get("Set-Cookie") == "i_like_gitea=dummy" + assert.Equal(t, c.keep, has, "url = %q", c.url) + } + + req, _ = http.NewRequest("GET", "/", nil) + resp := httptest.NewRecorder() + req.Header.Add("HX-Request", "true") + b, cleanup := NewBaseContext(resp, req) + b.Redirect("/other") + cleanup() + assert.Equal(t, "/other", resp.Header().Get("HX-Redirect")) + assert.Equal(t, http.StatusNoContent, resp.Code) +} diff --git a/services/context/context.go b/services/context/context.go index a06ebfb0dc..3e113e76ba 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -192,6 +192,7 @@ func Contexter() func(next http.Handler) http.Handler { httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform") ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) + ctx.Data["SystemConfig"] = setting.Config() ctx.Data["CsrfToken"] = ctx.Csrf.GetToken() ctx.Data["CsrfTokenHtml"] = template.HTML(``) diff --git a/services/context/repo.go b/services/context/repo.go index e78dfc585d..43eeab8098 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -702,7 +702,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) { ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch } else { - ctx.Repo.BranchName, _ = gitRepo.GetDefaultBranch() + ctx.Repo.BranchName, _ = gitrepo.GetDefaultBranch(ctx, ctx.Repo.Repository) if ctx.Repo.BranchName == "" { // If it still can't get a default branch, fall back to default branch from setting. // Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug. diff --git a/services/contexttest/context_tests.go b/services/contexttest/context_tests.go index 431017a30d..d3e6de7efe 100644 --- a/services/contexttest/context_tests.go +++ b/services/contexttest/context_tests.go @@ -7,6 +7,7 @@ package contexttest import ( gocontext "context" "io" + "maps" "net/http" "net/http/httptest" "net/url" @@ -36,7 +37,7 @@ func mockRequest(t *testing.T, reqPath string) *http.Request { } requestURL, err := url.Parse(path) assert.NoError(t, err) - req := &http.Request{Method: method, URL: requestURL, Form: url.Values{}} + req := &http.Request{Method: method, URL: requestURL, Form: maps.Clone(requestURL.Query()), Header: http.Header{}} req = req.WithContext(middleware.WithContextData(req.Context())) return req } diff --git a/services/forms/user_form.go b/services/forms/user_form.go index 77316fb13a..0f3cd0ceec 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -10,9 +10,9 @@ import ( "strings" auth_model "code.gitea.io/gitea/models/auth" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/services/context" @@ -109,11 +109,7 @@ func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding. // domains in the whitelist or if it doesn't match any of // domains in the blocklist, if any such list is not empty. func (f *RegisterForm) IsEmailDomainAllowed() bool { - if len(setting.Service.EmailDomainAllowList) == 0 { - return !validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, f.Email) - } - - return validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, f.Email) + return user_model.IsEmailDomainAllowed(f.Email) } // MustChangePasswordForm form for updating your password after account creation diff --git a/services/mailer/mail.go b/services/mailer/mail.go index f72ae45f89..26aebd2583 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -222,7 +222,8 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient body, err := markdown.RenderString(&markup.RenderContext{ Ctx: ctx, Links: markup.Links{ - Base: ctx.Issue.Repo.HTMLURL(), + AbsolutePrefix: true, + Base: ctx.Issue.Repo.HTMLURL(), }, Metas: ctx.Issue.Repo.ComposeMetas(ctx), }, ctx.Content) diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index e300aeccb0..d87c57ffe7 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -8,6 +8,8 @@ import ( "context" "fmt" "html/template" + "io" + "mime/quotedprintable" "regexp" "strings" "testing" @@ -19,6 +21,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -67,6 +70,12 @@ func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *repo_model.Re func TestComposeIssueCommentMessage(t *testing.T) { doer, _, issue, comment := prepareMailerTest(t) + markup.Init(&markup.ProcessorHelper{ + IsUsernameMentionable: func(ctx context.Context, username string) bool { + return username == doer.Name + }, + }) + setting.IncomingEmail.Enabled = true defer func() { setting.IncomingEmail.Enabled = false }() @@ -77,7 +86,8 @@ func TestComposeIssueCommentMessage(t *testing.T) { msgs, err := composeIssueCommentMessages(&mailCommentContext{ Context: context.TODO(), // TODO: use a correct context Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue, - Content: "test body", Comment: comment, + Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index), + Comment: comment, }, "en-US", recipients, false, "issue comment") assert.NoError(t, err) assert.Len(t, msgs, 2) @@ -96,6 +106,20 @@ func TestComposeIssueCommentMessage(t *testing.T) { assert.Equal(t, "", gomailMsg.GetHeader("Message-ID")[0], "Message-ID header doesn't match") assert.Equal(t, "", gomailMsg.GetHeader("List-Post")[0]) assert.Len(t, gomailMsg.GetHeader("List-Unsubscribe"), 2) // url + mailto + + var buf bytes.Buffer + gomailMsg.WriteTo(&buf) + + b, err := io.ReadAll(quotedprintable.NewReader(&buf)) + assert.NoError(t, err) + + // text/plain + assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, doer.HTMLURL())) + assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, issue.HTMLURL())) + + // text/html + assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, doer.HTMLURL())) + assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, issue.HTMLURL())) } func TestComposeIssueMessage(t *testing.T) { diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_gitea%2Ftest_repo b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026 similarity index 94% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_gitea%2Ftest_repo rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026 index 490eebe791..81fb1f9e01 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_gitea%2Ftest_repo +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026 @@ -1,17 +1,17 @@ -X-Frame-Options: SAMEORIGIN -X-Gitlab-Meta: {"correlation_id":"d08798c744b97af051e81bc1f6fecabe","version":"1"} -Cf-Cache-Status: MISS -Vary: Origin, Accept-Encoding -X-Runtime: 0.282076 -Gitlab-Lb: haproxy-main-43-lb-gprd -Etag: W/"8db4917b3be5f4ca0d101a702179b75a" -Referrer-Policy: strict-origin-when-cross-origin -Set-Cookie: _cfuvid=q8qrvxAlkuGzqRsYQXzKrbLTHH6CWOF_x.icF64i9VQ-1709516921514-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Content-Security-Policy: default-src 'none' -Cache-Control: max-age=0, private, must-revalidate -X-Content-Type-Options: nosniff -Strict-Transport-Security: max-age=31536000 Gitlab-Sv: api-gke-us-east1-c Content-Type: application/json +Cache-Control: max-age=0, private, must-revalidate +Content-Security-Policy: default-src 'none' +Etag: W/"8db4917b3be5f4ca0d101a702179b75a" +X-Content-Type-Options: nosniff +X-Runtime: 0.150020 +Referrer-Policy: strict-origin-when-cross-origin +Set-Cookie: _cfuvid=2JDVzeRhKxkwd0xbLccErO2vFlf0KnUzsvPv1ZY4.H4-1710504205506-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +X-Gitlab-Meta: {"correlation_id":"fc467ca540c06233f6a25d0deae604d2","version":"1"} +Vary: Origin, Accept-Encoding +X-Frame-Options: SAMEORIGIN +Strict-Transport-Security: max-age=31536000 +Gitlab-Lb: haproxy-main-01-lb-gprd +Cf-Cache-Status: MISS {"id":15578026,"description":"Test repository for testing migration from gitlab to gitea","name":"test_repo","name_with_namespace":"gitea / test_repo","path":"test_repo","path_with_namespace":"gitea/test_repo","created_at":"2019-11-28T08:20:33.019Z","default_branch":"master","tag_list":["migration","test"],"topics":["migration","test"],"ssh_url_to_repo":"git@gitlab.com:gitea/test_repo.git","http_url_to_repo":"https://gitlab.com/gitea/test_repo.git","web_url":"https://gitlab.com/gitea/test_repo","readme_url":"https://gitlab.com/gitea/test_repo/-/blob/master/README.md","forks_count":1,"avatar_url":null,"star_count":0,"last_activity_at":"2020-04-19T19:46:04.527Z","namespace":{"id":3181312,"name":"gitea","path":"gitea","kind":"group","full_path":"gitea","parent_id":null,"avatar_url":"/uploads/-/system/group/avatar/3181312/gitea.png","web_url":"https://gitlab.com/groups/gitea"},"container_registry_image_prefix":"registry.gitlab.com/gitea/test_repo","_links":{"self":"https://gitlab.com/api/v4/projects/15578026","issues":"https://gitlab.com/api/v4/projects/15578026/issues","merge_requests":"https://gitlab.com/api/v4/projects/15578026/merge_requests","repo_branches":"https://gitlab.com/api/v4/projects/15578026/repository/branches","labels":"https://gitlab.com/api/v4/projects/15578026/labels","events":"https://gitlab.com/api/v4/projects/15578026/events","members":"https://gitlab.com/api/v4/projects/15578026/members","cluster_agents":"https://gitlab.com/api/v4/projects/15578026/cluster_agents"},"packages_enabled":true,"empty_repo":false,"archived":false,"visibility":"public","resolve_outdated_diff_discussions":false,"repository_object_format":"sha1","issues_enabled":true,"merge_requests_enabled":true,"wiki_enabled":true,"jobs_enabled":true,"snippets_enabled":true,"container_registry_enabled":true,"service_desk_enabled":true,"can_create_merge_request_in":true,"issues_access_level":"enabled","repository_access_level":"enabled","merge_requests_access_level":"enabled","forking_access_level":"enabled","wiki_access_level":"enabled","builds_access_level":"enabled","snippets_access_level":"enabled","pages_access_level":"enabled","analytics_access_level":"enabled","container_registry_access_level":"enabled","security_and_compliance_access_level":"private","releases_access_level":"enabled","environments_access_level":"enabled","feature_flags_access_level":"enabled","infrastructure_access_level":"enabled","monitor_access_level":"enabled","model_experiments_access_level":"enabled","model_registry_access_level":"enabled","emails_disabled":false,"emails_enabled":true,"shared_runners_enabled":true,"lfs_enabled":true,"creator_id":1241334,"import_status":"none","open_issues_count":0,"description_html":"\u003cp data-sourcepos=\"1:1-1:58\" dir=\"auto\"\u003eTest repository for testing migration from gitlab to gitea\u003c/p\u003e","updated_at":"2024-01-11T01:23:21.057Z","ci_config_path":null,"public_jobs":true,"shared_with_groups":[],"only_allow_merge_if_pipeline_succeeds":false,"allow_merge_on_skipped_pipeline":null,"request_access_enabled":true,"only_allow_merge_if_all_discussions_are_resolved":false,"remove_source_branch_after_merge":true,"printing_merge_request_link_enabled":true,"merge_method":"ff","squash_option":"default_off","enforce_auth_checks_on_uploads":true,"suggestion_commit_message":null,"merge_commit_template":null,"squash_commit_template":null,"issue_branch_template":null,"warn_about_potentially_unwanted_characters":true,"autoclose_referenced_issues":true,"external_authorization_classification_label":"","requirements_enabled":false,"requirements_access_level":"enabled","security_and_compliance_enabled":false,"compliance_frameworks":[],"permissions":{"project_access":null,"group_access":null}} \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_1_award_emoji!page=1&per_page=2 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F1%2Faward_emoji%3Fpage=1&per_page=2 similarity index 85% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_1_award_emoji!page=1&per_page=2 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F1%2Faward_emoji%3Fpage=1&per_page=2 index 7b37f59f5e..cbdfdde527 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_1_award_emoji!page=1&per_page=2 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F1%2Faward_emoji%3Fpage=1&per_page=2 @@ -1,24 +1,24 @@ -X-Total-Pages: 1 -Cf-Cache-Status: MISS -Set-Cookie: _cfuvid=UtNiLvpRoIS0606tZnebN.nDbGog2IEOTI0M6iH6tKE-1709516925325-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Content-Type: application/json -Link: ; rel="first", ; rel="last" -X-Gitlab-Meta: {"correlation_id":"7078187b1af666261210081509905846","version":"1"} -Strict-Transport-Security: max-age=31536000 Vary: Origin, Accept-Encoding -X-Total: 2 Gitlab-Sv: api-gke-us-east1-b -Content-Security-Policy: default-src 'none' -X-Content-Type-Options: nosniff -X-Page: 1 -X-Frame-Options: SAMEORIGIN -X-Prev-Page: -X-Runtime: 0.070454 -X-Next-Page: +X-Runtime: 0.203565 Referrer-Policy: strict-origin-when-cross-origin -Gitlab-Lb: haproxy-main-51-lb-gprd -X-Per-Page: 2 +X-Frame-Options: SAMEORIGIN +X-Next-Page: +X-Gitlab-Meta: {"correlation_id":"9ee8f715be2950b629eff875667dab37","version":"1"} +X-Total-Pages: 1 +Gitlab-Lb: haproxy-main-57-lb-gprd +Cf-Cache-Status: MISS +Content-Type: application/json Cache-Control: max-age=0, private, must-revalidate +X-Content-Type-Options: nosniff +Strict-Transport-Security: max-age=31536000 Etag: W/"69c922434ed11248c864d157eb8eabfc" +X-Per-Page: 2 +X-Prev-Page: +Set-Cookie: _cfuvid=lj07r.PfLt5YP9_Ms5dtsY_JOkTSmeFWB1sd2Z8SLuM-1710504207278-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Content-Security-Policy: default-src 'none' +Link: ; rel="first", ; rel="last" +X-Page: 1 +X-Total: 2 [{"id":3009580,"name":"thumbsup","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:43:40.322Z","updated_at":"2019-11-28T08:43:40.322Z","awardable_id":27687675,"awardable_type":"Issue","url":null},{"id":3009585,"name":"open_mouth","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:44:01.902Z","updated_at":"2019-11-28T08:44:01.902Z","awardable_id":27687675,"awardable_type":"Issue","url":null}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_1_award_emoji!page=2&per_page=2 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F1%2Faward_emoji%3Fpage=2&per_page=2 similarity index 75% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_1_award_emoji!page=2&per_page=2 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F1%2Faward_emoji%3Fpage=2&per_page=2 index 7f64231eb7..262bf891ee 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_1_award_emoji!page=2&per_page=2 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F1%2Faward_emoji%3Fpage=2&per_page=2 @@ -1,26 +1,26 @@ -Content-Length: 2 -Cache-Control: max-age=0, private, must-revalidate -Accept-Ranges: bytes -Strict-Transport-Security: max-age=31536000 -Gitlab-Lb: haproxy-main-57-lb-gprd -Gitlab-Sv: api-gke-us-east1-b -X-Prev-Page: -X-Runtime: 0.048686 -Cf-Cache-Status: MISS -Vary: Origin, Accept-Encoding -X-Per-Page: 2 -Referrer-Policy: strict-origin-when-cross-origin -X-Gitlab-Meta: {"correlation_id":"53764b779171a3a641999caf45eb5cda","version":"1"} -X-Page: 2 -X-Total: 2 -X-Total-Pages: 1 -Content-Type: application/json -X-Next-Page: -Set-Cookie: _cfuvid=.aWihkIt1YOKnLoix6oJ3avCXZ_942rAkWJpHXKtKXk-1709516926358-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Content-Security-Policy: default-src 'none' -X-Content-Type-Options: nosniff -X-Frame-Options: SAMEORIGIN -Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" Link: ; rel="first", ; rel="last" +X-Content-Type-Options: nosniff +X-Page: 2 +X-Per-Page: 2 +Gitlab-Sv: api-gke-us-east1-b +Content-Type: application/json +Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" +X-Frame-Options: SAMEORIGIN +X-Gitlab-Meta: {"correlation_id":"05db2c172a3be5ea9e65494882e77167","version":"1"} +X-Next-Page: +Set-Cookie: _cfuvid=UDvTcjnLBRvcY_axm9MwnCJ0PmPtOKE9vnIQ4uoOUGE-1710504207498-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +X-Runtime: 0.061429 +X-Total-Pages: 1 +Strict-Transport-Security: max-age=31536000 +Gitlab-Lb: haproxy-main-51-lb-gprd +Accept-Ranges: bytes +Content-Length: 2 +Cf-Cache-Status: MISS +Cache-Control: max-age=0, private, must-revalidate +Content-Security-Policy: default-src 'none' +Vary: Origin, Accept-Encoding +X-Prev-Page: +X-Total: 2 +Referrer-Policy: strict-origin-when-cross-origin [] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=1&per_page=2 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=1&per_page=2 similarity index 84% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=1&per_page=2 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=1&per_page=2 index db1573c43c..52822db98b 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=1&per_page=2 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=1&per_page=2 @@ -1,24 +1,24 @@ +X-Frame-Options: SAMEORIGIN +Referrer-Policy: strict-origin-when-cross-origin +Gitlab-Lb: haproxy-main-40-lb-gprd +Content-Type: application/json +Strict-Transport-Security: max-age=31536000 Vary: Origin, Accept-Encoding -Set-Cookie: _cfuvid=eR1OqXm9Zq42ps0oFIf0s4qBdC4lKtotvp9jBqOVcko-1709516927407-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Content-Security-Policy: default-src 'none' +X-Page: 1 +X-Runtime: 0.078016 +Link: ; rel="next", ; rel="first", ; rel="last" +X-Prev-Page: +X-Total: 6 +X-Total-Pages: 3 X-Content-Type-Options: nosniff X-Next-Page: 2 -X-Page: 1 -X-Prev-Page: -X-Total-Pages: 3 -Link: ; rel="next", ; rel="first", ; rel="last" -Referrer-Policy: strict-origin-when-cross-origin -X-Total: 6 +Gitlab-Sv: api-gke-us-east1-c Cache-Control: max-age=0, private, must-revalidate -X-Per-Page: 2 -Gitlab-Sv: api-gke-us-east1-d -X-Gitlab-Meta: {"correlation_id":"05328c4fe2465085c0b9a5a5a1103836","version":"1"} -X-Runtime: 0.075509 -Strict-Transport-Security: max-age=31536000 -Gitlab-Lb: haproxy-main-50-lb-gprd -Content-Type: application/json -Etag: W/"5fdbcbf64f34ba0e74ce9dd8d6e0efe3" -X-Frame-Options: SAMEORIGIN Cf-Cache-Status: MISS +Set-Cookie: _cfuvid=YByIjysnuUyVymulLPR72WWURJsjsdM2aiUwKWAGtZI-1710504207733-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Content-Security-Policy: default-src 'none' +Etag: W/"5fdbcbf64f34ba0e74ce9dd8d6e0efe3" +X-Gitlab-Meta: {"correlation_id":"2c82a2ec8ad8bdd3c0d2adb0e208f69a","version":"1"} +X-Per-Page: 2 [{"id":3009627,"name":"thumbsup","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:46:42.657Z","updated_at":"2019-11-28T08:46:42.657Z","awardable_id":27687706,"awardable_type":"Issue","url":null},{"id":3009628,"name":"thumbsdown","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:46:43.471Z","updated_at":"2019-11-28T08:46:43.471Z","awardable_id":27687706,"awardable_type":"Issue","url":null}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=2&per_page=2 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=2&per_page=2 similarity index 85% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=2&per_page=2 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=2&per_page=2 index d52e358e91..2ebb34db88 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=2&per_page=2 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=2&per_page=2 @@ -1,24 +1,24 @@ -X-Content-Type-Options: nosniff -X-Runtime: 0.091658 -Set-Cookie: _cfuvid=aVGYOkTTE5ngoJripH93fZ9JhYhafbwSPqzjRZ7JCyk-1709516927910-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Referrer-Policy: strict-origin-when-cross-origin -X-Frame-Options: SAMEORIGIN -X-Gitlab-Meta: {"correlation_id":"b1d1356c71e3f52fc9bccd80a711467f","version":"1"} +X-Gitlab-Meta: {"correlation_id":"3f684de509b846cafc057f0e2982ad76","version":"1"} X-Prev-Page: 1 +X-Total-Pages: 3 +Gitlab-Lb: haproxy-main-24-lb-gprd +Gitlab-Sv: api-gke-us-east1-b +Vary: Origin, Accept-Encoding +Set-Cookie: _cfuvid=Bs.X45qZvylPDZxkoXQ0YQS72rXFkViMP2IaqBS6C0s-1710504207991-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Etag: W/"d16c513b32212d9286fce6f53340c1cf" +X-Content-Type-Options: nosniff +Cache-Control: max-age=0, private, must-revalidate +X-Next-Page: 3 Strict-Transport-Security: max-age=31536000 -Gitlab-Sv: api-gke-us-east1-c +Referrer-Policy: strict-origin-when-cross-origin Content-Type: application/json Content-Security-Policy: default-src 'none' -Vary: Origin, Accept-Encoding +X-Frame-Options: SAMEORIGIN +X-Runtime: 0.098833 +Cf-Cache-Status: MISS +Link: ; rel="prev", ; rel="next", ; rel="first", ; rel="last" X-Page: 2 X-Per-Page: 2 -Cf-Cache-Status: MISS X-Total: 6 -Gitlab-Lb: haproxy-main-13-lb-gprd -Etag: W/"d16c513b32212d9286fce6f53340c1cf" -X-Next-Page: 3 -X-Total-Pages: 3 -Cache-Control: max-age=0, private, must-revalidate -Link: ; rel="prev", ; rel="next", ; rel="first", ; rel="last" [{"id":3009632,"name":"laughing","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:47:14.381Z","updated_at":"2019-11-28T08:47:14.381Z","awardable_id":27687706,"awardable_type":"Issue","url":null},{"id":3009634,"name":"tada","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:47:18.254Z","updated_at":"2019-11-28T08:47:18.254Z","awardable_id":27687706,"awardable_type":"Issue","url":null}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=3&per_page=2 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=3&per_page=2 similarity index 84% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=3&per_page=2 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=3&per_page=2 index e35aa62cdf..23da417c01 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=3&per_page=2 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=3&per_page=2 @@ -1,24 +1,24 @@ -Gitlab-Lb: haproxy-main-13-lb-gprd -Set-Cookie: _cfuvid=t4_LSY2Wo_P8jPVZKgbX7DhNDlsiazGyv6awHkbP29M-1709516929175-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Cache-Control: max-age=0, private, must-revalidate -Vary: Origin, Accept-Encoding -Referrer-Policy: strict-origin-when-cross-origin -Cf-Cache-Status: MISS -X-Total: 6 X-Total-Pages: 3 -Etag: W/"165d37bf09a54bb31f4619cca8722cb4" -Link: ; rel="prev", ; rel="first", ; rel="last" -Gitlab-Sv: api-gke-us-east1-c -X-Runtime: 0.084688 -Strict-Transport-Security: max-age=31536000 -Content-Type: application/json -X-Content-Type-Options: nosniff -X-Prev-Page: 2 -X-Next-Page: -X-Page: 3 -X-Frame-Options: SAMEORIGIN -X-Gitlab-Meta: {"correlation_id":"3d4b896e9b25fba2880f8cf4179b4db3","version":"1"} Content-Security-Policy: default-src 'none' +Vary: Origin, Accept-Encoding +X-Page: 3 +Strict-Transport-Security: max-age=31536000 +Etag: W/"165d37bf09a54bb31f4619cca8722cb4" +X-Next-Page: +X-Frame-Options: SAMEORIGIN +X-Prev-Page: 2 +Cf-Cache-Status: MISS +Set-Cookie: _cfuvid=HHUVNinfPq8fL7PXFgbDm8yTm6pwWCXctd6JjWwfzY4-1710504208221-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +X-Runtime: 0.071448 +X-Total: 6 +Cache-Control: max-age=0, private, must-revalidate +X-Gitlab-Meta: {"correlation_id":"886dbf65fe0de14ba39622416ae0ca1b","version":"1"} +Referrer-Policy: strict-origin-when-cross-origin +Link: ; rel="prev", ; rel="first", ; rel="last" +X-Content-Type-Options: nosniff +Content-Type: application/json +Gitlab-Lb: haproxy-main-50-lb-gprd +Gitlab-Sv: api-gke-us-east1-d X-Per-Page: 2 [{"id":3009636,"name":"confused","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:47:27.248Z","updated_at":"2019-11-28T08:47:27.248Z","awardable_id":27687706,"awardable_type":"Issue","url":null},{"id":3009640,"name":"hearts","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:47:33.059Z","updated_at":"2019-11-28T08:47:33.059Z","awardable_id":27687706,"awardable_type":"Issue","url":null}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=4&per_page=2 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=4&per_page=2 similarity index 75% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=4&per_page=2 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=4&per_page=2 index 2c757e2034..086cfcd3b5 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_award_emoji!page=4&per_page=2 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Faward_emoji%3Fpage=4&per_page=2 @@ -1,26 +1,26 @@ -Vary: Origin, Accept-Encoding -Content-Length: 2 -Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" -X-Next-Page: -X-Page: 4 -Strict-Transport-Security: max-age=31536000 -Gitlab-Lb: haproxy-main-40-lb-gprd -Set-Cookie: _cfuvid=V6zFYEypjQbDjYSsP9fTmKa2nKJP5kvVJDoSbH7rU34-1709516930295-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Content-Type: application/json -X-Gitlab-Meta: {"correlation_id":"58338440ce0dd25dea95208140617efc","version":"1"} X-Total-Pages: 3 -Cf-Cache-Status: MISS -Cache-Control: max-age=0, private, must-revalidate +Gitlab-Lb: haproxy-main-52-lb-gprd +X-Next-Page: +Content-Security-Policy: default-src 'none' +Content-Type: application/json +Content-Length: 2 +X-Content-Type-Options: nosniff +X-Per-Page: 2 +X-Runtime: 0.083061 +Referrer-Policy: strict-origin-when-cross-origin +X-Gitlab-Meta: {"correlation_id":"561369f96102c1a6ab8acd558d16a4d0","version":"1"} +X-Prev-Page: +Vary: Origin, Accept-Encoding X-Frame-Options: SAMEORIGIN X-Total: 6 -X-Prev-Page: -Accept-Ranges: bytes -X-Content-Type-Options: nosniff -X-Runtime: 0.052295 -Referrer-Policy: strict-origin-when-cross-origin -Gitlab-Sv: api-gke-us-east1-c -Content-Security-Policy: default-src 'none' -X-Per-Page: 2 +Cache-Control: max-age=0, private, must-revalidate Link: ; rel="first", ; rel="last" +Gitlab-Sv: api-gke-us-east1-c +Accept-Ranges: bytes +Strict-Transport-Security: max-age=31536000 +Cf-Cache-Status: MISS +Set-Cookie: _cfuvid=GkrvFxTx5xbwrDM0Jz5hSAycmMJcwb02y6n04i5gv2s-1710504208464-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" +X-Page: 4 [] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_discussions!page=1&per_page=100 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Fdiscussions%3Fpage=1&per_page=100 similarity index 93% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_discussions!page=1&per_page=100 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Fdiscussions%3Fpage=1&per_page=100 index 2dc290a405..6b62a85016 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_discussions!page=1&per_page=100 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Fdiscussions%3Fpage=1&per_page=100 @@ -1,24 +1,24 @@ -X-Gitlab-Meta: {"correlation_id":"f7ea96beef459528c06bcf21a37f8950","version":"1"} -X-Per-Page: 100 -X-Runtime: 0.294630 -Cache-Control: max-age=0, private, must-revalidate -Gitlab-Sv: gke-cny-api -X-Frame-Options: SAMEORIGIN -X-Next-Page: -Etag: W/"bcc91e8a7b2eac98b4d96ae791e0649d" -X-Total: 4 -X-Page: 1 -Referrer-Policy: strict-origin-when-cross-origin -Gitlab-Lb: haproxy-main-32-lb-gprd Strict-Transport-Security: max-age=31536000 -Set-Cookie: _cfuvid=bVDOhVwoTt.rATMQeCGhPoIH4lvwoCZQqEHaWL0EOVY-1709516931074-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Link: ; rel="first", ; rel="last" -X-Content-Type-Options: nosniff -X-Prev-Page: +Gitlab-Lb: haproxy-main-32-lb-gprd Content-Type: application/json -Cf-Cache-Status: MISS -Content-Security-Policy: default-src 'none' -Vary: Origin, Accept-Encoding +Link: ; rel="first", ; rel="last" +Set-Cookie: _cfuvid=CZZEZqJQZ97MpqkqjenLKOUdtc5tMbwPjVBKat9VrFo-1710504208832-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Cache-Control: max-age=0, private, must-revalidate +Referrer-Policy: strict-origin-when-cross-origin +Gitlab-Sv: gke-cny-api X-Total-Pages: 1 +X-Page: 1 +X-Per-Page: 100 +X-Total: 4 +X-Content-Type-Options: nosniff +X-Runtime: 0.215193 +X-Frame-Options: SAMEORIGIN +Vary: Origin, Accept-Encoding +X-Gitlab-Meta: {"correlation_id":"d84b389e2f5604d766104c7236dbfdf8","version":"1"} +X-Next-Page: +Content-Security-Policy: default-src 'none' +Etag: W/"bcc91e8a7b2eac98b4d96ae791e0649d" +X-Prev-Page: +Cf-Cache-Status: MISS [{"id":"617967369d98d8b73b6105a40318fe839f931a24","individual_note":true,"notes":[{"id":251637434,"type":null,"body":"This is a comment","attachment":null,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:44:52.501Z","updated_at":"2019-11-28T08:44:52.501Z","system":false,"noteable_id":27687706,"noteable_type":"Issue","project_id":15578026,"resolvable":false,"confidential":false,"internal":false,"noteable_iid":2,"commands_changes":{}}]},{"id":"b92d74daee411a17d844041bcd3c267ade58f680","individual_note":true,"notes":[{"id":251637528,"type":null,"body":"changed milestone to %2","attachment":null,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:45:02.329Z","updated_at":"2019-11-28T08:45:02.335Z","system":true,"noteable_id":27687706,"noteable_type":"Issue","project_id":15578026,"resolvable":false,"confidential":false,"internal":false,"noteable_iid":2,"commands_changes":{}}]},{"id":"6010f567d2b58758ef618070372c97891ac75349","individual_note":true,"notes":[{"id":251637892,"type":null,"body":"closed","attachment":null,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:45:45.007Z","updated_at":"2019-11-28T08:45:45.010Z","system":true,"noteable_id":27687706,"noteable_type":"Issue","project_id":15578026,"resolvable":false,"confidential":false,"internal":false,"noteable_iid":2,"commands_changes":{}}]},{"id":"632d0cbfd6a1a08f38aaf9ef7715116f4b188ebb","individual_note":true,"notes":[{"id":251637999,"type":null,"body":"A second comment","attachment":null,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:45:53.501Z","updated_at":"2019-11-28T08:45:53.501Z","system":false,"noteable_id":27687706,"noteable_type":"Issue","project_id":15578026,"resolvable":false,"confidential":false,"internal":false,"noteable_iid":2,"commands_changes":{}}]}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_resource_state_events!page=1&per_page=100 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Fresource_state_events%3Fpage=1&per_page=100 similarity index 73% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_resource_state_events!page=1&per_page=100 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Fresource_state_events%3Fpage=1&per_page=100 index a67d0c45b7..33dce623cf 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues_2_resource_state_events!page=1&per_page=100 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%2F2%2Fresource_state_events%3Fpage=1&per_page=100 @@ -1,26 +1,26 @@ -Cf-Cache-Status: MISS -Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" -X-Content-Type-Options: nosniff -Referrer-Policy: strict-origin-when-cross-origin -Gitlab-Lb: haproxy-main-52-lb-gprd -Content-Length: 2 -X-Prev-Page: -Set-Cookie: _cfuvid=W2TdoA.hvJeMOjx.ZmMBWpZVvCP6nNxmeM6PQsr8.Kw-1709516931498-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Link: ; rel="first", ; rel="last" -X-Frame-Options: SAMEORIGIN -Strict-Transport-Security: max-age=31536000 -Vary: Origin, Accept-Encoding -Content-Security-Policy: default-src 'none' -X-Gitlab-Meta: {"correlation_id":"527e08a612a163b94cd95046842d5e64","version":"1"} -X-Page: 1 +Cache-Control: max-age=0, private, must-revalidate +X-Gitlab-Meta: {"correlation_id":"9564d6f62bbab2cb1f60ca66015e3840","version":"1"} +X-Runtime: 0.091327 X-Per-Page: 100 X-Total: 0 -Content-Type: application/json -Accept-Ranges: bytes -Cache-Control: max-age=0, private, must-revalidate -X-Next-Page: X-Total-Pages: 1 -Gitlab-Sv: api-gke-us-east1-c -X-Runtime: 0.123529 +Gitlab-Sv: api-gke-us-east1-d +Cf-Cache-Status: MISS +X-Prev-Page: +Content-Length: 2 +Content-Security-Policy: default-src 'none' +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Next-Page: +Link: ; rel="first", ; rel="last" +Vary: Origin, Accept-Encoding +Set-Cookie: _cfuvid=JAECWgzRO1L40L3GhX4c7HSSpyYna2z1sybaZdKrJ18-1710504209104-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Strict-Transport-Security: max-age=31536000 +Referrer-Policy: strict-origin-when-cross-origin +Gitlab-Lb: haproxy-main-11-lb-gprd +Content-Type: application/json +Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" +X-Page: 1 +Accept-Ranges: bytes [] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues!page=1&per_page=2&sort=asc&state=all b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%3Fpage=1&per_page=2&sort=asc&state=all similarity index 94% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues!page=1&per_page=2&sort=asc&state=all rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%3Fpage=1&per_page=2&sort=asc&state=all index 17139efa29..ff6128e8ac 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_issues!page=1&per_page=2&sort=asc&state=all +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fissues%3Fpage=1&per_page=2&sort=asc&state=all @@ -1,24 +1,24 @@ +Content-Security-Policy: default-src 'none' +Etag: W/"4c0531a3595f741f229f5a105e013b95" +Link: ; rel="first", ; rel="last" +X-Next-Page: +Cf-Cache-Status: MISS +Strict-Transport-Security: max-age=31536000 X-Content-Type-Options: nosniff -X-Per-Page: 2 -X-Total-Pages: 1 -Content-Type: application/json -X-Prev-Page: X-Total: 2 +X-Total-Pages: 1 +Gitlab-Lb: haproxy-main-16-lb-gprd Cache-Control: max-age=0, private, must-revalidate Vary: Origin, Accept-Encoding -Referrer-Policy: strict-origin-when-cross-origin -Gitlab-Sv: api-gke-us-east1-d -X-Gitlab-Meta: {"correlation_id":"52846fcab419461a2c2f29f707dbd103","version":"1"} -X-Next-Page: -Strict-Transport-Security: max-age=31536000 -Cf-Cache-Status: MISS -Link: ; rel="first", ; rel="last" -Etag: W/"4c0531a3595f741f229f5a105e013b95" -X-Frame-Options: SAMEORIGIN +X-Runtime: 0.143514 +Gitlab-Sv: api-gke-us-east1-c +X-Gitlab-Meta: {"correlation_id":"6e8b9d619f3148fd839ba0c5f6747df9","version":"1"} X-Page: 1 -Content-Security-Policy: default-src 'none' -Set-Cookie: _cfuvid=0ScIwDvsVw_dRSgtT9czuTLY6R5TGzB2UIk9653BEf0-1709516924974-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Gitlab-Lb: haproxy-main-38-lb-gprd -X-Runtime: 0.187797 +Content-Type: application/json +X-Per-Page: 2 +Referrer-Policy: strict-origin-when-cross-origin +X-Frame-Options: SAMEORIGIN +X-Prev-Page: +Set-Cookie: _cfuvid=9pOTnEAVQzMgmNMabGvRXD3ad16MkUCZTAQQameWnO8-1710504206909-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None [{"id":27687675,"iid":1,"project_id":15578026,"title":"Please add an animated gif icon to the merge button","description":"I just want the merge button to hurt my eyes a little. :stuck_out_tongue_closed_eyes:","state":"closed","created_at":"2019-11-28T08:43:35.459Z","updated_at":"2019-11-28T08:46:23.304Z","closed_at":"2019-11-28T08:46:23.275Z","closed_by":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"labels":["bug","discussion"],"milestone":{"id":1082926,"iid":1,"project_id":15578026,"title":"1.0.0","description":"","state":"closed","created_at":"2019-11-28T08:42:30.301Z","updated_at":"2019-11-28T15:57:52.401Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/1"},"assignees":[],"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"type":"ISSUE","assignee":null,"user_notes_count":0,"merge_requests_count":0,"upvotes":1,"downvotes":0,"due_date":null,"confidential":false,"discussion_locked":null,"issue_type":"issue","web_url":"https://gitlab.com/gitea/test_repo/-/issues/1","time_stats":{"time_estimate":0,"total_time_spent":0,"human_time_estimate":null,"human_total_time_spent":null},"task_completion_status":{"count":0,"completed_count":0},"blocking_issues_count":0,"has_tasks":true,"task_status":"0 of 0 checklist items completed","_links":{"self":"https://gitlab.com/api/v4/projects/15578026/issues/1","notes":"https://gitlab.com/api/v4/projects/15578026/issues/1/notes","award_emoji":"https://gitlab.com/api/v4/projects/15578026/issues/1/award_emoji","project":"https://gitlab.com/api/v4/projects/15578026","closed_as_duplicate_of":null},"references":{"short":"#1","relative":"#1","full":"gitea/test_repo#1"},"severity":"UNKNOWN","moved_to_id":null,"service_desk_reply_to":null},{"id":27687706,"iid":2,"project_id":15578026,"title":"Test issue","description":"This is test issue 2, do not touch!","state":"closed","created_at":"2019-11-28T08:44:46.277Z","updated_at":"2019-11-28T08:45:44.987Z","closed_at":"2019-11-28T08:45:44.959Z","closed_by":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"labels":["duplicate"],"milestone":{"id":1082927,"iid":2,"project_id":15578026,"title":"1.1.0","description":"","state":"active","created_at":"2019-11-28T08:42:44.575Z","updated_at":"2019-11-28T08:42:44.575Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/2"},"assignees":[],"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"type":"ISSUE","assignee":null,"user_notes_count":2,"merge_requests_count":0,"upvotes":1,"downvotes":1,"due_date":null,"confidential":false,"discussion_locked":null,"issue_type":"issue","web_url":"https://gitlab.com/gitea/test_repo/-/issues/2","time_stats":{"time_estimate":0,"total_time_spent":0,"human_time_estimate":null,"human_total_time_spent":null},"task_completion_status":{"count":0,"completed_count":0},"blocking_issues_count":0,"has_tasks":true,"task_status":"0 of 0 checklist items completed","_links":{"self":"https://gitlab.com/api/v4/projects/15578026/issues/2","notes":"https://gitlab.com/api/v4/projects/15578026/issues/2/notes","award_emoji":"https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji","project":"https://gitlab.com/api/v4/projects/15578026","closed_as_duplicate_of":null},"references":{"short":"#2","relative":"#2","full":"gitea/test_repo#2"},"severity":"UNKNOWN","moved_to_id":null,"service_desk_reply_to":null}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_labels!page=1&per_page=100 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Flabels%3Fpage=1&per_page=100 similarity index 88% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_labels!page=1&per_page=100 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Flabels%3Fpage=1&per_page=100 index 03a46d057b..95924923d1 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_labels!page=1&per_page=100 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Flabels%3Fpage=1&per_page=100 @@ -1,24 +1,24 @@ -X-Prev-Page: -Referrer-Policy: strict-origin-when-cross-origin -Content-Type: application/json X-Per-Page: 100 +X-Prev-Page: X-Total: 9 -X-Runtime: 0.114167 -X-Total-Pages: 1 -Set-Cookie: _cfuvid=UMIyFl_InnSbX4Uxs5X.6ssGMrTVlv5uJzGE_UhDR3w-1709516923392-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Cache-Control: max-age=0, private, must-revalidate -Link: ; rel="first", ; rel="last" +Content-Type: application/json +X-Next-Page: X-Content-Type-Options: nosniff -Gitlab-Sv: api-gke-us-east1-b +Gitlab-Lb: haproxy-main-56-lb-gprd +Cf-Cache-Status: MISS +Set-Cookie: _cfuvid=5K2rwnMRyftEWt3OXSN3FeV8T9nf3Cgb20WFj.p4hyw-1710504206334-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Cache-Control: max-age=0, private, must-revalidate +X-Runtime: 0.424823 +Strict-Transport-Security: max-age=31536000 +Link: ; rel="first", ; rel="last" Content-Security-Policy: default-src 'none' Vary: Origin, Accept-Encoding -X-Next-Page: -X-Frame-Options: SAMEORIGIN -X-Gitlab-Meta: {"correlation_id":"3b4e48efc61f09520ea23b3bca5ea469","version":"1"} +X-Gitlab-Meta: {"correlation_id":"b02b63b76c2351a971670a8db9c57af9","version":"1"} X-Page: 1 -Cf-Cache-Status: MISS +X-Total-Pages: 1 Etag: W/"5a3fb9bc7b1018070943f4aa1353f8b6" -Strict-Transport-Security: max-age=31536000 -Gitlab-Lb: haproxy-main-30-lb-gprd +X-Frame-Options: SAMEORIGIN +Referrer-Policy: strict-origin-when-cross-origin +Gitlab-Sv: api-gke-us-east1-d [{"id":12959095,"name":"bug","description":null,"description_html":"","text_color":"#FFFFFF","color":"#d9534f","subscribed":false,"priority":null,"is_project_label":true},{"id":12959097,"name":"confirmed","description":null,"description_html":"","text_color":"#FFFFFF","color":"#d9534f","subscribed":false,"priority":null,"is_project_label":true},{"id":12959096,"name":"critical","description":null,"description_html":"","text_color":"#FFFFFF","color":"#d9534f","subscribed":false,"priority":null,"is_project_label":true},{"id":12959100,"name":"discussion","description":null,"description_html":"","text_color":"#FFFFFF","color":"#428bca","subscribed":false,"priority":null,"is_project_label":true},{"id":12959098,"name":"documentation","description":null,"description_html":"","text_color":"#1F1E24","color":"#f0ad4e","subscribed":false,"priority":null,"is_project_label":true},{"id":12959554,"name":"duplicate","description":null,"description_html":"","text_color":"#FFFFFF","color":"#7F8C8D","subscribed":false,"priority":null,"is_project_label":true},{"id":12959102,"name":"enhancement","description":null,"description_html":"","text_color":"#FFFFFF","color":"#5cb85c","subscribed":false,"priority":null,"is_project_label":true},{"id":12959101,"name":"suggestion","description":null,"description_html":"","text_color":"#FFFFFF","color":"#428bca","subscribed":false,"priority":null,"is_project_label":true},{"id":12959099,"name":"support","description":null,"description_html":"","text_color":"#1F1E24","color":"#f0ad4e","subscribed":false,"priority":null,"is_project_label":true}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_1_approvals b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F1%2Fapprovals similarity index 84% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_1_approvals rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F1%2Fapprovals index 7a3ab6b858..6a16667f83 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_1_approvals +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F1%2Fapprovals @@ -1,17 +1,17 @@ +Gitlab-Sv: api-gke-us-east1-c +Set-Cookie: _cfuvid=zeoNBfBKfrdVGUvp0nfh4oigIhB1U14XXmzniKufB0A-1710504211497-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +X-Runtime: 0.137677 Cache-Control: max-age=0, private, must-revalidate -Content-Security-Policy: default-src 'none' -Strict-Transport-Security: max-age=31536000 -X-Content-Type-Options: nosniff -X-Frame-Options: SAMEORIGIN -Set-Cookie: _cfuvid=z0y3rhqnpB208dDD5k02RKi_y6HV6.i7lc65_MGJIHM-1709516935718-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Content-Type: application/json Vary: Origin, Accept-Encoding -Gitlab-Sv: api-gke-us-east1-d -Referrer-Policy: strict-origin-when-cross-origin -Gitlab-Lb: haproxy-main-38-lb-gprd -Cf-Cache-Status: MISS +X-Frame-Options: SAMEORIGIN +X-Gitlab-Meta: {"correlation_id":"8585fe858d5623c4d3d1b77cfcdad845","version":"1"} +Content-Security-Policy: default-src 'none' Etag: W/"632b3d0f41fe1650d82b84feaa7b125d" -X-Gitlab-Meta: {"correlation_id":"68a6a29f5330b299ef95868936bd1b4b","version":"1"} -X-Runtime: 0.150028 +Strict-Transport-Security: max-age=31536000 +Cf-Cache-Status: MISS +Content-Type: application/json +X-Content-Type-Options: nosniff +Referrer-Policy: strict-origin-when-cross-origin +Gitlab-Lb: haproxy-main-55-lb-gprd {"id":43486906,"iid":1,"project_id":15578026,"title":"Update README.md","description":"add warning to readme","state":"merged","created_at":"2019-11-28T08:54:41.034Z","updated_at":"2019-11-28T16:02:08.377Z","merge_status":"can_be_merged","approved":true,"approvals_required":0,"approvals_left":0,"require_password_to_approve":false,"approved_by":[{"user":{"id":527793,"username":"axifive","name":"Alexey Terentyev","state":"active","locked":false,"avatar_url":"https://secure.gravatar.com/avatar/b5eee878c9129969b55d221a823fd15e55aad8dc15d521f4170e3c93728e02b6?s=80\u0026d=identicon","web_url":"https://gitlab.com/axifive"}},{"user":{"id":4102996,"username":"zeripath","name":"zeripath","state":"active","locked":false,"avatar_url":"https://secure.gravatar.com/avatar/3bad2cdad37aa0bbb3ad276ce8f77e32a1a9567a7083f0866d8df8ed0e92e5b5?s=80\u0026d=identicon","web_url":"https://gitlab.com/zeripath"}}],"suggested_approvers":[],"approvers":[],"approver_groups":[],"user_has_approved":false,"user_can_approve":false,"approval_rules_left":[],"has_approval_rules":true,"merge_request_approvers_available":false,"multiple_approval_rules_available":false,"invalid_approvers_rules":[]} \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2 similarity index 91% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2 index 73441c917c..8848af8e48 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2 @@ -1,17 +1,17 @@ -X-Content-Type-Options: nosniff -X-Gitlab-Meta: {"correlation_id":"d351bb99dc07c885c71d8f5299208320","version":"1"} -Gitlab-Lb: haproxy-main-56-lb-gprd -X-Frame-Options: SAMEORIGIN -Content-Type: application/json -Referrer-Policy: strict-origin-when-cross-origin -X-Runtime: 0.237643 -Strict-Transport-Security: max-age=31536000 -Gitlab-Sv: api-gke-us-east1-d Cache-Control: max-age=0, private, must-revalidate -Content-Security-Policy: default-src 'none' +X-Content-Type-Options: nosniff Etag: W/"914149155d75f8d8f7ed2e5351f0fadb" +Referrer-Policy: strict-origin-when-cross-origin +Content-Security-Policy: default-src 'none' Vary: Origin, Accept-Encoding +X-Runtime: 0.634688 Cf-Cache-Status: MISS -Set-Cookie: _cfuvid=yE9p3NWSIseWCeQl9amdJ0bfdfTyf7TIEOGJSzjcxTQ-1709516933254-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Set-Cookie: _cfuvid=Z._ut3jKk_GobWpwV3pdT8AP8FDBG3hXVJphHhFBiBg-1710504210170-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Content-Type: application/json +X-Gitlab-Meta: {"correlation_id":"c36a4f0267a05143404246325892000a","version":"1"} +Strict-Transport-Security: max-age=31536000 +Gitlab-Lb: haproxy-main-59-lb-gprd +Gitlab-Sv: api-gke-us-east1-d +X-Frame-Options: SAMEORIGIN {"id":43524600,"iid":2,"project_id":15578026,"title":"Test branch","description":"do not merge this PR","state":"opened","created_at":"2019-11-28T15:56:54.104Z","updated_at":"2020-04-19T19:24:21.108Z","merged_by":null,"merge_user":null,"merged_at":null,"closed_by":null,"closed_at":null,"target_branch":"master","source_branch":"feat/test","user_notes_count":0,"upvotes":1,"downvotes":0,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"assignees":[],"assignee":null,"reviewers":[],"source_project_id":15578026,"target_project_id":15578026,"labels":["bug"],"draft":false,"work_in_progress":false,"milestone":{"id":1082926,"iid":1,"project_id":15578026,"title":"1.0.0","description":"","state":"closed","created_at":"2019-11-28T08:42:30.301Z","updated_at":"2019-11-28T15:57:52.401Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/1"},"merge_when_pipeline_succeeds":false,"merge_status":"can_be_merged","detailed_merge_status":"mergeable","sha":"9f733b96b98a4175276edf6a2e1231489c3bdd23","merge_commit_sha":null,"squash_commit_sha":null,"discussion_locked":null,"should_remove_source_branch":null,"force_remove_source_branch":true,"prepared_at":"2019-11-28T15:56:54.104Z","reference":"!2","references":{"short":"!2","relative":"!2","full":"gitea/test_repo!2"},"web_url":"https://gitlab.com/gitea/test_repo/-/merge_requests/2","time_stats":{"time_estimate":0,"total_time_spent":0,"human_time_estimate":null,"human_total_time_spent":null},"squash":true,"squash_on_merge":true,"task_completion_status":{"count":0,"completed_count":0},"has_conflicts":false,"blocking_discussions_resolved":true,"approvals_before_merge":null,"subscribed":false,"changes_count":"1","latest_build_started_at":null,"latest_build_finished_at":null,"first_deployed_to_production_at":null,"pipeline":null,"head_pipeline":null,"diff_refs":{"base_sha":"c59c9b451acca9d106cc19d61d87afe3fbbb8b83","head_sha":"9f733b96b98a4175276edf6a2e1231489c3bdd23","start_sha":"c59c9b451acca9d106cc19d61d87afe3fbbb8b83"},"merge_error":null,"first_contribution":false,"user":{"can_merge":false}} \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_approvals b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Fapprovals similarity index 88% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_approvals rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Fapprovals index e9e5f3ad0c..be119c18b7 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_approvals +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Fapprovals @@ -1,17 +1,17 @@ -Gitlab-Sv: api-gke-us-east1-c +Set-Cookie: _cfuvid=sImrE_lz4VAFrw_o2FHA_8y6kxUoFm4G31.BEqR9M_E-1710504211811-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Gitlab-Meta: {"correlation_id":"cf4eeb7f9cb45f6d15ef0297231a3250","version":"1"} +X-Runtime: 0.163465 Content-Type: application/json Vary: Origin, Accept-Encoding -Gitlab-Lb: haproxy-main-49-lb-gprd -X-Gitlab-Meta: {"correlation_id":"ea044fc89155ed7f76093b9cc3a7d4ea","version":"1"} -X-Runtime: 0.144068 -Strict-Transport-Security: max-age=31536000 -Etag: W/"58109b687618e6b9e49ff812d5a911df" -X-Content-Type-Options: nosniff -Cache-Control: max-age=0, private, must-revalidate -X-Frame-Options: SAMEORIGIN -Set-Cookie: _cfuvid=bjDdu13341ZREKL1340bXPm8TFCQkeafZBecRxoqNv0-1709516936936-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Cf-Cache-Status: MISS -Content-Security-Policy: default-src 'none' +Gitlab-Lb: haproxy-main-17-lb-gprd +Gitlab-Sv: api-gke-us-east1-d Referrer-Policy: strict-origin-when-cross-origin +Cf-Cache-Status: MISS +Cache-Control: max-age=0, private, must-revalidate +Content-Security-Policy: default-src 'none' +Etag: W/"58109b687618e6b9e49ff812d5a911df" +Strict-Transport-Security: max-age=31536000 {"id":43524600,"iid":2,"project_id":15578026,"title":"Test branch","description":"do not merge this PR","state":"opened","created_at":"2019-11-28T15:56:54.104Z","updated_at":"2020-04-19T19:24:21.108Z","merge_status":"can_be_merged","approved":true,"approvals_required":0,"approvals_left":0,"require_password_to_approve":false,"approved_by":[{"user":{"id":4575606,"username":"real6543","name":"6543","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/4575606/avatar.png","web_url":"https://gitlab.com/real6543"}}],"suggested_approvers":[],"approvers":[],"approver_groups":[{"group":{"id":3181312,"web_url":"https://gitlab.com/groups/gitea","name":"gitea","path":"gitea","description":"Mirror of Gitea source code repositories","visibility":"public","share_with_group_lock":false,"require_two_factor_authentication":false,"two_factor_grace_period":48,"project_creation_level":"maintainer","auto_devops_enabled":null,"subgroup_creation_level":"owner","emails_disabled":false,"emails_enabled":true,"mentions_disabled":null,"lfs_enabled":true,"math_rendering_limits_enabled":true,"lock_math_rendering_limits_enabled":false,"default_branch_protection":2,"default_branch_protection_defaults":{"allowed_to_push":[{"access_level":30}],"allow_force_push":true,"allowed_to_merge":[{"access_level":30}]},"avatar_url":"https://gitlab.com/uploads/-/system/group/avatar/3181312/gitea.png","request_access_enabled":true,"full_name":"gitea","full_path":"gitea","created_at":"2018-07-04T16:32:10.176Z","parent_id":null,"organization_id":1,"shared_runners_setting":"enabled","ldap_cn":null,"ldap_access":null,"wiki_access_level":"enabled"}}],"user_has_approved":false,"user_can_approve":false,"approval_rules_left":[],"has_approval_rules":true,"merge_request_approvers_available":false,"multiple_approval_rules_available":false,"invalid_approvers_rules":[]} \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_award_emoji!page=1&per_page=1 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Faward_emoji%3Fpage=1&per_page=1 similarity index 83% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_award_emoji!page=1&per_page=1 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Faward_emoji%3Fpage=1&per_page=1 index 3d99cfbe24..eb72d3fc54 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_award_emoji!page=1&per_page=1 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Faward_emoji%3Fpage=1&per_page=1 @@ -1,24 +1,24 @@ -Set-Cookie: _cfuvid=2_c1M0pqgnG1D4bNm2QluJ9AGXZ7FyB14t_v4ittTjY-1709516933765-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None Etag: W/"798718b23a2ec66b16cce20cb7155116" -X-Runtime: 0.125829 -X-Total-Pages: 2 -Gitlab-Sv: api-gke-us-east1-b -Cf-Cache-Status: MISS -X-Prev-Page: -Cache-Control: max-age=0, private, must-revalidate +X-Gitlab-Meta: {"correlation_id":"64dce1b90fe8fbfda281a99e34d0905c","version":"1"} Link: ; rel="next", ; rel="first", ; rel="last" +X-Frame-Options: SAMEORIGIN +X-Runtime: 0.092139 Content-Type: application/json X-Content-Type-Options: nosniff -X-Gitlab-Meta: {"correlation_id":"9d8976ef2244076825f1e8a8bae3a090","version":"1"} -Vary: Origin, Accept-Encoding -Strict-Transport-Security: max-age=31536000 +X-Prev-Page: +Referrer-Policy: strict-origin-when-cross-origin +X-Per-Page: 1 X-Total: 2 -X-Frame-Options: SAMEORIGIN +Strict-Transport-Security: max-age=31536000 +Content-Security-Policy: default-src 'none' +Cache-Control: max-age=0, private, must-revalidate +Vary: Origin, Accept-Encoding +Gitlab-Lb: haproxy-main-30-lb-gprd +Cf-Cache-Status: MISS +Gitlab-Sv: api-gke-us-east1-b +Set-Cookie: _cfuvid=vG12ThddZrDMG_flNdCfEfuN3Vma3YHPWrU1MJOBFhY-1710504210427-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None X-Next-Page: 2 X-Page: 1 -X-Per-Page: 1 -Content-Security-Policy: default-src 'none' -Referrer-Policy: strict-origin-when-cross-origin -Gitlab-Lb: haproxy-main-45-lb-gprd +X-Total-Pages: 2 [{"id":5541414,"name":"thumbsup","user":{"id":4575606,"username":"real6543","name":"6543","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/4575606/avatar.png","web_url":"https://gitlab.com/real6543"},"created_at":"2020-09-02T23:42:34.310Z","updated_at":"2020-09-02T23:42:34.310Z","awardable_id":43524600,"awardable_type":"MergeRequest","url":null}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_award_emoji!page=2&per_page=1 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Faward_emoji%3Fpage=2&per_page=1 similarity index 81% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_award_emoji!page=2&per_page=1 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Faward_emoji%3Fpage=2&per_page=1 index 9981fc1c38..63f7d02a17 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_award_emoji!page=2&per_page=1 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Faward_emoji%3Fpage=2&per_page=1 @@ -1,24 +1,24 @@ -X-Content-Type-Options: nosniff -Gitlab-Lb: haproxy-main-32-lb-gprd +X-Next-Page: +Content-Type: application/json +Vary: Origin, Accept-Encoding +Cf-Cache-Status: MISS +Content-Security-Policy: default-src 'none' +X-Frame-Options: SAMEORIGIN +X-Page: 2 +Strict-Transport-Security: max-age=31536000 +Set-Cookie: _cfuvid=VgzG7aSZyu0lycKl6YVe9GTRYeLa0XUB5lv3pROs3tk-1710504210672-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Referrer-Policy: strict-origin-when-cross-origin Cache-Control: max-age=0, private, must-revalidate Etag: W/"e6776aaa57e6a81bf8a2d8823272cc70" X-Prev-Page: 1 -X-Runtime: 0.066771 -Referrer-Policy: strict-origin-when-cross-origin -Content-Security-Policy: default-src 'none' -Vary: Origin, Accept-Encoding -X-Frame-Options: SAMEORIGIN -X-Next-Page: -X-Per-Page: 1 -Set-Cookie: _cfuvid=XlmPD5BVPxHofXNdPbazM3JF2i5DbLlQUVqNW9K6Y28-1709516934208-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -X-Gitlab-Meta: {"correlation_id":"e51679dffc5216e7c77a018a26343380","version":"1"} +X-Runtime: 0.073747 +Link: ; rel="prev", ; rel="first", ; rel="last" +X-Gitlab-Meta: {"correlation_id":"50f2f6c2fa586f2699010189215c0531","version":"1"} X-Total: 2 X-Total-Pages: 2 -Content-Type: application/json -X-Page: 2 -Strict-Transport-Security: max-age=31536000 -Cf-Cache-Status: MISS -Link: ; rel="prev", ; rel="first", ; rel="last" -Gitlab-Sv: api-gke-us-east1-d +Gitlab-Lb: haproxy-main-60-lb-gprd +X-Content-Type-Options: nosniff +X-Per-Page: 1 +Gitlab-Sv: api-gke-us-east1-b [{"id":5541415,"name":"tada","user":{"id":4575606,"username":"real6543","name":"6543","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/4575606/avatar.png","web_url":"https://gitlab.com/real6543"},"created_at":"2020-09-02T23:42:59.060Z","updated_at":"2020-09-02T23:42:59.060Z","awardable_id":43524600,"awardable_type":"MergeRequest","url":null}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_award_emoji!page=3&per_page=1 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Faward_emoji%3Fpage=3&per_page=1 similarity index 73% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_award_emoji!page=3&per_page=1 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Faward_emoji%3Fpage=3&per_page=1 index 07ff6e5ba4..9cce5e0bdb 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests_2_award_emoji!page=3&per_page=1 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%2F2%2Faward_emoji%3Fpage=3&per_page=1 @@ -1,26 +1,26 @@ -X-Content-Type-Options: nosniff -X-Prev-Page: -Accept-Ranges: bytes -Gitlab-Sv: api-gke-us-east1-d -X-Gitlab-Meta: {"correlation_id":"e19c4433df21888b10c912bab5311ecd","version":"1"} -X-Next-Page: -Strict-Transport-Security: max-age=31536000 -Gitlab-Lb: haproxy-main-26-lb-gprd -Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" -Link: ; rel="first", ; rel="last" -X-Per-Page: 1 -Set-Cookie: _cfuvid=Lv9tmhZRx2Sa.lToTQ.C73MDyRvwOtmVmgEi4cbTrkQ-1709516935278-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None X-Total-Pages: 2 -Cf-Cache-Status: MISS -Content-Length: 2 -X-Page: 3 -X-Total: 2 -X-Frame-Options: SAMEORIGIN -Referrer-Policy: strict-origin-when-cross-origin -Content-Type: application/json Cache-Control: max-age=0, private, must-revalidate +Link: ; rel="first", ; rel="last" +X-Prev-Page: +Content-Type: application/json +Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5" +X-Per-Page: 1 +Cf-Cache-Status: MISS +Accept-Ranges: bytes +X-Content-Type-Options: nosniff +X-Runtime: 0.064101 +X-Total: 2 +Gitlab-Lb: haproxy-main-18-lb-gprd +Content-Length: 2 +Referrer-Policy: strict-origin-when-cross-origin +Set-Cookie: _cfuvid=I18ivb.i14P1hql2L0PDHGFAIFBr6CdHc5Xp3CQ7Z78-1710504211202-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None Vary: Origin, Accept-Encoding -X-Runtime: 0.056300 +X-Next-Page: +Gitlab-Sv: api-gke-us-east1-b +Strict-Transport-Security: max-age=31536000 Content-Security-Policy: default-src 'none' +X-Frame-Options: SAMEORIGIN +X-Gitlab-Meta: {"correlation_id":"6c902fe6782c24f23059e0ab39caf051","version":"1"} +X-Page: 3 [] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests!page=1&per_page=1&view=simple b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%3Fpage=1&per_page=1&view=simple similarity index 84% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests!page=1&per_page=1&view=simple rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%3Fpage=1&per_page=1&view=simple index 6f8d70200c..1beb5e698c 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_merge_requests!page=1&per_page=1&view=simple +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmerge_requests%3Fpage=1&per_page=1&view=simple @@ -1,24 +1,24 @@ -Content-Security-Policy: default-src 'none' Etag: W/"14f72c1f555b0e6348d338190e9e4839" -Strict-Transport-Security: max-age=31536000 -Cf-Cache-Status: MISS -Set-Cookie: _cfuvid=V__PxQ60qzlepbd7My22SIsuTFfjEafoCsqahPDPjMg-1709516932676-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -X-Frame-Options: SAMEORIGIN -X-Per-Page: 1 -X-Total: 2 -X-Total-Pages: 2 -Gitlab-Sv: api-gke-us-east1-d -Content-Type: application/json -X-Content-Type-Options: nosniff -X-Next-Page: 2 +X-Page: 1 Referrer-Policy: strict-origin-when-cross-origin -Vary: Origin, Accept-Encoding +Gitlab-Lb: haproxy-main-44-lb-gprd +Content-Type: application/json +Content-Security-Policy: default-src 'none' +Set-Cookie: _cfuvid=xtxwnC3sB7qZrUtCFdAaMiSOKDnQPiLD3iYq9hTj39I-1710504209365-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +X-Per-Page: 1 +X-Runtime: 0.102877 +X-Gitlab-Meta: {"correlation_id":"a779d4e8ffae8bdf01f20a6d0c545247","version":"1"} +Cf-Cache-Status: MISS +X-Frame-Options: SAMEORIGIN +X-Total-Pages: 2 Cache-Control: max-age=0, private, must-revalidate Link: ; rel="next", ; rel="first", ; rel="last" X-Prev-Page: -X-Gitlab-Meta: {"correlation_id":"7e815ab626b3ec958eb45b614106a759","version":"1"} -X-Page: 1 -X-Runtime: 0.130064 -Gitlab-Lb: haproxy-main-38-lb-gprd +X-Content-Type-Options: nosniff +X-Next-Page: 2 +Gitlab-Sv: api-gke-us-east1-d +Vary: Origin, Accept-Encoding +Strict-Transport-Security: max-age=31536000 +X-Total: 2 [{"id":43524600,"iid":2,"project_id":15578026,"title":"Test branch","description":"do not merge this PR","state":"opened","created_at":"2019-11-28T15:56:54.104Z","updated_at":"2020-04-19T19:24:21.108Z","web_url":"https://gitlab.com/gitea/test_repo/-/merge_requests/2"}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_milestones!page=1&per_page=100&state=all b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmilestones%3Fpage=1&per_page=100&state=all similarity index 81% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_milestones!page=1&per_page=100&state=all rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmilestones%3Fpage=1&per_page=100&state=all index 14a40dc505..6d7d482138 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_milestones!page=1&per_page=100&state=all +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Fmilestones%3Fpage=1&per_page=100&state=all @@ -1,24 +1,24 @@ -X-Total: 2 +X-Prev-Page: +Vary: Origin, Accept-Encoding +X-Gitlab-Meta: {"correlation_id":"2998cbf0e39d3b81710b1d1b82c03b80","version":"1"} Referrer-Policy: strict-origin-when-cross-origin -Gitlab-Sv: api-gke-us-east1-d -X-Content-Type-Options: nosniff -X-Frame-Options: SAMEORIGIN -X-Next-Page: -X-Total-Pages: 1 +Link: ; rel="first", ; rel="last" +X-Page: 1 +Etag: W/"c8e2d3a5f05ee29c58b665c86684f9f9" +Content-Security-Policy: default-src 'none' +X-Total: 2 Cf-Cache-Status: MISS Content-Type: application/json +X-Next-Page: +X-Frame-Options: SAMEORIGIN +X-Total-Pages: 1 +Gitlab-Lb: haproxy-main-24-lb-gprd +Set-Cookie: _cfuvid=UO1GaUJc3jsd8W85u2xy74QFY1Ez71cmGWi0WbQoYpU-1710504205756-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None Cache-Control: max-age=0, private, must-revalidate -Link: ; rel="first", ; rel="last" -Gitlab-Lb: haproxy-main-02-lb-gprd -Vary: Origin, Accept-Encoding -X-Per-Page: 100 -X-Runtime: 0.072314 +X-Content-Type-Options: nosniff Strict-Transport-Security: max-age=31536000 -Set-Cookie: _cfuvid=AAzb4a45dTRraCeBWB2eYtD0LUdZnyMdcLZpKLKFKQY-1709516922946-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -X-Page: 1 -Content-Security-Policy: default-src 'none' -Etag: W/"c8e2d3a5f05ee29c58b665c86684f9f9" -X-Gitlab-Meta: {"correlation_id":"3786d93aa260a78e86bd229427022c67","version":"1"} -X-Prev-Page: +Gitlab-Sv: api-gke-us-east1-b +X-Per-Page: 100 +X-Runtime: 0.078614 [{"id":1082927,"iid":2,"project_id":15578026,"title":"1.1.0","description":"","state":"active","created_at":"2019-11-28T08:42:44.575Z","updated_at":"2019-11-28T08:42:44.575Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/2"},{"id":1082926,"iid":1,"project_id":15578026,"title":"1.0.0","description":"","state":"closed","created_at":"2019-11-28T08:42:30.301Z","updated_at":"2019-11-28T15:57:52.401Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/1"}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_releases!page=1&per_page=100 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Freleases%3Fpage=1&per_page=100 similarity index 91% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_releases!page=1&per_page=100 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Freleases%3Fpage=1&per_page=100 index 5e0f064cde..fc63173e9f 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026_releases!page=1&per_page=100 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2F15578026%2Freleases%3Fpage=1&per_page=100 @@ -1,24 +1,24 @@ -Gitlab-Lb: haproxy-main-38-lb-gprd -X-Gitlab-Meta: {"correlation_id":"fe487506ab79b2641f2798820a003e65","version":"1"} -X-Runtime: 0.126232 -X-Total-Pages: 1 -X-Prev-Page: -Cache-Control: max-age=0, private, must-revalidate -Link: ; rel="first", ; rel="last" -X-Page: 1 -X-Per-Page: 100 Content-Type: application/json -Vary: Origin, Accept-Encoding -X-Frame-Options: SAMEORIGIN -X-Next-Page: -Gitlab-Sv: api-gke-us-east1-d -X-Content-Type-Options: nosniff -Referrer-Policy: strict-origin-when-cross-origin -Cf-Cache-Status: MISS -Strict-Transport-Security: max-age=31536000 -Set-Cookie: _cfuvid=2dextQvrlcispQRNmodfvb.IH__P1bCF5jvS5aFrRjY-1709516924499-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -Etag: W/"dccc7159dc4b46989d13128a7d6ee859" Content-Security-Policy: default-src 'none' +Link: ; rel="first", ; rel="last" +X-Prev-Page: +Gitlab-Sv: api-gke-us-east1-b +X-Content-Type-Options: nosniff +X-Runtime: 0.123532 +Vary: Origin, Accept-Encoding +X-Per-Page: 100 +Referrer-Policy: strict-origin-when-cross-origin +Set-Cookie: _cfuvid=Eoqdcle3awcN8Jyrig.dSmC4hTIPuXqZ5ruJIG9c56I-1710504206613-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None X-Total: 1 +Cf-Cache-Status: MISS +X-Next-Page: +Cache-Control: max-age=0, private, must-revalidate +X-Gitlab-Meta: {"correlation_id":"1c01187e563b0819c5ad553bc7525ce8","version":"1"} +Etag: W/"dccc7159dc4b46989d13128a7d6ee859" +X-Page: 1 +Gitlab-Lb: haproxy-main-30-lb-gprd +X-Frame-Options: SAMEORIGIN +X-Total-Pages: 1 +Strict-Transport-Security: max-age=31536000 [{"name":"First Release","tag_name":"v0.9.99","description":"A test release","created_at":"2019-11-28T09:09:48.840Z","released_at":"2019-11-28T09:09:48.836Z","upcoming_release":false,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"commit":{"id":"0720a3ec57c1f843568298117b874319e7deee75","short_id":"0720a3ec","created_at":"2019-11-28T08:49:16.000+00:00","parent_ids":["93ea21ce45d35690c35e80961d239645139e872c"],"title":"Add new file","message":"Add new file","author_name":"Lauris BH","author_email":"lauris@nix.lv","authored_date":"2019-11-28T08:49:16.000+00:00","committer_name":"Lauris BH","committer_email":"lauris@nix.lv","committed_date":"2019-11-28T08:49:16.000+00:00","trailers":{},"extended_trailers":{},"web_url":"https://gitlab.com/gitea/test_repo/-/commit/0720a3ec57c1f843568298117b874319e7deee75"},"commit_path":"/gitea/test_repo/-/commit/0720a3ec57c1f843568298117b874319e7deee75","tag_path":"/gitea/test_repo/-/tags/v0.9.99","assets":{"count":4,"sources":[{"format":"zip","url":"https://gitlab.com/gitea/test_repo/-/archive/v0.9.99/test_repo-v0.9.99.zip"},{"format":"tar.gz","url":"https://gitlab.com/gitea/test_repo/-/archive/v0.9.99/test_repo-v0.9.99.tar.gz"},{"format":"tar.bz2","url":"https://gitlab.com/gitea/test_repo/-/archive/v0.9.99/test_repo-v0.9.99.tar.bz2"},{"format":"tar","url":"https://gitlab.com/gitea/test_repo/-/archive/v0.9.99/test_repo-v0.9.99.tar"}],"links":[]},"evidences":[{"sha":"89f1223473ee01f192a83d0cb89f4d1eac1de74f01ad","filepath":"https://gitlab.com/gitea/test_repo/-/releases/v0.9.99/evidences/52147.json","collected_at":"2019-11-28T09:09:48.888Z"}],"_links":{"closed_issues_url":"https://gitlab.com/gitea/test_repo/-/issues?release_tag=v0.9.99\u0026scope=all\u0026state=closed","closed_merge_requests_url":"https://gitlab.com/gitea/test_repo/-/merge_requests?release_tag=v0.9.99\u0026scope=all\u0026state=closed","merged_merge_requests_url":"https://gitlab.com/gitea/test_repo/-/merge_requests?release_tag=v0.9.99\u0026scope=all\u0026state=merged","opened_issues_url":"https://gitlab.com/gitea/test_repo/-/issues?release_tag=v0.9.99\u0026scope=all\u0026state=opened","opened_merge_requests_url":"https://gitlab.com/gitea/test_repo/-/merge_requests?release_tag=v0.9.99\u0026scope=all\u0026state=opened","self":"https://gitlab.com/gitea/test_repo/-/releases/v0.9.99"}}] \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026 b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2Fgitea%252Ftest_repo similarity index 94% rename from services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026 rename to services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2Fgitea%252Ftest_repo index e102c2ee11..96f1ea86b5 100644 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_projects_15578026 +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fprojects%2Fgitea%252Ftest_repo @@ -1,17 +1,17 @@ -X-Frame-Options: SAMEORIGIN -Vary: Origin, Accept-Encoding -Strict-Transport-Security: max-age=31536000 -Cache-Control: max-age=0, private, must-revalidate -X-Content-Type-Options: nosniff -X-Gitlab-Meta: {"correlation_id":"fcbfb8d789b3f092196e301b01add051","version":"1"} -Gitlab-Sv: api-gke-us-east1-b -Cf-Cache-Status: MISS -Content-Security-Policy: default-src 'none' -Etag: W/"8db4917b3be5f4ca0d101a702179b75a" -X-Runtime: 0.230015 Referrer-Policy: strict-origin-when-cross-origin -Gitlab-Lb: haproxy-main-39-lb-gprd -Set-Cookie: _cfuvid=cvUyaJC1KTpP.SBvDCjaIYRgZYh4zbY7BihbhwIUzt4-1709516922501-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Gitlab-Lb: haproxy-main-51-lb-gprd +Cf-Cache-Status: MISS +Etag: W/"8db4917b3be5f4ca0d101a702179b75a" +X-Content-Type-Options: nosniff +Strict-Transport-Security: max-age=31536000 +Gitlab-Sv: api-gke-us-east1-b Content-Type: application/json +Cache-Control: max-age=0, private, must-revalidate +X-Gitlab-Meta: {"correlation_id":"9b3859cf6d73ce5de261a56d286072a5","version":"1"} +X-Runtime: 0.119487 +Content-Security-Policy: default-src 'none' +Vary: Origin, Accept-Encoding +Set-Cookie: _cfuvid=Cmc.ycVkdwA_tBvmR2tOVLQ5B.khzzU39ZUxgf4RNlw-1710504204838-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +X-Frame-Options: SAMEORIGIN {"id":15578026,"description":"Test repository for testing migration from gitlab to gitea","name":"test_repo","name_with_namespace":"gitea / test_repo","path":"test_repo","path_with_namespace":"gitea/test_repo","created_at":"2019-11-28T08:20:33.019Z","default_branch":"master","tag_list":["migration","test"],"topics":["migration","test"],"ssh_url_to_repo":"git@gitlab.com:gitea/test_repo.git","http_url_to_repo":"https://gitlab.com/gitea/test_repo.git","web_url":"https://gitlab.com/gitea/test_repo","readme_url":"https://gitlab.com/gitea/test_repo/-/blob/master/README.md","forks_count":1,"avatar_url":null,"star_count":0,"last_activity_at":"2020-04-19T19:46:04.527Z","namespace":{"id":3181312,"name":"gitea","path":"gitea","kind":"group","full_path":"gitea","parent_id":null,"avatar_url":"/uploads/-/system/group/avatar/3181312/gitea.png","web_url":"https://gitlab.com/groups/gitea"},"container_registry_image_prefix":"registry.gitlab.com/gitea/test_repo","_links":{"self":"https://gitlab.com/api/v4/projects/15578026","issues":"https://gitlab.com/api/v4/projects/15578026/issues","merge_requests":"https://gitlab.com/api/v4/projects/15578026/merge_requests","repo_branches":"https://gitlab.com/api/v4/projects/15578026/repository/branches","labels":"https://gitlab.com/api/v4/projects/15578026/labels","events":"https://gitlab.com/api/v4/projects/15578026/events","members":"https://gitlab.com/api/v4/projects/15578026/members","cluster_agents":"https://gitlab.com/api/v4/projects/15578026/cluster_agents"},"packages_enabled":true,"empty_repo":false,"archived":false,"visibility":"public","resolve_outdated_diff_discussions":false,"repository_object_format":"sha1","issues_enabled":true,"merge_requests_enabled":true,"wiki_enabled":true,"jobs_enabled":true,"snippets_enabled":true,"container_registry_enabled":true,"service_desk_enabled":true,"can_create_merge_request_in":true,"issues_access_level":"enabled","repository_access_level":"enabled","merge_requests_access_level":"enabled","forking_access_level":"enabled","wiki_access_level":"enabled","builds_access_level":"enabled","snippets_access_level":"enabled","pages_access_level":"enabled","analytics_access_level":"enabled","container_registry_access_level":"enabled","security_and_compliance_access_level":"private","releases_access_level":"enabled","environments_access_level":"enabled","feature_flags_access_level":"enabled","infrastructure_access_level":"enabled","monitor_access_level":"enabled","model_experiments_access_level":"enabled","model_registry_access_level":"enabled","emails_disabled":false,"emails_enabled":true,"shared_runners_enabled":true,"lfs_enabled":true,"creator_id":1241334,"import_status":"none","open_issues_count":0,"description_html":"\u003cp data-sourcepos=\"1:1-1:58\" dir=\"auto\"\u003eTest repository for testing migration from gitlab to gitea\u003c/p\u003e","updated_at":"2024-01-11T01:23:21.057Z","ci_config_path":null,"public_jobs":true,"shared_with_groups":[],"only_allow_merge_if_pipeline_succeeds":false,"allow_merge_on_skipped_pipeline":null,"request_access_enabled":true,"only_allow_merge_if_all_discussions_are_resolved":false,"remove_source_branch_after_merge":true,"printing_merge_request_link_enabled":true,"merge_method":"ff","squash_option":"default_off","enforce_auth_checks_on_uploads":true,"suggestion_commit_message":null,"merge_commit_template":null,"squash_commit_template":null,"issue_branch_template":null,"warn_about_potentially_unwanted_characters":true,"autoclose_referenced_issues":true,"external_authorization_classification_label":"","requirements_enabled":false,"requirements_access_level":"enabled","security_and_compliance_enabled":false,"compliance_frameworks":[],"permissions":{"project_access":null,"group_access":null}} \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fversion b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fversion new file mode 100644 index 0000000000..b8561d4303 --- /dev/null +++ b/services/migrations/testdata/gitlab/full_download/GET_%2Fapi%2Fv4%2Fversion @@ -0,0 +1,17 @@ +Content-Type: application/json +Cache-Control: max-age=0, private, must-revalidate +Vary: Origin, Accept-Encoding +X-Frame-Options: SAMEORIGIN +Strict-Transport-Security: max-age=31536000 +X-Gitlab-Meta: {"correlation_id":"5e1b0f0c600e3127952b0bc933bfe0fd","version":"1"} +Referrer-Policy: strict-origin-when-cross-origin +Gitlab-Sv: api-gke-us-east1-b +Set-Cookie: _cfuvid=ve7lWeCgOflkqyU5mzcjS4rdE91f0uaUXBG.po.9VLs-1710504204253-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None +Etag: W/"a7e5ac2ae5500f226c1020b94327a605" +X-Runtime: 0.025760 +Content-Security-Policy: default-src 'none' +X-Content-Type-Options: nosniff +Gitlab-Lb: haproxy-main-39-lb-gprd +Cf-Cache-Status: MISS + +{"version":"16.10.0-pre","revision":"7da39369465","kas":{"enabled":true,"externalUrl":"wss://kas.gitlab.com","version":"v16.10.1"},"enterprise":true} \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/full_download/_api_v4_version b/services/migrations/testdata/gitlab/full_download/_api_v4_version deleted file mode 100644 index e61313f6bc..0000000000 --- a/services/migrations/testdata/gitlab/full_download/_api_v4_version +++ /dev/null @@ -1,17 +0,0 @@ -Etag: W/"796348ca31c2f886c4bf67753358302b" -X-Content-Type-Options: nosniff -Gitlab-Lb: haproxy-main-22-lb-gprd -Set-Cookie: _cfuvid=XONUYaYMv2vYBZLhOZoclracol_W8SXVwL7kwoXROjM-1709516920902-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None -X-Gitlab-Meta: {"correlation_id":"0defa7748a7bd70616af6bd0f338094c","version":"1"} -Strict-Transport-Security: max-age=31536000 -Gitlab-Sv: api-gke-us-east1-c -Cf-Cache-Status: MISS -Content-Type: application/json -Cache-Control: max-age=0, private, must-revalidate -Content-Security-Policy: default-src 'none' -X-Frame-Options: SAMEORIGIN -X-Runtime: 0.035674 -Vary: Origin, Accept-Encoding -Referrer-Policy: strict-origin-when-cross-origin - -{"version":"16.10.0-pre","revision":"432cc3c7389","kas":{"enabled":true,"externalUrl":"wss://kas.gitlab.com","version":"v16.10.0-rc1"},"enterprise":true} \ No newline at end of file diff --git a/services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996 b/services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996 similarity index 100% rename from services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996 rename to services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996 diff --git a/services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_issues_2_award_emoji!page=1&per_page=10 b/services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fissues%2F2%2Faward_emoji%3Fpage=1&per_page=10 similarity index 100% rename from services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_issues_2_award_emoji!page=1&per_page=10 rename to services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fissues%2F2%2Faward_emoji%3Fpage=1&per_page=10 diff --git a/services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_issues!page=1&per_page=10&sort=asc&state=all b/services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fissues%3Fpage=1&per_page=10&sort=asc&state=all similarity index 100% rename from services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_issues!page=1&per_page=10&sort=asc&state=all rename to services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fissues%3Fpage=1&per_page=10&sort=asc&state=all diff --git a/services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_merge_requests_1 b/services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fmerge_requests%2F1 similarity index 100% rename from services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_merge_requests_1 rename to services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fmerge_requests%2F1 diff --git a/services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_merge_requests_1_award_emoji!page=1&per_page=10 b/services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fmerge_requests%2F1%2Faward_emoji%3Fpage=1&per_page=10 similarity index 100% rename from services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_merge_requests_1_award_emoji!page=1&per_page=10 rename to services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fmerge_requests%2F1%2Faward_emoji%3Fpage=1&per_page=10 diff --git a/services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_merge_requests!page=1&per_page=10&view=simple b/services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fmerge_requests%3Fpage=1&per_page=10&view=simple similarity index 100% rename from services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_6590996_merge_requests!page=1&per_page=10&view=simple rename to services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2F6590996%2Fmerge_requests%3Fpage=1&per_page=10&view=simple diff --git a/services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_troyengel%2Farchbuild b/services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2Ftroyengel%252Farchbuild similarity index 100% rename from services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_projects_troyengel%2Farchbuild rename to services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fprojects%2Ftroyengel%252Farchbuild diff --git a/services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_version b/services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fversion similarity index 100% rename from services/migrations/testdata/gitlab/skipped_issue_number/_api_v4_version rename to services/migrations/testdata/gitlab/skipped_issue_number/GET_%2Fapi%2Fv4%2Fversion diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 3418cf90df..2a38d4ba55 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -479,10 +479,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.refName, err) continue } - objectFormat, err := git.GetObjectFormatOfRepo(ctx, m.Repo.RepoPath()) - if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to GetHashTypeOfRepo: %v", m.Repo, err) - } + objectFormat := git.ObjectFormatFromName(m.Repo.ObjectFormatName) notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ RefFullName: result.refName, OldCommitID: objectFormat.EmptyObjectID().String(), @@ -593,7 +590,7 @@ func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, gi m.Repo.DefaultBranch = firstName } // Update the git repository default branch - if err := gitRepo.SetDefaultBranch(m.Repo.DefaultBranch); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, m.Repo, m.Repo.DefaultBranch); err != nil { if !git.IsErrUnsupportedVersion(err) { log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err) desc := fmt.Sprintf("Failed to update default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err) diff --git a/services/packages/cargo/index.go b/services/packages/cargo/index.go index e8a8313625..59823cd3de 100644 --- a/services/packages/cargo/index.go +++ b/services/packages/cargo/index.go @@ -62,9 +62,9 @@ func InitializeIndexRepository(ctx context.Context, doer, owner *user_model.User } func RebuildIndex(ctx context.Context, doer, owner *user_model.User) error { - repo, err := getOrCreateIndexRepository(ctx, doer, owner) + repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, owner.Name, IndexRepositoryName) if err != nil { - return err + return fmt.Errorf("GetRepositoryByOwnerAndName: %w", err) } ps, err := packages_model.GetPackagesByType(ctx, owner.ID, packages_model.TypeCargo) diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index 27ee572640..514bcee8f1 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -36,9 +36,9 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, } } - for _, commitStatus := range commitStatuses { + for _, gp := range requiredContextsGlob { var targetStatus structs.CommitStatusState - for _, gp := range requiredContextsGlob { + for _, commitStatus := range commitStatuses { if gp.Match(commitStatus.Context) { targetStatus = commitStatus.State matchedCount++ @@ -46,17 +46,21 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, } } - if targetStatus != "" && targetStatus.NoBetterThan(returnedStatus) { + // If required rule not match any action, then it is pending + if targetStatus == "" { + if structs.CommitStatusPending.NoBetterThan(returnedStatus) { + returnedStatus = structs.CommitStatusPending + } + break + } + + if targetStatus.NoBetterThan(returnedStatus) { returnedStatus = targetStatus } } } - if matchedCount != len(requiredContexts) { - return structs.CommitStatusPending - } - - if matchedCount == 0 { + if matchedCount == 0 && returnedStatus == structs.CommitStatusSuccess { status := git_model.CalcCommitStatus(commitStatuses) if status != nil { return status.State diff --git a/services/pull/commit_status_test.go b/services/pull/commit_status_test.go new file mode 100644 index 0000000000..592acdd55c --- /dev/null +++ b/services/pull/commit_status_test.go @@ -0,0 +1,65 @@ +// Copyright 2024 The Gitea Authors. +// All rights reserved. +// SPDX-License-Identifier: MIT + +package pull + +import ( + "testing" + + git_model "code.gitea.io/gitea/models/git" + "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" +) + +func TestMergeRequiredContextsCommitStatus(t *testing.T) { + testCases := [][]*git_model.CommitStatus{ + { + {Context: "Build 1", State: structs.CommitStatusSuccess}, + {Context: "Build 2", State: structs.CommitStatusSuccess}, + {Context: "Build 3", State: structs.CommitStatusSuccess}, + }, + { + {Context: "Build 1", State: structs.CommitStatusSuccess}, + {Context: "Build 2", State: structs.CommitStatusSuccess}, + {Context: "Build 2t", State: structs.CommitStatusPending}, + }, + { + {Context: "Build 1", State: structs.CommitStatusSuccess}, + {Context: "Build 2", State: structs.CommitStatusSuccess}, + {Context: "Build 2t", State: structs.CommitStatusFailure}, + }, + { + {Context: "Build 1", State: structs.CommitStatusSuccess}, + {Context: "Build 2", State: structs.CommitStatusSuccess}, + {Context: "Build 2t", State: structs.CommitStatusSuccess}, + }, + { + {Context: "Build 1", State: structs.CommitStatusSuccess}, + {Context: "Build 2", State: structs.CommitStatusSuccess}, + {Context: "Build 2t", State: structs.CommitStatusSuccess}, + }, + } + testCasesRequiredContexts := [][]string{ + {"Build*"}, + {"Build*", "Build 2t*"}, + {"Build*", "Build 2t*"}, + {"Build*", "Build 2t*", "Build 3*"}, + {"Build*", "Build *", "Build 2t*", "Build 1*"}, + } + + testCasesExpected := []structs.CommitStatusState{ + structs.CommitStatusSuccess, + structs.CommitStatusPending, + structs.CommitStatusFailure, + structs.CommitStatusPending, + structs.CommitStatusSuccess, + } + + for i, commitStatuses := range testCases { + if MergeRequiredContextsCommitStatus(commitStatuses, testCasesRequiredContexts[i]) != testCasesExpected[i] { + assert.Fail(t, "Test case failed", "Test case %d failed", i+1) + } + } +} diff --git a/services/pull/pull.go b/services/pull/pull.go index 34f3391a63..29a49fb4dc 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -351,7 +351,7 @@ func TestPullRequest(ctx context.Context, doer *user_model.User, repoID, maxPR i } if err == nil { for _, pr := range prs { - objectFormat, _ := git.GetObjectFormatOfRepo(ctx, pr.BaseRepo.RepoPath()) + objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() { changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) if err != nil { diff --git a/services/release/release.go b/services/release/release.go index a359e5078e..ba5fd1dd98 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -326,10 +326,7 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re } refName := git.RefNameFromTag(rel.TagName) - objectFormat, err := git.GetObjectFormatOfRepo(ctx, repo.RepoPath()) - if err != nil { - return err - } + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) notify_service.PushCommits( ctx, doer, repo, &repository.PushUpdateOptions{ diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 7ca68776b5..0ac3c774b7 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -127,24 +127,17 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r repo.IsEmpty = false - // Don't bother looking this repo in the context it won't be there - gitRepo, err := gitrepo.OpenRepository(ctx, repo) - if err != nil { - return fmt.Errorf("openRepository: %w", err) - } - defer gitRepo.Close() - if len(defaultBranch) > 0 { repo.DefaultBranch = defaultBranch - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } } else { - repo.DefaultBranch, err = gitRepo.GetDefaultBranch() + repo.DefaultBranch, err = gitrepo.GetDefaultBranch(ctx, repo) if err != nil { repo.DefaultBranch = setting.Repository.DefaultBranch - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } } @@ -188,7 +181,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r repo.DefaultBranch = setting.Repository.DefaultBranch } - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } } @@ -197,6 +190,13 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r return fmt.Errorf("updateRepository: %w", err) } + // Don't bother looking this repo in the context it won't be there + gitRepo, err := gitrepo.OpenRepository(ctx, repo) + if err != nil { + return fmt.Errorf("openRepository: %w", err) + } + defer gitRepo.Close() + if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { return fmt.Errorf("SyncReleasesWithTags: %w", err) } diff --git a/services/repository/branch.go b/services/repository/branch.go index c3a7282f43..b683553245 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -225,44 +225,91 @@ func checkBranchName(ctx context.Context, repo *repo_model.Repository, name stri return err } -// syncBranchToDB sync the branch information in the database. It will try to update the branch first, -// if updated success with affect records > 0, then all are done. Because that means the branch has been in the database. -// If no record is affected, that means the branch does not exist in database. So there are two possibilities. -// One is this is a new branch, then we just need to insert the record. Another is the branches haven't been synced, -// then we need to sync all the branches into database. -func syncBranchToDB(ctx context.Context, repoID, pusherID int64, branchName string, commit *git.Commit) error { - cnt, err := git_model.UpdateBranch(ctx, repoID, pusherID, branchName, commit) - if err != nil { - return fmt.Errorf("git_model.UpdateBranch %d:%s failed: %v", repoID, branchName, err) - } - if cnt > 0 { // This means branch does exist, so it's a normal update. It also means the branch has been synced. - return nil +// SyncBranchesToDB sync the branch information in the database. +// It will check whether the branches of the repository have never been synced before. +// If so, it will sync all branches of the repository. +// Otherwise, it will sync the branches that need to be updated. +func SyncBranchesToDB(ctx context.Context, repoID, pusherID int64, branchNames, commitIDs []string, getCommit func(commitID string) (*git.Commit, error)) error { + // Some designs that make the code look strange but are made for performance optimization purposes: + // 1. Sync branches in a batch to reduce the number of DB queries. + // 2. Lazy load commit information since it may be not necessary. + // 3. Exit early if synced all branches of git repo when there's no branch in DB. + // 4. Check the branches in DB if they are already synced. + // + // If the user pushes many branches at once, the Git hook will call the internal API in batches, rather than all at once. + // See https://github.com/go-gitea/gitea/blob/cb52b17f92e2d2293f7c003649743464492bca48/cmd/hook.go#L27 + // For the first batch, it will hit optimization 3. + // For other batches, it will hit optimization 4. + + if len(branchNames) != len(commitIDs) { + return fmt.Errorf("branchNames and commitIDs length not match") } - // if user haven't visit UI but directly push to a branch after upgrading from 1.20 -> 1.21, - // we cannot simply insert the branch but need to check we have branches or not - hasBranch, err := db.Exist[git_model.Branch](ctx, git_model.FindBranchOptions{ - RepoID: repoID, - IsDeletedBranch: optional.Some(false), - }.ToConds()) - if err != nil { - return err - } - if !hasBranch { - if _, err = repo_module.SyncRepoBranches(ctx, repoID, pusherID); err != nil { - return fmt.Errorf("repo_module.SyncRepoBranches %d:%s failed: %v", repoID, branchName, err) + return db.WithTx(ctx, func(ctx context.Context) error { + branches, err := git_model.GetBranches(ctx, repoID, branchNames) + if err != nil { + return fmt.Errorf("git_model.GetBranches: %v", err) + } + + if len(branches) == 0 { + // if user haven't visit UI but directly push to a branch after upgrading from 1.20 -> 1.21, + // we cannot simply insert the branch but need to check we have branches or not + hasBranch, err := db.Exist[git_model.Branch](ctx, git_model.FindBranchOptions{ + RepoID: repoID, + IsDeletedBranch: optional.Some(false), + }.ToConds()) + if err != nil { + return err + } + if !hasBranch { + if _, err = repo_module.SyncRepoBranches(ctx, repoID, pusherID); err != nil { + return fmt.Errorf("repo_module.SyncRepoBranches %d failed: %v", repoID, err) + } + return nil + } + } + + branchMap := make(map[string]*git_model.Branch, len(branches)) + for _, branch := range branches { + branchMap[branch.Name] = branch + } + + newBranches := make([]*git_model.Branch, 0, len(branchNames)) + + for i, branchName := range branchNames { + commitID := commitIDs[i] + branch, exist := branchMap[branchName] + if exist && branch.CommitID == commitID && !branch.IsDeleted { + continue + } + + commit, err := getCommit(commitID) + if err != nil { + return fmt.Errorf("get commit of %s failed: %v", branchName, err) + } + + if exist { + if _, err := git_model.UpdateBranch(ctx, repoID, pusherID, branchName, commit); err != nil { + return fmt.Errorf("git_model.UpdateBranch %d:%s failed: %v", repoID, branchName, err) + } + return nil + } + + // if database have branches but not this branch, it means this is a new branch + newBranches = append(newBranches, &git_model.Branch{ + RepoID: repoID, + Name: branchName, + CommitID: commit.ID.String(), + CommitMessage: commit.Summary(), + PusherID: pusherID, + CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), + }) + } + + if len(newBranches) > 0 { + return db.Insert(ctx, newBranches) } return nil - } - - // if database have branches but not this branch, it means this is a new branch - return db.Insert(ctx, &git_model.Branch{ - RepoID: repoID, - Name: branchName, - CommitID: commit.ID.String(), - CommitMessage: commit.Summary(), - PusherID: pusherID, - CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), }) } @@ -317,7 +364,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m } if isDefault { - err2 = gitRepo.SetDefaultBranch(to) + err2 = gitrepo.SetDefaultBranch(ctx, repo, to) if err2 != nil { return err2 } diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go new file mode 100644 index 0000000000..145fc7d53c --- /dev/null +++ b/services/repository/commitstatus/commitstatus.go @@ -0,0 +1,135 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package commitstatus + +import ( + "context" + "crypto/sha256" + "fmt" + + "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/cache" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/log" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/automerge" +) + +func getCacheKey(repoID int64, brancheName string) string { + hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%d:%s", repoID, brancheName))) + return fmt.Sprintf("commit_status:%x", hashBytes) +} + +func updateCommitStatusCache(ctx context.Context, repoID int64, branchName string, status api.CommitStatusState) error { + c := cache.GetCache() + return c.Put(getCacheKey(repoID, branchName), string(status), 3*24*60) +} + +func deleteCommitStatusCache(ctx context.Context, repoID int64, branchName string) error { + c := cache.GetCache() + return c.Delete(getCacheKey(repoID, branchName)) +} + +// CreateCommitStatus creates a new CommitStatus given a bunch of parameters +// NOTE: All text-values will be trimmed from whitespaces. +// Requires: Repo, Creator, SHA +func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creator *user_model.User, sha string, status *git_model.CommitStatus) error { + repoPath := repo.RepoPath() + + // confirm that commit is exist + gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) + if err != nil { + return fmt.Errorf("OpenRepository[%s]: %w", repoPath, err) + } + defer closer.Close() + + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) + + commit, err := gitRepo.GetCommit(sha) + if err != nil { + return fmt.Errorf("GetCommit[%s]: %w", sha, err) + } + if len(sha) != objectFormat.FullLength() { + // use complete commit sha + sha = commit.ID.String() + } + + if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ + Repo: repo, + Creator: creator, + SHA: commit.ID, + CommitStatus: status, + }); err != nil { + return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) + } + + defaultBranchCommit, err := gitRepo.GetBranchCommit(repo.DefaultBranch) + if err != nil { + return fmt.Errorf("GetBranchCommit[%s]: %w", repo.DefaultBranch, err) + } + + if commit.ID.String() == defaultBranchCommit.ID.String() { // since one commit status updated, the combined commit status should be invalid + if err := deleteCommitStatusCache(ctx, repo.ID, repo.DefaultBranch); err != nil { + log.Error("deleteCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) + } + } + + if status.State.IsSuccess() { + if err := automerge.MergeScheduledPullRequest(ctx, sha, repo); err != nil { + return fmt.Errorf("MergeScheduledPullRequest[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) + } + } + + return nil +} + +// FindReposLastestCommitStatuses loading repository default branch latest combinded commit status with cache +func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Repository) ([]*git_model.CommitStatus, error) { + results := make([]*git_model.CommitStatus, len(repos)) + c := cache.GetCache() + + for i, repo := range repos { + status, ok := c.Get(getCacheKey(repo.ID, repo.DefaultBranch)).(string) + if ok && status != "" { + results[i] = &git_model.CommitStatus{State: api.CommitStatusState(status)} + } + } + + // collect the latest commit of each repo + // at most there are dozens of repos (limited by MaxResponseItems), so it's not a big problem at the moment + repoBranchNames := make(map[int64]string, len(repos)) + for i, repo := range repos { + if results[i] == nil { + repoBranchNames[repo.ID] = repo.DefaultBranch + } + } + + repoIDsToLatestCommitSHAs, err := git_model.FindBranchesByRepoAndBranchName(ctx, repoBranchNames) + if err != nil { + return nil, fmt.Errorf("FindBranchesByRepoAndBranchName: %v", err) + } + + // call the database O(1) times to get the commit statuses for all repos + repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoIDsToLatestCommitSHAs, db.ListOptionsAll) + if err != nil { + return nil, fmt.Errorf("GetLatestCommitStatusForPairs: %v", err) + } + + for i, repo := range repos { + if results[i] == nil { + results[i] = git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID]) + if results[i].State != "" { + if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil { + log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) + } + } + } + } + + return results, nil +} diff --git a/services/repository/create.go b/services/repository/create.go index 9bc0b93eff..d092d02a1f 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -177,12 +177,7 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re if len(opts.DefaultBranch) > 0 { repo.DefaultBranch = opts.DefaultBranch - gitRepo, err := gitrepo.OpenRepository(ctx, repo) - if err != nil { - return fmt.Errorf("openRepository: %w", err) - } - defer gitRepo.Close() - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go index 512aec7c81..e0dad29273 100644 --- a/services/repository/files/commit.go +++ b/services/repository/files/commit.go @@ -5,61 +5,13 @@ package files import ( "context" - "fmt" asymkey_model "code.gitea.io/gitea/models/asymkey" - git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/services/automerge" ) -// CreateCommitStatus creates a new CommitStatus given a bunch of parameters -// NOTE: All text-values will be trimmed from whitespaces. -// Requires: Repo, Creator, SHA -func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creator *user_model.User, sha string, status *git_model.CommitStatus) error { - repoPath := repo.RepoPath() - - // confirm that commit is exist - gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) - if err != nil { - return fmt.Errorf("OpenRepository[%s]: %w", repoPath, err) - } - defer closer.Close() - - objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) - - commit, err := gitRepo.GetCommit(sha) - if err != nil { - gitRepo.Close() - return fmt.Errorf("GetCommit[%s]: %w", sha, err) - } else if len(sha) != objectFormat.FullLength() { - // use complete commit sha - sha = commit.ID.String() - } - gitRepo.Close() - - if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ - Repo: repo, - Creator: creator, - SHA: commit.ID, - CommitStatus: status, - }); err != nil { - return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) - } - - if status.State.IsSuccess() { - if err := automerge.MergeScheduledPullRequest(ctx, sha, repo); err != nil { - return fmt.Errorf("MergeScheduledPullRequest[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) - } - } - - return nil -} - // CountDivergingCommits determines how many commits a branch is ahead or behind the repository's base branch func CountDivergingCommits(ctx context.Context, repo *repo_model.Repository, branch string) (*git.DivergeObject, error) { divergence, err := git.GetDivergingCommits(ctx, repo.RepoPath(), repo.DefaultBranch, branch) diff --git a/services/repository/files/search.go b/services/repository/files/search.go index f8317c4892..09c3ab5bf3 100644 --- a/services/repository/files/search.go +++ b/services/repository/files/search.go @@ -16,14 +16,18 @@ import ( ) type Result struct { - RepoID int64 // ignored - Filename string - CommitID string // branch - UpdatedUnix timeutil.TimeStamp // ignored - Language string - Color string - LineNumbers []int64 - FormattedLines template.HTML + RepoID int64 // ignored + Filename string + CommitID string // branch + UpdatedUnix timeutil.TimeStamp // ignored + Language string + Color string + Lines []ResultLine +} + +type ResultLine struct { + Num int64 + FormattedContent template.HTML } const pHEAD = "HEAD:" @@ -46,7 +50,8 @@ func NewRepoGrep(ctx context.Context, repo *repo_model.Repository, keyword strin "-n", // line nums "-i", // ignore case "--full-name", // full file path, rel to repo - //"--column", // for adding better highlighting support + //"--column", // for adding better highlighting support + "-e", // for queries starting with "-" ). AddDynamicArguments(keyword). AddArguments("HEAD"). @@ -57,6 +62,8 @@ func NewRepoGrep(ctx context.Context, repo *repo_model.Repository, keyword strin for _, block := range strings.Split(stdout, "\n\n") { res := Result{CommitID: repo.DefaultBranch} + + linenum := []int64{} code := []string{} for _, line := range strings.Split(block, "\n") { @@ -71,18 +78,32 @@ func NewRepoGrep(ctx context.Context, repo *repo_model.Repository, keyword strin continue } - res.LineNumbers = append(res.LineNumbers, i) + linenum = append(linenum, i) code = append(code, after) } } - if res.Filename == "" || len(code) == 0 || len(res.LineNumbers) == 0 { + if res.Filename == "" || len(code) == 0 || len(linenum) == 0 { continue } - res.FormattedLines, res.Language = highlight.Code(res.Filename, "", strings.Join(code, "\n")) + var hl template.HTML + + hl, res.Language = highlight.Code(res.Filename, "", strings.Join(code, "\n")) res.Color = enry.GetColor(res.Language) + hlCode := strings.Split(string(hl), "\n") + n := min(len(hlCode), len(linenum)) + + res.Lines = make([]ResultLine, n) + + for i := 0; i < n; i++ { + res.Lines[i] = ResultLine{ + Num: linenum[i], + FormattedContent: template.HTML(hlCode[i]), + } + } + data = append(data, &res) } diff --git a/services/repository/files/search_test.go b/services/repository/files/search_test.go index 959ddaa9f9..2f2f87368d 100644 --- a/services/repository/files/search_test.go +++ b/services/repository/files/search_test.go @@ -25,14 +25,16 @@ func TestNewRepoGrep(t *testing.T) { expected := []*Result{ { - RepoID: 0, - Filename: "README.md", - CommitID: "master", - UpdatedUnix: 0, - Language: "Markdown", - Color: "#083fa1", - LineNumbers: []int64{2, 3}, - FormattedLines: "\nDescription for repo1", + RepoID: 0, + Filename: "README.md", + CommitID: "master", + UpdatedUnix: 0, + Language: "Markdown", + Color: "#083fa1", + Lines: []ResultLine{ + {Num: 2, FormattedContent: ""}, + {Num: 3, FormattedContent: "Description for repo1"}, + }, }, } diff --git a/services/repository/generate.go b/services/repository/generate.go index c444b60b2c..9b09e271ab 100644 --- a/services/repository/generate.go +++ b/services/repository/generate.go @@ -272,12 +272,7 @@ func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *r repo.DefaultBranch = templateRepo.DefaultBranch } - gitRepo, err := gitrepo.OpenRepository(ctx, repo) - if err != nil { - return fmt.Errorf("openRepository: %w", err) - } - defer gitRepo.Close() - if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err = gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { return fmt.Errorf("setDefaultBranch: %w", err) } if err = UpdateRepository(ctx, repo, false); err != nil { diff --git a/services/repository/migrate.go b/services/repository/migrate.go index b218a2ef46..5800f2b5cb 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -16,6 +16,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/migration" @@ -97,7 +98,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, } defer gitRepo.Close() - branch, err := gitRepo.GetDefaultBranch() + branch, err := gitrepo.GetDefaultBranch(ctx, repo) if err != nil { log.Warn("Failed to get the default branch of a migrated wiki repo: %v", err) if err := util.RemoveAll(wikiPath); err != nil { diff --git a/services/repository/push.go b/services/repository/push.go index bb080e30cc..8b27f07196 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -11,7 +11,6 @@ import ( "time" "code.gitea.io/gitea/models/db" - git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/cache" @@ -183,7 +182,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { repo.DefaultBranch = refName repo.IsEmpty = false if repo.DefaultBranch != setting.Repository.DefaultBranch { - if err := gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, repo, repo.DefaultBranch); err != nil { if !git.IsErrUnsupportedVersion(err) { return err } @@ -259,10 +258,6 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum] } - if err = syncBranchToDB(ctx, repo.ID, opts.PusherID, branch, newCommit); err != nil { - return fmt.Errorf("git_model.UpdateBranch %s:%s failed: %v", repo.FullName(), branch, err) - } - notify_service.PushCommits(ctx, pusher, repo, opts, commits) // Cache for big repository @@ -275,10 +270,6 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { // close all related pulls log.Error("close related pull request failed: %v", err) } - - if err := git_model.AddDeletedBranch(ctx, repo.ID, branch, pusher.ID); err != nil { - return fmt.Errorf("AddDeletedBranch %s:%s failed: %v", repo.FullName(), branch, err) - } } // Even if user delete a branch on a repository which he didn't watch, he will be watch that. diff --git a/services/user/email.go b/services/user/email.go index 0b579cf792..9dc5270842 100644 --- a/services/user/email.go +++ b/services/user/email.go @@ -14,12 +14,13 @@ import ( "code.gitea.io/gitea/modules/util" ) -func AddOrSetPrimaryEmailAddress(ctx context.Context, u *user_model.User, emailStr string) error { +// AdminAddOrSetPrimaryEmailAddress is used by admins to add or set a user's primary email address +func AdminAddOrSetPrimaryEmailAddress(ctx context.Context, u *user_model.User, emailStr string) error { if strings.EqualFold(u.Email, emailStr) { return nil } - if err := user_model.ValidateEmail(emailStr); err != nil { + if err := user_model.ValidateEmailForAdmin(emailStr); err != nil { return err } diff --git a/services/user/email_test.go b/services/user/email_test.go index 66d4821346..0784b4f803 100644 --- a/services/user/email_test.go +++ b/services/user/email_test.go @@ -10,11 +10,13 @@ import ( organization_model "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + "github.com/gobwas/glob" "github.com/stretchr/testify/assert" ) -func TestAddOrSetPrimaryEmailAddress(t *testing.T) { +func TestAdminAddOrSetPrimaryEmailAddress(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 27}) @@ -28,7 +30,7 @@ func TestAddOrSetPrimaryEmailAddress(t *testing.T) { assert.NotEqual(t, "new-primary@example.com", primary.Email) assert.Equal(t, user.Email, primary.Email) - assert.NoError(t, AddOrSetPrimaryEmailAddress(db.DefaultContext, user, "new-primary@example.com")) + assert.NoError(t, AdminAddOrSetPrimaryEmailAddress(db.DefaultContext, user, "new-primary@example.com")) primary, err = user_model.GetPrimaryEmailAddressOfUser(db.DefaultContext, user.ID) assert.NoError(t, err) @@ -39,7 +41,19 @@ func TestAddOrSetPrimaryEmailAddress(t *testing.T) { assert.NoError(t, err) assert.Len(t, emails, 2) - assert.NoError(t, AddOrSetPrimaryEmailAddress(db.DefaultContext, user, "user27@example.com")) + setting.Service.EmailDomainAllowList = []glob.Glob{glob.MustCompile("example.org")} + defer func() { + setting.Service.EmailDomainAllowList = []glob.Glob{} + }() + + assert.NoError(t, AdminAddOrSetPrimaryEmailAddress(db.DefaultContext, user, "new-primary2@example2.com")) + + primary, err = user_model.GetPrimaryEmailAddressOfUser(db.DefaultContext, user.ID) + assert.NoError(t, err) + assert.Equal(t, "new-primary2@example2.com", primary.Email) + assert.Equal(t, user.Email, primary.Email) + + assert.NoError(t, AdminAddOrSetPrimaryEmailAddress(db.DefaultContext, user, "user27@example.com")) primary, err = user_model.GetPrimaryEmailAddressOfUser(db.DefaultContext, user.ID) assert.NoError(t, err) @@ -48,7 +62,7 @@ func TestAddOrSetPrimaryEmailAddress(t *testing.T) { emails, err = user_model.GetEmailAddresses(db.DefaultContext, user.ID) assert.NoError(t, err) - assert.Len(t, emails, 2) + assert.Len(t, emails, 3) } func TestReplacePrimaryEmailAddress(t *testing.T) { diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index 16819f846a..32f1a3de45 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -32,36 +32,17 @@ import ( "github.com/gobwas/glob" ) -// Deliver deliver hook task -func Deliver(ctx context.Context, t *webhook_model.HookTask) error { - w, err := webhook_model.GetWebhookByID(ctx, t.HookID) - if err != nil { - return err - } - - defer func() { - err := recover() - if err == nil { - return - } - // There was a panic whilst delivering a hook... - log.Error("PANIC whilst trying to deliver webhook task[%d] to webhook %s Panic: %v\nStacktrace: %s", t.ID, w.URL, err, log.Stack(2)) - }() - - t.IsDelivered = true - - var req *http.Request - +func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) { switch w.HTTPMethod { case "": - log.Info("HTTP Method for webhook %s empty, setting to POST as default", w.URL) + log.Info("HTTP Method for %s webhook %s [ID: %d] is not set, defaulting to POST", w.Type, w.URL, w.ID) fallthrough case http.MethodPost: switch w.ContentType { case webhook_model.ContentTypeJSON: req, err = http.NewRequest("POST", w.URL, strings.NewReader(t.PayloadContent)) if err != nil { - return err + return nil, nil, err } req.Header.Set("Content-Type", "application/json") @@ -72,50 +53,58 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { req, err = http.NewRequest("POST", w.URL, strings.NewReader(forms.Encode())) if err != nil { - return err + return nil, nil, err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + default: + return nil, nil, fmt.Errorf("invalid content type: %v", w.ContentType) } case http.MethodGet: u, err := url.Parse(w.URL) if err != nil { - return fmt.Errorf("unable to deliver webhook task[%d] as cannot parse webhook url %s: %w", t.ID, w.URL, err) + return nil, nil, fmt.Errorf("invalid URL: %w", err) } vals := u.Query() vals["payload"] = []string{t.PayloadContent} u.RawQuery = vals.Encode() req, err = http.NewRequest("GET", u.String(), nil) if err != nil { - return fmt.Errorf("unable to deliver webhook task[%d] as unable to create HTTP request for webhook url %s: %w", t.ID, w.URL, err) + return nil, nil, err } case http.MethodPut: switch w.Type { - case webhook_module.MATRIX: + case webhook_module.MATRIX: // used when t.Version == 1 txnID, err := getMatrixTxnID([]byte(t.PayloadContent)) if err != nil { - return err + return nil, nil, err } url := fmt.Sprintf("%s/%s", w.URL, url.PathEscape(txnID)) req, err = http.NewRequest("PUT", url, strings.NewReader(t.PayloadContent)) if err != nil { - return fmt.Errorf("unable to deliver webhook task[%d] as cannot create matrix request for webhook url %s: %w", t.ID, w.URL, err) + return nil, nil, err } default: - return fmt.Errorf("invalid http method for webhook task[%d] in webhook %s: %v", t.ID, w.URL, w.HTTPMethod) + return nil, nil, fmt.Errorf("invalid http method: %v", w.HTTPMethod) } default: - return fmt.Errorf("invalid http method for webhook task[%d] in webhook %s: %v", t.ID, w.URL, w.HTTPMethod) + return nil, nil, fmt.Errorf("invalid http method: %v", w.HTTPMethod) } + body = []byte(t.PayloadContent) + return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body) +} + +func addDefaultHeaders(req *http.Request, secret []byte, t *webhook_model.HookTask, payloadContent []byte) error { var signatureSHA1 string var signatureSHA256 string - if len(w.Secret) > 0 { - sig1 := hmac.New(sha1.New, []byte(w.Secret)) - sig256 := hmac.New(sha256.New, []byte(w.Secret)) - _, err = io.MultiWriter(sig1, sig256).Write([]byte(t.PayloadContent)) + if len(secret) > 0 { + sig1 := hmac.New(sha1.New, secret) + sig256 := hmac.New(sha256.New, secret) + _, err := io.MultiWriter(sig1, sig256).Write(payloadContent) if err != nil { - log.Error("prepareWebhooks.sigWrite: %v", err) + // this error should never happen, since the hashes are writing to []byte and always return a nil error. + return fmt.Errorf("prepareWebhooks.sigWrite: %w", err) } signatureSHA1 = hex.EncodeToString(sig1.Sum(nil)) signatureSHA256 = hex.EncodeToString(sig256.Sum(nil)) @@ -140,15 +129,36 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { req.Header["X-GitHub-Delivery"] = []string{t.UUID} req.Header["X-GitHub-Event"] = []string{event} req.Header["X-GitHub-Event-Type"] = []string{eventType} + return nil +} - // Add Authorization Header - authorization, err := w.HeaderAuthorization() +// Deliver creates the [http.Request] (depending on the webhook type), sends it +// and records the status and response. +func Deliver(ctx context.Context, t *webhook_model.HookTask) error { + w, err := webhook_model.GetWebhookByID(ctx, t.HookID) if err != nil { - log.Error("Webhook could not get Authorization header [%d]: %v", w.ID, err) return err } - if authorization != "" { - req.Header["Authorization"] = []string{authorization} + + defer func() { + err := recover() + if err == nil { + return + } + // There was a panic whilst delivering a hook... + log.Error("PANIC whilst trying to deliver webhook task[%d] to webhook %s Panic: %v\nStacktrace: %s", t.ID, w.URL, err, log.Stack(2)) + }() + + t.IsDelivered = true + + newRequest := webhookRequesters[w.Type] + if t.PayloadVersion == 1 || newRequest == nil { + newRequest = newDefaultRequest + } + + req, body, err := newRequest(ctx, w, t) + if err != nil { + return fmt.Errorf("cannot create http request for webhook %s[%d %s]: %w", w.Type, w.ID, w.URL, err) } // Record delivery information. @@ -156,11 +166,22 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { URL: req.URL.String(), HTTPMethod: req.Method, Headers: map[string]string{}, + Body: string(body), } for k, vals := range req.Header { t.RequestInfo.Headers[k] = strings.Join(vals, ",") } + // Add Authorization Header + authorization, err := w.HeaderAuthorization() + if err != nil { + return fmt.Errorf("cannot get Authorization header for webhook %s[%d %s]: %w", w.Type, w.ID, w.URL, err) + } + if authorization != "" { + req.Header.Set("Authorization", authorization) + t.RequestInfo.Headers["Authorization"] = "******" + } + t.ResponseInfo = &webhook_model.HookResponse{ Headers: map[string]string{}, } diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go index eca2ba244b..bc06e43e03 100644 --- a/services/webhook/deliver_test.go +++ b/services/webhook/deliver_test.go @@ -5,10 +5,12 @@ package webhook import ( "context" + "io" "net/http" "net/http/httptest" "net/url" "os" + "strings" "testing" "time" @@ -17,7 +19,6 @@ import ( webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/hostmatcher" "code.gitea.io/gitea/modules/setting" - api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" "github.com/stretchr/testify/assert" @@ -114,13 +115,15 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) { assert.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, hook)) db.GetEngine(db.DefaultContext).NoAutoTime().DB().Logger.ShowSQL(true) - hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush, Payloader: &api.PushPayload{}} + hookTask := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadVersion: 2, + } hookTask, err = webhook_model.CreateHookTask(db.DefaultContext, hookTask) assert.NoError(t, err) - if !assert.NotNil(t, hookTask) { - return - } + assert.NotNil(t, hookTask) assert.NoError(t, Deliver(context.Background(), hookTask)) select { @@ -130,4 +133,202 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) { } assert.True(t, hookTask.IsSucceed) + assert.Equal(t, "******", hookTask.RequestInfo.Headers["Authorization"]) +} + +func TestWebhookDeliverHookTask(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + done := make(chan struct{}, 1) + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "PUT", r.Method) + switch r.URL.Path { + case "/webhook/66d222a5d6349e1311f551e50722d837e30fce98": + // Version 1 + assert.Equal(t, "push", r.Header.Get("X-GitHub-Event")) + assert.Equal(t, "", r.Header.Get("Content-Type")) + body, err := io.ReadAll(r.Body) + assert.NoError(t, err) + assert.Equal(t, `{"data": 42}`, string(body)) + + case "/webhook/6db5dc1e282529a8c162c7fe93dd2667494eeb51": + // Version 2 + assert.Equal(t, "push", r.Header.Get("X-GitHub-Event")) + assert.Equal(t, "application/json", r.Header.Get("Content-Type")) + body, err := io.ReadAll(r.Body) + assert.NoError(t, err) + assert.Len(t, body, 2147) + + default: + w.WriteHeader(404) + t.Fatalf("unexpected url path %s", r.URL.Path) + return + } + w.WriteHeader(200) + done <- struct{}{} + })) + t.Cleanup(s.Close) + + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.MATRIX, + URL: s.URL + "/webhook", + HTTPMethod: "PUT", + ContentType: webhook_model.ContentTypeJSON, + Meta: `{"message_type":0}`, // text + } + assert.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, hook)) + + t.Run("Version 1", func(t *testing.T) { + hookTask := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: `{"data": 42}`, + PayloadVersion: 1, + } + + hookTask, err := webhook_model.CreateHookTask(db.DefaultContext, hookTask) + assert.NoError(t, err) + assert.NotNil(t, hookTask) + + assert.NoError(t, Deliver(context.Background(), hookTask)) + select { + case <-done: + case <-time.After(5 * time.Second): + t.Fatal("waited to long for request to happen") + } + + assert.True(t, hookTask.IsSucceed) + }) + + t.Run("Version 2", func(t *testing.T) { + p := pushTestPayload() + data, err := p.JSONPayload() + assert.NoError(t, err) + + hookTask := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + hookTask, err = webhook_model.CreateHookTask(db.DefaultContext, hookTask) + assert.NoError(t, err) + assert.NotNil(t, hookTask) + + assert.NoError(t, Deliver(context.Background(), hookTask)) + select { + case <-done: + case <-time.After(5 * time.Second): + t.Fatal("waited to long for request to happen") + } + + assert.True(t, hookTask.IsSucceed) + }) +} + +func TestWebhookDeliverSpecificTypes(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + type hookCase struct { + gotBody chan []byte + expectedMethod string + } + + cases := map[string]hookCase{ + webhook_module.SLACK: { + gotBody: make(chan []byte, 1), + }, + webhook_module.DISCORD: { + gotBody: make(chan []byte, 1), + }, + webhook_module.DINGTALK: { + gotBody: make(chan []byte, 1), + }, + webhook_module.TELEGRAM: { + gotBody: make(chan []byte, 1), + }, + webhook_module.MSTEAMS: { + gotBody: make(chan []byte, 1), + }, + webhook_module.FEISHU: { + gotBody: make(chan []byte, 1), + }, + webhook_module.MATRIX: { + gotBody: make(chan []byte, 1), + expectedMethod: "PUT", + }, + webhook_module.WECHATWORK: { + gotBody: make(chan []byte, 1), + }, + webhook_module.PACKAGIST: { + gotBody: make(chan []byte, 1), + }, + } + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "application/json", r.Header.Get("Content-Type"), r.URL.Path) + + typ := strings.Split(r.URL.Path, "/")[1] // take first segment (after skipping leading slash) + hc := cases[typ] + + if hc.expectedMethod != "" { + assert.Equal(t, hc.expectedMethod, r.Method, r.URL.Path) + } else { + assert.Equal(t, "POST", r.Method, r.URL.Path) + } + + require.NotNil(t, hc.gotBody, r.URL.Path) + body, err := io.ReadAll(r.Body) + assert.NoError(t, err) + w.WriteHeader(200) + hc.gotBody <- body + })) + t.Cleanup(s.Close) + + p := pushTestPayload() + data, err := p.JSONPayload() + assert.NoError(t, err) + + for typ, hc := range cases { + typ := typ + hc := hc + t.Run(typ, func(t *testing.T) { + t.Parallel() + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: typ, + URL: s.URL + "/" + typ, + HTTPMethod: "", // should fallback to POST, when left unset by the specific hook + ContentType: 0, // set to 0 so that falling back to default request fails with "invalid content type" + Meta: "{}", + } + assert.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, hook)) + + hookTask := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + hookTask, err := webhook_model.CreateHookTask(db.DefaultContext, hookTask) + assert.NoError(t, err) + assert.NotNil(t, hookTask) + + assert.NoError(t, Deliver(context.Background(), hookTask)) + select { + case gotBody := <-hc.gotBody: + assert.NotEqual(t, string(data), string(gotBody), "request body must be different from the event payload") + assert.Equal(t, hookTask.RequestInfo.Body, string(gotBody), "request body was not saved") + case <-time.After(5 * time.Second): + t.Fatal("waited to long for request to happen") + } + + assert.True(t, hookTask.IsSucceed) + }) + } } diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go index d615e7254f..c57d04415a 100644 --- a/services/webhook/dingtalk.go +++ b/services/webhook/dingtalk.go @@ -4,12 +4,14 @@ package webhook import ( + "context" "fmt" + "net/http" "net/url" "strings" + webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -22,19 +24,8 @@ type ( DingtalkPayload dingtalk.Payload ) -var _ PayloadConvertor = &DingtalkPayload{} - -// JSONPayload Marshals the DingtalkPayload to json -func (d *DingtalkPayload) JSONPayload() ([]byte, error) { - data, err := json.MarshalIndent(d, "", " ") - if err != nil { - return []byte{}, err - } - return data, nil -} - // Create implements PayloadConvertor Create method -func (d *DingtalkPayload) Create(p *api.CreatePayload) (api.Payloader, error) { +func (dc dingtalkConvertor) Create(p *api.CreatePayload) (DingtalkPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) @@ -43,7 +34,7 @@ func (d *DingtalkPayload) Create(p *api.CreatePayload) (api.Payloader, error) { } // Delete implements PayloadConvertor Delete method -func (d *DingtalkPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { +func (dc dingtalkConvertor) Delete(p *api.DeletePayload) (DingtalkPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) @@ -52,14 +43,14 @@ func (d *DingtalkPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { } // Fork implements PayloadConvertor Fork method -func (d *DingtalkPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { +func (dc dingtalkConvertor) Fork(p *api.ForkPayload) (DingtalkPayload, error) { title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) return createDingtalkPayload(title, title, fmt.Sprintf("view forked repo %s", p.Repo.FullName), p.Repo.HTMLURL), nil } // Push implements PayloadConvertor Push method -func (d *DingtalkPayload) Push(p *api.PushPayload) (api.Payloader, error) { +func (dc dingtalkConvertor) Push(p *api.PushPayload) (DingtalkPayload, error) { var ( branchName = git.RefName(p.Ref).ShortName() commitDesc string @@ -100,14 +91,14 @@ func (d *DingtalkPayload) Push(p *api.PushPayload) (api.Payloader, error) { } // Issue implements PayloadConvertor Issue method -func (d *DingtalkPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { +func (dc dingtalkConvertor) Issue(p *api.IssuePayload) (DingtalkPayload, error) { text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true) return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+attachmentText, "view issue", p.Issue.HTMLURL), nil } // Wiki implements PayloadConvertor Wiki method -func (d *DingtalkPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { +func (dc dingtalkConvertor) Wiki(p *api.WikiPayload) (DingtalkPayload, error) { text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, true) url := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page) @@ -115,27 +106,27 @@ func (d *DingtalkPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { } // IssueComment implements PayloadConvertor IssueComment method -func (d *DingtalkPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { +func (dc dingtalkConvertor) IssueComment(p *api.IssueCommentPayload) (DingtalkPayload, error) { text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true) return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+p.Comment.Body, "view issue comment", p.Comment.HTMLURL), nil } // PullRequest implements PayloadConvertor PullRequest method -func (d *DingtalkPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { +func (dc dingtalkConvertor) PullRequest(p *api.PullRequestPayload) (DingtalkPayload, error) { text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true) return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+attachmentText, "view pull request", p.PullRequest.HTMLURL), nil } // Review implements PayloadConvertor Review method -func (d *DingtalkPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) { +func (dc dingtalkConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (DingtalkPayload, error) { var text, title string switch p.Action { case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { - return nil, err + return DingtalkPayload{}, err } title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) @@ -146,14 +137,14 @@ func (d *DingtalkPayload) Review(p *api.PullRequestPayload, event webhook_module } // Repository implements PayloadConvertor Repository method -func (d *DingtalkPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { +func (dc dingtalkConvertor) Repository(p *api.RepositoryPayload) (DingtalkPayload, error) { switch p.Action { case api.HookRepoCreated: title := fmt.Sprintf("[%s] Repository created", p.Repository.FullName) return createDingtalkPayload(title, title, "view repository", p.Repository.HTMLURL), nil case api.HookRepoDeleted: title := fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) - return &DingtalkPayload{ + return DingtalkPayload{ MsgType: "text", Text: struct { Content string `json:"content"` @@ -163,24 +154,24 @@ func (d *DingtalkPayload) Repository(p *api.RepositoryPayload) (api.Payloader, e }, nil } - return nil, nil + return DingtalkPayload{}, nil } // Release implements PayloadConvertor Release method -func (d *DingtalkPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { +func (dc dingtalkConvertor) Release(p *api.ReleasePayload) (DingtalkPayload, error) { text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true) return createDingtalkPayload(text, text, "view release", p.Release.HTMLURL), nil } -func (d *DingtalkPayload) Package(p *api.PackagePayload) (api.Payloader, error) { +func (dc dingtalkConvertor) Package(p *api.PackagePayload) (DingtalkPayload, error) { text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true) return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil } -func createDingtalkPayload(title, text, singleTitle, singleURL string) *DingtalkPayload { - return &DingtalkPayload{ +func createDingtalkPayload(title, text, singleTitle, singleURL string) DingtalkPayload { + return DingtalkPayload{ MsgType: "actionCard", ActionCard: dingtalk.ActionCard{ Text: strings.TrimSpace(text), @@ -195,7 +186,10 @@ func createDingtalkPayload(title, text, singleTitle, singleURL string) *Dingtalk } } -// GetDingtalkPayload converts a ding talk webhook into a DingtalkPayload -func GetDingtalkPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) { - return convertPayloader(new(DingtalkPayload), p, event) +type dingtalkConvertor struct{} + +var _ payloadConvertor[DingtalkPayload] = dingtalkConvertor{} + +func newDingtalkRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { + return newJSONRequest(dingtalkConvertor{}, w, t, true) } diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go index a03fa46f14..25f47347d0 100644 --- a/services/webhook/dingtalk_test.go +++ b/services/webhook/dingtalk_test.go @@ -4,9 +4,12 @@ package webhook import ( + "context" "net/url" "testing" + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -24,248 +27,226 @@ func TestDingTalkPayload(t *testing.T) { } return "" } + dc := dingtalkConvertor{} t.Run("Create", func(t *testing.T) { p := createTestPayload() - d := new(DingtalkPayload) - pl, err := d.Create(p) + pl, err := dc.Create(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] branch test created", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "[test/repo] branch test created", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view ref test", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/src/test", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] branch test created", pl.ActionCard.Text) + assert.Equal(t, "[test/repo] branch test created", pl.ActionCard.Title) + assert.Equal(t, "view ref test", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("Delete", func(t *testing.T) { p := deleteTestPayload() - d := new(DingtalkPayload) - pl, err := d.Delete(p) + pl, err := dc.Delete(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] branch test deleted", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "[test/repo] branch test deleted", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view ref test", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/src/test", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] branch test deleted", pl.ActionCard.Text) + assert.Equal(t, "[test/repo] branch test deleted", pl.ActionCard.Title) + assert.Equal(t, "view ref test", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("Fork", func(t *testing.T) { p := forkTestPayload() - d := new(DingtalkPayload) - pl, err := d.Fork(p) + pl, err := dc.Fork(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view forked repo test/repo", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "test/repo2 is forked to test/repo", pl.ActionCard.Text) + assert.Equal(t, "test/repo2 is forked to test/repo", pl.ActionCard.Title) + assert.Equal(t, "view forked repo test/repo", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("Push", func(t *testing.T) { p := pushTestPayload() - d := new(DingtalkPayload) - pl, err := d.Push(p) + pl, err := dc.Push(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view commits", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/src/test", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.ActionCard.Text) + assert.Equal(t, "[test/repo:test] 2 new commits", pl.ActionCard.Title) + assert.Equal(t, "view commits", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("Issue", func(t *testing.T) { p := issueTestPayload() - d := new(DingtalkPayload) p.Action = api.HookIssueOpened - pl, err := d.Issue(p) + pl, err := dc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] Issue opened: #2 crash by user1\r\n\r\nissue body", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "#2 crash", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view issue", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/issues/2", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] Issue opened: #2 crash by user1\r\n\r\nissue body", pl.ActionCard.Text) + assert.Equal(t, "#2 crash", pl.ActionCard.Title) + assert.Equal(t, "view issue", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", parseRealSingleURL(pl.ActionCard.SingleURL)) p.Action = api.HookIssueClosed - pl, err = d.Issue(p) + pl, err = dc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] Issue closed: #2 crash by user1", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "#2 crash", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view issue", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/issues/2", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] Issue closed: #2 crash by user1", pl.ActionCard.Text) + assert.Equal(t, "#2 crash", pl.ActionCard.Title) + assert.Equal(t, "view issue", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("IssueComment", func(t *testing.T) { p := issueCommentTestPayload() - d := new(DingtalkPayload) - pl, err := d.IssueComment(p) + pl, err := dc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] New comment on issue #2 crash by user1\r\n\r\nmore info needed", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "#2 crash", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view issue comment", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] New comment on issue #2 crash by user1\r\n\r\nmore info needed", pl.ActionCard.Text) + assert.Equal(t, "#2 crash", pl.ActionCard.Title) + assert.Equal(t, "view issue comment", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("PullRequest", func(t *testing.T) { p := pullRequestTestPayload() - d := new(DingtalkPayload) - pl, err := d.PullRequest(p) + pl, err := dc.PullRequest(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug by user1\r\n\r\nfixes bug #2", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "#12 Fix bug", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view pull request", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug by user1\r\n\r\nfixes bug #2", pl.ActionCard.Text) + assert.Equal(t, "#12 Fix bug", pl.ActionCard.Title) + assert.Equal(t, "view pull request", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("PullRequestComment", func(t *testing.T) { p := pullRequestCommentTestPayload() - d := new(DingtalkPayload) - pl, err := d.IssueComment(p) + pl, err := dc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug by user1\r\n\r\nchanges requested", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "#12 Fix bug", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view issue comment", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug by user1\r\n\r\nchanges requested", pl.ActionCard.Text) + assert.Equal(t, "#12 Fix bug", pl.ActionCard.Title) + assert.Equal(t, "view issue comment", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("Review", func(t *testing.T) { p := pullRequestTestPayload() p.Action = api.HookIssueReviewed - d := new(DingtalkPayload) - pl, err := d.Review(p, webhook_module.HookEventPullRequestReviewApproved) + pl, err := dc.Review(p, webhook_module.HookEventPullRequestReviewApproved) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] Pull request review approved : #12 Fix bug\r\n\r\ngood job", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "[test/repo] Pull request review approved : #12 Fix bug", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view pull request", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] Pull request review approved : #12 Fix bug\r\n\r\ngood job", pl.ActionCard.Text) + assert.Equal(t, "[test/repo] Pull request review approved : #12 Fix bug", pl.ActionCard.Title) + assert.Equal(t, "view pull request", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("Repository", func(t *testing.T) { p := repositoryTestPayload() - d := new(DingtalkPayload) - pl, err := d.Repository(p) + pl, err := dc.Repository(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] Repository created", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "[test/repo] Repository created", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view repository", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] Repository created", pl.ActionCard.Text) + assert.Equal(t, "[test/repo] Repository created", pl.ActionCard.Title) + assert.Equal(t, "view repository", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("Package", func(t *testing.T) { p := packageTestPayload() - d := new(DingtalkPayload) - pl, err := d.Package(p) + pl, err := dc.Package(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view package", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.ActionCard.Text) + assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.ActionCard.Title) + assert.Equal(t, "view package", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() - d := new(DingtalkPayload) p.Action = api.HookWikiCreated - pl, err := d.Wiki(p) + pl, err := dc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view wiki", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.ActionCard.Text) + assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.ActionCard.Title) + assert.Equal(t, "view wiki", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.ActionCard.SingleURL)) p.Action = api.HookWikiEdited - pl, err = d.Wiki(p) + pl, err = dc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view wiki", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.ActionCard.Text) + assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.ActionCard.Title) + assert.Equal(t, "view wiki", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.ActionCard.SingleURL)) p.Action = api.HookWikiDeleted - pl, err = d.Wiki(p) + pl, err = dc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view wiki", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.ActionCard.Text) + assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.ActionCard.Title) + assert.Equal(t, "view wiki", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.ActionCard.SingleURL)) }) t.Run("Release", func(t *testing.T) { p := pullReleaseTestPayload() - d := new(DingtalkPayload) - pl, err := d.Release(p) + pl, err := dc.Release(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*DingtalkPayload).ActionCard.Text) - assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view release", pl.(*DingtalkPayload).ActionCard.SingleTitle) - assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.ActionCard.Text) + assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.ActionCard.Title) + assert.Equal(t, "view release", pl.ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", parseRealSingleURL(pl.ActionCard.SingleURL)) }) } func TestDingTalkJSONPayload(t *testing.T) { p := pushTestPayload() - - pl, err := new(DingtalkPayload).Push(p) + data, err := p.JSONPayload() require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DingtalkPayload{}, pl) - json, err := pl.JSONPayload() + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.DINGTALK, + URL: "https://dingtalk.example.com/", + Meta: ``, + HTTPMethod: "POST", + } + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := newDingtalkRequest(context.Background(), hook, task) + require.NotNil(t, req) + require.NotNil(t, reqBody) require.NoError(t, err) - assert.NotEmpty(t, json) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://dingtalk.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body DingtalkPayload + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", body.ActionCard.Text) } diff --git a/services/webhook/discord.go b/services/webhook/discord.go index e2ac1410b8..659754d5e0 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -4,8 +4,10 @@ package webhook import ( + "context" "errors" "fmt" + "net/http" "net/url" "strconv" "strings" @@ -98,19 +100,8 @@ var ( redColor = color("ff3232") ) -// JSONPayload Marshals the DiscordPayload to json -func (d *DiscordPayload) JSONPayload() ([]byte, error) { - data, err := json.MarshalIndent(d, "", " ") - if err != nil { - return []byte{}, err - } - return data, nil -} - -var _ PayloadConvertor = &DiscordPayload{} - // Create implements PayloadConvertor Create method -func (d *DiscordPayload) Create(p *api.CreatePayload) (api.Payloader, error) { +func (d discordConvertor) Create(p *api.CreatePayload) (DiscordPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) @@ -119,7 +110,7 @@ func (d *DiscordPayload) Create(p *api.CreatePayload) (api.Payloader, error) { } // Delete implements PayloadConvertor Delete method -func (d *DiscordPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { +func (d discordConvertor) Delete(p *api.DeletePayload) (DiscordPayload, error) { // deleted tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) @@ -128,14 +119,14 @@ func (d *DiscordPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { } // Fork implements PayloadConvertor Fork method -func (d *DiscordPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { +func (d discordConvertor) Fork(p *api.ForkPayload) (DiscordPayload, error) { title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL, greenColor), nil } // Push implements PayloadConvertor Push method -func (d *DiscordPayload) Push(p *api.PushPayload) (api.Payloader, error) { +func (d discordConvertor) Push(p *api.PushPayload) (DiscordPayload, error) { var ( branchName = git.RefName(p.Ref).ShortName() commitDesc string @@ -170,35 +161,35 @@ func (d *DiscordPayload) Push(p *api.PushPayload) (api.Payloader, error) { } // Issue implements PayloadConvertor Issue method -func (d *DiscordPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { +func (d discordConvertor) Issue(p *api.IssuePayload) (DiscordPayload, error) { title, _, text, color := getIssuesPayloadInfo(p, noneLinkFormatter, false) return d.createPayload(p.Sender, title, text, p.Issue.HTMLURL, color), nil } // IssueComment implements PayloadConvertor IssueComment method -func (d *DiscordPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { +func (d discordConvertor) IssueComment(p *api.IssueCommentPayload) (DiscordPayload, error) { title, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false) return d.createPayload(p.Sender, title, p.Comment.Body, p.Comment.HTMLURL, color), nil } // PullRequest implements PayloadConvertor PullRequest method -func (d *DiscordPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { +func (d discordConvertor) PullRequest(p *api.PullRequestPayload) (DiscordPayload, error) { title, _, text, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false) return d.createPayload(p.Sender, title, text, p.PullRequest.HTMLURL, color), nil } // Review implements PayloadConvertor Review method -func (d *DiscordPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) { +func (d discordConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (DiscordPayload, error) { var text, title string var color int switch p.Action { case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { - return nil, err + return DiscordPayload{}, err } title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) @@ -220,7 +211,7 @@ func (d *DiscordPayload) Review(p *api.PullRequestPayload, event webhook_module. } // Repository implements PayloadConvertor Repository method -func (d *DiscordPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { +func (d discordConvertor) Repository(p *api.RepositoryPayload) (DiscordPayload, error) { var title, url string var color int switch p.Action { @@ -237,7 +228,7 @@ func (d *DiscordPayload) Repository(p *api.RepositoryPayload) (api.Payloader, er } // Wiki implements PayloadConvertor Wiki method -func (d *DiscordPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { +func (d discordConvertor) Wiki(p *api.WikiPayload) (DiscordPayload, error) { text, color, _ := getWikiPayloadInfo(p, noneLinkFormatter, false) htmlLink := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page) @@ -250,30 +241,35 @@ func (d *DiscordPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { } // Release implements PayloadConvertor Release method -func (d *DiscordPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { +func (d discordConvertor) Release(p *api.ReleasePayload) (DiscordPayload, error) { text, color := getReleasePayloadInfo(p, noneLinkFormatter, false) return d.createPayload(p.Sender, text, p.Release.Note, p.Release.HTMLURL, color), nil } -func (d *DiscordPayload) Package(p *api.PackagePayload) (api.Payloader, error) { +func (d discordConvertor) Package(p *api.PackagePayload) (DiscordPayload, error) { text, color := getPackagePayloadInfo(p, noneLinkFormatter, false) return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil } -// GetDiscordPayload converts a discord webhook into a DiscordPayload -func GetDiscordPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) { - s := new(DiscordPayload) +type discordConvertor struct { + Username string + AvatarURL string +} - discord := &DiscordMeta{} - if err := json.Unmarshal([]byte(meta), &discord); err != nil { - return s, errors.New("GetDiscordPayload meta json:" + err.Error()) +var _ payloadConvertor[DiscordPayload] = discordConvertor{} + +func newDiscordRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { + meta := &DiscordMeta{} + if err := json.Unmarshal([]byte(w.Meta), meta); err != nil { + return nil, nil, fmt.Errorf("newDiscordRequest meta json: %w", err) } - s.Username = discord.Username - s.AvatarURL = discord.IconURL - - return convertPayloader(s, p, event) + sc := discordConvertor{ + Username: meta.Username, + AvatarURL: meta.IconURL, + } + return newJSONRequest(sc, w, t, true) } func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, error) { @@ -291,8 +287,8 @@ func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, } } -func (d *DiscordPayload) createPayload(s *api.User, title, text, url string, color int) *DiscordPayload { - return &DiscordPayload{ +func (d discordConvertor) createPayload(s *api.User, title, text, url string, color int) DiscordPayload { + return DiscordPayload{ Username: d.Username, AvatarURL: d.AvatarURL, Embeds: []DiscordEmbed{ diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go index b567cbc395..c04b95383b 100644 --- a/services/webhook/discord_test.go +++ b/services/webhook/discord_test.go @@ -4,8 +4,11 @@ package webhook import ( + "context" "testing" + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -15,295 +18,274 @@ import ( ) func TestDiscordPayload(t *testing.T) { + dc := discordConvertor{} + t.Run("Create", func(t *testing.T) { p := createTestPayload() - d := new(DiscordPayload) - pl, err := d.Create(p) + pl, err := dc.Create(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] branch test created", pl.(*DiscordPayload).Embeds[0].Title) - assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] branch test created", pl.Embeds[0].Title) + assert.Empty(t, pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("Delete", func(t *testing.T) { p := deleteTestPayload() - d := new(DiscordPayload) - pl, err := d.Delete(p) + pl, err := dc.Delete(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] branch test deleted", pl.(*DiscordPayload).Embeds[0].Title) - assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] branch test deleted", pl.Embeds[0].Title) + assert.Empty(t, pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("Fork", func(t *testing.T) { p := forkTestPayload() - d := new(DiscordPayload) - pl, err := d.Fork(p) + pl, err := dc.Fork(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*DiscordPayload).Embeds[0].Title) - assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "test/repo2 is forked to test/repo", pl.Embeds[0].Title) + assert.Empty(t, pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("Push", func(t *testing.T) { p := pushTestPayload() - d := new(DiscordPayload) - pl, err := d.Push(p) + pl, err := dc.Push(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*DiscordPayload).Embeds[0].Title) - assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo:test] 2 new commits", pl.Embeds[0].Title) + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("Issue", func(t *testing.T) { p := issueTestPayload() - d := new(DiscordPayload) p.Action = api.HookIssueOpened - pl, err := d.Issue(p) + pl, err := dc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.(*DiscordPayload).Embeds[0].Title) - assert.Equal(t, "issue body", pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.Embeds[0].Title) + assert.Equal(t, "issue body", pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) p.Action = api.HookIssueClosed - pl, err = d.Issue(p) + pl, err = dc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.(*DiscordPayload).Embeds[0].Title) - assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.Embeds[0].Title) + assert.Empty(t, pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("IssueComment", func(t *testing.T) { p := issueCommentTestPayload() - d := new(DiscordPayload) - pl, err := d.IssueComment(p) + pl, err := dc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.(*DiscordPayload).Embeds[0].Title) - assert.Equal(t, "more info needed", pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.Embeds[0].Title) + assert.Equal(t, "more info needed", pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("PullRequest", func(t *testing.T) { p := pullRequestTestPayload() - d := new(DiscordPayload) - pl, err := d.PullRequest(p) + pl, err := dc.PullRequest(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.(*DiscordPayload).Embeds[0].Title) - assert.Equal(t, "fixes bug #2", pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.Embeds[0].Title) + assert.Equal(t, "fixes bug #2", pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("PullRequestComment", func(t *testing.T) { p := pullRequestCommentTestPayload() - d := new(DiscordPayload) - pl, err := d.IssueComment(p) + pl, err := dc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.(*DiscordPayload).Embeds[0].Title) - assert.Equal(t, "changes requested", pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.Embeds[0].Title) + assert.Equal(t, "changes requested", pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("Review", func(t *testing.T) { p := pullRequestTestPayload() p.Action = api.HookIssueReviewed - d := new(DiscordPayload) - pl, err := d.Review(p, webhook_module.HookEventPullRequestReviewApproved) + pl, err := dc.Review(p, webhook_module.HookEventPullRequestReviewApproved) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.(*DiscordPayload).Embeds[0].Title) - assert.Equal(t, "good job", pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.Embeds[0].Title) + assert.Equal(t, "good job", pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("Repository", func(t *testing.T) { p := repositoryTestPayload() - d := new(DiscordPayload) - pl, err := d.Repository(p) + pl, err := dc.Repository(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] Repository created", pl.(*DiscordPayload).Embeds[0].Title) - assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] Repository created", pl.Embeds[0].Title) + assert.Empty(t, pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("Package", func(t *testing.T) { p := packageTestPayload() - d := new(DiscordPayload) - pl, err := d.Package(p) + pl, err := dc.Package(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "Package created: GiteaContainer:latest", pl.(*DiscordPayload).Embeds[0].Title) - assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "Package created: GiteaContainer:latest", pl.Embeds[0].Title) + assert.Empty(t, pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() - d := new(DiscordPayload) p.Action = api.HookWikiCreated - pl, err := d.Wiki(p) + pl, err := dc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.(*DiscordPayload).Embeds[0].Title) - assert.Equal(t, "Wiki change comment", pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.Embeds[0].Title) + assert.Equal(t, "Wiki change comment", pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) p.Action = api.HookWikiEdited - pl, err = d.Wiki(p) + pl, err = dc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.(*DiscordPayload).Embeds[0].Title) - assert.Equal(t, "Wiki change comment", pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.Embeds[0].Title) + assert.Equal(t, "Wiki change comment", pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) p.Action = api.HookWikiDeleted - pl, err = d.Wiki(p) + pl, err = dc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.(*DiscordPayload).Embeds[0].Title) - assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.Embeds[0].Title) + assert.Empty(t, pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) t.Run("Release", func(t *testing.T) { p := pullReleaseTestPayload() - d := new(DiscordPayload) - pl, err := d.Release(p) + pl, err := dc.Release(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - assert.Len(t, pl.(*DiscordPayload).Embeds, 1) - assert.Equal(t, "[test/repo] Release created: v1.0", pl.(*DiscordPayload).Embeds[0].Title) - assert.Equal(t, "Note of first stable release", pl.(*DiscordPayload).Embeds[0].Description) - assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", pl.(*DiscordPayload).Embeds[0].URL) - assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) - assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) - assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + assert.Len(t, pl.Embeds, 1) + assert.Equal(t, "[test/repo] Release created: v1.0", pl.Embeds[0].Title) + assert.Equal(t, "Note of first stable release", pl.Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", pl.Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) }) } func TestDiscordJSONPayload(t *testing.T) { p := pushTestPayload() - - pl, err := new(DiscordPayload).Push(p) + data, err := p.JSONPayload() require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &DiscordPayload{}, pl) - json, err := pl.JSONPayload() + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.DISCORD, + URL: "https://discord.example.com/", + Meta: `{}`, + HTTPMethod: "POST", + } + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := newDiscordRequest(context.Background(), hook, task) + require.NotNil(t, req) + require.NotNil(t, reqBody) require.NoError(t, err) - assert.NotEmpty(t, json) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://discord.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body DiscordPayload + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", body.Embeds[0].Description) } diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go index 556443e70b..1ec436894b 100644 --- a/services/webhook/feishu.go +++ b/services/webhook/feishu.go @@ -4,11 +4,13 @@ package webhook import ( + "context" "fmt" + "net/http" "strings" + webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" ) @@ -23,8 +25,8 @@ type ( } ) -func newFeishuTextPayload(text string) *FeishuPayload { - return &FeishuPayload{ +func newFeishuTextPayload(text string) FeishuPayload { + return FeishuPayload{ MsgType: "text", Content: struct { Text string `json:"text"` @@ -34,19 +36,8 @@ func newFeishuTextPayload(text string) *FeishuPayload { } } -// JSONPayload Marshals the FeishuPayload to json -func (f *FeishuPayload) JSONPayload() ([]byte, error) { - data, err := json.MarshalIndent(f, "", " ") - if err != nil { - return []byte{}, err - } - return data, nil -} - -var _ PayloadConvertor = &FeishuPayload{} - // Create implements PayloadConvertor Create method -func (f *FeishuPayload) Create(p *api.CreatePayload) (api.Payloader, error) { +func (fc feishuConvertor) Create(p *api.CreatePayload) (FeishuPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() text := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) @@ -55,7 +46,7 @@ func (f *FeishuPayload) Create(p *api.CreatePayload) (api.Payloader, error) { } // Delete implements PayloadConvertor Delete method -func (f *FeishuPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { +func (fc feishuConvertor) Delete(p *api.DeletePayload) (FeishuPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() text := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) @@ -64,14 +55,14 @@ func (f *FeishuPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { } // Fork implements PayloadConvertor Fork method -func (f *FeishuPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { +func (fc feishuConvertor) Fork(p *api.ForkPayload) (FeishuPayload, error) { text := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) return newFeishuTextPayload(text), nil } // Push implements PayloadConvertor Push method -func (f *FeishuPayload) Push(p *api.PushPayload) (api.Payloader, error) { +func (fc feishuConvertor) Push(p *api.PushPayload) (FeishuPayload, error) { var ( branchName = git.RefName(p.Ref).ShortName() commitDesc string @@ -96,48 +87,40 @@ func (f *FeishuPayload) Push(p *api.PushPayload) (api.Payloader, error) { } // Issue implements PayloadConvertor Issue method -func (f *FeishuPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { +func (fc feishuConvertor) Issue(p *api.IssuePayload) (FeishuPayload, error) { title, link, by, operator, result, assignees := getIssuesInfo(p) - var res api.Payloader if assignees != "" { if p.Action == api.HookIssueAssigned || p.Action == api.HookIssueUnassigned || p.Action == api.HookIssueMilestoned { - res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.Issue.Body)) - } else { - res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.Issue.Body)) + return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.Issue.Body)), nil } - } else { - res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.Issue.Body)) + return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.Issue.Body)), nil } - return res, nil + return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.Issue.Body)), nil } // IssueComment implements PayloadConvertor IssueComment method -func (f *FeishuPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { +func (fc feishuConvertor) IssueComment(p *api.IssueCommentPayload) (FeishuPayload, error) { title, link, by, operator := getIssuesCommentInfo(p) return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.Comment.Body)), nil } // PullRequest implements PayloadConvertor PullRequest method -func (f *FeishuPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { +func (fc feishuConvertor) PullRequest(p *api.PullRequestPayload) (FeishuPayload, error) { title, link, by, operator, result, assignees := getPullRequestInfo(p) - var res api.Payloader if assignees != "" { if p.Action == api.HookIssueAssigned || p.Action == api.HookIssueUnassigned || p.Action == api.HookIssueMilestoned { - res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.PullRequest.Body)) - } else { - res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.PullRequest.Body)) + return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.PullRequest.Body)), nil } - } else { - res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.PullRequest.Body)) + return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.PullRequest.Body)), nil } - return res, nil + return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.PullRequest.Body)), nil } // Review implements PayloadConvertor Review method -func (f *FeishuPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) { +func (fc feishuConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (FeishuPayload, error) { action, err := parseHookPullRequestEventType(event) if err != nil { - return nil, err + return FeishuPayload{}, err } title := fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) @@ -147,7 +130,7 @@ func (f *FeishuPayload) Review(p *api.PullRequestPayload, event webhook_module.H } // Repository implements PayloadConvertor Repository method -func (f *FeishuPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { +func (fc feishuConvertor) Repository(p *api.RepositoryPayload) (FeishuPayload, error) { var text string switch p.Action { case api.HookRepoCreated: @@ -158,30 +141,33 @@ func (f *FeishuPayload) Repository(p *api.RepositoryPayload) (api.Payloader, err return newFeishuTextPayload(text), nil } - return nil, nil + return FeishuPayload{}, nil } // Wiki implements PayloadConvertor Wiki method -func (f *FeishuPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { +func (fc feishuConvertor) Wiki(p *api.WikiPayload) (FeishuPayload, error) { text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, true) return newFeishuTextPayload(text), nil } // Release implements PayloadConvertor Release method -func (f *FeishuPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { +func (fc feishuConvertor) Release(p *api.ReleasePayload) (FeishuPayload, error) { text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true) return newFeishuTextPayload(text), nil } -func (f *FeishuPayload) Package(p *api.PackagePayload) (api.Payloader, error) { +func (fc feishuConvertor) Package(p *api.PackagePayload) (FeishuPayload, error) { text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true) return newFeishuTextPayload(text), nil } -// GetFeishuPayload converts a ding talk webhook into a FeishuPayload -func GetFeishuPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) { - return convertPayloader(new(FeishuPayload), p, event) +type feishuConvertor struct{} + +var _ payloadConvertor[FeishuPayload] = feishuConvertor{} + +func newFeishuRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { + return newJSONRequest(feishuConvertor{}, w, t, true) } diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go index 98bc50dede..ef18333fd4 100644 --- a/services/webhook/feishu_test.go +++ b/services/webhook/feishu_test.go @@ -4,8 +4,11 @@ package webhook import ( + "context" "testing" + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -14,199 +17,177 @@ import ( ) func TestFeishuPayload(t *testing.T) { + fc := feishuConvertor{} t.Run("Create", func(t *testing.T) { p := createTestPayload() - d := new(FeishuPayload) - pl, err := d.Create(p) + pl, err := fc.Create(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, `[test/repo] branch test created`, pl.(*FeishuPayload).Content.Text) + assert.Equal(t, `[test/repo] branch test created`, pl.Content.Text) }) t.Run("Delete", func(t *testing.T) { p := deleteTestPayload() - d := new(FeishuPayload) - pl, err := d.Delete(p) + pl, err := fc.Delete(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, `[test/repo] branch test deleted`, pl.(*FeishuPayload).Content.Text) + assert.Equal(t, `[test/repo] branch test deleted`, pl.Content.Text) }) t.Run("Fork", func(t *testing.T) { p := forkTestPayload() - d := new(FeishuPayload) - pl, err := d.Fork(p) + pl, err := fc.Fork(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, `test/repo2 is forked to test/repo`, pl.(*FeishuPayload).Content.Text) + assert.Equal(t, `test/repo2 is forked to test/repo`, pl.Content.Text) }) t.Run("Push", func(t *testing.T) { p := pushTestPayload() - d := new(FeishuPayload) - pl, err := d.Push(p) + pl, err := fc.Push(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[test/repo:test] \r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[test/repo:test] \r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.Content.Text) }) t.Run("Issue", func(t *testing.T) { p := issueTestPayload() - d := new(FeishuPayload) p.Action = api.HookIssueOpened - pl, err := d.Issue(p) + pl, err := fc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[Issue-test/repo #2]: opened\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Issue-test/repo #2]: opened\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.Content.Text) p.Action = api.HookIssueClosed - pl, err = d.Issue(p) + pl, err = fc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[Issue-test/repo #2]: closed\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Issue-test/repo #2]: closed\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.Content.Text) }) t.Run("IssueComment", func(t *testing.T) { p := issueCommentTestPayload() - d := new(FeishuPayload) - pl, err := d.IssueComment(p) + pl, err := fc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[Comment-test/repo #2]: created\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\n\nmore info needed", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Comment-test/repo #2]: created\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\n\nmore info needed", pl.Content.Text) }) t.Run("PullRequest", func(t *testing.T) { p := pullRequestTestPayload() - d := new(FeishuPayload) - pl, err := d.PullRequest(p) + pl, err := fc.PullRequest(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[PullRequest-test/repo #12]: opened\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\nAssignees: user1\n\nfixes bug #2", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[PullRequest-test/repo #12]: opened\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\nAssignees: user1\n\nfixes bug #2", pl.Content.Text) }) t.Run("PullRequestComment", func(t *testing.T) { p := pullRequestCommentTestPayload() - d := new(FeishuPayload) - pl, err := d.IssueComment(p) + pl, err := fc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[Comment-test/repo #12]: created\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\n\nchanges requested", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Comment-test/repo #12]: created\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\n\nchanges requested", pl.Content.Text) }) t.Run("Review", func(t *testing.T) { p := pullRequestTestPayload() p.Action = api.HookIssueReviewed - d := new(FeishuPayload) - pl, err := d.Review(p, webhook_module.HookEventPullRequestReviewApproved) + pl, err := fc.Review(p, webhook_module.HookEventPullRequestReviewApproved) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[test/repo] Pull request review approved : #12 Fix bug\r\n\r\ngood job", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[test/repo] Pull request review approved : #12 Fix bug\r\n\r\ngood job", pl.Content.Text) }) t.Run("Repository", func(t *testing.T) { p := repositoryTestPayload() - d := new(FeishuPayload) - pl, err := d.Repository(p) + pl, err := fc.Repository(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[test/repo] Repository created", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[test/repo] Repository created", pl.Content.Text) }) t.Run("Package", func(t *testing.T) { p := packageTestPayload() - d := new(FeishuPayload) - pl, err := d.Package(p) + pl, err := fc.Package(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.Content.Text) }) t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() - d := new(FeishuPayload) p.Action = api.HookWikiCreated - pl, err := d.Wiki(p) + pl, err := fc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.Content.Text) p.Action = api.HookWikiEdited - pl, err = d.Wiki(p) + pl, err = fc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.Content.Text) p.Action = api.HookWikiDeleted - pl, err = d.Wiki(p) + pl, err = fc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.Content.Text) }) t.Run("Release", func(t *testing.T) { p := pullReleaseTestPayload() - d := new(FeishuPayload) - pl, err := d.Release(p) + pl, err := fc.Release(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[test/repo] Release created: v1.0 by user1", pl.Content.Text) }) } func TestFeishuJSONPayload(t *testing.T) { p := pushTestPayload() - - pl, err := new(FeishuPayload).Push(p) + data, err := p.JSONPayload() require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &FeishuPayload{}, pl) - json, err := pl.JSONPayload() + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.FEISHU, + URL: "https://feishu.example.com/", + Meta: `{}`, + HTTPMethod: "POST", + } + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := newFeishuRequest(context.Background(), hook, task) + require.NotNil(t, req) + require.NotNil(t, reqBody) require.NoError(t, err) - assert.NotEmpty(t, json) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://feishu.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body FeishuPayload + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "[test/repo:test] \r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", body.Content.Text) } diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index 602d16ef39..0329804a8b 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -4,11 +4,12 @@ package webhook import ( + "bytes" + "context" "crypto/sha1" "encoding/hex" - "errors" "fmt" - "html" + "net/http" "net/url" "regexp" "strings" @@ -23,6 +24,37 @@ import ( webhook_module "code.gitea.io/gitea/modules/webhook" ) +func newMatrixRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { + meta := &MatrixMeta{} + if err := json.Unmarshal([]byte(w.Meta), meta); err != nil { + return nil, nil, fmt.Errorf("GetMatrixPayload meta json: %w", err) + } + mc := matrixConvertor{ + MsgType: messageTypeText[meta.MessageType], + } + payload, err := newPayload(mc, []byte(t.PayloadContent), t.EventType) + if err != nil { + return nil, nil, err + } + + body, err := json.MarshalIndent(payload, "", " ") + if err != nil { + return nil, nil, err + } + + txnID, err := getMatrixTxnID(body) + if err != nil { + return nil, nil, err + } + req, err := http.NewRequest(http.MethodPut, w.URL+"/"+txnID, bytes.NewReader(body)) + if err != nil { + return nil, nil, err + } + req.Header.Set("Content-Type", "application/json") + + return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body) // likely useless, but has always been sent historially +} + const matrixPayloadSizeLimit = 1024 * 64 // MatrixMeta contains the Matrix metadata @@ -46,8 +78,6 @@ func GetMatrixHook(w *webhook_model.Webhook) *MatrixMeta { return s } -var _ PayloadConvertor = &MatrixPayload{} - // MatrixPayload contains payload for a Matrix room type MatrixPayload struct { Body string `json:"body"` @@ -57,90 +87,79 @@ type MatrixPayload struct { Commits []*api.PayloadCommit `json:"io.gitea.commits,omitempty"` } -// JSONPayload Marshals the MatrixPayload to json -func (m *MatrixPayload) JSONPayload() ([]byte, error) { - data, err := json.MarshalIndent(m, "", " ") - if err != nil { - return []byte{}, err - } - return data, nil +var _ payloadConvertor[MatrixPayload] = matrixConvertor{} + +type matrixConvertor struct { + MsgType string } -// MatrixLinkFormatter creates a link compatible with Matrix -func MatrixLinkFormatter(url, text string) string { - return fmt.Sprintf(`%s`, html.EscapeString(url), html.EscapeString(text)) +func (m matrixConvertor) newPayload(text string, commits ...*api.PayloadCommit) (MatrixPayload, error) { + return MatrixPayload{ + Body: getMessageBody(text), + MsgType: m.MsgType, + Format: "org.matrix.custom.html", + FormattedBody: text, + Commits: commits, + }, nil } -// MatrixLinkToRef Matrix-formatter link to a repo ref -func MatrixLinkToRef(repoURL, ref string) string { - refName := git.RefName(ref).ShortName() - switch { - case strings.HasPrefix(ref, git.BranchPrefix): - return MatrixLinkFormatter(repoURL+"/src/branch/"+util.PathEscapeSegments(refName), refName) - case strings.HasPrefix(ref, git.TagPrefix): - return MatrixLinkFormatter(repoURL+"/src/tag/"+util.PathEscapeSegments(refName), refName) - default: - return MatrixLinkFormatter(repoURL+"/src/commit/"+util.PathEscapeSegments(refName), refName) - } -} - -// Create implements PayloadConvertor Create method -func (m *MatrixPayload) Create(p *api.CreatePayload) (api.Payloader, error) { - repoLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) +// Create implements payloadConvertor Create method +func (m matrixConvertor) Create(p *api.CreatePayload) (MatrixPayload, error) { + repoLink := htmlLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) refLink := MatrixLinkToRef(p.Repo.HTMLURL, p.Ref) text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName) - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } // Delete composes Matrix payload for delete a branch or tag. -func (m *MatrixPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { +func (m matrixConvertor) Delete(p *api.DeletePayload) (MatrixPayload, error) { refName := git.RefName(p.Ref).ShortName() - repoLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) + repoLink := htmlLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName) - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } // Fork composes Matrix payload for forked by a repository. -func (m *MatrixPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { - baseLink := MatrixLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName) - forkLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) +func (m matrixConvertor) Fork(p *api.ForkPayload) (MatrixPayload, error) { + baseLink := htmlLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName) + forkLink := htmlLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink) - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } -// Issue implements PayloadConvertor Issue method -func (m *MatrixPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { - text, _, _, _ := getIssuesPayloadInfo(p, MatrixLinkFormatter, true) +// Issue implements payloadConvertor Issue method +func (m matrixConvertor) Issue(p *api.IssuePayload) (MatrixPayload, error) { + text, _, _, _ := getIssuesPayloadInfo(p, htmlLinkFormatter, true) - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } -// IssueComment implements PayloadConvertor IssueComment method -func (m *MatrixPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { - text, _, _ := getIssueCommentPayloadInfo(p, MatrixLinkFormatter, true) +// IssueComment implements payloadConvertor IssueComment method +func (m matrixConvertor) IssueComment(p *api.IssueCommentPayload) (MatrixPayload, error) { + text, _, _ := getIssueCommentPayloadInfo(p, htmlLinkFormatter, true) - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } -// Wiki implements PayloadConvertor Wiki method -func (m *MatrixPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { - text, _, _ := getWikiPayloadInfo(p, MatrixLinkFormatter, true) +// Wiki implements payloadConvertor Wiki method +func (m matrixConvertor) Wiki(p *api.WikiPayload) (MatrixPayload, error) { + text, _, _ := getWikiPayloadInfo(p, htmlLinkFormatter, true) - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } -// Release implements PayloadConvertor Release method -func (m *MatrixPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { - text, _ := getReleasePayloadInfo(p, MatrixLinkFormatter, true) +// Release implements payloadConvertor Release method +func (m matrixConvertor) Release(p *api.ReleasePayload) (MatrixPayload, error) { + text, _ := getReleasePayloadInfo(p, htmlLinkFormatter, true) - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } -// Push implements PayloadConvertor Push method -func (m *MatrixPayload) Push(p *api.PushPayload) (api.Payloader, error) { +// Push implements payloadConvertor Push method +func (m matrixConvertor) Push(p *api.PushPayload) (MatrixPayload, error) { var commitDesc string if p.TotalCommits == 1 { @@ -149,13 +168,13 @@ func (m *MatrixPayload) Push(p *api.PushPayload) (api.Payloader, error) { commitDesc = fmt.Sprintf("%d commits", p.TotalCommits) } - repoLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) + repoLink := htmlLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) branchLink := MatrixLinkToRef(p.Repo.HTMLURL, p.Ref) text := fmt.Sprintf("[%s] %s pushed %s to %s:
      ", repoLink, p.Pusher.UserName, commitDesc, branchLink) // for each commit, generate a new line text for i, commit := range p.Commits { - text += fmt.Sprintf("%s: %s - %s", MatrixLinkFormatter(commit.URL, commit.ID[:7]), commit.Message, commit.Author.Name) + text += fmt.Sprintf("%s: %s - %s", htmlLinkFormatter(commit.URL, commit.ID[:7]), commit.Message, commit.Author.Name) // add linebreak to each commit but the last if i < len(p.Commits)-1 { text += "
      " @@ -163,41 +182,41 @@ func (m *MatrixPayload) Push(p *api.PushPayload) (api.Payloader, error) { } - return getMatrixPayload(text, p.Commits, m.MsgType), nil + return m.newPayload(text, p.Commits...) } -// PullRequest implements PayloadConvertor PullRequest method -func (m *MatrixPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { - text, _, _, _ := getPullRequestPayloadInfo(p, MatrixLinkFormatter, true) +// PullRequest implements payloadConvertor PullRequest method +func (m matrixConvertor) PullRequest(p *api.PullRequestPayload) (MatrixPayload, error) { + text, _, _, _ := getPullRequestPayloadInfo(p, htmlLinkFormatter, true) - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } -// Review implements PayloadConvertor Review method -func (m *MatrixPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) { - senderLink := MatrixLinkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName) +// Review implements payloadConvertor Review method +func (m matrixConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (MatrixPayload, error) { + senderLink := htmlLinkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName) title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title) - titleLink := MatrixLinkFormatter(p.PullRequest.HTMLURL, title) - repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) + titleLink := htmlLinkFormatter(p.PullRequest.HTMLURL, title) + repoLink := htmlLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) var text string switch p.Action { case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { - return nil, err + return MatrixPayload{}, err } text = fmt.Sprintf("[%s] Pull request review %s: %s by %s", repoLink, action, titleLink, senderLink) } - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } -// Repository implements PayloadConvertor Repository method -func (m *MatrixPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { - senderLink := MatrixLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) - repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) +// Repository implements payloadConvertor Repository method +func (m matrixConvertor) Repository(p *api.RepositoryPayload) (MatrixPayload, error) { + senderLink := htmlLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) + repoLink := htmlLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) var text string switch p.Action { @@ -206,13 +225,12 @@ func (m *MatrixPayload) Repository(p *api.RepositoryPayload) (api.Payloader, err case api.HookRepoDeleted: text = fmt.Sprintf("[%s] Repository deleted by %s", repoLink, senderLink) } - - return getMatrixPayload(text, nil, m.MsgType), nil + return m.newPayload(text) } -func (m *MatrixPayload) Package(p *api.PackagePayload) (api.Payloader, error) { - senderLink := MatrixLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) - packageLink := MatrixLinkFormatter(p.Package.HTMLURL, p.Package.Name) +func (m matrixConvertor) Package(p *api.PackagePayload) (MatrixPayload, error) { + senderLink := htmlLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) + packageLink := htmlLinkFormatter(p.Package.HTMLURL, p.Package.Name) var text string switch p.Action { @@ -222,31 +240,7 @@ func (m *MatrixPayload) Package(p *api.PackagePayload) (api.Payloader, error) { text = fmt.Sprintf("[%s] Package deleted by %s", packageLink, senderLink) } - return getMatrixPayload(text, nil, m.MsgType), nil -} - -// GetMatrixPayload converts a Matrix webhook into a MatrixPayload -func GetMatrixPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) { - s := new(MatrixPayload) - - matrix := &MatrixMeta{} - if err := json.Unmarshal([]byte(meta), &matrix); err != nil { - return s, errors.New("GetMatrixPayload meta json:" + err.Error()) - } - - s.MsgType = messageTypeText[matrix.MessageType] - - return convertPayloader(s, p, event) -} - -func getMatrixPayload(text string, commits []*api.PayloadCommit, msgType string) *MatrixPayload { - p := MatrixPayload{} - p.FormattedBody = text - p.Body = getMessageBody(text) - p.Format = "org.matrix.custom.html" - p.MsgType = msgType - p.Commits = commits - return &p + return m.newPayload(text) } var urlRegex = regexp.MustCompile(`]*?href="([^">]*?)">(.*?)`) @@ -271,3 +265,16 @@ func getMatrixTxnID(payload []byte) (string, error) { return hex.EncodeToString(h.Sum(nil)), nil } + +// MatrixLinkToRef Matrix-formatter link to a repo ref +func MatrixLinkToRef(repoURL, ref string) string { + refName := git.RefName(ref).ShortName() + switch { + case strings.HasPrefix(ref, git.BranchPrefix): + return htmlLinkFormatter(repoURL+"/src/branch/"+util.PathEscapeSegments(refName), refName) + case strings.HasPrefix(ref, git.TagPrefix): + return htmlLinkFormatter(repoURL+"/src/tag/"+util.PathEscapeSegments(refName), refName) + default: + return htmlLinkFormatter(repoURL+"/src/commit/"+util.PathEscapeSegments(refName), refName) + } +} diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go index 99a22fbd7e..058f8e3c5f 100644 --- a/services/webhook/matrix_test.go +++ b/services/webhook/matrix_test.go @@ -4,8 +4,11 @@ package webhook import ( + "context" "testing" + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -14,217 +17,213 @@ import ( ) func TestMatrixPayload(t *testing.T) { + mc := matrixConvertor{ + MsgType: "m.text", + } + t.Run("Create", func(t *testing.T) { p := createTestPayload() - d := new(MatrixPayload) - pl, err := d.Create(p) + pl, err := mc.Create(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):[test](http://localhost:3000/test/repo/src/branch/test)] branch created by user1", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo:test] branch created by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):[test](http://localhost:3000/test/repo/src/branch/test)] branch created by user1", pl.Body) + assert.Equal(t, `[test/repo:test] branch created by user1`, pl.FormattedBody) }) t.Run("Delete", func(t *testing.T) { p := deleteTestPayload() - d := new(MatrixPayload) - pl, err := d.Delete(p) + pl, err := mc.Delete(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):test] branch deleted by user1", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo:test] branch deleted by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):test] branch deleted by user1", pl.Body) + assert.Equal(t, `[test/repo:test] branch deleted by user1`, pl.FormattedBody) }) t.Run("Fork", func(t *testing.T) { p := forkTestPayload() - d := new(MatrixPayload) - pl, err := d.Fork(p) + pl, err := mc.Fork(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[test/repo2](http://localhost:3000/test/repo2) is forked to [test/repo](http://localhost:3000/test/repo)", pl.(*MatrixPayload).Body) - assert.Equal(t, `test/repo2 is forked to test/repo`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[test/repo2](http://localhost:3000/test/repo2) is forked to [test/repo](http://localhost:3000/test/repo)", pl.Body) + assert.Equal(t, `test/repo2 is forked to test/repo`, pl.FormattedBody) }) t.Run("Push", func(t *testing.T) { p := pushTestPayload() - d := new(MatrixPayload) - pl, err := d.Push(p) + pl, err := mc.Push(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] user1 pushed 2 commits to [test](http://localhost:3000/test/repo/src/branch/test):\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] user1 pushed 2 commits to test:
      2020558: commit message - user1
      2020558: commit message - user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] user1 pushed 2 commits to [test](http://localhost:3000/test/repo/src/branch/test):\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", pl.Body) + assert.Equal(t, `[test/repo] user1 pushed 2 commits to test:
      2020558: commit message - user1
      2020558: commit message - user1`, pl.FormattedBody) }) t.Run("Issue", func(t *testing.T) { p := issueTestPayload() - d := new(MatrixPayload) p.Action = api.HookIssueOpened - pl, err := d.Issue(p) + pl, err := mc.Issue(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue opened: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] Issue opened: #2 crash by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue opened: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] Issue opened: #2 crash by user1`, pl.FormattedBody) p.Action = api.HookIssueClosed - pl, err = d.Issue(p) + pl, err = mc.Issue(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue closed: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue closed: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.FormattedBody) }) t.Run("IssueComment", func(t *testing.T) { p := issueCommentTestPayload() - d := new(MatrixPayload) - pl, err := d.IssueComment(p) + pl, err := mc.IssueComment(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on issue [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] New comment on issue #2 crash by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on issue [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] New comment on issue #2 crash by user1`, pl.FormattedBody) }) t.Run("PullRequest", func(t *testing.T) { p := pullRequestTestPayload() - d := new(MatrixPayload) - pl, err := d.PullRequest(p) + pl, err := mc.PullRequest(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request opened: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] Pull request opened: #12 Fix bug by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request opened: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] Pull request opened: #12 Fix bug by user1`, pl.FormattedBody) }) t.Run("PullRequestComment", func(t *testing.T) { p := pullRequestCommentTestPayload() - d := new(MatrixPayload) - pl, err := d.IssueComment(p) + pl, err := mc.IssueComment(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on pull request [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] New comment on pull request #12 Fix bug by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on pull request [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] New comment on pull request #12 Fix bug by user1`, pl.FormattedBody) }) t.Run("Review", func(t *testing.T) { p := pullRequestTestPayload() p.Action = api.HookIssueReviewed - d := new(MatrixPayload) - pl, err := d.Review(p, webhook_module.HookEventPullRequestReviewApproved) + pl, err := mc.Review(p, webhook_module.HookEventPullRequestReviewApproved) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] Pull request review approved: #12 Fix bug by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] Pull request review approved: #12 Fix bug by user1`, pl.FormattedBody) }) t.Run("Repository", func(t *testing.T) { p := repositoryTestPayload() - d := new(MatrixPayload) - pl, err := d.Repository(p) + pl, err := mc.Repository(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, `[[test/repo](http://localhost:3000/test/repo)] Repository created by [user1](https://try.gitea.io/user1)`, pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] Repository created by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, `[[test/repo](http://localhost:3000/test/repo)] Repository created by [user1](https://try.gitea.io/user1)`, pl.Body) + assert.Equal(t, `[test/repo] Repository created by user1`, pl.FormattedBody) }) t.Run("Package", func(t *testing.T) { p := packageTestPayload() - d := new(MatrixPayload) - pl, err := d.Package(p) + pl, err := mc.Package(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, `[[GiteaContainer](http://localhost:3000/user1/-/packages/container/GiteaContainer/latest)] Package published by [user1](https://try.gitea.io/user1)`, pl.(*MatrixPayload).Body) - assert.Equal(t, `[GiteaContainer] Package published by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, `[[GiteaContainer](http://localhost:3000/user1/-/packages/container/GiteaContainer/latest)] Package published by [user1](https://try.gitea.io/user1)`, pl.Body) + assert.Equal(t, `[GiteaContainer] Package published by user1`, pl.FormattedBody) }) t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() - d := new(MatrixPayload) p.Action = api.HookWikiCreated - pl, err := d.Wiki(p) + pl, err := mc.Wiki(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New wiki page '[index](http://localhost:3000/test/repo/wiki/index)' (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New wiki page '[index](http://localhost:3000/test/repo/wiki/index)' (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.FormattedBody) p.Action = api.HookWikiEdited - pl, err = d.Wiki(p) + pl, err = mc.Wiki(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' edited (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' edited (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.FormattedBody) p.Action = api.HookWikiDeleted - pl, err = d.Wiki(p) + pl, err = mc.Wiki(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' deleted by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' deleted by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.FormattedBody) }) t.Run("Release", func(t *testing.T) { p := pullReleaseTestPayload() - d := new(MatrixPayload) - pl, err := d.Release(p) + pl, err := mc.Release(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Release created: [v1.0](http://localhost:3000/test/repo/releases/tag/v1.0) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) - assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.(*MatrixPayload).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Release created: [v1.0](http://localhost:3000/test/repo/releases/tag/v1.0) by [user1](https://try.gitea.io/user1)", pl.Body) + assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.FormattedBody) }) } func TestMatrixJSONPayload(t *testing.T) { p := pushTestPayload() - - pl, err := new(MatrixPayload).Push(p) + data, err := p.JSONPayload() require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MatrixPayload{}, pl) - json, err := pl.JSONPayload() + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.MATRIX, + URL: "https://matrix.example.com/_matrix/client/r0/rooms/ROOM_ID/send/m.room.message", + Meta: `{"message_type":0}`, // text + } + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := newMatrixRequest(context.Background(), hook, task) + require.NotNil(t, req) + require.NotNil(t, reqBody) require.NoError(t, err) - assert.NotEmpty(t, json) + + assert.Equal(t, "PUT", req.Method) + assert.Equal(t, "/_matrix/client/r0/rooms/ROOM_ID/send/m.room.message/6db5dc1e282529a8c162c7fe93dd2667494eeb51", req.URL.Path) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body MatrixPayload + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] user1 pushed 2 commits to [test](http://localhost:3000/test/repo/src/branch/test):\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", body.Body) } func Test_getTxnID(t *testing.T) { diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go index 37810b4cd3..99d0106184 100644 --- a/services/webhook/msteams.go +++ b/services/webhook/msteams.go @@ -4,12 +4,14 @@ package webhook import ( + "context" "fmt" + "net/http" "net/url" "strings" + webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -56,19 +58,8 @@ type ( } ) -// JSONPayload Marshals the MSTeamsPayload to json -func (m *MSTeamsPayload) JSONPayload() ([]byte, error) { - data, err := json.MarshalIndent(m, "", " ") - if err != nil { - return []byte{}, err - } - return data, nil -} - -var _ PayloadConvertor = &MSTeamsPayload{} - // Create implements PayloadConvertor Create method -func (m *MSTeamsPayload) Create(p *api.CreatePayload) (api.Payloader, error) { +func (m msteamsConvertor) Create(p *api.CreatePayload) (MSTeamsPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) @@ -85,7 +76,7 @@ func (m *MSTeamsPayload) Create(p *api.CreatePayload) (api.Payloader, error) { } // Delete implements PayloadConvertor Delete method -func (m *MSTeamsPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { +func (m msteamsConvertor) Delete(p *api.DeletePayload) (MSTeamsPayload, error) { // deleted tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) @@ -102,7 +93,7 @@ func (m *MSTeamsPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { } // Fork implements PayloadConvertor Fork method -func (m *MSTeamsPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { +func (m msteamsConvertor) Fork(p *api.ForkPayload) (MSTeamsPayload, error) { title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) return createMSTeamsPayload( @@ -117,7 +108,7 @@ func (m *MSTeamsPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { } // Push implements PayloadConvertor Push method -func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) { +func (m msteamsConvertor) Push(p *api.PushPayload) (MSTeamsPayload, error) { var ( branchName = git.RefName(p.Ref).ShortName() commitDesc string @@ -160,7 +151,7 @@ func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) { } // Issue implements PayloadConvertor Issue method -func (m *MSTeamsPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { +func (m msteamsConvertor) Issue(p *api.IssuePayload) (MSTeamsPayload, error) { title, _, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter, false) return createMSTeamsPayload( @@ -175,7 +166,7 @@ func (m *MSTeamsPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { } // IssueComment implements PayloadConvertor IssueComment method -func (m *MSTeamsPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { +func (m msteamsConvertor) IssueComment(p *api.IssueCommentPayload) (MSTeamsPayload, error) { title, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false) return createMSTeamsPayload( @@ -190,7 +181,7 @@ func (m *MSTeamsPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader } // PullRequest implements PayloadConvertor PullRequest method -func (m *MSTeamsPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { +func (m msteamsConvertor) PullRequest(p *api.PullRequestPayload) (MSTeamsPayload, error) { title, _, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false) return createMSTeamsPayload( @@ -205,14 +196,14 @@ func (m *MSTeamsPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, } // Review implements PayloadConvertor Review method -func (m *MSTeamsPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) { +func (m msteamsConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (MSTeamsPayload, error) { var text, title string var color int switch p.Action { case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { - return nil, err + return MSTeamsPayload{}, err } title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) @@ -242,7 +233,7 @@ func (m *MSTeamsPayload) Review(p *api.PullRequestPayload, event webhook_module. } // Repository implements PayloadConvertor Repository method -func (m *MSTeamsPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { +func (m msteamsConvertor) Repository(p *api.RepositoryPayload) (MSTeamsPayload, error) { var title, url string var color int switch p.Action { @@ -267,7 +258,7 @@ func (m *MSTeamsPayload) Repository(p *api.RepositoryPayload) (api.Payloader, er } // Wiki implements PayloadConvertor Wiki method -func (m *MSTeamsPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { +func (m msteamsConvertor) Wiki(p *api.WikiPayload) (MSTeamsPayload, error) { title, color, _ := getWikiPayloadInfo(p, noneLinkFormatter, false) return createMSTeamsPayload( @@ -282,7 +273,7 @@ func (m *MSTeamsPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { } // Release implements PayloadConvertor Release method -func (m *MSTeamsPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { +func (m msteamsConvertor) Release(p *api.ReleasePayload) (MSTeamsPayload, error) { title, color := getReleasePayloadInfo(p, noneLinkFormatter, false) return createMSTeamsPayload( @@ -296,7 +287,7 @@ func (m *MSTeamsPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { ), nil } -func (m *MSTeamsPayload) Package(p *api.PackagePayload) (api.Payloader, error) { +func (m msteamsConvertor) Package(p *api.PackagePayload) (MSTeamsPayload, error) { title, color := getPackagePayloadInfo(p, noneLinkFormatter, false) return createMSTeamsPayload( @@ -310,12 +301,7 @@ func (m *MSTeamsPayload) Package(p *api.PackagePayload) (api.Payloader, error) { ), nil } -// GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload -func GetMSTeamsPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) { - return convertPayloader(new(MSTeamsPayload), p, event) -} - -func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, fact *MSTeamsFact) *MSTeamsPayload { +func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, fact *MSTeamsFact) MSTeamsPayload { facts := make([]MSTeamsFact, 0, 2) if r != nil { facts = append(facts, MSTeamsFact{ @@ -327,7 +313,7 @@ func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTar facts = append(facts, *fact) } - return &MSTeamsPayload{ + return MSTeamsPayload{ Type: "MessageCard", Context: "https://schema.org/extensions", ThemeColor: fmt.Sprintf("%x", color), @@ -356,3 +342,11 @@ func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTar }, } } + +type msteamsConvertor struct{} + +var _ payloadConvertor[MSTeamsPayload] = msteamsConvertor{} + +func newMSTeamsRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { + return newJSONRequest(msteamsConvertor{}, w, t, true) +} diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go index 8d1aed6040..01e08b918e 100644 --- a/services/webhook/msteams_test.go +++ b/services/webhook/msteams_test.go @@ -4,8 +4,11 @@ package webhook import ( + "context" "testing" + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -14,22 +17,20 @@ import ( ) func TestMSTeamsPayload(t *testing.T) { + mc := msteamsConvertor{} t.Run("Create", func(t *testing.T) { p := createTestPayload() - d := new(MSTeamsPayload) - pl, err := d.Create(p) + pl, err := mc.Create(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] branch test created", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] branch test created", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] branch test created", pl.Title) + assert.Equal(t, "[test/repo] branch test created", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Empty(t, pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repo.FullName, fact.Value) } else if fact.Name == "branch:" { @@ -38,27 +39,24 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.PotentialAction[0].Targets[0].URI) }) t.Run("Delete", func(t *testing.T) { p := deleteTestPayload() - d := new(MSTeamsPayload) - pl, err := d.Delete(p) + pl, err := mc.Delete(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] branch test deleted", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] branch test deleted", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] branch test deleted", pl.Title) + assert.Equal(t, "[test/repo] branch test deleted", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Empty(t, pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repo.FullName, fact.Value) } else if fact.Name == "branch:" { @@ -67,27 +65,24 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.PotentialAction[0].Targets[0].URI) }) t.Run("Fork", func(t *testing.T) { p := forkTestPayload() - d := new(MSTeamsPayload) - pl, err := d.Fork(p) + pl, err := mc.Fork(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "test/repo2 is forked to test/repo", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "test/repo2 is forked to test/repo", pl.Title) + assert.Equal(t, "test/repo2 is forked to test/repo", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Empty(t, pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repo.FullName, fact.Value) } else if fact.Name == "Forkee:" { @@ -96,27 +91,24 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo", pl.PotentialAction[0].Targets[0].URI) }) t.Run("Push", func(t *testing.T) { p := pushTestPayload() - d := new(MSTeamsPayload) - pl, err := d.Push(p) + pl, err := mc.Push(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\n\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo:test] 2 new commits", pl.Title) + assert.Equal(t, "[test/repo:test] 2 new commits", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\n\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repo.FullName, fact.Value) } else if fact.Name == "Commit count:" { @@ -125,28 +117,25 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/src/test", pl.PotentialAction[0].Targets[0].URI) }) t.Run("Issue", func(t *testing.T) { p := issueTestPayload() - d := new(MSTeamsPayload) p.Action = api.HookIssueOpened - pl, err := d.Issue(p) + pl, err := mc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Equal(t, "issue body", pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.Title) + assert.Equal(t, "[test/repo] Issue opened: #2 crash", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Equal(t, "issue body", pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else if fact.Name == "Issue #:" { @@ -155,23 +144,21 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.PotentialAction[0].Targets[0].URI) p.Action = api.HookIssueClosed - pl, err = d.Issue(p) + pl, err = mc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.Title) + assert.Equal(t, "[test/repo] Issue closed: #2 crash", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Empty(t, pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else if fact.Name == "Issue #:" { @@ -180,27 +167,24 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2", pl.PotentialAction[0].Targets[0].URI) }) t.Run("IssueComment", func(t *testing.T) { p := issueCommentTestPayload() - d := new(MSTeamsPayload) - pl, err := d.IssueComment(p) + pl, err := mc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Equal(t, "more info needed", pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.Title) + assert.Equal(t, "[test/repo] New comment on issue #2 crash", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Equal(t, "more info needed", pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else if fact.Name == "Issue #:" { @@ -209,27 +193,24 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/issues/2#issuecomment-4", pl.PotentialAction[0].Targets[0].URI) }) t.Run("PullRequest", func(t *testing.T) { p := pullRequestTestPayload() - d := new(MSTeamsPayload) - pl, err := d.PullRequest(p) + pl, err := mc.PullRequest(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Equal(t, "fixes bug #2", pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.Title) + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Equal(t, "fixes bug #2", pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else if fact.Name == "Pull request #:" { @@ -238,27 +219,24 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.PotentialAction[0].Targets[0].URI) }) t.Run("PullRequestComment", func(t *testing.T) { p := pullRequestCommentTestPayload() - d := new(MSTeamsPayload) - pl, err := d.IssueComment(p) + pl, err := mc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Equal(t, "changes requested", pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.Title) + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Equal(t, "changes requested", pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else if fact.Name == "Issue #:" { @@ -267,28 +245,25 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12#issuecomment-4", pl.PotentialAction[0].Targets[0].URI) }) t.Run("Review", func(t *testing.T) { p := pullRequestTestPayload() p.Action = api.HookIssueReviewed - d := new(MSTeamsPayload) - pl, err := d.Review(p, webhook_module.HookEventPullRequestReviewApproved) + pl, err := mc.Review(p, webhook_module.HookEventPullRequestReviewApproved) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Equal(t, "good job", pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.Title) + assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Equal(t, "good job", pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else if fact.Name == "Pull request #:" { @@ -297,155 +272,139 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/pulls/12", pl.PotentialAction[0].Targets[0].URI) }) t.Run("Repository", func(t *testing.T) { p := repositoryTestPayload() - d := new(MSTeamsPayload) - pl, err := d.Repository(p) + pl, err := mc.Repository(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] Repository created", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] Repository created", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 1) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] Repository created", pl.Title) + assert.Equal(t, "[test/repo] Repository created", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Empty(t, pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 1) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo", pl.PotentialAction[0].Targets[0].URI) }) t.Run("Package", func(t *testing.T) { p := packageTestPayload() - d := new(MSTeamsPayload) - pl, err := d.Package(p) + pl, err := mc.Package(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "Package created: GiteaContainer:latest", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "Package created: GiteaContainer:latest", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 1) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "Package created: GiteaContainer:latest", pl.Title) + assert.Equal(t, "Package created: GiteaContainer:latest", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Empty(t, pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 1) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Package:" { assert.Equal(t, p.Package.Name, fact.Value) } else { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.PotentialAction[0].Targets[0].URI) }) t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() - d := new(MSTeamsPayload) p.Action = api.HookWikiCreated - pl, err := d.Wiki(p) + pl, err := mc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Equal(t, "", pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.Title) + assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Equal(t, "", pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.PotentialAction[0].Targets[0].URI) p.Action = api.HookWikiEdited - pl, err = d.Wiki(p) + pl, err = mc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Equal(t, "", pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.Title) + assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Equal(t, "", pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.PotentialAction[0].Targets[0].URI) p.Action = api.HookWikiDeleted - pl, err = d.Wiki(p) + pl, err = mc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.Title) + assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Empty(t, pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.PotentialAction[0].Targets[0].URI) }) t.Run("Release", func(t *testing.T) { p := pullReleaseTestPayload() - d := new(MSTeamsPayload) - pl, err := d.Release(p) + pl, err := mc.Release(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - assert.Equal(t, "[test/repo] Release created: v1.0", pl.(*MSTeamsPayload).Title) - assert.Equal(t, "[test/repo] Release created: v1.0", pl.(*MSTeamsPayload).Summary) - assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) - assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) - assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) - assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2) - for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + assert.Equal(t, "[test/repo] Release created: v1.0", pl.Title) + assert.Equal(t, "[test/repo] Release created: v1.0", pl.Summary) + assert.Len(t, pl.Sections, 1) + assert.Equal(t, "user1", pl.Sections[0].ActivitySubtitle) + assert.Empty(t, pl.Sections[0].Text) + assert.Len(t, pl.Sections[0].Facts, 2) + for _, fact := range pl.Sections[0].Facts { if fact.Name == "Repository:" { assert.Equal(t, p.Repository.FullName, fact.Value) } else if fact.Name == "Tag:" { @@ -454,21 +413,43 @@ func TestMSTeamsPayload(t *testing.T) { t.Fail() } } - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) - assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) - assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + assert.Len(t, pl.PotentialAction, 1) + assert.Len(t, pl.PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/test/repo/releases/tag/v1.0", pl.PotentialAction[0].Targets[0].URI) }) } func TestMSTeamsJSONPayload(t *testing.T) { p := pushTestPayload() - - pl, err := new(MSTeamsPayload).Push(p) + data, err := p.JSONPayload() require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &MSTeamsPayload{}, pl) - json, err := pl.JSONPayload() + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.MSTEAMS, + URL: "https://msteams.example.com/", + Meta: ``, + HTTPMethod: "POST", + } + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := newMSTeamsRequest(context.Background(), hook, task) + require.NotNil(t, req) + require.NotNil(t, reqBody) require.NoError(t, err) - assert.NotEmpty(t, json) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://msteams.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body MSTeamsPayload + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "[test/repo:test] 2 new commits", body.Summary) } diff --git a/services/webhook/packagist.go b/services/webhook/packagist.go index 714a4c076e..0a2b62c89a 100644 --- a/services/webhook/packagist.go +++ b/services/webhook/packagist.go @@ -4,17 +4,18 @@ package webhook import ( - "errors" + "context" + "fmt" + "net/http" webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" - api "code.gitea.io/gitea/modules/structs" - webhook_module "code.gitea.io/gitea/modules/webhook" ) type ( - // PackagistPayload represents + // PackagistPayload represents a packagist payload + // as expected by https://packagist.org/about PackagistPayload struct { PackagistRepository struct { URL string `json:"url"` @@ -38,84 +39,19 @@ func GetPackagistHook(w *webhook_model.Webhook) *PackagistMeta { return s } -// JSONPayload Marshals the PackagistPayload to json -func (f *PackagistPayload) JSONPayload() ([]byte, error) { - data, err := json.MarshalIndent(f, "", " ") - if err != nil { - return []byte{}, err +// newPackagistRequest creates a request with the [PackagistPayload] for packagist (same payload for all events). +func newPackagistRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { + meta := &PackagistMeta{} + if err := json.Unmarshal([]byte(w.Meta), meta); err != nil { + return nil, nil, fmt.Errorf("newpackagistRequest meta json: %w", err) } - return data, nil -} -var _ PayloadConvertor = &PackagistPayload{} - -// Create implements PayloadConvertor Create method -func (f *PackagistPayload) Create(_ *api.CreatePayload) (api.Payloader, error) { - return nil, nil -} - -// Delete implements PayloadConvertor Delete method -func (f *PackagistPayload) Delete(_ *api.DeletePayload) (api.Payloader, error) { - return nil, nil -} - -// Fork implements PayloadConvertor Fork method -func (f *PackagistPayload) Fork(_ *api.ForkPayload) (api.Payloader, error) { - return nil, nil -} - -// Push implements PayloadConvertor Push method -func (f *PackagistPayload) Push(_ *api.PushPayload) (api.Payloader, error) { - return f, nil -} - -// Issue implements PayloadConvertor Issue method -func (f *PackagistPayload) Issue(_ *api.IssuePayload) (api.Payloader, error) { - return nil, nil -} - -// IssueComment implements PayloadConvertor IssueComment method -func (f *PackagistPayload) IssueComment(_ *api.IssueCommentPayload) (api.Payloader, error) { - return nil, nil -} - -// PullRequest implements PayloadConvertor PullRequest method -func (f *PackagistPayload) PullRequest(_ *api.PullRequestPayload) (api.Payloader, error) { - return nil, nil -} - -// Review implements PayloadConvertor Review method -func (f *PackagistPayload) Review(_ *api.PullRequestPayload, _ webhook_module.HookEventType) (api.Payloader, error) { - return nil, nil -} - -// Repository implements PayloadConvertor Repository method -func (f *PackagistPayload) Repository(_ *api.RepositoryPayload) (api.Payloader, error) { - return nil, nil -} - -// Wiki implements PayloadConvertor Wiki method -func (f *PackagistPayload) Wiki(_ *api.WikiPayload) (api.Payloader, error) { - return nil, nil -} - -// Release implements PayloadConvertor Release method -func (f *PackagistPayload) Release(_ *api.ReleasePayload) (api.Payloader, error) { - return nil, nil -} - -func (f *PackagistPayload) Package(_ *api.PackagePayload) (api.Payloader, error) { - return nil, nil -} - -// GetPackagistPayload converts a packagist webhook into a PackagistPayload -func GetPackagistPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) { - s := new(PackagistPayload) - - packagist := &PackagistMeta{} - if err := json.Unmarshal([]byte(meta), &packagist); err != nil { - return s, errors.New("GetPackagistPayload meta json:" + err.Error()) + payload := PackagistPayload{ + PackagistRepository: struct { + URL string `json:"url"` + }{ + URL: meta.PackageURL, + }, } - s.PackagistRepository.URL = packagist.PackageURL - return convertPayloader(s, p, event) + return newJSONRequestWithPayload(payload, w, t, false) } diff --git a/services/webhook/packagist_test.go b/services/webhook/packagist_test.go index 26d01b0555..e6a963a9dd 100644 --- a/services/webhook/packagist_test.go +++ b/services/webhook/packagist_test.go @@ -4,8 +4,12 @@ package webhook import ( + "context" + "fmt" "testing" + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -14,155 +18,53 @@ import ( ) func TestPackagistPayload(t *testing.T) { - t.Run("Create", func(t *testing.T) { - p := createTestPayload() + payloads := []api.Payloader{ + createTestPayload(), + deleteTestPayload(), + forkTestPayload(), + pushTestPayload(), + issueTestPayload(), + issueCommentTestPayload(), + pullRequestCommentTestPayload(), + pullRequestTestPayload(), + repositoryTestPayload(), + packageTestPayload(), + wikiTestPayload(), + pullReleaseTestPayload(), + } - d := new(PackagistPayload) - pl, err := d.Create(p) - require.NoError(t, err) - require.Nil(t, pl) - }) + for _, payloader := range payloads { + t.Run(fmt.Sprintf("%T", payloader), func(t *testing.T) { + data, err := payloader.JSONPayload() + require.NoError(t, err) - t.Run("Delete", func(t *testing.T) { - p := deleteTestPayload() + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.PACKAGIST, + URL: "https://packagist.org/api/update-package?username=THEUSERNAME&apiToken=TOPSECRETAPITOKEN", + Meta: `{"package_url":"https://packagist.org/packages/example"}`, + HTTPMethod: "POST", + } + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } - d := new(PackagistPayload) - pl, err := d.Delete(p) - require.NoError(t, err) - require.Nil(t, pl) - }) + req, reqBody, err := newPackagistRequest(context.Background(), hook, task) + require.NotNil(t, req) + require.NotNil(t, reqBody) + require.NoError(t, err) - t.Run("Fork", func(t *testing.T) { - p := forkTestPayload() - - d := new(PackagistPayload) - pl, err := d.Fork(p) - require.NoError(t, err) - require.Nil(t, pl) - }) - - t.Run("Push", func(t *testing.T) { - p := pushTestPayload() - - d := new(PackagistPayload) - d.PackagistRepository.URL = "https://packagist.org/api/update-package?username=THEUSERNAME&apiToken=TOPSECRETAPITOKEN" - pl, err := d.Push(p) - require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &PackagistPayload{}, pl) - - assert.Equal(t, "https://packagist.org/api/update-package?username=THEUSERNAME&apiToken=TOPSECRETAPITOKEN", pl.(*PackagistPayload).PackagistRepository.URL) - }) - - t.Run("Issue", func(t *testing.T) { - p := issueTestPayload() - - d := new(PackagistPayload) - p.Action = api.HookIssueOpened - pl, err := d.Issue(p) - require.NoError(t, err) - require.Nil(t, pl) - - p.Action = api.HookIssueClosed - pl, err = d.Issue(p) - require.NoError(t, err) - require.Nil(t, pl) - }) - - t.Run("IssueComment", func(t *testing.T) { - p := issueCommentTestPayload() - - d := new(PackagistPayload) - pl, err := d.IssueComment(p) - require.NoError(t, err) - require.Nil(t, pl) - }) - - t.Run("PullRequest", func(t *testing.T) { - p := pullRequestTestPayload() - - d := new(PackagistPayload) - pl, err := d.PullRequest(p) - require.NoError(t, err) - require.Nil(t, pl) - }) - - t.Run("PullRequestComment", func(t *testing.T) { - p := pullRequestCommentTestPayload() - - d := new(PackagistPayload) - pl, err := d.IssueComment(p) - require.NoError(t, err) - require.Nil(t, pl) - }) - - t.Run("Review", func(t *testing.T) { - p := pullRequestTestPayload() - p.Action = api.HookIssueReviewed - - d := new(PackagistPayload) - pl, err := d.Review(p, webhook_module.HookEventPullRequestReviewApproved) - require.NoError(t, err) - require.Nil(t, pl) - }) - - t.Run("Repository", func(t *testing.T) { - p := repositoryTestPayload() - - d := new(PackagistPayload) - pl, err := d.Repository(p) - require.NoError(t, err) - require.Nil(t, pl) - }) - - t.Run("Package", func(t *testing.T) { - p := packageTestPayload() - - d := new(PackagistPayload) - pl, err := d.Package(p) - require.NoError(t, err) - require.Nil(t, pl) - }) - - t.Run("Wiki", func(t *testing.T) { - p := wikiTestPayload() - - d := new(PackagistPayload) - p.Action = api.HookWikiCreated - pl, err := d.Wiki(p) - require.NoError(t, err) - require.Nil(t, pl) - - p.Action = api.HookWikiEdited - pl, err = d.Wiki(p) - require.NoError(t, err) - require.Nil(t, pl) - - p.Action = api.HookWikiDeleted - pl, err = d.Wiki(p) - require.NoError(t, err) - require.Nil(t, pl) - }) - - t.Run("Release", func(t *testing.T) { - p := pullReleaseTestPayload() - - d := new(PackagistPayload) - pl, err := d.Release(p) - require.NoError(t, err) - require.Nil(t, pl) - }) -} - -func TestPackagistJSONPayload(t *testing.T) { - p := pushTestPayload() - - pl, err := new(PackagistPayload).Push(p) - require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &PackagistPayload{}, pl) - - json, err := pl.JSONPayload() - require.NoError(t, err) - assert.NotEmpty(t, json) + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://packagist.org/api/update-package?username=THEUSERNAME&apiToken=TOPSECRETAPITOKEN", req.URL.String()) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body PackagistPayload + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "https://packagist.org/packages/example", body.PackagistRepository.URL) + }) + } } diff --git a/services/webhook/payloader.go b/services/webhook/payloader.go index bd482c04ea..f87e6e4eec 100644 --- a/services/webhook/payloader.go +++ b/services/webhook/payloader.go @@ -4,58 +4,112 @@ package webhook import ( + "bytes" + "fmt" + "net/http" + + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" ) -// PayloadConvertor defines the interface to convert system webhook payload to external payload -type PayloadConvertor interface { - api.Payloader - Create(*api.CreatePayload) (api.Payloader, error) - Delete(*api.DeletePayload) (api.Payloader, error) - Fork(*api.ForkPayload) (api.Payloader, error) - Issue(*api.IssuePayload) (api.Payloader, error) - IssueComment(*api.IssueCommentPayload) (api.Payloader, error) - Push(*api.PushPayload) (api.Payloader, error) - PullRequest(*api.PullRequestPayload) (api.Payloader, error) - Review(*api.PullRequestPayload, webhook_module.HookEventType) (api.Payloader, error) - Repository(*api.RepositoryPayload) (api.Payloader, error) - Release(*api.ReleasePayload) (api.Payloader, error) - Wiki(*api.WikiPayload) (api.Payloader, error) - Package(*api.PackagePayload) (api.Payloader, error) +// payloadConvertor defines the interface to convert system payload to webhook payload +type payloadConvertor[T any] interface { + Create(*api.CreatePayload) (T, error) + Delete(*api.DeletePayload) (T, error) + Fork(*api.ForkPayload) (T, error) + Issue(*api.IssuePayload) (T, error) + IssueComment(*api.IssueCommentPayload) (T, error) + Push(*api.PushPayload) (T, error) + PullRequest(*api.PullRequestPayload) (T, error) + Review(*api.PullRequestPayload, webhook_module.HookEventType) (T, error) + Repository(*api.RepositoryPayload) (T, error) + Release(*api.ReleasePayload) (T, error) + Wiki(*api.WikiPayload) (T, error) + Package(*api.PackagePayload) (T, error) } -func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_module.HookEventType) (api.Payloader, error) { +func convertUnmarshalledJSON[T, P any](convert func(P) (T, error), data []byte) (T, error) { + var p P + if err := json.Unmarshal(data, &p); err != nil { + var t T + return t, fmt.Errorf("could not unmarshal payload: %w", err) + } + return convert(p) +} + +func newPayload[T any](rc payloadConvertor[T], data []byte, event webhook_module.HookEventType) (T, error) { switch event { case webhook_module.HookEventCreate: - return s.Create(p.(*api.CreatePayload)) + return convertUnmarshalledJSON(rc.Create, data) case webhook_module.HookEventDelete: - return s.Delete(p.(*api.DeletePayload)) + return convertUnmarshalledJSON(rc.Delete, data) case webhook_module.HookEventFork: - return s.Fork(p.(*api.ForkPayload)) + return convertUnmarshalledJSON(rc.Fork, data) case webhook_module.HookEventIssues, webhook_module.HookEventIssueAssign, webhook_module.HookEventIssueLabel, webhook_module.HookEventIssueMilestone: - return s.Issue(p.(*api.IssuePayload)) + return convertUnmarshalledJSON(rc.Issue, data) case webhook_module.HookEventIssueComment, webhook_module.HookEventPullRequestComment: - pl, ok := p.(*api.IssueCommentPayload) - if ok { - return s.IssueComment(pl) - } - return s.PullRequest(p.(*api.PullRequestPayload)) + // previous code sometimes sent s.PullRequest(p.(*api.PullRequestPayload)) + // however I couldn't find in notifier.go such a payload with an HookEvent***Comment event + + // History (most recent first): + // - refactored in https://github.com/go-gitea/gitea/pull/12310 + // - assertion added in https://github.com/go-gitea/gitea/pull/12046 + // - issue raised in https://github.com/go-gitea/gitea/issues/11940#issuecomment-645713996 + // > That's because for HookEventPullRequestComment event, some places use IssueCommentPayload and others use PullRequestPayload + + // In modules/actions/workflows.go:183 the type assertion is always payload.(*api.IssueCommentPayload) + return convertUnmarshalledJSON(rc.IssueComment, data) case webhook_module.HookEventPush: - return s.Push(p.(*api.PushPayload)) + return convertUnmarshalledJSON(rc.Push, data) case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestAssign, webhook_module.HookEventPullRequestLabel, webhook_module.HookEventPullRequestMilestone, webhook_module.HookEventPullRequestSync, webhook_module.HookEventPullRequestReviewRequest: - return s.PullRequest(p.(*api.PullRequestPayload)) + return convertUnmarshalledJSON(rc.PullRequest, data) case webhook_module.HookEventPullRequestReviewApproved, webhook_module.HookEventPullRequestReviewRejected, webhook_module.HookEventPullRequestReviewComment: - return s.Review(p.(*api.PullRequestPayload), event) + return convertUnmarshalledJSON(func(p *api.PullRequestPayload) (T, error) { + return rc.Review(p, event) + }, data) case webhook_module.HookEventRepository: - return s.Repository(p.(*api.RepositoryPayload)) + return convertUnmarshalledJSON(rc.Repository, data) case webhook_module.HookEventRelease: - return s.Release(p.(*api.ReleasePayload)) + return convertUnmarshalledJSON(rc.Release, data) case webhook_module.HookEventWiki: - return s.Wiki(p.(*api.WikiPayload)) + return convertUnmarshalledJSON(rc.Wiki, data) case webhook_module.HookEventPackage: - return s.Package(p.(*api.PackagePayload)) + return convertUnmarshalledJSON(rc.Package, data) } - return s, nil + var t T + return t, fmt.Errorf("newPayload unsupported event: %s", event) +} + +func newJSONRequest[T any](pc payloadConvertor[T], w *webhook_model.Webhook, t *webhook_model.HookTask, withDefaultHeaders bool) (*http.Request, []byte, error) { + payload, err := newPayload(pc, []byte(t.PayloadContent), t.EventType) + if err != nil { + return nil, nil, err + } + return newJSONRequestWithPayload(payload, w, t, withDefaultHeaders) +} + +func newJSONRequestWithPayload(payload any, w *webhook_model.Webhook, t *webhook_model.HookTask, withDefaultHeaders bool) (*http.Request, []byte, error) { + body, err := json.MarshalIndent(payload, "", " ") + if err != nil { + return nil, nil, err + } + + method := w.HTTPMethod + if method == "" { + method = http.MethodPost + } + + req, err := http.NewRequest(method, w.URL, bytes.NewReader(body)) + if err != nil { + return nil, nil, err + } + req.Header.Set("Content-Type", "application/json") + + if withDefaultHeaders { + return req, body, addDefaultHeaders(req, []byte(w.Secret), t, body) + } + return req, body, nil } diff --git a/services/webhook/slack.go b/services/webhook/slack.go index 945b0662d8..ba8bac27d9 100644 --- a/services/webhook/slack.go +++ b/services/webhook/slack.go @@ -4,8 +4,9 @@ package webhook import ( - "errors" + "context" "fmt" + "net/http" "regexp" "strings" @@ -39,7 +40,6 @@ func GetSlackHook(w *webhook_model.Webhook) *SlackMeta { type SlackPayload struct { Channel string `json:"channel"` Text string `json:"text"` - Color string `json:"-"` Username string `json:"username"` IconURL string `json:"icon_url"` UnfurlLinks int `json:"unfurl_links"` @@ -56,15 +56,6 @@ type SlackAttachment struct { Text string `json:"text"` } -// JSONPayload Marshals the SlackPayload to json -func (s *SlackPayload) JSONPayload() ([]byte, error) { - data, err := json.MarshalIndent(s, "", " ") - if err != nil { - return []byte{}, err - } - return data, nil -} - // SlackTextFormatter replaces &, <, > with HTML characters // see: https://api.slack.com/docs/formatting func SlackTextFormatter(s string) string { @@ -98,10 +89,8 @@ func SlackLinkToRef(repoURL, ref string) string { return SlackLinkFormatter(url, refName) } -var _ PayloadConvertor = &SlackPayload{} - -// Create implements PayloadConvertor Create method -func (s *SlackPayload) Create(p *api.CreatePayload) (api.Payloader, error) { +// Create implements payloadConvertor Create method +func (s slackConvertor) Create(p *api.CreatePayload) (SlackPayload, error) { repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) refLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref) text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName) @@ -110,7 +99,7 @@ func (s *SlackPayload) Create(p *api.CreatePayload) (api.Payloader, error) { } // Delete composes Slack payload for delete a branch or tag. -func (s *SlackPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { +func (s slackConvertor) Delete(p *api.DeletePayload) (SlackPayload, error) { refName := git.RefName(p.Ref).ShortName() repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName) @@ -119,7 +108,7 @@ func (s *SlackPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { } // Fork composes Slack payload for forked by a repository. -func (s *SlackPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { +func (s slackConvertor) Fork(p *api.ForkPayload) (SlackPayload, error) { baseLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName) forkLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink) @@ -127,8 +116,8 @@ func (s *SlackPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { return s.createPayload(text, nil), nil } -// Issue implements PayloadConvertor Issue method -func (s *SlackPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { +// Issue implements payloadConvertor Issue method +func (s slackConvertor) Issue(p *api.IssuePayload) (SlackPayload, error) { text, issueTitle, attachmentText, color := getIssuesPayloadInfo(p, SlackLinkFormatter, true) var attachments []SlackAttachment @@ -146,8 +135,8 @@ func (s *SlackPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { return s.createPayload(text, attachments), nil } -// IssueComment implements PayloadConvertor IssueComment method -func (s *SlackPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { +// IssueComment implements payloadConvertor IssueComment method +func (s slackConvertor) IssueComment(p *api.IssueCommentPayload) (SlackPayload, error) { text, issueTitle, color := getIssueCommentPayloadInfo(p, SlackLinkFormatter, true) return s.createPayload(text, []SlackAttachment{{ @@ -158,28 +147,28 @@ func (s *SlackPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, }}), nil } -// Wiki implements PayloadConvertor Wiki method -func (s *SlackPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { +// Wiki implements payloadConvertor Wiki method +func (s slackConvertor) Wiki(p *api.WikiPayload) (SlackPayload, error) { text, _, _ := getWikiPayloadInfo(p, SlackLinkFormatter, true) return s.createPayload(text, nil), nil } -// Release implements PayloadConvertor Release method -func (s *SlackPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { +// Release implements payloadConvertor Release method +func (s slackConvertor) Release(p *api.ReleasePayload) (SlackPayload, error) { text, _ := getReleasePayloadInfo(p, SlackLinkFormatter, true) return s.createPayload(text, nil), nil } -func (s *SlackPayload) Package(p *api.PackagePayload) (api.Payloader, error) { +func (s slackConvertor) Package(p *api.PackagePayload) (SlackPayload, error) { text, _ := getPackagePayloadInfo(p, SlackLinkFormatter, true) return s.createPayload(text, nil), nil } -// Push implements PayloadConvertor Push method -func (s *SlackPayload) Push(p *api.PushPayload) (api.Payloader, error) { +// Push implements payloadConvertor Push method +func (s slackConvertor) Push(p *api.PushPayload) (SlackPayload, error) { // n new commits var ( commitDesc string @@ -219,8 +208,8 @@ func (s *SlackPayload) Push(p *api.PushPayload) (api.Payloader, error) { }}), nil } -// PullRequest implements PayloadConvertor PullRequest method -func (s *SlackPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { +// PullRequest implements payloadConvertor PullRequest method +func (s slackConvertor) PullRequest(p *api.PullRequestPayload) (SlackPayload, error) { text, issueTitle, attachmentText, color := getPullRequestPayloadInfo(p, SlackLinkFormatter, true) var attachments []SlackAttachment @@ -238,8 +227,8 @@ func (s *SlackPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, er return s.createPayload(text, attachments), nil } -// Review implements PayloadConvertor Review method -func (s *SlackPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) { +// Review implements payloadConvertor Review method +func (s slackConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (SlackPayload, error) { senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title) titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index) @@ -250,7 +239,7 @@ func (s *SlackPayload) Review(p *api.PullRequestPayload, event webhook_module.Ho case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { - return nil, err + return SlackPayload{}, err } text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink) @@ -259,8 +248,8 @@ func (s *SlackPayload) Review(p *api.PullRequestPayload, event webhook_module.Ho return s.createPayload(text, nil), nil } -// Repository implements PayloadConvertor Repository method -func (s *SlackPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { +// Repository implements payloadConvertor Repository method +func (s slackConvertor) Repository(p *api.RepositoryPayload) (SlackPayload, error) { senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) var text string @@ -275,8 +264,8 @@ func (s *SlackPayload) Repository(p *api.RepositoryPayload) (api.Payloader, erro return s.createPayload(text, nil), nil } -func (s *SlackPayload) createPayload(text string, attachments []SlackAttachment) *SlackPayload { - return &SlackPayload{ +func (s slackConvertor) createPayload(text string, attachments []SlackAttachment) SlackPayload { + return SlackPayload{ Channel: s.Channel, Text: text, Username: s.Username, @@ -285,21 +274,27 @@ func (s *SlackPayload) createPayload(text string, attachments []SlackAttachment) } } -// GetSlackPayload converts a slack webhook into a SlackPayload -func GetSlackPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) { - s := new(SlackPayload) +type slackConvertor struct { + Channel string + Username string + IconURL string + Color string +} - slack := &SlackMeta{} - if err := json.Unmarshal([]byte(meta), &slack); err != nil { - return s, errors.New("GetSlackPayload meta json:" + err.Error()) +var _ payloadConvertor[SlackPayload] = slackConvertor{} + +func newSlackRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { + meta := &SlackMeta{} + if err := json.Unmarshal([]byte(w.Meta), meta); err != nil { + return nil, nil, fmt.Errorf("newSlackRequest meta json: %w", err) } - - s.Channel = slack.Channel - s.Username = slack.Username - s.IconURL = slack.IconURL - s.Color = slack.Color - - return convertPayloader(s, p, event) + sc := slackConvertor{ + Channel: meta.Channel, + Username: meta.Username, + IconURL: meta.IconURL, + Color: meta.Color, + } + return newJSONRequest(sc, w, t, true) } var slackChannel = regexp.MustCompile(`^#?[a-z0-9_-]{1,80}$`) diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go index b1340963e2..7ebf16aba2 100644 --- a/services/webhook/slack_test.go +++ b/services/webhook/slack_test.go @@ -4,8 +4,11 @@ package webhook import ( + "context" "testing" + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -14,201 +17,180 @@ import ( ) func TestSlackPayload(t *testing.T) { + sc := slackConvertor{} + t.Run("Create", func(t *testing.T) { p := createTestPayload() - d := new(SlackPayload) - pl, err := d.Create(p) + pl, err := sc.Create(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[:] branch created by user1", pl.(*SlackPayload).Text) + assert.Equal(t, "[:] branch created by user1", pl.Text) }) t.Run("Delete", func(t *testing.T) { p := deleteTestPayload() - d := new(SlackPayload) - pl, err := d.Delete(p) + pl, err := sc.Delete(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[:test] branch deleted by user1", pl.(*SlackPayload).Text) + assert.Equal(t, "[:test] branch deleted by user1", pl.Text) }) t.Run("Fork", func(t *testing.T) { p := forkTestPayload() - d := new(SlackPayload) - pl, err := d.Fork(p) + pl, err := sc.Fork(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, " is forked to ", pl.(*SlackPayload).Text) + assert.Equal(t, " is forked to ", pl.Text) }) t.Run("Push", func(t *testing.T) { p := pushTestPayload() - d := new(SlackPayload) - pl, err := d.Push(p) + pl, err := sc.Push(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[:] 2 new commits pushed by user1", pl.(*SlackPayload).Text) + assert.Equal(t, "[:] 2 new commits pushed by user1", pl.Text) }) t.Run("Issue", func(t *testing.T) { p := issueTestPayload() - d := new(SlackPayload) p.Action = api.HookIssueOpened - pl, err := d.Issue(p) + pl, err := sc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] Issue opened: by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] Issue opened: by ", pl.Text) p.Action = api.HookIssueClosed - pl, err = d.Issue(p) + pl, err = sc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] Issue closed: by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] Issue closed: by ", pl.Text) }) t.Run("IssueComment", func(t *testing.T) { p := issueCommentTestPayload() - d := new(SlackPayload) - pl, err := d.IssueComment(p) + pl, err := sc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] New comment on issue by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] New comment on issue by ", pl.Text) }) t.Run("PullRequest", func(t *testing.T) { p := pullRequestTestPayload() - d := new(SlackPayload) - pl, err := d.PullRequest(p) + pl, err := sc.PullRequest(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] Pull request opened: by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] Pull request opened: by ", pl.Text) }) t.Run("PullRequestComment", func(t *testing.T) { p := pullRequestCommentTestPayload() - d := new(SlackPayload) - pl, err := d.IssueComment(p) + pl, err := sc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] New comment on pull request by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] New comment on pull request by ", pl.Text) }) t.Run("Review", func(t *testing.T) { p := pullRequestTestPayload() p.Action = api.HookIssueReviewed - d := new(SlackPayload) - pl, err := d.Review(p, webhook_module.HookEventPullRequestReviewApproved) + pl, err := sc.Review(p, webhook_module.HookEventPullRequestReviewApproved) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by ", pl.Text) }) t.Run("Repository", func(t *testing.T) { p := repositoryTestPayload() - d := new(SlackPayload) - pl, err := d.Repository(p) + pl, err := sc.Repository(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] Repository created by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] Repository created by ", pl.Text) }) t.Run("Package", func(t *testing.T) { p := packageTestPayload() - d := new(SlackPayload) - pl, err := d.Package(p) + pl, err := sc.Package(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "Package created: by ", pl.(*SlackPayload).Text) + assert.Equal(t, "Package created: by ", pl.Text) }) t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() - d := new(SlackPayload) p.Action = api.HookWikiCreated - pl, err := d.Wiki(p) + pl, err := sc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] New wiki page '' (Wiki change comment) by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] New wiki page '' (Wiki change comment) by ", pl.Text) p.Action = api.HookWikiEdited - pl, err = d.Wiki(p) + pl, err = sc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] Wiki page '' edited (Wiki change comment) by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] Wiki page '' edited (Wiki change comment) by ", pl.Text) p.Action = api.HookWikiDeleted - pl, err = d.Wiki(p) + pl, err = sc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] Wiki page '' deleted by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] Wiki page '' deleted by ", pl.Text) }) t.Run("Release", func(t *testing.T) { p := pullReleaseTestPayload() - d := new(SlackPayload) - pl, err := d.Release(p) + pl, err := sc.Release(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - assert.Equal(t, "[] Release created: by ", pl.(*SlackPayload).Text) + assert.Equal(t, "[] Release created: by ", pl.Text) }) } func TestSlackJSONPayload(t *testing.T) { p := pushTestPayload() - - pl, err := new(SlackPayload).Push(p) + data, err := p.JSONPayload() require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &SlackPayload{}, pl) - json, err := pl.JSONPayload() + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.SLACK, + URL: "https://slack.example.com/", + Meta: `{}`, + HTTPMethod: "POST", + } + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := newSlackRequest(context.Background(), hook, task) + require.NotNil(t, req) + require.NotNil(t, reqBody) require.NoError(t, err) - assert.NotEmpty(t, json) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://slack.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body SlackPayload + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "[:] 2 new commits pushed by user1", body.Text) } func TestIsValidSlackChannel(t *testing.T) { diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go index 1bdc74e183..e4a5b5a424 100644 --- a/services/webhook/telegram.go +++ b/services/webhook/telegram.go @@ -4,14 +4,15 @@ package webhook import ( + "context" "fmt" + "net/http" "strings" webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" ) @@ -41,22 +42,8 @@ func GetTelegramHook(w *webhook_model.Webhook) *TelegramMeta { return s } -var _ PayloadConvertor = &TelegramPayload{} - -// JSONPayload Marshals the TelegramPayload to json -func (t *TelegramPayload) JSONPayload() ([]byte, error) { - t.ParseMode = "HTML" - t.DisableWebPreview = true - t.Message = markup.Sanitize(t.Message) - data, err := json.MarshalIndent(t, "", " ") - if err != nil { - return []byte{}, err - } - return data, nil -} - // Create implements PayloadConvertor Create method -func (t *TelegramPayload) Create(p *api.CreatePayload) (api.Payloader, error) { +func (t telegramConvertor) Create(p *api.CreatePayload) (TelegramPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf(`[%s] %s %s created`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType, @@ -66,7 +53,7 @@ func (t *TelegramPayload) Create(p *api.CreatePayload) (api.Payloader, error) { } // Delete implements PayloadConvertor Delete method -func (t *TelegramPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { +func (t telegramConvertor) Delete(p *api.DeletePayload) (TelegramPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf(`[%s] %s %s deleted`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType, @@ -76,14 +63,14 @@ func (t *TelegramPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { } // Fork implements PayloadConvertor Fork method -func (t *TelegramPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { +func (t telegramConvertor) Fork(p *api.ForkPayload) (TelegramPayload, error) { title := fmt.Sprintf(`%s is forked to %s`, p.Forkee.FullName, p.Repo.HTMLURL, p.Repo.FullName) return createTelegramPayload(title), nil } // Push implements PayloadConvertor Push method -func (t *TelegramPayload) Push(p *api.PushPayload) (api.Payloader, error) { +func (t telegramConvertor) Push(p *api.PushPayload) (TelegramPayload, error) { var ( branchName = git.RefName(p.Ref).ShortName() commitDesc string @@ -121,34 +108,34 @@ func (t *TelegramPayload) Push(p *api.PushPayload) (api.Payloader, error) { } // Issue implements PayloadConvertor Issue method -func (t *TelegramPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { +func (t telegramConvertor) Issue(p *api.IssuePayload) (TelegramPayload, error) { text, _, attachmentText, _ := getIssuesPayloadInfo(p, htmlLinkFormatter, true) return createTelegramPayload(text + "\n\n" + attachmentText), nil } // IssueComment implements PayloadConvertor IssueComment method -func (t *TelegramPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { +func (t telegramConvertor) IssueComment(p *api.IssueCommentPayload) (TelegramPayload, error) { text, _, _ := getIssueCommentPayloadInfo(p, htmlLinkFormatter, true) return createTelegramPayload(text + "\n" + p.Comment.Body), nil } // PullRequest implements PayloadConvertor PullRequest method -func (t *TelegramPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { +func (t telegramConvertor) PullRequest(p *api.PullRequestPayload) (TelegramPayload, error) { text, _, attachmentText, _ := getPullRequestPayloadInfo(p, htmlLinkFormatter, true) return createTelegramPayload(text + "\n" + attachmentText), nil } // Review implements PayloadConvertor Review method -func (t *TelegramPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) { +func (t telegramConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (TelegramPayload, error) { var text, attachmentText string switch p.Action { case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { - return nil, err + return TelegramPayload{}, err } text = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) @@ -159,7 +146,7 @@ func (t *TelegramPayload) Review(p *api.PullRequestPayload, event webhook_module } // Repository implements PayloadConvertor Repository method -func (t *TelegramPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { +func (t telegramConvertor) Repository(p *api.RepositoryPayload) (TelegramPayload, error) { var title string switch p.Action { case api.HookRepoCreated: @@ -169,36 +156,39 @@ func (t *TelegramPayload) Repository(p *api.RepositoryPayload) (api.Payloader, e title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) return createTelegramPayload(title), nil } - return nil, nil + return TelegramPayload{}, nil } // Wiki implements PayloadConvertor Wiki method -func (t *TelegramPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { +func (t telegramConvertor) Wiki(p *api.WikiPayload) (TelegramPayload, error) { text, _, _ := getWikiPayloadInfo(p, htmlLinkFormatter, true) return createTelegramPayload(text), nil } // Release implements PayloadConvertor Release method -func (t *TelegramPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { +func (t telegramConvertor) Release(p *api.ReleasePayload) (TelegramPayload, error) { text, _ := getReleasePayloadInfo(p, htmlLinkFormatter, true) return createTelegramPayload(text), nil } -func (t *TelegramPayload) Package(p *api.PackagePayload) (api.Payloader, error) { +func (t telegramConvertor) Package(p *api.PackagePayload) (TelegramPayload, error) { text, _ := getPackagePayloadInfo(p, htmlLinkFormatter, true) return createTelegramPayload(text), nil } -// GetTelegramPayload converts a telegram webhook into a TelegramPayload -func GetTelegramPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) { - return convertPayloader(new(TelegramPayload), p, event) -} - -func createTelegramPayload(message string) *TelegramPayload { - return &TelegramPayload{ +func createTelegramPayload(message string) TelegramPayload { + return TelegramPayload{ Message: strings.TrimSpace(message), } } + +type telegramConvertor struct{} + +var _ payloadConvertor[TelegramPayload] = telegramConvertor{} + +func newTelegramRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { + return newJSONRequest(telegramConvertor{}, w, t, true) +} diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go index 5b9927d057..27ab96cd09 100644 --- a/services/webhook/telegram_test.go +++ b/services/webhook/telegram_test.go @@ -4,8 +4,11 @@ package webhook import ( + "context" "testing" + webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" @@ -14,199 +17,177 @@ import ( ) func TestTelegramPayload(t *testing.T) { + tc := telegramConvertor{} t.Run("Create", func(t *testing.T) { p := createTestPayload() - d := new(TelegramPayload) - pl, err := d.Create(p) + pl, err := tc.Create(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `[test/repo] branch test created`, pl.(*TelegramPayload).Message) + assert.Equal(t, `[test/repo] branch test created`, pl.Message) }) t.Run("Delete", func(t *testing.T) { p := deleteTestPayload() - d := new(TelegramPayload) - pl, err := d.Delete(p) + pl, err := tc.Delete(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `[test/repo] branch test deleted`, pl.(*TelegramPayload).Message) + assert.Equal(t, `[test/repo] branch test deleted`, pl.Message) }) t.Run("Fork", func(t *testing.T) { p := forkTestPayload() - d := new(TelegramPayload) - pl, err := d.Fork(p) + pl, err := tc.Fork(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `test/repo2 is forked to test/repo`, pl.(*TelegramPayload).Message) + assert.Equal(t, `test/repo2 is forked to test/repo`, pl.Message) }) t.Run("Push", func(t *testing.T) { p := pushTestPayload() - d := new(TelegramPayload) - pl, err := d.Push(p) + pl, err := tc.Push(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, "[test/repo:test] 2 new commits\n[2020558] commit message - user1\n[2020558] commit message - user1", pl.(*TelegramPayload).Message) + assert.Equal(t, "[test/repo:test] 2 new commits\n[2020558] commit message - user1\n[2020558] commit message - user1", pl.Message) }) t.Run("Issue", func(t *testing.T) { p := issueTestPayload() - d := new(TelegramPayload) p.Action = api.HookIssueOpened - pl, err := d.Issue(p) + pl, err := tc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, "[test/repo] Issue opened: #2 crash by user1\n\nissue body", pl.(*TelegramPayload).Message) + assert.Equal(t, "[test/repo] Issue opened: #2 crash by user1\n\nissue body", pl.Message) p.Action = api.HookIssueClosed - pl, err = d.Issue(p) + pl, err = tc.Issue(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.(*TelegramPayload).Message) + assert.Equal(t, `[test/repo] Issue closed: #2 crash by user1`, pl.Message) }) t.Run("IssueComment", func(t *testing.T) { p := issueCommentTestPayload() - d := new(TelegramPayload) - pl, err := d.IssueComment(p) + pl, err := tc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, "[test/repo] New comment on issue #2 crash by user1\nmore info needed", pl.(*TelegramPayload).Message) + assert.Equal(t, "[test/repo] New comment on issue #2 crash by user1\nmore info needed", pl.Message) }) t.Run("PullRequest", func(t *testing.T) { p := pullRequestTestPayload() - d := new(TelegramPayload) - pl, err := d.PullRequest(p) + pl, err := tc.PullRequest(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug by user1\nfixes bug #2", pl.(*TelegramPayload).Message) + assert.Equal(t, "[test/repo] Pull request opened: #12 Fix bug by user1\nfixes bug #2", pl.Message) }) t.Run("PullRequestComment", func(t *testing.T) { p := pullRequestCommentTestPayload() - d := new(TelegramPayload) - pl, err := d.IssueComment(p) + pl, err := tc.IssueComment(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug by user1\nchanges requested", pl.(*TelegramPayload).Message) + assert.Equal(t, "[test/repo] New comment on pull request #12 Fix bug by user1\nchanges requested", pl.Message) }) t.Run("Review", func(t *testing.T) { p := pullRequestTestPayload() p.Action = api.HookIssueReviewed - d := new(TelegramPayload) - pl, err := d.Review(p, webhook_module.HookEventPullRequestReviewApproved) + pl, err := tc.Review(p, webhook_module.HookEventPullRequestReviewApproved) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug\ngood job", pl.(*TelegramPayload).Message) + assert.Equal(t, "[test/repo] Pull request review approved: #12 Fix bug\ngood job", pl.Message) }) t.Run("Repository", func(t *testing.T) { p := repositoryTestPayload() - d := new(TelegramPayload) - pl, err := d.Repository(p) + pl, err := tc.Repository(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `[test/repo] Repository created`, pl.(*TelegramPayload).Message) + assert.Equal(t, `[test/repo] Repository created`, pl.Message) }) t.Run("Package", func(t *testing.T) { p := packageTestPayload() - d := new(TelegramPayload) - pl, err := d.Package(p) + pl, err := tc.Package(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `Package created: GiteaContainer:latest by user1`, pl.(*TelegramPayload).Message) + assert.Equal(t, `Package created: GiteaContainer:latest by user1`, pl.Message) }) t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() - d := new(TelegramPayload) p.Action = api.HookWikiCreated - pl, err := d.Wiki(p) + pl, err := tc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.(*TelegramPayload).Message) + assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.Message) p.Action = api.HookWikiEdited - pl, err = d.Wiki(p) + pl, err = tc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.(*TelegramPayload).Message) + assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.Message) p.Action = api.HookWikiDeleted - pl, err = d.Wiki(p) + pl, err = tc.Wiki(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.(*TelegramPayload).Message) + assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.Message) }) t.Run("Release", func(t *testing.T) { p := pullReleaseTestPayload() - d := new(TelegramPayload) - pl, err := d.Release(p) + pl, err := tc.Release(p) require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.(*TelegramPayload).Message) + assert.Equal(t, `[test/repo] Release created: v1.0 by user1`, pl.Message) }) } func TestTelegramJSONPayload(t *testing.T) { p := pushTestPayload() - - pl, err := new(TelegramPayload).Push(p) + data, err := p.JSONPayload() require.NoError(t, err) - require.NotNil(t, pl) - require.IsType(t, &TelegramPayload{}, pl) - json, err := pl.JSONPayload() + hook := &webhook_model.Webhook{ + RepoID: 3, + IsActive: true, + Type: webhook_module.TELEGRAM, + URL: "https://telegram.example.com/", + Meta: ``, + HTTPMethod: "POST", + } + task := &webhook_model.HookTask{ + HookID: hook.ID, + EventType: webhook_module.HookEventPush, + PayloadContent: string(data), + PayloadVersion: 2, + } + + req, reqBody, err := newTelegramRequest(context.Background(), hook, task) + require.NotNil(t, req) + require.NotNil(t, reqBody) require.NoError(t, err) - assert.NotEmpty(t, json) + + assert.Equal(t, "POST", req.Method) + assert.Equal(t, "https://telegram.example.com/", req.URL.String()) + assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "application/json", req.Header.Get("Content-Type")) + var body TelegramPayload + err = json.NewDecoder(req.Body).Decode(&body) + assert.NoError(t, err) + assert.Equal(t, "[test/repo:test] 2 new commits\n[2020558] commit message - user1\n[2020558] commit message - user1", body.Message) } diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index 996942d1e5..e6646501da 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "net/http" "strings" "code.gitea.io/gitea/models/db" @@ -26,48 +27,16 @@ import ( "github.com/gobwas/glob" ) -type webhook struct { - name webhook_module.HookType - payloadCreator func(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) -} - -var webhooks = map[webhook_module.HookType]*webhook{ - webhook_module.SLACK: { - name: webhook_module.SLACK, - payloadCreator: GetSlackPayload, - }, - webhook_module.DISCORD: { - name: webhook_module.DISCORD, - payloadCreator: GetDiscordPayload, - }, - webhook_module.DINGTALK: { - name: webhook_module.DINGTALK, - payloadCreator: GetDingtalkPayload, - }, - webhook_module.TELEGRAM: { - name: webhook_module.TELEGRAM, - payloadCreator: GetTelegramPayload, - }, - webhook_module.MSTEAMS: { - name: webhook_module.MSTEAMS, - payloadCreator: GetMSTeamsPayload, - }, - webhook_module.FEISHU: { - name: webhook_module.FEISHU, - payloadCreator: GetFeishuPayload, - }, - webhook_module.MATRIX: { - name: webhook_module.MATRIX, - payloadCreator: GetMatrixPayload, - }, - webhook_module.WECHATWORK: { - name: webhook_module.WECHATWORK, - payloadCreator: GetWechatworkPayload, - }, - webhook_module.PACKAGIST: { - name: webhook_module.PACKAGIST, - payloadCreator: GetPackagistPayload, - }, +var webhookRequesters = map[webhook_module.HookType]func(context.Context, *webhook_model.Webhook, *webhook_model.HookTask) (req *http.Request, body []byte, err error){ + webhook_module.SLACK: newSlackRequest, + webhook_module.DISCORD: newDiscordRequest, + webhook_module.DINGTALK: newDingtalkRequest, + webhook_module.TELEGRAM: newTelegramRequest, + webhook_module.MSTEAMS: newMSTeamsRequest, + webhook_module.FEISHU: newFeishuRequest, + webhook_module.MATRIX: newMatrixRequest, + webhook_module.WECHATWORK: newWechatworkRequest, + webhook_module.PACKAGIST: newPackagistRequest, } // IsValidHookTaskType returns true if a webhook registered @@ -75,7 +44,7 @@ func IsValidHookTaskType(name string) bool { if name == webhook_module.FORGEJO || name == webhook_module.GITEA || name == webhook_module.GOGS { return true } - _, ok := webhooks[name] + _, ok := webhookRequesters[name] return ok } @@ -159,7 +128,9 @@ func checkBranch(w *webhook_model.Webhook, branch string) bool { return g.Match(branch) } -// PrepareWebhook creates a hook task and enqueues it for processing +// PrepareWebhook creates a hook task and enqueues it for processing. +// The payload is saved as-is. The adjustments depending on the webhook type happen +// right before delivery, in the [Deliver] method. func PrepareWebhook(ctx context.Context, w *webhook_model.Webhook, event webhook_module.HookEventType, p api.Payloader) error { // Skip sending if webhooks are disabled. if setting.DisableWebhooks { @@ -193,25 +164,19 @@ func PrepareWebhook(ctx context.Context, w *webhook_model.Webhook, event webhook } } - var payloader api.Payloader - var err error - webhook, ok := webhooks[w.Type] - if ok { - payloader, err = webhook.payloadCreator(p, event, w.Meta) - if err != nil { - return fmt.Errorf("create payload for %s[%s]: %w", w.Type, event, err) - } - } else { - payloader = p + payload, err := p.JSONPayload() + if err != nil { + return fmt.Errorf("JSONPayload for %s: %w", event, err) } task, err := webhook_model.CreateHookTask(ctx, &webhook_model.HookTask{ - HookID: w.ID, - Payloader: payloader, - EventType: event, + HookID: w.ID, + PayloadContent: string(payload), + EventType: event, + PayloadVersion: 2, }) if err != nil { - return fmt.Errorf("CreateHookTask: %w", err) + return fmt.Errorf("CreateHookTask for %s: %w", event, err) } return enqueueHookTask(task.ID) diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go index 338b94360b..5f5c146232 100644 --- a/services/webhook/webhook_test.go +++ b/services/webhook/webhook_test.go @@ -77,7 +77,3 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) { unittest.AssertNotExistsBean(t, hookTask) } } - -// TODO TestHookTask_deliver - -// TODO TestDeliverHooks diff --git a/services/webhook/wechatwork.go b/services/webhook/wechatwork.go index 80245c7e77..46e7856ecf 100644 --- a/services/webhook/wechatwork.go +++ b/services/webhook/wechatwork.go @@ -4,11 +4,13 @@ package webhook import ( + "context" "fmt" + "net/http" "strings" + webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" ) @@ -28,20 +30,8 @@ type ( } ) -// SetSecret sets the Wechatwork secret -func (f *WechatworkPayload) SetSecret(_ string) {} - -// JSONPayload Marshals the WechatworkPayload to json -func (f *WechatworkPayload) JSONPayload() ([]byte, error) { - data, err := json.MarshalIndent(f, "", " ") - if err != nil { - return []byte{}, err - } - return data, nil -} - -func newWechatworkMarkdownPayload(title string) *WechatworkPayload { - return &WechatworkPayload{ +func newWechatworkMarkdownPayload(title string) WechatworkPayload { + return WechatworkPayload{ Msgtype: "markdown", Markdown: struct { Content string `json:"content"` @@ -51,10 +41,8 @@ func newWechatworkMarkdownPayload(title string) *WechatworkPayload { } } -var _ PayloadConvertor = &WechatworkPayload{} - // Create implements PayloadConvertor Create method -func (f *WechatworkPayload) Create(p *api.CreatePayload) (api.Payloader, error) { +func (wc wechatworkConvertor) Create(p *api.CreatePayload) (WechatworkPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) @@ -63,7 +51,7 @@ func (f *WechatworkPayload) Create(p *api.CreatePayload) (api.Payloader, error) } // Delete implements PayloadConvertor Delete method -func (f *WechatworkPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { +func (wc wechatworkConvertor) Delete(p *api.DeletePayload) (WechatworkPayload, error) { // created tag/branch refName := git.RefName(p.Ref).ShortName() title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) @@ -72,14 +60,14 @@ func (f *WechatworkPayload) Delete(p *api.DeletePayload) (api.Payloader, error) } // Fork implements PayloadConvertor Fork method -func (f *WechatworkPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { +func (wc wechatworkConvertor) Fork(p *api.ForkPayload) (WechatworkPayload, error) { title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) return newWechatworkMarkdownPayload(title), nil } // Push implements PayloadConvertor Push method -func (f *WechatworkPayload) Push(p *api.PushPayload) (api.Payloader, error) { +func (wc wechatworkConvertor) Push(p *api.PushPayload) (WechatworkPayload, error) { var ( branchName = git.RefName(p.Ref).ShortName() commitDesc string @@ -108,7 +96,7 @@ func (f *WechatworkPayload) Push(p *api.PushPayload) (api.Payloader, error) { } // Issue implements PayloadConvertor Issue method -func (f *WechatworkPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { +func (wc wechatworkConvertor) Issue(p *api.IssuePayload) (WechatworkPayload, error) { text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true) var content string content += fmt.Sprintf(" >%s\n >%s \n > %s \n [%s](%s)", text, attachmentText, issueTitle, p.Issue.HTMLURL, p.Issue.HTMLURL) @@ -117,7 +105,7 @@ func (f *WechatworkPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { } // IssueComment implements PayloadConvertor IssueComment method -func (f *WechatworkPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { +func (wc wechatworkConvertor) IssueComment(p *api.IssueCommentPayload) (WechatworkPayload, error) { text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true) var content string content += fmt.Sprintf(" >%s\n >%s \n >%s \n [%s](%s)", text, p.Comment.Body, issueTitle, p.Comment.HTMLURL, p.Comment.HTMLURL) @@ -126,7 +114,7 @@ func (f *WechatworkPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloa } // PullRequest implements PayloadConvertor PullRequest method -func (f *WechatworkPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { +func (wc wechatworkConvertor) PullRequest(p *api.PullRequestPayload) (WechatworkPayload, error) { text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true) pr := fmt.Sprintf("> %s \r\n > %s \r\n > %s \r\n", text, issueTitle, attachmentText) @@ -135,13 +123,13 @@ func (f *WechatworkPayload) PullRequest(p *api.PullRequestPayload) (api.Payloade } // Review implements PayloadConvertor Review method -func (f *WechatworkPayload) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (api.Payloader, error) { +func (wc wechatworkConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (WechatworkPayload, error) { var text, title string switch p.Action { case api.HookIssueReviewed: action, err := parseHookPullRequestEventType(event) if err != nil { - return nil, err + return WechatworkPayload{}, err } title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) text = p.Review.Content @@ -151,7 +139,7 @@ func (f *WechatworkPayload) Review(p *api.PullRequestPayload, event webhook_modu } // Repository implements PayloadConvertor Repository method -func (f *WechatworkPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { +func (wc wechatworkConvertor) Repository(p *api.RepositoryPayload) (WechatworkPayload, error) { var title string switch p.Action { case api.HookRepoCreated: @@ -162,30 +150,33 @@ func (f *WechatworkPayload) Repository(p *api.RepositoryPayload) (api.Payloader, return newWechatworkMarkdownPayload(title), nil } - return nil, nil + return WechatworkPayload{}, nil } // Wiki implements PayloadConvertor Wiki method -func (f *WechatworkPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { +func (wc wechatworkConvertor) Wiki(p *api.WikiPayload) (WechatworkPayload, error) { text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, true) return newWechatworkMarkdownPayload(text), nil } // Release implements PayloadConvertor Release method -func (f *WechatworkPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { +func (wc wechatworkConvertor) Release(p *api.ReleasePayload) (WechatworkPayload, error) { text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true) return newWechatworkMarkdownPayload(text), nil } -func (f *WechatworkPayload) Package(p *api.PackagePayload) (api.Payloader, error) { +func (wc wechatworkConvertor) Package(p *api.PackagePayload) (WechatworkPayload, error) { text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true) return newWechatworkMarkdownPayload(text), nil } -// GetWechatworkPayload GetWechatworkPayload converts a ding talk webhook into a WechatworkPayload -func GetWechatworkPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) { - return convertPayloader(new(WechatworkPayload), p, event) +type wechatworkConvertor struct{} + +var _ payloadConvertor[WechatworkPayload] = wechatworkConvertor{} + +func newWechatworkRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { + return newJSONRequest(wechatworkConvertor{}, w, t, true) } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 81e0b84ea8..01c8cf987f 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/sync" @@ -87,7 +88,7 @@ func NormalizeWikiBranch(ctx context.Context, repo *repo_model.Repository, to st return err } - if err := gitRepo.SetDefaultBranch(to); err != nil { + if err := gitrepo.SetDefaultBranch(ctx, repo, to); err != nil { return err } diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml deleted file mode 100644 index 7c10074bc5..0000000000 --- a/snap/snapcraft.yaml +++ /dev/null @@ -1,86 +0,0 @@ -name: gitea -summary: Gitea - A painless self-hosted Git service -description: | - The goal of this project is to make the easiest, fastest, and most painless - way of setting up a self-hosted Git service. With Go, this can be done with - an independent binary distribution across ALL platforms that Go supports, - including Linux, Mac OS X, Windows and ARM. - -icon: public/assets/img/logo.png -confinement: strict -base: core22 -adopt-info: gitea - -architectures: - - build-on: armhf - - build-on: amd64 - - build-on: arm64 - -environment: - GITEA_CUSTOM: "$SNAP_COMMON" - GITEA_WORK_DIR: "$SNAP_COMMON" - GIT_TEMPLATE_DIR: "$SNAP/usr/share/git-core/templates" - GIT_EXEC_PATH: "$SNAP/usr/lib/git-core" - -apps: - gitea: - command: gitea - plugs: [network, network-bind, removable-media] - web: - command: gitea web - daemon: simple - plugs: [network, network-bind, removable-media] - dump: - command: gitea dump - plugs: [home, removable-media] - version: - command: gitea --version - sqlite: - command: usr/bin/sqlite3 - -parts: - gitea: - plugin: make - source: . - stage-packages: [ git, sqlite3, openssh-client ] - build-packages: [ git, libpam0g-dev, libsqlite3-dev, build-essential] - build-snaps: [ go/1.21/stable, node/18/stable ] - build-environment: - - LDFLAGS: "" - override-pull: | - craftctl default - - git config --global --add safe.directory /root/parts/gitea/src - last_committed_tag="$(git for-each-ref --sort=taggerdate --format '%(tag)' refs/tags | tail -n 1)" - last_released_tag="$(snap info gitea | awk '$1 == "latest/candidate:" { print $2 }')" - # If the latest tag from the upstream project has not been released to - # stable, build that tag instead of master. - if [ "${last_committed_tag}" != "${last_released_tag}" ]; then - git fetch - git checkout "${last_committed_tag}" - fi - - version="$(git describe --always | sed -e 's/-/+git/;y/-/./')" - [ -n "$(echo $version | grep "+git")" ] && grade=devel || grade=stable - craftctl set version="$version" - craftctl set grade="$grade" - - override-build: | - set -x - sed -i 's/os.Getuid()/1/g' modules/setting/setting.go - TAGS="bindata sqlite sqlite_unlock_notify pam cert" make build - install -D gitea "${SNAPCRAFT_PART_INSTALL}/gitea" - cp -r options "${SNAPCRAFT_PART_INSTALL}/" - - prime: - - -etc - - -usr/lib/systemd - - -usr/lib/gcc - - -usr/lib/sasl2 - - -usr/lib/x86_64-linux-gnu/krb5 - - -usr/share/apport - - -usr/share/bash-completion - - -usr/share/git-core/contrib - - -usr/share/man - - -usr/share/upstart - - -var diff --git a/tailwind.config.js b/tailwind.config.js index 7f36822001..d783268bd7 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,17 +1,41 @@ import {readFileSync} from 'node:fs'; import {env} from 'node:process'; -import {parse} from 'css-variables-parser'; +import {parse} from 'postcss'; const isProduction = env.NODE_ENV !== 'development'; +function extractRootVars(css) { + const root = parse(css); + const vars = new Set(); + root.walkRules((rule) => { + if (rule.selector !== ':root') return; + rule.each((decl) => { + if (decl.value && decl.prop.startsWith('--')) { + vars.add(decl.prop.substring(2)); + } + }); + }); + return Array.from(vars); +} + +const vars = extractRootVars([ + readFileSync(new URL('web_src/css/themes/theme-gitea-light.css', import.meta.url), 'utf8'), + readFileSync(new URL('web_src/css/themes/theme-gitea-dark.css', import.meta.url), 'utf8'), +].join('\n')); + export default { prefix: 'tw-', important: true, // the frameworks are mixed together, so tailwind needs to override other framework's styles content: [ isProduction && '!./templates/devtest/**/*', isProduction && '!./web_src/js/standalone/devtest.js', + '!./templates/swagger/v1_json.tmpl', + '!./templates/user/auth/oidc_wellknown.tmpl', + '!**/*_test.go', + '!./modules/{public,options,templates}/bindata.go', + './{build,models,modules,routers,services}/**/*.go', './templates/**/*.tmpl', - './web_src/**/*.{js,vue}', + './web_src/js/**/*.{js,vue}', ].filter(Boolean), blocklist: [ // classes that don't work without CSS variables from "@tailwind base" which we don't use @@ -23,15 +47,10 @@ export default { theme: { colors: { // make `tw-bg-red` etc work with our CSS variables - ...Object.fromEntries( - Object.keys(parse([ - readFileSync(new URL('web_src/css/themes/theme-gitea-light.css', import.meta.url), 'utf8'), - readFileSync(new URL('web_src/css/themes/theme-gitea-dark.css', import.meta.url), 'utf8'), - ].join('\n'), {})).filter((prop) => prop.startsWith('color-')).map((prop) => { - const color = prop.substring(6); - return [color, `var(--color-${color})`]; - }) - ), + ...Object.fromEntries(vars.filter((prop) => prop.startsWith('color-')).map((prop) => { + const color = prop.substring(6); + return [color, `var(--color-${color})`]; + })), inherit: 'inherit', current: 'currentcolor', transparent: 'transparent', diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 65e82ba26f..2187761828 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -438,7 +438,7 @@ {{ctx.Locale.Tr "admin.auths.tips"}}
      -
      GMail Settings:
      +
      {{ctx.Locale.Tr "admin.auths.tips.gmail_settings"}}

      Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true

      {{ctx.Locale.Tr "admin.auths.tips.oauth2.general"}}:
      diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl index f32f77d5dc..d8935341f4 100644 --- a/templates/admin/auth/new.tmpl +++ b/templates/admin/auth/new.tmpl @@ -82,7 +82,7 @@ {{ctx.Locale.Tr "admin.auths.tips"}}
      -
      GMail Settings:
      +
      {{ctx.Locale.Tr "admin.auths.tips.gmail_settings"}}

      Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true

      {{ctx.Locale.Tr "admin.auths.tips.oauth2.general"}}:
      diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index ce6edf8a97..0c944fcb8f 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -285,27 +285,6 @@
      -

      - {{ctx.Locale.Tr "admin.config.picture_config"}} -

      -
      -
      -
      {{ctx.Locale.Tr "admin.config.disable_gravatar"}}
      -
      -
      - -
      -
      -
      -
      {{ctx.Locale.Tr "admin.config.enable_federated_avatar"}}
      -
      -
      - -
      -
      -
      -
      -

      {{ctx.Locale.Tr "admin.config.git_config"}}

      diff --git a/templates/admin/config_settings.tmpl b/templates/admin/config_settings.tmpl new file mode 100644 index 0000000000..22ad5c24ac --- /dev/null +++ b/templates/admin/config_settings.tmpl @@ -0,0 +1,42 @@ +{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin config")}} +

      + {{ctx.Locale.Tr "admin.config.picture_config"}} +

      +
      +
      +
      {{ctx.Locale.Tr "admin.config.disable_gravatar"}}
      +
      +
      + +
      +
      +
      +
      {{ctx.Locale.Tr "admin.config.enable_federated_avatar"}}
      +
      +
      + +
      +
      +
      +
      + +

      + {{ctx.Locale.Tr "repository"}} +

      +
      +
      +
      +
      + {{ctx.Locale.Tr "admin.config.open_with_editor_app_help"}} +
      {{.DefaultOpenWithEditorAppsString}}
      +
      +
      +
      + +
      +
      + +
      +
      +
      +{{template "admin/layout_footer" .}} diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index 53a12cbe27..cc7d338589 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -2,7 +2,7 @@
      {{if .NeedUpdate}}
      -

      {{(ctx.Locale.Tr "admin.dashboard.new_version_hint" .RemoteVersion AppVer) | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "admin.dashboard.new_version_hint" .RemoteVersion AppVer}}

      {{end}}

      diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl index bcd80368e6..29fbb5f039 100644 --- a/templates/admin/emails/list.tmpl +++ b/templates/admin/emails/list.tmpl @@ -47,8 +47,8 @@ {{range .Emails}} {{.Name}} - {{.FullName}} - {{.Email}} + {{.FullName}} + {{.Email}} {{if .IsPrimary}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} {{if .CanChange}} diff --git a/templates/admin/layout_head.tmpl b/templates/admin/layout_head.tmpl index 0067f336e0..b326c82a6c 100644 --- a/templates/admin/layout_head.tmpl +++ b/templates/admin/layout_head.tmpl @@ -3,7 +3,7 @@
      {{template "base/alert" .ctxData}}
      -
      +
      {{template "admin/navbar" .ctxData}}
      {{/* block: admin-setting-content */}} diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl index f23bdee124..16ec1b4b5b 100644 --- a/templates/admin/navbar.tmpl +++ b/templates/admin/navbar.tmpl @@ -77,9 +77,17 @@
      {{end}} - - {{ctx.Locale.Tr "admin.config"}} - +
      + {{ctx.Locale.Tr "admin.config"}} + +
      {{ctx.Locale.Tr "admin.notices"}} diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index ed410425b5..e0abe4f8c0 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -31,7 +31,7 @@ -
      + {{.CsrfTokenHtml}}
      diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl index cf860dab2a..aef4815424 100644 --- a/templates/admin/packages/list.tmpl +++ b/templates/admin/packages/list.tmpl @@ -62,8 +62,8 @@ {{end}} {{.Package.Type.Name}} - {{.Package.Name}} - {{.Version.Version}} + {{.Package.Name}} + {{.Version.Version}} {{.Creator.Name}} {{if .Repository}} diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl index 8fdc80fc70..e9ce17ac90 100644 --- a/templates/admin/user/list.tmpl +++ b/templates/admin/user/list.tmpl @@ -96,7 +96,7 @@ {{ctx.Locale.Tr "admin.users.remote"}} {{end}} - {{.Email}} + {{.Email}} {{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} {{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} {{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 06fc5913d0..e755775985 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -14,7 +14,7 @@

      -
      - - - - - - - -
      - {{range .LineNumbers}} - {{.}} - {{end}} - {{.FormattedLines}}
      -
      + {{template "shared/searchfile" dict "RepoLink" $repo.Link "IsIndexer" true "SearchResult" .}}
      {{template "shared/searchbottom" dict "root" $ "result" .}}
      diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl index ccf188609c..0b1f982ee4 100644 --- a/templates/devtest/gitea-ui.tmpl +++ b/templates/devtest/gitea-ui.tmpl @@ -105,9 +105,46 @@
      -

      GiteaOriginUrl

      -
      -
      +

      <origin-url>

      +
      +
      +
      + +
      +

      <overflow-menu>

      + + + +
      + +
      +

      GiteaAbsoluteDate

      +
      +
      +
      +
      +
      +
      relative-time:
      diff --git a/templates/explore/navbar.tmpl b/templates/explore/navbar.tmpl index 7f2aea497a..8841613b9f 100644 --- a/templates/explore/navbar.tmpl +++ b/templates/explore/navbar.tmpl @@ -1,5 +1,5 @@ -
      diff --git a/templates/home.tmpl b/templates/home.tmpl index 393525dcd4..1e5369e7ee 100644 --- a/templates/home.tmpl +++ b/templates/home.tmpl @@ -17,7 +17,7 @@ {{svg "octicon-flame"}} {{ctx.Locale.Tr "startpage.install"}}

      - {{ctx.Locale.Tr "startpage.install_desc" | SanitizeHTML}} + {{ctx.Locale.Tr "startpage.install_desc"}}

      @@ -25,7 +25,7 @@ {{svg "octicon-device-desktop"}} {{ctx.Locale.Tr "startpage.platform"}}

      - {{ctx.Locale.Tr "startpage.platform_desc" | SanitizeHTML}} + {{ctx.Locale.Tr "startpage.platform_desc"}}

      @@ -35,7 +35,7 @@ {{svg "octicon-rocket"}} {{ctx.Locale.Tr "startpage.lightweight"}}

      - {{ctx.Locale.Tr "startpage.lightweight_desc" | SanitizeHTML}} + {{ctx.Locale.Tr "startpage.lightweight_desc"}}

      @@ -43,7 +43,7 @@ {{svg "octicon-code"}} {{ctx.Locale.Tr "startpage.license"}}

      - {{ctx.Locale.Tr "startpage.license_desc" | SanitizeHTML}} + {{ctx.Locale.Tr "startpage.license_desc"}}

      diff --git a/templates/install.tmpl b/templates/install.tmpl index c18f25f79c..b3aea39ee5 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -336,7 +336,7 @@
      - These configuration options will be written into: {{.CustomConfFile}} + {{ctx.Locale.Tr "install.config_location_hint"}} {{.CustomConfFile}}
      diff --git a/templates/mail/auth/activate.tmpl b/templates/mail/auth/activate.tmpl index c50717d315..b1bb4cb463 100644 --- a/templates/mail/auth/activate.tmpl +++ b/templates/mail/auth/activate.tmpl @@ -8,8 +8,8 @@ {{$activate_url := printf "%suser/activate?code=%s" AppUrl (QueryEscape .Code)}} -

      {{.locale.Tr "mail.activate_account.text_1" (.DisplayName|DotEscape) AppName | SanitizeHTML}}


      -

      {{.locale.Tr "mail.activate_account.text_2" .ActiveCodeLives | SanitizeHTML}}

      {{$activate_url}}


      +

      {{.locale.Tr "mail.activate_account.text_1" (.DisplayName|DotEscape) AppName}}


      +

      {{.locale.Tr "mail.activate_account.text_2" .ActiveCodeLives}}

      {{$activate_url}}


      {{.locale.Tr "mail.link_not_working_do_paste"}}

      © {{AppName}}

      diff --git a/templates/mail/auth/activate_email.tmpl b/templates/mail/auth/activate_email.tmpl index 30fcb99ab8..3d32f80a4e 100644 --- a/templates/mail/auth/activate_email.tmpl +++ b/templates/mail/auth/activate_email.tmpl @@ -8,8 +8,8 @@ {{$activate_url := printf "%suser/activate_email?code=%s&email=%s" AppUrl (QueryEscape .Code) (QueryEscape .Email)}} -

      {{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape) | SanitizeHTML}}


      -

      {{.locale.Tr "mail.activate_email.text" .ActiveCodeLives | SanitizeHTML}}

      {{$activate_url}}


      +

      {{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape)}}


      +

      {{.locale.Tr "mail.activate_email.text" .ActiveCodeLives}}

      {{$activate_url}}


      {{.locale.Tr "mail.link_not_working_do_paste"}}

      © {{AppName}}

      diff --git a/templates/mail/auth/register_notify.tmpl b/templates/mail/auth/register_notify.tmpl index 27c685e58f..62dbf7d927 100644 --- a/templates/mail/auth/register_notify.tmpl +++ b/templates/mail/auth/register_notify.tmpl @@ -8,7 +8,7 @@ {{$set_pwd_url := printf "%[1]suser/forgot_password" AppUrl}} -

      {{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape) | SanitizeHTML}}


      +

      {{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape)}}


      {{.locale.Tr "mail.register_notify.text_1" AppName}}


      {{.locale.Tr "mail.register_notify.text_2" .Username}}

      {{AppUrl}}user/login


      {{.locale.Tr "mail.register_notify.text_3" $set_pwd_url}}


      diff --git a/templates/mail/auth/reset_passwd.tmpl b/templates/mail/auth/reset_passwd.tmpl index e1af5b483c..55b1ecec3f 100644 --- a/templates/mail/auth/reset_passwd.tmpl +++ b/templates/mail/auth/reset_passwd.tmpl @@ -8,8 +8,8 @@ {{$recover_url := printf "%suser/recover_account?code=%s" AppUrl (QueryEscape .Code)}} -

      {{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape) | SanitizeHTML}}


      -

      {{.locale.Tr "mail.reset_password.text" .ResetPwdCodeLives | SanitizeHTML}}

      {{$recover_url}}


      +

      {{.locale.Tr "mail.hi_user_x" (.DisplayName|DotEscape)}}


      +

      {{.locale.Tr "mail.reset_password.text" .ResetPwdCodeLives}}

      {{$recover_url}}


      {{.locale.Tr "mail.link_not_working_do_paste"}}

      © {{AppName}}

      diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl index 796dc403b7..4e83dbcfdb 100644 --- a/templates/mail/issue/default.tmpl +++ b/templates/mail/issue/default.tmpl @@ -16,7 +16,7 @@ - {{if .IsMention}}

      {{.locale.Tr "mail.issue.x_mentioned_you" .Doer.Name | SanitizeHTML}}

      {{end}} + {{if .IsMention}}

      {{.locale.Tr "mail.issue.x_mentioned_you" .Doer.Name}}

      {{end}} {{if eq .ActionName "push"}}

      {{if .Comment.IsForcePush}} @@ -58,7 +58,7 @@ {{.locale.Tr "mail.issue.action.new" .Doer.Name .Issue.Index}} {{end}} {{else}} - {{.Body | SanitizeHTML}} + {{.Body}} {{end -}} {{- range .ReviewComments}}


      diff --git a/templates/mail/notify/admin_new_user.tmpl b/templates/mail/notify/admin_new_user.tmpl index 04f90b61ab..613c58194a 100644 --- a/templates/mail/notify/admin_new_user.tmpl +++ b/templates/mail/notify/admin_new_user.tmpl @@ -13,8 +13,8 @@
        -

        {{.Locale.Tr "mail.admin.new_user.user_info" | SanitizeHTML}}: @{{.NewUser.Name}}

        -
      • {{.Locale.Tr "admin.users.created" | SanitizeHTML}}: {{DateTime "full" .NewUser.CreatedUnix}}
      • +

        {{.Locale.Tr "mail.admin.new_user.user_info"}}: @{{.NewUser.Name}}

        +
      • {{.Locale.Tr "admin.users.created"}}: {{DateTime "full" .NewUser.CreatedUnix}}

      {{.Body | SanitizeHTML}}

      diff --git a/templates/mail/team_invite.tmpl b/templates/mail/team_invite.tmpl index 67b368f348..cb0c0c0a50 100644 --- a/templates/mail/team_invite.tmpl +++ b/templates/mail/team_invite.tmpl @@ -5,7 +5,7 @@ -

      {{.locale.Tr "mail.team_invite.text_1" (DotEscape .Inviter.DisplayName) (DotEscape .Team.Name) (DotEscape .Organization.DisplayName) | SanitizeHTML}}

      +

      {{.locale.Tr "mail.team_invite.text_1" (DotEscape .Inviter.DisplayName) (DotEscape .Team.Name) (DotEscape .Organization.DisplayName)}}

      {{.locale.Tr "mail.team_invite.text_2"}}

      {{.InviteURL}}

      {{.locale.Tr "mail.link_not_working_do_paste"}}

      {{.locale.Tr "mail.team_invite.text_3" .Invite.Email}}

      diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl index efbbc43b1d..943557b1ca 100644 --- a/templates/org/header.tmpl +++ b/templates/org/header.tmpl @@ -7,7 +7,7 @@ {{if .Org.Visibility.IsLimited}}{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}{{end}} {{if .Org.Visibility.IsPrivate}}{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}{{end}} - + {{if .EnableFeed}} {{svg "octicon-rss" 24}} diff --git a/templates/org/menu.tmpl b/templates/org/menu.tmpl index f07b26865a..8eacc17e82 100644 --- a/templates/org/menu.tmpl +++ b/templates/org/menu.tmpl @@ -1,50 +1,49 @@
      - +
      diff --git a/templates/org/settings/delete.tmpl b/templates/org/settings/delete.tmpl index 8c93e7548d..e1ef471e34 100644 --- a/templates/org/settings/delete.tmpl +++ b/templates/org/settings/delete.tmpl @@ -6,7 +6,7 @@
      -

      {{svg "octicon-alert"}} {{ctx.Locale.Tr "org.settings.delete_prompt" | SanitizeHTML}}

      +

      {{svg "octicon-alert"}} {{ctx.Locale.Tr "org.settings.delete_prompt"}}

      {{.CsrfTokenHtml}} diff --git a/templates/org/settings/labels.tmpl b/templates/org/settings/labels.tmpl index 56931def82..8eb7b4584e 100644 --- a/templates/org/settings/labels.tmpl +++ b/templates/org/settings/labels.tmpl @@ -2,7 +2,7 @@
      - {{ctx.Locale.Tr "org.settings.labels_desc" | SanitizeHTML}} + {{ctx.Locale.Tr "org.settings.labels_desc"}}
      diff --git a/templates/org/team/invite.tmpl b/templates/org/team/invite.tmpl index 5a8780c205..1167828d14 100644 --- a/templates/org/team/invite.tmpl +++ b/templates/org/team/invite.tmpl @@ -7,7 +7,7 @@ {{ctx.AvatarUtils.Avatar .Organization 140}}
      -
      {{ctx.Locale.Tr "org.teams.invite.title" .Team.Name .Organization.Name | SanitizeHTML}}
      +
      {{ctx.Locale.Tr "org.teams.invite.title" .Team.Name .Organization.Name}}
      {{ctx.Locale.Tr "org.teams.invite.by" .Inviter.Name}}
      {{ctx.Locale.Tr "org.teams.invite.description"}}
      diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl index 99b16ee22d..50ef53b91b 100644 --- a/templates/org/team/new.tmpl +++ b/templates/org/team/new.tmpl @@ -32,14 +32,14 @@
      - {{ctx.Locale.Tr "org.teams.specific_repositories_helper" | SanitizeHTML}} + {{ctx.Locale.Tr "org.teams.specific_repositories_helper"}}
      - {{ctx.Locale.Tr "org.teams.all_repositories_helper" | SanitizeHTML}} + {{ctx.Locale.Tr "org.teams.all_repositories_helper"}}
      diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl index e59c0e5613..9311a46e38 100644 --- a/templates/org/team/sidebar.tmpl +++ b/templates/org/team/sidebar.tmpl @@ -27,16 +27,16 @@
      {{if eq .Team.LowerName "owners"}}
      - {{ctx.Locale.Tr "org.teams.owners_permission_desc" | SanitizeHTML}} + {{ctx.Locale.Tr "org.teams.owners_permission_desc"}}
      {{else}}

      {{ctx.Locale.Tr "org.team_access_desc"}}

        {{if .Team.IncludesAllRepositories}} -
      • {{ctx.Locale.Tr "org.teams.all_repositories" | SanitizeHTML}}
      • +
      • {{ctx.Locale.Tr "org.teams.all_repositories"}}
      • {{else}} -
      • {{ctx.Locale.Tr "org.teams.specific_repositories" | SanitizeHTML}}
      • +
      • {{ctx.Locale.Tr "org.teams.specific_repositories"}}
      • {{end}} {{if .Team.CanCreateOrgRepo}}
      • {{ctx.Locale.Tr "org.teams.can_create_org_repo"}}
      • @@ -44,10 +44,10 @@
      {{if (eq .Team.AccessMode 2)}}

      {{ctx.Locale.Tr "org.settings.permission"}}

      - {{ctx.Locale.Tr "org.teams.write_permission_desc" | SanitizeHTML}} + {{ctx.Locale.Tr "org.teams.write_permission_desc"}} {{else if (eq .Team.AccessMode 3)}}

      {{ctx.Locale.Tr "org.settings.permission"}}

      - {{ctx.Locale.Tr "org.teams.admin_permission_desc" | SanitizeHTML}} + {{ctx.Locale.Tr "org.teams.admin_permission_desc"}} {{else}} diff --git a/templates/package/content/alpine.tmpl b/templates/package/content/alpine.tmpl index 496ffbc7b5..8914006ff0 100644 --- a/templates/package/content/alpine.tmpl +++ b/templates/package/content/alpine.tmpl @@ -4,12 +4,12 @@
      -
      /$branch/$repository
      +
      /$branch/$repository

      {{ctx.Locale.Tr "packages.alpine.registry.info"}}

      -
      curl -JO 
      +
      curl -JO 
      diff --git a/templates/package/content/cargo.tmpl b/templates/package/content/cargo.tmpl index 53b2ef1152..2f14945d1e 100644 --- a/templates/package/content/cargo.tmpl +++ b/templates/package/content/cargo.tmpl @@ -8,8 +8,8 @@ default = "forgejo" [registries.forgejo] -index = "sparse+" # Sparse index -# index = "" # Git +index = "sparse+" # Sparse index +# index = "" # Git [net] git-fetch-with-cli = true
      diff --git a/templates/package/content/chef.tmpl b/templates/package/content/chef.tmpl index 0f7694edc8..d39164b90b 100644 --- a/templates/package/content/chef.tmpl +++ b/templates/package/content/chef.tmpl @@ -4,7 +4,7 @@
      -
      knife[:supermarket_site] = ''
      +
      knife[:supermarket_site] = ''
      diff --git a/templates/package/content/composer.tmpl b/templates/package/content/composer.tmpl index 7da94095dd..bcc6d3099f 100644 --- a/templates/package/content/composer.tmpl +++ b/templates/package/content/composer.tmpl @@ -7,7 +7,7 @@
      {
       	"repositories": [{
       			"type": "composer",
      -			"url": ""
      +			"url": ""
       		}
       	]
       }
      diff --git a/templates/package/content/conan.tmpl b/templates/package/content/conan.tmpl index 0a9f508dcc..13a7723fe4 100644 --- a/templates/package/content/conan.tmpl +++ b/templates/package/content/conan.tmpl @@ -4,7 +4,7 @@
      -
      conan remote add gitea 
      +
      conan remote add gitea 
      diff --git a/templates/package/content/conda.tmpl b/templates/package/content/conda.tmpl index 313b05ffe9..5ff79445fe 100644 --- a/templates/package/content/conda.tmpl +++ b/templates/package/content/conda.tmpl @@ -4,11 +4,11 @@
      -
      channel_alias: 
      +				
      channel_alias: 
       channels:
      -  - 
      +  - 
       default_channels:
      -  - 
      + -
      diff --git a/templates/package/content/cran.tmpl b/templates/package/content/cran.tmpl index 766dd43a4c..df7a48c3d6 100644 --- a/templates/package/content/cran.tmpl +++ b/templates/package/content/cran.tmpl @@ -4,7 +4,7 @@
      -
      options("repos" = c(getOption("repos"), c(forgejo="")))
      +
      options("repos" = c(getOption("repos"), c(forgejo="")))
      diff --git a/templates/package/content/debian.tmpl b/templates/package/content/debian.tmpl index 3c03eec396..782ac1c8b3 100644 --- a/templates/package/content/debian.tmpl +++ b/templates/package/content/debian.tmpl @@ -4,8 +4,8 @@
      -
      sudo curl  -o /etc/apt/keyrings/forgejo-{{$.PackageDescriptor.Owner.Name}}.asc
      -echo "deb [signed-by=/etc/apt/keyrings/forgejo-{{$.PackageDescriptor.Owner.Name}}.asc]  $distribution $component" | sudo tee -a /etc/apt/sources.list.d/forgejo.list
      +				
      sudo curl  -o /etc/apt/keyrings/forgejo-{{$.PackageDescriptor.Owner.Name}}.asc
      +echo "deb [signed-by=/etc/apt/keyrings/forgejo-{{$.PackageDescriptor.Owner.Name}}.asc]  $distribution $component" | sudo tee -a /etc/apt/sources.list.d/forgejo.list
       sudo apt update

      {{ctx.Locale.Tr "packages.debian.registry.info"}}

      diff --git a/templates/package/content/generic.tmpl b/templates/package/content/generic.tmpl index aec8eb314e..4ebfb9103f 100644 --- a/templates/package/content/generic.tmpl +++ b/templates/package/content/generic.tmpl @@ -6,7 +6,7 @@
      
       {{- range .PackageDescriptor.Files -}}
      -curl -OJ 
      +curl -OJ 
       {{end -}}
       				
      diff --git a/templates/package/content/go.tmpl b/templates/package/content/go.tmpl index 853218e51c..5e32ea7f87 100644 --- a/templates/package/content/go.tmpl +++ b/templates/package/content/go.tmpl @@ -4,7 +4,7 @@
      -
      GOPROXY= go install {{$.PackageDescriptor.Package.Name}}@{{$.PackageDescriptor.Version.Version}}
      +
      GOPROXY= go install {{$.PackageDescriptor.Package.Name}}@{{$.PackageDescriptor.Version.Version}}
      diff --git a/templates/package/content/helm.tmpl b/templates/package/content/helm.tmpl index 59f89be637..9d8555597e 100644 --- a/templates/package/content/helm.tmpl +++ b/templates/package/content/helm.tmpl @@ -4,7 +4,7 @@
      -
      helm repo add {{AppDomain}} 
      +				
      helm repo add {{AppDomain}} 
       helm repo update
      diff --git a/templates/package/content/maven.tmpl b/templates/package/content/maven.tmpl index e764684595..49ada6a3a3 100644 --- a/templates/package/content/maven.tmpl +++ b/templates/package/content/maven.tmpl @@ -7,19 +7,19 @@
      <repositories>
       	<repository>
       		<id>gitea</id>
      -		<url></url>
      +		<url></url>
       	</repository>
       </repositories>
       
       <distributionManagement>
       	<repository>
       		<id>gitea</id>
      -		<url></url>
      +		<url></url>
       	</repository>
       
       	<snapshotRepository>
       		<id>gitea</id>
      -		<url></url>
      +		<url></url>
       	</snapshotRepository>
       </distributionManagement>
      @@ -37,7 +37,7 @@
      -
      mvn dependency:get -DremoteRepositories= -Dartifact={{.PackageDescriptor.Metadata.GroupID}}:{{.PackageDescriptor.Metadata.ArtifactID}}:{{.PackageDescriptor.Version.Version}}
      +
      mvn dependency:get -DremoteRepositories= -Dartifact={{.PackageDescriptor.Metadata.GroupID}}:{{.PackageDescriptor.Metadata.ArtifactID}}:{{.PackageDescriptor.Version.Version}}
      diff --git a/templates/package/content/npm.tmpl b/templates/package/content/npm.tmpl index cfd7595bfc..c5d9b3f428 100644 --- a/templates/package/content/npm.tmpl +++ b/templates/package/content/npm.tmpl @@ -4,7 +4,7 @@
      -
      {{if .PackageDescriptor.Metadata.Scope}}{{.PackageDescriptor.Metadata.Scope}}:{{end}}registry=
      +
      {{if .PackageDescriptor.Metadata.Scope}}{{.PackageDescriptor.Metadata.Scope}}:{{end}}registry=
      diff --git a/templates/package/content/nuget.tmpl b/templates/package/content/nuget.tmpl index d56f50cb22..fadeaffe10 100644 --- a/templates/package/content/nuget.tmpl +++ b/templates/package/content/nuget.tmpl @@ -4,7 +4,7 @@
      -
      dotnet nuget add source --name {{.PackageDescriptor.Owner.Name}} --username your_username --password your_token 
      +
      dotnet nuget add source --name {{.PackageDescriptor.Owner.Name}} --username your_username --password your_token 
      diff --git a/templates/package/content/pub.tmpl b/templates/package/content/pub.tmpl index e83b0d3570..8448b97466 100644 --- a/templates/package/content/pub.tmpl +++ b/templates/package/content/pub.tmpl @@ -4,7 +4,7 @@
      -
      dart pub add {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}} --hosted-url=
      +
      dart pub add {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}} --hosted-url=
      diff --git a/templates/package/content/pypi.tmpl b/templates/package/content/pypi.tmpl index e0353c91c6..6addac3f8e 100644 --- a/templates/package/content/pypi.tmpl +++ b/templates/package/content/pypi.tmpl @@ -4,7 +4,7 @@
      -
      pip install --index-url  {{.PackageDescriptor.Package.Name}}
      +
      pip install --index-url  {{.PackageDescriptor.Package.Name}}
      diff --git a/templates/package/content/rpm.tmpl b/templates/package/content/rpm.tmpl index a7e2141ccd..f5d56623ac 100644 --- a/templates/package/content/rpm.tmpl +++ b/templates/package/content/rpm.tmpl @@ -11,13 +11,13 @@ # {{ctx.Locale.Tr "packages.rpm.distros.redhat"}} {{- range $group := .Groups}} {{- if $group}}{{$group = print "/" $group}}{{end}} -dnf config-manager --add-repo +dnf config-manager --add-repo {{- end}} # {{ctx.Locale.Tr "packages.rpm.distros.suse"}} {{- range $group := .Groups}} {{- if $group}}{{$group = print "/" $group}}{{end}} -zypper addrepo +zypper addrepo {{- end}}
      diff --git a/templates/package/content/rubygems.tmpl b/templates/package/content/rubygems.tmpl index 412c3a2954..009fd703e0 100644 --- a/templates/package/content/rubygems.tmpl +++ b/templates/package/content/rubygems.tmpl @@ -4,11 +4,11 @@
      -
      gem install {{.PackageDescriptor.Package.Name}} --version "{{.PackageDescriptor.Version.Version}}" --source ""
      +
      gem install {{.PackageDescriptor.Package.Name}} --version "{{.PackageDescriptor.Version.Version}}" --source ""
      -
      source "" do
      +				
      source "" do
       	gem "{{.PackageDescriptor.Package.Name}}", "{{.PackageDescriptor.Version.Version}}"
       end
      diff --git a/templates/package/content/swift.tmpl b/templates/package/content/swift.tmpl index 471f5b5457..68db444883 100644 --- a/templates/package/content/swift.tmpl +++ b/templates/package/content/swift.tmpl @@ -4,7 +4,7 @@
      -
      swift package-registry set 
      +
      swift package-registry set 
      diff --git a/templates/package/content/vagrant.tmpl b/templates/package/content/vagrant.tmpl index 79b4d2f054..4dc3e62a06 100644 --- a/templates/package/content/vagrant.tmpl +++ b/templates/package/content/vagrant.tmpl @@ -4,7 +4,7 @@
      -
      vagrant box add --box-version {{.PackageDescriptor.Version.Version}} ""
      +
      vagrant box add --box-version {{.PackageDescriptor.Version.Version}} ""
      diff --git a/templates/package/settings.tmpl b/templates/package/settings.tmpl index 10e26c7010..9424baf493 100644 --- a/templates/package/settings.tmpl +++ b/templates/package/settings.tmpl @@ -10,7 +10,7 @@ {{template "user/overview/header" .}} {{end}} {{template "base/alert" .}} -

      {{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}}) / {{ctx.Locale.Tr "repo.settings"}}

      +

      {{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}}) / {{ctx.Locale.Tr "repo.settings"}}

      {{ctx.Locale.Tr "packages.settings.link"}}

      diff --git a/templates/package/shared/cargo.tmpl b/templates/package/shared/cargo.tmpl index 401d909002..5b0f63965d 100644 --- a/templates/package/shared/cargo.tmpl +++ b/templates/package/shared/cargo.tmpl @@ -3,13 +3,7 @@
      -
      - -
      - - {{.CsrfTokenHtml}} - - + {{if .CargoIndexExists}}
      @@ -17,6 +11,15 @@ {{.CsrfTokenHtml}} + {{else}} +
      + +
      +
      + {{.CsrfTokenHtml}} + + + {{end}}
      diff --git a/templates/package/shared/cleanup_rules/preview.tmpl b/templates/package/shared/cleanup_rules/preview.tmpl index 7a50d5ccca..cff8e8249f 100644 --- a/templates/package/shared/cleanup_rules/preview.tmpl +++ b/templates/package/shared/cleanup_rules/preview.tmpl @@ -19,7 +19,7 @@
      - + diff --git a/templates/package/shared/list.tmpl b/templates/package/shared/list.tmpl index 67c686675c..7b10e52ff7 100644 --- a/templates/package/shared/list.tmpl +++ b/templates/package/shared/list.tmpl @@ -20,7 +20,7 @@
      - {{.Package.Name}} + {{.Package.Name}} {{svg .Package.Type.SVGName 16}} {{.Package.Type.Name}}
      diff --git a/templates/package/shared/versionlist.tmpl b/templates/package/shared/versionlist.tmpl index eee952c096..59d6d89b53 100644 --- a/templates/package/shared/versionlist.tmpl +++ b/templates/package/shared/versionlist.tmpl @@ -23,7 +23,7 @@
      - {{.Version.LowerVersion}} + {{.Version.LowerVersion}}
      {{ctx.Locale.Tr "packages.published_by" (TimeSinceUnix .Version.CreatedUnix ctx.Locale) .Creator.HomeLink .Creator.GetDisplayName}}
      diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl index 0fa23d67fd..54af71126f 100644 --- a/templates/package/view.tmpl +++ b/templates/package/view.tmpl @@ -87,7 +87,7 @@ {{end}}
      {{ctx.Locale.Tr "packages.versions"}} ({{.TotalVersionCount}}) - {{ctx.Locale.Tr "packages.versions.view_all"}} + {{ctx.Locale.Tr "packages.versions.view_all"}}
      {{range .LatestVersions}}
      diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index 30fbd498a4..54a41221bf 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -10,7 +10,7 @@ {{ctx.Locale.PrettyNumber .ClosedCount}} {{ctx.Locale.Tr "repo.issues.closed_title"}}
      - diff --git a/templates/projects/new.tmpl b/templates/projects/new.tmpl index 711dbe842a..92ee36c1c4 100644 --- a/templates/projects/new.tmpl +++ b/templates/projects/new.tmpl @@ -55,7 +55,7 @@
      -
      +
      {{ctx.Locale.Tr "repo.milestones.cancel"}} diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index 3792ccca0e..a6e84024bc 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -41,7 +41,7 @@
      - +
      {{template "repo/issue/label_precolors"}} @@ -165,9 +165,9 @@
      -
      +
      {{range (index $.IssuesMap .ID)}} -
      +
      {{template "repo/issue/card" (dict "Issue" . "Page" $)}}
      {{end}} diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl index 75104cdcd2..6e0d0d1a5e 100644 --- a/templates/repo/blame.tmpl +++ b/templates/repo/blame.tmpl @@ -2,11 +2,11 @@ {{$revsFileLink := URLJoin .RepoLink "src" .BranchNameSubURL "/.git-blame-ignore-revs"}} {{if .UsesIgnoreRevs}}
      -

      {{ctx.Locale.Tr "repo.blame.ignore_revs" $revsFileLink (print $revsFileLink "?bypass-blame-ignore=true") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.blame.ignore_revs" $revsFileLink (print $revsFileLink "?bypass-blame-ignore=true")}}

      {{else}}
      -

      {{ctx.Locale.Tr "repo.blame.ignore_revs.failed" $revsFileLink | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.blame.ignore_revs.failed" $revsFileLink}}

      {{end}} {{end}} diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl index 4aa0e22b78..916111faca 100644 --- a/templates/repo/branch/list.tmpl +++ b/templates/repo/branch/list.tmpl @@ -72,7 +72,7 @@
      {{ctx.Locale.Tr "repo.branches"}}
      -
      +
      -

      {{ctx.Locale.Tr "repo.branch.delete_desc" | SanitizeHTML}}

      +

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

      {{template "base/modal_actions_confirm" .}}
      diff --git a/templates/repo/cite/cite_buttons.tmpl b/templates/repo/cite/cite_buttons.tmpl index 9953c92c8a..426ca3858e 100644 --- a/templates/repo/cite/cite_buttons.tmpl +++ b/templates/repo/cite/cite_buttons.tmpl @@ -6,6 +6,6 @@ BibTeX - diff --git a/templates/repo/cite/cite_modal.tmpl b/templates/repo/cite/cite_modal.tmpl index c34c77e0c4..fb251442ca 100644 --- a/templates/repo/cite/cite_modal.tmpl +++ b/templates/repo/cite/cite_modal.tmpl @@ -1,16 +1,14 @@ -
      {{.Package.Type.Name}} {{.Package.Name}}{{.Version.Version}}{{.Version.Version}} {{.Creator.Name}} {{FileSize .CalculateBlobSize}} {{DateTime "short" .Version.CreatedUnix}}
      {{if $isImage}} {{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead "sniffedTypeBase" $sniffedTypeBase "sniffedTypeHead" $sniffedTypeHead}} diff --git a/templates/repo/diff/comment_form.tmpl b/templates/repo/diff/comment_form.tmpl index 54817d4740..6005ea28ef 100644 --- a/templates/repo/diff/comment_form.tmpl +++ b/templates/repo/diff/comment_form.tmpl @@ -27,7 +27,7 @@ - diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index fe2764aa83..99d75b8a84 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -80,7 +80,7 @@
      {{if .Repository.IsArchived}} -
      +
      {{ctx.Locale.Tr "repo.settings.archive.mirrors_unavailable"}}
      {{else}} @@ -191,7 +191,7 @@
      -

      {{ctx.Locale.Tr "repo.mirror_lfs_endpoint_desc" "https://github.com/git-lfs/git-lfs/blob/main/docs/api/server-discovery.md#server-discovery" | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.mirror_lfs_endpoint_desc" "https://github.com/git-lfs/git-lfs/blob/main/docs/api/server-discovery.md#server-discovery"}}

      {{end}}
      @@ -742,7 +742,13 @@
      - +
      diff --git a/templates/repo/settings/tags.tmpl b/templates/repo/settings/tags.tmpl index e4fcf2ee6b..31fb59e5e3 100644 --- a/templates/repo/settings/tags.tmpl +++ b/templates/repo/settings/tags.tmpl @@ -1,7 +1,7 @@ {{template "repo/settings/layout_head" (dict "ctxData" . "pageClass" "repository settings edit")}}
      {{if .Repository.IsArchived}} -
      +
      {{ctx.Locale.Tr "repo.settings.archive.tagsettings_unavailable"}}
      {{else}} diff --git a/templates/repo/settings/units/issues.tmpl b/templates/repo/settings/units/issues.tmpl index 7ddafd1c6a..77f5782151 100644 --- a/templates/repo/settings/units/issues.tmpl +++ b/templates/repo/settings/units/issues.tmpl @@ -61,7 +61,7 @@
      -

      {{ctx.Locale.Tr "repo.settings.tracker_url_format_desc" | SanitizeHTML}}

      +

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

      @@ -89,7 +89,7 @@
      -

      {{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern_desc" | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern_desc"}}

      diff --git a/templates/repo/settings/webhook/base_list.tmpl b/templates/repo/settings/webhook/base_list.tmpl index 00f9a48ba7..e56929b70f 100644 --- a/templates/repo/settings/webhook/base_list.tmpl +++ b/templates/repo/settings/webhook/base_list.tmpl @@ -10,7 +10,7 @@
      - {{.Description | SanitizeHTML}} + {{.Description}}
      {{range .Webhooks}}
      diff --git a/templates/repo/settings/webhook/dingtalk.tmpl b/templates/repo/settings/webhook/dingtalk.tmpl index a620d9e241..0ba99e98ee 100644 --- a/templates/repo/settings/webhook/dingtalk.tmpl +++ b/templates/repo/settings/webhook/dingtalk.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "dingtalk"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://dingtalk.com" (ctx.Locale.Tr "repo.settings.web_hook_name_dingtalk") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://dingtalk.com" (ctx.Locale.Tr "repo.settings.web_hook_name_dingtalk")}}

      {{.CsrfTokenHtml}}
      diff --git a/templates/repo/settings/webhook/discord.tmpl b/templates/repo/settings/webhook/discord.tmpl index ff1ca75d3f..b623a6d8d3 100644 --- a/templates/repo/settings/webhook/discord.tmpl +++ b/templates/repo/settings/webhook/discord.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "discord"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://discord.com" (ctx.Locale.Tr "repo.settings.web_hook_name_discord") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://discord.com" (ctx.Locale.Tr "repo.settings.web_hook_name_discord")}}

      {{.CsrfTokenHtml}}
      diff --git a/templates/repo/settings/webhook/feishu.tmpl b/templates/repo/settings/webhook/feishu.tmpl index 1acb42c76c..d80deab26f 100644 --- a/templates/repo/settings/webhook/feishu.tmpl +++ b/templates/repo/settings/webhook/feishu.tmpl @@ -1,6 +1,6 @@ {{if eq .HookType "feishu"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://feishu.cn" (ctx.Locale.Tr "repo.settings.web_hook_name_feishu") | SanitizeHTML}}

      -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://larksuite.com" (ctx.Locale.Tr "repo.settings.web_hook_name_larksuite") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://feishu.cn" (ctx.Locale.Tr "repo.settings.web_hook_name_feishu")}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://larksuite.com" (ctx.Locale.Tr "repo.settings.web_hook_name_larksuite")}}

      {{.CsrfTokenHtml}}
      diff --git a/templates/repo/settings/webhook/forgejo.tmpl b/templates/repo/settings/webhook/forgejo.tmpl index 0045f27616..0bfb99115c 100644 --- a/templates/repo/settings/webhook/forgejo.tmpl +++ b/templates/repo/settings/webhook/forgejo.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "forgejo"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://forgejo.org/docs/latest/user/webhooks/" (ctx.Locale.Tr "repo.settings.web_hook_name_forgejo") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://forgejo.org/docs/latest/user/webhooks/" (ctx.Locale.Tr "repo.settings.web_hook_name_forgejo")}}

      {{template "base/disable_form_autofill"}} {{.CsrfTokenHtml}} diff --git a/templates/repo/settings/webhook/gitea.tmpl b/templates/repo/settings/webhook/gitea.tmpl index cb2e789459..38ec29c784 100644 --- a/templates/repo/settings/webhook/gitea.tmpl +++ b/templates/repo/settings/webhook/gitea.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "gitea"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://forgejo.org/docs/latest/user/webhooks/" (ctx.Locale.Tr "repo.settings.web_hook_name_gitea") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://forgejo.org/docs/latest/user/webhooks/" (ctx.Locale.Tr "repo.settings.web_hook_name_gitea")}}

      {{template "base/disable_form_autofill"}} {{.CsrfTokenHtml}} diff --git a/templates/repo/settings/webhook/gogs.tmpl b/templates/repo/settings/webhook/gogs.tmpl index a81b15fd5b..ff1742aa18 100644 --- a/templates/repo/settings/webhook/gogs.tmpl +++ b/templates/repo/settings/webhook/gogs.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "gogs"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://forgejo.org/docs/latest/user/webhooks/" (ctx.Locale.Tr "repo.settings.web_hook_name_gogs") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://forgejo.org/docs/latest/user/webhooks/" (ctx.Locale.Tr "repo.settings.web_hook_name_gogs")}}

      {{template "base/disable_form_autofill"}} {{.CsrfTokenHtml}} diff --git a/templates/repo/settings/webhook/history.tmpl b/templates/repo/settings/webhook/history.tmpl index 3c21a42421..4e0f0e9c3e 100644 --- a/templates/repo/settings/webhook/history.tmpl +++ b/templates/repo/settings/webhook/history.tmpl @@ -19,6 +19,8 @@
      {{if .IsSucceed}} {{svg "octicon-check"}} + {{else if not .IsDelivered}} + {{svg "octicon-stopwatch"}} {{else}} {{svg "octicon-alert"}} {{end}} @@ -62,7 +64,7 @@ {{range $key, $val := .RequestInfo.Headers}}{{$key}}: {{$val}} {{end}}
      {{ctx.Locale.Tr "repo.settings.webhook.payload"}}
      -
      {{.PayloadContent}}
      +
      {{or .RequestInfo.Body .PayloadContent}}
      {{else}} - {{end}} diff --git a/templates/repo/settings/webhook/matrix.tmpl b/templates/repo/settings/webhook/matrix.tmpl index 662beeaccb..7f1c9f08e6 100644 --- a/templates/repo/settings/webhook/matrix.tmpl +++ b/templates/repo/settings/webhook/matrix.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "matrix"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://matrix.org/" (ctx.Locale.Tr "repo.settings.web_hook_name_matrix") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://matrix.org/" (ctx.Locale.Tr "repo.settings.web_hook_name_matrix")}}

      {{.CsrfTokenHtml}}
      diff --git a/templates/repo/settings/webhook/msteams.tmpl b/templates/repo/settings/webhook/msteams.tmpl index 08e381de38..62ea24e763 100644 --- a/templates/repo/settings/webhook/msteams.tmpl +++ b/templates/repo/settings/webhook/msteams.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "msteams"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://teams.microsoft.com" (ctx.Locale.Tr "repo.settings.web_hook_name_msteams") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://teams.microsoft.com" (ctx.Locale.Tr "repo.settings.web_hook_name_msteams")}}

      {{.CsrfTokenHtml}}
      diff --git a/templates/repo/settings/webhook/packagist.tmpl b/templates/repo/settings/webhook/packagist.tmpl index 48a4ecce6d..5330daf339 100644 --- a/templates/repo/settings/webhook/packagist.tmpl +++ b/templates/repo/settings/webhook/packagist.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "packagist"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://packagist.org" (ctx.Locale.Tr "repo.settings.web_hook_name_packagist") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://packagist.org" (ctx.Locale.Tr "repo.settings.web_hook_name_packagist")}}

      {{.CsrfTokenHtml}}
      diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl index a3764371e2..3ef8894444 100644 --- a/templates/repo/settings/webhook/settings.tmpl +++ b/templates/repo/settings/webhook/settings.tmpl @@ -5,19 +5,19 @@
      - +
      - +
      - +
      @@ -255,7 +255,7 @@
      - {{ctx.Locale.Tr "repo.settings.branch_filter_desc" | SanitizeHTML}} + {{ctx.Locale.Tr "repo.settings.branch_filter_desc"}}
      diff --git a/templates/repo/settings/webhook/slack.tmpl b/templates/repo/settings/webhook/slack.tmpl index 5dfc7a67b7..78fc25b9f4 100644 --- a/templates/repo/settings/webhook/slack.tmpl +++ b/templates/repo/settings/webhook/slack.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "slack"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://slack.com" (ctx.Locale.Tr "repo.settings.web_hook_name_slack") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://slack.com" (ctx.Locale.Tr "repo.settings.web_hook_name_slack")}}

      {{.CsrfTokenHtml}}
      diff --git a/templates/repo/settings/webhook/telegram.tmpl b/templates/repo/settings/webhook/telegram.tmpl index 62cac464b6..f92c2be0db 100644 --- a/templates/repo/settings/webhook/telegram.tmpl +++ b/templates/repo/settings/webhook/telegram.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "telegram"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://core.telegram.org/bots" (ctx.Locale.Tr "repo.settings.web_hook_name_telegram") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://core.telegram.org/bots" (ctx.Locale.Tr "repo.settings.web_hook_name_telegram")}}

      {{.CsrfTokenHtml}}
      diff --git a/templates/repo/settings/webhook/wechatwork.tmpl b/templates/repo/settings/webhook/wechatwork.tmpl index 95c4cefe64..78a1617123 100644 --- a/templates/repo/settings/webhook/wechatwork.tmpl +++ b/templates/repo/settings/webhook/wechatwork.tmpl @@ -1,5 +1,5 @@ {{if eq .HookType "wechatwork"}} -

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://work.weixin.qq.com" (ctx.Locale.Tr "repo.settings.web_hook_name_wechatwork") | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.settings.add_web_hook_desc" "https://work.weixin.qq.com" (ctx.Locale.Tr "repo.settings.web_hook_name_wechatwork")}}

      {{.CsrfTokenHtml}}
      diff --git a/templates/repo/unicode_escape_prompt.tmpl b/templates/repo/unicode_escape_prompt.tmpl index 9fe458f97b..8bceafa8bb 100644 --- a/templates/repo/unicode_escape_prompt.tmpl +++ b/templates/repo/unicode_escape_prompt.tmpl @@ -1,22 +1,22 @@ {{if .EscapeStatus}} {{if .EscapeStatus.HasInvisible}} -
      +
      {{ctx.Locale.Tr "repo.invisible_runes_header"}}
      -

      {{ctx.Locale.Tr "repo.invisible_runes_description" | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.invisible_runes_description"}}

      {{if .EscapeStatus.HasAmbiguous}} -

      {{ctx.Locale.Tr "repo.ambiguous_runes_description" | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.ambiguous_runes_description"}}

      {{end}}
      {{else if .EscapeStatus.HasAmbiguous}} -
      +
      {{ctx.Locale.Tr "repo.ambiguous_runes_header"}}
      -

      {{ctx.Locale.Tr "repo.ambiguous_runes_description" | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.ambiguous_runes_description"}}

      {{end}} {{end}} diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 91b10f744a..851e67db63 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -1,12 +1,12 @@
      {{- if .FileError}}
      -
      {{.FileError}}
      +
      {{.FileError}}
      {{end}} {{- if .FileWarning}}
      -
      {{.FileWarning}}
      +
      {{.FileWarning}}
      {{end}} @@ -144,12 +144,12 @@ {{end}}
      {{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{/* - */}}{{/* */}}{{end}}{{/* @@ -62,7 +62,7 @@ {{if $match.RightIdx}}{{end}} {{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{/* - */}}{{/* */}}{{end}}{{/* @@ -79,7 +79,7 @@ {{if $line.LeftIdx}}{{end}} {{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 2))}}{{/* - */}}{{/* */}}{{end}}{{/* @@ -94,7 +94,7 @@ {{if $line.RightIdx}}{{end}} {{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 3))}}{{/* - */}}{{/* */}}{{end}}{{/* diff --git a/templates/repo/diff/section_unified.tmpl b/templates/repo/diff/section_unified.tmpl index ae97dc6db6..e3249c26d5 100644 --- a/templates/repo/diff/section_unified.tmpl +++ b/templates/repo/diff/section_unified.tmpl @@ -52,7 +52,7 @@ {{else}} {{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{/* - */}}{{/* */}}{{end}}{{/* diff --git a/templates/repo/diff/stats.tmpl b/templates/repo/diff/stats.tmpl index 64ee17a7c5..b7acb3d49b 100644 --- a/templates/repo/diff/stats.tmpl +++ b/templates/repo/diff/stats.tmpl @@ -1,5 +1,5 @@ {{Eval .file.Addition "+" .file.Deletion}} - + {{/* if the denominator is zero, then the float result is "width: NaNpx", as before, it just works */}}
      diff --git a/templates/repo/diff/whitespace_dropdown.tmpl b/templates/repo/diff/whitespace_dropdown.tmpl index 7bf2ac9aec..cfabf836d6 100644 --- a/templates/repo/diff/whitespace_dropdown.tmpl +++ b/templates/repo/diff/whitespace_dropdown.tmpl @@ -2,26 +2,26 @@ {{svg "gitea-whitespace"}}
      @@ -45,7 +45,7 @@ {{ctx.Locale.Tr "loading"}}
      - {{ctx.Locale.Tr "loading"}} +
      {{template "repo/editor/commit_form" .}} diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl index 7a0e6926c0..a858a728e9 100644 --- a/templates/repo/empty.tmpl +++ b/templates/repo/empty.tmpl @@ -6,7 +6,7 @@
      {{template "base/alert" .}} {{if .Repository.IsArchived}} -
      +
      {{if .Repository.ArchivedUnix.IsZero}} {{ctx.Locale.Tr "repo.archive.title"}} {{else}} @@ -24,7 +24,7 @@
      -

      {{ctx.Locale.Tr "repo.clone_this_repo"}} {{ctx.Locale.Tr "repo.clone_helper" "http://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository" | SanitizeHTML}}

      +

      {{ctx.Locale.Tr "repo.clone_this_repo"}} {{ctx.Locale.Tr "repo.clone_helper" "http://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository"}}

      {{if and .CanWriteCode (not .Repository.IsArchived)}} diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 286eedff18..64eb790d41 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -79,9 +79,9 @@ {{if .IsGenerated}}
      {{ctx.Locale.Tr "repo.generated_from"}} {{(.TemplateRepo ctx).FullName}}
      {{end}}
      {{end}} -
      {{if .InRepo}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} {{if .Exists}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} {{if .Accessible}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} + {{ctx.Locale.Tr "repo.settings.lfs_findcommits"}}
      -